#include	<stdio.h>
#include	<stdlib.h>

#include	"system.h"
#include	"cpu.h"
#include	"atari.h"
#include	"atari_custom.h"

#define	FALSE	0
#define	TRUE	1

int	colortable[128];

/*
	=========================================
	Allocate variables for Hardware Registers
	=========================================
*/

UBYTE	ALLPOT;
UBYTE	AUDC1;
UBYTE	AUDC2;
UBYTE	AUDC3;
UBYTE	AUDC4;
UBYTE	AUDCTL;
UBYTE	AUDF1;
UBYTE	AUDF2;
UBYTE	AUDF3;
UBYTE	AUDF4;
UBYTE	CHACTL;
UBYTE	CHBASE;
UBYTE	COLBK;
UBYTE	COLPF0;
UBYTE	COLPF1;
UBYTE	COLPF2;
UBYTE	COLPF3;
UBYTE	COLPM0;
UBYTE	COLPM1;
UBYTE	COLPM2;
UBYTE	COLPM3;
UBYTE	CONSOL;
UBYTE	DLISTH;
UBYTE	DLISTL;
UBYTE	DMACTL;
UBYTE	GRACTL;
UBYTE	GRAFM;
UBYTE	GRAFP0;
UBYTE	GRAFP1;
UBYTE	GRAFP2;
UBYTE	GRAFP3;
UBYTE	HITCLR;
UBYTE	HPOSM0;
UBYTE	HPOSM1;
UBYTE	HPOSM2;
UBYTE	HPOSM3;
UBYTE	HPOSP0;
UBYTE	HPOSP1;
UBYTE	HPOSP2;
UBYTE	HPOSP3;
UBYTE	HSCROL;
UBYTE	IRQEN;
UBYTE	IRQST;
UBYTE	KBCODE;
UBYTE	M0PF;
UBYTE	M0PL;
UBYTE	M1PF;
UBYTE	M1PL;
UBYTE	M2PF;
UBYTE	M2PL;
UBYTE	M3PF;
UBYTE	M3PL;
UBYTE	NMIEN;
UBYTE	NMIRES;
UBYTE	NMIST;
UBYTE	P0PF;
UBYTE	P0PL;
UBYTE	P1PF;
UBYTE	P1PL;
UBYTE	P2PF;
UBYTE	P2PL;
UBYTE	P3PF;
UBYTE	P3PL;
UBYTE	PACTL;
UBYTE	PAL;
UBYTE	PBCTL;
UBYTE	PENH;
UBYTE	PENV;
UBYTE	PMBASE;
UBYTE	PORTA;
UBYTE	PORTB;
UBYTE	POT0;
UBYTE	POT1;
UBYTE	POT2;
UBYTE	POT3;
UBYTE	POT4;
UBYTE	POT5;
UBYTE	POT6;
UBYTE	POT7;
UBYTE	POTGO;
UBYTE	PRIOR;
UBYTE	RANDOM;
UBYTE	SERIN;
UBYTE	SEROUT;
UBYTE	SIZEM;
UBYTE	SIZEP0;
UBYTE	SIZEP1;
UBYTE	SIZEP2;
UBYTE	SIZEP3;
UBYTE	SKCTL;
UBYTE	SKREST;
UBYTE	SKSTAT;
UBYTE	STIMER;
UBYTE	TRIG0;
UBYTE	TRIG1;
UBYTE	TRIG2;
UBYTE	TRIG3;
UBYTE	VCOUNT;
UBYTE	VDELAY;
UBYTE	VSCROL;
UBYTE	WSYNC;

extern UBYTE	*super;

/*
	============================================================
	atari_basic and under_basic are required for XL/XE emulation
	============================================================
*/

int	rom_inserted;
UBYTE	atari_basic[8192];
UBYTE	atarixl_os[16384];
UBYTE	under_atari_basic[8192];
UBYTE	under_atarixl_os[16384];

#ifndef BASIC
UWORD	scanline[ATARI_WIDTH];
int	ypos;
#endif

UBYTE Atari800_GetByte (UWORD addr);
void  Atari800_PutByte (UWORD addr, UBYTE byte);
void  Atari800_Hardware (void);

void Atari800_ReadColourTable ()
{
	FILE	*fp;
	char	string[128];
	int	i;

	fp = fopen ("colours.dat", "r");
	if (!fp)
	{
		perror ("colours.dat");
		exit (1);
	}

	while (fgets(string, 128, fp) != 0)
	{
		int	colnum;
		int	red;
		int	green;
		int	blue;
		int	rgb;

		sscanf (string, "%04x%04x%04x%04x", &colnum, &red, &green, &blue);

		red = (red << 8) & 0x00ff0000;
		green = green & 0x0000ff00;
		blue = (blue >> 8) & 0x000000ff;

		rgb = red | green | blue;
		colortable[colnum>>1] = rgb;
	}

	fclose (fp);
}

void Atari800_Initialise ()
{
	int	i;

	Atari800_ReadColourTable ();

#ifndef BASIC
	Atari_Initialise ();
#endif

	for (i=0;i<65536;i++)
	{
		memory[i] = 0;
		attrib[i] = RAM;
	}
/*
	=======================================
	Install functions for CPU memory access
	=======================================
*/
	XGetByte = Atari800_GetByte;
	XPutByte = Atari800_PutByte;
	Hardware = Atari800_Hardware;
/*
	=============================
	Initialise Hardware Registers
	=============================
*/
	CONSOL = 0x07;
	PORTA = 0xff;
	PORTB = 0xff;
}

void Atari800_Exit ()
{
#ifndef BASIC
	Atari_Exit ();
#endif
}

void SetRAM (int addr1, int addr2)
{
	int	i;

	for (i=addr1;i<=addr2;i++)
	{
		attrib[i] = RAM;
	}
}

void SetROM (int addr1, int addr2)
{
	int	i;

	for (i=addr1;i<=addr2;i++)
	{
		attrib[i] = ROM;
	}
}

void SetHARDWARE (int addr1, int addr2)
{
	int	i;

	for (i=addr1;i<=addr2;i++)
	{
		attrib[i] = HARDWARE;
	}
}

/*
	*****************************************************************
	*								*
	*	Section			:	Player Missile Graphics	*
	*	Original Author		:	David Firth		*
	*	Date Written		:	28th May 1995		*
	*	Version			:	1.0			*
	*								*
	*****************************************************************
*/

/*
	=========================================
	Width of each bit within a Player/Missile
	=========================================
*/

#ifndef BASIC
static UBYTE	PM_Width[4] = { 2, 4, 2, 8 };
static int	PM_XPos[256];

static UBYTE	singleline;
static UWORD	pl0adr;
static UWORD	pl1adr;
static UWORD	pl2adr;
static UWORD	pl3adr;
static UWORD	m0123adr;

void PM_InitFrame ()
{
	UWORD	pmbase = PMBASE << 8;

	static int	init = FALSE;

	if (!init)
	{
		int	i;

		for (i=0;i<256;i++)
		{
			PM_XPos[i] = (i - 0x20) * 2;
		}

		init = TRUE;
	}

	switch (DMACTL & 0x10)
	{
		case 0x00 :
			singleline = FALSE;
			m0123adr = pmbase + 384 + 4;
			pl0adr = pmbase + 512 + 4;
			pl1adr = pmbase + 640 + 4;
			pl2adr = pmbase + 768 + 4;
			pl3adr = pmbase + 896 + 4;
			break;
		case 0x10 :
			singleline = TRUE;
			m0123adr = pmbase + 768 + 8;
			pl0adr = pmbase + 1024 + 8;
			pl1adr = pmbase + 1280 + 8;
			pl2adr = pmbase + 1536 + 8;
			pl3adr = pmbase + 1792 + 8;
			break;
	}
}

