// license:BSD-3-Clause
// copyright-holders:Curt Coder
#include "includes/newbrain.h"
#include "formats/mfi_dsk.h"

/*

    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:

    - bitmapped graphics mode
    - COP420 microbus access
    - escape key is missing
    - CP/M 2.2 ROMs
    - floppy disc controller
    - convert FDC into a device
    - convert EIM into a device

    - Micropage ROM/RAM card
    - Z80 PIO board
    - peripheral (PI) box
    - sound card

*/

void newbrain_state::check_interrupt()
{
	int level = (!m_clkint || !m_copint) ? ASSERT_LINE : CLEAR_LINE;

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, level);
}

/* Bank Switching */

#define memory_install_unmapped(program, bank, bank_start, bank_end) \
	program.unmap_readwrite(bank_start, bank_end);

#define memory_install_rom_helper(program, bank, bank_start, bank_end) \
	program.install_read_bank(bank_start, bank_end, bank); \
	program.unmap_write(bank_start, bank_end);

#define memory_install_ram_helper(program, bank, bank_start, bank_end) \
	program.install_readwrite_bank(bank_start, bank_end, bank);

void newbrain_eim_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	int bank;

	for (bank = 1; bank < 9; bank++)
	{
		int page = (m_a16 << 3) | bank;
		UINT8 data = ~m_pr[page];
		int ch = (data >> 3) & 0x03;
		int eim_bank = data & 0x07;

		UINT16 eim_bank_start = eim_bank * 0x2000;
		UINT16 bank_start = (bank - 1) * 0x2000;
		UINT16 bank_end = bank_start + 0x1fff;
		char bank_name[10];
		sprintf(bank_name,"bank%d",bank);
		switch (ch)
		{
		case 0:
			/* ROM */
			memory_install_rom_helper(program, bank_name, bank_start, bank_end);
			membank(bank_name)->configure_entry(0, m_eim_rom->base() + eim_bank_start);
			break;

		case 2:
			/* RAM */
			memory_install_ram_helper(program, bank_name, bank_start, bank_end);
			membank(bank_name)->configure_entry(0, m_eim_ram + eim_bank_start);
			break;

		default:
			logerror("Invalid memory channel %u!\n", ch);
			break;
		}
	}
}

void newbrain_state::bankswitch()
{
	address_space &program = m_maincpu->space(AS_PROGRAM);
	int bank;

	for (bank = 1; bank < 9; bank++)
	{
		UINT16 bank_start = (bank - 1) * 0x2000;
		UINT16 bank_end = bank_start + 0x1fff;
		char bank_name[10];
		sprintf(bank_name,"bank%d",bank);

		if (m_pwrup)
		{
			/* all banks point to ROM at 0xe000 */
			memory_install_rom_helper(program, bank_name, bank_start, bank_end);
			membank(bank_name)->configure_entry(0, m_rom->base() + 0xe000);
		}
		else
		{
			membank(bank_name)->configure_entry(0, m_rom->base() + bank_start);

			if (bank < 5)
			{
				/* bank is RAM */
				memory_install_ram_helper(program, bank_name, bank_start, bank_end);
			}
			else if (bank == 5)
			{
				/* 0x8000-0x9fff */
				if (m_eim_rom)
				{
					/* expansion interface ROM */
					memory_install_rom_helper(program, bank_name, bank_start, bank_end);
					membank(bank_name)->configure_entry(0, m_eim_rom->base() + 0x4000);
				}
				else
				{
					/* mirror of 0xa000-0xbfff */
					if (m_rom->base()[0xa001] == 0)
					{
						/* unmapped on the M model */
						memory_install_unmapped(program, bank_name, bank_start, bank_end);
					}
					else
					{
						/* bank is ROM on the A model */
						memory_install_rom_helper(program, bank_name, bank_start, bank_end);
					}

					membank(bank_name)->configure_entry(0, m_rom->base() + 0xa000);
				}
			}
			else if (bank == 6)
			{
				/* 0xa000-0xbfff */
				if (m_rom->base()[0xa001] == 0)
				{
					/* unmapped on the M model */
					memory_install_unmapped(program, bank_name, bank_start, bank_end);
				}
				else
				{
					/* bank is ROM on the A model */
					memory_install_rom_helper(program, bank_name, bank_start, bank_end);
				}
			}
			else
			{
				/* bank is ROM */
				memory_install_rom_helper(program, bank_name, bank_start, bank_end);
			}
		}

		membank(bank_name)->set_entry(0);
	}
}

/* Enable/Status */

WRITE8_MEMBER( newbrain_state::enrg1_w )
{
	/*

	    bit     signal      description

	    0       _CLK        enable frame frequency clock interrupts
	    1                   enable user interrupt
	    2       TVP         enable video display
	    3                   enable V24
	    4                   V24 Select Receive Bit 0
	    5                   V24 Select Receive Bit 1
	    6                   V24 Select Transmit Bit 0
	    7                   V24 Select Transmit Bit 1

	*/

	m_enrg1 = data;
}

WRITE8_MEMBER( newbrain_state::a_enrg1_w )
{
	/*

	    bit     signal      description

	    0       _CLK        Clock Enable
	    1
	    2       TVP         TV Enable
	    3       IOPOWER
	    4       _CTS        Clear to Send V24
	    5       DO          Transmit Data V24
	    6
	    7       PO          Transmit Data Printer

	*/

	m_enrg1 = data;
}

READ8_MEMBER( newbrain_state::ust_r )
{
	/*

	    bit     signal      description

	    0       variable
	    1       variable
	    2       MNS         mains present
	    3       _USRINT0    user status
	    4       _USRINT     user interrupt
	    5       _CLKINT     clock interrupt
	    6       _ACINT      ACIA interrupt
	    7       _COPINT     COP interrupt

	*/

	UINT8 data = (m_copint << 7) | (m_aciaint << 6) | (m_clkint << 5) | (m_userint << 4) | 0x04;

	switch ((m_enrg1 & NEWBRAIN_ENRG1_UST_BIT_0_MASK) >> 6)
	{
	case 0:
		// excess, 1=24, 0=4
		if (m_tvctl & NEWBRAIN_VIDEO_32_40)
		{
			data |= 0x01;
		}
		break;

	case 1:
		// characters per line, 1=40, 0=80
		if (m_tvctl & NEWBRAIN_VIDEO_80L)
		{
			data |= 0x01;
		}
		break;

	case 2:
		// tape in
		break;

	case 3:
		// calling indicator
		break;
	}

	switch ((m_enrg1 & NEWBRAIN_ENRG1_UST_BIT_1_MASK) >> 4)
	{
	case 0:
		// PWRUP, if set indicates that power is supplied to Z80 and memory
		if (m_pwrup)
		{
			data |= 0x02;
		}
		break;

	case 1:
		// TVCNSL, if set then processor has video device as primary console output
		if (m_tvcnsl)
		{
			data |= 0x02;
		}
		break;

	case 2:
		// _BEE, if set then processor is Model A type
		if (m_bee)
		{
			data |= 0x02;
		}
		break;

	case 3:
		// _CALLIND, calling indicator
		data |= 0x02;
		break;
	}

	return data;
}

