/*
** mtx.c : driver for Memotech MTX512
** 
** The memory address space of the MTX512 is divided into 8 banks,
** with a size of 8KB each. These banks are mapped differently based
** upon the memory model, selected ROM and RAM page, and the amount
** of available RAM memory. There are two memory models: a ROM-based
** model and a CPM model. The memory model, ROM, and RAM page are
** selected at IO port 0 (i.e., with out (0),a)
**
** In the ROM-based model, bank 1 en bank 2 map to ROM images. Bank 1
** maps to the OSROM image; bank2 maps to either the BASICROM image
** or the ASSEMROM image, depending on the selected ROM page. The
** available RAM is allocated to the banks from top to bottom. Bank 8
** and bank 7, always start at offset 0x0 and 0x2000, respectively.
** Bank 6, 5, 4, and 3 are mapped depending on the selected RAM page
** and the available memory.
**
** In the CPM model, all banks map to RAM memory. The available RAM
** is (again) allocated from top to bottom. Bank 8 and bank 7, always
** start at offset 0x0 and 0x2000, respectively. Bank 6, 5, 4, 3, 2,
** and 1 are mapped depending on the read-only memory register and
** the avaiable memory.
*/

#include "driver.h"
#include "cpuintrf.h"
#include "cpu/z80/z80.h"
#include "cpu/z80/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "vidhrdw/generic.h"
#include "vidhrdw/tms9928a.h"
#include "sound/sn76496.h"
#include "devices/cartslot.h"
#include "devices/printer.h"


#define MTX_SYSTEM_CLOCK		4000000

#define MTX_PRT_BUSY		1
#define MTX_PRT_NOERROR		2
#define MTX_PRT_EMPTY		4
#define MTX_PRT_SELECTED	8


static unsigned char key_sense;

static unsigned char mtx_relcpmh;
static unsigned char mtx_rampage;
static unsigned char mtx_rompage;

static unsigned char * mtx_null_mem = NULL;
static unsigned char * mtx_zero_mem = NULL;

static char mtx_prt_strobe = 0;
static char mtx_prt_data = 0;

static unsigned char * mtx_loadbuffer = NULL;
static unsigned char * mtx_savebuffer = NULL;
static int mtx_loadsize;
static int mtx_saveindex;


static WRITE8_HANDLER ( mtx_cst_w )
{
}

static  READ8_HANDLER ( mtx_psg_r )
{
	return 0xff;
}

static WRITE8_HANDLER ( mtx_psg_w )
{
	SN76496_0_w(offset,data);
}

static mess_image * mtx_printer_image (void)
{
	return image_from_devtype_and_index (IO_PRINTER, 0);
}

static  READ8_HANDLER ( mtx_strobe_r )
{
	if (mtx_prt_strobe == 0)
		printer_output (mtx_printer_image (), mtx_prt_data);

	mtx_prt_strobe = 1;

	return 0;
}

static  READ8_HANDLER ( mtx_prt_r )
{
	mtx_prt_strobe = 0;

	return MTX_PRT_NOERROR | (printer_status (mtx_printer_image (), 0)
			? MTX_PRT_SELECTED : 0);
}

static  WRITE8_HANDLER ( mtx_prt_w )
{
	mtx_prt_data = data;
}

static  READ8_HANDLER ( mtx_vdp_r )
{
	if (offset & 0x01)
		return TMS9928A_register_r(0);
	else
		return TMS9928A_vram_r(0);
}

static WRITE8_HANDLER ( mtx_vdp_w )
{
	if (offset & 0x01)
		TMS9928A_register_w(0, data);
	else
		TMS9928A_vram_w(0, data);
}

static WRITE8_HANDLER ( mtx_sense_w )
{
	key_sense = data;
}

static  READ8_HANDLER ( mtx_key_lo_r )
{
	unsigned char rtn = 0;

	if (key_sense==0xfe)
		rtn = readinputport(0);

	if (key_sense==0xfd)
		rtn = readinputport(1);

	if (key_sense==0xfb)
		rtn = readinputport(2);

	if (key_sense==0xf7)
		rtn = readinputport(3);

	if (key_sense==0xef)
		rtn = readinputport(4);

	if (key_sense==0xdf)
		rtn = readinputport(5);

	if (key_sense==0xbf)
		rtn = readinputport(6);

	if (key_sense==0x7f)
		rtn = readinputport(7);

	return(rtn);
}