void PM_ScanLine ()
{
	int	hposp0;
	int	hposp1;
	int	hposp2;
	int	hposp3;

	int	hposm0;
	int	hposm1;
	int	hposm2;
	int	hposm3;

	UBYTE	grafp0 = memory[pl0adr];
	UBYTE	grafp1 = memory[pl1adr];
	UBYTE	grafp2 = memory[pl2adr];
	UBYTE	grafp3 = memory[pl3adr];
	UBYTE	grafm = memory[m0123adr];

	UBYTE	s0;
	UBYTE	s1;
	UBYTE	s2;
	UBYTE	s3;
	UBYTE	sm;

	int	nextdata;
	int	i;

	hposp0 = PM_XPos[HPOSP0];
	hposp1 = PM_XPos[HPOSP1];
	hposp2 = PM_XPos[HPOSP2];
	hposp3 = PM_XPos[HPOSP3];

	hposm0 = PM_XPos[HPOSM0];
	hposm1 = PM_XPos[HPOSM1];
	hposm2 = PM_XPos[HPOSM2];
	hposm3 = PM_XPos[HPOSM3];

	s0 = PM_Width[SIZEP0 & 0x03];
	s1 = PM_Width[SIZEP1 & 0x03];
	s2 = PM_Width[SIZEP2 & 0x03];
	s3 = PM_Width[SIZEP3 & 0x03];
	sm = PM_Width[SIZEM & 0x03];

	for (i=0;i<8;i++)
	{
		int	j;

		if (grafp0 & 0x80)
		{
			for (j=0;j<s0;j++)
			{
				if ((hposp0 >= 0) && (hposp0 < ATARI_WIDTH))
					scanline[hposp0] |= 0x0100;
				hposp0++;
			}
		}
		else
		{
			hposp0 += s0;
		}

		if (grafp1 & 0x80)
		{
			for (j=0;j<s1;j++)
			{
				if ((hposp1 >= 0) && (hposp1 < ATARI_WIDTH))
					scanline[hposp1] |= 0x0200;
				hposp1++;
			}
		}
		else
		{
			hposp1 += s1;
		}

		if (grafp2 & 0x80)
		{
			for (j=0;j<s2;j++)
			{
				if ((hposp2 >= 0) && (hposp2 < ATARI_WIDTH))
					scanline[hposp2] |= 0x0400;
				hposp2++;
			}
		}
		else
		{
			hposp2 += s2;
		}

		if (grafp3 & 0x80)
		{
			for (j=0;j<s3;j++)
			{
				if ((hposp3 >= 0) && (hposp3 < ATARI_WIDTH))
					scanline[hposp3] |= 0x0800;
				hposp3++;
			}
		}
		else
		{
			hposp3 += s3;
		}

		if (grafm & 0x80)
		{
			for (j=0;j<sm;j++)
			{
				switch (i & 0x06)
				{
					case 0x00 :
						if ((hposm3 >= 0) && (hposm3 < ATARI_WIDTH))
							scanline[hposm3] |= 0x8000;
						hposm3++;
						break;
					case 0x02 :
						if ((hposm2 >= 0) && (hposm2 < ATARI_WIDTH))
							scanline[hposm2] |= 0x4000;
						hposm2++;
						break;
					case 0x04 :
						if ((hposm1 >= 0) && (hposm1 < ATARI_WIDTH))
							scanline[hposm1] |= 0x2000;
						hposm1++;
						break;
					case 0x06 :
						if ((hposm0 >= 0) && (hposm0 < ATARI_WIDTH))
							scanline[hposm0] |= 0x1000;
						hposm0++;
						break;
				}
			}
		}
		else
		{
			switch (i & 0x06)
			{
				case 0x00 :
					hposm3 += sm;
					break;
				case 0x02 :
					hposm2 += sm;
					break;
				case 0x04 :
					hposm1 += sm;
					break;
				case 0x06 :
					hposm0 += sm;
					break;
			}
		}

		grafp0 = grafp0 << 1;
		grafp1 = grafp1 << 1;
		grafp2 = grafp2 << 1;
		grafp3 = grafp3 << 1;
		grafm = grafm << 1;
	}

	if (singleline)
		nextdata = TRUE;
	else
		nextdata = (ypos & 0x01);

	if (nextdata)
	{
		pl0adr++;
		pl1adr++;
		pl2adr++;
		pl3adr++;
		m0123adr++;
	}
}
#endif

/*
	*****************************************************************
	*								*
	*	Section			:	Antic Display Modes	*
	*	Original Author		:	David Firth		*
	*	Date Written		:	28th May 1995		*
	*	Version			:	1.0			*
	*								*
	*								*
	*   Description							*
	*   -----------							*
	*								*
	*   Section that handles Antic display modes. Not required	*
	*   for BASIC version.						*
	*								*
	*****************************************************************
*/

#ifndef BASIC
#ifndef CURSES

static UWORD	screenaddr;
static UWORD	chbase;

static int	xmin;
static int	xmax;

void antic_blank (int nlines)
{
	while (nlines > 0)
	{
		int	i;

		for (i=0;i<ATARI_WIDTH;i++) scanline[i] = 0;

		if (DMACTL & 0x0c)
		{
			PM_ScanLine ();
		}

		Atari_ScanLine ();

		ypos++;
		nlines--;
	}
}

void antic_2 ()
{
	UWORD	chadr[48];
	UBYTE	invert[48];
	UBYTE	blank[48];
	int	charoffset;
	int	chardelta;
	int	i;

	int	nchars;
	int	invert_mask;
	int	blank_mask;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 2\n");
#endif

	chbase = CHBASE << 8;

	nchars = (xmax - xmin) >> 3;
/*
	==========================
	Check for vertical reflect
	==========================
*/
	if (CHACTL & 0x04)
	{
		charoffset = 7;
		chardelta = -1;
	}
	else
	{
		charoffset = 0;
		chardelta = 1;
	}
/*
	======================
	Check for video invert
	======================
*/
	if (CHACTL & 0x02)
	{
		invert_mask = 0x80;
	}
	else
	{
		invert_mask = 0x00;
	}
/*
	=========================
	Check for character blank
	=========================
*/
	if (CHACTL & 0x01)
	{
		blank_mask = 0x00;
	}
	else
	{
		blank_mask = 0x80;
	}
/*
	==============================================
	Extract required characters from screen memory
	and locate start position in character set.
	==============================================
*/
	for (i=0;i<nchars;i++)
	{
		UBYTE	screendata;

		screendata = memory[screenaddr];
		screenaddr++;
		chadr[i] = chbase + ((UWORD)(screendata & 0x7f) << 3) + charoffset;

		if (screendata & invert_mask)
			invert[i] = 0xff;
		else
			invert[i] = 0x00;

		if (screendata & 0x80)
			blank[i] = screendata & blank_mask;
		else
			blank[i] = 0x80;
		if (blank[i]) blank[i] = 0xff;
	}

	for (i=0;i<8;i++)
	{
		int	j;
		int	xpos = xmin;

		for (j=0;j<xpos;j++) scanline[j] = 0;

		for (j=0;j<nchars;j++)
		{
			UBYTE	chdata;
			UWORD	addr;
			int	k;

			addr = chadr[j];
			chdata = memory[addr];
			chadr[j] = addr + chardelta;

			chdata = (chdata ^ invert[j]) & blank[j];

			if (chdata)
			{
				for (k=0;k<8;k++)
				{
					if (chdata & 0x80)
					{
						scanline[xpos++] = 0x0002;
					}
					else
					{
						scanline[xpos++] = 0x0004;
					}

					chdata = chdata << 1;
				}
			}
			else
			{
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
				scanline[xpos++] = 0x0004;
			}
		}

		while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

		if (DMACTL & 0x0c)
			PM_ScanLine ();

		Atari_ScanLine ();

		ypos++;
	}
}