READ8_MEMBER( newbrain_state::a_ust_r )
{
	/*

	    bit     signal      description

	    0                   +5V
	    1       PWRUP
	    2
	    3
	    4
	    5       _CLKINT     clock interrupt
	    6
	    7       _COPINT     COP interrupt

	*/

	return (m_copint << 7) | (m_clkint << 5) | (m_pwrup << 1) | 0x01;
}

READ8_MEMBER( newbrain_state::user_r )
{
	/*

	    bit     signal      description

	    0       RDDK        Received Data V24
	    1       _CTSD       _Clear to Send V24
	    2
	    3
	    4
	    5       TPIN        Tape in
	    6
	    7       _CTSP       _Clear to Send Printer

	*/

	m_user = 0;

	return 0xff;
}

WRITE8_MEMBER( newbrain_state::user_w )
{
	m_user = data;
}

/* Interrupts */

READ8_MEMBER( newbrain_state::clclk_r )
{
	m_clkint = 1;
	check_interrupt();

	return 0xff;
}

WRITE8_MEMBER( newbrain_state::clclk_w )
{
	m_clkint = 1;
	check_interrupt();
}

READ8_MEMBER( newbrain_state::clusr_r )
{
	m_userint = 1;

	return 0xff;
}

WRITE8_MEMBER( newbrain_state::clusr_w )
{
	m_userint = 1;
}

/* COP420 */

READ8_MEMBER( newbrain_state::cop_l_r )
{
	// connected to the Z80 data bus
	return m_cop_bus;
}

WRITE8_MEMBER( newbrain_state::cop_l_w )
{
	// connected to the Z80 data bus
	m_cop_bus = data;
}

WRITE8_MEMBER( newbrain_state::cop_g_w )
{
	/*

	    bit     description

	    G0      _COPINT
	    G1      _TM1
	    G2      not connected
	    G3      _TM2

	*/

	m_copint = BIT(data, 0);
	check_interrupt();

	/* tape motor enable */

	m_cassette1->change_state(BIT(data,1) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
	m_cassette2->change_state(BIT(data,3) ? CASSETTE_MOTOR_DISABLED : CASSETTE_MOTOR_ENABLED, CASSETTE_MASK_MOTOR);
}

READ8_MEMBER( newbrain_state::cop_g_r )
{
	/*

	    bit     description

	    G0      not connected
	    G1      K9
	    G2      K7
	    G3      K3

	*/

	return (BIT(m_keydata, 3) << 3) | (BIT(m_keydata, 0) << 2) | (BIT(m_keydata, 1) << 1);
}

WRITE8_MEMBER( newbrain_state::cop_d_w )
{
	/*
	    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

	*/

	/* keyboard row reset */

	if (!BIT(data, 0))
	{
		m_keylatch = 0;
	}

	/* tape data output */

	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);

	/* keyboard and display clock */

	if (!BIT(data, 2))
	{
		m_keylatch++;

		if (m_keylatch == 16)
		{
			m_keylatch = 0;
		}

		m_keydata = m_key_row[m_keylatch]->read();

		output_set_digit_value(m_keylatch, m_segment_data[m_keylatch]);
	}
}

READ8_MEMBER( newbrain_state::cop_in_r )
{
	/*

	    bit     description

	    IN0     K8
	    IN1     _RD
	    IN2     _COP
	    IN3     _WR

	*/

	return (m_cop_wr << 3) | (m_cop_access << 2) | (m_cop_rd << 1) | BIT(m_keydata, 2);
}

WRITE8_MEMBER( newbrain_state::cop_so_w )
{
	// connected to K1
	m_cop_so = data;
}

WRITE8_MEMBER( newbrain_state::cop_sk_w )
{
	// connected to K2
	m_segment_data[m_keylatch] >>= 1;
	m_segment_data[m_keylatch] = (m_cop_so << 15) | (m_segment_data[m_keylatch] & 0x7fff);
}

READ8_MEMBER( newbrain_state::cop_si_r )
{
	// connected to TDI
	m_cop_tdi = (((m_cassette1)->input() > +1.0) || ((m_cassette2)->input() > +1.0)) ^ m_cop_tdo;

	return m_cop_tdi;
}

/* Video */

void newbrain_state::tvram_w(UINT8 data, int a6)
{
	/* latch video address counter bits A5-A0 */
	m_tvram = (m_tvctl & NEWBRAIN_VIDEO_80L) ? 0x04 : 0x02;

	/* latch video address counter bit A6 */
	m_tvram |= a6 << 6;

	/* latch data to video address counter bits A14-A7 */
	m_tvram = (data << 7);
}

READ8_MEMBER( newbrain_state::tvl_r )
{
	UINT8 data = 0xff;

	tvram_w(data, !offset);

	return data;
}

WRITE8_MEMBER( newbrain_state::tvl_w )
{
	tvram_w(data, !offset);
}

WRITE8_MEMBER( newbrain_state::tvctl_w )
{
	/*

	    bit     signal      description

	    0       RV          1 reverses video over entire field, ie. black on white
	    1       FS          0 generates 128 characters and 128 reverse field characters from 8 bit character code. 1 generates 256 characters from 8 bit character code
	    2       32/_40      0 generates 320 or 640 horizontal dots in pixel graphics mode. 1 generates 256 or 512 horizontal dots in pixel graphics mode
	    3       UCR         0 selects 256 characters expressed in an 8x10 matrix, and 25 lines (max) displayed. 1 selects 256 characters in an 8x8 matrix, and 31 lines (max) displayed
	    4
	    5
	    6       80L         0 selects 40 character line length. 1 selects 80 character line length
	    7

	*/

	m_tvctl = data;
}

/* Disc Controller */