static  READ8_HANDLER ( mtx_key_hi_r )
{
	unsigned char rtn = 0;

	unsigned char tmp;
	tmp = ((readinputport(10) & 0x03) << 2) | 0xf0;
	rtn = tmp;

	if (key_sense==0xfe)
		rtn = (readinputport(8) & 0x03) | tmp;

	if (key_sense==0xfd)
		rtn = ((readinputport(8) >> 2) & 0x03) | tmp;

	if (key_sense==0xfb)
		rtn = ((readinputport(8) >> 4) & 0x03) | tmp;

	if (key_sense==0xf7)
		rtn = ((readinputport(8) >> 6) & 0x03) | tmp;

	if (key_sense==0xef)
		rtn = (readinputport(9) & 0x03) | tmp;

	if (key_sense==0xdf)
		rtn = ((readinputport(9) >> 2) & 0x03) | tmp;

	if (key_sense==0xbf)
		rtn = ((readinputport(9) >> 4) & 0x03) | tmp;

	if (key_sense==0x7f)
		rtn = ((readinputport(9) >> 6) & 0x03) | tmp;

	return(rtn);
}

static void mtx_ctc_interrupt(int state)
{
	/*logerror("interrupting ctc %02x\r\n ",state); */
	cpunum_set_input_line(0, 0, state);
}

static  READ8_HANDLER ( mtx_ctc_r )
{
	return z80ctc_0_r(offset);
}

static WRITE8_HANDLER ( mtx_ctc_w )
{
	/*logerror("CTC W: %02x\r\n",data); */
	if (offset < 3)
		z80ctc_0_w(offset,data);
}

static z80ctc_interface	mtx_ctc_intf =
{
	MTX_SYSTEM_CLOCK,
	0,
	mtx_ctc_interrupt,
	0,
	0,
	0
};

static void mtx_tms9929A_interrupt (int data)
{
/*	logerror ("tms9929A_interrupt: %d\n", data); */
	z80ctc_0_trg0_w (0, data ? 0 : 1);
}

static void mtx_set_bank_offsets (unsigned int bank1, unsigned int bank2, 
	                          unsigned int bank3, unsigned int bank4,
	                          unsigned int bank5, unsigned int bank6,
	                          unsigned int bank7, unsigned int bank8)
{
	unsigned char * romimage;

/*	logerror ("CPM %d  RAM %x  ROM %x\n", mtx_relcpmh, */
/*			mtx_rampage, mtx_rompage); */
/*	logerror ("map: [0000] %04x [2000] %04x [4000] %04x [6000] %04x\n", */
/*			bank1, bank2, bank3, bank4); */
/*	logerror ("     [8000] %04x [a000] %04x [c000] %04x [e000] %04x\n", */
/*			bank5, bank6, bank7, bank8); */

	romimage = memory_region (REGION_CPU1);

	if (mtx_relcpmh)
	{
		memory_set_bankptr (1, mess_ram + bank1);
		/* bank 9 is handled by the mtx_trap_write function */
		memory_set_bankptr (2, mess_ram + bank2);
		memory_set_bankptr (10, mess_ram + bank2);
	}
	else
	{
		memory_set_bankptr (1, romimage + bank1);
		/* bank is 9 handled by the mtx_trap_write function */
		memory_set_bankptr (2, romimage + bank2);
		memory_set_bankptr (10, mtx_null_mem);
	}

	if (bank3 < mess_ram_size)
	{
		memory_set_bankptr (3, mess_ram + bank3);
		memory_set_bankptr (11, mess_ram + bank3);
	}
	else
	{
		memory_set_bankptr (3, mtx_null_mem);
		memory_set_bankptr (11, mtx_zero_mem);
	}

	if (bank4 < mess_ram_size)
	{
		memory_set_bankptr (4, mess_ram + bank4);
		memory_set_bankptr (12, mess_ram + bank4);
	}
	else
	{
		memory_set_bankptr (4, mtx_null_mem);
		memory_set_bankptr (12, mtx_zero_mem);
	}

	if (bank5 < mess_ram_size)
	{
		memory_set_bankptr (5, mess_ram + bank5);
		memory_set_bankptr (13, mess_ram + bank5);
	}
	else
	{
		memory_set_bankptr (5, mtx_null_mem);
		memory_set_bankptr (13, mtx_zero_mem);
	}

	if (bank6 < mess_ram_size)
	{
		memory_set_bankptr (6, mess_ram + bank6);
		memory_set_bankptr (14, mess_ram + bank6);
	}
	else
	{
		memory_set_bankptr (6, mtx_null_mem);
		memory_set_bankptr (14, mtx_zero_mem);
	}

	memory_set_bankptr (7, mess_ram + bank7);
	memory_set_bankptr (15, mess_ram + bank7);

	memory_set_bankptr (8, mess_ram + bank8);
	memory_set_bankptr (16, mess_ram + bank8);
}

