/***************************************************************************
		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 classifed 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 runing (i.e. Paperboy).
			Corrected frames per second value for 48k and 128k
			Sincalir 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.
			Remaped some Spectrum+ keys.
				Presing F3 to reset was seting 0xf7 on keyboard
				input port. Problem occured for snapshots of
				some programms where it was readed 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
				completly 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 "driver.h"
#include "video/generic.h"
#include "includes/spectrum.h"
#include "eventlst.h"
#include "devices/snapquik.h"
#include "devices/cartslot.h"
#include "devices/cassette.h"
#include "sound/ay8910.h"
#include "sound/speaker.h"
#include "formats/tzx_cas.h"
#include "mslegacy.h"

/* +3 hardware */
#include "machine/nec765.h"
#include "cpuintrf.h"
#include "devices/dsk.h"


extern void spectrum_128_update_memory(void);
extern void spectrum_plus3_update_memory(void);


static const struct AY8910interface spectrum_ay_interface =
{
	NULL
};


/****************************************************************************************************/
/* Spectrum 48k functions */

/*
 bit 7-5: not used
 bit 4: Ear output/Speaker
 bit 3: MIC/Tape Output
 bit 2-0: border colour
*/

static int		motor_toggle_previous = 0;
static int		cassette_motor_mode = 0;

int PreviousFE = 0;

static WRITE8_HANDLER(spectrum_port_fe_w)
{
	unsigned char Changed;

	Changed = PreviousFE^data;

	/* border colour changed? */
	if ((Changed & 0x07)!=0)
	{
		/* yes - send event */
		EventList_AddItemOffset(0x0fe, data & 0x07, ATTOTIME_TO_CYCLES(0,attotime_mul(video_screen_get_scan_period(0), video_screen_get_vpos(0))));
	}

	if ((Changed & (1<<4))!=0)
	{
		/* DAC output state */
		speaker_level_w(0,(data>>4) & 0x01);
	}

	if ((Changed & (1<<3))!=0)
	{
		/* write cassette data */
		cassette_output(image_from_devtype_and_index(IO_CASSETTE, 0), (data & (1<<3)) ? -1.0 : +1.0);
	}

	PreviousFE = data;
}




static ADDRESS_MAP_START (spectrum_mem, ADDRESS_SPACE_PROGRAM, 8)
	AM_RANGE(0x0000, 0x3fff) AM_ROM
	AM_RANGE(0x4000, 0x57ff) AM_READWRITE( spectrum_characterram_r, spectrum_characterram_w )
	AM_RANGE(0x5800, 0x5aff) AM_READWRITE( spectrum_colorram_r, spectrum_colorram_w )
	AM_RANGE(0x5b00, 0xffff) AM_RAM
ADDRESS_MAP_END