WRITE8_MEMBER( newbrain_eim_state::fdc_auxiliary_w )
{
	/*

	    bit     description

	    0       MOTON
	    1       765 RESET
	    2       TC
	    3
	    4
	    5       PA15
	    6
	    7

	*/

	m_floppy->mon_w(!BIT(data, 0));

	if(BIT(data, 1))
		m_fdc->reset();

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

READ8_MEMBER( newbrain_eim_state::fdc_control_r )
{
	/*

	    bit     description

	    0
	    1
	    2
	    3
	    4
	    5       FDC INT
	    6       PAGING
	    7       FDC ATT

	*/

	return (m_fdc_att << 7) | (m_paging << 6) | (m_fdc_int << 5);
}

READ8_MEMBER( newbrain_eim_state::ust2_r )
{
	/*

	    bit     description

	    0       RDDK (V24 RxD)
	    1       _CTSD (V24 Clear to Send)
	    2
	    3
	    4
	    5       TPIN
	    6
	    7       _CTSP (Printer Clear to Send)

	*/

	return 0;
}

#define NEWBRAIN_COPCMD_NULLCOM     0xd0
#define NEWBRAIN_COPCMD_DISPCOM     0xa0
//#define NEWBRAIN_COPCMD_TIMCOM        0x
#define NEWBRAIN_COPCMD_PDNCOM      0xb8
#define NEWBRAIN_COPCMD_TAPECOM     0x80

#define NEWBRAIN_COP_TAPE_RECORD    0x00
#define NEWBRAIN_COP_TAPE_PLAYBK    0x04
#define NEWBRAIN_COP_TAPE_MOTOR1    0x08
#define NEWBRAIN_COP_TAPE_MOTOR2    0x02

#define NEWBRAIN_COP_TIMER0         0x01

#define NEWBRAIN_COP_NO_DATA        0x01
#define NEWBRAIN_COP_BREAK_PRESSED  0x02

#define NEWBRAIN_COP_REGINT         0x00
/*#define NEWBRAIN_COP_CASSERR      0x
#define NEWBRAIN_COP_CASSIN         0x
#define NEWBRAIN_COP_KBD            0x
#define NEWBRAIN_COP_CASSOUT        0x*/

enum
{
	NEWBRAIN_COP_STATE_COMMAND = 0,
	NEWBRAIN_COP_STATE_DATA
};


READ8_MEMBER( newbrain_state::cop_r )
{
	m_copint = 1;
	check_interrupt();

	return m_copdata;
}

WRITE8_MEMBER( newbrain_state::cop_w )
{
	m_copdata = data;

	switch (m_copstate)
	{
	case NEWBRAIN_COP_STATE_COMMAND:
		logerror("COP command %02x\n", data);

		switch (data)
		{
		case NEWBRAIN_COPCMD_NULLCOM:
			break;

		case NEWBRAIN_COPCMD_DISPCOM:
			m_copregint = 0;
			m_copbytes = 18;
			m_copstate = NEWBRAIN_COP_STATE_DATA;

			m_copdata = NEWBRAIN_COP_NO_DATA;
			m_copint = 0;
			check_interrupt();

			break;

#if 0
		case NEWBRAIN_COPCMD_TIMCOM:
			m_copregint = 0;
			m_copbytes = 6;
			m_copstate = NEWBRAIN_COP_STATE_DATA;
			break;
#endif
		case NEWBRAIN_COPCMD_PDNCOM:
			/* power down */
			m_copregint = 0;
			break;

		default:
			if (data & NEWBRAIN_COPCMD_TAPECOM)
			{
				m_copregint = 0;
			}
		}
		break;

	case NEWBRAIN_COP_STATE_DATA:
		logerror("COP data %02x\n", data);
		m_copbytes--;

		if (m_copbytes == 0)
		{
			m_copstate = NEWBRAIN_COP_STATE_COMMAND;
			m_copregint = 1;
		}

		m_copdata = NEWBRAIN_COP_NO_DATA;
		m_copint = 0;
		check_interrupt();

		break;
	}
}

TIMER_DEVICE_CALLBACK_MEMBER(newbrain_state::cop_regint_tick)
{
	if (m_copregint)
	{
		logerror("COP REGINT\n");
		m_copint = 0;
		check_interrupt();
	}
}

/* Expansion Interface Module */

WRITE8_MEMBER( newbrain_eim_state::enrg2_w )
{
	/*

	    bit     signal      description

	    0       _USERP      0 enables user data bus interrupt and also parallel latched data output (or centronics printer) interrupt
	    1       ANP         1 enables ADC conversion complete interrupt and also calling indicator interrupt
	    2       MLTMD       1 enables serial receive clock into multiplier input of DAC and signals data terminal not ready
	    3       MSPD        1 enables 50K Baud serial data rate to be obtained ie. CTC input clock of 800 kHz. 0 selects xxx.692 kHz
	    4       ENOR        1 enables serial receive clock to sound output summer, and also selects serial input from the printer port. 0 selects serial input from the comms port
	    5       ANSW        1 enables second bank of 4 analogue inputs (voltage, non-ratiometric), ie. ch4-7, and enabled sound output, 0 selects ch03
	    6       ENOT        1 enables serial transmit clock to sound ouput summer, and also selects serial output to the printer port. 0 selects serial output to the comms port
	    7                   9th output bit for centronics printer port

	*/

	m_enrg2 = data;
}

WRITE8_MEMBER( newbrain_eim_state::pr_w )
{
	/*

	    bit     signal      description

	    0       HP0
	    1       HP1
	    2       HP2
	    3       HP3
	    4       HP4
	    5       HP5
	    6       HP6
	    7       HP11

	    HP0-HP2 are decoded to _ROM0..._ROM7 signals
	    HP3-HP4 are decoded to _CH0..._CH3 signals

	*/

	int page = (BIT(offset, 12) >> 9) | (BIT(offset, 15) >> 13) | (BIT(offset, 14) >> 13) | (BIT(offset, 13) >> 13);
	int bank = (BIT(offset, 11) >> 3) | (data & 0x7f);

	m_pr[page] = bank;

	bankswitch();
}

READ8_MEMBER( newbrain_eim_state::user_r )
{
	m_user = 0xff;

	return 0xff;
}

WRITE8_MEMBER( newbrain_eim_state::user_w )
{
	m_user = data;
}

READ8_MEMBER( newbrain_eim_state::anout_r )
{
	return 0xff;
}

WRITE8_MEMBER( newbrain_eim_state::anout_w )
{
}

READ8_MEMBER( newbrain_eim_state::anin_r )
{
//  int channel = offset & 0x03;

	return 0;
}

WRITE8_MEMBER( newbrain_eim_state::anio_w )
{
//  int channel = offset & 0x03;
}

READ8_MEMBER( newbrain_eim_state::st0_r )
{
	/*

	    bit     signal      description

	    0                   fixed at 1 - indicates excess of 24 or 48, obsolete
	    1       PWRUP       1 indicates power up from 'cold' - necessary in battery machines with power switching
	    2                   1 indicates analogue or calling indicator interrupts
	    3       _USRINT0    0 indicates centronics printer (latched output data) port interrupt
	    4       _USRINT     0 indicates parallel data bus port interrupt
	    5       _CLKINT     0 indicates frame frequency clock interrupt
	    6       _ACINT      0 indicates ACIA interrupt
	    7       _COPINT     0 indicates interrupt from micro-controller COP420M

	*/

	return (m_copint << 7) | (m_aciaint << 6) | (m_clkint << 5) | (m_userint << 4) | (m_userint0 << 3) | (m_pwrup) << 1 | 0x01;
}

READ8_MEMBER( newbrain_eim_state::st1_r )
{
	/*

	    bit     signal      description

	    0
	    1
	    2       N/_RV       1 selects normal video on power up (white on black), 0 selects reversed video (appears as D0 on the first 200 EI's)
	    3       ANCH        1 indicates power is being taken from the mains supply
	    4       40/_80      1 indicates that 40 column video is selected on power up. 0 selects 80 column video
	    5
	    6       TVCNSL      1 indicates that a video display is required on power up
	    7

	*/

	return (m_tvcnsl << 6) | 0x10 | 0x08 | 0x04;
}

READ8_MEMBER( newbrain_eim_state::st2_r )
{
	/*

	    bit     signal      description

	    0                   received serial data from communications port
	    1                   0 indicates 'clear-to-send' condition at communications port
	    2
	    3
	    4
	    5                   logic level tape input
	    6
	    7                   0 indicates 'clear-to-send' condition at printer port

	*/

	return 0;
}

READ8_MEMBER( newbrain_eim_state::usbs_r )
{
	return 0xff;
}

WRITE8_MEMBER( newbrain_eim_state::usbs_w )
{
}

WRITE8_MEMBER( newbrain_eim_state::paging_w )
{
	if (BIT(offset, 8))
	{
		// expansion interface module

		/*

		    bit     signal      description

		    0       PG          1 enables paging circuits
		    1       WPL         unused
		    2       A16         1 sets local A16 to 1 (ie. causes second set of 8 page registers to select addressed memory)
		    3       _MPM        0 selects multi-processing mode. among other effects this extends the page registers from 8 to 12 bits in length
		    4       HISLT       1 isolates the local machine. this is used in multi-processing mode
		    5
		    6
		    7

		*/

		m_paging = BIT(data, 0);
		m_a16 = BIT(data, 2);
		m_mpm = BIT(data, 3);
	}
	else if (BIT(offset, 9))
	{
		// disc controller

		/*

		    bit     signal      description

		    0       PAGING      1 enables paging circuits
		    1
		    2       HA16        1 sets local A16 to 1 (ie. causes second set of 8 page registers to select addressed memory)
		    3       MPM         0 selects multi-processing mode. among other effects this extends the page registers from 8 to 12 bits in length
		    4
		    5       _FDC RESET
		    6
		    7       FDC ATT

		*/

		m_fdccpu->set_input_line(INPUT_LINE_RESET, BIT(data, 5) ? HOLD_LINE : CLEAR_LINE);

		m_paging = BIT(data, 0);
		m_a16 = BIT(data, 2);
		m_mpm = BIT(data, 3);
		m_fdc_att = BIT(data, 7);
	}
	else if (BIT(offset, 10))
	{
		// network controller
	}
}

/* A/D Converter */

WRITE_LINE_MEMBER( newbrain_eim_state::adc_eoc_w )
{
	m_anint = state;
}

ADC0808_ANALOG_READ_CB( newbrain_eim_state::adc_vref_pos_r )
{
	return 5.0;
}

ADC0808_ANALOG_READ_CB( newbrain_eim_state::adc_vref_neg_r )
{
	return 0.0;
}

ADC0808_ANALOG_READ_CB( newbrain_eim_state::adc_input_r )
{
	return 0.0;
}

/* Memory Maps */

static ADDRESS_MAP_START( newbrain_map, AS_PROGRAM, 8, newbrain_state )
	ADDRESS_MAP_UNMAP_HIGH
	AM_RANGE(0x0000, 0x1fff) AM_RAMBANK("bank1")
	AM_RANGE(0x2000, 0x3fff) AM_RAMBANK("bank2")
	AM_RANGE(0x4000, 0x5fff) AM_RAMBANK("bank3")
	AM_RANGE(0x6000, 0x7fff) AM_RAMBANK("bank4")
	AM_RANGE(0x8000, 0x9fff) AM_RAMBANK("bank5")
	AM_RANGE(0xa000, 0xbfff) AM_RAMBANK("bank6")
	AM_RANGE(0xc000, 0xdfff) AM_RAMBANK("bank7")
	AM_RANGE(0xe000, 0xffff) AM_RAMBANK("bank8")
ADDRESS_MAP_END

static ADDRESS_MAP_START( newbrain_ei_io_map, AS_IO, 8, newbrain_eim_state )
	ADDRESS_MAP_UNMAP_HIGH
	AM_RANGE(0x00, 0x00) AM_MIRROR(0xff00) AM_READWRITE(clusr_r, clusr_w)
	AM_RANGE(0x01, 0x01) AM_MIRROR(0xff00) AM_WRITE(enrg2_w)
	AM_RANGE(0x02, 0x02) AM_MIRROR(0xff00) AM_MASK(0xff00) AM_WRITE(pr_w)
	AM_RANGE(0x03, 0x03) AM_MIRROR(0xff00) AM_READWRITE(user_r, user_w)
	AM_RANGE(0x04, 0x04) AM_MIRROR(0xff00) AM_READWRITE(clclk_r, clclk_w)
	AM_RANGE(0x05, 0x05) AM_MIRROR(0xff00) AM_READWRITE(anout_r, anout_w)
	AM_RANGE(0x06, 0x06) AM_MIRROR(0xff00) AM_READWRITE(cop_r, cop_w)
	AM_RANGE(0x07, 0x07) AM_MIRROR(0xff00) AM_WRITE(enrg1_w)
	AM_RANGE(0x08, 0x09) AM_MIRROR(0xff02) AM_READWRITE(tvl_r, tvl_w)
	AM_RANGE(0x0c, 0x0c) AM_MIRROR(0xff03) AM_WRITE(tvctl_w)
	AM_RANGE(0x10, 0x13) AM_MIRROR(0xff00) AM_READWRITE(anin_r, anio_w)
	AM_RANGE(0x14, 0x14) AM_MIRROR(0xff00) AM_READ(st0_r)
	AM_RANGE(0x15, 0x15) AM_MIRROR(0xff00) AM_READ(st1_r)
	AM_RANGE(0x16, 0x16) AM_MIRROR(0xff00) AM_READ(st2_r)
	AM_RANGE(0x17, 0x17) AM_MIRROR(0xff00) AM_READWRITE(usbs_r, usbs_w)
	AM_RANGE(0x18, 0x18) AM_MIRROR(0xff00) AM_DEVREADWRITE(MC6850_TAG, acia6850_device, status_r, control_w)
	AM_RANGE(0x19, 0x19) AM_MIRROR(0xff00) AM_DEVREADWRITE(MC6850_TAG, acia6850_device, data_r, data_w)
	AM_RANGE(0x1c, 0x1f) AM_MIRROR(0xff00) AM_DEVREADWRITE(Z80CTC_TAG, z80ctc_device, read, write)
	AM_RANGE(0xff, 0xff) AM_MIRROR(0xff00) AM_MASK(0xff00) AM_WRITE(paging_w)
ADDRESS_MAP_END

static ADDRESS_MAP_START( newbrain_a_io_map, AS_IO, 8, newbrain_state )
	ADDRESS_MAP_UNMAP_HIGH
	AM_RANGE(0x00, 0x00) AM_MIRROR(0xffc0) AM_READWRITE(clusr_r, clusr_w)
	AM_RANGE(0x03, 0x03) AM_MIRROR(0xffc0) AM_WRITE(user_w)
	AM_RANGE(0x04, 0x04) AM_MIRROR(0xffc0) AM_READWRITE(clclk_r, clclk_w)
	AM_RANGE(0x06, 0x06) AM_MIRROR(0xffc0) AM_READWRITE(cop_r, cop_w)
	AM_RANGE(0x07, 0x07) AM_MIRROR(0xffc0) AM_WRITE(a_enrg1_w)
	AM_RANGE(0x08, 0x09) AM_MIRROR(0xffc2) AM_READWRITE(tvl_r, tvl_w)
	AM_RANGE(0x0c, 0x0c) AM_MIRROR(0xffc3) AM_WRITE(tvctl_w)
	AM_RANGE(0x14, 0x14) AM_MIRROR(0xffc3) AM_READ(a_ust_r)
	AM_RANGE(0x16, 0x16) AM_MIRROR(0xffc0) AM_READ(user_r)
ADDRESS_MAP_END

static ADDRESS_MAP_START( newbrain_cop_io_map, AS_IO, 8, newbrain_state )
	AM_RANGE(COP400_PORT_L, COP400_PORT_L) AM_READWRITE(cop_l_r, cop_l_w)
	AM_RANGE(COP400_PORT_G, COP400_PORT_G) AM_READWRITE(cop_g_r, cop_g_w)
	AM_RANGE(COP400_PORT_D, COP400_PORT_D) AM_WRITE(cop_d_w)
	AM_RANGE(COP400_PORT_IN, COP400_PORT_IN) AM_READ(cop_in_r)
	AM_RANGE(COP400_PORT_SK, COP400_PORT_SK) AM_WRITE(cop_sk_w)
	AM_RANGE(COP400_PORT_SIO, COP400_PORT_SIO) AM_READWRITE(cop_si_r, cop_so_w)
	AM_RANGE(COP400_PORT_CKO, COP400_PORT_CKO) AM_READNOP
ADDRESS_MAP_END

static ADDRESS_MAP_START( newbrain_fdc_map, AS_PROGRAM, 8, newbrain_eim_state )
	ADDRESS_MAP_UNMAP_HIGH
	AM_RANGE(0x0000, 0x1fff) AM_ROM
ADDRESS_MAP_END

static ADDRESS_MAP_START( newbrain_fdc_io_map, AS_IO, 8, newbrain_eim_state )
	ADDRESS_MAP_UNMAP_HIGH
	ADDRESS_MAP_GLOBAL_MASK(0xff)
	AM_RANGE(0x00, 0x01) AM_DEVICE(UPD765_TAG, upd765a_device, map)
	AM_RANGE(0x20, 0x20) AM_WRITE(fdc_auxiliary_w)
	AM_RANGE(0x40, 0x40) AM_READ(fdc_control_r)
ADDRESS_MAP_END

/* Input Ports */

static INPUT_PORTS_START( newbrain )
	PORT_START("Y0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("STOP") PORT_CODE(KEYCODE_END) PORT_CHAR(UCHAR_MAMEKEY(END))
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("Y1")
	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_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( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME(UTF8_UP) PORT_CODE(KEYCODE_UP) PORT_CHAR(UCHAR_MAMEKEY(UP))

	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_8) PORT_CHAR('8')
	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_J) PORT_CHAR('j') PORT_CHAR('J')

	PORT_START("Y3")
	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_9) PORT_CHAR('9')
	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_N) PORT_CHAR('n') PORT_CHAR('N')

	PORT_START("Y4")
	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_0) PORT_CHAR('0')
	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_M) PORT_CHAR('m') PORT_CHAR('M')

	PORT_START("Y5")
	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_MINUS) PORT_CHAR('(') 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_COMMA) PORT_CHAR(',') PORT_CHAR('<')

	PORT_START("Y6")
	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_EQUALS) PORT_CHAR(')') 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_STOP) PORT_CHAR('.') PORT_CHAR('>')

	PORT_START("Y7")
	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("* \xC2\xA3") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CHAR('*') PORT_CHAR(0x00A3)
	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_B) PORT_CHAR('b') PORT_CHAR('B')

	PORT_START("Y8")
	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("VIDEO TEXT") PORT_CODE(KEYCODE_RALT) PORT_CHAR(UCHAR_MAMEKEY(RALT)) // Vd
	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_V) PORT_CHAR('v') PORT_CHAR('V')

	PORT_START("Y9")
	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_P) PORT_CHAR('p') PORT_CHAR('P')
	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_C) PORT_CHAR('c') PORT_CHAR('C')

	PORT_START("Y10")
	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_EQUALS) 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_X) PORT_CHAR('x') PORT_CHAR('X')

	PORT_START("Y11")
	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_OPENBRACE) 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_Z) PORT_CHAR('z') PORT_CHAR('Z')

	PORT_START("Y12")
	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_QUOTE) PORT_CHAR('+') 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_NAME("INSERT") PORT_CODE(KEYCODE_INSERT) PORT_CHAR(UCHAR_MAMEKEY(INSERT))

	PORT_START("Y13")
	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_NAME("NEW LINE") PORT_CODE(KEYCODE_ENTER) PORT_CHAR(13) // NL
	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_K) PORT_CHAR('k') PORT_CHAR('K')

	PORT_START("Y14")
	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_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("HOME") PORT_CODE(KEYCODE_HOME) PORT_CHAR(UCHAR_MAMEKEY(HOME)) // CH

	PORT_START("Y15")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) // SH
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("GRAPHICS") PORT_CODE(KEYCODE_LALT) PORT_CHAR(UCHAR_MAMEKEY(LALT)) // GR
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("REPEAT") // RPT
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYBOARD ) PORT_NAME("CONTROL") PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) // GL
INPUT_PORTS_END