static WRITE8_HANDLER ( mtx_bankswitch_w )
{
	unsigned int bank1, bank2, bank3, bank4;
	unsigned int bank5, bank6, bank7, bank8;

	mtx_relcpmh = (data & 0x80) >> 7;
	mtx_rampage = (data & 0x0f);
	mtx_rompage = (data & 0x70) >> 4;

	if (mtx_relcpmh)
	{
		bank1 = 0xe000 + mtx_rampage * 0xc000;
		bank2 = 0xc000 + mtx_rampage * 0xc000;
		bank3 = 0xa000 + mtx_rampage * 0xc000;
		bank4 = 0x8000 + mtx_rampage * 0xc000;
		bank5 = 0x6000 + mtx_rampage * 0xc000;
		bank6 = 0x4000 + mtx_rampage * 0xc000;
	}
	else
	{
		bank1 = 0x0;
		bank2 = 0x2000 + mtx_rompage * 0x2000;
		bank3 = 0xa000 + mtx_rampage * 0x8000;
		bank4 = 0x8000 + mtx_rampage * 0x8000;
		bank5 = 0x6000 + mtx_rampage * 0x8000;
		bank6 = 0x4000 + mtx_rampage * 0x8000;
	}

	bank7 = 0x2000;
	bank8 = 0x0;

	mtx_set_bank_offsets (bank1, bank2, bank3, bank4,
                              bank5, bank6, bank7, bank8);
}

static void mtx_virt_to_phys (const char * what, int vaddress,
		int * ramspace, int * paddress)
{
	int offset = vaddress & 0x1fff;
	int vbase  = vaddress & ~0x1fff;
	int pbase = 0x20000;

	*ramspace = mtx_relcpmh || vaddress >= 0x4000;

	if (0xc000 <= vaddress && vaddress < 0x10000)
		pbase = 0xe000 - vbase;
	else if (mtx_relcpmh)
		pbase = 0xe000 - vbase + mtx_rampage * 0xc000;
	else if (0 <= vaddress && vaddress < 0x2000)
		pbase = 0;
	else if (0x2000 <= vaddress && vaddress < 0x4000)
		pbase = 0x2000 + mtx_rompage * 0x2000;
	else if (0x4000 <= vaddress && vaddress < 0xc000)
		pbase = (0xe000 - vbase) + mtx_rampage * 0x8000;

	*paddress = pbase + offset;

/*	logerror ("%s (%d,%d,%d,%04x) -> (%d,%06x)\n", what, */
/*			mtx_relcpmh, mtx_rompage, mtx_rampage, vaddress, */
/*			*ramspace, *paddress); */
}

static unsigned char mtx_peek (int vaddress)
{
	unsigned char * romimage;
	int ramspace;
	int paddress;

	romimage = memory_region (REGION_CPU1);
	mtx_virt_to_phys ("peek", vaddress, &ramspace, &paddress);

	if (paddress > mess_ram_size) {
		logerror ("peek into non-existing memory\n");
		return 0;
	}

	if (ramspace)
		return mess_ram[paddress];
	else 
		return romimage[paddress];
}

static void mtx_poke (int vaddress, unsigned char data)
{
	int ramspace;
	int paddress;

	mtx_virt_to_phys ("poke", vaddress, &ramspace, &paddress);
	if (!ramspace)
		logerror ("poke into the ROM address space\n");
	else if (paddress >= mess_ram_size)
		logerror ("poke into non-existing RAM\n");
	else
    		mess_ram[paddress] = data;
}

