void SIO_SEROUT (unsigned char byte, int cmd)
{
  checksum += (unsigned char)byte;
  while (checksum > 255)
    checksum = checksum - 255;

#ifdef DEBUG
  printf ("SIO_SEROUT: byte = %x, checksum = %x, cmd = %d\n",
	  byte, checksum, cmd);
#endif

  if (cmd)
    {
      CommandFrame[ncmd++] = byte;
      if (ncmd == 5)
	{
	  Command_Frame ();

	  offst = 0;
	  checksum = 0;
	  ncmd = 0;
	}
      else
	{
	  DELAYED_SEROUT_IRQ = 1;
	}

      if (CommandFrame[0] == 0)
	ncmd = 0;
    }
  else if (sio_state == SIO_Put)
    {
      DataBuffer[buffer_offset++] = byte;
      if (buffer_offset == 130)
	{
	  int sector;
	  int dskno;
	  int i;

	  checksum = 0;

	  for (i=1;i<129;i++)
	    {
	      checksum += (unsigned char)DataBuffer[i];
	      while (checksum > 255)
		checksum = checksum - 255;
	    }

	  if (checksum != DataBuffer[129])
	    {
	      printf ("Direct SIO Write Error\n");
	      printf ("Calculated Checksum = %x\n", checksum);
	      printf ("Actual Checksum = %x\n", DataBuffer[129]);
	      exit (1);
	    }

	  dskno = CommandFrame[0] - 0x31;
	  sector = CommandFrame[2] + CommandFrame[3] * 256;

#ifdef DEBUG
	  printf ("Sector: %d(%x)\n", sector, sector);
#endif

	  SeekSector (dskno, sector);

	  write (disk[dskno], &DataBuffer[1], 128);
	  DataBuffer[buffer_offset] = 0x41; /* ACK */
	  DataBuffer[buffer_offset+1] = 0x43; /* OPERATION COMPLETE */
	  buffer_size = buffer_offset + 2;
	  DELAYED_SEROUT_IRQ = 1;
	  DELAYED_XMTDONE_IRQ = 3;
	  DELAYED_SERIN_IRQ = 7;
	  DELAYED_XMTDONE_IRQ = 5;
	  DELAYED_SERIN_IRQ = 150;
	}
      else
	{
	  DELAYED_SEROUT_IRQ = 4;
	}
    }
  else
    {
      DELAYED_SEROUT_IRQ = 1;
      ncmd = 0;
    }
}

int SIO_SERIN (void)
{
  int byte;

  if (buffer_offset < buffer_size)
    {
      byte = (int)DataBuffer[buffer_offset++];

#ifdef DEBUG
      printf ("SERIN: byte = %x\n", byte);
#endif

      if (buffer_offset < buffer_size)
	{
#ifdef DEBUG
	  printf ("Setting SERIN Interrupt again\n");
#endif
	  DELAYED_SERIN_IRQ = 3;
	  DELAYED_SERIN_IRQ = 4;
	}
    }

  return byte;
}

