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

	apple2gs.c

	Apple IIgs code


	Apple IIgs specific softswitches:
	
	C019 - RDVBLBAR
		bits 7 - set during vblank (when at scanline 192 or higher)

	C022 - TBCOLOR
		bits 7-4 - text foreground color
		bits 3-0 - text background color

	C023 - VGCINT
		bit 7 - set for interrupt generated by VGC
		bit 6 - set during one second interrupt
		bit 5 - set during scanline interrupt
		bit 4 - set during external interrupt
		bit 3 - ???
		bit 2 - set for interrupt every second
		bit 1 - set for scanline interrupt
		bit 0 - set for external interrupt

	C025 - KEYMODREG
		bit 7 - option key pressed
		bit 6 - command key presssed
		bit 5 - modified latch
		bit 4 - keypad key pressed
		bit 3 - repeating
		bit 2 - caps lock latched
		bit 1 - control key pressed
		bit 0 - shift key pressed

	C027 - KMSTATUS
		bit 7 - set if mouse register full
		bit 6 - mouse interupt enable flag
		bit 5 - set if data register full
		bit 4 - data interrupt enabled
		bit 3 - set if key data full
		bit 2 - key data interurpt enabled
		bit 1 - clear if horizontal mouse data, set if vertical
		bit 0 - command register full

	C02D - SLTROMSEL
	
	C031 - DISKREG
		bit 7 - set to select head on 3.5" drive
		bit 6 - set to enable 3.5" drive, clear to enable 5.25" drive

	C035 - SHADOW
		bit 7 - ???
		bit 6 - set to inhibit I/O and LC operations ($C000-$FFFF)
		bit 5 - ???
		bit 4 - set to inhibit shadowing aux hires page
		bit 3 - set to inhibit shadowing super hires video
		bit 2 - set to inhibit shadowing hires page 2
		bit 1 - set to inhibit shadowing hires page 2
		bit 0 - set to inhibit shadowing text pages

	C036 - CYAREG
		bit 7 - clear for slow speed, set for hi speed
		bit 6 - ???
		bit 5 - ???
		bit 4 - shadow in all RAM banks
		bit 3 - slot 7 motor on
		bit 2 - slot 6 motor on
		bit 1 - slot 5 motor on
		bit 0 - slot 4 motor on

	C041 - INTEN
		bit 4 - set to enable quarter second interrupts
		bit 3 - set to enable VBL interrupts
		bit 2 - set to enable Mega II mouse switch interrupts
		bit 1 - set to enable Mega II mouse movement interrupts
		bit 0 - set to enable Mega II mouse mouse operation

	C046 - DIAGTYPE/INTFLAG
		bit 7 - set if mouse button currently down
		bit 6 - set if mouse button down on last read
		bit 5 - set for AN3
		bit 4 -	set if currently in quarter second interrupt
		bit 3 - set if currently in VBL interrupt
		bit 2 - set if currently in Mega II mouse switch interrupt
		bit 1 - set if currently in Mega II mouse movement interrupt
		bit 0 - set if system IRQ line asserted

	C047 - CLRVBLINT

	C068 - STATEREG
		bit 7 - ALTZP status
		bit 6 - PAGE2 status
		bit 5 - RAMRD status
		bit 4 - RAMWRT status
		bit 3 - !LCRAM status (inverted)
		bit 2 - LCRAM2 status
		bit 1 - ROMBANK status (unimplemented)
		bit 0 - INTCXROM status

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

#include <assert.h>
#include "driver.h"

#include "includes/apple2gs.h"
#include "includes/apple2.h"
#include "machine/ay3600.h"
#include "machine/applefdc.h"
#include "machine/sonydriv.h"
#include "machine/8530scc.h"
#include "devices/flopdrv.h"
#include "cpu/g65816/g65816.h"
#include "sound/es5503.h"

#define LOG_C0XX			0
#define LOG_ADB				0
#define LOG_IRQ				0

#define IRQ_KBD_SRQ			0x01
#define IRQ_ADB_DATA		0x02
#define IRQ_ADB_MOUSE		0x04
#define IRQ_VGC_SCANLINE	0x08
#define IRQ_VGC_SECOND		0x10
#define IRQ_INTEN_QSECOND	0x20
#define IRQ_INTEN_VBL		0x40
#define IRQ_DOC			0x80

UINT8 *apple2gs_slowmem;
UINT8 apple2gs_newvideo;

static UINT8 apple2gs_vgcint;
static UINT8 apple2gs_langsel;
static UINT8 apple2gs_sltromsel;
static UINT8 apple2gs_cyareg;
static UINT8 apple2gs_inten;
static UINT8 apple2gs_intflag;
static UINT8 apple2gs_shadow;
static UINT8 apple2gs_pending_irqs;
static UINT8 apple2gs_mouse_x;
static UINT8 apple2gs_mouse_y;
static INT8 apple2gs_mouse_dx;
static INT8 apple2gs_mouse_dy;
static mess_image *apple2gs_cur_slot6_image;


/* -----------------------------------------------------------------------
 * Apple IIgs clock
 * ----------------------------------------------------------------------- */

typedef enum
{
	CLOCKMODE_IDLE,
	CLOCKMODE_TIME,
	CLOCKMODE_INTERNALREGS,
	CLOCKMODE_BRAM1,
	CLOCKMODE_BRAM2
} apple2gs_clock_mode;

static UINT8 clock_data;
static UINT8 clock_control;
static UINT8 clock_read;
static UINT8 clock_reg1;
static apple2gs_clock_mode clock_mode;
static UINT32 clock_curtime;				/* number of seconds since 1-Jan-1904 */
static seconds_t clock_curtime_interval;	/* second index at which clock_curtime is valid */
static UINT8 clock_bram[256];


