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

    Atari 400/800

    Machine driver

    Juergen Buchmueller, June 1998

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

#include "driver.h"
#include "cpu/m6502/m6502.h"
#include "includes/atari.h"
#include "sound/pokey.h"
#include "machine/6821pia.h"
#include "sound/dac.h"
#include "video/gtia.h"

#define VERBOSE_POKEY	0
#define VERBOSE_SERIAL	0
#define VERBOSE_TIMERS	0

//#define LOG(x) if (errorlog) fprintf x

static int atari = 0;
#define ATARI_5200	0
#define ATARI_400	1
#define ATARI_800	2
#define ATARI_600XL 3
#define ATARI_800XL 4

#ifdef MESS
static int a800_cart_loaded = 0;
static int a800_cart_is_16k = 0;
#endif

static void a800xl_mmu(running_machine *machine, UINT8 new_mmu);
static void a600xl_mmu(running_machine *machine, UINT8 new_mmu);

static void pokey_reset(running_machine *machine);

void atari_interrupt_cb(const device_config *device, int mask)
{

	if (VERBOSE_POKEY)
	{
		if (mask & 0x80)
			logerror("atari interrupt_cb BREAK\n");
		if (mask & 0x40)
			logerror("atari interrupt_cb KBCOD\n");
	}
	if (VERBOSE_SERIAL)
	{
		if (mask & 0x20)
			logerror("atari interrupt_cb SERIN\n");
		if (mask & 0x10)
			logerror("atari interrupt_cb SEROR\n");
		if (mask & 0x08)
			logerror("atari interrupt_cb SEROC\n");
	}
	if (VERBOSE_TIMERS)
	{
		if (mask & 0x04)
			logerror("atari interrupt_cb TIMR4\n");
		if (mask & 0x02)
			logerror("atari interrupt_cb TIMR2\n");
		if (mask & 0x01)
			logerror("atari interrupt_cb TIMR1\n");
	}

	cpu_set_input_line(device->machine->cpu[0], 0, HOLD_LINE);
}

/**************************************************************
 *
 * PIA interface
 *
 **************************************************************/

static READ8_DEVICE_HANDLER(atari_pia_pa_r)
{
	return atari_input_disabled() ? 0xFF : input_port_read_safe(device->machine, "djoy_0_1", 0);
}

static READ8_DEVICE_HANDLER(atari_pia_pb_r)
{
	return atari_input_disabled() ? 0xFF : input_port_read_safe(device->machine, "djoy_2_3", 0);
}

static WRITE8_DEVICE_HANDLER(a600xl_pia_pb_w) { a600xl_mmu(device->machine, data); }
static WRITE8_DEVICE_HANDLER(a800xl_pia_pb_w) { a800xl_mmu(device->machine, data); }

#ifndef MESS
WRITE_LINE_DEVICE_HANDLER(atari_pia_cb2_w) { }
#endif

const pia6821_interface atari_pia_interface =
{
	DEVCB_HANDLER(atari_pia_pa_r),		/* port A in */
	DEVCB_HANDLER(atari_pia_pb_r),	/* port B in */
	DEVCB_NULL,		/* line CA1 in */
	DEVCB_NULL,		/* line CB1 in */
	DEVCB_NULL,		/* line CA2 in */
	DEVCB_NULL,		/* line CB2 in */
	DEVCB_NULL,		/* port A out */
	DEVCB_NULL,		/* port B out */
	DEVCB_NULL,		/* line CA2 out */
	DEVCB_LINE(atari_pia_cb2_w),		/* port CB2 out */
	DEVCB_NULL,		/* IRQA */
	DEVCB_NULL		/* IRQB */
};

const pia6821_interface a600xl_pia_interface =
{
	DEVCB_HANDLER(atari_pia_pa_r),		/* port A in */
	DEVCB_HANDLER(atari_pia_pb_r),	/* port B in */
	DEVCB_NULL,		/* line CA1 in */
	DEVCB_NULL,		/* line CB1 in */
	DEVCB_NULL,		/* line CA2 in */
	DEVCB_NULL,		/* line CB2 in */
	DEVCB_NULL,		/* port A out */
	DEVCB_HANDLER(a600xl_pia_pb_w),		/* port B out */
	DEVCB_NULL,		/* line CA2 out */
	DEVCB_LINE(atari_pia_cb2_w),		/* port CB2 out */
	DEVCB_NULL,		/* IRQA */
	DEVCB_NULL		/* IRQB */
};

const pia6821_interface a800xl_pia_interface =
{
	DEVCB_HANDLER(atari_pia_pa_r),		/* port A in */
	DEVCB_HANDLER(atari_pia_pb_r),	/* port B in */
	DEVCB_NULL,		/* line CA1 in */
	DEVCB_NULL,		/* line CB1 in */
	DEVCB_NULL,		/* line CA2 in */
	DEVCB_NULL,		/* line CB2 in */
	DEVCB_NULL,		/* port A out */
	DEVCB_HANDLER(a800xl_pia_pb_w),		/* port B out */
	DEVCB_NULL,		/* line CA2 out */
	DEVCB_LINE(atari_pia_cb2_w),		/* port CB2 out */
	DEVCB_NULL,		/* IRQA */
	DEVCB_NULL		/* IRQB */
};