/* KT: more accurate keyboard reading */
/* DJR: Spectrum+ keys added */
static  READ8_HANDLER(spectrum_port_fe_r)
{
   int lines = offset>>8;
   int data = 0xff;

   int cs_extra1 = readinputport(8)  & 0x1f;
   int cs_extra2 = readinputport(9)  & 0x1f;
   int cs_extra3 = readinputport(10) & 0x1f;
   int ss_extra1 = readinputport(11) & 0x1f;
   int ss_extra2 = readinputport(12) & 0x1f;

	if ( readinputport(17) & 0x01 ) {
		if ( motor_toggle_previous == 0 ) {
			cassette_motor_mode = cassette_motor_mode ^ 0x01;
			cassette_change_state( image_from_devtype_and_index( IO_CASSETTE, 0 ), cassette_motor_mode ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED, CASSETTE_MASK_MOTOR );
		}
		motor_toggle_previous = 1;
	} else {
		motor_toggle_previous = 0;
	}

   /* Caps - V */
   if ((lines & 1)==0)
   {
		data &= readinputport(0);
		/* CAPS for extra keys */
		if (cs_extra1 != 0x1f || cs_extra2 != 0x1f || cs_extra3 != 0x1f)
			data &= ~0x01;
   }

   /* A - G */
   if ((lines & 2)==0)
		data &= readinputport(1);

   /* Q - T */
   if ((lines & 4)==0)
		data &= readinputport(2);

   /* 1 - 5 */
   if ((lines & 8)==0)
		data &= readinputport(3) & cs_extra1;

   /* 6 - 0 */
   if ((lines & 16)==0)
		data &= readinputport(4) & cs_extra2;

   /* Y - P */
   if ((lines & 32)==0)
		data &= readinputport(5) & ss_extra1;

   /* H - Enter */
   if ((lines & 64)==0)
		data &= readinputport(6);

	/* B - Space */
	if ((lines & 128)==0)
	{
		data &= readinputport(7) & 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 (cassette_input(image_from_devtype_and_index(IO_CASSETTE, 0)) > 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 (readinputport(16) & 0x80)
		data ^= (0x40);
	return data;
}

/* kempston joystick interface */
static  READ8_HANDLER(spectrum_port_1f_r)
{
  return readinputport(13) & 0x1f;
}

/* fuller joystick interface */
static  READ8_HANDLER(spectrum_port_7f_r)
{
  return readinputport(14) | (0xff^0x8f);
}

/* mikrogen joystick interface */
static  READ8_HANDLER(spectrum_port_df_r)
{
  return readinputport(15) | (0xff^0x1f);
}

static  READ8_HANDLER ( spectrum_port_r )
{
		if ((offset & 1)==0)
			return spectrum_port_fe_r(offset);

		if ((offset & 0xff)==0x1f)
			return spectrum_port_1f_r(offset);

		if ((offset & 0xff)==0x7f)
			return spectrum_port_7f_r(offset);

		if ((offset & 0xff)==0xdf)
			return spectrum_port_df_r(offset);

		return video_screen_get_vpos(0)<193 ? spectrum_colorram[(video_screen_get_vpos(0)&0xf8)<<2]:0xff;
}

static WRITE8_HANDLER ( spectrum_port_w )
{
	if ((offset & 1)==0)
		spectrum_port_fe_w(offset,data);
	else
	{
		logerror("Write %02x to Port: %04x\n", data, offset);
	}
}

/* ports are not decoded full.
The function decodes the ports appropriately */
static ADDRESS_MAP_START (spectrum_io, ADDRESS_SPACE_IO, 8)
	AM_RANGE(0x0000, 0xffff) AM_READWRITE( spectrum_port_r, spectrum_port_w )
ADDRESS_MAP_END


/****************************************************************************************************/
/* functions and data used by spectrum 128, spectrum +2, spectrum +3 and scorpion */
static unsigned char *spectrum_ram = NULL;

static void spectrum_alloc_ram(int ram_size_in_k)
{
	spectrum_ram = (unsigned char *)auto_malloc(ram_size_in_k*1024);
	memset(spectrum_ram, 0, ram_size_in_k*1024);
}


/****************************************************************************************************/
/* Spectrum 128 specific functions */

int spectrum_128_port_7ffd_data = -1;
unsigned char *spectrum_128_screen_location = NULL;

static WRITE8_HANDLER(spectrum_128_port_7ffd_w)
{
	   /* 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 */

		/* disable paging? */
		if (spectrum_128_port_7ffd_data & 0x20)
				return;

		/* store new state */
		spectrum_128_port_7ffd_data = data;

		/* update memory */
		spectrum_128_update_memory();
}

extern void spectrum_128_update_memory(void)
{
		unsigned char *ChosenROM;
		int ROMSelection;

		if (spectrum_128_port_7ffd_data & 8)
		{
				logerror("SCREEN 1: BLOCK 7\n");
				spectrum_128_screen_location = spectrum_ram + (7<<14);
		}
		else
		{
				logerror("SCREEN 0: BLOCK 5\n");
				spectrum_128_screen_location = spectrum_ram + (5<<14);
		}

		/* select ram at 0x0c000-0x0ffff */
		{
				int ram_page;
				unsigned char *ram_data;

				ram_page = spectrum_128_port_7ffd_data & 0x07;
				ram_data = spectrum_ram + (ram_page<<14);

				memory_set_bankptr(4, ram_data);
				memory_set_bankptr(8, ram_data);

				logerror("RAM at 0xc000: %02x\n",ram_page);
		}

		/* ROM switching */
		ROMSelection = ((spectrum_128_port_7ffd_data>>4) & 0x01);

		/* rom 0 is 128K rom, rom 1 is 48 BASIC */

		ChosenROM = memory_region(REGION_CPU1) + 0x010000 + (ROMSelection<<14);

		memory_set_bankptr(1, ChosenROM);

		logerror("rom switch: %02x\n", ROMSelection);
}



static WRITE8_HANDLER(spectrum_128_port_bffd_w)
{
		AY8910_write_port_0_w(0, data);
}

static WRITE8_HANDLER(spectrum_128_port_fffd_w)
{
		AY8910_control_port_0_w(0, data);
}

static  READ8_HANDLER(spectrum_128_port_fffd_r)
{
		return AY8910_read_port_0_r(0);
}

static  READ8_HANDLER ( spectrum_128_port_r )
{
	 if ((offset & 1)==0)
	 {
		 return spectrum_port_fe_r(offset);
	 }

	 if ((offset & 2)==0)
	 {
		switch ((offset>>14) & 0x03)
		{
			default:
				break;

			case 3:
				return spectrum_128_port_fffd_r(offset);
		}
	 }

	 /* don't think these are correct! */
	 if ((offset & 0xff)==0x1f)
		 return spectrum_port_1f_r(offset);

	 if ((offset & 0xff)==0x7f)
		 return spectrum_port_7f_r(offset);

	 if ((offset & 0xff)==0xdf)
		 return spectrum_port_df_r(offset);

	 return video_screen_get_vpos(0)<193 ? spectrum_128_screen_location[0x1800|(video_screen_get_vpos(0)&0xf8)<<2]:0xff;
}

static WRITE8_HANDLER ( spectrum_128_port_w )
{
		if ((offset & 1)==0)
				spectrum_port_fe_w(offset,data);

		/* Only decodes on A15, A14 & A1 */
		else if ((offset & 2)==0)
		{
				switch ((offset>>8) & 0xc0)
				{
						case 0x40:
								spectrum_128_port_7ffd_w(offset, data);
								break;
						case 0x80:
								spectrum_128_port_bffd_w(offset, data);
								break;
						case 0xc0:
								spectrum_128_port_fffd_w(offset, data);
								break;
						default:
								logerror("Write %02x to 128 port: %04x\n", data, offset);
				}
		}
		else
		{
			logerror("Write %02x to 128 port: %04x\n", data, offset);
		}
}

/* ports are not decoded full.
The function decodes the ports appropriately */
static ADDRESS_MAP_START (spectrum_128_io, ADDRESS_SPACE_IO, 8)
	AM_RANGE(0x0000, 0xffff) AM_READWRITE( spectrum_128_port_r, spectrum_128_port_w )
ADDRESS_MAP_END

static ADDRESS_MAP_START (spectrum_128_mem, ADDRESS_SPACE_PROGRAM, 8)
	AM_RANGE( 0x0000, 0x3fff) AM_READWRITE( MRA8_BANK1, MWA8_BANK5 )
	AM_RANGE( 0x4000, 0x7fff) AM_READWRITE( MRA8_BANK2, MWA8_BANK6 )
	AM_RANGE( 0x8000, 0xbfff) AM_READWRITE( MRA8_BANK3, MWA8_BANK7 )
	AM_RANGE( 0xc000, 0xffff) AM_READWRITE( MRA8_BANK4, MWA8_BANK8 )
ADDRESS_MAP_END

static MACHINE_RESET( spectrum_128 )
{
	spectrum_alloc_ram(128);

	/* 0x0000-0x3fff always holds ROM */

	/* Bank 5 is always in 0x4000 - 0x7fff */
	memory_set_bankptr(2, spectrum_ram + (5<<14));
	memory_set_bankptr(6, spectrum_ram + (5<<14));

	/* Bank 2 is always in 0x8000 - 0xbfff */
	memory_set_bankptr(3, spectrum_ram + (2<<14));
	memory_set_bankptr(7, spectrum_ram + (2<<14));

	/* set initial ram config */
	spectrum_128_port_7ffd_data = 0;
	spectrum_128_update_memory();

	machine_reset_spectrum(machine);
}

/****************************************************************************************************/
/* 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 */

int spectrum_plus3_port_1ffd_data = -1;


static const nec765_interface spectrum_plus3_nec765_interface =
{
		NULL,
		NULL
};


static const int spectrum_plus3_memory_selections[]=
{
		0,1,2,3,
		4,5,6,7,
		4,5,6,3,
		4,7,6,3
};

static WRITE8_HANDLER(spectrum_plus3_port_3ffd_w)
{
		if (~readinputport(16) & 0x20)
				nec765_data_w(0,data);
}

static  READ8_HANDLER(spectrum_plus3_port_3ffd_r)
{
		if (readinputport(16) & 0x20)
				return 0xff;
		else
				return nec765_data_r(0);
}


static  READ8_HANDLER(spectrum_plus3_port_2ffd_r)
{
		if (readinputport(16) & 0x20)
				return 0xff;
		else
				return nec765_status_r(0);
}


void spectrum_plus3_update_memory(void)
{
	if (spectrum_128_port_7ffd_data & 8)
	{
			logerror("+3 SCREEN 1: BLOCK 7\n");
			spectrum_128_screen_location = spectrum_ram + (7<<14);
	}
	else
	{
			logerror("+3 SCREEN 0: BLOCK 5\n");
			spectrum_128_screen_location = spectrum_ram + (5<<14);
	}

	if ((spectrum_plus3_port_1ffd_data & 0x01)==0)
	{
			int ram_page;
			unsigned char *ram_data;

			/* ROM switching */
			unsigned char *ChosenROM;
			int ROMSelection;

			/* select ram at 0x0c000-0x0ffff */
			ram_page = spectrum_128_port_7ffd_data & 0x07;
			ram_data = spectrum_ram + (ram_page<<14);

			memory_set_bankptr(4, ram_data);
			memory_set_bankptr(8, ram_data);

			logerror("RAM at 0xc000: %02x\n",ram_page);

			/* Reset memory between 0x4000 - 0xbfff in case extended paging was being used */
			/* Bank 5 in 0x4000 - 0x7fff */
			memory_set_bankptr(2, spectrum_ram + (5<<14));
			memory_set_bankptr(6, spectrum_ram + (5<<14));

			/* Bank 2 in 0x8000 - 0xbfff */
			memory_set_bankptr(3, spectrum_ram + (2<<14));
			memory_set_bankptr(7, spectrum_ram + (2<<14));


			ROMSelection = ((spectrum_128_port_7ffd_data>>4) & 0x01) |
				((spectrum_plus3_port_1ffd_data>>1) & 0x02);

			/* rom 0 is editor, rom 1 is syntax, rom 2 is DOS, rom 3 is 48 BASIC */

			ChosenROM = memory_region(REGION_CPU1) + 0x010000 + (ROMSelection<<14);

			memory_set_bankptr(1, ChosenROM);
			memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x0000, 0x3fff, 0, 0, MWA8_ROM);

			logerror("rom switch: %02x\n", ROMSelection);
	}
	else
	{
			/* Extended memory paging */

			const int *memory_selection;
			int MemorySelection;
			unsigned char *ram_data;

			MemorySelection = (spectrum_plus3_port_1ffd_data>>1) & 0x03;

			memory_selection = &spectrum_plus3_memory_selections[(MemorySelection<<2)];

			ram_data = spectrum_ram + (memory_selection[0]<<14);
			memory_set_bankptr(1, ram_data);
			memory_set_bankptr(5, ram_data);
			/* allow writes to 0x0000-0x03fff */
			memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x0000, 0x3fff, 0, 0, MWA8_BANK5);

			ram_data = spectrum_ram + (memory_selection[1]<<14);
			memory_set_bankptr(2, ram_data);
			memory_set_bankptr(6, ram_data);

			ram_data = spectrum_ram + (memory_selection[2]<<14);
			memory_set_bankptr(3, ram_data);
			memory_set_bankptr(7, ram_data);

			ram_data = spectrum_ram + (memory_selection[3]<<14);
			memory_set_bankptr(4, ram_data);
			memory_set_bankptr(8, ram_data);

			logerror("extended memory paging: %02x\n",MemorySelection);
		}
}



static WRITE8_HANDLER(spectrum_plus3_port_7ffd_w)
{
	   /* 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 */

		/* disable paging? */
		if (spectrum_128_port_7ffd_data & 0x20)
				return;

		/* store new state */
		spectrum_128_port_7ffd_data = data;

		/* update memory */
		spectrum_plus3_update_memory();
}

static WRITE8_HANDLER(spectrum_plus3_port_1ffd_w)
{

		/* D0-D1: ROM/RAM paging */
		/* D2: Affects if d0-d1 work on ram/rom */
		/* D3 - Disk motor on/off */
		/* D4 - parallel port strobe */

		floppy_drive_set_motor_state(image_from_devtype_and_index(IO_FLOPPY, 0), data & (1<<3));
		floppy_drive_set_motor_state(image_from_devtype_and_index(IO_FLOPPY, 1), data & (1<<3));
		floppy_drive_set_ready_state(image_from_devtype_and_index(IO_FLOPPY, 0), 1, 1);
		floppy_drive_set_ready_state(image_from_devtype_and_index(IO_FLOPPY, 1), 1, 1);

		spectrum_plus3_port_1ffd_data = data;

		/* disable paging? */
		if ((spectrum_128_port_7ffd_data & 0x20)==0)
		{
				/* no */
				spectrum_plus3_update_memory();
		}
}

/* decoding as per spectrum FAQ on www.worldofspectrum.org */
static  READ8_HANDLER ( spectrum_plus3_port_r )
{
	 if ((offset & 1)==0)
	 {
		 return spectrum_port_fe_r(offset);
	 }

	 if ((offset & 2)==0)
	 {
		 switch ((offset>>14) & 0x03)
		 {
			/* +3 fdc,memory,centronics */
			case 0:
			{
				switch ((offset>>12) & 0x03)
				{
					/* +3 centronics */
					case 0:
						break;

					/* +3 fdc status */
					case 2:
						return spectrum_plus3_port_2ffd_r(offset);
					/* +3 fdc data */
					case 3:
						return spectrum_plus3_port_3ffd_r(offset);

					default:
						break;
				}
			}
			break;

			/* 128k AY data */
			case 3:
				return spectrum_128_port_fffd_r(offset);

			default:
				break;
		 }
	 }

	 return video_screen_get_vpos(0)<193 ? spectrum_128_screen_location[0x1800|(video_screen_get_vpos(0)&0xf8)<<2]:0xff;
}