static void process_clock(void)
{
	UINT8 operation;
	seconds_t current_interval;

	/* update clock_curtime */
	current_interval = mame_timer_get_time().seconds;
	clock_curtime += current_interval - clock_curtime_interval;
	clock_curtime_interval = current_interval;

	switch(clock_mode)
	{
		case CLOCKMODE_IDLE:
			clock_read = (clock_data >> 7);
			clock_reg1 = (clock_data >> 2) & 0x03;
			operation = (clock_data >> 4) & 0x07;

			if ((clock_data & 0x40) == 0x00)
			{
				switch(operation)
				{
					case 0x00:
						/* read/write seconds register */
						clock_mode = CLOCKMODE_TIME;
						break;

					case 0x03:
						/* internal registers */
						if (clock_reg1 & 0x02)
						{
							clock_mode = CLOCKMODE_BRAM2;
							clock_reg1 = (clock_data & 0x07) << 5;
						}
						else
						{
							clock_mode = CLOCKMODE_INTERNALREGS;
						}
						break;

					default:
						//osd_die("NYI");
						break;
				}
			}
			break;

		case CLOCKMODE_BRAM1:
			if (clock_read)
				clock_data = clock_bram[clock_reg1];
			else
				clock_bram[clock_reg1] = clock_data;
			clock_mode = CLOCKMODE_IDLE;
			break;

		case CLOCKMODE_BRAM2:
			clock_reg1 |= (clock_data >> 2) & 0x1F;
			clock_mode = CLOCKMODE_BRAM1;
			break;

		case CLOCKMODE_INTERNALREGS:
			switch (clock_reg1)
			{
				case 0x00:
					/* test register */
					break;

				case 0x01:
					/* write protect register */
					break;
			}
			clock_mode = CLOCKMODE_IDLE;
			break;

		case CLOCKMODE_TIME:
			if (clock_data & 0x40)
			{
				clock_data = clock_curtime >> (clock_reg1 * 8);
			}
			else
			{
				clock_curtime &= ~(0xFF << (clock_reg1 * 8));
				clock_curtime |= clock_data << (clock_reg1 * 8);
			}
			clock_mode = CLOCKMODE_IDLE;
			break;

		default:
			//osd_die("NYI");
			break;
	}
}



NVRAM_HANDLER( apple2gs )
{
	if (read_or_write)
	{
		mame_fwrite(file, clock_bram, sizeof(clock_bram));
	}
	else if (file)
	{
		mame_fread(file, clock_bram, sizeof(clock_bram));
	}
	else
	{
		memset(clock_bram, 0x00, sizeof(clock_bram));
	}
}



/* -----------------------------------------------------------------------
 * Interrupts
 * ----------------------------------------------------------------------- */

static const char *apple2gs_irq_name(UINT8 irq_mask)
{
	switch(irq_mask)
	{
		case IRQ_KBD_SRQ:			return "IRQ_KBD_SRQ";
		case IRQ_ADB_DATA:			return "IRQ_ADB_DATA";
		case IRQ_ADB_MOUSE:			return "IRQ_ADB_MOUSE";
		case IRQ_VGC_SCANLINE:		return "IRQ_VGC_SCANLINE";
		case IRQ_VGC_SECOND:		return "IRQ_VGC_SECOND";
		case IRQ_INTEN_QSECOND:		return "IRQ_INTEN_QSECOND";
		case IRQ_INTEN_VBL:			return "IRQ_INTEN_VBL";
		case IRQ_DOC:				return "IRQ_DOC";
	}
	return NULL;
}



static void apple2gs_add_irq(UINT8 irq_mask)
{
	if ((apple2gs_pending_irqs & irq_mask) == 0x00)
	{
		if (LOG_IRQ)
			logerror("apple2gs_add_irq(): adding %s\n", apple2gs_irq_name(irq_mask));

		apple2gs_pending_irqs |= irq_mask;
		cpunum_set_input_line(0, G65816_LINE_IRQ, apple2gs_pending_irqs ? ASSERT_LINE : CLEAR_LINE);
	}
}



static void apple2gs_remove_irq(UINT8 irq_mask)
{
	if (apple2gs_pending_irqs & irq_mask)
	{
		if (LOG_IRQ)
			logerror("apple2gs_remove_irq(): removing %s\n", apple2gs_irq_name(irq_mask));

		apple2gs_pending_irqs &= ~irq_mask;
		cpunum_set_input_line(0, G65816_LINE_IRQ, apple2gs_pending_irqs ? ASSERT_LINE : CLEAR_LINE);
	}
}

void apple2gs_doc_irq(int state)
{
	if (state)
	{
		apple2gs_add_irq(IRQ_DOC);
	}
	else
	{
		apple2gs_remove_irq(IRQ_DOC);
	}
}


/* -----------------------------------------------------------------------
 * ADB
 * ----------------------------------------------------------------------- */

typedef enum 
{
	ADBSTATE_IDLE,
	ADBSTATE_INCOMMAND,
	ADBSTATE_INRESPONSE
} adbstate_t;

static adbstate_t adb_state;
static UINT8 adb_command;
static UINT8 adb_mode;
static UINT8 adb_kmstatus;
static UINT8 adb_latent_result;
static size_t adb_command_length;
static size_t adb_command_pos;
static UINT8 adb_command_bytes[8];
static UINT8 adb_response_bytes[8];
static UINT8 adb_response_length;
static size_t adb_response_pos;
static UINT8 adb_memory[0x100];
static int adb_address_keyboard;
static int adb_address_mouse;


static UINT8 adb_read_memory(UINT32 address)
{
	if (address < (sizeof(adb_memory) / sizeof(adb_memory[0])))
		return adb_memory[address];
	else
		return 0x00;
}



static void adb_write_memory(UINT32 address, UINT8 data)
{
	if (address < (sizeof(adb_memory) / sizeof(adb_memory[0])))
		adb_memory[address] = data;
}



static void adb_set_mode(UINT8 mode)
{
	adb_mode = mode;
}



static void adb_set_config(UINT8 b1, UINT8 b2, UINT8 b3)
{
	/* ignore for now */
}



static void adb_post_response(const UINT8 *bytes, size_t length)
{
	assert(length < (sizeof(adb_response_bytes) / sizeof(adb_response_bytes[0])));
	memcpy(adb_response_bytes, bytes, length);

	adb_state = ADBSTATE_INRESPONSE;
	adb_response_length = length;
	adb_response_pos = 0;
}



static void adb_post_response_1(UINT8 b)
{
	adb_post_response(&b, 1);
}



static void adb_post_response_2(UINT8 b1, UINT8 b2)
{
	UINT8 b[2];
	b[0] = b1;
	b[1] = b2;
	adb_post_response(b, 2);
}