/**************************************************************
 *
 * Reset hardware
 *
 **************************************************************/

void a600xl_mmu(running_machine *machine, UINT8 new_mmu)
{
	read8_space_func rbank2;
	write8_space_func wbank2;

	/* check if self-test ROM changed */
	if ( new_mmu & 0x80 )
	{
		logerror("%s MMU SELFTEST RAM\n", machine->gamedrv->name);
		rbank2 = SMH_NOP;
		wbank2 = SMH_NOP;
	}
	else
	{
		logerror("%s MMU SELFTEST ROM\n", machine->gamedrv->name);
		rbank2 = SMH_BANK2;
		wbank2 = SMH_UNMAP;
	}
	memory_install_readwrite8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0x5000, 0x57ff, 0, 0, rbank2, wbank2);
	if (rbank2 == SMH_BANK2)
		memory_set_bankptr(machine, 2, memory_region(machine, "maincpu")+0x5000);
}

void a800xl_mmu(running_machine *machine, UINT8 new_mmu)
{
	read8_space_func rbank1, rbank2, rbank3, rbank4;
	write8_space_func wbank1, wbank2, wbank3, wbank4;
	UINT8 *base1, *base2, *base3, *base4;

	/* check if memory C000-FFFF changed */
	if( new_mmu & 0x01 )
	{
		logerror("%s MMU BIOS ROM\n", machine->gamedrv->name);
		rbank3 = SMH_BANK3;
		wbank3 = SMH_UNMAP;
		base3 = memory_region(machine, "maincpu")+0x14000;  /* 8K lo BIOS */
		rbank4 = SMH_BANK4;
		wbank4 = SMH_UNMAP;
		base4 = memory_region(machine, "maincpu")+0x15800;  /* 4K FP ROM + 8K hi BIOS */
	}
	else
	{
		logerror("%s MMU BIOS RAM\n", machine->gamedrv->name);
		rbank3 = SMH_BANK3;
		wbank3 = SMH_BANK3;
		base3 = memory_region(machine, "maincpu")+0x0c000;  /* 8K RAM */
		rbank4 = SMH_BANK4;
		wbank4 = SMH_BANK4;
		base4 = memory_region(machine, "maincpu")+0x0d800;  /* 4K RAM + 8K RAM */
	}
	memory_install_readwrite8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0xc000, 0xcfff, 0, 0, rbank3, wbank3);
	memory_install_readwrite8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0xd800, 0xffff, 0, 0, rbank4, wbank4);
	memory_set_bankptr(machine, 3, base3);
	memory_set_bankptr(machine, 4, base4);

	/* check if BASIC changed */
	if( new_mmu & 0x02 )
	{
		logerror("%s MMU BASIC RAM\n", machine->gamedrv->name);
		rbank1 = SMH_BANK1;
		wbank1 = SMH_BANK1;
		base1 = memory_region(machine, "maincpu")+0x0a000;  /* 8K RAM */
	}
	else
	{
		logerror("%s MMU BASIC ROM\n", machine->gamedrv->name);
		rbank1 = SMH_BANK1;
		wbank1 = SMH_UNMAP;
		base1 = memory_region(machine, "maincpu")+0x10000;  /* 8K BASIC */
	}
	memory_install_readwrite8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0xa000, 0xbfff, 0, 0, rbank1, wbank1);
	memory_set_bankptr(machine, 1, base1);

	/* check if self-test ROM changed */
	if( new_mmu & 0x80 )
	{
		logerror("%s MMU SELFTEST RAM\n", machine->gamedrv->name);
		rbank2 = SMH_BANK2;
		wbank2 = SMH_BANK2;
		base2 = memory_region(machine, "maincpu")+0x05000;  /* 0x0800 bytes */
	}
	else
	{
		logerror("%s MMU SELFTEST ROM\n", machine->gamedrv->name);
		rbank2 = SMH_BANK2;
		wbank2 = SMH_UNMAP;
		base2 = memory_region(machine, "maincpu")+0x15000;  /* 0x0800 bytes */
	}
	memory_install_readwrite8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0x5000, 0x57ff, 0, 0, rbank2, wbank2);
	memory_set_bankptr(machine, 2, base2);
}



/**************************************************************
 *
 * Keyboard
 *
 **************************************************************/

