#include  <stdio.h>
#include  <string.h>
#include  "bbc.h"

#include  "general.h"
#include  "Externals.h"

#define BITBUSY (1<<0)
#define BITDRQ (1<<1)
#define BITNOTREADY (1<<7)

#define RESTORE (0<<4)
#define SEEK (1<<4)
#define STEP (2<<4)
#define STEPu (3<<4)
#define STEPIN (4<<4)
#define STEPINu (5<<4)
#define STEPOUT (6<<4)
#define STEPOUTu (7<<4)
#define READ (8<<4)
#define READm (9<<4)
#define WRITE (10<<4)
#define WRITEm (11<<4)
#define FORCEINTERRUPT (13<<4)

FILE *diskfile= NULL;

u_char com;

BOOL busy= FALSE, crcerr= FALSE, recnfnd= FALSE, writeprot= FALSE, seekerror= FALSE;
BOOL drq;
BOOL direction= FALSE;
BOOL multisec= FALSE;
BOOL motorON= FALSE;
char track= 0;
u_char sector= 0;
u_char dr;

char realtrack;
u_char offset;
BOOL dwriteprot;

static void Pulse(BOOL direction)
{
int position;
realtrack+= direction ? 1 : -1;
if(realtrack < 35)
	{
	position= realtrack * 2560;
	if(position < 0)
		{
		position= 0;
		realtrack= 0;
		}
	if(diskfile) (void) fseek(diskfile,position,SEEK_SET);
	}
}

static u_char lecture(char realtrack, u_char sector, int offset)
{
u_char vl;
long pos= (((long) realtrack * 10 + sector)<<8) + offset;
fseek(diskfile, pos, SEEK_SET);
fread(&vl, 1, 1, diskfile);
return(vl);
}

static void handleCMDT1(u_char v)
{
BOOL update, verify;
switch(v)
	{
	case STEPIN:
	case STEPOUT:
		direction= (v == STEPOUT);
	case STEP:
		update= (com & 0x10)!= 0;
		if(update)
			track+= direction ? 1 : -1;
		if(!realtrack && !direction)
			track= 0;
		else	Pulse(direction);
		break;
	case RESTORE:
		track= 0xff;
		dr= 0;
	case SEEK:
		while(track!= dr)
			{
			direction= (dr > track);
			track+= direction ? 1 : -1;
			if(!realtrack && !direction)
				{
				track= 0;
				break;
				}
			Pulse(direction);
			}
	}
verify= (com & 0x04)!= 0;
if(verify)
	if(!motorON || (realtrack!= track))
		seekerror= TRUE;
busy= FALSE;
}

static void handleCMDT2(u_char v)
{
if((v == WRITE) && dwriteprot)
	{
	writeprot= TRUE;
	return;
	}
if((!motorON) || (realtrack!= track) || (sector > 9))
	{
	recnfnd= TRUE;
	return;
	}
busy= TRUE;
offset= 0;
multisec= (com & 0x10)!= 0;
if(v == READ)
	dr= lecture(realtrack,sector,offset);
drq= TRUE;
}

static void handleCMDT4(u_char v)
{
}

void HandleCMD(u_char command)
{
u_char v= command & 0xf0;
if((v != FORCEINTERRUPT) && busy) return;
com= command; 
switch(v)
	{
	case RESTORE:
	case SEEK:
	case STEP:
	case STEPu:
	case STEPIN:
	case STEPINu:
	case STEPOUT:
	case STEPOUTu:
		busy= FALSE;
		crcerr= seekerror= FALSE;
		drq= FALSE;
		handleCMDT1(v & 0xe0);
		break;
	case READ:
	case READm:
	case WRITE:
	case WRITEm:
		busy= FALSE;
		crcerr= recnfnd= FALSE;
		drq= FALSE;
		handleCMDT2(v & 0xe0);
		break;
	case FORCEINTERRUPT:
		busy= FALSE;
		drq= FALSE; 
		handleCMDT4(v);
	}
}


static void ecriture(char realtrack, u_char sector, int offset, u_char vl)
{
u_char *pvl;
*pvl= vl;
fseek(diskfile, (((long) realtrack * 10 + sector)<<8) + offset, SEEK_SET);
fwrite(pvl, 1, 1, diskfile);
}

static void EcrDR(u_char vl)
{
if((com & 0xe0)!= WRITE || !drq || !motorON) return;
ecriture(realtrack,sector,offset,vl);
if(!(offset= ++offset & 0xff))
	{
	if(multisec)
		{
		if(++sector > 9)
			{
			recnfnd= TRUE;
			busy= drq= FALSE;
			}
		}
	else	busy= drq= FALSE;
	}
}
 
static u_char LecDR(void)
{
u_char tampon;
if((com & 0xe0)!= READ || !drq || !motorON) return(dr);
tampon= dr;
if(!(offset= ++offset & 0xff))
	if(multisec)
		{
		if(++sector > 9)
			{
			recnfnd= TRUE;
			busy= drq= FALSE;
			}
		else	dr= lecture(realtrack, sector, offset);
		}
	else	busy= drq= FALSE;
else	dr= lecture(realtrack, sector, offset);
return(tampon);
}

void WrDisk(u_char lad, u_char vx)
{
switch(lad)
	{
	case 0xe0:
	case 0xe1:
		motorON= (vx == 1);
		break;
	case 0xec:
		HandleCMD(vx);
		break;
	case 0xed:
		track= (char) vx;
		break;
	case 0xee:
		sector= vx;
		break;
	case 0xef:
		EcrDR(vx);
		break;
	}
}

u_char RdDisk(u_int ax)
{
switch(ax & 0xff)
	{
	case 0xec:
		return(busy | (drq<<1) | (seekerror<<2) | (writeprot<<6));
	case 0xed: /* Track register */
		return(track);
	case 0xee: /* Sector register */
		return(sector);
	case 0xef: /* Data register */
		return(LecDR());
	}
return(0);
}