/*
 * A filename is at most 14 characters long, always ending with a space.
 * The save file can be at most 65536 long.
 * Note: the empty string is saved as a single space.
 */
static void mtx_save_hack(int start, int length)
{
	int i;
	int saved = 0;
	mame_file *f;
	static char filename[16] = "";

/*	logerror("mtx_save_hack: start=%#x  length=%#x (%d)  index=%#x (%d)\n", */
/*			start, length, length, mtx_saveindex, mtx_saveindex); */

	if ((start > 0xc000) && (length == 20))
	{
		for (i = 0; i < 18; i++)
			mtx_savebuffer[i] = mtx_peek(start + i);

		mtx_saveindex = 18;

		memcpy(filename, mtx_savebuffer + 1, 15);

		for (i = 14; i > 0 && filename[i] == 0x20; i--)
			;

		filename[i + 1] = '\0';
	}
	else
	{
		if (mtx_saveindex + length > 65536)
			length = 65536 - mtx_saveindex;

		for (i = 0; i < length; i++)
			mtx_savebuffer[mtx_saveindex + i] = mtx_peek(start + i);

		mtx_saveindex += length;
	}

	if (start == 0xc000)
	{
		logerror("saving buffer into '%s', ", filename);

		if ((f = mame_fopen(Machine->gamedrv->name, filename,
						FILETYPE_IMAGE, 1)) != 0)
		{
			saved = mame_fwrite(f, mtx_savebuffer, mtx_saveindex);
			mame_fclose(f);
		}

		logerror("saved %d bytes\n", saved);
		filename[0] = '\0';
	}
}

/*
 * A filename is at most 14 characters long, ending with a space.
 * At most 65536 bytes are loaded from the file.
 * Note: the empty string is loaded as a single space (ugly),
 *       not the 'next' file.
 */
static void mtx_load_hack(int start, int length)
{
	int i;
	int filesize;
	mame_file *f;
	static char filename[16] = "";

/*	logerror("mtx_load_hack: start=%#x  length=%#x (%d)  size=%#x (%d)\n", */
/*			start, length, length, mtx_loadsize, mtx_loadsize); */

	if ((start > 0xc000) && (length == 18) && (mtx_loadsize <= 0))
	{
		for (i = 0; i < 15; i++)
			filename[i] = mtx_peek(start - 0xf + i);

		for (i = 14; i > 0 && filename[i] == 0x20; i--)
			;

		filename[i+1] = '\0';
		logerror("loading '%s' into buffer, ", filename);
		if ((f = mame_fopen(Machine->gamedrv->name, filename,
						FILETYPE_IMAGE, 0)) != 0)
		{
			filesize = mame_fsize(f);
			if (filesize > 65536)
				filesize = 65536;

			mtx_loadsize = mame_fread(f, mtx_loadbuffer, filesize);
			mame_fclose(f);
		}

		logerror("loaded %d bytes\n", mtx_loadsize);
	}

	if (mtx_loadsize > 0)
	{
		if (length > mtx_loadsize)
		{
			logerror("file '%s' is too short\n", filename);
			length = mtx_loadsize;
		}

		for (i = 0; i < length; i++)
			mtx_poke(start + i, mtx_loadbuffer[i]);

		memcpy(mtx_loadbuffer, mtx_loadbuffer + length,
				mtx_loadsize - length);
		mtx_loadsize -= length;
	}
}

static void mtx_verify_hack(int start, int length)
{
	/* logerror("mtx_verify_hack:  start=0x%x  length=0x%x (%d)  not implemented\n", start, length, length); */
}

static WRITE8_HANDLER ( mtx_trap_write )
{
	int pc;
	int start;
	int length;

	if (mtx_relcpmh)
	{
		mtx_poke (offset, data);
		return;
	}

	pc = activecpu_get_reg(Z80_PC);
	if((offset == 0x0aae) && (pc == 0x0ab1))
	{
		start = activecpu_get_reg(Z80_HL);
		length = activecpu_get_reg(Z80_DE);

		/* logerror("PC %04x\nStart %04x, Length %04x, 0xFD67 %02x, 0xFD68 %02x index 0x%04x\n", pc, start, length, mess_ram[0xfd67], mess_ram[0xfd68], mtx_loadsize); */

		if(mtx_peek(0xfd68) == 0)
			mtx_save_hack(start, length);
		else if(mtx_peek(0xfd67) == 0)
			mtx_load_hack(start, length);
		else
			mtx_verify_hack(start, length);
	}
}