#define AKEY_L			0x00
#define AKEY_J			0x01
#define AKEY_SEMICOLON	0x02
#define AKEY_BREAK		0x03	/* this not really a scancode */
#define AKEY_K			0x05
#define AKEY_PLUS		0x06
#define AKEY_ASTERISK	0x07
#define AKEY_O			0x08
#define AKEY_NONE		0x09
#define AKEY_P			0x0a
#define AKEY_U			0x0b
#define AKEY_ENTER		0x0c
#define AKEY_I			0x0d
#define AKEY_MINUS		0x0e
#define AKEY_EQUALS 	0x0f
#define AKEY_V			0x10
#define AKEY_C			0x12
#define AKEY_B			0x15
#define AKEY_X			0x16
#define AKEY_Z			0x17
#define AKEY_4			0x18
#define AKEY_3			0x1a
#define AKEY_6			0x1b
#define AKEY_ESC		0x1c
#define AKEY_5			0x1d
#define AKEY_2			0x1e
#define AKEY_1			0x1f
#define AKEY_COMMA		0x20
#define AKEY_SPACE		0x21
#define AKEY_STOP		0x22
#define AKEY_N			0x23
#define AKEY_M			0x25
#define AKEY_SLASH		0x26
#define AKEY_ATARI		0x27
#define AKEY_R			0x28
#define AKEY_E			0x2a
#define AKEY_Y			0x2b
#define AKEY_TAB		0x2c
#define AKEY_T			0x2d
#define AKEY_W			0x2e
#define AKEY_Q			0x2f
#define AKEY_9			0x30
#define AKEY_0			0x32
#define AKEY_7			0x33
#define AKEY_BSP		0x34
#define AKEY_8			0x35
#define AKEY_LESSER 	0x36
#define AKEY_GREATER	0x37
#define AKEY_F			0x38
#define AKEY_H			0x39
#define AKEY_D			0x3a
#define AKEY_CAPS		0x3c
#define AKEY_G			0x3d
#define AKEY_S			0x3e
#define AKEY_A			0x3f
#define ASHF_L			0x40
#define ASHF_J			0x41
#define ASHF_COLON		0x42
#define ASHF_BREAK		0x43	/* this not really a scancode */
#define ASHF_K			0x45
#define ASHF_BACKSLASH	0x46
#define ASHF_TILDE		0x47
#define ASHF_O			0x48
#define ASHF_SHIFT		0x49
#define ASHF_P			0x4a
#define ASHF_U			0x4b
#define ASHF_ENTER		0x4c
#define ASHF_I			0x4d
#define ASHF_UNDERSCORE 0x4e
#define ASHF_BAR		0x4f
#define ASHF_V			0x50
#define ASHF_C			0x52
#define ASHF_B			0x55
#define ASHF_X			0x56
#define ASHF_Z			0x57
#define ASHF_DOLLAR 	0x58
#define ASHF_HASH		0x5a
#define ASHF_AMPERSAND	0x5b
#define ASHF_ESC		0x5c
#define ASHF_PERCENT	0x5d
#define ASHF_DQUOTE 	0x5e
#define ASHF_EXCLAM 	0x5f
#define ASHF_LBRACE 	0x60
#define ASHF_SPACE		0x61
#define ASHF_RBRACE 	0x62
#define ASHF_N			0x63
#define ASHF_M			0x65
#define ASHF_QUESTION	0x66
#define ASHF_ATARI		0x67
#define ASHF_R			0x68
#define ASHF_E			0x6a
#define ASHF_Y			0x6b
#define ASHF_TAB		0x6c
#define ASHF_T			0x6d
#define ASHF_W			0x6e
#define ASHF_Q			0x6f
#define ASHF_LPAREN 	0x70
#define ASHF_RPAREN 	0x72
#define ASHF_QUOTE		0x73
#define ASHF_BSP		0x74
#define ASHF_AT 		0x75
#define ASHF_CLEAR		0x76
#define ASHF_INSERT 	0x77
#define ASHF_F			0x78
#define ASHF_H			0x79
#define ASHF_D			0x7a
#define ASHF_CAPS		0x7c
#define ASHF_G			0x7d
#define ASHF_S			0x7e
#define ASHF_A			0x7f
#define ACTL_L			0x80
#define ACTL_J			0x81
#define ACTL_SEMICOLON	0x82
#define ACTL_BREAK		0x83	/* this not really a scancode */
#define ACTL_K			0x85
#define ACTL_PLUS		0x86
#define ACTL_ASTERISK	0x87
#define ACTL_O			0x88
#define ACTL_CONTROL	0x89
#define ACTL_P			0x8a
#define ACTL_U			0x8b
#define ACTL_ENTER		0x8c
#define ACTL_I			0x8d
#define ACTL_MINUS		0x8e
#define ACTL_EQUALS 	0x8f
#define ACTL_V			0x90
#define ACTL_C			0x92
#define ACTL_B			0x95
#define ACTL_X			0x96
#define ACTL_Z			0x97
#define ACTL_4			0x98
#define ACTL_3			0x9a
#define ACTL_6			0x9b
#define ACTL_ESC		0x9c
#define ACTL_5			0x9d
#define ACTL_2			0x9e
#define ACTL_1			0x9f
#define ACTL_COMMA		0xa0
#define ACTL_SPACE		0xa1
#define ACTL_STOP		0xa2
#define ACTL_N			0xa3
#define ACTL_M			0xa5
#define ACTL_SLASH		0xa6
#define ACTL_ATARI		0xa7
#define ACTL_R			0xa8
#define ACTL_E			0xaa
#define ACTL_Y			0xab
#define ACTL_TAB		0xac
#define ACTL_T			0xad
#define ACTL_W			0xae
#define ACTL_Q			0xaf
#define ACTL_9			0xb0
#define ACTL_0			0xb2
#define ACTL_7			0xb3
#define ACTL_BSP		0xb4
#define ACTL_8			0xb5
#define ACTL_LESSER 	0xb6
#define ACTL_GREATER	0xb7
#define ACTL_F			0xb8
#define ACTL_H			0xb9
#define ACTL_D			0xba
#define ACTL_CAPS		0xbc
#define ACTL_G			0xbd
#define ACTL_S			0xbe
#define ACTL_A			0xbf
#define ACSH_L			0xc0
#define ACSH_J			0xc1
#define ACSH_COLON		0xc2
#define ACSH_BREAK		0xc3	/* this not really a scancode */
#define ACSH_K			0xc5
#define ACSH_BACKSLASH	0xc6
#define ACSH_TILDE		0xc7
#define ACSH_O			0xc8
#define ACSH_CTRLSHIFT	0xc9
#define ACSH_P			0xca
#define ACSH_U			0xcb
#define ACSH_ENTER		0xcc
#define ACSH_I			0xcd
#define ACSH_UNDERSCORE 0xce
#define ACSH_BAR		0xcf
#define ACSH_V			0xd0
#define ACSH_C			0xd2
#define ACSH_B			0xd5
#define ACSH_X			0xd6
#define ACSH_Z			0xd7
#define ACSH_DOLLAR 	0xd8
#define ACSH_HASH		0xda
#define ACSH_AMPERSAND	0xdb
#define ACSH_ESC		0xdc
#define ACSH_PERCENT	0xdd
#define ACSH_DQUOTE 	0xde
#define ACSH_EXCLAM 	0xdf
#define ACSH_LBRACE 	0xe0
#define ACSH_SPACE		0xe1
#define ACSH_RBRACE 	0xe2
#define ACSH_N			0xe3
#define ACSH_M			0xe5
#define ACSH_QUESTION	0xe6
#define ACSH_ATARI		0xe7
#define ACSH_R			0xe8
#define ACSH_E			0xea
#define ACSH_Y			0xeb
#define ACSH_TAB		0xec
#define ACSH_T			0xed
#define ACSH_W			0xee
#define ACSH_Q			0xef
#define ACSH_LPAREN 	0xf0
#define ACSH_RPAREN 	0xf2
#define ACSH_QUOTE		0xf3
#define ACSH_BSP		0xf4
#define ACSH_AT 		0xf5
#define ACSH_CLEAR		0xf6
#define ACSH_INSERT 	0xf7
#define ACSH_F			0xf8
#define ACSH_H			0xf9
#define ACSH_D			0xfa
#define ACSH_CAPS		0xfc
#define ACSH_G			0xfd
#define ACSH_S			0xfe
#define ACSH_A			0xff