static void adb_do_command(void)
{
	int device;
	UINT32 address;
	UINT8 val;

	adb_state = ADBSTATE_IDLE;
	if (LOG_ADB)
		logerror("adb_do_command(): adb_command=0x%02x\n", adb_command);

	switch(adb_command)
	{
		case 0x00:	/* ??? */
			break;

		case 0x03:	/* flush keyboard buffer */
			break;

		case 0x04:	/* set modes */
			adb_set_mode(adb_mode | adb_command_bytes[0]);
			break;

		case 0x05:	/* clear modes */
			adb_set_mode(adb_mode & ~adb_command_bytes[0]);
			break;

		case 0x06:	/* set config */
			adb_set_config(adb_command_bytes[0], adb_command_bytes[1], adb_command_bytes[2]);
			break;

		case 0x07:	/* synchronize */
			adb_set_mode(adb_command_bytes[0]);
			adb_set_config(adb_command_bytes[1], adb_command_bytes[2], adb_command_bytes[3]);
			break;

		case 0x08:	/* write memory */
			address = adb_command_bytes[0];
			val = adb_command_bytes[1];
			adb_write_memory(address, val);
			break;

		case 0x09:	/* read memory */
			address = (adb_command_bytes[1] << 8) | adb_command_bytes[0];
			adb_post_response_1(adb_read_memory(address));
			break;

		case 0x0d:	/* get version */
			adb_post_response_1(0x06);
			break;

		case 0x0e:	/* read available charsets */
			adb_post_response_2(0x01, 0x00);
			break;

		case 0x0f:	/* read available layouts */
			adb_post_response_2(0x01, 0x00);
			break;

		case 0x12:	/* mystery command 0x12 */
		case 0x13:	/* mystery command 0x13 */
			break;

		case 0xb0: case 0xb1: case 0xb2: case 0xb3:
		case 0xb4: case 0xb5: case 0xb6: case 0xb7:
		case 0xb8: case 0xb9: case 0xba: case 0xbb:
		case 0xbc: case 0xbd: case 0xbe: case 0xbf:
			/* send data to device */
			device = adb_command & 0x0f;
			if (device == adb_address_keyboard)
			{
			}
			else if (device == adb_address_mouse)
			{
			}
			break;

		default:
			osd_die("ADB command 0x%02x unimplemented", adb_command);
			break;
	}
	adb_kmstatus |= 0x20;
}



static UINT8 adb_read_datareg(void)
{
	UINT8 result;

	switch(adb_state)
	{
		case ADBSTATE_INRESPONSE:
			result = adb_response_bytes[adb_response_pos++];
			if (adb_response_pos >= adb_response_length)
			{
				adb_state = ADBSTATE_IDLE;
				adb_latent_result = result;
				adb_kmstatus &= ~0x20;
			}
			break;

		default:
			result = adb_latent_result;
			break;
	}

	if (LOG_ADB)
		logerror("adb_read_datareg(): result=0x%02x\n", result);

	return result;
}



static void adb_write_datareg(UINT8 data)
{
	if (LOG_ADB)
		logerror("adb_write_datareg(): data=0x%02x\n", data);

	switch(adb_state)
	{
		case ADBSTATE_IDLE:
			adb_command = data;
			adb_command_length = 0;
			adb_command_pos = 0;

			switch(data)
			{
				case 0x00:	/* ??? */
				case 0x01:	/* abort */
					/* do nothing for now */
					break;

				case 0x03:	/* flush keyboard buffer */
					adb_command_length = 0;
					break;

				case 0x04:	/* set modes */
				case 0x05:	/* clear modes */
					adb_command_length = 1;
					break;

				case 0x06:	/* set config */
					adb_command_length = 3;
					break;

				case 0x07:	/* synchronize */
					if (memory_region_length(REGION_CPU1) == 0x40000)	/* HACK */
						adb_command_length = 8;
					else
						adb_command_length = 4;
					break;

				case 0x08:	/* write memory */
				case 0x09:	/* read memory */
					adb_command_length = 2;
					break;

				case 0x0d:	/* get version */
					adb_command_length = 0;
					break;

				case 0x0e:	/* read available charsets */
					adb_command_length = 0;
					adb_state = ADBSTATE_INCOMMAND;	/* HACK */
					break;

				case 0x0f:	/* read available layouts */
					adb_command_length = 0;
					adb_state = ADBSTATE_INCOMMAND;	/* HACK */
					break;

				case 0x12:	/* mystery command 0x12 */
				case 0x13:	/* mystery command 0x13 */
					adb_command_length = 2;
					break;

				case 0x70:	/* disable SRQ device 0 */
				case 0x71:	/* disable SRQ device 1 */
				case 0x72:	/* disable SRQ device 2 */
				case 0x73:	/* disable SRQ device 3 */
					/* ignore for now */
					break;

				case 0xb0: case 0xb1: case 0xb2: case 0xb3:
				case 0xb4: case 0xb5: case 0xb6: case 0xb7:
				case 0xb8: case 0xb9: case 0xba: case 0xbb:
				case 0xbc: case 0xbd: case 0xbe: case 0xbf:
					/* send data to device */
					adb_command_length = 2;
					break;

				default:
					osd_die("ADB command 0x%02x unimplemented", data);
					break;

			}

			if (adb_command_length > 0)
			{
				adb_state = ADBSTATE_INCOMMAND;
				if (LOG_ADB)
					logerror("adb_write_datareg(): in command length %d\n", adb_command_length);
			}
			break;

		case ADBSTATE_INCOMMAND:
			assert(adb_command_pos < (sizeof(adb_command_bytes) / sizeof(adb_command_bytes[0])));
			adb_command_bytes[adb_command_pos++] = data;
			break;

		case ADBSTATE_INRESPONSE:
			adb_state = ADBSTATE_IDLE;
			break;
	}

	/* do command if necessary */
	if ((adb_state == ADBSTATE_INCOMMAND) && (adb_command_pos >= adb_command_length))
		adb_do_command();
}



static UINT8 adb_read_kmstatus(void)
{
	return adb_kmstatus;
}



static void adb_write_kmstatus(UINT8 data)
{
	adb_kmstatus &= ~0x54;
	adb_kmstatus |= data & 0x54;
}



static UINT8 adb_read_mousedata(void)
{
	UINT8 result = 0x00;
	UINT8 absolute;
	INT8 delta;

	if (adb_kmstatus & 0x80)
	{
		if (adb_kmstatus & 0x02)
		{
			absolute = apple2gs_mouse_y;
			delta = apple2gs_mouse_dy;
			adb_kmstatus &= ~0x82;
			apple2gs_remove_irq(IRQ_ADB_MOUSE);
		}
		else
		{
			absolute = apple2gs_mouse_x;
			delta = apple2gs_mouse_dx;
			adb_kmstatus |= 0x02;
		}

		if (delta > 63)
			delta = 63;
		else if (delta < -64)
			delta = -64;

		result = (absolute & 0x80) | (delta & 0x7F);
	}
	return result;
}



static INT8 seven_bit_diff(UINT8 v1, UINT8 v2)
{
	v1 -= v2;
	if (v1 & 0x40)
		v1 |= 0x80;
	else
		v1 &= ~0x80;
	return v1;
}



static void adb_check_mouse(void)
{
	UINT8 new_mouse_x, new_mouse_y;

	/* read mouse values */
	if ((adb_kmstatus & 0x80) == 0x00)
	{
		new_mouse_x = readinputportbytag("adb_mouse_x");
		new_mouse_y = readinputportbytag("adb_mouse_y");

		if ((apple2gs_mouse_x != new_mouse_x) || (apple2gs_mouse_y != new_mouse_y))
		{
			apple2gs_mouse_dx = seven_bit_diff(new_mouse_x, apple2gs_mouse_x);
			apple2gs_mouse_dy = seven_bit_diff(new_mouse_y, apple2gs_mouse_y);
			apple2gs_mouse_x = new_mouse_x;
			apple2gs_mouse_y = new_mouse_y;

			adb_kmstatus |= 0x80;
			adb_kmstatus &= ~0x02;
			if (adb_kmstatus & 0x40)
				apple2gs_add_irq(IRQ_ADB_MOUSE);
		}
	}
}



