// license:GPL-2.0+
// copyright-holders:Kevin Thacker
/***************************************************************************

  spec_snqk.cpp

  TODO:

    - Implement the following snapshot formats
      .89C (Tezxas)
      .SLT (Various emulators)
      .SZX (Spectaculator)
      .ZLS (ZX-Live!)
      .ZX (ZXSpectr)
      .ZX82 (Speculator '97)
      .ZXS (ZX32)

    - Implement the following quickload formats
      .$? (HoBeta)
      .__? (SpecEm)
      .BAS (BASin)
      .H/.B (Roman ZX)
      .RAW (Various emulators)
      .SCR variants

    - Cleanup of the .Z80 format

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

#include "emu.h"
#include "ui/uimain.h"
#include "cpu/z80/z80.h"
#include "includes/spectrum.h"
#include "includes/spec128.h"
#include "includes/timex.h"
#include "includes/specpls3.h"
#include "sound/ay8910.h"
#include "machine/spec_snqk.h"

#define EXEC_NA "N/A"

//-------------------------------------------------
//  log_quickload - logs and displays useful
//  data for the end user
//-------------------------------------------------

void spectrum_state::log_quickload(const char *type, uint32_t start, uint32_t length, uint32_t exec, const char *exec_format)
{
	std::ostringstream tempstring;

	logerror("Loading %04X bytes of RAM at %04X\n", length, start);

	util::stream_format(tempstring, "Quickload type: %s   Length: %d bytes\n", type, length);
	util::stream_format(tempstring, "Start: 0x%04X   End: 0x%04X   Exec: ", start, start + length - 1);

	logerror("Quickload loaded.\n");
	if (!core_stricmp(exec_format, EXEC_NA))
		tempstring << "N/A";
	else
	{
		logerror("Execution can resume with ");
		logerror(exec_format, exec);
		logerror("\n");
		util::stream_format(tempstring, exec_format, exec);
	}

	machine().ui().popup_time(10, "%s", tempstring.str());
}

/*******************************************************************
 *
 *      Update the memory and paging of the spectrum being emulated
 *
 *      if port_7ffd_data is -1 then machine is 48K - no paging
 *      if port_1ffd_data is -1 then machine is 128K
 *      if neither port is -1 then machine is +2a/+3
 *
 *      Note: the 128K .SNA and .Z80 file formats do not store the
 *      port 1FFD setting so it is necessary to calculate the appropriate
 *      value for the ROM paging.
 *
 *******************************************************************/
void spectrum_state::update_paging()
{
	if (m_port_7ffd_data == -1)
		return;
	if (m_port_1ffd_data == -1)
		spectrum_128_update_memory();

	else
	{
		if (BIT(m_port_7ffd_data, 4))
			// Page in Spec 48K basic ROM
			m_port_1ffd_data = 0x04;
		else
			m_port_1ffd_data = 0x00;
		spectrum_plus3_update_memory();
	}
}

/* Page in the 48K Basic ROM. Used when running 48K snapshots on a 128K machine. */
void spectrum_state::page_basicrom()
{
	if (m_port_7ffd_data == -1)
		return;
	m_port_7ffd_data |= 0x10;
	update_paging();
}


SNAPSHOT_LOAD_MEMBER( spectrum_state,spectrum)
{
	std::vector<uint8_t> snapshot_data(snapshot_size);

	image.fread(&snapshot_data[0], snapshot_size);

	if (!core_stricmp(file_type, "sna"))
	{
		if ((snapshot_size != SNA48_SIZE) && (snapshot_size != SNA128_SIZE_1) && (snapshot_size != SNA128_SIZE_2))
		{
			logerror("Invalid .SNA file size.\n");
			goto error;
		}
		setup_sna(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "sp"))
	{
		if ((snapshot_data[0] != 'S' && snapshot_data[1] != 'P') && (snapshot_size != SP_NEW_SIZE_16K && snapshot_size != SP_NEW_SIZE_48K))
		{
			if (snapshot_size != SP_OLD_SIZE)
			{
				logerror("Invalid .SP file size.\n");
				goto error;
			}
		}
		setup_sp(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "ach"))
	{
		if (snapshot_size != ACH_SIZE)
		{
			logerror("Invalid .ACH file size.\n");
			goto error;
		}
		setup_ach(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "prg"))
	{
		if (snapshot_size != PRG_SIZE)
		{
			logerror("Invalid .PRG file size.\n");
			goto error;
		}
		setup_prg(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "plusd"))
	{
		if ((snapshot_size != PLUSD48_SIZE) && (snapshot_size != PLUSD128_SIZE))
		{
			logerror("Invalid .PLUSD file size.\n");
			goto error;
		}
		setup_plusd(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "sem"))
	{
		if (snapshot_data[0] != 0x05 && snapshot_data[1] != 'S' && \
			snapshot_data[2] != 'P' && snapshot_data[3] != 'E' && \
			snapshot_data[4] != 'C' && snapshot_data[5] != '1')
		{
			if (snapshot_size != SEM_SIZE)
			{
				logerror("Invalid .SEM file size.\n");
				goto error;
			}
		}
		setup_sem(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "sit"))
	{
		if (snapshot_size != SIT_SIZE)
		{
			logerror("Invalid .SIT file size.\n");
			goto error;
		}
		setup_sit(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "zx"))
	{
		if (snapshot_size != ZX_SIZE)
		{
			logerror("Invalid .ZX file size.\n");
			goto error;
		}
		setup_zx(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "snp"))
	{
		if (snapshot_size != SNP_SIZE)
		{
			logerror("Invalid .SNP file size.\n");
			goto error;
		}
		setup_snp(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "snx"))
	{
		if (snapshot_data[0] != 'X' && snapshot_data[1] != 'S' && \
			snapshot_data[2] != 'N' && snapshot_data[3] != 'A')
		{
			logerror("Invalid .SNX file size.\n");
			goto error;
		}
		setup_snx(&snapshot_data[0], snapshot_size);
	}
	else if (!core_stricmp(file_type, "frz"))
	{
		if (snapshot_size != FRZ_SIZE)
		{
			logerror("Invalid .FRZ file size.\n");
			goto error;
		}
		setup_frz(&snapshot_data[0], snapshot_size);
	}
	else
	{
		setup_z80(&snapshot_data[0], snapshot_size);
	}

	return image_init_result::PASS;

error:
	return image_init_result::FAIL;
}

/*******************************************************************
 *
 *      Load a .SP file.
 *
 *      There are two kinds of .SP files: "old" and "new".
 *
 *      The old version is always 49184 bytes long and is created
 *      by a leaked copy of the VGASpec emulator.
 *
 *      Subsequently Pedro Gimeno (the author of VGASpec) renamed it
 *      to "Spectrum" (but it's colloquially known as the "Spanish
 *      Spectrum emulator") and extended the header to break backward
 *      compatibility: the new format supports both 16K and 48K
 *      images and it's 16422 or 49190 bytes long.
 *
 *      http://www.formauri.es/personal/pgimeno/spec/spec.html
 *
 *      The formats are defined as follows:
 *
 *      Offset              Size    Description (all registers stored with LSB first)
 *      ------------------- ------- -------------------------------------------------
 *      VGASPEC SPECTRUM
 *      ------------------- ------- -------------------------------------------------
 *      N/A     0           2       "SP" (signature)
 *      N/A     2           2       Program length
 *      N/A     4           2       Program location
 *      0       6           2       BC
 *      2       8           2       DE
 *      4       10          2       HL
 *      6       12          2       AF
 *      8       14          2       IX
 *      10      16          2       IY
 *      12      18          2       BC'
 *      14      20          2       DE'
 *      16      22          2       HL'
 *      18      24          2       AF'
 *      20      26          1       R
 *      21      27          1       I
 *      22      28          2       SP
 *      24      30          2       PC
 *      26      32          2       0x00 (reserved for future use)
 *      28      34          1       Border color
 *      29      35          1       0x00 (reserved for future use)
 *      30      36          2       Status word
 *      32      38          16384/  RAM dump
 *                          49152
 *
 *  Status word:
 *  Bit     Description
 *  ------- ---------------------------------------------------
 *  15-8    Reserved for future use
 *  7-6     Reserved for internal use (0)
 *  5       Status of the flash attribute: 0=normal/1=inverse
 *  4       Interrupt pending for execution
 *  3       If 1, IM 0; if 0, bit 1 determines interrupt mode
 *              (Spectrum v0.99e had this behaviour reversed,
 *              and this bit was not used in versions previous
 *              to v 0.99e)
 *  2       IFF2: 0=DI/1=EI
 *  1       Interrupt mode (if bit 3 reset): 0=IM1/1=IM2
 *  0       IFF1: 0=DI/1=EI
 *
 *******************************************************************/
void spectrum_state::border_update(int data)
{
#if 0
	spectrum_EventList_Reset(machine());
	spectrum_border_set_last_color(machine(), data);
	spectrum_border_force_redraw(machine());
#endif
}