static void mtx_printer_getinfo(const device_class *devclass, UINT32 state, union devinfo *info)
{
	switch(state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case DEVINFO_INT_COUNT:							info->i = 1; break;

		default:										printer_device_getinfo(devclass, state, info); break;
	}
}

static MACHINE_START( mtx512 )
{
	unsigned char * romimage;

	mtx_null_mem = (unsigned char *) auto_malloc (16384);

	mtx_zero_mem = (unsigned char *) auto_malloc (16384);

	mtx_loadbuffer = (unsigned char *) auto_malloc (65536);

	mtx_savebuffer = (unsigned char *) auto_malloc (65536);

	memset (mtx_null_mem, 0, 16384);
	memset (mtx_zero_mem, 0, 16384);
	memset (mtx_loadbuffer, 0, 65536);
	memset (mtx_savebuffer, 0, 65536);

	z80ctc_init(0, &mtx_ctc_intf);

	/* Patch the Rom (Sneaky........ Who needs to trap opcodes?) */
	romimage = memory_region(REGION_CPU1);
	romimage[0x0aae] = 0x32;	/* ld ($0aae),a */
	romimage[0x0aaf] = 0xae;
	romimage[0x0ab0] = 0x0a;
	romimage[0x0ab1] = 0xc9;	/* ret */

	/* Set up the starting memory configuration */
	mtx_relcpmh = 0;
	mtx_rampage = 0;
	mtx_rompage = 0;
	mtx_set_bank_offsets (0, 0x2000, 0xa0000, 0x8000,
			      0x6000, 0x4000, 0x2000, 0);

	mtx_loadsize = 0;
	mtx_saveindex = 0;
	return 0;
}

static INTERRUPT_GEN( mtx_interrupt )
{
	TMS9928A_interrupt();
}

ADDRESS_MAP_START( mtx_mem, ADDRESS_SPACE_PROGRAM, 8)
	AM_RANGE( 0x0000, 0x1fff) AM_READWRITE( MRA8_BANK1, mtx_trap_write )
	AM_RANGE( 0x2000, 0x3fff) AM_READWRITE( MRA8_BANK2, MWA8_BANK10 )
	AM_RANGE( 0x4000, 0x5fff) AM_READWRITE( MRA8_BANK3, MWA8_BANK11 )
	AM_RANGE( 0x6000, 0x7fff) AM_READWRITE( MRA8_BANK4, MWA8_BANK12 )
	AM_RANGE( 0x8000, 0x9fff) AM_READWRITE( MRA8_BANK5, MWA8_BANK13 )
	AM_RANGE( 0xa000, 0xbfff) AM_READWRITE( MRA8_BANK6, MWA8_BANK14 )
	AM_RANGE( 0xc000, 0xdfff) AM_READWRITE( MRA8_BANK7, MWA8_BANK15 )
	AM_RANGE( 0xe000, 0xffff) AM_READWRITE( MRA8_BANK8, MWA8_BANK16 )
ADDRESS_MAP_END

ADDRESS_MAP_START( mtx_io, ADDRESS_SPACE_IO, 8)
	ADDRESS_MAP_FLAGS( AMEF_ABITS(8) ) 
	AM_RANGE( 0x00, 0x00) AM_READWRITE( mtx_strobe_r, mtx_bankswitch_w )
	AM_RANGE( 0x01, 0x02) AM_READWRITE( mtx_vdp_r, mtx_vdp_w )
	AM_RANGE( 0x03, 0x03) AM_READWRITE( mtx_psg_r, mtx_cst_w )
	AM_RANGE( 0x04, 0x04) AM_READWRITE( mtx_prt_r, mtx_prt_w )
	AM_RANGE( 0x05, 0x05) AM_READWRITE( mtx_key_lo_r, mtx_sense_w )
	AM_RANGE( 0x06, 0x06) AM_READWRITE( mtx_key_hi_r, mtx_psg_w )
	AM_RANGE( 0x08, 0x0b) AM_READWRITE( mtx_ctc_r, mtx_ctc_w )
ADDRESS_MAP_END