static void apple2gs_set_scanint(UINT8 data)
{
	/* second interrupt */
	if ((apple2gs_vgcint & 0x40) && !(data & 0x40))
	{
		apple2gs_remove_irq(IRQ_VGC_SECOND);
		apple2gs_vgcint &= ~0xC0;
	}

	/* scanline interrupt */
	if ((apple2gs_vgcint & 0x20) && !(data & 0x20))
	{
		apple2gs_remove_irq(IRQ_VGC_SCANLINE);
		apple2gs_vgcint &= ~0xA0;
	}

	if (apple2gs_pending_irqs & (IRQ_VGC_SECOND | IRQ_VGC_SCANLINE))
		apple2gs_vgcint |= 0x80;
}



INTERRUPT_GEN( apple2gs_interrupt )
{
	int scanline;
	int current_frame;
	static int last_scanline = -1;
	static int scanint_this_frame = 0;
	
	/* TODO: This handler should be called every scanline; that crappyness on
	 * the VBL handler is a consequence of not doing so */
	scanline = cpu_getscanline();
	if (last_scanline != scanline)
	{
		if (scanline == 0)
		{
			current_frame = cpu_getcurrentframe();

			/* quarter second interrupt */
			if ((apple2gs_inten & 0x10) && !(apple2gs_intflag & 0x10) && ((current_frame % 15) == 0))
			{
				apple2gs_intflag |= 0x10;
				apple2gs_add_irq(IRQ_INTEN_QSECOND);
			}

			/* one second interrupt */
			if ((apple2gs_vgcint & 0x04) && !(apple2gs_vgcint & 0x40) && ((current_frame % 60) == 0))
			{
				apple2gs_vgcint |= 0xc0;
				apple2gs_add_irq(IRQ_VGC_SECOND);
			}
		}
		else if ((scanline >= 192) && (last_scanline < 192))
		{
			scanint_this_frame = 0;

			/* VBL interrupt */
			if ((apple2gs_inten & 0x08) && !(apple2gs_intflag & 0x08))
			{
				apple2gs_intflag |= 0x08;
				apple2gs_add_irq(IRQ_INTEN_VBL);
			}
		}
		last_scanline = scanline;
	}

	/* scanline interrupt */
	if ((apple2gs_vgcint & 0x02) && !(apple2gs_vgcint & 0x20))
	{
		if (!scanint_this_frame)
		{
			scanint_this_frame = 1;
			apple2gs_vgcint |= 0xa0;
			apple2gs_add_irq(IRQ_VGC_SCANLINE);
		}
	}

	/* check the mouse status */
	adb_check_mouse();

	/* call Apple II interrupt handler */
	if ((cpu_getscanline() % 8) == 7)
		apple2_interrupt();
}



/* -----------------------------------------------------------------------
 * Sound handlers
 * ----------------------------------------------------------------------- */

static UINT8 sndglu_ctrl;
static int sndglu_addr, sndglu_dummy_read;

static READ8_HANDLER( gssnd_r )
{
	UINT8 ret = 0;

	switch (offset)
	{
		case 0:	// control
			ret = sndglu_ctrl;
			break;
		case 1:	// data read
			if (sndglu_dummy_read)
			{
				sndglu_dummy_read = 0;
				return 0;
			}

			if (sndglu_ctrl & 0x40)	// docram access
			{
				ret = ES5503_ram_0_r(sndglu_addr);
			}
			else
			{
				ret = ES5503_reg_0_r(sndglu_addr);
			}

			if (sndglu_ctrl & 0x20)	// auto-increment
			{
				sndglu_addr++;
			}
			break;
		case 2:	// addr l
			ret = sndglu_addr & 0xff;
			break;
		case 3:	// addr h
			ret = (sndglu_addr >> 8) & 0xff;
			break;
	}

	return ret;
}



static WRITE8_HANDLER( gssnd_w )
{
	switch (offset)
	{
		case 0:	// control
			sndglu_ctrl = data & 0x7f;	// make sure DOC is never busy
			if (!(sndglu_ctrl & 0x40))	// clear hi byte of address pointer on DOC access
			{
				sndglu_addr &= 0xff;
			}
			break;
		case 1:	// data write
			if (sndglu_ctrl & 0x40)	// docram access
			{
				ES5503_ram_0_w(sndglu_addr, data);
			}
			else
			{
				ES5503_reg_0_w(sndglu_addr, data);
			}

			if (sndglu_ctrl & 0x20)	// auto-increment
			{
				sndglu_addr++;
			}
			break;
		case 2:	// addr l
			sndglu_addr &= 0xff00;
			sndglu_addr |= data;
			sndglu_dummy_read = 1;
			break;
		case 3: // addr h
			sndglu_addr &= 0x00ff;
			sndglu_addr |= data<<8;
			sndglu_dummy_read = 1;
			break;
	}
}



/* -----------------------------------------------------------------------
 * IO handlers
 * ----------------------------------------------------------------------- */