void spectrum_state::setup_sp(uint8_t *snapdata, uint32_t snapsize)
{
	int i, SP_OFFSET;
	uint8_t intr;
	uint16_t start, size, data, status;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	if (snapsize == SP_NEW_SIZE_16K || snapsize == SP_NEW_SIZE_48K)
	{
		SP_OFFSET = 0;
		start = (snapdata[SP_OFFSET +  5] << 8) | snapdata[SP_OFFSET +  4];
		size = (snapdata[SP_OFFSET +  3] << 8) | snapdata[SP_OFFSET +  2];
	}
	else
	{
		SP_OFFSET = SP_OLD_HDR - SP_NEW_HDR;
		start = BASE_RAM;
		size = 3*SPECTRUM_BANK;
	}

	data = (snapdata[SP_OFFSET + 13] << 8) | snapdata[SP_OFFSET + 12];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[SP_OFFSET +  7] << 8) | snapdata[SP_OFFSET +  6];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[SP_OFFSET +  9] << 8) | snapdata[SP_OFFSET +  8];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[SP_OFFSET + 11] << 8) | snapdata[SP_OFFSET + 10];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[SP_OFFSET + 25] << 8) | snapdata[SP_OFFSET + 24];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[SP_OFFSET + 19] << 8) | snapdata[SP_OFFSET + 18];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[SP_OFFSET + 21] << 8) | snapdata[SP_OFFSET + 20];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[SP_OFFSET + 23] << 8) | snapdata[SP_OFFSET + 22];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[SP_OFFSET + 15] << 8) | snapdata[SP_OFFSET + 14];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[SP_OFFSET + 17] << 8) | snapdata[SP_OFFSET + 16];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[SP_OFFSET + 26];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[SP_OFFSET + 27];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[SP_OFFSET + 29] << 8) | snapdata[SP_OFFSET + 28];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[SP_OFFSET + 31] << 8) | snapdata[SP_OFFSET + 30];
	m_maincpu->set_state_int(Z80_PC, data);


	status = (snapdata[SP_OFFSET + 37] << 8) | snapdata[SP_OFFSET + 36];

	data = (BIT(status, 3) << 1) | BIT(status, 1);
	switch(data)
	{
		case 0: // case 2: in version 0.99e of the emulator
		m_maincpu->set_state_int(Z80_IM, 1);
		break;
		case 1: // case 3: in version 0.99e of the emulator
		m_maincpu->set_state_int(Z80_IM, 2);
		break;
		case 2: // case 0: in version 0.99e of the emulator
		case 3: // case 1: in version 0.99e of the emulator
		m_maincpu->set_state_int(Z80_IM, (uint64_t)0);
	}

	data = BIT(status, 0);
	m_maincpu->set_state_int(Z80_IFF1, data);

	data = BIT(status, 2);
	m_maincpu->set_state_int(Z80_IFF2, data);

	intr = BIT(status, 4) ? ASSERT_LINE : CLEAR_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	data = BIT(status, 5);
	m_flash_invert = data;
	logerror("FLASH state: %s\n", data ? "PAPER on INK" : "INK on PAPER");

	// Memory dump
	logerror("Loading %04X bytes of RAM at %04X\n", size, start);
	for (i = 0; i < size; i++)
		space.write_byte(start + i, snapdata[SP_OFFSET + SP_NEW_HDR + i]);

	// Set border color
	data = snapdata[SP_OFFSET + 34] & 0x07;
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .SNA file.
 *
 *      This format was used by Arnt Gulbrandsen for its JPP
 *      emulator, and it's based on the format used by the Mirage
 *      Microdriver, a Microdrive backup accessory.
 *
 *      http://www.worldofspectrum.org/infoseekid.cgi?id=1000266
 *
 *      The 48K image is always 49179 bytes long.
 *
 *      .SNA snapshots save the PC on the top of the stack, thus
 *      deleting the original bytes: not a good feature for a snapshot...
 *
 *      It's been reported that snapshots saved at critical moments would
 *      subsequently crash on restore due to the fact that the stack was full
 *      or pointed to a ROM address. A workaround for this situation is to
 *      store 0x0000 into the word where the PC was located.
 *
 *      For sake of fidelity, the code that zeroes the top of the stack has
 *      been written but commented out.
 *
 *      Later, the format has been extended to store the whole Spectrum 128 status.
 *
 *      48K format as follows:
 *
 *      Offset  Size    Description (all registers stored with LSB first)
 *      ------- ------- -------------------------------------------------
 *      0       1       I
 *      1       2       HL'
 *      3       2       DE'
 *      5       2       BC'
 *      7       2       AF'
 *      9       2       HL
 *      11      2       DE
 *      13      2       BC
 *      15      2       IY
 *      17      2       IX
 *      19      1       IFF1/2: bit 0 contains IFF1, bit 2 contains IFF2 (0=DI/1=EI)
 *      20      1       R
 *      21      2       AF
 *      23      2       SP
 *      25      1       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *      26      1       Border color
 *      27      49152   RAM dump
 *
 *      PC is stored on stack.
 *
 *      128K format as follows:
 *
 *      Offset  Size    Description (all registers stored with LSB first)
 *      ------- ------- -------------------------------------------------
 *      0       1       I
 *      1       2       HL'
 *      3       2       DE'
 *      5       2       BC'
 *      7       2       AF'
 *      9       2       HL
 *      11      2       DE
 *      13      2       BC
 *      15      2       IY
 *      17      2       IX
 *      19      1       IFF1/2: bit 0 contains IFF1, bit 2 contains IFF2 (0=DI/1=EI)
 *      20      1       R
 *      21      2       AF
 *      23      2       SP
 *      25      1       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *      26      1       Border color
 *      27      16384   RAM bank 5 (0x4000-0x7fff)
 *      16411   16384   RAM bank 2 (0x8000-0xbfff)
 *      32795   16384   RAM bank n (0xc000-0xffff - currently paged bank)
 *      49179   2       PC
 *      49181   1       Port 0x7FFD data
 *      49182   1       TR-DOS rom paged (0=no/1=yes)
 *      49183   81920/  Remaining RAM banks in ascending order
 *              98304
 *
 *      PC is NOT stored on stack.
 *
 *      The bank in 0xc000 is always included even if it is page 2 or 5
 *      in which case it is included twice.
 *
 *******************************************************************/
void spectrum_state::setup_sna(uint8_t *snapdata, uint32_t snapsize)
{
	int i, j, usedbanks[8];
	long bank_offset;
	uint8_t intr;
	uint16_t data, addr;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	if ((snapsize != SNA48_SIZE) && (m_port_7ffd_data == -1))
	{
		logerror("Can't load 128K .SNA file into 48K machine\n");
		return;
	}


	data = (snapdata[SNA48_OFFSET + 22] << 8) | snapdata[SNA48_OFFSET + 21];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[SNA48_OFFSET + 14] << 8) | snapdata[SNA48_OFFSET + 13];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[SNA48_OFFSET + 12] << 8) | snapdata[SNA48_OFFSET + 11];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[SNA48_OFFSET + 10] << 8) | snapdata[SNA48_OFFSET +  9];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[SNA48_OFFSET +  8] << 8) | snapdata[SNA48_OFFSET +  7];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[SNA48_OFFSET +  6] << 8) | snapdata[SNA48_OFFSET +  5];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[SNA48_OFFSET +  4] << 8) | snapdata[SNA48_OFFSET +  3];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[SNA48_OFFSET +  2] << 8) | snapdata[SNA48_OFFSET +  1];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[SNA48_OFFSET + 18] << 8) | snapdata[SNA48_OFFSET + 17];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[SNA48_OFFSET + 16] << 8) | snapdata[SNA48_OFFSET + 15];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[SNA48_OFFSET + 20];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[SNA48_OFFSET +  0];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[SNA48_OFFSET + 24] << 8) | snapdata[SNA48_OFFSET + 23];
	m_maincpu->set_state_int(Z80_SP, data);


	data = snapdata[SNA48_OFFSET + 25] & 0x03;
	if (data == 3)
		data = 2;
	m_maincpu->set_state_int(Z80_IM, data);

	data = snapdata[SNA48_OFFSET + 19];
	m_maincpu->set_state_int(Z80_IFF1, BIT(data, 0));
	m_maincpu->set_state_int(Z80_IFF2, BIT(data, 2));

	intr = BIT(data, 0) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	if (snapsize == SNA48_SIZE)
		// 48K Snapshot
		page_basicrom();
	else
	{
		// 128K Snapshot
		m_port_7ffd_data = snapdata[SNA128_OFFSET + 2];
		logerror ("Port 7FFD:%02X\n", m_port_7ffd_data);
		if (snapdata[SNA128_OFFSET + 3])
		{
			/* TODO: page TR-DOS ROM when supported */
		}
		update_paging();
	}

	if (snapsize == SNA48_SIZE)
	{
		// Memory dump
		logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
		for (i = 0; i < 3*SPECTRUM_BANK; i++)
			space.write_byte(BASE_RAM + i, snapdata[SNA48_HDR + i]);

		// Get PC from stack
		addr = m_maincpu->state_int(Z80_SP);

		if (addr < BASE_RAM || addr > 4*SPECTRUM_BANK - 2)
			logerror("Corrupted SP out of range:%04X", addr);
		else
			logerror("Fetching PC from the stack at SP:%04X\n", addr);

		data = (space.read_byte(addr + 1) << 8) | space.read_byte(addr + 0);
		m_maincpu->set_state_int(Z80_PC, data);

#if 0
		space.write_byte(addr + 0, 0); // It's been reported that zeroing these locations fixes the loading
		space.write_byte(addr + 1, 0); // of a few images that were snapshot at a "wrong" instant
#endif

		addr += 2;
		logerror("Fixing SP:%04X\n", addr);
		m_maincpu->set_state_int(Z80_SP, addr);

		// Set border color
		data = snapdata[SNA48_OFFSET + 26] & 0x07;
		m_port_fe_data = (m_port_fe_data & 0xf8) | data;
		border_update(data);
		logerror("Border color:%02X\n", data);

		//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
	}
	else
	{
		// Set up other RAM banks
		for (i = 0; i < 8; i++)
			usedbanks[i] = 0;

		usedbanks[5] = 1;                               // 0x4000-0x7fff
		usedbanks[2] = 1;                               // 0x8000-0xbfff
		usedbanks[m_port_7ffd_data & 0x07] = 1;    // Banked memory

		logerror("Loading %05X bytes of RAM at %04X\n", 8*SPECTRUM_BANK, BASE_RAM);
		logerror("Loading bank 5 from offset:0001B\n");
		logerror("Loading bank 2 from offset:0401B\n");
		logerror("Loading bank %d from offset:0801B\n", snapdata[SNA128_OFFSET + 2] & 0x07);
		for (i = 0; i < 3*SPECTRUM_BANK; i++)
			space.write_byte(BASE_RAM + i, snapdata[SNA48_HDR + i]);

		bank_offset = SNA48_SIZE + SNA128_HDR;
		for (i = 0; i < 8; i++)
		{
			if (!usedbanks[i])
			{
				logerror("Loading bank %d from offset:%05lX\n", i, bank_offset);
				m_port_7ffd_data &= 0xf8;
				m_port_7ffd_data += i;
				update_paging();
				for (j = 0; j < SPECTRUM_BANK; j++)
					space.write_byte(j + 3*SPECTRUM_BANK, snapdata[bank_offset + j]);
				bank_offset += SPECTRUM_BANK;
			}
		}

		// program counter
		data = (snapdata[SNA128_OFFSET + 1] << 8) | snapdata[SNA128_OFFSET + 0];
		m_maincpu->set_state_int(Z80_PC, data);

		// Set border color
		data = snapdata[SNA48_OFFSET + 26] & 0x07;
		m_port_fe_data = (m_port_fe_data & 0xf8) | data;
		border_update(data);
		logerror("Border color:%02X\n", data);
		data = m_port_7ffd_data & 0x07;

		// Reset paging
		m_port_7ffd_data = snapdata[SNA128_OFFSET + 2];
		update_paging();

		//logerror("Snapshot loaded.\nExecution resuming at bank:%d %s\n", data, m_maincpu->state_string(Z80_PC).c_str());
	}
}