static const UINT8 keys[64][4] = {
{AKEY_NONE		 ,AKEY_NONE 	  ,AKEY_NONE	   ,AKEY_NONE		}, /* ""       CODE_NONE             */
{AKEY_ESC		 ,ASHF_ESC		  ,ACTL_ESC 	   ,ACSH_ESC		}, /*"Escape" KEYCODE_ESC              */
{AKEY_1 		 ,ASHF_EXCLAM	  ,ACTL_1		   ,ACSH_EXCLAM 	}, /* "1 !"    KEYCODE_1               */
{AKEY_2 		 ,ASHF_DQUOTE	  ,ACTL_2		   ,ACSH_DQUOTE 	}, /* "2 \""   KEYCODE_2               */
{AKEY_3 		 ,ASHF_HASH 	  ,ACTL_3		   ,ACSH_HASH		}, /* "3 #"    KEYCODE_3               */
{AKEY_4 		 ,ASHF_DOLLAR	  ,ACTL_4		   ,ACSH_DOLLAR 	}, /* "4 $"    KEYCODE_4               */
{AKEY_5 		 ,ASHF_PERCENT	  ,ACTL_5		   ,ACSH_PERCENT	}, /* "5 %"    KEYCODE_5               */
{AKEY_6 		 ,ASHF_TILDE	  ,ACTL_6		   ,ACSH_AMPERSAND	}, /* "6 ^"    KEYCODE_6               */
{AKEY_7 		 ,ASHF_AMPERSAND  ,ACTL_7		   ,ACSH_N			}, /* "7 &"    KEYCODE_7               */
{AKEY_8 		 ,AKEY_ASTERISK   ,ACTL_8		   ,ACSH_M			}, /* "8 *"    KEYCODE_8               */
{AKEY_9 		 ,ASHF_LPAREN	  ,ACTL_9		   ,ACSH_LBRACE 	}, /* "9 ("    KEYCODE_9               */
{AKEY_0 		 ,ASHF_RPAREN	  ,ACTL_0		   ,ACSH_RBRACE 	}, /* "0 )"    KEYCODE_0               */
{AKEY_MINUS 	 ,ASHF_UNDERSCORE ,ACTL_MINUS	   ,ACSH_UNDERSCORE }, /* "- _"    KEYCODE_MINUS           */
{AKEY_EQUALS	 ,AKEY_PLUS 	  ,ACTL_EQUALS	   ,ACTL_PLUS		}, /* "= +"    KEYCODE_EQUALS          */
{AKEY_BSP		 ,ASHF_BSP		  ,ACTL_BSP 	   ,ACSH_BSP		}, /* "Backsp" KEYCODE_BACKSPACE       */
{AKEY_TAB		 ,ASHF_TAB		  ,ACTL_TAB 	   ,ACSH_TAB		}, /* "Tab"    KEYCODE_TAB             */
{AKEY_Q 		 ,ASHF_Q		  ,ACTL_Q		   ,ACSH_Q			}, /* "q Q"    KEYCODE_Q               */
{AKEY_W 		 ,ASHF_W		  ,ACTL_W		   ,ACSH_W			}, /* "w W"    KEYCODE_W               */
{AKEY_E 		 ,ASHF_E		  ,ACTL_E		   ,ACSH_E			}, /* "e E"    KEYCODE_E               */
{AKEY_R 		 ,ASHF_R		  ,ACTL_R		   ,ACSH_R			}, /* "r R"    KEYCODE_R               */
{AKEY_T 		 ,ASHF_T		  ,ACTL_T		   ,ACSH_T			}, /* "t T"    KEYCODE_T               */
{AKEY_Y 		 ,ASHF_Y		  ,ACTL_Y		   ,ACSH_Y			}, /* "y Y"    KEYCODE_Y               */
{AKEY_U 		 ,ASHF_U		  ,ACTL_U		   ,ACTL_U			}, /* "u U"    KEYCODE_U               */
{AKEY_I 		 ,ASHF_I		  ,ACTL_I		   ,ACTL_I			}, /* "i I"    KEYCODE_I               */
{AKEY_O 		 ,ASHF_O		  ,ACTL_O		   ,ACTL_O			}, /* "o O"    KEYCODE_O               */
{AKEY_P 		 ,ASHF_P		  ,ACTL_P		   ,ACTL_P			}, /* "p P"    KEYCODE_P               */
{ASHF_LBRACE	 ,ACTL_COMMA	  ,ACTL_COMMA	   ,ACSH_LBRACE 	}, /* "[ {"    KEYCODE_LBRACE          */
{ASHF_RBRACE	 ,ACTL_STOP 	  ,ACTL_STOP	   ,ACSH_RBRACE 	}, /* "] }"    KEYCODE_RBRACE          */
{AKEY_ENTER 	 ,ASHF_ENTER	  ,ACTL_ENTER	   ,ACSH_ENTER		}, /* "Enter"  KEYCODE_ENTER           */
{AKEY_A 		 ,ASHF_A		  ,ACTL_A		   ,ACSH_A			}, /* "a A"    KEYCODE_A               */
{AKEY_S 		 ,ASHF_S		  ,ACTL_S		   ,ACSH_S			}, /* "s S"    KEYCODE_S               */
{AKEY_D 		 ,ASHF_D		  ,ACTL_D		   ,ACSH_D			}, /* "d D"    KEYCODE_D               */
{AKEY_F 		 ,ASHF_F		  ,ACTL_F		   ,ACSH_F			}, /* "f F"    KEYCODE_F               */
{AKEY_G 		 ,ASHF_G		  ,ACTL_G		   ,ACSH_G			}, /* "g G"    KEYCODE_G               */
{AKEY_H 		 ,ASHF_H		  ,ACTL_H		   ,ACSH_H			}, /* "h H"    KEYCODE_H               */
{AKEY_J 		 ,ASHF_J		  ,ACTL_J		   ,ACSH_J			}, /* "j J"    KEYCODE_J               */
{AKEY_K 		 ,ASHF_K		  ,ACTL_K		   ,ACSH_K			}, /* "k K"    KEYCODE_K               */
{AKEY_L 		 ,ASHF_L		  ,ACTL_L		   ,ACSH_L			}, /* "l L"    KEYCODE_L               */
{AKEY_SEMICOLON  ,ASHF_COLON	  ,ACTL_SEMICOLON  ,ACSH_COLON		}, /* "; :"    KEYCODE_COLON           */
{ASHF_QUOTE 	 ,ACSH_QUOTE	  ,ASHF_DQUOTE	   ,ACSH_DQUOTE 	}, /* "+ \\"   KEYCODE_QUOTE           */
{ASHF_QUOTE 	 ,ACSH_QUOTE	  ,ACTL_ASTERISK   ,ACSH_TILDE		}, /* "* ^"    KEYCODE_TILDE           */
{ASHF_BACKSLASH  ,ASHF_BAR		  ,ACSH_BACKSLASH  ,ACSH_BAR		}, /* "\ |"    KEYCODE_BACKSLASH       */
{AKEY_Z 		 ,ASHF_Z		  ,ACTL_Z		   ,ACSH_Z			}, /* "z Z"    KEYCODE_Z               */
{AKEY_X 		 ,ASHF_X		  ,ACTL_X		   ,ACTL_X			}, /* "x X"    KEYCODE_X               */
{AKEY_C 		 ,ASHF_C		  ,ACTL_C		   ,ACTL_C			}, /* "c C"    KEYCODE_C               */
{AKEY_V 		 ,ASHF_V		  ,ACTL_V		   ,ACTL_V			}, /* "v V"    KEYCODE_V               */
{AKEY_B 		 ,ASHF_B		  ,ACTL_B		   ,ACTL_B			}, /* "b B"    KEYCODE_B               */
{AKEY_N 		 ,ASHF_N		  ,ACTL_N		   ,ACTL_N			}, /* "n N"    KEYCODE_N               */
{AKEY_M 		 ,ASHF_M		  ,ACTL_M		   ,ACTL_M			}, /* "m M"    KEYCODE_M               */
{AKEY_COMMA 	 ,AKEY_LESSER	  ,ACTL_COMMA	   ,ACTL_LESSER 	}, /* ", ["    KEYCODE_COMMA           */
{AKEY_STOP		 ,AKEY_GREATER	  ,ACTL_STOP	   ,ACTL_GREATER	}, /* ". ]"    KEYCODE_STOP            */
{AKEY_SLASH 	 ,ASHF_QUESTION   ,ACTL_SLASH	   ,ACSH_QUESTION	}, /* "/ ?"    KEYCODE_SLASH           */
{ASHF_BACKSLASH  ,ASHF_BAR		  ,ACSH_BACKSLASH  ,ACSH_BAR		}, /* "\ |"    KEYCODE_BACKSLASH2      */
{AKEY_ATARI 	 ,ASHF_ATARI	  ,ACTL_ATARI	   ,ACSH_ATARI		}, /* "Atari"  KEYCODE_LALT            */
{AKEY_SPACE 	 ,ASHF_SPACE	  ,ACTL_SPACE	   ,ACSH_SPACE		}, /* "Space"  KEYCODE_SPACE           */
{AKEY_CAPS		 ,ASHF_CAPS 	  ,ACTL_CAPS	   ,ACSH_CAPS		}, /* "Caps"   KEYCODE_CAPSLOCK        */
{ASHF_CLEAR 	 ,ASHF_CLEAR	  ,ACSH_CLEAR	   ,ACSH_CLEAR		}, /* "Clear"  KEYCODE_HOME            */
{ASHF_INSERT	 ,ASHF_INSERT	  ,ASHF_INSERT	   ,ASHF_INSERT 	}, /* "Insert" KEYCODE_INSERT          */
{AKEY_BSP		 ,AKEY_BSP		  ,AKEY_BSP 	   ,AKEY_BSP		}, /* "Delete" KEYCODE_DEL             */
{AKEY_BREAK 	 ,ASHF_BREAK	  ,ACTL_BREAK	   ,ACSH_BREAK		}, /* "Break"  KEYCODE_PGUP            */
{ACTL_PLUS		 ,ACTL_PLUS 	  ,ACTL_PLUS	   ,ACTL_PLUS		}, /* "(Left)" KEYCODE_LEFT            */
{ACTL_ASTERISK	 ,ACTL_ASTERISK   ,ACTL_ASTERISK   ,ACTL_ASTERISK	}, /* "(Right)"KEYCODE_RIGHT           */
{ACTL_MINUS 	 ,ACTL_MINUS	  ,ACTL_MINUS	   ,ACTL_MINUS		}, /* "(Up)"   KEYCODE_UP              */
{ACTL_EQUALS	 ,ACTL_EQUALS	  ,ACTL_EQUALS	   ,ACTL_EQUALS 	}  /* "(Down)" KEYCODE_DOWN            */
};