static READ8_HANDLER( apple2gs_c0xx_r )
{
	UINT8 result;

	offset &= 0xFF;

	switch(offset)
	{
		case 0x19:	/* C019 - RDVBLBAR */
			result = (cpu_getscanline() >= 192) ? 0x80 : 0x00;
			break;
			
		case 0x22:	/* C022 - TBCOLOR */
			result = (apple2_get_fgcolor() << 4)
				| apple2_get_bgcolor();
			break;

		case 0x23:	/* C023 - VGCINT */
			result = apple2gs_vgcint;
			break;

		case 0x24:	/* C024 - MOUSEDATA */
			result = adb_read_mousedata();
			break;

		case 0x25:	/* C025 - KEYMODREG */
			result = 0x00;
			break;

		case 0x26:	/* C026 - DATAREG */
			result = adb_read_datareg();
			break;

		case 0x27:	/* C027 - KMSTATUS */
			result = adb_read_kmstatus();
			break;

		case 0x29:	/* C029 - NEWVIDEO */
			result = apple2gs_newvideo;
			break;

		case 0x2B:	/* C02B - LANGSEL */
			result = apple2gs_langsel;
			break;

		case 0x2D:	/* C02D - SLTROMSEL */
			result = apple2gs_sltromsel;
			break;

		case 0x2E:	/* C02E - VERTCNT */
			result = cpu_getscanline() + 55;
			break;

		case 0x2F:	/* C02F - HORIZCNT */
			result = ((cpu_getscanline() & 0x01) << 7) | (cpu_gethorzbeampos() / 5);
			break;

		case 0x31:	/* C031 - DISKREG */
			result = apple2_iwm_getdiskreg();
			break;

		case 0x33:	/* C033 - CLOCKDATA */
			result = clock_data;
			break;

		case 0x34:	/* C034 - CLOCKCTL */
			result = clock_control;
			break;

		case 0x35:	/* C035 - SHADOW */
			result = apple2gs_shadow;
			break;

		case 0x36:	/* C036 - CYAREG */
			result = apple2gs_cyareg;
			break;

		case 0x38:	/* C038 - SCCBREG */
		case 0x39:	/* C039 - SCCAREG */
		case 0x3A:	/* C03A - SCCBDATA */
		case 0x3B:	/* C03B - SCCADATA */
			result = scc_r(offset & 0x03);
			break;

		case 0x3C:	/* C03C - SOUNDCTL */
		case 0x3D:	/* C03D - SOUNDDATA */
		case 0x3E:	/* C03E - SOUNDADRL */
		case 0x3F:	/* C03F - SOUNDADRH */
			result = gssnd_r(offset & 0x03);
			break;

		case 0x41:	/* C041 - INTEN */
			result = apple2gs_inten;
			break;

		case 0x46:	/* C046 - INTFLAG */
			result = apple2gs_intflag;
			break;

		case 0x68:	/* C068 - STATEREG */
			result = ((a2 & VAR_ALTZP)	? 0x80 : 0x00)
				|	((a2 & VAR_PAGE2)	? 0x40 : 0x00)
				|	((a2 & VAR_RAMRD)	? 0x20 : 0x00)
				|	((a2 & VAR_RAMWRT)	? 0x10 : 0x00)
				|	((a2 & VAR_LCRAM)	? 0x00 : 0x08)
				|	((a2 & VAR_LCRAM2)	? 0x04 : 0x00)
				|	((a2 & VAR_INTCXROM)? 0x01 : 0x00);
			break;

		case 0x71: case 0x72: case 0x73:
		case 0x74: case 0x75: case 0x76: case 0x77:
		case 0x78: case 0x79: case 0x7a: case 0x7b:
		case 0x7c: case 0x7d: case 0x7e: case 0x7f:
			offset |= (memory_region_length(REGION_CPU1) - 1) & ~0x3FFF;
			result = memory_region(REGION_CPU1)[offset];
			break;

		case 0x21:	/* C021 - MONOCOLOR */
		case 0x2C:	/* C02C - CHARROM */
			result = 0x00;

		default:
			result = apple2_c0xx_r(offset);
			break;
	}

	if (LOG_C0XX)
		logerror("apple2gs_c0xx_r(): offset=0x%02x result=0x%02x\n", offset, result);
	return result;
}



static WRITE8_HANDLER( apple2gs_c0xx_w )
{
	offset &= 0xFF;

	if (LOG_C0XX)
		logerror("apple2gs_c0xx_w(): offset=0x%02x data=0x%02x\n", offset, data);

	switch(offset)
	{
		case 0x22:	/* C022 - TBCOLOR */
			apple2_set_fgcolor((data >> 4) & 0x0F);
			apple2_set_bgcolor((data >> 0) & 0x0F);
			break;

		case 0x23:	/* C023 - VGCINT */
			apple2gs_vgcint &= ~0x0F;
			apple2gs_vgcint |= data & 0x0F;
			break;

		case 0x24:	/* C024 - MOUSEDATA */
		case 0x25:	/* C025 - KEYMODREG */
		case 0x28:	/* C028 - ROMBANK */
		case 0x2C:	/* C02C - CHARROM */
		case 0x2E:	/* C02E - VERTCNT */
		case 0x2F:	/* C02F - HORIZCNT */
			/* ignore these writes */
			break;

		case 0x26:	/* C026 - DATAREG */
			adb_write_datareg(data);
			break;

		case 0x27:	/* C027 - KMSTATUS */
			adb_write_kmstatus(data);
			break;

		case 0x29:	/* C029 - NEWVIDEO */
			apple2gs_newvideo = data;
			break;

		case 0x2B:	/* C02B - LANGSEL */
			apple2gs_langsel = data;
			break;

		case 0x2D:	/* C02D - SLTROMSEL */
			apple2gs_sltromsel = data;
			apple2_update_memory();
			break;

		case 0x31:	/* C031 - DISKREG */
			apple2_iwm_setdiskreg(data);
			break;

		case 0x32:	/* C032 - SCANINT */
			apple2gs_set_scanint(data);
			break;

		case 0x33:	/* C033 - CLOCKDATA */
			clock_data = data;
			break;

		case 0x34:	/* C034 - CLOCKCTL */
			clock_control = data & 0x7F;
			apple2gs_bordercolor = data & 0x0F;
			if (data & 0x80)
				process_clock();
			break;

		case 0x35:	/* C035 - SHADOW */
			if (apple2gs_shadow != data)
			{
				apple2gs_shadow = data;
				apple2_update_memory();
			}
			break;

		case 0x36:	/* C036 - CYAREG */
			apple2gs_cyareg = data & ~0x20;
			cpunum_set_clockscale(0, (data & 0x80) ? 2.8 : 1.0);
			break;

		case 0x38:	/* C038 - SCCBREG */
		case 0x39:	/* C039 - SCCAREG */
		case 0x3A:	/* C03A - SCCBDATA */
		case 0x3B:	/* C03B - SCCADATA */
			scc_w(offset & 0x03, data);
			break;

		case 0x3C:	/* C03C - SOUNDCTL */
		case 0x3D:	/* C03D - SOUNDDATA */
		case 0x3E:	/* C03E - SOUNDADRL */
		case 0x3F:	/* C03F - SOUNDADRH */
			gssnd_w(offset & 0x03, data);
			break;

		case 0x41:	/* C041 - INTEN */
			apple2gs_inten = data & 0x1F;
			if ((apple2gs_inten & 0x10) == 0x00)
				apple2gs_remove_irq(IRQ_INTEN_QSECOND);
			if ((apple2gs_inten & 0x08) == 0x00)
				apple2gs_remove_irq(IRQ_INTEN_VBL);
			break;

		case 0x47:	/* C047 - CLRVBLINT */
			apple2gs_intflag &= ~0x18;
			apple2gs_remove_irq(IRQ_INTEN_QSECOND);
			apple2gs_remove_irq(IRQ_INTEN_VBL);
			break;

		case 0x68:	/* C068 - STATEREG */
			apple2_setvar(
				((data & 0x80) ? VAR_ALTZP : 0) |
				((data & 0x40) ? VAR_PAGE2 : 0) |
				((data & 0x20) ? VAR_RAMRD : 0) |
				((data & 0x10) ? VAR_RAMWRT : 0) |
				((data & 0x08) ? 0 : VAR_LCRAM) |
				((data & 0x04) ? VAR_LCRAM2 : 0) |
				((data & 0x01) ? VAR_INTCXROM : 0),
				VAR_ALTZP | VAR_PAGE2 | VAR_RAMRD | VAR_RAMWRT | VAR_LCRAM | VAR_LCRAM2 | VAR_INTCXROM);
			break;

		default:
			apple2_c0xx_w(offset, data);
			break;
	}
}