void antic_3 ()
{
	UWORD	chadr[48];
	UBYTE	invert[48];
	UBYTE	blank[48];
	UBYTE	lowercase[48];
	UBYTE	first[48];
	UBYTE	second[48];
	int	charoffset;
	int	chardelta;
	int	i;

	int	nchars;
	int	invert_mask;
	int	blank_mask;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 3\n");
#endif

	chbase = CHBASE << 8;

	nchars = (xmax - xmin) >> 3;
/*
	==========================
	Check for vertical reflect
	==========================
*/
	if (CHACTL & 0x04)
	{
		charoffset = 7;
		chardelta = -1;
	}
	else
	{
		charoffset = 0;
		chardelta = 1;
	}
/*
	======================
	Check for video invert
	======================
*/
	if (CHACTL & 0x02)
	{
		invert_mask = 0x80;
	}
	else
	{
		invert_mask = 0x00;
	}
/*
	=========================
	Check for character blank
	=========================
*/
	if (CHACTL & 0x01)
	{
		blank_mask = 0x00;
	}
	else
	{
		blank_mask = 0x80;
	}
/*
	==============================================
	Extract required characters from screen memory
	and locate start position in character set.
	==============================================
*/
	for (i=0;i<nchars;i++)
	{
		UBYTE	screendata;

		screendata = memory[screenaddr];
		screenaddr++;
		chadr[i] = chbase + ((UWORD)(screendata & 0x7f) << 3) + charoffset;
		invert[i] = screendata & invert_mask;

		if (screendata & 0x80)
			blank[i] = screendata & blank_mask;
		else
			blank[i] = 0x80;

		if ((screendata & 0x60) == 0x60)
		{
			lowercase[i] = TRUE;
		}
		else
		{
			lowercase[i] = FALSE;
		}
	}

	for (i=0;i<10;i++)
	{
		int	j;
		int	xpos = xmin;

		for (j=0;j<xpos;j++) scanline[j] = 0;

		for (j=0;j<nchars;j++)
		{
			UBYTE	chdata;
			int	t_invert;
			int	t_blank;
			int	k;

			if (lowercase[j])
			{
				switch (i)
				{
					case 0 :
						first[j] = memory[chadr[j]];
						chdata = 0;
						break;
					case 1 :
						second[j] = memory[chadr[j]];
						chdata = 0;
						break;
					case 8 :
						chdata = first[j];
						break;
					case 9 :
						chdata = second[j];
						break;
					default :
						chdata = memory[chadr[j]];
						break;
				}
			}
			else if (i < 8)
			{
				chdata = memory[chadr[j]];
			}
			else
			{
				chdata = 0;
			}

			chadr[j] += chardelta;
			t_invert = invert[j];
			t_blank = blank[j];

			for (k=0;k<8;k++)
			{
				if (((chdata & 0x80) ^ t_invert) & t_blank)
				{
					scanline[xpos++] = 0x0002;
				}
				else
				{
					scanline[xpos++] = 0x0004;
				}

				chdata = chdata << 1;
			}
		}

		while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

		if (DMACTL & 0x0c)
			PM_ScanLine ();

		Atari_ScanLine ();

		ypos++;
	}
}

void antic_4 ()
{
	UBYTE	screendata[48];
	UWORD	chadr[48];
	int	charoffset;
	int	chardelta;
	int	i;

	int	nchars;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 4 (%d to %d)\n", xmin, xmax);
#endif

	chbase = CHBASE << 8;

	nchars = (xmax - xmin) >> 3;
/*
	==========================
	Check for vertical reflect
	==========================
*/
	if (CHACTL & 0x04)
	{
		charoffset = 7;
		chardelta = -1;
	}
	else
	{
		charoffset = 0;
		chardelta = 1;
	}
/*
	==============================================
	Extract required characters from screen memory
	and locate start position in character set.
	==============================================
*/
	for (i=0;i<nchars;i++)
	{
		screendata[i] = memory[screenaddr];
		screenaddr++;
		chadr[i] = chbase + ((UWORD)(screendata[i] & 0x7f) << 3) + charoffset;
	}

	for (i=0;i<8;i++)
	{
		int	j;
		int	xpos = xmin;

		for (j=0;j<xpos;j++) scanline[j] = 0;

		for (j=0;j<nchars;j++)
		{
			UBYTE	chdata;
			UBYTE	t_screendata;
			int	k;

			chdata = memory[chadr[j]];
			chadr[j] += chardelta;
			t_screendata = screendata[j];

			for (k=0;k<4;k++)
			{
				switch (chdata & 0xc0)
				{
					case 0x00 :
						scanline[xpos++] = 0x0000;
						scanline[xpos++] = 0x0000;
						break;
					case 0x40 :
						scanline[xpos++] = 0x0001;
						scanline[xpos++] = 0x0001;
						break;
					case 0x80 :
						if (t_screendata & 0x80)
						{
							scanline[xpos++] = 0x0004;
							scanline[xpos++] = 0x0004;
						}
						else
						{
							scanline[xpos++] = 0x0002;
							scanline[xpos++] = 0x0002;
						}
						break;
					case 0xc0 :
						scanline[xpos++] = 0x0008;
						scanline[xpos++] = 0x0008;
						break;
				}

				chdata = chdata << 2;
			}
		}

		while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

		if (DMACTL & 0x0c)
			PM_ScanLine ();

		Atari_ScanLine ();

		ypos++;
	}
}

void antic_5 ()
{
	UBYTE	screendata[48];
	UWORD	chadr[48];
	int	charoffset;
	int	chardelta;
	int	i;

	int	nchars;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 5\n");
#endif

	chbase = CHBASE << 8;

	nchars = (xmax - xmin) >> 3;
/*
	==========================
	Check for vertical reflect
	==========================
*/
	if (CHACTL & 0x04)
	{
		charoffset = 7;
		chardelta = -1;
	}
	else
	{
		charoffset = 0;
		chardelta = 1;
	}
/*
	==============================================
	Extract required characters from screen memory
	and locate start position in character set.
	==============================================
*/
	for (i=0;i<nchars;i++)
	{
		screendata[i] = memory[screenaddr];
		screenaddr++;
		chadr[i] = chbase + ((UWORD)(screendata[i] & 0x7f) << 3) + charoffset;
	}

	for (i=0;i<8;i++)
	{
		int	j;
		int	xpos = xmin;

		for (j=0;j<xpos;j++) scanline[j] = 0;

		for (j=0;j<nchars;j++)
		{
			UBYTE	chdata;
			UBYTE	t_screendata;
			int	k;

			chdata = memory[chadr[j]];
			chadr[j] += chardelta;
			t_screendata = screendata[j];

			for (k=0;k<4;k++)
			{
				switch (chdata & 0xc0)
				{
					case 0x00 :
						scanline[xpos++] = 0x0000;
						scanline[xpos++] = 0x0000;
						break;
					case 0x40 :
						scanline[xpos++] = 0x0001;
						scanline[xpos++] = 0x0001;
						break;
					case 0x80 :
						if (t_screendata & 0x80)
						{
							scanline[xpos++] = 0x0004;
							scanline[xpos++] = 0x0004;
						}
						else
						{
							scanline[xpos++] = 0x0002;
							scanline[xpos++] = 0x0002;
						}
						break;
					case 0xc0 :
						scanline[xpos++] = 0x0008;
						scanline[xpos++] = 0x0008;
						break;
				}

				chdata = chdata << 2;
			}
		}

		while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

		if (DMACTL & 0x0c)
		{
			PM_ScanLine ();

			Atari_ScanLine ();

			ypos++;

			for (j=0;j<ATARI_WIDTH;j++) scanline[j] &= 0x000f;

			PM_ScanLine ();

			Atari_ScanLine ();

			ypos++;
		}
		else
		{
			Atari_ScanLine ();
			ypos++;
			Atari_ScanLine ();
			ypos++;
		}
	}
}