void a800_handle_keyboard(running_machine *machine)
{
	const device_config *pokey = devtag_get_device(machine, "pokey");
	static int atari_last = 0xff;
	int i, modifiers, atari_code;
	char tag[64];

    modifiers = 0;

    /* with shift ? */
	if( input_code_pressed(KEYCODE_LSHIFT) || input_code_pressed(KEYCODE_RSHIFT) )
		modifiers |= 1;

    /* with control ? */
	if( input_code_pressed(KEYCODE_LCONTROL) || input_code_pressed(KEYCODE_RCONTROL) )
		modifiers |= 2;

	for( i = 0; i < 64; i++ )
	{
		sprintf(tag, "keyboard_%d", i / 16);
		if( input_port_read_safe(machine, tag, 0) & (1 << (i&15)) )
		{
			atari_code = keys[i][modifiers];
			if( atari_code != AKEY_NONE )
			{
				if( atari_code == atari_last )
					return;
				atari_last = atari_code;
				if( (atari_code & 0x3f) == AKEY_BREAK )
				{
					pokey_break_w(pokey, atari_code & 0x40);
					return;
				}
				pokey_kbcode_w(pokey, atari_code, 1);
				return;
			}
		}
	}
	/* remove key pressed status bit from skstat */
	pokey_kbcode_w(pokey, AKEY_NONE, 0);
	atari_last = AKEY_NONE;
}