/* Machine Initialization */

WRITE_LINE_MEMBER( newbrain_eim_state::acia_tx )
{
	m_acia_txd = state;
}

WRITE_LINE_MEMBER( newbrain_eim_state::acia_interrupt )
{
	m_aciaint = state;
}

WRITE_LINE_MEMBER( newbrain_eim_state::fdc_interrupt )
{
	m_fdc_int = state;
}

WRITE_LINE_MEMBER( newbrain_eim_state::ctc_z2_w )
{
	/* connected to CTC channel 0/1 clock inputs */
	m_ctc->trg0(state);
	m_ctc->trg1(state);
}

TIMER_DEVICE_CALLBACK_MEMBER(newbrain_eim_state::ctc_c2_tick)
{
	m_ctc->trg2(1);
	m_ctc->trg2(0);
}

inline int newbrain_state::get_reset_t()
{
	return RES_K(220) * CAP_U(10) * 1000; // t = R128 * C125 = 2.2s
}

inline int newbrain_state::get_pwrup_t()
{
	return RES_K(560) * CAP_U(10) * 1000; // t = R129 * C127 = 5.6s
}

//-------------------------------------------------
//  device_timer - handler timer events
//-------------------------------------------------

void newbrain_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
	case TIMER_ID_RESET:
		m_maincpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		m_copcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
		break;

	case TIMER_ID_PWRUP:
		m_pwrup = 0;
		bankswitch();
		break;
	}
}