/*******************************************************************
 *
 *      Load a .ACH file.
 *
 *      .ACH files were produced by a commercial Archimedes
 *      (hence the name) emulator called !Speccy.
 *
 *      The files are always 65792 bytes long and store both
 *      a copy of the ROM and the whole 48K RAM.
 *
 *
 *      Offset  Size    Description (all registers stored with LSB first
 *                      except when noted with *BE*)
 *      ------- ------- -------------------------------------------------
 *      0       4       A
 *      4       4       F
 *      8       4       B
 *      12      4       C
 *      16      4       D
 *      20      4       E
 *      24      4       H
 *      28      4       L
 *      32      4       PC
 *      36      4       0x00 (reserved for future use)
 *      40      4       SP
 *      44      104     0x00 (reserved for future use)
 *      148     4       R
 *      152     4       0x00 (reserved for future use)
 *      156     4       Border color
 *      160     4       0x00 (reserved for future use)
 *      164     2       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *      166     24      0x00 (reserved for future use)
 *      190     1       I
 *      191     1       IFF1/2: (0=DI/1=EI)
 *      192     44      0x00 (reserved for future use)
 *      236     4       AF' *BE*
 *      240     4       BC' *BE*
 *      244     2       DE' *BE*
 *      246     2       HL' *BE*
 *      248     4       IX
 *      252     4       IY
 *      256     16384   ROM dump
 *      16640   49152   RAM dump
 *
 *******************************************************************/
void spectrum_state::setup_ach(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t intr;
	uint16_t data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = (snapdata[ACH_OFFSET +   0] << 8) | snapdata[ACH_OFFSET +   4];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[ACH_OFFSET +   8] << 8) | snapdata[ACH_OFFSET +  12];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[ACH_OFFSET +  16] << 8) | snapdata[ACH_OFFSET +  20];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[ACH_OFFSET +  24] << 8) | snapdata[ACH_OFFSET +  28];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[ACH_OFFSET + 236] << 8) | snapdata[ACH_OFFSET + 237];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[ACH_OFFSET + 240] << 8) | snapdata[ACH_OFFSET + 241];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[ACH_OFFSET + 244] << 8) | snapdata[ACH_OFFSET + 245];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[ACH_OFFSET + 246] << 8) | snapdata[ACH_OFFSET + 247];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[ACH_OFFSET + 249] << 8) | snapdata[ACH_OFFSET + 248];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[ACH_OFFSET + 253] << 8) | snapdata[ACH_OFFSET + 252];
	m_maincpu->set_state_int(Z80_IY, data);

	data = snapdata[ACH_OFFSET + 148];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[ACH_OFFSET + 190];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[ACH_OFFSET +  41] << 8) | snapdata[ACH_OFFSET +  40];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[ACH_OFFSET +  33] << 8) | snapdata[ACH_OFFSET +  32];
	m_maincpu->set_state_int(Z80_PC, data);

	data = snapdata[ACH_OFFSET + 164] & 0x03;
	if (data == 3)
		data = 0;
	m_maincpu->set_state_int(Z80_IM, data);

	data = snapdata[ACH_OFFSET + 191] ? 1 : 0;
	m_maincpu->set_state_int(Z80_IFF1, data);
	m_maincpu->set_state_int(Z80_IFF2, data);

	intr = snapdata[ACH_OFFSET + 191] ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	logerror("Skipping the 16K ROM dump at offset:%04X\n", ACH_OFFSET + 256);

	// Memory dump
	logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 3*SPECTRUM_BANK; i++)
		space.write_byte(BASE_RAM + i, snapdata[ACH_HDR + SPECTRUM_BANK + i]);

	// Set border color
	data = snapdata[ACH_OFFSET + 156] & 0x07;
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .PRG file.
 *
 *      .PRG files were produced by an irish emulator called SpecEm
 *      written by Kevin Phair (thanks for the help, Kevin!)
 *
 *      http://code.google.com/p/specem/
 *
 *      The files are always 49408 bytes long and use some stack space
 *      to store registers.
 *
 *      The header actually mimics the file descriptor format used by
 *      the DISCiPLE/+D interface.
 *
 *      Offset  Size    Description (all registers stored with LSB first
 *                      except when noted with *BE*)
 *      ------- ------- -------------------------------------------------
 *      0       1       File type (always 0x05, corresponding to "48K Snapshot"
 *      1       10      Program name (padded with 0x20)
 *      11      2       Number of sectors occupied by the file *BE*
 *      13      1       Track number of the first sector of the file
 *      14      1       Sector number of the first sector of the file
 *      15      195     Sector allocation bitmap
 *      210     10      0x00 (reserved for future use)
 *      220     2       IY
 *      222     2       IX
 *      224     2       DE'
 *      226     2       BC'
 *      228     2       HL'
 *      230     2       AF'
 *      232     2       DE
 *      234     2       BC
 *      236     2       HL
 *      238     1       Junk created when saving I via LD A,I/PUSH AF
 *      239     1       I
 *      240     2       SP
 *      242     14      0x00 (reserved for future use)
 *      256     49152   RAM dump
 *
 *      IFF1/2, R, AF and PC are stored on the stack. Due to a bug in the
 *      BIOS, the snapshots created with the DISCiPLE have the AF' register
 *      replaced with the AF register.
 *
 *      It's unknown (but likely possible) that the snapshots could
 *      suffer from the same "top of the stack" bug as well as .SNA images.
 *
 *******************************************************************/
void spectrum_state::setup_prg(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t intr;
	uint16_t addr, data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = snapdata[PRG_OFFSET +   0];
	if (data != 0x05)
		logerror("Wrong DISCiPLE/+D file type: %02X instead of 05\n", data);

	data = (snapdata[PRG_OFFSET + 235] << 8) | snapdata[PRG_OFFSET + 234];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[PRG_OFFSET + 233] << 8) | snapdata[PRG_OFFSET + 232];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[PRG_OFFSET + 237] << 8) | snapdata[PRG_OFFSET + 236];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[PRG_OFFSET + 231] << 8) | snapdata[PRG_OFFSET + 230];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[PRG_OFFSET + 227] << 8) | snapdata[PRG_OFFSET + 226];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[PRG_OFFSET + 225] << 8) | snapdata[PRG_OFFSET + 224];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[PRG_OFFSET + 229] << 8) | snapdata[PRG_OFFSET + 228];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[PRG_OFFSET + 223] << 8) | snapdata[PRG_OFFSET + 222];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[PRG_OFFSET + 221] << 8) | snapdata[PRG_OFFSET + 220];
	m_maincpu->set_state_int(Z80_IY, data);

	data = snapdata[PRG_OFFSET + 239];
	m_maincpu->set_state_int(Z80_I, data);

	m_maincpu->set_state_int(Z80_IM, (data == 0x00 || data == 0x3f) ? 1 : 2);

	// Memory dump
	logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 3*SPECTRUM_BANK; i++)
		space.write_byte(BASE_RAM + i, snapdata[PRG_HDR + i]);

	addr = (snapdata[PRG_OFFSET + 241] << 8) | snapdata[PRG_OFFSET + 240];
	if (addr < BASE_RAM || addr > 4*SPECTRUM_BANK - 6)
		logerror("Corrupted SP out of range:%04X", addr);
	else
		logerror("Fetching registers IFF1/2, R, AF and PC from the stack at SP:%04X\n", addr);

	data = space.read_byte(addr + 0); // IFF1/2: (bit 2, 0=DI/1=EI)
	m_maincpu->set_state_int(Z80_IFF1, BIT(data, 2));
	m_maincpu->set_state_int(Z80_IFF2, BIT(data, 2));

	intr = BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	data = space.read_byte(addr + 1);
	m_maincpu->set_state_int(Z80_R, data);

	data = (space.read_byte(addr + 3) << 8) | space.read_byte(addr + 2);
	m_maincpu->set_state_int(Z80_AF, data);

	data = (space.read_byte(addr + 5) << 8) | space.read_byte(addr + 4);
	m_maincpu->set_state_int(Z80_PC, data);

#if 0
	space.write_byte(addr + 0, 0); // It's been reported that zeroing these locations fixes the loading
	space.write_byte(addr + 1, 0); // of a few images that were snapshot at a "wrong" instant
	space.write_byte(addr + 2, 0);
	space.write_byte(addr + 3, 0);
	space.write_byte(addr + 4, 0);
	space.write_byte(addr + 5, 0);