#define VKEY_BREAK		0x10

/* absolutely no clue what to do here :((( */
void a5200_handle_keypads(running_machine *machine)
{
	const device_config *pokey = devtag_get_device(machine, "pokey");
	int i, modifiers;
	static int atari_last = 0xff;

    modifiers = 0;

    /* with shift ? */
	if (input_code_pressed(KEYCODE_LSHIFT) || input_code_pressed(KEYCODE_RSHIFT))
		modifiers |= 1;

    /* with control ? */
	if (input_code_pressed(KEYCODE_LCONTROL) || input_code_pressed(KEYCODE_RCONTROL))
		modifiers |= 2;

	/* check keypad */
	for (i = 0; i < 16; i++)
	{
		if( input_port_read(machine, "keypad") & (1 << i) )
		{
			if( i == atari_last )
				return;
			atari_last = i;
			if( i == 0 )
			{
				pokey_break_w(pokey, i & 0x40);
				return;
			}
			pokey_kbcode_w(pokey, (i << 1) | 0x21, 1);
			return;
		}
	}

	/* check top button */
	if ((input_port_read(machine, "djoy_b") & 0x10) == 0)
	{
		if (atari_last == 0xFE)
			return;
		pokey_kbcode_w(pokey, 0x61, 1);
		//pokey_break_w(pokey, 0x40);
		atari_last = 0xFE;
		return;
	}
	else if (atari_last == 0xFE)
		pokey_kbcode_w(pokey, 0x21, 1);

	/* remove key pressed status bit from skstat */
	pokey_kbcode_w(pokey, 0xFF, 0);
	atari_last = 0xff;
}

