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

#ifndef AMIGA
#include	<unistd.h>
#endif

#ifdef VMS
#include	<file.h>
#endif

#define FALSE   0
#define TRUE    1

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

#define	MAX_DRIVES	8

#define	MAGIC1	0x96
#define	MAGIC2	0x02

struct ATR_Header
{
	unsigned char	magic1;
	unsigned char	magic2;
	unsigned char	seccountlo;
	unsigned char	seccounthi;
	unsigned char	secsizelo;
	unsigned char	secsizehi;
	unsigned char	hiseccountlo;
	unsigned char	hiseccounthi;
	unsigned char	gash[8];
};

typedef enum Format { XFD, ATR } Format;

static Format	format[MAX_DRIVES];
static int	disk[MAX_DRIVES] = { -1, -1, -1, -1, -1, -1, -1, -1 };
static int	sectorcount[MAX_DRIVES];
static int	sectorsize[MAX_DRIVES];

int SIO_Mount (int diskno, char *filename)
{
	struct ATR_Header	header;

	int	fd;

	fd = open (filename, O_RDWR, 0777);
	if (fd)
	{
		int	status;

		status = read (fd, &header, sizeof(struct ATR_Header));
		if (status == -1)
		{
			perror ("SIO_Mount");
			exit (1);
		}

		if ((header.magic1 == MAGIC1) && (header.magic2 == MAGIC2))
		{
			sectorcount[diskno-1] = header.hiseccounthi << 24 |
						header.hiseccountlo << 16 |
						header.seccounthi << 8 |
						header.seccountlo;

			sectorsize[diskno-1] = header.secsizehi << 8 |
					       header.secsizelo;

			printf ("ATR: sectorcount = %d, sectorsize = %d\n",
					sectorcount[diskno-1],
					sectorsize[diskno-1]);

			format[diskno-1] = ATR;
		}
		else
		{
			format[diskno-1] = XFD;
		}
	}

	disk[diskno-1] = fd;

	return (disk[diskno-1] != -1) ? TRUE : FALSE;
}

int SIO_Dismount (int diskno)
{
	if (disk[diskno-1] != -1)
	{
		close (disk[diskno-1]);
		disk[diskno-1] = -1;
	}
}

SIO ()
{
	CPU_Status	cpu_status;

	UBYTE DDEVIC = GetByte (0x0300);
	UBYTE DUNIT = GetByte (0x0301);
	UBYTE DCOMND = GetByte (0x0302);
	UBYTE DSTATS = GetByte(0x0303);
	UBYTE DBUFLO = GetByte(0x0304);
	UBYTE DBUFHI = GetByte(0x0305);
	UBYTE DTIMLO = GetByte(0x0306);
	UBYTE DBYTLO = GetByte(0x0308);
	UBYTE DBYTHI = GetByte(0x0309);
	UBYTE DAUX1 = GetByte(0x030a);
	UBYTE DAUX2 = GetByte(0x030b);

	int	sector;
	int	buffer;
	int	count;
	int	i;

	CPU_GetStatus (&cpu_status);

	if (disk[DUNIT-1] != -1)
	{
		int	offset;

		sector = DAUX1 + DAUX2 * 256;
		buffer = DBUFLO + DBUFHI * 256;
		count = DBYTLO + DBYTHI * 256;

		switch (format[DUNIT-1])
		{
			case XFD :
				offset = (sector-1)*128+0;
				break;
			case ATR :
				if (sector < 4)
					offset = (sector-1) * 128 + 16;
				else
					offset = (sector-4) * sectorsize[DUNIT-1] + 16 + 384;
				break;
			default :
				printf ("Fatal Error in atari_sio.c\n");
				exit (1);
		}

		lseek (disk[DUNIT-1], offset, SEEK_SET);

#ifdef DEBUG
		printf ("SIO: DCOMND = %x, SECTOR = %d, BUFADR = %x, BUFLEN = %d\n",
			DCOMND, sector, buffer, count);
#endif

		switch (DCOMND)
		{
			case 0x50 :
			case 0x57 :
				for (i=0;i<count;i++)
				{
					char	ch;

					ch = GetByte (buffer+i);
					write (disk[DUNIT-1], &ch, 1);
				}

				cpu_status.Y = 1;
				cpu_status.flag.N = 0;
				break;
			case 0x52 :
				for (i=0;i<count;i++)
				{
					char	ch;

					read (disk[DUNIT-1], &ch, 1);
					PutByte (buffer+i, ch);
				}

				cpu_status.Y = 1;
				cpu_status.flag.N = 0;
				break;
			case 0x21 :	/* Single Density Format */
			case 0x53 :	/* Get Status */
				cpu_status.Y = 1;
				cpu_status.flag.N = 0;
				break;
			default :
				printf ("SIO: DCOMND = %0x\n", DCOMND);
				cpu_status.Y = 146;
				cpu_status.flag.N = 1;
				break;
		}
	}
	else
	{
		cpu_status.Y = 146;
		cpu_status.flag.N = 1;
	}

	CPU_PutStatus (&cpu_status);

	PutByte(0x0303, cpu_status.Y);
}