#endif

	addr += 6;
	logerror("Fixing SP:%04X\n", addr);
	m_maincpu->set_state_int(Z80_SP, addr);

	// Set border color
	data = (space.read_byte(0x5c48) >> 3) & 0x07; // Get the current border color from BORDCR system variable.
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .PLUSD file.
 *
 *      .PLUSD files are the snapshots produced by the DISCiPLE or
 *      the +D disk interface, thus share the same internal organization
 *      of the .PRG files, with a couple of notable exceptions:
 *
 *      1) The filesystem metadata is missing
 *      2) Spectrum 128 snapshots (file type 9) are allowed
 *
 *      A commented disassembly of both BIOSes is avilable at:
 *
 *      http://www.biehold.nl/rudy/
 *
 *      48K snapshots are 49174 bytes long, 128K snapshots are 131095
 *      bytes long.
 *
 *      Philip Kendall's FUSE emulator at http://fuse-emulator.sourceforge.net
 *      supports this format, but no extension is officially provided.
 *      Therefore I have used .PLUSD - corrections are welcome!
 *
 *      48K snapshot format
 *      -------------------
 *
 *      Offset  Size    Description (all registers stored with LSB first
 *      ------- ------- -------------------------------------------------
 *      0       2       IY
 *      2       2       IX
 *      4       2       DE'
 *      6       2       BC'
 *      8       2       HL'
 *      10      2       AF'
 *      12      2       DE
 *      14      2       BC
 *      16      2       HL
 *      18      1       Junk created when saving I via LD A,I/PUSH AF
 *      19      1       I
 *      20      2       SP
 *      22      49152   RAM dump
 *
 *      128K snapshot format
 *      --------------------
 *
 *      Offset  Size    Description (all registers stored with LSB first
 *      ------- ------- -------------------------------------------------
 *      0       2       IY
 *      2       2       IX
 *      4       2       DE'
 *      6       2       BC'
 *      8       2       HL'
 *      10      2       AF'
 *      12      2       DE
 *      14      2       BC
 *      16      2       HL
 *      18      1       Junk created when saving I via LD A,I/PUSH AF
 *      19      1       I
 *      20      2       SP
 *      22      1       Port 0x7FFD data
 *      23      131072  RAM dump
 *
 *      The 8 RAM banks are stored in the order 0-7.
 *
 *      IFF1/2, R, AF and PC are stored on the stack. Due to a bug in the
 *      BIOS, the snapshots created with the DISCiPLE have the AF' register
 *      replaced with the AF register.
 *
 *      It's unknown (but likely possible) that the snapshots could
 *      suffer from the same "top of the stack" bug as well as .SNA images.
 *
 *******************************************************************/
void spectrum_state::setup_plusd(uint8_t *snapdata, uint32_t snapsize)
{
	int i, j;
	uint8_t intr;
	uint16_t addr = 0, data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = (snapdata[PLUSD_OFFSET + 15] << 8) | snapdata[PLUSD_OFFSET + 14];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[PLUSD_OFFSET + 13] << 8) | snapdata[PLUSD_OFFSET + 12];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[PLUSD_OFFSET + 17] << 8) | snapdata[PLUSD_OFFSET + 16];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[PLUSD_OFFSET + 11] << 8) | snapdata[PLUSD_OFFSET + 10];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[PLUSD_OFFSET +  7] << 8) | snapdata[PLUSD_OFFSET +  6];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[PLUSD_OFFSET +  5] << 8) | snapdata[PLUSD_OFFSET +  4];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[PLUSD_OFFSET +  9] << 8) | snapdata[PLUSD_OFFSET +  8];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[PLUSD_OFFSET +  3] << 8) | snapdata[PLUSD_OFFSET +  2];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[PLUSD_OFFSET +  1] << 8) | snapdata[PLUSD_OFFSET +  0];
	m_maincpu->set_state_int(Z80_IY, data);

	data = snapdata[PLUSD_OFFSET + 19];
	m_maincpu->set_state_int(Z80_I, data);

	m_maincpu->set_state_int(Z80_IM, (data == 0x00 || data == 0x3f) ? 1 : 2);

	if (snapsize == PLUSD48_SIZE)
	{
		page_basicrom();

		// Memory dump
		logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
		for (i = 0; i < 3*SPECTRUM_BANK; i++)
			space.write_byte(BASE_RAM + i, snapdata[PLUSD48_HDR + i]);
	}
	else
	{
		logerror("Loading %05X bytes of RAM at %04X\n", 8*SPECTRUM_BANK, BASE_RAM);
		for (i = 0; i < 8; i++)
		{
			switch (i)
			{
				case 5: addr = SPECTRUM_BANK;
						break;
				case 2: addr = 2*SPECTRUM_BANK;
						break;
				case 0:
				case 1:
				case 3:
				case 4:
				case 6:
				case 7: addr = 3*SPECTRUM_BANK;
						m_port_7ffd_data &= 0xf8;
						m_port_7ffd_data += i;
						update_paging();
						break;
			};
			logerror("Loading bank %d from offset:%05X\n", i, PLUSD128_HDR + i*SPECTRUM_BANK);
			for (j = 0; j < SPECTRUM_BANK; j++)
				space.write_byte(j + addr, snapdata[j + PLUSD128_HDR + i*SPECTRUM_BANK]);
		}
		m_port_7ffd_data = snapdata[PLUSD_OFFSET + 22];
		logerror("Port 7FFD:%02X\n", m_port_7ffd_data);
		logerror("Paging bank:%d\n", m_port_7ffd_data & 0x07);
		update_paging();
	}

	addr = (snapdata[PLUSD_OFFSET + 21] << 8) | snapdata[PLUSD_OFFSET + 20];
	if (addr < BASE_RAM || addr > 4*SPECTRUM_BANK - 6)
		logerror("Corrupted SP out of range:%04X", addr);
	else
		logerror("Fetching registers IFF1/2, R, AF and PC from the stack at SP:%04X\n", addr);

	data = space.read_byte(addr + 0); // IFF1/2: (bit 2, 0=DI/1=EI)
	m_maincpu->set_state_int(Z80_IFF1, BIT(data, 2));
	m_maincpu->set_state_int(Z80_IFF2, BIT(data, 2));

	intr = BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	data = space.read_byte(addr + 1);
	m_maincpu->set_state_int(Z80_R, data);

	data = (space.read_byte(addr + 3) << 8) | space.read_byte(addr + 2);
	m_maincpu->set_state_int(Z80_AF, data);

	data = (space.read_byte(addr + 5) << 8) | space.read_byte(addr + 4);
	m_maincpu->set_state_int(Z80_PC, data);

#if 0
	space.write_byte(addr + 0, 0); // It's been reported that zeroing these locations fixes the loading
	space.write_byte(addr + 1, 0); // of a few images that were snapshot at a "wrong" instant
	space.write_byte(addr + 2, 0);
	space.write_byte(addr + 3, 0);
	space.write_byte(addr + 4, 0);
	space.write_byte(addr + 5, 0);
#endif

	addr += 6;
	logerror("Fixing SP:%04X\n", addr);
	m_maincpu->set_state_int(Z80_SP, addr);

	// Set border color
	data = (space.read_byte(0x5c48) >> 3) & 0x07; // Get the current border color from BORDCR system variable.
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	//if (snapsize == PLUSD48_SIZE)
		//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
	//else
		//logerror("Snapshot loaded.\nExecution resuming at bank:%d %s\n", m_port_7ffd_data & 0x07, m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .SEM file.
 *
 *      .SEM files were produced by a german emulator called SpecEmu
 *      written by Bernd Waschke.
 *
 *      The files are usually 49192 bytes long unless optional POKE
 *      blocks are stored at the end of the file.
 *
 *      Offset  Size    Description (all registers stored with LSB first)
 *      ------- ------- -------------------------------------------------
 *      0       1       0x05 (signature length)
 *      1       5       "SPEC1" (signature)
 *      6       49152   RAM dump
 *      49158   2       AF
 *      49160   2       BC
 *      49162   2       DE
 *      49164   2       HL
 *      49166   2       AF'
 *      49168   2       BC'
 *      49170   2       DE'
 *      49172   2       HL'
 *      49174   2       PC
 *      49176   2       SP
 *      49178   2       IX
 *      49180   2       IY
 *      49182   2       I
 *      49184   2       R
 *      49186   2       IFF1 (0=DI/1=EI)
 *      49188   2       IFF2 (0=DI/1=EI)
 *      49190   2       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *
 *      Following these data, there are optional POKE blocks
 *
 *******************************************************************/
void spectrum_state::setup_sem(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t intr;
	uint16_t data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = (snapdata[SEM_OFFSET +  1] << 8) | snapdata[SEM_OFFSET +  0];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[SEM_OFFSET +  3] << 8) | snapdata[SEM_OFFSET +  2];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[SEM_OFFSET +  5] << 8) | snapdata[SEM_OFFSET +  4];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[SEM_OFFSET +  7] << 8) | snapdata[SEM_OFFSET +  6];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[SEM_OFFSET +  9] << 8) | snapdata[SEM_OFFSET +  8];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[SEM_OFFSET + 11] << 8) | snapdata[SEM_OFFSET + 10];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[SEM_OFFSET + 13] << 8) | snapdata[SEM_OFFSET + 12];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[SEM_OFFSET + 15] << 8) | snapdata[SEM_OFFSET + 14];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[SEM_OFFSET + 21] << 8) | snapdata[SEM_OFFSET + 20];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[SEM_OFFSET + 23] << 8) | snapdata[SEM_OFFSET + 22];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[SEM_OFFSET + 26];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[SEM_OFFSET + 24];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[SEM_OFFSET + 19] << 8) | snapdata[SEM_OFFSET + 18];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[SEM_OFFSET + 17] << 8) | snapdata[SEM_OFFSET + 16];
	m_maincpu->set_state_int(Z80_PC, data);

	data = snapdata[SEM_OFFSET + 32];
	m_maincpu->set_state_int(Z80_IM, data);

	data = snapdata[SEM_OFFSET + 28];
	m_maincpu->set_state_int(Z80_IFF1, BIT(data, 0));
	data = snapdata[SEM_OFFSET + 30];
	m_maincpu->set_state_int(Z80_IFF2, BIT(data, 0));

	intr = BIT(snapdata[SEM_OFFSET + 30], 0) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// Memory dump
	logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 3*SPECTRUM_BANK; i++)
		space.write_byte(BASE_RAM + i, snapdata[SEM_SIGNATURE + i]);

	// Set border color
	data = (space.read_byte(0x5c48) >> 3) & 0x07; // Get the current border color from BORDCR system variable.
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());

/* TODO: Decode the optional POKE bank at the end of the image */

}

/*******************************************************************
 *
 *      Load a .SIT file.
 *
 *      .SIT files were produced by a spanish emulator called Sinclair
 *      written by Pedro M. R. Salas.
 *
 *      http://www.ugr.es/~pedrom/sinclair.htm
 *
 *      The files are always 65564 bytes long and store both ROM and RAM.
 *
 *      Offset  Size    Description (all registers stored with LSB first)
 *      ------- ------- -------------------------------------------------
 *      0       2       BC
 *      2       2       DE
 *      4       2       HL
 *      6       2       AF
 *      8       2       IX
 *      10      2       IY
 *      12      2       SP
 *      14      2       PC
 *      16      1       R
 *      17      1       I
 *      18      2       BC'
 *      20      2       DE'
 *      22      2       HL'
 *      24      2       AF'
 *      26      1       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *      27      1       Border color
 *      28      16384   ROM dump
 *      16412   49152   RAM dump
 *
 *******************************************************************/