static WRITE8_HANDLER ( spectrum_plus3_port_w )
{
		if ((offset & 1)==0)
				spectrum_port_fe_w(offset,data);

		/* the following is not decoded exactly, need to check
		what is correct! */

		if ((offset & 2)==0)
		{
			switch ((offset>>14) & 0x03)
			{
				/* +3 fdc,memory,centronics */
				case 0:
				{
					switch ((offset>>12) & 0x03)
					{
						/* +3 centronics */
						case 0:
						{


						}
						break;

						/* +3 memory */
						case 1:
							spectrum_plus3_port_1ffd_w(offset, data);
							break;

						/* +3 fdc data */
						case 3:
							spectrum_plus3_port_3ffd_w(offset,data);
							break;

						default:
							break;
					}
				}
				break;

				/* 128k memory */
				case 1:
					spectrum_plus3_port_7ffd_w(offset, data);
					break;

				/* 128k AY data */
				case 2:
					spectrum_128_port_bffd_w(offset, data);
					break;

				/* 128K AY register */
				case 3:
					spectrum_128_port_fffd_w(offset, data);

				default:
					break;
			}
		}

/*logerror("Write %02x to +3 port: %04x\n", data, offset); */
}

/* ports are not decoded full.
The function decodes the ports appropriately */
static ADDRESS_MAP_START (spectrum_plus3_io, ADDRESS_SPACE_IO, 8)
		AM_RANGE(0x0000, 0xffff) AM_READWRITE( spectrum_plus3_port_r, spectrum_plus3_port_w )
ADDRESS_MAP_END

static MACHINE_RESET( spectrum_plus3 )
{
	spectrum_alloc_ram(128);

	nec765_init(&spectrum_plus3_nec765_interface, NEC765A);

	floppy_drive_set_geometry(image_from_devtype_and_index(IO_FLOPPY, 0), FLOPPY_DRIVE_SS_40);
	floppy_drive_set_geometry(image_from_devtype_and_index(IO_FLOPPY, 1), FLOPPY_DRIVE_SS_40);

	/* Initial configuration */
	spectrum_128_port_7ffd_data = 0;
	spectrum_plus3_port_1ffd_data = 0;
	spectrum_plus3_update_memory();

	machine_reset_spectrum(machine);
}


/****************************************************************************************************/
/* TS2048 specific functions */


int ts2068_port_ff_data = -1; /* Display enhancement control */
int ts2068_port_f4_data = -1; /* Horizontal Select Register */

static  READ8_HANDLER(ts2068_port_f4_r)
{
		return ts2068_port_f4_data;
}

static WRITE8_HANDLER(ts2068_port_f4_w)
{
		ts2068_port_f4_data = data;
		ts2068_update_memory();
}

static WRITE8_HANDLER(ts2068_port_f5_w)
{
		AY8910_control_port_0_w(0, data);
}

static  READ8_HANDLER(ts2068_port_f6_r)
{
		/* TODO - Reading from register 14 reads the joystick ports
		   set bit 8 of address to read joystick #1
		   set bit 9 of address to read joystick #2
		   if both bits are set then OR values
		   Bit 0 up, 1 down, 2 left, 3 right, 7 fire active low. Other bits 1
		*/
		return AY8910_read_port_0_r(0);
}

static WRITE8_HANDLER(ts2068_port_f6_w)
{
		AY8910_write_port_0_w(0, data);
}

static  READ8_HANDLER(ts2068_port_ff_r)
{
		return ts2068_port_ff_data;
}

static WRITE8_HANDLER(ts2068_port_ff_w)
{
		/* 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
		*/
		ts2068_port_ff_data = data;
		ts2068_update_memory();
		logerror("Port %04x write %02x\n", offset, data);
}


static  READ8_HANDLER ( ts2068_port_r )
{
		switch (offset & 0xff)
		{
				/* Note: keys only decoded on port #fe not all even ports so
				   ports #f4 & #f6 correctly read */
				case 0xf4: return ts2068_port_f4_r(offset);
				case 0xf6: return ts2068_port_f6_r(offset);
				case 0xff: return ts2068_port_ff_r(offset);

				case 0xfe: return spectrum_port_fe_r(offset);
				case 0x1f: return spectrum_port_1f_r(offset);
				case 0x7f: return spectrum_port_7f_r(offset);
				case 0xdf: return spectrum_port_df_r(offset);
		}
		logerror("Read from port: %04x\n", offset);

		return 0xff;
}

static WRITE8_HANDLER ( ts2068_port_w )
{
/* Ports #fd & #fc were reserved by Timex for bankswitching and are not used
   by either the hardware or system software.
   Port #fb is the Thermal printer port and works exactly as the Sinclair
   Printer - ie not yet emulated.
*/
		switch (offset & 0xff)
		{
				case 0xfe: spectrum_port_fe_w(offset,data); break;
				case 0xf4: ts2068_port_f4_w(offset,data); break;
				case 0xf5: ts2068_port_f5_w(offset,data); break;
				case 0xf6: ts2068_port_f6_w(offset,data); break;
				case 0xff: ts2068_port_ff_w(offset,data); break;
				default:
						logerror("Write %02x to Port: %04x\n", data, offset);
		}
}



/*******************************************************************
 *
 *		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 that the Z80 can't see chunks of the EXROM and DOCK
 *		at the same time.
 *
 *******************************************************************/