/* -----------------------------------------------------------------------
 * Memory management
 * ----------------------------------------------------------------------- */

static WRITE8_HANDLER( apple2gs_main0400_w )
{
	offset += 0x000400;
	mess_ram[offset] = data;

	if (!(apple2gs_shadow & 0x01))
	{
		apple2gs_slowmem[offset] = data;
		apple2_video_touch(offset);
	}
}

static WRITE8_HANDLER( apple2gs_aux0400_w )
{
	offset += 0x010400;
	mess_ram[offset] = data;

	if (!(apple2gs_shadow & 0x01))
	{
		apple2gs_slowmem[offset] = data;
		apple2_video_touch(offset);
	}
}

static WRITE8_HANDLER( apple2gs_main2000_w )
{
	offset += 0x002000;
	mess_ram[offset] = data;

	if (!(apple2gs_shadow & 0x02))
	{
		apple2gs_slowmem[offset] = data;
		apple2_video_touch(offset);
	}
}

static WRITE8_HANDLER( apple2gs_aux2000_w )
{
	offset += 0x012000;
	mess_ram[offset] = data;

	if (!(apple2gs_shadow & 0x12) || !(apple2gs_shadow & 0x08))
	{
		apple2gs_slowmem[offset] = data;
		apple2_video_touch(offset);
	}
}

static WRITE8_HANDLER( apple2gs_main4000_w )
{
	offset += 0x004000;
	mess_ram[offset] = data;

	if ((offset >= 0x004000) && (offset <= 0x005FFF))
	{
		if (!(apple2gs_shadow & 0x04))
			apple2gs_slowmem[offset] = data;
	}
}

static WRITE8_HANDLER( apple2gs_aux4000_w )
{
	offset += 0x014000;
	mess_ram[offset] = data;

	if ((offset >= 0x014000) && (offset <= 0x015FFF))
	{
		if (!(apple2gs_shadow & 0x14) || !(apple2gs_shadow & 0x08))
			apple2gs_slowmem[offset] = data;
	}
	else if ((offset >= 0x016000) && (offset <= 0x019FFF))
	{
		if (!(apple2gs_shadow & 0x08))
			apple2gs_slowmem[offset] = data;
	}
}



static void apple2gs_mem_000000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	meminfo->read_mem			= (a2 & VAR_ALTZP)	? 0x010000 : 0x000000;
	meminfo->write_mem			= (a2 & VAR_ALTZP)	? 0x010000 : 0x000000;
}

static void apple2gs_mem_000200(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	meminfo->read_mem			= (a2 & VAR_RAMRD)	? 0x010200 : 0x000200;
	meminfo->write_mem			= (a2 & VAR_RAMWRT)	? 0x010200 : 0x000200;
}

static void apple2gs_mem_000400(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	if (a2 & VAR_80STORE)
	{
		meminfo->read_mem		= (a2 & VAR_PAGE2)	? 0x010400 : 0x000400;
		meminfo->write_mem		= (a2 & VAR_PAGE2)	? 0x010400 : 0x000400;
		meminfo->write_handler	= (a2 & VAR_PAGE2)	? apple2gs_aux0400_w : apple2gs_main0400_w;
	}
	else
	{
		meminfo->read_mem		= (a2 & VAR_RAMRD)	? 0x010400 : 0x000400;
		meminfo->write_mem		= (a2 & VAR_RAMWRT)	? 0x010400 : 0x000400;
		meminfo->write_handler	= (a2 & VAR_RAMWRT)	? apple2gs_aux0400_w : apple2gs_main0400_w;
	}
}

static void apple2gs_mem_000800(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	meminfo->read_mem			= (a2 & VAR_RAMRD)	? 0x010800 : 0x000800;
	meminfo->write_mem			= (a2 & VAR_RAMWRT)	? 0x010800 : 0x000800;
}

static void apple2gs_mem_002000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	if ((a2 & (VAR_80STORE|VAR_HIRES)) == (VAR_80STORE|VAR_HIRES))
	{
		meminfo->read_mem		= (a2 & VAR_PAGE2)	? 0x012000 : 0x002000;
		meminfo->write_mem		= (a2 & VAR_PAGE2)	? 0x012000 : 0x002000;
		meminfo->write_handler	= (a2 & VAR_PAGE2)	? apple2gs_aux2000_w : apple2gs_main2000_w;
	}
	else
	{
		meminfo->read_mem		= (a2 & VAR_RAMRD)	? 0x012000 : 0x002000;
		meminfo->write_mem		= (a2 & VAR_RAMWRT)	? 0x012000 : 0x002000;
		meminfo->write_handler	= (a2 & VAR_RAMWRT)	? apple2gs_aux2000_w : apple2gs_main2000_w;
	}
}

static void apple2gs_mem_004000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	meminfo->read_mem			= (a2 & VAR_RAMRD)	? 0x014000 : 0x004000;
	meminfo->write_handler		= (a2 & VAR_RAMWRT)	? apple2gs_aux4000_w : apple2gs_main4000_w;
}

static void apple2gs_mem_xxD000(struct apple2_meminfo *meminfo, UINT32 lcmem)
{
	if (a2 & VAR_LCRAM)
	{
		if (a2 & VAR_LCRAM2)
			meminfo->read_mem	= lcmem | 0x00C000;
		else
			meminfo->read_mem	= lcmem | 0x00D000;
	}
	else
	{
		meminfo->read_mem		= 0x03D000 | APPLE2_MEM_ROM;
	}
	
	if (a2 & VAR_LCWRITE)
	{
		if (a2 & VAR_LCRAM2)
			meminfo->write_mem	= lcmem | 0x00C000;
		else
			meminfo->write_mem	= lcmem | 0x00D000;
	}
	else
	{
		meminfo->write_mem = APPLE2_MEM_FLOATING;
	}
}

static void apple2gs_mem_xxE000(struct apple2_meminfo *meminfo, UINT32 lcmem)
{
	if (a2 & VAR_LCRAM)
		meminfo->read_mem		= lcmem | 0x00E000;
	else
		meminfo->read_mem		= 0x03E000 | APPLE2_MEM_ROM;

	if (a2 & VAR_LCWRITE)
		meminfo->write_mem		= lcmem | 0x00E000;
	else
		meminfo->write_mem		= APPLE2_MEM_FLOATING;
}