void antic_6 ()
{
	UBYTE	screendata[24];
	UWORD	chadr[24];
	int	charoffset;
	int	chardelta;
	int	i;

	int	nchars;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 6\n");
#endif

	chbase = CHBASE << 8;

	nchars = (xmax - xmin) / 16;
/*
	==========================
	Check for vertical reflect
	==========================
*/
	if (CHACTL & 0x04)
	{
		charoffset = 7;
		chardelta = -1;
	}
	else
	{
		charoffset = 0;
		chardelta = 1;
	}
/*
	==============================================
	Extract required characters from screen memory
	and locate start position in character set.
	==============================================
*/
	for (i=0;i<nchars;i++)
	{
		screendata[i] = memory[screenaddr];
		screenaddr++;
		chadr[i] = chbase + ((UWORD)(screendata[i] & 0x3f) << 3) + charoffset;
	}

	for (i=0;i<8;i++)
	{
		int	j;
		int	xpos = xmin;

		for (j=0;j<xpos;j++) scanline[j] = 0;

		for (j=0;j<nchars;j++)
		{
			UBYTE	chdata;
			UBYTE	t_screendata;
			int	k;

			chdata = memory[chadr[j]];
			chadr[j] += chardelta;
			t_screendata = screendata[j];

			for (k=0;k<8;k++)
			{
				if (chdata & 0x80)
				{
					switch (t_screendata & 0xc0)
					{
						case 0x00 :
							scanline[xpos++] = 0x0001;
							scanline[xpos++] = 0x0001;
							break;
						case 0x40 :
							scanline[xpos++] = 0x0002;
							scanline[xpos++] = 0x0002;
							break;
						case 0x80 :
							scanline[xpos++] = 0x0004;
							scanline[xpos++] = 0x0004;
							break;
						case 0xc0 :
							scanline[xpos++] = 0x0008;
							scanline[xpos++] = 0x0008;
							break;
					}
				}
				else
				{
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
				}

				chdata = chdata << 1;
			}
		}

		while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

		if (DMACTL & 0x0c)
			PM_ScanLine ();

		Atari_ScanLine ();

		ypos++;
	}
}

void antic_7 ()
{
	UBYTE	screendata[24];
	UWORD	chadr[24];
	int	charoffset;
	int	chardelta;
	int	i;

	int	nchars;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 7\n");
#endif

	chbase = CHBASE << 8;

	nchars = (xmax - xmin) / 16;
/*
	==========================
	Check for vertical reflect
	==========================
*/
	if (CHACTL & 0x04)
	{
		charoffset = 7;
		chardelta = -1;
	}
	else
	{
		charoffset = 0;
		chardelta = 1;
	}
/*
	==============================================
	Extract required characters from screen memory
	and locate start position in character set.
	==============================================
*/
	for (i=0;i<nchars;i++)
	{
		screendata[i] = memory[screenaddr];
		screenaddr++;
		chadr[i] = chbase + ((UWORD)(screendata[i] & 0x3f) << 3) + charoffset;
	}

	for (i=0;i<8;i++)
	{
		int	j;
		int	xpos = xmin;

		for (j=0;j<xpos;j++) scanline[j] = 0;

		for (j=0;j<nchars;j++)
		{
			UBYTE	chdata;
			UBYTE	t_screendata;
			int	k;

			chdata = memory[chadr[j]];
			chadr[j] += chardelta;
			t_screendata = screendata[j];

			for (k=0;k<8;k++)
			{
				if (chdata & 0x80)
				{
					switch (t_screendata & 0xc0)
					{
						case 0x00 :
							scanline[xpos++] = 0x0001;
							scanline[xpos++] = 0x0001;
							break;
						case 0x40 :
							scanline[xpos++] = 0x0002;
							scanline[xpos++] = 0x0002;
							break;
						case 0x80 :
							scanline[xpos++] = 0x0004;
							scanline[xpos++] = 0x0004;
							break;
						case 0xc0 :
							scanline[xpos++] = 0x0008;
							scanline[xpos++] = 0x0008;
							break;
					}
				}
				else
				{
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
				}

				chdata = chdata << 1;
			}
		}

		while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

		if (DMACTL & 0x0c)
		{
			PM_ScanLine ();
			Atari_ScanLine ();
			ypos++;

			for (j=0;j<ATARI_WIDTH;j++) scanline[j] &= 0x000f;

			PM_ScanLine ();
			Atari_ScanLine ();
			ypos++;
		}
		else
		{
			Atari_ScanLine ();
			ypos++;
			Atari_ScanLine ();
			ypos++;
		}
	}
}

void antic_8 ()
{
	int	xpos;
	int	nbytes;
	int	i;
	int	j;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 8\n");
#endif

	nbytes = (xmax - xmin) / 32;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<4;j++)
		{
			switch (screendata & 0xc0)
			{
				case 0x00 :
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					break;
				case 0x40 :
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					break;
				case 0x80 :
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					break;
				case 0xc0 :
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					break;
			}

			screendata = screendata << 2;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
	{
		for (i=0;i<8;i++)
		{
			PM_ScanLine ();
			Atari_ScanLine ();
			ypos++;

			if (i < 7)
				for (j=0;j<ATARI_WIDTH;j++) scanline[j] &= 0x000f;
		}
	}
	else
	{
		for (i=0;i<8;i++)
		{
			Atari_ScanLine ();
			ypos++;
		}
	}
}

void antic_9 ()
{
	int	xpos;
	int	nbytes;
	int	i;
	int	j;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode 9\n");
#endif

	nbytes = (xmax - xmin) / 32;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<8;j++)
		{
			switch (screendata & 0x80)
			{
				case 0x00 :
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					break;
				case 0x80 :
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					break;
			}

			screendata = screendata << 1;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
	{
		for (i=0;i<4;i++)
		{
			PM_ScanLine ();
			Atari_ScanLine ();
			ypos++;


			if (i < 3)
				for (j=0;j<ATARI_WIDTH;j++) scanline[j] &= 0x000f;
		}
	}
	else
	{
		for (i=0;i<4;i++)
		{
			Atari_ScanLine ();
			ypos++;
		}
	}
}

void antic_a ()
{
	int	xpos;
	int	nbytes;
	int	i;
	int	j;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode a\n");
#endif

	nbytes = (xmax - xmin) / 16;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<4;j++)
		{
			switch (screendata & 0xc0)
			{
				case 0x00 :
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					break;
				case 0x40 :
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					break;
				case 0x80 :
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					break;
				case 0xc0 :
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					scanline[xpos++] = 0x0003;
					break;
			}

			screendata = screendata << 2;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
	{
		for (i=0;i<4;i++)
		{
			PM_ScanLine ();
			Atari_ScanLine ();
			ypos++;

			if (i < 3)
				for (j=0;j<ATARI_WIDTH;j++) scanline[j] &= 0x000f;
		}
	}
	else
	{
		for (i=0;i<4;i++)
		{
			Atari_ScanLine ();
			ypos++;
		}
	}
}

void antic_b ()
{
	int	xpos;
	int	nbytes;
	int	i;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode b\n");
#endif

	nbytes = (xmax - xmin) / 16;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;
		int	j;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<8;j++)
		{
			switch (screendata & 0x80)
			{
				case 0x00 :
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					break;
				case 0x80 :
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					break;
			}

			screendata = screendata << 1;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
	{
		PM_ScanLine ();
		Atari_ScanLine ();
		ypos++;

		for (i=0;i<ATARI_WIDTH;i++) scanline[i] &= 0x000f;

		PM_ScanLine ();
		Atari_ScanLine ();
		ypos++;
	}
	else
	{
		Atari_ScanLine ();
		ypos++;
		Atari_ScanLine ();
		ypos++;
	}
}