void spectrum_state::setup_sit(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t intr;
	uint16_t data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = (snapdata[SIT_OFFSET +  7] << 8) | snapdata[SIT_OFFSET +  6];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[SIT_OFFSET +  1] << 8) | snapdata[SIT_OFFSET +  0];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[SIT_OFFSET +  3] << 8) | snapdata[SIT_OFFSET +  2];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[SIT_OFFSET +  5] << 8) | snapdata[SIT_OFFSET +  4];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[SIT_OFFSET + 25] << 8) | snapdata[SIT_OFFSET + 24];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[SIT_OFFSET + 19] << 8) | snapdata[SIT_OFFSET + 18];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[SIT_OFFSET + 21] << 8) | snapdata[SIT_OFFSET + 20];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[SIT_OFFSET + 23] << 8) | snapdata[SIT_OFFSET + 22];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[SIT_OFFSET +  9] << 8) | snapdata[SIT_OFFSET +  8];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[SIT_OFFSET + 11] << 8) | snapdata[SIT_OFFSET + 10];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[SIT_OFFSET + 16];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[SIT_OFFSET + 17];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[SIT_OFFSET + 13] << 8) | snapdata[SIT_OFFSET + 12];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[SIT_OFFSET + 15] << 8) | snapdata[SIT_OFFSET + 14];
	m_maincpu->set_state_int(Z80_PC, data);

	data = snapdata[SIT_OFFSET + 26];
	m_maincpu->set_state_int(Z80_IM, data);

	data = 0x01; // .SIT snapshots don't specify whether interrupts are enabled or not, so I assume they are.
	m_maincpu->set_state_int(Z80_IFF1, data);
	m_maincpu->set_state_int(Z80_IFF2, data);

	intr = data ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// Memory dump
	logerror("Skipping the 16K ROM dump at offset:%04X\n", SIT_OFFSET + 28);
	logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 3*SPECTRUM_BANK; i++)
		space.write_byte(BASE_RAM + i, snapdata[SIT_HDR + SPECTRUM_BANK + i]);

	// Set border color
	data = snapdata[SIT_OFFSET + 27] & 0x07;
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .ZX file.
 *
 *      .ZX files were produced by the Amiga emulator called KGB
 *
 *      The files are usually 49486 bytes and store at the beginning
 *      of the file the last 132 bytes of the ROM.
 *
 *      Offset  Size    Description (all registers stored with MSB first)
 *      ------- ------- -------------------------------------------------
 *      0       132     Last 132 bytes of ROM dump
 *      132     49152   RAM dump
 *      49284   132     0x00 (reserved for future use)
 *      49416   10      Various KGB settings
 *      49426   1       IFF1/2: (0=DI/1=EI)
 *      49427   2       Reserved (must be 0x0003)
 *      49429   1       KGB ColorMode (0=BW/1=Color)
 *      49430   4       0x00 (reserved for future use)
 *      49434   2       BC
 *      49436   2       BC'
 *      49438   2       DE
 *      49440   2       DE'
 *      49442   2       HL
 *      49444   2       HL'
 *      49446   2       IX
 *      49448   2       IY
 *      49450   1       I
 *      49451   1       R
 *      49452   3       0x00 (reserved for future use)
 *      49455   1       A'
 *      49456   1       0x00 (reserved for future use)
 *      49457   1       A
 *      49458   1       0x00 (reserved for future use)
 *      49459   1       F'
 *      49460   1       0x00 (reserved for future use)
 *      49461   1       F
 *      49462   2       0x00 (reserved for future use)
 *      49464   2       PC
 *      49466   2       0x00 (reserved for future use)
 *      49468   2       SP
 *      49470   2       KGB Soundmode (0=Simple/1=Pitch/2=RomOnly)
 *      49472   2       KGB HaltMode (0=NoHalt/1=Halt)
 *      49474   2       Interrupt mode (-1=IM0/0=IM1/1=IM2)
 *      49476   10      0x00 (reserved for future use)
 *
 *******************************************************************/
void spectrum_state::setup_zx(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t intr;
	uint16_t data, mode;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	logerror("Skipping last 132 bytes of the 16K ROM dump at offset:0000\n");

	data = (snapdata[ZX_OFFSET + 173] << 8) | snapdata[ZX_OFFSET + 177];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[ZX_OFFSET + 150] << 8) | snapdata[ZX_OFFSET + 151];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[ZX_OFFSET + 154] << 8) | snapdata[ZX_OFFSET + 155];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[ZX_OFFSET + 158] << 8) | snapdata[ZX_OFFSET + 159];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[ZX_OFFSET + 171] << 8) | snapdata[ZX_OFFSET + 175];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[ZX_OFFSET + 152] << 8) | snapdata[ZX_OFFSET + 153];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[ZX_OFFSET + 156] << 8) | snapdata[ZX_OFFSET + 157];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[ZX_OFFSET + 160] << 8) | snapdata[ZX_OFFSET + 161];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[ZX_OFFSET + 162] << 8) | snapdata[ZX_OFFSET + 163];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[ZX_OFFSET + 164] << 8) | snapdata[ZX_OFFSET + 165];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[ZX_OFFSET + 167];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[ZX_OFFSET + 166];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[ZX_OFFSET + 184] << 8) | snapdata[ZX_OFFSET + 185];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[ZX_OFFSET + 180] << 8) | snapdata[ZX_OFFSET + 181];
	m_maincpu->set_state_int(Z80_PC, data);

	mode = (snapdata[ZX_OFFSET + 190] << 8) | snapdata[ZX_OFFSET + 191];
	switch (mode)
	{
		case 0xffff:
		m_maincpu->set_state_int(Z80_IM, (uint64_t)0);
		break;
		case 0x00:
		m_maincpu->set_state_int(Z80_IM, 1);
		break;
		case 0x01:
		m_maincpu->set_state_int(Z80_IM, 2);
		break;
		default:
		logerror("Invalid IM:%04X\n", mode);
	}

	data = BIT(snapdata[ZX_OFFSET + 142], 0);
	m_maincpu->set_state_int(Z80_IFF1, data);
	m_maincpu->set_state_int(Z80_IFF2, data);

	intr = BIT(snapdata[ZX_OFFSET + 142], 0) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// Memory dump
	logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 3*SPECTRUM_BANK; i++)
		space.write_byte(BASE_RAM + i, snapdata[132 + i]);

	// Set border color
	data = (space.read_byte(0x5c48) >> 3) & 0x07; // Get the current border color from BORDCR system variable.
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .SNP file.
 *
 *      .SNP files were produced by a polish emulator called Nuclear ZX
 *      written by Radovan Garabik and Lubomir Salanci.
 *
 *      http://korpus.juls.savba.sk/~garabik/old/zx.html
 *
 *      The files are always 49183 bytes long.
 *
 *      Offset  Size    Description (all registers stored with LSB first)
 *      ------- ------- -------------------------------------------------
 *      0       49152   RAM dump
 *      49152   2       AF
 *      49154   1       Border color
 *      49155   1       0x00 (reserved for future use)
 *      49156   2       BC
 *      49158   2       DE
 *      49160   2       HL
 *      49162   2       PC
 *      49164   2       SP
 *      49166   2       IX
 *      49168   2       IY
 *      49170   1       0x00 (reserved for IFF2 but not implemented)
 *      49171   1       IFF1 (0=DI/other=EI)
 *      49172   1       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *      49173   1       R
 *      49174   1       I
 *      49175   2       AF'
 *      49177   2       BC'
 *      49179   2       DE'
 *      49181   2       HL'
 *
 *******************************************************************/