void SIO (void)
{
  /* UBYTE DDEVIC = memory[0x0300]; */
  UBYTE DUNIT = memory[0x0301];
  UBYTE DCOMND = memory[0x0302];
  /* UBYTE DSTATS = memory[0x0303]; */
  UBYTE DBUFLO = memory[0x0304];
  UBYTE DBUFHI = memory[0x0305];
  /* UBYTE DTIMLO = memory[0x0306]; */
  UBYTE DBYTLO = memory[0x0308];
  UBYTE DBYTHI = memory[0x0309];
  UBYTE DAUX1 = memory[0x030a];
  UBYTE DAUX2 = memory[0x030b];

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

  if (drive_status[DUNIT-1] != Off)
    {
      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 - 1) * sectorsize[DUNIT-1] + 16;
/*
   offset = 3*128 + (sector-4) * sectorsize[DUNIT-1] + 16;
*/
	      break;
	    default :
	      printf ("Fatal Error in atari_sio.c\n");
	      Atari800_Exit (FALSE);
	      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 :
	      if (drive_status[DUNIT-1] == ReadWrite)
		{
		  write (disk[DUNIT-1], &memory[buffer], count);
		  regY = 1;
		  ClrN;
		}
	      else
		{
		  regY = 146;
		  SetN;
		}
	      break;
	    case 0x52 :
	      read (disk[DUNIT-1], &memory[buffer], count);
	      regY = 1;
	      ClrN;
	      break;
	    case 0x21 : /* Single Density Format */
	    case 0x22 : /* Duel Density Format */
	    case 0x66 : /* US Doubler Format - I think! */
	      regY = 1;
	      ClrN;
	      break;
/*
   Status Request from Atari 400/800 Technical Reference Notes

   DVSTAT + 0	Command Status
   DVSTAT + 1	Hardware Status
   DVSTAT + 2	Timeout
   DVSTAT + 3	Unused

   Command Status Bits

   Bit 0 = 1 indicates an invalid command frame was received
   Bit 1 = 1 indicates an invalid data frame was received
   Bit 2 = 1 indicates that a PUT operation was unsuccessful
   Bit 3 = 1 indicates that the diskete is write protected
   Bit 4 = 1 indicates active/standby
   
   plus

   Bit 5 = 1 indicates double density
   Bit 7 = 1 indicates duel density disk (1050 format)
*/
	    case 0x53 :	/* Get Status */
	      /*
	      for (i=0;i<count;i++)
		{
		  if (sectorsize[DUNIT-1] == 256)
		    memory[buffer+i] = 32 + 16;
		  else
		    memory[buffer+i] = 16;
		}
		*/
	      if (count!=4) {
		regY = 146;
		SetN;
		break;
	      }
	      if (drive_status[DUNIT-1] == ReadWrite) {
		memory[buffer]=(sectorsize[DUNIT-1] == 256)?(32+16):(16);
		memory[buffer+1]=192;
	      } else {
		memory[buffer]=(sectorsize[DUNIT-1] == 256)?(32):(0);
		memory[buffer+1]=64;
	      }		
	      if (sectorcount[DUNIT-1]==1040) memory[buffer]|=128;
	      memory[buffer+2]=1;
	      memory[buffer+3]=0;
	      regY = 1;
	      ClrN;
	      break;
	    default :
	      printf ("SIO: DCOMND = %0x\n", DCOMND);
	      regY = 146;
	      SetN;
	      break;
	    }
	}
      else
	{
	  regY = 146;
	  SetN;
	}
    }
  else
    {
      regY = 138;
      SetN;
    }

  memory[0x0303] = regY;
}
/*
  printf ("Command frame: %02x %02x %02x %02x %02x\n",
	  CommandFrame[0], CommandFrame[1], CommandFrame[2],
	  CommandFrame[3], CommandFrame[4]);
*/
  
  switch (CommandFrame[1]) {
    case 'R' : /* Read */
      {
	int sector;
	int dskno;
	int i;
	int checksum;

	dskno = CommandFrame[0] - 0x31;
	sector = CommandFrame[2] + CommandFrame[3] * 256;
	SeekSector (dskno, sector);
	
	DataBuffer[0] = 0x41; /* ACK */
	DataBuffer[1] = 0x43; /* OPERATION COMPLETE */

	read (disk[dskno], &DataBuffer[2], 128);
	checksum = 0;
	for (i=2;i<130;i++) {
	    checksum += (unsigned char)(DataBuffer[i]);
	    while (checksum > 255)
	      checksum = checksum - 255;
	  }
	DataBuffer[130] = checksum;
	ExpectedBytes = 131;
	DataIndex = 0;
	DELAYED_SERIN_IRQ += SERIN_INTERVAL+312;
      }
      break;
    case 'S' : /* Status */
#ifdef DEBUG
      printf ("Status command\n");
#endif
      DataBuffer[0] = 0x41; /* ACK */
      DataBuffer[1] = 0x43; /* OPERATION COMPLETE */

      /* Should be really done in a different way */
      DataBuffer[2] = 0x10; /* 2ea */
      DataBuffer[3] = 0x00; /* 2eb */
      DataBuffer[4] = 0x01; /* 2ec */
      DataBuffer[5] = 0x00; /* 2ed */
      DataBuffer[6] = 0x11; /* Checksum */

      ExpectedBytes = 7;
      DataIndex = 0;
      DELAYED_SERIN_IRQ += SERIN_INTERVAL;
      break;
    case 'W' : /* Write with verify */
    case 'P' : /* Put without verify */
      DataBuffer[0] = 0x41; /* ACK */
      
      ExpectedBytes = 1;
      DataIndex = 0;

      DELAYED_SERIN_IRQ += SERIN_INTERVAL;
      break;
    case '!' : /* Format */
      printf ("Format command\n");
      break;
    case 'T' : /* Read Address */
      printf ("Read Address command\n");
      break;
    case 'Q' : /* Read Spin */
      printf ("Read Spin command\n");
      break;
    case 'U' : /* Motor On */
      printf ("Motor On command\n");
      break;
    case 'V' : /* Verify Sector */
      printf ("Verify Sector\n");
      break;
    default :
      printf ("Command frame: %02x %02x %02x %02x %02x\n",
	      CommandFrame[0], CommandFrame[1], CommandFrame[2],
	      CommandFrame[3], CommandFrame[4]);
      break;
  }
}