void ts2068_update_memory(void)
{
	unsigned char *ChosenROM, *ExROM, *DOCK;
	read8_handler rh;
	write8_handler wh;

	DOCK = timex_cart_data;

	ExROM = memory_region(REGION_CPU1) + 0x014000;

	if (ts2068_port_f4_data & 0x01)
	{
		if (ts2068_port_ff_data & 0x80)
		{
				rh = MRA8_BANK1;
				wh = MWA8_ROM;
				memory_set_bankptr(1, ExROM);
				logerror("0000-1fff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(1, DOCK);
				rh = MRA8_BANK1;
				if (timex_cart_chunks&0x01)
					wh = MWA8_BANK9;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("0000-1fff Cartridge\n");
		}
	}
	else
	{
		ChosenROM = memory_region(REGION_CPU1) + 0x010000;
		memory_set_bankptr(1, ChosenROM);
		rh = MRA8_BANK1;
		wh = MWA8_ROM;
		logerror("0000-1fff HOME\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x0000, 0x1fff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x0000, 0x1fff, 0, 0, wh);

	if (ts2068_port_f4_data & 0x02)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(2, ExROM);
			rh = MRA8_BANK2;
			wh = MWA8_ROM;
			logerror("2000-3fff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(2, DOCK+0x2000);
				rh = MRA8_BANK2;
				if (timex_cart_chunks&0x02)
					wh = MWA8_BANK10;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("2000-3fff Cartridge\n");
		}
	}
	else
	{
		ChosenROM = memory_region(REGION_CPU1) + 0x012000;
		memory_set_bankptr(2, ChosenROM);
		rh = MRA8_BANK2;
		wh = MWA8_ROM;
		logerror("2000-3fff HOME\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x2000, 0x3fff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x2000, 0x3fff, 0, 0, wh);

	if (ts2068_port_f4_data & 0x04)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(3, ExROM);
			rh = MRA8_BANK3;
			wh = MWA8_ROM;
			logerror("4000-5fff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(3, DOCK+0x4000);
				rh = MRA8_BANK3;
				if (timex_cart_chunks&0x04)
					wh = MWA8_BANK11;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("4000-5fff Cartridge\n");
		}
	}
	else
	{
		memory_set_bankptr(3, mess_ram);
		memory_set_bankptr(11, mess_ram);
		rh = MRA8_BANK3;
		wh = MWA8_BANK11;
		logerror("4000-5fff RAM\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x4000, 0x5fff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x4000, 0x5fff, 0, 0, wh);

	if (ts2068_port_f4_data & 0x08)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(4, ExROM);
			rh = MRA8_BANK4;
			wh = MWA8_ROM;
			logerror("6000-7fff EXROM\n");
		}
		else
		{
				if (timex_cart_type == TIMEX_CART_DOCK)
				{
					memory_set_bankptr(4, DOCK+0x6000);
					rh = MRA8_BANK4;
					if (timex_cart_chunks&0x08)
						wh = MWA8_BANK12;
					else
						wh = MWA8_ROM;
				}
				else
				{
					rh = MRA8_NOP;
					wh = MWA8_ROM;
				}
				logerror("6000-7fff Cartridge\n");
		}
	}
	else
	{
		memory_set_bankptr(4, mess_ram + 0x2000);
		memory_set_bankptr(12, mess_ram + 0x2000);
		rh = MRA8_BANK4;
		wh = MWA8_BANK12;
		logerror("6000-7fff RAM\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x6000, 0x7fff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x6000, 0x7fff, 0, 0, wh);

	if (ts2068_port_f4_data & 0x10)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(5, ExROM);
			rh = MRA8_BANK5;
			wh = MWA8_ROM;
			logerror("8000-9fff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(5, DOCK+0x8000);
				rh = MRA8_BANK5;
				if (timex_cart_chunks&0x10)
					wh = MWA8_BANK13;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("8000-9fff Cartridge\n");
		}
	}
	else
	{
		memory_set_bankptr(5, mess_ram + 0x4000);
		memory_set_bankptr(13, mess_ram + 0x4000);
		rh = MRA8_BANK5;
		wh = MWA8_BANK13;
		logerror("8000-9fff RAM\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x8000, 0x9fff, 0, 0,rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x8000, 0x9fff, 0, 0,wh);

	if (ts2068_port_f4_data & 0x20)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(6, ExROM);
			rh = MRA8_BANK6;
			wh = MWA8_ROM;
			logerror("a000-bfff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(6, DOCK+0xa000);
				rh = MRA8_BANK6;
				if (timex_cart_chunks&0x20)
					wh = MWA8_BANK14;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("a000-bfff Cartridge\n");
		}
	}
	else
	{
		memory_set_bankptr(6, mess_ram + 0x6000);
		memory_set_bankptr(14, mess_ram + 0x6000);
		rh = MRA8_BANK6;
		wh = MWA8_BANK14;
		logerror("a000-bfff RAM\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0xa000, 0xbfff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xa000, 0xbfff, 0, 0, wh);

	if (ts2068_port_f4_data & 0x40)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(7, ExROM);
			rh = MRA8_BANK7;
			wh = MWA8_ROM;
			logerror("c000-dfff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(7, DOCK+0xc000);
				rh = MRA8_BANK7;
				if (timex_cart_chunks&0x40)
					wh = MWA8_BANK15;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("c000-dfff Cartridge\n");
		}
	}
	else
	{
		memory_set_bankptr(7, mess_ram + 0x8000);
		memory_set_bankptr(15, mess_ram + 0x8000);
		rh = MRA8_BANK7;
		wh = MWA8_BANK15;
		logerror("c000-dfff RAM\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0xc000, 0xdfff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xc000, 0xdfff, 0, 0, wh);

	if (ts2068_port_f4_data & 0x80)
	{
		if (ts2068_port_ff_data & 0x80)
		{
			memory_set_bankptr(8, ExROM);
			rh = MRA8_BANK8;
			wh = MWA8_ROM;
			logerror("e000-ffff EXROM\n");
		}
		else
		{
			if (timex_cart_type == TIMEX_CART_DOCK)
			{
				memory_set_bankptr(8, DOCK+0xe000);
				rh = MRA8_BANK8;
				if (timex_cart_chunks&0x80)
					wh = MWA8_BANK16;
				else
					wh = MWA8_ROM;
			}
			else
			{
				rh = MRA8_NOP;
				wh = MWA8_ROM;
			}
			logerror("e000-ffff Cartridge\n");
		}
	}
	else
	{
		memory_set_bankptr(8, mess_ram + 0xa000);
		memory_set_bankptr(16, mess_ram + 0xa000);
		rh = MRA8_BANK8;
		wh = MWA8_BANK16;
		logerror("e000-ffff RAM\n");
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe000, 0xffff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe000, 0xffff, 0, 0, wh);
}


static ADDRESS_MAP_START (ts2068_io, ADDRESS_SPACE_IO, 8)
	AM_RANGE(0x0000, 0xffff) AM_READWRITE( ts2068_port_r, ts2068_port_w )
ADDRESS_MAP_END

static ADDRESS_MAP_START (ts2068_mem, ADDRESS_SPACE_PROGRAM, 8)
	AM_RANGE(0x0000, 0x1fff) AM_READWRITE( MRA8_BANK1, MWA8_BANK9 )
	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


static MACHINE_RESET( ts2068 )
{
	ts2068_port_ff_data = 0;
	ts2068_port_f4_data = 0;
	ts2068_update_memory();

	machine_reset_spectrum(machine);
}


/****************************************************************************************************/
/* TC2048 specific functions */


static void tc2048_port_ff_w(int offset, int data)
{
		ts2068_port_ff_data = data;
		logerror("Port %04x write %02x\n", offset, data);
}

static  READ8_HANDLER ( tc2048_port_r )
{
		if ((offset & 1)==0)
				return spectrum_port_fe_r(offset);
		switch (offset & 0xff)
		{
				case 0xff: return ts2068_port_ff_r(offset);
				case 0x1f: return spectrum_port_1f_r(offset);
				case 0x7f: return spectrum_port_7f_r(offset);
				case 0xdf: return spectrum_port_df_r(offset);
		}

		logerror("Read from port: %04x\n", offset);
		return 0xff;
}

static WRITE8_HANDLER ( tc2048_port_w )
{
		if ((offset & 1)==0)
				spectrum_port_fe_w(offset,data);
		else if ((offset & 0xff)==0xff)
				tc2048_port_ff_w(offset,data);
		else
		{
				logerror("Write %02x to Port: %04x\n", data, offset);
		}
}

/* ports are not decoded full.
The function decodes the ports appropriately */
static ADDRESS_MAP_START (tc2048_io, ADDRESS_SPACE_IO, 8)
	AM_RANGE(0x0000, 0xffff) AM_READWRITE( tc2048_port_r, tc2048_port_w )
ADDRESS_MAP_END

static ADDRESS_MAP_START (tc2048_mem, ADDRESS_SPACE_PROGRAM, 8)
	AM_RANGE( 0x0000, 0x3fff) AM_ROM
	AM_RANGE( 0x4000, 0xffff) AM_READWRITE( MRA8_BANK1, MWA8_BANK2 )
ADDRESS_MAP_END

static MACHINE_RESET( tc2048 )
{
	memory_set_bankptr(1, mess_ram);
	memory_set_bankptr(2, mess_ram);
	ts2068_port_ff_data = 0;

	machine_reset_spectrum(machine);
}


/****************************************************************************************************/
/* BETADISK/TR-DOS disc controller emulation */
/* microcontroller KR1818VG93 is a russian wd179x clone */
#include "machine/wd17xx.h"

/*
DRQ (D6) and INTRQ (D7).
DRQ - signal showing request of data by microcontroller
INTRQ - signal of completion of execution of command.
*/

static int betadisk_status;
static int betadisk_active;
#ifdef UNUSED_FUNCTION
static void (*betadisk_memory_update)(void);

static OPBASE_HANDLER(betadisk_opbase_handler)
{

	int pc;

	pc = activecpu_get_pc();

	if ((pc & 0xc000)!=0x0000)
	{
		/* outside rom area */
		betadisk_active = 0;

		betadisk_memory_update();
	}
	else
	{
		/* inside rom area, switch on betadisk */
	//	betadisk_active = 1;

	//	betadisk_memory_update();
	}


	return pc & 0x0ffff;
}
#endif

static void betadisk_wd179x_callback(wd17xx_state_t state, void *param)
{
	switch (state)
	{
		case WD17XX_DRQ_SET:
		{
			betadisk_status |= (1<<6);
		}
		break;

		case WD17XX_DRQ_CLR:
		{
			betadisk_status &=~(1<<6);
		}
		break;

		case WD17XX_IRQ_SET:
		{
			betadisk_status |= (1<<7);
		}
		break;

		case WD17XX_IRQ_CLR:
		{
			betadisk_status &=~(1<<7);
		}
		break;
	}
}

#ifdef UNUSED_FUNCTION
/* these are active only when betadisk is enabled */
static WRITE8_HANDLER(betadisk_w)
{

	if (betadisk_active)
	{

	}
}

/* these are active only when betadisk is enabled */
static  READ8_HANDLER(betadisk_r)
{
	if (betadisk_active)
	{
		/* decoding of these ports might be wrong - to be checked! */
		if ((offset & 0x01f)==0x01f)
		{
			switch (offset & 0x0ff)
			{

			}
		}

	}

	return 0x0ff;
}
#endif

static void	 betadisk_init(void)
{
	betadisk_active = 0;
	betadisk_status = 0x03f;
	wd17xx_init(WD_TYPE_179X, betadisk_wd179x_callback, NULL);
}

/****************************************************************************************************/
/* 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 */

static int scorpion_256_port_1ffd_data = 0;

static void scorpion_update_memory(void)
{
	unsigned char *ChosenROM;
	int ROMSelection;
	read8_handler rh;
	write8_handler wh;

	if (spectrum_128_port_7ffd_data & 8)
	{
		logerror("SCREEN 1: BLOCK 7\n");
		spectrum_128_screen_location = spectrum_ram + (7<<14);
	}
	else
	{
		logerror("SCREEN 0: BLOCK 5\n");
		spectrum_128_screen_location = spectrum_ram + (5<<14);
	}

	/* select ram at 0x0c000-0x0ffff */
	{
		int ram_page;
		unsigned char *ram_data;

		ram_page = (spectrum_128_port_7ffd_data & 0x07) | ((scorpion_256_port_1ffd_data & (1<<4))>>1);
		ram_data = spectrum_ram + (ram_page<<14);

		memory_set_bankptr(4, ram_data);
		memory_set_bankptr(8, ram_data);

		logerror("RAM at 0xc000: %02x\n",ram_page);
	}

	if (scorpion_256_port_1ffd_data & (1<<0))
	{
		/* ram at 0x0000 */
		logerror("RAM at 0x0000\n");

		/* connect page 0 of ram to 0x0000 */
		rh = MRA8_BANK1;
		wh = MWA8_BANK5;
		memory_set_bankptr(1, spectrum_ram+(8<<14));
		memory_set_bankptr(5, spectrum_ram+(8<<14));
	}
	else
	{
		/* rom at 0x0000 */
		logerror("ROM at 0x0000\n");

		/* connect page 0 of rom to 0x0000 */
		rh = MRA8_BANK1;
		wh = MWA8_NOP;

		if (scorpion_256_port_1ffd_data & (1<<1))
		{
			ROMSelection = 2;
		}
		else
		{

			/* ROM switching */
			ROMSelection = ((spectrum_128_port_7ffd_data>>4) & 0x01);
		}

		/* rom 0 is 128K rom, rom 1 is 48 BASIC */
		ChosenROM = memory_region(REGION_CPU1) + 0x010000 + (ROMSelection<<14);

		memory_set_bankptr(1, ChosenROM);

		logerror("rom switch: %02x\n", ROMSelection);
	}
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x0000, 0x3fff, 0, 0, rh);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x0000, 0x3fff, 0, 0, wh);
}


static WRITE8_HANDLER(scorpion_port_7ffd_w)
{
	logerror("scorpion 7ffd w: %02x\n", data);

	/* disable paging? */
	if (spectrum_128_port_7ffd_data & 0x20)
		return;

	/* store new state */
	spectrum_128_port_7ffd_data = data;

	/* update memory */
	scorpion_update_memory();
}

static WRITE8_HANDLER(scorpion_port_1ffd_w)
{
	logerror("scorpion 1ffd w: %02x\n", data);

	scorpion_256_port_1ffd_data = data;

	/* disable paging? */
	if ((spectrum_128_port_7ffd_data & 0x20)==0)
	{
		scorpion_update_memory();
	}
}


/* not sure if decoding is full or partial on scorpion */
/* TO BE CHECKED! */
static  READ8_HANDLER(scorpion_port_r)
{
	 if ((offset & 1)==0)
	 {
		 return spectrum_port_fe_r(offset);
	 }

	 /* KT: the following is not decoded exactly, need to check what
	 is correct */
	 if ((offset & 2)==0)
	 {
		 switch ((offset>>8) & 0xff)
		 {
				case 0xff: return spectrum_128_port_fffd_r(offset);
				case 0x1f: return spectrum_port_1f_r(offset);
				case 0x7f: return spectrum_port_7f_r(offset);
				case 0xdf: return spectrum_port_df_r(offset);
		 }
	 }
#if 0
	 switch (offset & 0x0ff)
	 {
		case 0x01f:
			return wd17xx_status_r(offset);
		case 0x03f:
			return wd17xx_track_r(offset);
		case 0x05f:
			return wd17xx_sector_r(offset);
		case 0x07f:
			return wd17xx_data_r(offset);
		case 0x0ff:
			return betadisk_status;
	 }
#endif
	 logerror("Read from scorpion port: %04x\n", offset);

	 return 0xff;
}


/* not sure if decoding is full or partial on scorpion */
/* TO BE CHECKED! */
static WRITE8_HANDLER(scorpion_port_w)
{
	if ((offset & 1)==0)
		spectrum_port_fe_w(offset,data);

	else if ((offset & 2)==0)
	{
			switch ((offset>>8) & 0xf0)
			{
				case 0x70:
						scorpion_port_7ffd_w(offset, data);
						break;
				case 0xb0:
						spectrum_128_port_bffd_w(offset, data);
						break;
				case 0xf0:
						spectrum_128_port_fffd_w(offset, data);
						break;
				case 0x10:
						scorpion_port_1ffd_w(offset, data);
						break;
				default:
						logerror("Write %02x to scorpion port: %04x\n", data, offset);
			}
	}
	else
	{
		logerror("Write %02x to scorpion port: %04x\n", data, offset);
	}
}



/* ports are not decoded full.
The function decodes the ports appropriately */
static ADDRESS_MAP_START (scorpion_io, ADDRESS_SPACE_IO, 8)
	AM_RANGE(0x0000, 0xffff) AM_READWRITE(scorpion_port_r, scorpion_port_w)
ADDRESS_MAP_END

static MACHINE_RESET( scorpion )
{
	spectrum_alloc_ram(256);

	/* Bank 5 is always in 0x4000 - 0x7fff */
	memory_set_bankptr(2, spectrum_ram + (5<<14));
	memory_set_bankptr(6, spectrum_ram + (5<<14));

	/* Bank 2 is always in 0x8000 - 0xbfff */
	memory_set_bankptr(3, spectrum_ram + (2<<14));
	memory_set_bankptr(7, spectrum_ram + (2<<14));

	spectrum_128_port_7ffd_data = 0;
	scorpion_256_port_1ffd_data = 0;

	scorpion_update_memory();

	betadisk_init();
}

/****************************************************************************************************/
/* pentagon */

static  READ8_HANDLER(pentagon_port_r)
{
	return 0x0ff;
}


static WRITE8_HANDLER(pentagon_port_w)
{
}



/* ports are not decoded full.
The function decodes the ports appropriately */
static ADDRESS_MAP_START (pentagon_io, ADDRESS_SPACE_IO, 8)
	AM_RANGE(0x0000, 0xffff) AM_READWRITE( pentagon_port_r, pentagon_port_w )
ADDRESS_MAP_END

static MACHINE_RESET( pentagon )
{
	spectrum_alloc_ram(128);

	/* Bank 5 is always in 0x4000 - 0x7fff */
	memory_set_bankptr(2, spectrum_ram + (5<<14));
	memory_set_bankptr(6, spectrum_ram + (5<<14));

	/* Bank 2 is always in 0x8000 - 0xbfff */
	memory_set_bankptr(3, spectrum_ram + (2<<14));
	memory_set_bankptr(7, spectrum_ram + (2<<14));

	betadisk_init();
}

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

static const gfx_layout spectrum_charlayout =
{
	8,8,
	256,
	1,						/* 1 bits per pixel */

	{ 0 },					/* no bitplanes; 1 bit per pixel */

	{ 0, 1, 2, 3, 4, 5, 6, 7 },
	{ 0, 8*256, 16*256, 24*256, 32*256, 40*256, 48*256, 56*256 },

	8				/* every char takes 1 consecutive byte */
};

static GFXDECODE_START( spectrum )
	GFXDECODE_ENTRY( 0, 0x0, spectrum_charlayout, 0, 0x80 )
	GFXDECODE_ENTRY( 0, 0x0, spectrum_charlayout, 0, 0x80 )
	GFXDECODE_ENTRY( 0, 0x0, spectrum_charlayout, 0, 0x80 )
GFXDECODE_END

static INPUT_PORTS_START( spectrum )
	PORT_START /* [0] 0xFEFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS SHIFT") PORT_CODE(KEYCODE_LSHIFT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z  COPY    :      LN       BEEP") PORT_CODE(KEYCODE_Z)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X  CLEAR   Pound  EXP      INK") PORT_CODE(KEYCODE_X)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C  CONT    ?      LPRINT   PAPER") PORT_CODE(KEYCODE_C)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V  CLS     /      LLIST    FLASH") PORT_CODE(KEYCODE_V)

	PORT_START /* [1] 0xFDFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A  NEW     STOP   READ     ~") PORT_CODE(KEYCODE_A)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S  SAVE    NOT    RESTORE  |") PORT_CODE(KEYCODE_S)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D  DIM     STEP   DATA     \\") PORT_CODE(KEYCODE_D)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F  FOR     TO     SGN      {") PORT_CODE(KEYCODE_F)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G  GOTO    THEN   ABS      }") PORT_CODE(KEYCODE_G)

	PORT_START /* [2] 0xFBFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q  PLOT    <=     SIN      ASN") PORT_CODE(KEYCODE_Q)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W  DRAW    <>     COS      ACS") PORT_CODE(KEYCODE_W)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E  REM     >=     TAN      ATN") PORT_CODE(KEYCODE_E)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R  RUN     <      INT      VERIFY") PORT_CODE(KEYCODE_R)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T  RAND    >      RND      MERGE") PORT_CODE(KEYCODE_T)

	/* interface II uses this port for joystick */
	PORT_START /* [3] 0xF7FE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1          !      BLUE     DEF FN") PORT_CODE(KEYCODE_1)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2          @      RED      FN") PORT_CODE(KEYCODE_2)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3          #      MAGENTA  LINE") PORT_CODE(KEYCODE_3)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4          $      GREEN    OPEN#") PORT_CODE(KEYCODE_4)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5          %      CYAN     CLOSE#") PORT_CODE(KEYCODE_5)

	/* protek clashes with interface II! uses 5 = left, 6 = down, 7 = up, 8 = right, 0 = fire */
	PORT_START /* [4] 0xEFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0          _      BLACK    FORMAT") PORT_CODE(KEYCODE_0)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9          )               POINT") PORT_CODE(KEYCODE_9)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8          (               CAT") PORT_CODE(KEYCODE_8)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7          '      WHITE    ERASE") PORT_CODE(KEYCODE_7)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6          &      YELLOW   MOVE") PORT_CODE(KEYCODE_6)

	PORT_START /* [5] 0xDFFE */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P  PRINT   \"      TAB      (c)") PORT_CODE(KEYCODE_P)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O  POKE    ;      PEEK     OUT") PORT_CODE(KEYCODE_O)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I  INPUT   AT     CODE     IN") PORT_CODE(KEYCODE_I)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U  IF      OR     CHR$     ]") PORT_CODE(KEYCODE_U)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y  RETURN  AND    STR$     [") PORT_CODE(KEYCODE_Y)

	PORT_START /* [6] 0xBFFE */
	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("L  LET     =      USR      ATTR") PORT_CODE(KEYCODE_L)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K  LIST    +      LEN      SCREEN$") PORT_CODE(KEYCODE_K)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J  LOAD    -      VAL      VAL$") PORT_CODE(KEYCODE_J)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H  GOSUB   ^      SQR      CIRCLE") PORT_CODE(KEYCODE_H)

	PORT_START /* [7] 0x7FFE */
	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("SYMBOL SHIFT") PORT_CODE(KEYCODE_RSHIFT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M  PAUSE   .      PI       INVERSE") PORT_CODE(KEYCODE_M)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N  NEXT    ,      INKEY$   OVER") PORT_CODE(KEYCODE_N)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B  BORDER  *      BIN      BRIGHT") PORT_CODE(KEYCODE_B)

	PORT_START /* [8] Spectrum+ Keys (set CAPS + 1-5) */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("EDIT          (CAPS + 1)") PORT_CODE(KEYCODE_INSERT)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS LOCK     (CAPS + 2)") PORT_CODE(KEYCODE_CAPSLOCK)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("TRUE VID      (CAPS + 3)") PORT_CODE(KEYCODE_HOME)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("INV VID       (CAPS + 4)") PORT_CODE(KEYCODE_END)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor left   (CAPS + 5)") PORT_CODE(KEYCODE_LEFT)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START /* [9] Spectrum+ Keys (set CAPS + 6-0) */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("DEL           (CAPS + 0)") PORT_CODE(KEYCODE_BACKSPACE)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("GRAPH         (CAPS + 9)") PORT_CODE(KEYCODE_LALT)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor right  (CAPS + 8)") PORT_CODE(KEYCODE_RIGHT)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor up     (CAPS + 7)") PORT_CODE(KEYCODE_UP)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Cursor down   (CAPS + 6)") PORT_CODE(KEYCODE_DOWN)
	PORT_BIT(0xe0, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START /* [10] Spectrum+ Keys (set 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 /* [11] Spectrum+ Keys (set SYMBOL SHIFT + O/P */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("\"") PORT_CODE(KEYCODE_F4)
	/*		  PORT_BITX(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD, "\"", KEYCODE_QUOTE,  CODE_NONE ) */
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
	PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START /* [12] Spectrum+ Keys (set 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)

	PORT_START /* [13] Kempston joystick interface */
	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KEMPSTON JOYSTICK RIGHT") PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KEMPSTON JOYSTICK LEFT") PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KEMPSTON JOYSTICK DOWN") PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KEMPSTON JOYSTICK UP") PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("KEMPSTON JOYSTICK FIRE") PORT_CODE(JOYCODE_BUTTON1)

	PORT_START /* [14] Fuller joystick interface */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FULLER JOYSTICK UP") PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FULLER JOYSTICK DOWN") PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FULLER JOYSTICK LEFT") PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FULLER JOYSTICK RIGHT") PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("FULLER JOYSTICK FIRE") PORT_CODE(JOYCODE_BUTTON1)

	PORT_START /* [15] Mikrogen joystick interface */
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIKROGEN JOYSTICK UP") PORT_CODE(JOYCODE_Y_UP_SWITCH)
	PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIKROGEN JOYSTICK DOWN") PORT_CODE(JOYCODE_Y_DOWN_SWITCH)
	PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIKROGEN JOYSTICK RIGHT") PORT_CODE(JOYCODE_X_RIGHT_SWITCH)
	PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIKROGEN JOYSTICK LEFT") PORT_CODE(JOYCODE_X_LEFT_SWITCH)
	PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("MIKROGEN JOYSTICK FIRE") PORT_CODE(JOYCODE_BUTTON1)


	PORT_START /* [16] */
	PORT_DIPNAME(0x80, 0x00, "Hardware Version")
	PORT_DIPSETTING(0x00, "Issue 2" )
	PORT_DIPSETTING(0x80, "Issue 3" )
	PORT_DIPNAME(0x40, 0x00, "End of .TAP action")
	PORT_DIPSETTING(0x00, "Disable .TAP support" )
	PORT_DIPSETTING(0x40, "Rewind tape to start (to reload earlier levels)" )
	PORT_DIPNAME(0x20, 0x00, "+3/+2a etc. Disk Drive")
	PORT_DIPSETTING(0x00, "Enabled" )
	PORT_DIPSETTING(0x20, "Disabled" )
	PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_UNUSED)

	PORT_START /* [17] Toggle cassette motor */
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Motor") PORT_CODE(KEYCODE_F1)