void newbrain_state::machine_start()
{
	m_copregint = 1;

	/* set power up timer */
	timer_set(attotime::from_usec(get_pwrup_t()), TIMER_ID_PWRUP);

	/* initialize variables */
	m_pwrup = 1;
	m_userint = 1;
	m_userint0 = 1;
	m_clkint = 1;
	m_aciaint = 1;
	m_copint = 1;
	m_bee = 1;
	m_tvcnsl = 1;

	/* set up memory banking */
	bankswitch();

	// find keyboard rows
	m_key_row[0] = m_y0;
	m_key_row[1] = m_y1;
	m_key_row[2] = m_y2;
	m_key_row[3] = m_y3;
	m_key_row[4] = m_y4;
	m_key_row[5] = m_y5;
	m_key_row[6] = m_y6;
	m_key_row[7] = m_y7;
	m_key_row[8] = m_y8;
	m_key_row[9] = m_y9;
	m_key_row[10] = m_y10;
	m_key_row[11] = m_y11;
	m_key_row[12] = m_y12;
	m_key_row[13] = m_y13;
	m_key_row[14] = m_y14;
	m_key_row[15] = m_y15;

	/* register for state saving */
	save_item(NAME(m_pwrup));
	save_item(NAME(m_userint));
	save_item(NAME(m_userint0));
	save_item(NAME(m_clkint));
	save_item(NAME(m_aciaint));
	save_item(NAME(m_copint));
	save_item(NAME(m_anint));
	save_item(NAME(m_bee));
	save_item(NAME(m_enrg1));
	save_item(NAME(m_enrg2));
	save_item(NAME(m_cop_bus));
	save_item(NAME(m_cop_so));
	save_item(NAME(m_cop_tdo));
	save_item(NAME(m_cop_tdi));
	save_item(NAME(m_cop_rd));
	save_item(NAME(m_cop_wr));
	save_item(NAME(m_cop_access));
	save_item(NAME(m_keylatch));
	save_item(NAME(m_keydata));
	save_item(NAME(m_user));
}