void spectrum_state::setup_snp(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t intr;
	uint16_t data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = (snapdata[SNP_OFFSET +  1] << 8) | snapdata[SNP_OFFSET +  0];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[SNP_OFFSET +  5] << 8) | snapdata[SNP_OFFSET +  4];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[SNP_OFFSET +  7] << 8) | snapdata[SNP_OFFSET +  6];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[SNP_OFFSET +  9] << 8) | snapdata[SNP_OFFSET +  8];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[SNP_OFFSET + 24] << 8) | snapdata[SNP_OFFSET + 23];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[SNP_OFFSET + 26] << 8) | snapdata[SNP_OFFSET + 25];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[SNP_OFFSET + 28] << 8) | snapdata[SNP_OFFSET + 27];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[SNP_OFFSET + 30] << 8) | snapdata[SNP_OFFSET + 29];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[SNP_OFFSET + 15] << 8) | snapdata[SNP_OFFSET + 14];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[SNP_OFFSET + 17] << 8) | snapdata[SNP_OFFSET + 16];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[SNP_OFFSET + 21];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[SNP_OFFSET + 22];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[SNP_OFFSET + 13] << 8) | snapdata[SNP_OFFSET + 12];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[SNP_OFFSET + 11] << 8) | snapdata[SNP_OFFSET + 10];
	m_maincpu->set_state_int(Z80_PC, data);


	data = snapdata[SNP_OFFSET + 20] & 0x03;
	m_maincpu->set_state_int(Z80_IM, data);

	data = BIT(snapdata[SNP_OFFSET + 19], 0);
	m_maincpu->set_state_int(Z80_IFF1, data);
	m_maincpu->set_state_int(Z80_IFF2, data);

	intr = BIT(snapdata[SNP_OFFSET + 19], 0) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// Memory dump
	logerror("Loading %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 3*SPECTRUM_BANK; i++)
		space.write_byte(BASE_RAM + i, snapdata[i]);

	// Set border color
	data = snapdata[SNP_OFFSET +  2] & 0x07;
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .SNX file.
 *
 *      .SNX files were produced by an Atari ST emulator called Specci
 *      written by Christian Gandler
 *
 *      http://cd.textfiles.com/crawlycrypt1/apps/misc/zx_sp207/
 *
 *      The header is an extension of the .SNA format and the RAM dump
 *      is compressed with a simple run-length algorithm.
 *
 *      Offset  Size    Description (all registers stored with LSB first
 *                      except when noted with *BE*)
 *      ------- ------- -------------------------------------------------
 *      0       4       "XSNA" (signature)
 *      4       2       Header length *BE*
 *      6       1       I
 *      7       2       HL'
 *      9       2       DE'
 *      11      2       BC'
 *      13      2       AF'
 *      15      2       HL
 *      17      2       DE
 *      19      2       BC
 *      21      2       IY
 *      23      2       IX
 *      25      1       IFF1/2: bit 0 contains IFF1, bit 2 contains IFF2 (0=DI/1=EI)
 *      26      1       R
 *      27      2       AF
 *      29      2       SP
 *      31      1       Interrupt Mode (0=IM0/1=IM1/2=IM2)
 *      32      1       Border color
 *      33      1       Specci settings: Interface 1 (0=no/1=yes)
 *      34      1       Specci settings: FLASH emulation (0=no/1=yes)
 *      35      1       Specci settings: Attributes emulation (0=no/1=yes)
 *      36      1       Specci settings: Bit 7 - Keyboard (0=QWERTY/1=QWERTZ)
 *                                       Bit 6 - ULA emulation (0=no/1=yes)
 *                                       Bit 0,1 - Joystick interface
 *                                            00 = Kempston
 *                                            01 = IF2 Left
 *                                            10 = IF2 Right
 *                                            11 = Cursor/AGF/Protek
 *      37      1       Specci settings: R register emulation (0=no/1=yes)
 *                                       Bit 7 - EAR bit (0=Issue 3/1=Issue 2)
 *      38      1       Specci settings: Interrupt frequency (0=50 Hz/1=100 Hz)
 *      39      1       Specci settings: RS232 redirection (0=RS232/1=Centronics)
 *      40      1       Specci settings: Sound emulation
 *                                       Bit 4,7 - Frequency (0..4) for mode 2
 *                                       Bit 0,3 - Mode (0=off/1=direct/2=interrupt)
 *      41      1       Specci settings: Border emulation (0=off/1=direct/2=interrupt)
 *      42      1       Specci settings: IM2 hardware vector (0..255)
 *      43      ?????   RAM dump
 *
 *      PC is stored on stack.
 *
 *      The RAM dump is divided into short or long datablocks:
 *      if the block is composed of the same byte, it is replaced by
 *      a compressed version. The format of the various blocks follows:
 *
 *      Uncompressed short block
 *      ------------------------
 *      Offset  Size    Description
 *      ------- ------- -------------------------------------------------
 *      0       1       Marker byte. Values between 0xe0 and 0xef.
 *                      Low nibble is (block length - 1).
 *      1       ?????   Data block
 *
 *      Compressed short block
 *      ----------------------
 *      Offset  Size    Description
 *      ------- ------- -------------------------------------------------
 *      0       1       Marker byte. Values between 0xf0 and 0xff.
 *                      Low nibble is (block length - 1).
 *      1       1       Filler byte. This byte is repeated for the whole
 *                      length of the block.
 *
 *      Uncompressed long block
 *      -----------------------
 *      Offset  Size    Description
 *      ------- ------- -------------------------------------------------
 *      0       2       Block length in big endian format.
 *                      Values between 0x0000 and 0xdfff.
 *      2       1       Uncompressed block flag: 0xff.
 *      3       ?????   Data block
 *
 *      Compressed long block
 *      ---------------------
 *      Offset  Size    Description
 *      ------- ------- -------------------------------------------------
 *      0       2       Block length in big endian format.
 *                      Values between 0x0000 and 0xdfff.
 *      2       1       Compressed block flag: 0x00.
 *      3       1       Filler byte. This byte is repeated for the whole
 *                      length of the block.
 *
 *******************************************************************/
void spectrum_state::snx_decompress_block(address_space &space, uint8_t *source, uint16_t dest, uint16_t size)
{
	uint8_t counthi, countlo, compress, fill;
	uint16_t block = 0, count, i, j, numbytes;
	i = SNX_HDR - 1;
	numbytes = 0;

	while (numbytes < size)
	{
		counthi = source[++i];
		if (counthi >= 0xe0)
		{
			count = (counthi & 0x0f) + 1;     // Short block
			if ((counthi & 0xf0) == 0xf0)
				compress = SNX_COMPRESSED;
			else
				compress = SNX_UNCOMPRESSED;
			logerror("Block:%05d  Type:Short  Compr:%s  Offset:%04X  Len:%04X  ", block++, compress == SNX_COMPRESSED ? "Y" : "N", i-1, count);
		}
		else
		{
			countlo = source[++i];
			count = (counthi << 8) | countlo; // Long block
			compress = source[++i];
			logerror("Block:%05d  Type:Long   Compr:%s  Offset:%04X  Len:%04X  ", block++, compress == SNX_COMPRESSED ? "Y" : "N", i-3, count);
		}

		if (compress == SNX_COMPRESSED)
		{
			fill = source[++i];
			logerror("Dest:%04X  Filler:%02X\n", BASE_RAM + numbytes, fill);
			for(j = 0; j < count; j++)
				space.write_byte(BASE_RAM + numbytes + j, fill);
			numbytes += count;
		}
		else
		{
			logerror("Dest:%04X\n", BASE_RAM + numbytes);
			j = 0;
			while (j < count)
				space.write_byte(BASE_RAM + numbytes + j++, source[++i]);
			numbytes += count;
		}
	}
}

void spectrum_state::setup_snx(uint8_t *snapdata, uint32_t snapsize)
{
	uint8_t intr;
	uint16_t data, addr;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	data = (snapdata[SNX_OFFSET +  4] << 8) | snapdata[SNX_OFFSET +  5];
	if (data != 0x25)
		logerror("Corrupted header length: %02X instead of 0x25\n", data);

	data = (snapdata[SNX_OFFSET + 28] << 8) | snapdata[SNX_OFFSET + 27];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[SNX_OFFSET + 20] << 8) | snapdata[SNX_OFFSET + 19];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[SNX_OFFSET + 18] << 8) | snapdata[SNX_OFFSET + 17];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[SNX_OFFSET + 16] << 8) | snapdata[SNX_OFFSET + 15];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[SNX_OFFSET + 14] << 8) | snapdata[SNX_OFFSET + 13];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[SNX_OFFSET + 12] << 8) | snapdata[SNX_OFFSET + 11];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[SNX_OFFSET + 10] << 8) | snapdata[SNX_OFFSET +  9];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[SNX_OFFSET +  8] << 8) | snapdata[SNX_OFFSET +  7];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[SNX_OFFSET + 24] << 8) | snapdata[SNX_OFFSET + 23];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[SNX_OFFSET + 22] << 8) | snapdata[SNX_OFFSET + 21];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[SNX_OFFSET + 26];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[SNX_OFFSET +  6];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[SNX_OFFSET + 30] << 8) | snapdata[SNX_OFFSET + 29];
	m_maincpu->set_state_int(Z80_SP, data);


	data = snapdata[SNX_OFFSET + 31] & 0x03;
	if (data == 3)
		data = 2;
	m_maincpu->set_state_int(Z80_IM, data);

	data = snapdata[SNX_OFFSET + 25];
	m_maincpu->set_state_int(Z80_IFF1, BIT(data, 0));
	m_maincpu->set_state_int(Z80_IFF2, BIT(data, 2));

	intr = BIT(data, 0) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// Memory dump
	logerror("Uncompressing %04X bytes of RAM at %04X\n", 3*SPECTRUM_BANK, BASE_RAM);
	snx_decompress_block(space, snapdata, BASE_RAM, 3*SPECTRUM_BANK);

	// get pc from stack
	addr = m_maincpu->state_int(Z80_SP);

	if (addr < BASE_RAM || addr > 4*SPECTRUM_BANK - 2)
		logerror("Corrupted SP out of range:%04X", addr);
	else
		logerror("Fetching PC from the stack at SP:%04X\n", addr);

	m_maincpu->set_state_int(Z80_PC, (space.read_byte(addr + 1) << 8) | space.read_byte(addr + 0));

#if 0
	space.write_byte(addr + 0, 0); // It's been reported that zeroing these locations fixes the loading
	space.write_byte(addr + 1, 0); // of a few images that were snapshot at a "wrong" instant
#endif

	addr += 2;
	logerror("Fixed the stack at SP:%04X\n", addr);
	m_maincpu->set_state_int(Z80_SP, addr);

	// Set border color
	data = snapdata[SNX_OFFSET + 32] & 0x07;
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	page_basicrom();

/* TODO: Enable/disable IF1 as per snapdata[SNX_OFFSET + 33] */

/* TODO: Enable/disable joysticks as per snapdata[SNX_OFFSET + 36] */

/* TODO: Enable selection of Issue 2/3 config switch as per snapdata[SNX_OFFSET + 37] */

	//logerror("Snapshot loaded.\nExecution resuming at %s\n", m_maincpu->state_string(Z80_PC).c_str());
}

/*******************************************************************
 *
 *      Load a .FRZ file.
 *
 *      .FRZ files were produced by the czech Amiga emulator CBSpeccy
 *      written by the CodeBusters. Kudos to Dmitriy Zhivilov and
 *      Ian Greenway for having reverse-engineered and shared the
 *      description of this format.
 *
 *      The original specs of this format were published on a
 *      russian electronics magazine.
 *
 *      The format is always 131114 bytes long and supports only
 *      Spectrum 128K images.
 *
 *      Offset  Size    Description (all registers stored with MSB first)
 *      ------- ------- -------------------------------------------------
 *      0       1       0x00 (reserved for future use)
 *      1       1       Port 0x7FFD data
 *      2       2       HL'
 *      4       2       HL
 *      6       2       DE'
 *      8       2       DE
 *      10      2       BC'
 *      12      2       BC
 *      14      2       AF'
 *      16      2       AF
 *      18      7       Emulation disk registers and T-states
 *      25      1       R
 *      26      2       PC
 *      28      2       SP
 *      30      1       I
 *      31      1       0xFF (reserved for future use)
 *      32      1       0x00 (reserved for future use)
 *      33      1       Interrupt mode (0=IM0/1=IM1/2=IM2)
 *      34      3       0x00 (reserved for future use)
 *      37      1       IFF1: bit 2 contains IFF1 (0=DI/1=EI)
 *      38      2       IY
 *      40      2       IX
 *      42      131072  RAM dump
 *
 *      The 8 16K banks are stored in the order 5, 2, 0, 1, 3, 4, 6, 7
 *
 *******************************************************************/