void antic_c ()
{
	int	xpos;
	int	nbytes;
	int	i;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode c\n");
#endif

	nbytes = (xmax - xmin) / 16;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;
		int	j;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<8;j++)
		{
			if (screendata & 0x80)
			{
				scanline[xpos++] = 0x0001;
				scanline[xpos++] = 0x0001;
			}
			else
			{
				scanline[xpos++] = 0x0000;
				scanline[xpos++] = 0x0000;
			}

			screendata = screendata << 1;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
		PM_ScanLine ();

	Atari_ScanLine ();

	ypos++;
}

void antic_d ()
{
	int	xpos;
	int	nbytes;
	int	i;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode d\n");
#endif

	nbytes = (xmax - xmin) >> 3;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;
		int	j;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<4;j++)
		{
			switch (screendata & 0xc0)
			{
				case 0x00 :
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					break;
				case 0x40 :
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					break;
				case 0x80 :
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					break;
				case 0xc0 :
					scanline[xpos++] = 0x0004;
					scanline[xpos++] = 0x0004;
					break;
			}

			screendata = screendata << 2;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
	{
		PM_ScanLine ();
		Atari_ScanLine ();
		ypos++;

		for (i=0;i<ATARI_WIDTH;i++) scanline[i] &= 0x000f;

		PM_ScanLine ();
		Atari_ScanLine ();
		ypos++;
	}
	else
	{
		Atari_ScanLine ();
		ypos++;
		Atari_ScanLine ();
		ypos++;
	}
}

void antic_e ()
{
	int	xpos;
	int	nbytes;
	int	i;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode e\n");
#endif

	nbytes = (xmax - xmin) >> 3;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;
		int	j;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<4;j++)
		{
			switch (screendata & 0xc0)
			{
				case 0x00 :
					scanline[xpos++] = 0x0000;
					scanline[xpos++] = 0x0000;
					break;
				case 0x40 :
					scanline[xpos++] = 0x0001;
					scanline[xpos++] = 0x0001;
					break;
				case 0x80 :
					scanline[xpos++] = 0x0002;
					scanline[xpos++] = 0x0002;
					break;
				case 0xc0 :
					scanline[xpos++] = 0x0004;
					scanline[xpos++] = 0x0004;
					break;
			}

			screendata = screendata << 2;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
		PM_ScanLine ();

	Atari_ScanLine ();

	ypos++;
}

void antic_f ()
{
	int	xpos;
	int	nbytes;
	int	i;

#ifdef DEBUG
	fprintf (stderr, "ANTIC: mode f\n");
#endif

	nbytes = (xmax - xmin) >> 3;

	xpos = xmin;

	for (i=0;i<xpos;i++) scanline[i] = 0;

	for (i=0;i<nbytes;i++)
	{
		UBYTE	screendata;
		int	j;

		screendata = memory[screenaddr];
		screenaddr++;

		for (j=0;j<8;j++)
		{
			if (screendata & 0x80)
				scanline[xpos++] = 0x0002;
			else
				scanline[xpos++] = 0x0004;

			screendata = screendata << 1;
		}
	}

	while (xpos < ATARI_WIDTH) scanline[xpos++] = 0;

	if (DMACTL & 0x0c)
		PM_ScanLine ();

	Atari_ScanLine ();

	ypos++;
}

/*
	*****************************************************************
	*								*
	*	Section			:	Display List		*
	*	Original Author		:	David Firth		*
	*	Date Written		:	28th May 1995		*
	*	Version			:	1.0			*
	*								*
	*   Description							*
	*   -----------							*
	*								*
	*   Section that handles Antic Display List. Not required for	*
	*   BASIC version.						*
	*								*
	*****************************************************************
*/

void Atari800_UpdateScreen ()
{
	UWORD	dlist;

	int	JVB;
	int	abort_count = 300;

	Atari_PreUpdate ();

	PM_InitFrame ();

	ypos = 0;

	if (DMACTL & 0x20)
	{
#ifdef DEBUG
		fprintf (stderr, "Atari800_UpdateScreen: started\n");
#endif

		dlist = (DLISTH << 8) | DLISTL;

		JVB = FALSE;

		do
		{
			UBYTE	IR;

			if (abort_count-- == 0)
			{
				fprintf (stderr, "DLIST: ABORT\n");
				break;
			}

			IR = memory[dlist];
			dlist++;

			switch (IR & 0x0f)
			{
				case 0x00 :
					{
						int	nlines;

						nlines = ((IR >> 4)  & 0x07) + 1;
						antic_blank (nlines);
					}
					break;
				case 0x01 :
					if (IR & 0x40)
					{
						JVB = TRUE;
					}
					else
					{
						dlist = (memory[dlist+1] << 8) | memory[dlist];
						antic_blank (1);	/* Jump aparently uses 1 scan line */
					}
					break;
				default :
					if (IR & 0x40)
					{
						screenaddr = (memory[dlist+1] << 8) | memory[dlist];
						dlist += 2;
					}

					if (IR & 0x20)
					{
						static int	flag = TRUE;

						if (flag)
						{
							fprintf (stderr, "DLIST: vertical scroll unsupported\n");
							flag = FALSE;
						}
					}

					if (IR & 0x10)
					{
						switch (DMACTL & 0x03)
						{
							case 0x00 :
								continue;
							case 0x01 :
								xmin = 32;
								xmax = 352;
								break;
							case 0x02 :
								xmin = 0;
								xmax = 384;
								break;
							case 0x03 :
								xmin = 0;
								xmax = 384;
								break;
						}
					}
					else
					{
						switch (DMACTL & 0x03)
						{
							case 0x00 :
								continue;
							case 0x01 :
								xmin = 64;
								xmax = 320;
								break;
							case 0x02 :
								xmin = 32;
								xmax = 352;
								break;
							case 0x03 :
								xmin = 0;
								xmax = 384;
								break;
						}
					}

					switch (IR & 0x0f)
					{
						case 0x02 :
							antic_2 ();
							break;
						case 0x03 :
							antic_3 ();
							break;
						case 0x04 :
							antic_4 ();
							break;
						case 0x05 :
							antic_5 ();
							break;
						case 0x06 :
							antic_6 ();
							break;
						case 0x07 :
							antic_7 ();
							break;
						case 0x08 :
							antic_8 ();
							break;
						case 0x09 :
							antic_9 ();
							break;
						case 0x0a :
							antic_a ();
							break;
						case 0x0b :
							antic_b ();
							break;
						case 0x0c :
							antic_c ();
							break;
						case 0x0d :
							antic_d ();
							break;
						case 0x0e :
							antic_e ();
							break;
						case 0x0f :
							antic_f ();
							break;
						default :
							JVB = TRUE;
							break;
					}

					break;
			}

			if (IR & 0x80)
			{
				if (NMIEN & 0x80)
				{
					NMIST = NMIST | 0x80;

					INTERRUPT |= NMI_MASK;

					GO (-1);
				}
			}
		} while (!JVB && (ypos < ATARI_HEIGHT));
	}

	antic_blank (ATARI_HEIGHT - ypos);

	Atari_PostUpdate ();

#ifdef DEBUG
	fprintf (stderr, "Atari800_UpdateScreen: finished\n");
#endif
}

#endif
#endif

static int	SHIFT = 0x00;