void newbrain_eim_state::machine_start()
{
	newbrain_state::machine_start();

	/* allocate expansion RAM */
	m_eim_ram.allocate(NEWBRAIN_EIM_RAM_SIZE);

	/* register for state saving */
	save_item(NAME(m_mpm));
	save_item(NAME(m_a16));
	save_item(NAME(m_pr));
	save_item(NAME(m_fdc_int));
	save_item(NAME(m_fdc_att));
	save_item(NAME(m_paging));
}

void newbrain_state::machine_reset()
{
	timer_set(attotime::from_usec(get_reset_t()), TIMER_ID_RESET);
}

INTERRUPT_GEN_MEMBER(newbrain_state::newbrain_interrupt)
{
	if (!(m_enrg1 & NEWBRAIN_ENRG1_CLK))
	{
		m_clkint = 0;
		check_interrupt();
	}
}

/* Machine Drivers */

/* F4 Character Displayer */
static const gfx_layout newbrain_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*256*8, 1*256*8, 2*256*8, 3*256*8, 4*256*8, 5*256*8, 6*256*8, 7*256*8, 8*256*8, 9*256*8, 10*256*8, 11*256*8, 12*256*8, 13*256*8, 14*256*8, 15*256*8 },
	8                   /* every char takes 16 x 1 bytes */
};

static GFXDECODE_START( newbrain )
	GFXDECODE_ENTRY( "chargen", 0x0000, newbrain_charlayout, 0, 1 )
GFXDECODE_END

static MACHINE_CONFIG_START( newbrain_a, newbrain_state )
	// basic system hardware
	MCFG_CPU_ADD(Z80_TAG, Z80, XTAL_16MHz/8)
	MCFG_CPU_PROGRAM_MAP(newbrain_map)
	MCFG_CPU_IO_MAP(newbrain_a_io_map)
	MCFG_CPU_VBLANK_INT_DRIVER(SCREEN_TAG, newbrain_state,  newbrain_interrupt)

	MCFG_CPU_ADD(COP420_TAG, COP420, XTAL_16MHz/8) // COP420-GUW/N
	MCFG_CPU_IO_MAP(newbrain_cop_io_map)
	MCFG_COP400_CONFIG( COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, COP400_MICROBUS_ENABLED )

	MCFG_GFXDECODE_ADD("gfxdecode", "palette", newbrain)

	MCFG_TIMER_DRIVER_ADD_PERIODIC("cop_regint", newbrain_state, cop_regint_tick, attotime::from_usec(12500))

	// video hardware
	MCFG_FRAGMENT_ADD(newbrain_video)

	// devices
	MCFG_CASSETTE_ADD("cassette")
	MCFG_CASSETTE_DEFAULT_STATE(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_MUTED)

	MCFG_CASSETTE_ADD("cassette2")
	MCFG_CASSETTE_DEFAULT_STATE(CASSETTE_STOPPED | CASSETTE_MOTOR_ENABLED | CASSETTE_SPEAKER_MUTED)

	// internal ram
	MCFG_RAM_ADD(RAM_TAG)
	MCFG_RAM_DEFAULT_SIZE("32K")