INPUT_PORTS_END

static const unsigned char spectrum_palette[16*3] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0xbf,
	0xbf, 0x00, 0x00, 0xbf, 0x00, 0xbf,
	0x00, 0xbf, 0x00, 0x00, 0xbf, 0xbf,
	0xbf, 0xbf, 0x00, 0xbf, 0xbf, 0xbf,

	0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
	0xff, 0x00, 0x00, 0xff, 0x00, 0xff,
	0x00, 0xff, 0x00, 0x00, 0xff, 0xff,
	0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
};

static const unsigned short spectrum_colortable[128*2] = {
	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,

	 8,8,  8,9,  8,10,	8,11,  8,12,  8,13,  8,14,	8,15,
	 9,8,  9,9,  9,10,	9,11,  9,12,  9,13,  9,14,	9,15,
	10,8, 10,9, 10,10, 10,11, 10,12, 10,13, 10,14, 10,15,
	11,8, 11,9, 11,10, 11,11, 11,12, 11,13, 11,14, 11,15,
	12,8, 12,9, 12,10, 12,11, 12,12, 12,13, 12,14, 12,15,
	13,8, 13,9, 13,10, 13,11, 13,12, 13,13, 13,14, 13,15,
	14,8, 14,9, 14,10, 14,11, 14,12, 14,13, 14,14, 14,15,
	15,8, 15,9, 15,10, 15,11, 15,12, 15,13, 15,14, 15,15
};
/* Initialise the palette */
static PALETTE_INIT( spectrum )
{
	palette_set_colors_rgb(machine, 0, spectrum_palette, sizeof(spectrum_palette) / 3);
	memcpy(colortable, spectrum_colortable, sizeof(spectrum_colortable));
}