void spectrum_state::setup_frz(uint8_t *snapdata, uint32_t snapsize)
{
	int i, j;
	uint8_t intr;
	uint16_t addr, data;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	if (m_port_7ffd_data == -1)
	{
		logerror("Can't load 128K .FRZ file into 48K machine\n");
		return;
	}

	data = (snapdata[FRZ_OFFSET + 16] << 8) | snapdata[FRZ_OFFSET + 17];
	m_maincpu->set_state_int(Z80_AF, data);

	data = (snapdata[FRZ_OFFSET + 12] << 8) | snapdata[FRZ_OFFSET + 13];
	m_maincpu->set_state_int(Z80_BC, data);

	data = (snapdata[FRZ_OFFSET +  8] << 8) | snapdata[FRZ_OFFSET +  9];
	m_maincpu->set_state_int(Z80_DE, data);

	data = (snapdata[FRZ_OFFSET +  4] << 8) | snapdata[FRZ_OFFSET +  5];
	m_maincpu->set_state_int(Z80_HL, data);


	data = (snapdata[FRZ_OFFSET + 14] << 8) | snapdata[FRZ_OFFSET + 15];
	m_maincpu->set_state_int(Z80_AF2, data);

	data = (snapdata[FRZ_OFFSET + 10] << 8) | snapdata[FRZ_OFFSET + 11];
	m_maincpu->set_state_int(Z80_BC2, data);

	data = (snapdata[FRZ_OFFSET +  6] << 8) | snapdata[FRZ_OFFSET +  7];
	m_maincpu->set_state_int(Z80_DE2, data);

	data = (snapdata[FRZ_OFFSET +  2] << 8) | snapdata[FRZ_OFFSET +  3];
	m_maincpu->set_state_int(Z80_HL2, data);


	data = (snapdata[FRZ_OFFSET + 40] << 8) | snapdata[FRZ_OFFSET + 41];
	m_maincpu->set_state_int(Z80_IX, data);

	data = (snapdata[FRZ_OFFSET + 38] << 8) | snapdata[FRZ_OFFSET + 39];
	m_maincpu->set_state_int(Z80_IY, data);


	data = snapdata[FRZ_OFFSET + 25];
	m_maincpu->set_state_int(Z80_R, data);

	data = snapdata[FRZ_OFFSET + 30];
	m_maincpu->set_state_int(Z80_I, data);


	data = (snapdata[FRZ_OFFSET + 28] << 8) | snapdata[FRZ_OFFSET + 29];
	m_maincpu->set_state_int(Z80_SP, data);

	data = (snapdata[FRZ_OFFSET + 26] << 8) | snapdata[FRZ_OFFSET + 27];
	m_maincpu->set_state_int(Z80_PC, data);


	data = snapdata[FRZ_OFFSET + 33];
	m_maincpu->set_state_int(Z80_IM, data);

	data = snapdata[FRZ_OFFSET + 37];
	m_maincpu->set_state_int(Z80_IFF1, BIT(data, 2));
	m_maincpu->set_state_int(Z80_IFF2, BIT(data, 2));

	intr = BIT(data, 2) ? CLEAR_LINE : ASSERT_LINE;
	m_maincpu->set_input_line(INPUT_LINE_IRQ0, intr);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// Memory dump
	addr = 0;
	static const uint8_t banks[] = { 5, 2, 0, 1, 3, 4, 6, 7 };
	logerror("Loading %05X bytes of RAM at %04X\n", 8*SPECTRUM_BANK, BASE_RAM);
	for (i = 0; i < 8; i++)
	{
		switch (banks[i])
		{
			case 5: addr = SPECTRUM_BANK;
					break;
			case 2: addr = 2*SPECTRUM_BANK;
					break;
			case 0:
			case 1:
			case 3:
			case 4:
			case 6:
			case 7: addr = 3*SPECTRUM_BANK;
					m_port_7ffd_data &= 0xf8;
					m_port_7ffd_data += banks[i];
					update_paging();
					break;
		};
		logerror("Loading bank %d from offset:%05X\n", banks[i], FRZ_HDR + i*SPECTRUM_BANK);
		for (j = 0; j < SPECTRUM_BANK; j++)
			space.write_byte(j + addr, snapdata[j + FRZ_HDR + i*SPECTRUM_BANK]);
	}
	m_port_7ffd_data = snapdata[FRZ_OFFSET +  1];
	logerror("Port 7FFD:%02X\n", m_port_7ffd_data);
	logerror("Paging bank:%d\n", m_port_7ffd_data & 0x07);
	update_paging();

	// Set border color
	data = (space.read_byte(0x5c48) >> 3) & 0x07; // Get the current border color from BORDCR system variable.
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	//logerror("Snapshot loaded.\nExecution resuming at bank:%d %s\n", m_port_7ffd_data & 0x07, m_maincpu->state_string(Z80_PC).c_str());
}

void spectrum_state::z80_decompress_block(address_space &space, uint8_t *source, uint16_t dest, uint16_t size)
{
	uint8_t ch;
	int i;

	do
	{
		/* get byte */
		ch = source[0];

		/* either start 0f 0x0ed, 0x0ed, xx yy or single 0x0ed */
		if (ch == 0x0ed)
		{
			if (source[1] == 0x0ed)
			{
				/* 0x0ed, 0x0ed, xx yy - repetition */
				uint8_t count;
				uint8_t data;

				count = source[2];

				if (count == 0)
					return;

				data = source[3];

				source += 4;

				if (count > size)
					count = size;

				size -= count;

				for (i = 0; i < count; i++)
				{
					space.write_byte(dest, data);
					dest++;
				}
			}
			else
			{
				/* single 0x0ed */
				space.write_byte(dest, ch);
				dest++;
				source++;
				size--;
			}
		}
		else
		{
			/* not 0x0ed */
			space.write_byte(dest, ch);
			dest++;
			source++;
			size--;
		}
	}
	while (size > 0);
}

static SPECTRUM_Z80_SNAPSHOT_TYPE spectrum_identify_z80 (uint8_t *snapdata, uint32_t snapsize)
{
	uint8_t lo, hi, data;

	if (snapsize < 30)
		return SPECTRUM_Z80_SNAPSHOT_INVALID;   /* Invalid file */

	lo = snapdata[6] & 0x0ff;
	hi = snapdata[7] & 0x0ff;
	if (hi || lo)
		return SPECTRUM_Z80_SNAPSHOT_48K_OLD;   /* V1.45 - 48K only */

	lo = snapdata[30] & 0x0ff;
	hi = snapdata[31] & 0x0ff;
	data = snapdata[34] & 0x0ff;            /* Hardware mode */

	if ((hi == 0) && (lo == 23))
	{                       /* V2.01 */                         /* V2.01 format */
		switch (data)
		{
			case 0:
			case 1: return SPECTRUM_Z80_SNAPSHOT_48K;
			case 2: return SPECTRUM_Z80_SNAPSHOT_SAMRAM;
			case 3:
			case 4: return SPECTRUM_Z80_SNAPSHOT_128K;
			case 128: return SPECTRUM_Z80_SNAPSHOT_TS2068;
		}
	}

	if ((hi == 0) && (lo == 54))
	{                       /* V3.0x */                         /* V2.01 format */
		switch (data)
		{
			case 0:
			case 1:
			case 3: return SPECTRUM_Z80_SNAPSHOT_48K;
			case 2: return SPECTRUM_Z80_SNAPSHOT_SAMRAM;
			case 4:
			case 5:
			case 6: return SPECTRUM_Z80_SNAPSHOT_128K;
			case 128: return SPECTRUM_Z80_SNAPSHOT_TS2068;
		}
	}

	return SPECTRUM_Z80_SNAPSHOT_INVALID;
}