UBYTE Atari800_GetByte (UWORD addr)
{
	UBYTE	byte;
	int	ival;
/*
	============================================================
	GTIA, POKEY, PIA and ANTIC do not fully decode their address
	------------------------------------------------------------
	PIA (At least) is fully decoded when emulating the XL/XE
	============================================================
*/
	switch (addr & 0x0f00)
	{
		case 0xd000:        /* GTIA */
			addr &= 0xff1f;
			break;
		case 0xd200:        /* POKEY */
			addr &= 0xff0f;
			break;
		case 0xd300:        /* PIA */
			if (machine == Atari)
				addr &= 0xff03;
			break;
		case 0xd400:        /* ANTIC */
			addr &= 0xff0f;
			break;
		default:
			break;
	}

	switch (addr)
	{
		case _CHBASE :
			byte = CHBASE;
			break;
		case _CHACTL :
			byte = CHACTL;
			break;
		case _CONSOL :
			byte = CONSOL;
			break;
		case _DLISTL :
			byte = DLISTL;
			break;
		case _DLISTH :
			byte = DLISTH;
			break;
		case _DMACTL :
			byte = DMACTL;
			break;
		case _KBCODE :
			byte = KBCODE;
			break;
		case _IRQST :
			byte = IRQST;
			break;
		case _M0PF :
			byte = M0PF;
			break;
		case _M1PF :
			byte = M1PF;
			break;
		case _M2PF :
			byte = M2PF;
			break;
		case _M3PF :
			byte = M3PF;
			break;
		case _M0PL :
			byte = M0PL;
			break;
		case _M1PL :
			byte = M1PL;
			break;
		case _M2PL :
			byte = M2PL;
			break;
		case _M3PL :
			byte = M3PL;
			break;
		case _P0PF :
			byte = P0PF;
			break;
		case _P1PF :
			byte = P1PF;
			break;
		case _P2PF :
			byte = P2PF;
			break;
		case _P3PF :
			byte = P3PF;
			break;
		case _P0PL :
			byte = P0PL;
			break;
		case _P1PL :
			byte = P1PL;
			break;
		case _P2PL :
			byte = P2PL;
			break;
		case _P3PL :
			byte = P3PL;
			break;
		case _PENH :
		case _PENV :
			byte = 0x00;
			break;
		case _PORTA :
#ifdef BASIC
			byte = 0xff;
#else
			{
				int	stick0;
				int	stick1;

				stick0 = Atari_Joystick (0) & 0x0f;
				stick1 = Atari_Joystick (1) & 0x0f;
				byte = (stick1 << 4) | stick0;
			}
#endif
			break;
		case _PORTB :
#ifdef BASIC
			byte = 0xff;
#else
			{
				int	stick2;
				int	stick3;

				stick2 = Atari_Joystick (2) & 0x0f;
				stick3 = Atari_Joystick (3) & 0x0f;
				byte = (stick3 << 4) | stick2;
			}
#endif
			break;
		case _POT0 :
#ifdef BASIC
			byte = 0;
#else
			ival = Atari_Paddle (0);
			if (ival & 0x100)
				PORTA |= 0x04;
			else
				PORTA &= 0xfb;

			byte = ival & 0xff;
#endif
			break;
		case _POT1 :
		case _POT2 :
		case _POT3 :
		case _POT4 :
		case _POT5 :
		case _POT6 :
		case _POT7 :
			byte = 0x00;
			break;
		case _RANDOM :
			byte = rand();
			break;
		case _TRIG0 :
#ifdef BASIC
			byte = 1;
#else
			byte = (Atari_Joystick (0) >> 4) & 0x01;
#endif
			break;
		case _TRIG1 :
#ifdef BASIC
			byte = 1;
#else
			byte = (Atari_Joystick (1) >> 4) & 0x01;
#endif
			break;
		case _TRIG2 :
#ifdef BASIC
			byte = 1;
#else
			byte = (Atari_Joystick (2) >> 4) & 0x01;
#endif
			break;
		case _TRIG3 :
#ifdef BASIC
			byte = 1;
#else
			byte = (Atari_Joystick (3) >> 4) & 0x01;
#endif
			break;
		case _VCOUNT :
/*
	===================================================
	BUG: programs that monitor VCOUNT usually don't work
	     This is because the VBI + all DLI and screen
	     generation are all done together with no
	     intervening 6502 clock cycles.

	Example: programs that use vcount to determine
		 which character set to display will appear
		 wrong. Sometimes you will get a good
		 screen but usually it will be wrong.
	===================================================
*/
#ifdef DEBUG
			printf ("BUG: VCOUNT read\n");
#endif
			byte = VCOUNT++;
			break;
		case _NMIEN :
			byte = NMIEN;
			break;
		case _NMIST :
			byte = NMIST;
			break;
		case _SKSTAT :
			if (SHIFT)
				byte = 0x05;
			else
				byte = 0x0d;
			break;
		case _WSYNC :
			byte = 0;
			break;
		default :
#ifdef DEBUG
			fprintf (stderr, "read from %04x\n", addr);
#endif
			byte = 0;
			break;
	}

	return byte;
}

void Atari800_PutByte (UWORD addr, UBYTE byte)
{
/*
	============================================================
	GTIA, POKEY, PIA and ANTIC do not fully decode their address
	------------------------------------------------------------
	PIA (At least) is fully decoded when emulating the XL/XE
	============================================================
*/
	switch (addr & 0x0f00)
	{
		case 0xd000:        /* GTIA */
			addr &= 0xff1f;
			break;
		case 0xd200:        /* POKEY */
			addr &= 0xff0f;
			break;
		case 0xd300:        /* PIA */
			if (machine == Atari)
				addr &= 0xff03;
			break;
		case 0xd400:        /* ANTIC */
			addr &= 0xff0f;
			break;
		default:
			break;
	}

	switch (addr)
	{
		case 0xD500:	/* Check for oss supercart switch */
			if (super) memcpy (memory+0xA000, super+0x0000, 0x1000);
			break;
		case 0xD504:
			if (super) memcpy (memory+0xA000, super+0x1000, 0x1000);
			break;
		case 0xD503:
		case 0xD507:
			if (super) memcpy (memory+0xA000, super+0x2000, 0x1000);
			break;
		case _AUDC1 :
		case _AUDC2 :
		case _AUDC3 :
		case _AUDC4 :
		case _AUDCTL :
		case _AUDF1 :
		case _AUDF2 :
		case _AUDF3 :
		case _AUDF4 :
			break;
		case _CHBASE :
			CHBASE = byte;
			break;
		case _CHACTL :
			CHACTL = byte;
			break;
		case _COLBK :
			COLBK = byte;
			break;
		case _COLPF0 :
			COLPF0 = byte;
			break;
		case _COLPF1 :
			COLPF1 = byte;
			break;
		case _COLPF2 :
			COLPF2 = byte;
			break;
		case _COLPF3 :
			COLPF3 = byte;
			break;
		case _COLPM0 :
			COLPM0 = byte;
			break;
		case _COLPM1 :
			COLPM1 = byte;
			break;
		case _COLPM2 :
			COLPM2 = byte;
			break;
		case _COLPM3 :
			COLPM3 = byte;
			break;
		case _CONSOL :
			break;
		case _DLISTL :
			DLISTL = byte;
			break;
		case _DLISTH :
			DLISTH = byte;
			break;
		case _DMACTL :
			DMACTL = byte;
			break;
		case _HITCLR :
			M0PF = M1PF = M2PF = M3PF = 0;
			P0PF = P1PF = P2PF = P3PF = 0; 
			M0PL = M1PL = M2PL = M3PL = 0; 
			P0PL = P1PL = P2PL = P3PL = 0;
			break;
		case _HPOSM0 :
			HPOSM0 = byte;
			break;
		case _HPOSM1 :
			HPOSM1 = byte;
			break;
		case _HPOSM2 :
			HPOSM2 = byte;
			break;
		case _HPOSM3 :
			HPOSM3 = byte;
			break;
		case _HPOSP0 :
			HPOSP0 = byte;
			break;
		case _HPOSP1 :
			HPOSP1 = byte;
			break;
		case _HPOSP2 :
			HPOSP2 = byte;
			break;
		case _HPOSP3 :
			HPOSP3 = byte;
			break;
		case _HSCROL :
			HSCROL = byte;
			break;
		case _IRQEN :
			break;
		case _NMIEN :
			NMIEN = byte;
			break;
		case _NMIRES :
			NMIRES = 0;
			break;
		case _PMBASE :
			PMBASE = byte;
			break;
		case _PORTB :
			switch (machine)
			{
				case Atari :
					break;
				case AtariXL :
				case AtariXE :
#ifdef DEBUG
					printf ("Storing %x to PORTB\n", byte);
#endif
					if ((byte ^ PORTB) & 0x01)
					{
						if (byte & 0x01)
						{
#ifdef DEBUG
							printf ("\tEnable ROM at $c000-$cfff and $d800-$ffff\n");
#endif
							memcpy (under_atarixl_os, memory+0xc000, 0x1000);
							memcpy (under_atarixl_os+0x1800, memory+0xd800, 0x2800);
							memcpy (memory+0xc000, atarixl_os, 0x1000);
							memcpy (memory+0xd800, atarixl_os+0x1800, 0x2800);
							SetROM (0xc000, 0xcfff);
							SetROM (0xd800, 0xffff);
						}
						else
						{
#ifdef DEBUG
							printf ("\tEnable RAM at $c000-$cfff and $d800-$ffff\n");
#endif
							memcpy (memory+0xc000, under_atarixl_os, 0x1000);
							memcpy (memory+0xd800, under_atarixl_os+0x1800, 0x2800);
							SetRAM (0xc000, 0xcfff);
							SetRAM (0xd800, 0xffff);
						}
					}

/*
	=====================================
	An Atari XL/XE can only disable Basic
	Other cartridge cannot be disable
	=====================================
*/
					if (!rom_inserted)
					{
						if ((byte ^ PORTB) & 0x02)
						{
							if (byte & 0x02)
							{
#ifdef DEBUG
								printf ("\tDisable BASIC\n");
#endif
								memcpy (memory+0xa000, under_atari_basic, 0x2000);
								SetRAM (0xa000, 0xbfff);
							}
							else
							{
#ifdef DEBUG
								printf ("\tEnable BASIC at $a000-$bfff\n");
#endif
								memcpy (under_atari_basic, memory+0xa000, 0x2000);
								memcpy (memory+0xa000, atari_basic, 0x2000);
								SetROM (0xa000, 0xbfff);
							}
						}
					}

					if ((byte ^ PORTB) & 0x80)
					{
						if (byte & 0x80)
						{
#ifdef DEBUG
							printf ("\tEnable RAM at $5000-$57ff (Self Test)\n");
#endif
							memcpy (memory+0x5000, under_atarixl_os+0x1000, 0x0800);
							SetRAM (0x5000, 0x57ff);
						}
						else
						{
#ifdef DEBUG
							printf ("\tEnable ROM at $5000-$57ff (Self Test)\n");
#endif
							memcpy (under_atarixl_os+0x1000, memory+0x5000, 0x800);
							memcpy (memory+0x5000, atarixl_os+0x1000, 0x800);
							SetROM (0x5000, 0x57ff);
						}
					}
					PORTB = byte;
					break;
			}
			break;
		case _POTGO :
			break;
		case _PRIOR :
			PRIOR = byte;
			break;
		case _SIZEM :
			SIZEM = byte;
			break;
		case _SIZEP0 :
			SIZEP0 = byte;
			break;
		case _SIZEP1 :
			SIZEP1 = byte;
			break;
		case _SIZEP2 :
			SIZEP2 = byte;
			break;
		case _SIZEP3 :
			SIZEP3 = byte;
			break;
		case _WSYNC :
			break;
		default :
#ifdef DEBUG
			fprintf (stderr, "write %02x to %04x\n", byte, addr);
#endif
			break;
	}
}