#ifdef MESS
DRIVER_INIT( atari )
{
	offs_t ram_top;
	offs_t ram_size;

	if (!strcmp(machine->gamedrv->name, "a400")
		|| !strcmp(machine->gamedrv->name, "a400pal")
		|| !strcmp(machine->gamedrv->name, "a800")
		|| !strcmp(machine->gamedrv->name, "a800pal")
		|| !strcmp(machine->gamedrv->name, "a800xl"))
	{
		ram_size = 0xA000;
	}
	else
	{
		ram_size = 0x8000;
	}

	/* install RAM */
	ram_top = MIN(mess_ram_size, ram_size) - 1;
	memory_install_read8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM),
		0x0000, ram_top, 0, 0, SMH_BANK2);
	memory_install_write8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM),
		0x0000, ram_top, 0, 0, SMH_BANK2);
	memory_set_bankptr(machine, 2, mess_ram);
}
#endif



/*************************************
 *
 *  Generic Atari Code
 *
 *************************************/

#ifdef MESS
static void a800_setbank(running_machine *machine, int n)
{
	void *read_addr;
	void *write_addr;
	UINT8 *mem = memory_region(machine, "maincpu");

	switch (n)
	{
		case 1:
			read_addr = &mem[0x10000];
			write_addr = NULL;
			break;
		default:
			if( atari <= ATARI_400 )
			{
				/* Atari 400 has no RAM here, so install the NOP handler */
				read_addr = NULL;
				write_addr = NULL;
			}
			else
			{
				read_addr = &mess_ram[0x08000];
				write_addr = &mess_ram[0x08000];
			}
			break;
	}

	memory_install_read8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0x8000, 0xbfff, 0, 0,
		read_addr ? SMH_BANK1 : SMH_NOP);
	memory_install_write8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM), 0x8000, 0xbfff, 0, 0,
		write_addr ? SMH_BANK1 : SMH_NOP);
	if (read_addr)
		memory_set_bankptr(machine, 1, read_addr);
	if (write_addr)
		memory_set_bankptr(machine, 1, write_addr);
}
#endif



static void pokey_reset(running_machine *machine)
{
	const device_config *pokey = devtag_get_device(machine, "pokey");
	pokey_w(pokey,15,0);
}



static void cart_reset(running_machine *machine)
{
#ifdef MESS
	if (a800_cart_loaded)
		a800_setbank(machine, 1);
#endif /* MESS */
}



static UINT8 console_read(const address_space *space)
{
	return input_port_read(space->machine, "console");
}



static void console_write(const address_space *space, UINT8 data)
{
	const device_config *dac = devtag_get_device(space->machine, "dac");
	if (data & 0x08)
		dac_data_w(dac, -120);
	else
		dac_data_w(dac, +120);
}


static void _antic_reset(running_machine *machine)
{
	antic_reset();
}


static void atari_machine_start(running_machine *machine, int type, int has_cart)
{
	gtia_interface gtia_intf;

	atari = type;

	/* GTIA */
	memset(&gtia_intf, 0, sizeof(gtia_intf));
	if (input_port_by_tag(machine->portconfig, "console") != NULL)
		gtia_intf.console_read = console_read;
	if (devtag_get_device(machine, "dac") != NULL)
		gtia_intf.console_write = console_write;
	gtia_init(machine, &gtia_intf);

	/* pokey */
	add_reset_callback(machine, pokey_reset);

	/* ANTIC */
	add_reset_callback(machine, _antic_reset);

	/* cartridge */
	if (has_cart)
		add_reset_callback(machine, cart_reset);

#ifdef MESS
	{
		offs_t ram_top;
		offs_t ram_size;

		if (!strcmp(machine->gamedrv->name, "a400")
			|| !strcmp(machine->gamedrv->name, "a400pal")
			|| !strcmp(machine->gamedrv->name, "a800")
			|| !strcmp(machine->gamedrv->name, "a800pal")
			|| !strcmp(machine->gamedrv->name, "a800xl"))
		{
			ram_size = 0xA000;
		}
		else
		{
			ram_size = 0x8000;
		}

		/* install RAM */
		ram_top = MIN(mess_ram_size, ram_size) - 1;
		memory_install_read8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM),
			0x0000, ram_top, 0, 0, SMH_BANK2);
		memory_install_write8_handler(cpu_get_address_space(machine->cpu[0], ADDRESS_SPACE_PROGRAM),
			0x0000, ram_top, 0, 0, SMH_BANK2);
		memory_set_bankptr(machine, 2, mess_ram);
	}
#endif /* MESS */

	/* save states */
	state_save_register_global_pointer(machine, ((UINT8 *) &antic.r), sizeof(antic.r));
	state_save_register_global_pointer(machine, ((UINT8 *) &antic.w), sizeof(antic.w));
}