static INTERRUPT_GEN( spec_interrupt )
{
	cpunum_set_input_line(0, 0, HOLD_LINE);
}



static MACHINE_DRIVER_START( spectrum )
	/* basic machine hardware */
	MDRV_CPU_ADD_TAG("main", Z80, 3500000)        /* 3.5 Mhz */
	MDRV_CPU_PROGRAM_MAP(spectrum_mem, 0)
	MDRV_CPU_IO_MAP(spectrum_io, 0)
	MDRV_CPU_VBLANK_INT(spec_interrupt,1)
	MDRV_SCREEN_REFRESH_RATE(50.08)
	MDRV_SCREEN_VBLANK_TIME(DEFAULT_REAL_60HZ_VBLANK_DURATION)
	MDRV_INTERLEAVE(1)

	MDRV_MACHINE_RESET( spectrum )

    /* video hardware */
	MDRV_VIDEO_ATTRIBUTES(VIDEO_TYPE_RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
	MDRV_SCREEN_SIZE(SPEC_SCREEN_WIDTH, SPEC_SCREEN_HEIGHT)
	MDRV_SCREEN_VISIBLE_AREA(0, SPEC_SCREEN_WIDTH-1, 0, SPEC_SCREEN_HEIGHT-1)
	MDRV_GFXDECODE( spectrum )
	MDRV_PALETTE_LENGTH(16)
	MDRV_COLORTABLE_LENGTH(256)
	MDRV_PALETTE_INIT( spectrum )

	MDRV_VIDEO_START( spectrum )
	MDRV_VIDEO_UPDATE( spectrum )
	MDRV_VIDEO_EOF( spectrum )

	/* sound hardware */
	MDRV_SPEAKER_STANDARD_MONO("mono")
	MDRV_SOUND_ADD(WAVE, 0)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25)
	MDRV_SOUND_ADD(SPEAKER, 0)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( spectrum_128 )
	MDRV_IMPORT_FROM( spectrum )

	MDRV_CPU_REPLACE("main", Z80, 3500000)        /* 3.5 Mhz */
	MDRV_CPU_PROGRAM_MAP(spectrum_128_mem, 0)
	MDRV_CPU_IO_MAP(spectrum_128_io, 0)
	MDRV_SCREEN_REFRESH_RATE(50.021)

	MDRV_MACHINE_RESET( spectrum_128 )

    /* video hardware */
	MDRV_VIDEO_START( spectrum_128 )
	MDRV_VIDEO_UPDATE( spectrum_128 )

	/* sound hardware */
	MDRV_SOUND_ADD(AY8910, 1773400)
	MDRV_SOUND_CONFIG(spectrum_ay_interface)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25)
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( spectrum_plus3 )
	MDRV_IMPORT_FROM( spectrum_128 )
	MDRV_CPU_MODIFY("main")
	MDRV_CPU_IO_MAP(spectrum_plus3_io, 0)
	MDRV_SCREEN_REFRESH_RATE(50.01)

	MDRV_MACHINE_RESET( spectrum_plus3 )
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( ts2068 )
	MDRV_IMPORT_FROM( spectrum_128 )
	MDRV_CPU_REPLACE("main", Z80, 3580000)        /* 3.58 Mhz */
	MDRV_CPU_PROGRAM_MAP(ts2068_mem, 0)
	MDRV_CPU_IO_MAP(ts2068_io, 0)
	MDRV_SCREEN_REFRESH_RATE(60)

	MDRV_MACHINE_RESET( ts2068 )

    /* video hardware */
	MDRV_VIDEO_ATTRIBUTES(VIDEO_TYPE_RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
	MDRV_SCREEN_SIZE(TS2068_SCREEN_WIDTH, TS2068_SCREEN_HEIGHT)
	MDRV_SCREEN_VISIBLE_AREA(0, TS2068_SCREEN_WIDTH-1, 0, TS2068_SCREEN_HEIGHT-1)

	MDRV_VIDEO_UPDATE( ts2068 )
	MDRV_VIDEO_EOF( ts2068 )
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( uk2086 )
	MDRV_IMPORT_FROM( ts2068 )
	MDRV_SCREEN_REFRESH_RATE(50)
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( tc2048 )
	MDRV_IMPORT_FROM( spectrum )
	MDRV_CPU_MODIFY("main")
	MDRV_CPU_PROGRAM_MAP(tc2048_mem, 0)
	MDRV_CPU_IO_MAP(tc2048_io, 0)
	MDRV_SCREEN_REFRESH_RATE(50)

	MDRV_MACHINE_RESET( tc2048 )

    /* video hardware */
	MDRV_VIDEO_ATTRIBUTES(VIDEO_TYPE_RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
	MDRV_SCREEN_SIZE(TS2068_SCREEN_WIDTH, SPEC_SCREEN_HEIGHT)
	MDRV_SCREEN_VISIBLE_AREA(0, TS2068_SCREEN_WIDTH-1, 0, SPEC_SCREEN_HEIGHT-1)

	MDRV_VIDEO_START( spectrum_128 )
	MDRV_VIDEO_UPDATE( tc2048 )
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( scorpion )
	MDRV_IMPORT_FROM( spectrum_128 )
	MDRV_CPU_MODIFY("main")
	MDRV_CPU_IO_MAP(scorpion_io, 0)
	MDRV_MACHINE_RESET( scorpion )
MACHINE_DRIVER_END


static MACHINE_DRIVER_START( pentagon )
	MDRV_IMPORT_FROM( spectrum_128 )
	MDRV_CPU_MODIFY("main")
	MDRV_CPU_IO_MAP(pentagon_io, 0)
	MDRV_MACHINE_RESET( pentagon )
MACHINE_DRIVER_END


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

  Game driver(s)

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

ROM_START(spectrum)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("spectrum.rom", 0x0000, 0x4000, CRC(ddee531f) SHA1(5ea7c2b824672e914525d1d5c419d71b84a426a2))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specbusy)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("48-busy.rom", 0x0000, 0x4000, CRC(1511cddb) SHA1(ab3c36daad4325c1d3b907b6dc9a14af483d14ec))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specpsch)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("48-psych.rom", 0x0000, 0x4000, CRC(cd60b589) SHA1(0853e25857d51dd41b20a6dbc8e80f028c5befaa))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specgrot)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("48-groot.rom", 0x0000, 0x4000, CRC(abf18c45) SHA1(51165cde68e218512d3145467074bc7e786bf307))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specimc)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("48-imc.rom", 0x0000, 0x4000, CRC(d1be99ee) SHA1(dee814271c4d51de257d88128acdb324fb1d1d0d))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(speclec)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("80-lec.rom", 0x0000, 0x4000, CRC(5b5c92b1) SHA1(bb7a77d66e95d2e28ebb610e543c065e0d428619))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(spec128)
	ROM_REGION(0x18000,REGION_CPU1,0)
	ROM_LOAD("zx128_0.rom",0x10000,0x4000, CRC(e76799d2) SHA1(4f4b11ec22326280bdb96e3baf9db4b4cb1d02c5))
	ROM_LOAD("zx128_1.rom",0x14000,0x4000, CRC(b96a36be) SHA1(80080644289ed93d71a1103992a154cc9802b2fa))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(spec128s)
	ROM_REGION(0x18000,REGION_CPU1,0)
	ROM_LOAD("zx128s0.rom",0x10000,0x4000, CRC(453d86b2) SHA1(968937b1c750f0ef6205f01c6db4148da4cca4e3))
	ROM_LOAD("zx128s1.rom",0x14000,0x4000, CRC(6010e796) SHA1(bea3f397cc705eafee995ea629f4a82550562f90))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specpls2)
	ROM_REGION(0x18000,REGION_CPU1,0)
	ROM_LOAD("zxp2_0.rom",0x10000,0x4000, CRC(5d2e8c66) SHA1(72703f9a3e734f3c23ec34c0727aae4ccbef9a91))
	ROM_LOAD("zxp2_1.rom",0x14000,0x4000, CRC(98b1320b) SHA1(de8b0d2d0379cfe7c39322a086ca6da68c7f23cb))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specpl2a)
	ROM_REGION(0x20000,REGION_CPU1,0)
	ROM_LOAD("p2a41_0.rom",0x10000,0x4000, CRC(30c9f490) SHA1(62ec15a4af56cd1d206d0bd7011eac7c889a595d))
	ROM_LOAD("p2a41_1.rom",0x14000,0x4000, CRC(a7916b3f) SHA1(1a7812c383a3701e90e88d1da086efb0c033ac72))
	ROM_LOAD("p2a41_2.rom",0x18000,0x4000, CRC(c9a0b748) SHA1(8df145d10ff78f98138682ea15ebccb2874bf759))
	ROM_LOAD("p2a41_3.rom",0x1c000,0x4000, CRC(b88fd6e3) SHA1(be365f331942ec7ec35456b641dac56a0dbfe1f0))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specpls3)
	ROM_REGION(0x20000,REGION_CPU1,0)
	ROM_LOAD("pl3-0.rom",0x10000,0x4000, CRC(17373da2) SHA1(e319ed08b4d53a5e421a75ea00ea02039ba6555b))
	ROM_LOAD("pl3-1.rom",0x14000,0x4000, CRC(f1d1d99e) SHA1(c9969fc36095a59787554026a9adc3b87678c794))
	ROM_LOAD("pl3-2.rom",0x18000,0x4000, CRC(3dbf351d) SHA1(22e50c6ba4157a3f6a821bd9937cd26e292775c6))
	ROM_LOAD("pl3-3.rom",0x1c000,0x4000, CRC(04448eaa) SHA1(65f031caa8148a5493afe42c41f4929deab26b4e))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specpls4)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("plus4.rom",0x0000,0x4000, CRC(7e0f47cb) SHA1(c103e89ef58e6ade0c01cea0247b332623bd9a30))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(tk90x)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("tk90x.rom",0x0000,0x4000, CRC(3e785f6f) SHA1(9a943a008be13194fb006bddffa7d22d2277813f))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(tk95)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("tk95.rom",0x0000,0x4000, CRC(17368e07) SHA1(94edc401d43b0e9a9cdc1d35de4b6462dc414ab3))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(inves)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("inves.rom",0x0000,0x4000, CRC(8ff7a4d1) SHA1(d020440638aff4d39467128413ef795677be9c23))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(tc2048)
	ROM_REGION(0x10000,REGION_CPU1,0)
	ROM_LOAD("tc2048.rom",0x0000,0x4000, CRC(f1b5fa67) SHA1(febb2d495b6eda7cdcb4074935d6e9d9f328972d))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(ts2068)
	ROM_REGION(0x16000,REGION_CPU1,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,REGION_CPU1,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

ROM_START(specp2fr)
	ROM_REGION(0x18000,REGION_CPU1,0)
	ROM_LOAD("plus2fr0.rom",0x10000,0x4000, CRC(c684c535) SHA1(56684c4c85a616e726a50707483b9a42d8e724ed))
	ROM_LOAD("plus2fr1.rom",0x14000,0x4000, CRC(f5e509c5) SHA1(7e398f62689c9d90a36d3a101351ec9987207308))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specp2sp)
	ROM_REGION(0x18000,REGION_CPU1,0)
	ROM_LOAD("plus2sp0.rom",0x10000,0x4000, CRC(e807d06e) SHA1(8259241b28ff85441f1bedc2bee53445767c51c5))
	ROM_LOAD("plus2sp1.rom",0x14000,0x4000, CRC(41981d4b) SHA1(ec0d5a158842d20601b4fbeaefc6668db979d0e1))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specp3sp)
	ROM_REGION(0x20000,REGION_CPU1,0)
	ROM_LOAD("plus3sp0.rom",0x10000,0x4000, CRC(1f86147a) SHA1(e9b0a60a1a8def511d59090b945d175bdc646346))
	ROM_LOAD("plus3sp1.rom",0x14000,0x4000, CRC(a8ac4966) SHA1(4e48f196427596c7990c175d135c15a039c274a4))
	ROM_LOAD("plus3sp2.rom",0x18000,0x4000, CRC(f6bb0296) SHA1(09fc005625589ef5992515957ce7a3167dec24b2))
	ROM_LOAD("plus3sp3.rom",0x1c000,0x4000, CRC(f6d25389) SHA1(ec8f644a81e2e9bcb58ace974103ea960361bad2))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specpl3e)
	ROM_REGION(0x20000,REGION_CPU1,0)
	ROM_LOAD("roma-en.rom",0x10000,0x8000, CRC(2d533344) SHA1(5ff2dae32eb745d87e0b54c595d1d20a866f316f))
	ROM_LOAD("romb-en.rom",0x18000,0x8000, CRC(ef8d5d92) SHA1(983aa53aa76e25a3af123c896016bacf6829b72b))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(specp3es)
	ROM_REGION(0x20000,REGION_CPU1,0)
	ROM_LOAD("roma-es.rom",0x10000,0x8000, CRC(ba694b4b) SHA1(d15d9e43950483cffc79f1cfa89ecb114a88f6c2))
	ROM_LOAD("romb-es.rom",0x18000,0x8000, CRC(61ed94db) SHA1(935b14c13db75d872de8ad0d591aade0adbbc355))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(scorpion)
	ROM_REGION(0x020000, REGION_CPU1, 0)
	ROM_LOAD("scorp0.rom",0x010000, 0x4000, CRC(0eb40a09))
	ROM_LOAD("scorp1.rom",0x014000, 0x4000, CRC(9d513013))
	ROM_LOAD("scorp2.rom",0x018000, 0x4000, CRC(fd0d3ce1))
	ROM_LOAD("scorp3.rom",0x01c000, 0x4000, CRC(1fe1d003))
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