void Atari800_Hardware (void)
{
	static int	pil_on = FALSE;

	int	keycode;

	NMIST = 0x00;

#ifndef BASIC
	keycode = Atari_Keyboard ();
#endif

	CONSOL = 0x07;

	switch (keycode)
	{
		case AKEY_WARMSTART :
			NMIST = 0x20;
			INTERRUPT |= NMI_MASK;
			keycode = AKEY_NONE;
			break;
		case AKEY_COLDSTART :
			PutByte (0x244, 1);
			NMIST = 0x20;
			INTERRUPT |= NMI_MASK;
			keycode = AKEY_NONE;
			break;
		case AKEY_EXIT :
			Atari800_Exit ();
			exit (1);
		case AKEY_OPTION :
			CONSOL &= ~0x04;
			keycode = AKEY_NONE;
			break;
		case AKEY_SELECT :
			CONSOL &= ~0x02;
			keycode = AKEY_NONE;
			break;
		case AKEY_START :
			CONSOL &= ~0x01;
			keycode = AKEY_NONE;
			break;
		case AKEY_HELP :
			keycode = AKEY_NONE;
			break;
		case AKEY_BREAK :
			IRQST = 0x7f;
			keycode = AKEY_NONE;
			break;
		case AKEY_PIL :
			if (pil_on)
			{
				SetRAM (0x8000, 0xbfff);
				pil_on = FALSE;
			}
			else
			{
				SetROM (0x8000, 0xbfff);
				pil_on = TRUE;
			}
			keycode = AKEY_NONE;
			break;
		case AKEY_DISKCHANGE :
			{
				char	filename[128];

				printf ("Next Disk: ");
				scanf ("\n%s", filename);
				SIO_Dismount (1);
				if (!SIO_Mount (1, filename))
				{
					printf ("Failed to mount %s\n", filename);
				}
			}
			keycode = AKEY_NONE;
			break;
		case AKEY_DOWN :
			keycode = 0x8f;
			break;
		case AKEY_LEFT :
			keycode = 0x86;
			break;
		case AKEY_RIGHT :
			keycode = 0x87;
			break;
		case AKEY_UP :
			keycode = 0x8e;
			break;
		case AKEY_BACKSPACE :
			keycode = 0x74;
			keycode = 0x34;
			break;
		case AKEY_ESCAPE :
			keycode = 0x1c;
			break;
		case AKEY_ATARI :
			keycode = 0x27;
			break;
		case AKEY_CAPSLOCK :
			keycode = 0x7c;
			break;
		case AKEY_CAPSTOGGLE :
			keycode = 0x3c;
			break;
		case AKEY_RETURN :
			keycode = 0x0c;
			break;
		case ' ' :
			keycode = 0x21;
			break;
		case '!' :
			keycode = 0x5f;
			break;
		case '"' :
			keycode = 0x5e;
			break;
		case '#' :
			keycode = 0x5a;
			break;
		case '$' :
			keycode = 0x58;
			break;
		case '%' :
			keycode = 0x5d;
			break;
		case '&' :
			keycode = 0x5b;
			break;
		case '\'' :
			keycode = 0x73;
			break;
		case '@' :
			keycode = 0x75;
			break;
		case '(' :
			keycode = 0x70;
			break;
		case ')' :
			keycode = 0x72;
			break;
		case '<' :
			keycode = 0x36;
			break;
		case '>' :
			keycode = 0x37;
			break;
		case '=' :
			keycode = 0x0f;
			break;
		case '?' :
			keycode = 0x66;
			break;
		case '-' :
			keycode = 0x0e;
			break;
		case '+' :
			keycode = 0x06;
			break;
		case '*' :
			keycode = 0x07;
			break;
		case '/' :
			keycode = 0x26;
			break;
		case ':' :
			keycode = 0x42;
			break;
		case ';' :
			keycode = 0x02;
			break;
		case ',' :
			keycode = 0x20;
			break;
		case '.' :
			keycode = 0x22;
			break;
		case '_' :
			keycode = 0x4e;
			break;
		case '[' :
			keycode = 0x60;
			break;
		case ']' :
			keycode = 0x62;
			break;
		case '^' :
			keycode = 0x47;
			break;
		case '\\' :
			keycode = 0x46;
			break;
		case '|' :
			keycode = 0x4f;
			break;
		case '0' :
			keycode = 0x32;
			break;
		case '1' :
			keycode = 0x1f;
			break;
		case '2' :
			keycode = 0x1e;
			break;
		case '3' :
			keycode = 0x1a;
			break;
		case '4' :
			keycode = 0x18;
			break;
		case '5' :
			keycode = 0x1d;
			break;
		case '6' :
			keycode = 0x1b;
			break;
		case '7' :
			keycode = 0x33;
			break;
		case '8' :
			keycode = 0x35;
			break;
		case '9' :
			keycode = 0x30;
			break;
		case 'a' :
			keycode = 0x3f;
			break;
		case 'b' :
			keycode = 0x15;
			break;
		case 'c' :
			keycode = 0x12;
			break;
		case 'd' :
			keycode = 0x3a;
			break;
		case 'e' :
			keycode = 0x2a;
			break;
		case 'f' :
			keycode = 0x38;
			break;
		case 'g' :
			keycode = 0x3d;
			break;
		case 'h' :
			keycode = 0x39;
			break;
		case 'i' :
			keycode = 0x0d;
			break;
		case 'j' :
			keycode = 0x01;
			break;
		case 'k' :
			keycode = 0x05;
			break;
		case 'l' :
			keycode = 0x00;
			break;
		case 'm' :
			keycode = 0x25;
			break;
		case 'n' :
			keycode = 0x23;
			break;
		case 'o' :
			keycode = 0x08;
			break;
		case 'p' :
			keycode = 0x0a;
			break;
		case 'q' :
			keycode = 0x2f;
			break;
		case 'r' :
			keycode = 0x28;
			break;
		case 's' :
			keycode = 0x3e;
			break;
		case 't' :
			keycode = 0x2d;
			break;
		case 'u' :
			keycode = 0x0b;
			break;
		case 'v' :
			keycode = 0x10;
			break;
		case 'w' :
			keycode = 0x2e;
			break;
		case 'x' :
			keycode = 0x16;
			break;
		case 'y' :
			keycode = 0x2b;
			break;
		case 'z' :
			keycode = 0x17;
			break;
		case 'A' :
			keycode = 0x40 | 0x3f;
			break;
		case 'B' :
			keycode = 0x40 | 0x15;
			break;
		case 'C' :
			keycode = 0x40 | 0x12;
			break;
		case 'D' :
			keycode = 0x40 | 0x3a;
			break;
		case 'E' :
			keycode = 0x40 | 0x2a;
			break;
		case 'F' :
			keycode = 0x40 | 0x38;
			break;
		case 'G' :
			keycode = 0x40 | 0x3d;
			break;
		case 'H' :
			keycode = 0x40 | 0x39;
			break;
		case 'I' :
			keycode = 0x40 | 0x0d;
			break;
		case 'J' :
			keycode = 0x40 | 0x01;
			break;
		case 'K' :
			keycode = 0x40 | 0x05;
			break;
		case 'L' :
			keycode = 0x40 | 0x00;
			break;
		case 'M' :
			keycode = 0x40 | 0x25;
			break;
		case 'N' :
			keycode = 0x40 | 0x23;
			break;
		case 'O' :
			keycode = 0x40 | 0x08;
			break;
		case 'P' :
			keycode = 0x40 | 0x0a;
			break;
		case 'Q' :
			keycode = 0x40 | 0x2f;
			break;
		case 'R' :
			keycode = 0x40 | 0x28;
			break;
		case 'S' :
			keycode = 0x40 | 0x3e;
			break;
		case 'T' :
			keycode = 0x40 | 0x2d;
			break;
		case 'U' :
			keycode = 0x40 | 0x0b;
			break;
		case 'V' :
			keycode = 0x40 | 0x10;
			break;
		case 'W' :
			keycode = 0x40 | 0x2e;
			break;
		case 'X' :
			keycode = 0x40 | 0x16;
			break;
		case 'Y' :
			keycode = 0x40 | 0x2b;
			break;
		case 'Z' :
			keycode = 0x40 | 0x17;
			break;
		case AKEY_CTRL_0 :
			keycode = 0x80 | 0x32;
			break;
		case AKEY_CTRL_1 :
			keycode = 0x80 | 0x1f;
			break;
		case AKEY_CTRL_2 :
			keycode = 0x80 | 0x1e;
			break;
		case AKEY_CTRL_3 :
			keycode = 0x80 | 0x1a;
			break;
		case AKEY_CTRL_4 :
			keycode = 0x80 | 0x18;
			break;
		case AKEY_CTRL_5 :
			keycode = 0x80 | 0x1d;
			break;
		case AKEY_CTRL_6 :
			keycode = 0x80 | 0x1b;
			break;
		case AKEY_CTRL_7 :
			keycode = 0x80 | 0x33;
			break;
		case AKEY_CTRL_8 :
			keycode = 0x80 | 0x35;
			break;
		case AKEY_CTRL_9 :
			keycode = 0x80 | 0x30;
			break;
		case AKEY_CTRL_A :
			keycode = 0x80 | 0x3f;
			break;
		case AKEY_CTRL_B :
			keycode = 0x80 | 0x15;
			break;
		case AKEY_CTRL_C :
			keycode = 0x80 | 0x12;
			break;
		case AKEY_CTRL_D :
			keycode = 0x80 | 0x3a;
			break;
		case AKEY_CTRL_E :
			keycode = 0x80 | 0x2a;
			break;
		case AKEY_CTRL_F :
			keycode = 0x80 | 0x38;
			break;
		case AKEY_CTRL_G :
			keycode = 0x80 | 0x3d;
			break;
		case AKEY_CTRL_H :
			keycode = 0x80 | 0x39;
			break;
		case AKEY_CTRL_I :
			keycode = 0x80 | 0x0d;
			break;
		case AKEY_CTRL_J :
			keycode = 0x80 | 0x01;
			break;
		case AKEY_CTRL_K :
			keycode = 0x80 | 0x05;
			break;
		case AKEY_CTRL_L :
			keycode = 0x80 | 0x00;
			break;
		case AKEY_CTRL_M :
			keycode = 0x80 | 0x25;
			break;
		case AKEY_CTRL_N :
			keycode = 0x80 | 0x23;
			break;
		case AKEY_CTRL_O :
			keycode = 0x80 | 0x08;
			break;
		case AKEY_CTRL_P :
			keycode = 0x80 | 0x0a;
			break;
		case AKEY_CTRL_Q :
			keycode = 0x80 | 0x2f;
			break;
		case AKEY_CTRL_R :
			keycode = 0x80 | 0x28;
			break;
		case AKEY_CTRL_S :
			keycode = 0x80 | 0x3e;
			break;
		case AKEY_CTRL_T :
			keycode = 0x80 | 0x2d;
			break;
		case AKEY_CTRL_U :
			keycode = 0x80 | 0x0b;
			break;
		case AKEY_CTRL_V :
			keycode = 0x80 | 0x10;
			break;
		case AKEY_CTRL_W :
			keycode = 0x80 | 0x2e;
			break;
		case AKEY_CTRL_X :
			keycode = 0x80 | 0x16;
			break;
		case AKEY_CTRL_Y :
			keycode = 0x80 | 0x2b;
			break;
		case AKEY_CTRL_Z :
			keycode = 0x80 | 0x17;
			break;
		default :
			keycode = AKEY_NONE;
			break;
	}

	if (keycode != AKEY_NONE)
	{
		KBCODE = keycode;
		IRQST = 0xbf;
		INTERRUPT |= IRQ_MASK;
	}
	else
	{
		KBCODE = 0xff;
	}

	if (NMIEN & 0x40)
	{
		static int	test_val = 0;

#ifndef BASIC
#ifdef CURSES
		Atari_PostUpdate ();
#else
		if (++test_val == 2)
		{
			Atari800_UpdateScreen ();
			test_val = 0;
		}
#endif
#endif

		NMIST = NMIST | 0x40;
		INTERRUPT |= NMI_MASK;

		GO (-1);
	}
}