MACHINE_CONFIG_END

static SLOT_INTERFACE_START( newbrain_floppies )
	SLOT_INTERFACE( "525dd", FLOPPY_525_DD )
SLOT_INTERFACE_END

static MACHINE_CONFIG_DERIVED_CLASS( newbrain_eim, newbrain_a, newbrain_eim_state )
	// basic system hardware
	MCFG_CPU_MODIFY(Z80_TAG)
	MCFG_CPU_IO_MAP(newbrain_ei_io_map)

	MCFG_CPU_ADD(FDC_Z80_TAG, Z80, XTAL_4MHz)
	MCFG_CPU_PROGRAM_MAP(newbrain_fdc_map)
	MCFG_CPU_IO_MAP(newbrain_fdc_io_map)

	// devices
	MCFG_DEVICE_ADD(Z80CTC_TAG, Z80CTC, XTAL_16MHz/8)
	MCFG_Z80CTC_ZC0_CB(DEVWRITELINE(MC6850_TAG, acia6850_device, write_rxc))
	MCFG_Z80CTC_ZC1_CB(DEVWRITELINE(MC6850_TAG, acia6850_device, write_txc))
	MCFG_Z80CTC_ZC2_CB(WRITELINE(newbrain_eim_state, ctc_z2_w))

	MCFG_TIMER_DRIVER_ADD_PERIODIC("z80ctc_c2", newbrain_eim_state, ctc_c2_tick, attotime::from_hz(XTAL_16MHz/4/13))
	MCFG_DEVICE_ADD(ADC0809_TAG, ADC0808, 500000)
	MCFG_ADC0808_OUT_EOC_CB(WRITELINE(newbrain_eim_state, adc_eoc_w))
	MCFG_ADC0808_IN_VREF_POS_CB(newbrain_eim_state, adc_vref_pos_r)
	MCFG_ADC0808_IN_VREF_NEG_CB(newbrain_eim_state, adc_vref_neg_r)
	MCFG_ADC0808_IN_IN_0_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_1_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_2_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_3_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_4_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_5_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_6_CB(newbrain_eim_state, adc_input_r)
	MCFG_ADC0808_IN_IN_7_CB(newbrain_eim_state, adc_input_r)

	MCFG_DEVICE_ADD(MC6850_TAG, ACIA6850, 0)
	MCFG_ACIA6850_TXD_HANDLER(WRITELINE(newbrain_eim_state, acia_tx))
	MCFG_ACIA6850_IRQ_HANDLER(WRITELINE(newbrain_eim_state, acia_interrupt))

	MCFG_UPD765A_ADD(UPD765_TAG, false, true)
	MCFG_FLOPPY_DRIVE_ADD(UPD765_TAG ":0", newbrain_floppies, "525dd", floppy_image_device::default_floppy_formats)
	MCFG_FLOPPY_DRIVE_ADD(UPD765_TAG ":1", newbrain_floppies, "525dd", floppy_image_device::default_floppy_formats)

	// internal ram
	MCFG_RAM_MODIFY(RAM_TAG)
	MCFG_RAM_DEFAULT_SIZE("96K")
MACHINE_CONFIG_END

/* ROMs */