ROM_START(pentagon)
	ROM_REGION(0x020000, REGION_CPU1, 0)
	ROM_CART_LOAD(0, "rom", 0x0000, 0x4000, ROM_NOCLEAR | ROM_NOMIRROR | ROM_OPTIONAL)
ROM_END

static void spectrum_common_cassette_getinfo(const device_class *devclass, UINT32 state, union devinfo *info)
{
	/* cassette */
	switch(state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case DEVINFO_INT_COUNT:				info->i = 1; break;
		case DEVINFO_INT_CASSETTE_DEFAULT_STATE:	info->i = CASSETTE_PLAY | CASSETTE_SPEAKER_ENABLED | CASSETTE_MOTOR_DISABLED; break;
		case DEVINFO_PTR_CASSETTE_FORMATS:		info->p = (void *)tzx_cassette_formats; break;
		default:					cassette_device_getinfo(devclass, state, info); break;
	}
}

static void spectrum_common_snapshot_getinfo(const device_class *devclass, UINT32 state, union devinfo *info)
{
	/* snapshot */
	switch(state)
	{
		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_FILE_EXTENSIONS:				strcpy(info->s = device_temp_str(), "sna,z80,sp"); break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_PTR_SNAPSHOT_LOAD:					info->f = (genf *) snapshot_load_spectrum; break;

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

static void spectrum_common_quickload_getinfo(const device_class *devclass, UINT32 state, union devinfo *info)
{
	/* quickload */
	switch(state)
	{
		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_FILE_EXTENSIONS:				strcpy(info->s = device_temp_str(), "scr"); break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_PTR_QUICKLOAD_LOAD:				info->f = (genf *) quickload_load_spectrum; break;

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

SYSTEM_CONFIG_START(spectrum_common)
	CONFIG_DEVICE(spectrum_common_cassette_getinfo)
	CONFIG_DEVICE(spectrum_common_snapshot_getinfo)
	CONFIG_DEVICE(spectrum_common_quickload_getinfo)
SYSTEM_CONFIG_END

SYSTEM_CONFIG_START(spectrum)
	CONFIG_IMPORT_FROM(spectrum_common)
	CONFIG_DEVICE(cartslot_device_getinfo)
SYSTEM_CONFIG_END

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

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

SYSTEM_CONFIG_START(specpls3)
	CONFIG_IMPORT_FROM(spectrum_common)
	CONFIG_DEVICE(specpls3_floppy_getinfo)
SYSTEM_CONFIG_END

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

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_PTR_LOAD:							info->load = device_load_timex_cart; break;
		case DEVINFO_PTR_UNLOAD:						info->unload = device_unload_timex_cart; break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_FILE_EXTENSIONS:				strcpy(info->s = device_temp_str(), "dck"); break;

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

SYSTEM_CONFIG_START(ts2068)
	CONFIG_IMPORT_FROM(spectrum_common)
	CONFIG_DEVICE(ts2068_cartslot_getinfo)
	CONFIG_RAM_DEFAULT(48 * 1024)
SYSTEM_CONFIG_END

SYSTEM_CONFIG_START(tc2048)
	CONFIG_IMPORT_FROM(spectrum)
	CONFIG_RAM_DEFAULT(48 * 1024)
SYSTEM_CONFIG_END

/*    YEAR  NAME      PARENT    COMPAT	MACHINE		INPUT		INIT	CONFIG		COMPANY		FULLNAME */
COMP( 1982, spectrum, 0,        0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum" , 0)
COMP( 2000, specpls4, spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum +4", GAME_COMPUTER_MODIFIED )
COMP( 1994, specbusy, spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum (BusySoft Upgrade v1.18)", GAME_COMPUTER_MODIFIED )
COMP( ????, specpsch, spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum (Maly's Psycho Upgrade)", GAME_COMPUTER_MODIFIED )
COMP( ????, specgrot, spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum (De Groot's Upgrade)", GAME_COMPUTER_MODIFIED )
COMP( 1985, specimc,  spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum (Collier's Upgrade)", GAME_COMPUTER_MODIFIED )
COMP( 1987, speclec,  spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Sinclair Research",	"ZX Spectrum (LEC Upgrade)", GAME_COMPUTER_MODIFIED )
COMP( 1986, inves,    spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Investronica",	"Inves Spectrum 48K+" , 0)
COMP( 1985, tk90x,    spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Micro Digital",	"TK-90x Color Computer" , 0)
COMP( 1986, tk95,     spectrum, 0,		spectrum,		spectrum,	0,		spectrum,	"Micro Digital",	"TK-95 Color Computer" , 0)
COMP( 1984, tc2048,   spectrum, 0,		tc2048,			spectrum,	0,		tc2048,		"Timex of Portugal",	"TC-2048" , 0)
COMP( 1983, ts2068,   spectrum, 0,		ts2068,			spectrum,	0,		ts2068,		"Timex Sinclair",	"TS-2068" , 0)
COMP( 1986, uk2086,   spectrum, 0,		uk2086,			spectrum,	0,		ts2068,		"Unipolbrit",	"UK-2086 ver. 1.2" , 0)

COMP( 1986, spec128,  0,		 0,		spectrum_128,	spectrum,	0,		spectrum,	"Sinclair Research",    "ZX Spectrum 128" ,GAME_NOT_WORKING)
COMP( 1985, spec128s, spec128,  0,		spectrum_128,	spectrum,	0,		spectrum,	"Sinclair Research",    "ZX Spectrum 128 (Spain)" ,GAME_NOT_WORKING)
COMP( 1986, specpls2, spec128,  0,		spectrum_128,	spectrum,	0,		spectrum,	"Amstrad plc",          "ZX Spectrum +2" ,GAME_NOT_WORKING)
COMP( 1987, specpl2a, spec128,  0,		spectrum_plus3,spectrum,	0,		specpls3,	"Amstrad plc",          "ZX Spectrum +2a" ,GAME_NOT_WORKING)
COMP( 1987, specpls3, spec128,  0,		spectrum_plus3,spectrum,	0,		specpls3,	"Amstrad plc",          "ZX Spectrum +3" ,GAME_NOT_WORKING)

COMP( 1986, specp2fr, spec128,  0,		spectrum_128,	spectrum,	0,		spectrum,	"Amstrad plc",          "ZX Spectrum +2 (France)" ,GAME_NOT_WORKING)
COMP( 1986, specp2sp, spec128,  0,		spectrum_128,	spectrum,	0,		spectrum,	"Amstrad plc",          "ZX Spectrum +2 (Spain)" ,GAME_NOT_WORKING)
COMP( 1987, specp3sp, spec128,  0,		spectrum_plus3,spectrum,	0,		specpls3,	"Amstrad plc",          "ZX Spectrum +3 (Spain)" ,GAME_NOT_WORKING)
COMP( 2000, specpl3e, spec128,  0,		spectrum_plus3,spectrum,	0,		specpls3,	"Amstrad plc",          "ZX Spectrum +3e" , GAME_NOT_WORKING|GAME_COMPUTER_MODIFIED )
COMP( 2000, specp3es, spec128,  0,		spectrum_plus3,spectrum,	0,		specpls3,	"Amstrad plc",          "ZX Spectrum +3e (Spain)" , GAME_NOT_WORKING|GAME_COMPUTER_MODIFIED )

COMP( ????, scorpion, 0,		 0,		scorpion,		spectrum,	0,		specpls3,	"Zonov and Co.",		"Zs Scorpion 256", GAME_NOT_WORKING)
COMP( ????, pentagon, spectrum, 0,		pentagon,		spectrum,	0,		specpls3,	"???",		"Pentagon", GAME_NOT_WORKING)