/*************************************
 *
 *  Atari 400
 *  Atari 600XL
 *
 *************************************/

MACHINE_START( a400 )
{
	atari_machine_start(machine, ATARI_400, TRUE);
}

MACHINE_START( a600xl )
{
	atari_machine_start(machine, ATARI_600XL, TRUE);
}



/*************************************
 *
 *  Atari 800
 *
 *************************************/

MACHINE_START( a800 )
{
	atari_machine_start(machine, ATARI_800, TRUE);
}



#ifdef MESS
DEVICE_IMAGE_LOAD( a800_cart )
{
	UINT8 *mem = memory_region(image->machine, "maincpu");
	int size;

	/* load an optional (dual) cartridge (e.g. basic.rom) */
	if( strcmp(image->tag,"cart2") == 0 )
	{
		size = image_fread(image, &mem[0x12000], 0x2000);
		a800_cart_is_16k = (size == 0x2000);
		logerror("%s loaded right cartridge '%s' size 16K\n", image->machine->gamedrv->name, image_filename(image) );
	}
	else
	{
		size = image_fread(image, &mem[0x10000], 0x2000);
		a800_cart_loaded = size > 0x0000;
		size = image_fread(image, &mem[0x12000], 0x2000);
		a800_cart_is_16k = size > 0x2000;
		logerror("%s loaded left cartridge '%s' size %s\n", image->machine->gamedrv->name, image_filename(image) , (a800_cart_is_16k) ? "16K":"8K");
	}
	return INIT_PASS;
}

DEVICE_IMAGE_UNLOAD( a800_cart )
{
	if( strcmp(image->tag,"cart2") == 0 )
	{
		a800_cart_is_16k = 0;
		a800_setbank(image->machine, 1);
    }
	else
	{
		a800_cart_loaded = 0;
		a800_setbank(image->machine, 0);
    }
}
#endif



/*************************************
 *
 *  Atari 800XL
 *
 *************************************/

MACHINE_START( a800xl )
{
	atari_machine_start(machine, ATARI_800XL, TRUE);
}



#ifdef MESS
DEVICE_IMAGE_LOAD( a800xl_cart )
{
	UINT8 *mem = memory_region(image->machine, "maincpu");
	astring *fname;
	mame_file *basic_fp;
	file_error filerr;
	unsigned size;

	fname = astring_assemble_3(astring_alloc(), image->machine->gamedrv->name, PATH_SEPARATOR, "basic.rom");
	filerr = mame_fopen(SEARCHPATH_ROM, astring_c(fname), OPEN_FLAG_READ, &basic_fp);
	astring_free(fname);

	if (filerr != FILERR_NONE)
	{
		size = mame_fread(basic_fp, &mem[0x14000], 0x2000);
		if( size < 0x2000 )
		{
			logerror("%s image '%s' load failed (less than 8K)\n", image->machine->gamedrv->name, astring_c(fname));
			mame_fclose(basic_fp);
			return 2;
		}
	}

	/* load an optional (dual) cartidge (e.g. basic.rom) */
	if (filerr != FILERR_NONE)
	{
		{
			size = image_fread(image, &mem[0x14000], 0x2000);
			a800_cart_loaded = size / 0x2000;
			size = image_fread(image, &mem[0x16000], 0x2000);
			a800_cart_is_16k = size / 0x2000;
			logerror("%s loaded cartridge '%s' size %s\n",
					image->machine->gamedrv->name, image_filename(image), (a800_cart_is_16k) ? "16K":"8K");
		}
		mame_fclose(basic_fp);
	}

	return INIT_PASS;
}
#endif



/*************************************
 *
 *  Atari 5200 console
 *
 *************************************/

MACHINE_START( a5200 )
{
	atari_machine_start(machine, ATARI_800XL, FALSE);
}



#ifdef MESS
DEVICE_IMAGE_LOAD( a5200_cart )
{
	UINT8 *mem = memory_region(image->machine, "maincpu");
	int size;

	/* load an optional (dual) cartidge */
	size = image_fread(image, &mem[0x4000], 0x8000);
	if (size<0x8000) memmove(mem+0x4000+0x8000-size, mem+0x4000, size);
	// mirroring of smaller cartridges
	if (size <= 0x1000) memcpy(mem+0xa000, mem+0xb000, 0x1000);
	if (size <= 0x2000) memcpy(mem+0x8000, mem+0xa000, 0x2000);
	if (size <= 0x4000)
	{
		const char *info;
		memcpy(&mem[0x4000], &mem[0x8000], 0x4000);
		info = image_extrainfo(image);
		if (info!=NULL && strcmp(info, "A13MIRRORING")==0)
		{
			memcpy(&mem[0x8000], &mem[0xa000], 0x2000);
			memcpy(&mem[0x6000], &mem[0x4000], 0x2000);
		}
	}
	logerror("%s loaded cartridge '%s' size %dK\n",
		image->machine->gamedrv->name, image_filename(image) , size/1024);
	return INIT_PASS;
}

DEVICE_IMAGE_UNLOAD( a5200_cart )
{
	UINT8 *mem = memory_region(image->machine, "maincpu");
	/* zap the cartridge memory (again) */
	memset(&mem[0x4000], 0x00, 0x8000);
}
#endif