// supports 48k & 128k .Z80 files
void spectrum_state::setup_z80(uint8_t *snapdata, uint32_t snapsize)
{
	int i;
	uint8_t lo, hi, data;
	SPECTRUM_Z80_SNAPSHOT_TYPE z80_type;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	z80_type = spectrum_identify_z80(snapdata, snapsize);

	switch (z80_type)
	{
		case SPECTRUM_Z80_SNAPSHOT_INVALID:
				logerror("Invalid .Z80 file\n");
				return;
		case SPECTRUM_Z80_SNAPSHOT_48K_OLD:
		case SPECTRUM_Z80_SNAPSHOT_48K:
				logerror("48K .Z80 file\n");
				if (!strcmp(machine().system().name,"ts2068"))
					logerror("48K .Z80 file in TS2068\n");
				break;
		case SPECTRUM_Z80_SNAPSHOT_128K:
				logerror("128K .Z80 file\n");
				if (m_port_7ffd_data == -1)
				{
					logerror("Not a 48K .Z80 file\n");
					return;
				}
				if (!strcmp(machine().system().name,"ts2068"))
				{
					logerror("Not a TS2068 .Z80 file\n");
					return;
				}
				break;
		case SPECTRUM_Z80_SNAPSHOT_TS2068:
				logerror("TS2068 .Z80 file\n");
				if (strcmp(machine().system().name,"ts2068"))
					logerror("Not a TS2068 machine\n");
				break;
		case SPECTRUM_Z80_SNAPSHOT_SAMRAM:
				logerror("Hardware not supported - .Z80 file\n");
				return;
	}

	// AF
	hi = snapdata[0] & 0x0ff;
	lo = snapdata[1] & 0x0ff;
	m_maincpu->set_state_int(Z80_AF, (hi << 8) | lo);
	// BC
	lo = snapdata[2] & 0x0ff;
	hi = snapdata[3] & 0x0ff;
	m_maincpu->set_state_int(Z80_BC, (hi << 8) | lo);
	// HL
	lo = snapdata[4] & 0x0ff;
	hi = snapdata[5] & 0x0ff;
	m_maincpu->set_state_int(Z80_HL, (hi << 8) | lo);

	// SP
	lo = snapdata[8] & 0x0ff;
	hi = snapdata[9] & 0x0ff;
	m_maincpu->set_state_int(Z80_SP, (hi << 8) | lo);

	// I
	m_maincpu->set_state_int(Z80_I, (snapdata[10] & 0x0ff));

	// R
	data = (snapdata[11] & 0x07f) | ((snapdata[12] & 0x01) << 7);
	m_maincpu->set_state_int(Z80_R, data);

	// Set border color
	m_port_fe_data = (m_port_fe_data & 0xf8) | ((snapdata[12] & 0x0e) >> 1);
	border_update((snapdata[12] & 0x0e) >> 1);

	lo = snapdata[13] & 0x0ff;
	hi = snapdata[14] & 0x0ff;
	m_maincpu->set_state_int(Z80_DE, (hi << 8) | lo);

	lo = snapdata[15] & 0x0ff;
	hi = snapdata[16] & 0x0ff;
	m_maincpu->set_state_int(Z80_BC2, (hi << 8) | lo);

	lo = snapdata[17] & 0x0ff;
	hi = snapdata[18] & 0x0ff;
	m_maincpu->set_state_int(Z80_DE2, (hi << 8) | lo);

	lo = snapdata[19] & 0x0ff;
	hi = snapdata[20] & 0x0ff;
	m_maincpu->set_state_int(Z80_HL2, (hi << 8) | lo);

	hi = snapdata[21] & 0x0ff;
	lo = snapdata[22] & 0x0ff;
	m_maincpu->set_state_int(Z80_AF2, (hi << 8) | lo);

	lo = snapdata[23] & 0x0ff;
	hi = snapdata[24] & 0x0ff;
	m_maincpu->set_state_int(Z80_IY, (hi << 8) | lo);

	lo = snapdata[25] & 0x0ff;
	hi = snapdata[26] & 0x0ff;
	m_maincpu->set_state_int(Z80_IX, (hi << 8) | lo);

	// Interrupt Flip/Flop
	if (snapdata[27] == 0)
	{
		m_maincpu->set_state_int(Z80_IFF1, (uint64_t)0);
		// m_maincpu->set_state_int(Z80_IRQ_STATE, 0);
	}
	else
	{
		m_maincpu->set_state_int(Z80_IFF1, 1);
		// m_maincpu->set_state_int(Z80_IRQ_STATE, 1);
	}

	m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
//  m_maincpu->set_input_line(INPUT_LINE_NMI, data);
	m_maincpu->set_input_line(INPUT_LINE_HALT, CLEAR_LINE);

	// IFF2
	if (snapdata[28] != 0)
	{
		data = 1;
	}
	else
	{
		data = 0;
	}
	m_maincpu->set_state_int(Z80_IFF2, data);

	// Interrupt Mode
	m_maincpu->set_state_int(Z80_IM, (snapdata[29] & 0x03));

	if (z80_type == SPECTRUM_Z80_SNAPSHOT_48K_OLD)
	{
		lo = snapdata[6] & 0x0ff;
		hi = snapdata[7] & 0x0ff;
		m_maincpu->set_state_int(Z80_PC, (hi << 8) | lo);

		page_basicrom();

		if ((snapdata[12] & 0x020) == 0)
		{
			logerror("Not compressed\n");   // not compressed
			for (i = 0; i < 49152; i++)
				space.write_byte(i + 16384, snapdata[30 + i]);
		}
		else
		{
			logerror("Compressed\n");   // compressed
			z80_decompress_block(space, snapdata + 30, 16384, 49152);
		}
	}
	else
	{
		uint8_t *pSource;
		int header_size;

		header_size = 30 + 2 + ((snapdata[30] & 0x0ff) | ((snapdata[31] & 0x0ff) << 8));

		lo = snapdata[32] & 0x0ff;
		hi = snapdata[33] & 0x0ff;
		m_maincpu->set_state_int(Z80_PC, (hi << 8) | lo);

		if ((z80_type == SPECTRUM_Z80_SNAPSHOT_128K) || ((z80_type == SPECTRUM_Z80_SNAPSHOT_TS2068) && !strcmp(machine().system().name,"ts2068")))
		{
			ay8910_device *ay8912 = subdevice<ay8910_device>("ay8912");

			// Only set up sound registers for 128K machine or TS2068!
			for (i = 0; i < 16; i++)
			{
				ay8912->address_w(generic_space(), 0, i);
				ay8912->data_w(generic_space(), 0, snapdata[39 + i]);
			}
			ay8912->address_w(generic_space(), 0, snapdata[38]);
		}

		pSource = snapdata + header_size;

		if (z80_type == SPECTRUM_Z80_SNAPSHOT_48K)
			// Ensure 48K Basic ROM is used
			page_basicrom();

		do
		{
			unsigned short length;
			uint8_t page;
			int Dest = 0;

			length = (pSource[0] & 0x0ff) | ((pSource[1] & 0x0ff) << 8);
			page = pSource[2];

			if (z80_type == SPECTRUM_Z80_SNAPSHOT_48K || z80_type == SPECTRUM_Z80_SNAPSHOT_TS2068)
			{
				switch (page)
				{
					case 4: Dest = 0x08000; break;
					case 5: Dest = 0x0c000; break;
					case 8: Dest = 0x04000; break;
					default: Dest = 0; break;
				}
			}
			else
			{
				// 3 = bank 0, 4 = bank 1 ... 10 = bank 7
				if ((page >= 3) && (page <= 10))
				{
					// Page the appropriate bank into 0xc000 - 0xfff
					m_port_7ffd_data = page - 3;
					update_paging();
					Dest = 0x0c000;
				}
				else
					// Other values correspond to ROM pages
					Dest = 0x0;
			}

			if (Dest != 0)
			{
				if (length == 0x0ffff)
				{
					// block is uncompressed
					logerror("Not compressed\n");

					// not compressed
					for (i = 0; i < 16384; i++)
						space.write_byte(i + Dest, pSource[i]);
				}
				else
				{
					logerror("Compressed\n");

					// block is compressed
					z80_decompress_block(space, &pSource[3], Dest, 16384);
				}
			}

			// go to next block
			pSource += (3 + length);
		}
		while ((pSource - snapdata) < snapsize);

		if ((m_port_7ffd_data != -1) && (z80_type != SPECTRUM_Z80_SNAPSHOT_48K))
		{
			// Set up paging
			m_port_7ffd_data = (snapdata[35] & 0x0ff);
			update_paging();
		}
		if ((z80_type == SPECTRUM_Z80_SNAPSHOT_48K) && !strcmp(machine().system().name,"ts2068"))
		{
			m_port_f4_data = 0x03;
			m_port_ff_data = 0x00;
			ts2068_update_memory();
		}
		if (z80_type == SPECTRUM_Z80_SNAPSHOT_TS2068 && !strcmp(machine().system().name,"ts2068"))
		{
			m_port_f4_data = snapdata[35];
			m_port_ff_data = snapdata[36];
			ts2068_update_memory();
		}
	}
}

QUICKLOAD_LOAD_MEMBER( spectrum_state,spectrum)
{
	std::vector<uint8_t> quickload_data(quickload_size);

	image.fread(&quickload_data[0], quickload_size);

	if (!core_stricmp(file_type, "scr"))
	{
		if ((quickload_size != SCR_SIZE) && (quickload_size != SCR_BITMAP))
		{
			logerror("Invalid .SCR file size.\n");
			goto error;
		}
		setup_scr(&quickload_data[0], quickload_size);
	}
	else if (!core_stricmp(file_type, "raw"))
	{
		if (quickload_size != RAW_SIZE)
		{
			logerror("Invalid .RAW file size.\n");
			goto error;
		}
		setup_raw(&quickload_data[0], quickload_size);
	}

	return image_init_result::PASS;

error:
	return image_init_result::FAIL;
}

/*******************************************************************
 *
 *      Load a .SCR file.
 *
 *      .SCR files are just a binary dump of the ZX Spectrum's
 *      display file, which is 256x192 pixels large.
 *
 *      These file are created using the Sinclair BASIC command
 *
 *      SAVE "filename" SCREEN$
 *
 *      where the keyword "SCREEN$" is a shortcut for "CODE 16384,6912"
 *      i.e. the loading address and the length of the data.
 *
 *      The format is organized as follows:
 *
 *      Offset   Size    Description
 *      -------- ------- -------------------------------------------------
 *      0        6144    Screen bitmap
 *      6144     768     Screen attributes
 *
 *      Some utilities and emulators support a B&W version of this
 *      format, which stores only the bitmap. These files can't be
 *      produced using the SAVE SCREEN$ command - rather they're
 *      created by the command
 *
 *      SAVE "filename" CODE 16384,6144
 *
 *******************************************************************/
void spectrum_state::setup_scr(uint8_t *quickdata, uint32_t quicksize)
{
	int i;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	for (i = 0; i < quicksize; i++)
		space.write_byte(i + BASE_RAM, quickdata[i]);

	log_quickload(quicksize == SCR_SIZE ? "SCREEN$" : "SCREEN$ (Mono)", BASE_RAM, quicksize, 0, EXEC_NA);
}

/*******************************************************************
 *
 *      Load a .RAW file.
 *
 *      .RAW files are a binary dump of the ZX Spectrum RAM
 *      and are created using the Sinclair BASIC command
 *
 *      SAVE *"b" CODE 16384,49152
 *
 *      which copies the whole RAM to the Interface 1's RS232 interface.
 *      The resulting file is always 49161 bytes long.
 *
 *      The format is organized as follows:
 *
 *      Offset  Size    Description (all registers stored with LSB first)
 *      ------- ------- -------------------------------------------------
 *      0       1       0x03 (BYTES file type)
 *      1       2       Image size
 *      3       2       Image start address
 *      5       4       0xFF
 *
 *      Since the size and the start address are encoded in the header,
 *      it would be possible to create .RAW images of any part of the RAM.
 *      However, no image of such type has ever surfaced.
 *
 *******************************************************************/
void spectrum_state::setup_raw(uint8_t *quickdata, uint32_t quicksize)
{
	int i;
	uint8_t data;
	uint16_t start, len;
	address_space &space = m_maincpu->space(AS_PROGRAM);

	start = (quickdata[RAW_OFFSET + 4] << 8) | quickdata[RAW_OFFSET + 3];
	len   = (quickdata[RAW_OFFSET + 2] << 8) | quickdata[RAW_OFFSET + 1];

	for (i = 0; i < len; i++)
		space.write_byte(i + start, quickdata[i + RAW_HDR]);

	// Set border color
	data = (space.read_byte(0x5c48) >> 3) & 0x07; // Get the current border color from BORDCR system variable.
	m_port_fe_data = (m_port_fe_data & 0xf8) | data;
	border_update(data);
	logerror("Border color:%02X\n", data);

	log_quickload("BYTES", start, len, 0, EXEC_NA);
}