INPUT_PORTS_START( mtx512 )
 PORT_START /* 0 */
  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("3 #") PORT_CODE(KEYCODE_3)
  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("7 '") PORT_CODE(KEYCODE_7)
  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("- =") PORT_CODE(KEYCODE_MINUS)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\\ |") PORT_CODE(KEYCODE_BACKSLASH)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD7 PAGE )") PORT_CODE(KEYCODE_7_PAD)

 PORT_START /* 1 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ESCAPE") PORT_CODE(KEYCODE_ESC)
  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("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 0") PORT_CODE(KEYCODE_0)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("^ ~") PORT_CODE(KEYCODE_EQUALS)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD8 EOL") PORT_CODE(KEYCODE_8_PAD)

 PORT_START /* 2 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CTRL") PORT_CODE(KEYCODE_LCONTROL)
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w W") PORT_CODE(KEYCODE_W)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r R") PORT_CODE(KEYCODE_R)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y Y") PORT_CODE(KEYCODE_Y)
  PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i I") PORT_CODE(KEYCODE_I)
  PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p P") PORT_CODE(KEYCODE_P)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("[ {") PORT_CODE(KEYCODE_CLOSEBRACE)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD5 CURSORUP") PORT_CODE(KEYCODE_5_PAD)

 PORT_START /* 3 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q Q") PORT_CODE(KEYCODE_Q)
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e E") PORT_CODE(KEYCODE_E)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t T") PORT_CODE(KEYCODE_T)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u U") PORT_CODE(KEYCODE_U)
  PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o O") PORT_CODE(KEYCODE_O)
  PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("@ `") PORT_CODE(KEYCODE_OPENBRACE)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LINEFEED") PORT_CODE(KEYCODE_DOWN)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD1 CURSORLEFT") PORT_CODE(KEYCODE_1_PAD)

 PORT_START /* 4 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ALPHALOCK") PORT_CODE(KEYCODE_CAPSLOCK)
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s S") PORT_CODE(KEYCODE_S)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f F") PORT_CODE(KEYCODE_F)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h H") PORT_CODE(KEYCODE_H)
  PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k K") PORT_CODE(KEYCODE_K)
  PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("; +") PORT_CODE(KEYCODE_COLON)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("] }") PORT_CODE(KEYCODE_BACKSLASH2)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD3 CURSORRIGHT") PORT_CODE(KEYCODE_3_PAD)

 PORT_START /* 5 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a A") PORT_CODE(KEYCODE_A)
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d D") PORT_CODE(KEYCODE_D)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g G") PORT_CODE(KEYCODE_G)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j J") PORT_CODE(KEYCODE_J)
  PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l L") PORT_CODE(KEYCODE_L)
  PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(": *") PORT_CODE(KEYCODE_QUOTE)
  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("NUMERICPAD2 HOME") PORT_CODE(KEYCODE_2_PAD)

 PORT_START /* 6 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("LEFTSHIFT") PORT_CODE(KEYCODE_LSHIFT)
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x X") PORT_CODE(KEYCODE_X)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v V") PORT_CODE(KEYCODE_V)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n N") PORT_CODE(KEYCODE_N)
  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_SLASH)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("RIGHTSHIFT") PORT_CODE(KEYCODE_RSHIFT)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD(.) CURSORDOWN") PORT_CODE(KEYCODE_DEL_PAD)

 PORT_START /* 7 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z Z") PORT_CODE(KEYCODE_Z)
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c C") PORT_CODE(KEYCODE_C)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b B") PORT_CODE(KEYCODE_B)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m M") PORT_CODE(KEYCODE_M)
  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_MINUS_PAD)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD0 INS") PORT_CODE(KEYCODE_0_PAD)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPADENTER CLS") PORT_CODE(KEYCODE_ENTER_PAD)

/* Hi Bits */

 PORT_START /* 8 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD9 BRK") PORT_CODE(KEYCODE_9_PAD)
  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("BS") PORT_CODE(KEYCODE_BACKSPACE)
  PORT_BIT (0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F5") PORT_CODE(KEYCODE_F5)
  PORT_BIT (0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD4 TAB") PORT_CODE(KEYCODE_4_PAD)
  PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F2") PORT_CODE(KEYCODE_F2)
  PORT_BIT (0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("NUMERICPAD6 DEL") PORT_CODE(KEYCODE_6_PAD)
  PORT_BIT (0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F6") PORT_CODE(KEYCODE_F6)

 PORT_START /* 9 */
  PORT_BIT (0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(none)")
  PORT_BIT (0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F7") PORT_CODE(KEYCODE_F7)
  PORT_BIT (0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("(none)")
  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("(none)")
  PORT_BIT (0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F8") PORT_CODE(KEYCODE_F8)
  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("F4") PORT_CODE(KEYCODE_F4)

 PORT_START /* 10 */
  PORT_DIPNAME(0x02, 0x00, "Country Code Switch 1")
  PORT_DIPSETTING(0x02, DEF_STR(Off) )
  PORT_DIPSETTING(0x00, DEF_STR(On) )
  PORT_DIPNAME(0x01, 0x00, "Country Code Switch 0")
  PORT_DIPSETTING(0x01, DEF_STR(Off) )
  PORT_DIPSETTING(0x00, DEF_STR(On) )
  PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)

INPUT_PORTS_END


static struct z80_irq_daisy_chain mtx_daisy_chain[] =
{
	{z80ctc_reset, z80ctc_irq_state, z80ctc_irq_ack, z80ctc_irq_reti, 0},
	{0,0,0,0,-1}
};

static const TMS9928a_interface tms9928a_interface =
{
	TMS9929A,
	0x4000,
	mtx_tms9929A_interrupt
};

static MACHINE_DRIVER_START( mtx512 )
	/* basic machine hardware */
	MDRV_CPU_ADD(Z80, MTX_SYSTEM_CLOCK)
	MDRV_CPU_PROGRAM_MAP(mtx_mem, 0)
	MDRV_CPU_IO_MAP(mtx_io, 0)
	MDRV_CPU_VBLANK_INT(mtx_interrupt, 1)
	MDRV_CPU_CONFIG(mtx_daisy_chain)
	MDRV_FRAMES_PER_SECOND(50)
	MDRV_VBLANK_DURATION(DEFAULT_REAL_60HZ_VBLANK_DURATION)
	MDRV_INTERLEAVE(1)

	MDRV_MACHINE_START( mtx512 )

	/* video hardware */
	MDRV_TMS9928A( &tms9928a_interface )

	/* sound hardware */
	MDRV_SPEAKER_STANDARD_MONO("mono")
	MDRV_SOUND_ADD(SN76496, MTX_SYSTEM_CLOCK)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.00)
MACHINE_DRIVER_END

ROM_START (mtx512)
	ROM_REGION (0x12000, REGION_CPU1, 0)
	ROM_LOAD ("osrom", 0x0, 0x2000, CRC(9ca858cc) SHA1(3804503a58f0bcdea96bb6488833782ebd03976d))
	ROM_LOAD ("basicrom", 0x2000, 0x2000, CRC(87b4e59c) SHA1(c49782a82a7f068c1195cd967882ba9edd546eaf))
	ROM_LOAD ("assemrom", 0x4000, 0x2000, CRC(9d7538c3) SHA1(d1882c4ea61a68b1715bd634ded5603e18a99c5f))
ROM_END

SYSTEM_CONFIG_START(mtx512)
	CONFIG_RAM(32 * 1024)
	CONFIG_RAM_DEFAULT(64 * 1024)
	CONFIG_RAM(96 * 1024)
	CONFIG_RAM(128 * 1024)
	CONFIG_RAM(160 * 1024)
	CONFIG_RAM(192 * 1024)
	CONFIG_RAM(224 * 1024)
	CONFIG_RAM(256 * 1024)
	CONFIG_RAM(288 * 1024)
	CONFIG_RAM(320 * 1024)
	CONFIG_RAM(352 * 1024)
	CONFIG_RAM(384 * 1024)
	CONFIG_RAM(416 * 1024)
	CONFIG_RAM(448 * 1024)
	CONFIG_RAM(480 * 1024)
	CONFIG_RAM(512 * 1024)
	CONFIG_DEVICE(mtx_printer_getinfo)
SYSTEM_CONFIG_END

/*    YEAR  NAME      PARENT	COMPAT	MACHINE   INPUT     INIT     CONFIG,  COMPANY          FULLNAME */
COMP( 1983, mtx512,   0,		0,		mtx512,   mtx512,   0,       mtx512,  "Memotech Ltd.", "MTX 512" , 0)