ROM_START( newbrain )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "rom20" )

	ROM_SYSTEM_BIOS( 0, "issue1", "Issue 1 (v?)" )
	ROMX_LOAD( "aben.ic6",     0xa000, 0x2000, CRC(308f1f72) SHA1(a6fd9945a3dca47636887da2125fde3f9b1d4e25), ROM_BIOS(1) )
	ROMX_LOAD( "cd iss 1.ic7", 0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(1) )
	ROMX_LOAD( "ef iss 1.ic8", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 1, "issue2", "Issue 2 (v1.9)" )
	ROMX_LOAD( "aben19.ic6",   0xa000, 0x2000, CRC(d0283eb1) SHA1(351d248e69a77fa552c2584049006911fb381ff0), ROM_BIOS(2) )
	ROMX_LOAD( "cdi2.ic7",     0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(2) )
	ROMX_LOAD( "ef iss 1.ic8", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 2, "issue3", "Issue 3 (v1.91)" )
	ROMX_LOAD( "aben191.ic6",  0xa000, 0x2000, CRC(b7be8d89) SHA1(cce8d0ae7aa40245907ea38b7956c62d039d45b7), ROM_BIOS(3) )
	ROMX_LOAD( "cdi3.ic7",     0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(3) )
	ROMX_LOAD( "ef iss 1.ic8", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 3, "series2", "Series 2 (v?)" )
	ROMX_LOAD( "abs2.ic6",     0xa000, 0x2000, CRC(9a042acb) SHA1(80d83a2ea3089504aa68b6cf978d80d296cd9bda), ROM_BIOS(4) )
	ROMX_LOAD( "cds2.ic7",     0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(4) )
	ROMX_LOAD( "efs2.ic8",     0xe000, 0x2000, CRC(b222d798) SHA1(c0c816b4d4135b762f2c5f1b24209d0096f22e56), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS( 4, "rom20", "? (v2.0)" )
	ROMX_LOAD( "aben20.rom",   0xa000, 0x2000, CRC(3d76d0c8) SHA1(753b4530a518ad832e4b81c4e5430355ba3f62e0), ROM_BIOS(5) )
	ROMX_LOAD( "cd20tci.rom",  0xc000, 0x4000, CRC(f65b2350) SHA1(1ada7fbf207809537ec1ffb69808524300622ada), ROM_BIOS(5) )

	ROM_REGION( 0x400, COP420_TAG, 0 )
	ROM_LOAD( "cop420.419", 0x000, 0x400, NO_DUMP )

	ROM_REGION( 0x1000, "chargen", ROMREGION_ERASE00 )
	ROM_LOAD( "char eprom iss 1.ic453", 0x0000, 0x0a01, BAD_DUMP CRC(46ecbc65) SHA1(3fe064d49a4de5e3b7383752e98ad35a674e26dd) ) // 8248R7
ROM_END

#define rom_newbraina rom_newbrain

ROM_START( newbraineim )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_DEFAULT_BIOS( "rom20" )

	ROM_SYSTEM_BIOS( 0, "issue1", "Issue 1 (v?)" )
	ROMX_LOAD( "aben.ic6",     0xa000, 0x2000, CRC(308f1f72) SHA1(a6fd9945a3dca47636887da2125fde3f9b1d4e25), ROM_BIOS(1) )
	ROMX_LOAD( "cd iss 1.ic7", 0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(1) )
	ROMX_LOAD( "ef iss 1.ic8", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(1) )

	ROM_SYSTEM_BIOS( 1, "issue2", "Issue 2 (v1.9)" )
	ROMX_LOAD( "aben19.ic6",   0xa000, 0x2000, CRC(d0283eb1) SHA1(351d248e69a77fa552c2584049006911fb381ff0), ROM_BIOS(2) )
	ROMX_LOAD( "cdi2.ic7",     0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(2) )
	ROMX_LOAD( "ef iss 1.ic8", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(2) )

	ROM_SYSTEM_BIOS( 2, "issue3", "Issue 3 (v1.91)" )
	ROMX_LOAD( "aben191.ic6",  0xa000, 0x2000, CRC(b7be8d89) SHA1(cce8d0ae7aa40245907ea38b7956c62d039d45b7), ROM_BIOS(3) )
	ROMX_LOAD( "cdi3.ic7",     0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(3) )
	ROMX_LOAD( "ef iss 1.ic8", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6), ROM_BIOS(3) )

	ROM_SYSTEM_BIOS( 3, "series2", "Series 2 (v?)" )
	ROMX_LOAD( "abs2.ic6",     0xa000, 0x2000, CRC(9a042acb) SHA1(80d83a2ea3089504aa68b6cf978d80d296cd9bda), ROM_BIOS(4) )
	ROMX_LOAD( "cds2.ic7",     0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a), ROM_BIOS(4) )
	ROMX_LOAD( "efs2.ic8",     0xe000, 0x2000, CRC(b222d798) SHA1(c0c816b4d4135b762f2c5f1b24209d0096f22e56), ROM_BIOS(4) )

	ROM_SYSTEM_BIOS( 4, "rom20", "? (v2.0)" )
	ROMX_LOAD( "aben20.rom",   0xa000, 0x2000, CRC(3d76d0c8) SHA1(753b4530a518ad832e4b81c4e5430355ba3f62e0), ROM_BIOS(5) )
	ROMX_LOAD( "cd20tci.rom",  0xc000, 0x4000, CRC(f65b2350) SHA1(1ada7fbf207809537ec1ffb69808524300622ada), ROM_BIOS(5) )

	ROM_REGION( 0x400, COP420_TAG, 0 )
	ROM_LOAD( "cop420.419",   0x000, 0x400, NO_DUMP )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "char eprom iss 1.ic453", 0x0000, 0x0a01, BAD_DUMP CRC(46ecbc65) SHA1(3fe064d49a4de5e3b7383752e98ad35a674e26dd) ) // 8248R7

	ROM_REGION( 0x10000, "eim", 0 ) // Expansion Interface Module
	ROM_LOAD( "e415-2.rom", 0x4000, 0x2000, CRC(5b0e390c) SHA1(0f99cae57af2e64f3f6b02e5325138d6ba015e72) )
	ROM_LOAD( "e415-3.rom", 0x4000, 0x2000, CRC(2f88bae5) SHA1(04e03f230f4b368027442a7c2084dae877f53713) ) // 18/8/83.aci
	ROM_LOAD( "e416-3.rom", 0x6000, 0x2000, CRC(8b5099d8) SHA1(19b0cfce4c8b220eb1648b467f94113bafcb14e0) ) // 10/8/83.mtv
	ROM_LOAD( "e417-2.rom", 0x8000, 0x2000, CRC(6a7afa20) SHA1(f90db4f8318777313a862b3d5bab83c2fd260010) )

	ROM_REGION( 0x10000, FDC_Z80_TAG, 0 ) // Floppy Disk Controller
	ROM_LOAD( "d413-2.rom", 0x0000, 0x2000, CRC(097591f1) SHA1(c2aa1d27d4f3a24ab0c8135df746a4a44201a7f4) )
	ROM_LOAD( "d417-1.rom", 0x0000, 0x2000, CRC(40fad31c) SHA1(5137be4cc026972c0ffd4fa6990e8583bdfce163) )
	ROM_LOAD( "d417-2.rom", 0x0000, 0x2000, CRC(e8bda8b9) SHA1(c85a76a5ff7054f4ef4a472ce99ebaed1abd269c) )
ROM_END

ROM_START( newbrainmd )
	ROM_REGION( 0x10000, Z80_TAG, 0 )
	ROM_LOAD( "cdmd.rom", 0xc000, 0x2000, CRC(6b4d9429) SHA1(ef688be4e75aced61f487c928258c8932a0ae00a) )
	ROM_LOAD( "efmd.rom", 0xe000, 0x2000, CRC(20dd0b49) SHA1(74b517ca223cefb588e9f49e72ff2d4f1627efc6) )

	ROM_REGION( 0x400, COP420_TAG, 0 )
	ROM_LOAD( "cop420.419", 0x000, 0x400, NO_DUMP )

	ROM_REGION( 0x1000, "chargen", 0 )
	ROM_LOAD( "char eprom iss 1.ic453", 0x0000, 0x0a01, BAD_DUMP CRC(46ecbc65) SHA1(3fe064d49a4de5e3b7383752e98ad35a674e26dd) ) // 8248R7
ROM_END

/* System Drivers */

//    YEAR  NAME        PARENT      COMPAT  MACHINE         INPUT       INIT    COMPANY                         FULLNAME        FLAGS
COMP( 1981, newbrain,   0,          0,      newbrain_a,     newbrain, driver_device,   0,      "Grundy Business Systems Ltd",   "NewBrain AD",  GAME_NOT_WORKING | GAME_NO_SOUND)
COMP( 1981, newbraineim,newbrain,   0,      newbrain_eim,   newbrain, driver_device,   0,      "Grundy Business Systems Ltd",   "NewBrain AD with Expansion Interface", GAME_NOT_WORKING | GAME_NO_SOUND )
COMP( 1981, newbraina,  newbrain,   0,      newbrain_a,     newbrain, driver_device,   0,      "Grundy Business Systems Ltd",   "NewBrain A",   GAME_NOT_WORKING | GAME_NO_SOUND)
COMP( 1981, newbrainmd, newbrain,   0,      newbrain_a,     newbrain, driver_device,   0,      "Grundy Business Systems Ltd",   "NewBrain MD",  GAME_NOT_WORKING | GAME_NO_SOUND )