static void apple2gs_mem_00D000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	if (apple2gs_shadow & 0x40)
	{
		meminfo->read_mem		= (a2 & VAR_RAMRD) ? 0x01D000 : 0x00D000;
		meminfo->write_mem		= (a2 & VAR_RAMWRT) ? 0x01D000 : 0x00D000;
	}
	else
	{
		apple2gs_mem_xxD000(meminfo, (a2 & VAR_ALTZP) ? 0x010000 : 0x000000);
	}
}

static void apple2gs_mem_00E000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	if (apple2gs_shadow & 0x40)
	{
		meminfo->read_mem		= (a2 & VAR_RAMRD) ? 0x01E000 : 0x00E000;
		meminfo->write_mem		= (a2 & VAR_RAMWRT) ? 0x01E000 : 0x00E000;
	}
	else
	{
		apple2gs_mem_xxE000(meminfo, (a2 & VAR_ALTZP) ? 0x010000 : 0x000000);
	}
}

static void apple2gs_mem_01D000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	if (apple2gs_shadow & 0x40)
	{
		meminfo->read_mem		= 0x01D000;
		meminfo->write_mem		= 0x01D000;
	}
	else
	{
		apple2gs_mem_xxD000(meminfo, 0x010000);
	}
}

static void apple2gs_mem_01E000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	if (apple2gs_shadow & 0x40)
	{
		meminfo->read_mem		= 0x01E000;
		meminfo->write_mem		= 0x01E000;
	}
	else
	{
		apple2gs_mem_xxE000(meminfo, 0x010000);
	}
}

static void apple2gs_mem_E0D000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	apple2gs_mem_xxD000(meminfo, 0x000000 | APPLE2_MEM_AUX);
}

static void apple2gs_mem_E0E000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	apple2gs_mem_xxE000(meminfo, 0x000000 | APPLE2_MEM_AUX);
}

static void apple2gs_mem_E1D000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	apple2gs_mem_xxD000(meminfo, 0x010000 | APPLE2_MEM_AUX);
}

static void apple2gs_mem_E1E000(offs_t begin, offs_t end, struct apple2_meminfo *meminfo)
{
	apple2gs_mem_xxE000(meminfo, 0x010000 | APPLE2_MEM_AUX);
}



static const struct apple2_memmap_entry apple2gs_memmap_entries[] =
{
	{ 0x000000, 0x0001FF, apple2gs_mem_000000, A2MEM_MONO },
	{ 0x000200, 0x0003FF, apple2gs_mem_000200, A2MEM_DUAL },
	{ 0x000400, 0x0007FF, apple2gs_mem_000400, A2MEM_DUAL },
	{ 0x000800, 0x001FFF, apple2gs_mem_000800, A2MEM_DUAL },
	{ 0x002000, 0x003FFF, apple2gs_mem_002000, A2MEM_DUAL },
	{ 0x004000, 0x00BFFF, apple2gs_mem_004000, A2MEM_DUAL },
	{ 0x00D000, 0x00DFFF, apple2gs_mem_00D000, A2MEM_DUAL },
	{ 0x00E000, 0x00FFFF, apple2gs_mem_00E000, A2MEM_DUAL },

	{ 0x01D000, 0x01DFFF, apple2gs_mem_01D000, A2MEM_DUAL },
	{ 0x01E000, 0x01FFFF, apple2gs_mem_01E000, A2MEM_DUAL },
	{ 0xE0D000, 0xE0DFFF, apple2gs_mem_E0D000, A2MEM_DUAL },
	{ 0xE0E000, 0xE0FFFF, apple2gs_mem_E0E000, A2MEM_DUAL },
	{ 0xE1D000, 0xE1DFFF, apple2gs_mem_E1D000, A2MEM_DUAL },
	{ 0xE1E000, 0xE1FFFF, apple2gs_mem_E1E000, A2MEM_DUAL },

	{ 0 }
};



static UINT8 *apple2gs_getslotmem(offs_t address)
{
	UINT8 *rom;

	address %= 0x00FFFF;
	assert(address >= 0xC000);
	assert(address <= 0xCFFF);

	rom = memory_region(REGION_CPU1);
	rom += 0x030000 % memory_region_length(REGION_CPU1);
	return &rom[address];
}



static UINT8 apple2gs_xxCxxx_r(offs_t address)
{
	UINT8 result;
	int slot;

	if ((apple2gs_shadow & 0x40) && ((address & 0xF00000) == 0x000000))
	{
		result = mess_ram[address];
	}
	else if ((address & 0x000F00) == 0x000000)
	{
		result = apple2gs_c0xx_r(address);
	}
	else
	{
		slot = (address & 0x000F00) / 0x100;

		if ((slot > 7) || ((apple2gs_sltromsel & (1 << slot)) == 0))
			result = *apple2gs_getslotmem(address);
		else
			result = apple2_getfloatingbusvalue();
	}
	return result;
}



static void apple2gs_xxCxxx_w(offs_t address, UINT8 data)
{
	int slot;

	if ((apple2gs_shadow & 0x40) && ((address & 0xF00000) == 0x000000))
	{
		mess_ram[address] = data;
	}
	else if ((address & 0x000F00) == 0x000000)
	{
		apple2gs_c0xx_w(address, data);
	}
	else
	{
		slot = (address & 0x000F00) / 0x100;

		if ((slot > 7) || ((apple2gs_sltromsel & (1 << slot)) == 0))
			*apple2gs_getslotmem(address) = data;
	}
}



static OPBASE_HANDLER( apple2gs_opbase )
{
	UINT8 *opptr = NULL;
	int slot;
	
	if (((address & 0xFEF000) == 0x00C000) || ((address & 0xFEF000) == 0xE0C000))
	{
		if ((apple2gs_shadow & 0x40) && ((address & 0xF00000) == 0x000000))
		{
			opptr = &mess_ram[address];
		}
		else if ((address & 0x000F00) == 0x000000)
		{
			if (((address & 0xFF) >= 0x71) && ((address & 0xFF) <= 0x7F))
				opptr = apple2gs_getslotmem(address);
		}
		else
		{
			slot = (address & 0x000F00) / 0x100;

			if ((slot > 7) || ((apple2gs_sltromsel & (1 << slot)) == 0))
				opptr = apple2gs_getslotmem(address);
		}

		if (opptr)
		{
			opcode_mask = ~0;
			opcode_base = opcode_arg_base = opptr - address;
			opcode_memory_min = address;
			opcode_memory_max = address;
			address = ~0;
		}
	}
	return address;
}



static READ8_HANDLER( apple2gs_00Cxxx_r ) { return apple2gs_xxCxxx_r(offset | 0x00C000); }
static READ8_HANDLER( apple2gs_01Cxxx_r ) { return apple2gs_xxCxxx_r(offset | 0x01C000); }
static READ8_HANDLER( apple2gs_E0Cxxx_r ) { return apple2gs_xxCxxx_r(offset | 0xE0C000); }
static READ8_HANDLER( apple2gs_E1Cxxx_r ) { return apple2gs_xxCxxx_r(offset | 0xE1C000); }

static WRITE8_HANDLER( apple2gs_00Cxxx_w) { apple2gs_xxCxxx_w(offset | 0x00C000, data); }
static WRITE8_HANDLER( apple2gs_01Cxxx_w) { apple2gs_xxCxxx_w(offset | 0x01C000, data); }
static WRITE8_HANDLER( apple2gs_E0Cxxx_w) { apple2gs_xxCxxx_w(offset | 0xE0C000, data); }
static WRITE8_HANDLER( apple2gs_E1Cxxx_w) { apple2gs_xxCxxx_w(offset | 0xE1C000, data); }

static WRITE8_HANDLER( apple2gs_Exxxxx_w )
{
	apple2gs_slowmem[offset] = data;
	apple2_video_touch(offset);
}

static WRITE8_HANDLER( apple2gs_E004xx_w ) { apple2gs_Exxxxx_w(offset + 0x00400, data); }
static WRITE8_HANDLER( apple2gs_E02xxx_w ) { apple2gs_Exxxxx_w(offset + 0x02000, data); }
static WRITE8_HANDLER( apple2gs_E104xx_w ) { apple2gs_Exxxxx_w(offset + 0x10400, data); }
static WRITE8_HANDLER( apple2gs_E12xxx_w ) { apple2gs_Exxxxx_w(offset + 0x12000, data); }



static void apple2gs_setup_memory(void)
{
	offs_t begin, end;
	struct apple2_memmap_config cfg;

	/* allocate memory for E00000-E1FFFF */
	apple2gs_slowmem = auto_malloc(128*1024);
	memset(apple2gs_slowmem, 0, 128*1024);

	/* install expanded memory */
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x010000, mess_ram_size - 1, 0, 0, MRA8_BANK1);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x010000, mess_ram_size - 1, 0, 0, MWA8_BANK1);
	memory_set_bankptr(1, mess_ram + 0x010000);

	/* install hi memory */
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe00000, 0xe1ffff, 0, 0, MRA8_BANK2);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe00000, 0xe1ffff, 0, 0, MWA8_BANK2);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe00400, 0xe007ff, 0, 0, apple2gs_E004xx_w);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe02000, 0xe03fff, 0, 0, apple2gs_E02xxx_w);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe10400, 0xe107ff, 0, 0, apple2gs_E104xx_w);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe12000, 0xe13fff, 0, 0, apple2gs_E12xxx_w);
	memory_set_bankptr(2, apple2gs_slowmem);

	/* install alternate ROM bank */
	begin = 0x1000000 - memory_region_length(REGION_CPU1);
	end = 0xffffff;
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, begin, end, 0, 0, MRA8_BANK3);
	memory_set_bankptr(3, memory_region(REGION_CPU1));

	/* install new xxC000-xxCFFF handlers */
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x00c000, 0x00cfff, 0, 0, apple2gs_00Cxxx_r);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x00c000, 0x00cfff, 0, 0, apple2gs_00Cxxx_w);
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0x01c000, 0x01cfff, 0, 0, apple2gs_01Cxxx_r);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x01c000, 0x01cfff, 0, 0, apple2gs_01Cxxx_w);
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe0c000, 0xe0cfff, 0, 0, apple2gs_E0Cxxx_r);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe0c000, 0xe0cfff, 0, 0, apple2gs_E0Cxxx_w);
	memory_install_read8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe1c000, 0xe1cfff, 0, 0, apple2gs_E1Cxxx_r);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0xe1c000, 0xe1cfff, 0, 0, apple2gs_E1Cxxx_w);
	memory_set_opbase_handler(0, apple2gs_opbase);

	/* install aux memory writes (for shadowing) */
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x010400, 0x0107FF, 0, 0, apple2gs_aux0400_w);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x012000, 0x013FFF, 0, 0, apple2gs_aux2000_w);
	memory_install_write8_handler(0, ADDRESS_SPACE_PROGRAM, 0x014000, 0x019FFF, 0, 0, apple2gs_aux4000_w);

	/* setup the Apple II memory system */
	memset(&cfg, 0, sizeof(cfg));
	cfg.first_bank = 4;
	cfg.memmap = apple2gs_memmap_entries;
	cfg.auxmem = apple2gs_slowmem;
	cfg.auxmem_length = 0x20000;
	apple2_setup_memory(&cfg);
}



/* -----------------------------------------------------------------------
 * Driver Init
 * ----------------------------------------------------------------------- */

static READ8_HANDLER( apple2gs_read_vector )
{
	return program_read_byte(offset | 0xFF0000);
}



DRIVER_INIT( apple2gs )
{
	struct apple2_config cfg;
	
	memset(&cfg, 0, sizeof(cfg));
	cfg.slots[0] = &apple2_slot_langcard;
	cfg.slots[4] = &apple2_slot_mockingboard;
	cfg.slots[6] = &apple2_slot_iwm;
	
	apple2_init_common(&cfg);

	/* set up Apple IIgs vectoring */
	cpunum_set_info_fct(0, CPUINFO_PTR_G65816_READVECTOR_CALLBACK, (genf *) apple2gs_read_vector);

	/* setup globals */
	apple2gs_cur_slot6_image = NULL;
	apple2gs_langsel = 0;
	apple2gs_cyareg = 0x80;
	apple2gs_inten = 0x00;
	apple2gs_vgcint = 0x00;
	apple2gs_intflag = 0x00;
	apple2gs_shadow = 0x08;
	apple2gs_newvideo = 0;
	apple2gs_pending_irqs = 0x00;
	adb_state = ADBSTATE_IDLE;
	adb_kmstatus = 0x10;
	adb_command = 0;
	adb_mode = 0;
	adb_latent_result = 0;
	adb_command_length = 0;
	adb_command_pos = 0;
	memset(adb_command_bytes, 0, sizeof(adb_command_bytes));
	memset(adb_response_bytes, 0, sizeof(adb_response_bytes));
	adb_response_length = 0;
	adb_response_pos = 0;
	memset(adb_memory, 0, sizeof(adb_memory));
	adb_address_keyboard = 2;
	adb_address_mouse = 3;
	apple2gs_mouse_x = 0x00;
	apple2gs_mouse_y = 0x00;
	apple2gs_mouse_dx = 0x00;
	apple2gs_mouse_dy = 0x00;

	/* init time */
	clock_curtime = 0;
	clock_curtime_interval = 0;

	/* init the various subsystems */
	scc_init(NULL);
	apple2gs_setup_memory();
}
