/***********************************************************************
*
* io.c - IBM 7090 emulator I/O routines.
*
* Changes:
*   ??/??/??   PRP   Original.
*   01/20/05   DGP   Changes for correct channel operation.
*   01/28/05   DGP   Revamped channel and tape controls.
*   
***********************************************************************/

#define EXTERN extern

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "sysdef.h"
#include "regs.h"
#include "parity.h"
#include "asciibcd.h"
#include "chan.h"
#include "io.h"
#include "screen.h"

extern int errno;
extern int prtviewlen;
extern int panelmode;
extern char errview[5][81];

static FILE *logfd;
static uint8 bsbuf[300];
static char s[100];

char *
devstr(int dev)
{
   static char s[20];

   switch (dev)
   {

   case 0:
      return "Card reader";

   case 1:
      return "Card punch";

   case 2:
      return "Printer";

   default:
      sprintf(s, "Channel %c Tape %2d",
         (dev - 3)/10 + 'A', (dev - 3) % 10 + 1);
      return s;
   }
}

char *
parsedev(char *s, int *devp)
{
   char   c;
   int   dev;

   c = *s++;
   switch (c)
   {

   case 'r':
      dev = 0;
      sysio[dev].iorw = READ;
      break;

   case 'u':
      dev = 1;
      sysio[dev].iorw = WRITE;
      break;

   case 'p':
      dev = 2;
      sysio[dev].iorw = WRITE;
      break;

   case 'a':
   case 'b':
   case 'c':
   case 'd':
   case 'e':
   case 'f':
   case 'g':
      dev = 10*(c - 'a') + 2;
      c = *s++;
      if (c >= '1' && c <= '9')
      {
         if (c == 1 && *s == '0')
	 {
            dev += 10;
            s++;
         }
	 else
	 {
            dev += c - '0';
         }
         sysio[dev].iorw = RDWRT;
         break;
      }

   default:
      iochk = 1;
      sprintf (errview[0], "parsedev: I/O check: s = %c%s\n",
	       c, s);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return (char *)0;
   }

   while (*s == ' ' || *s == '=')
      s++;

   *devp = dev;
   return s;
}

int
opendev(char *s, int dev)
{
   register i;
   int dot;

   if (*s == '@')
   {
      dorewind(dev);
      return 0;
   }
   else if (*s == '#')
   {
      if (dev <= 2)
      {
         iochk = 1;
         sprintf (errview[0],
		  "opendev: I/O check: s = %s\n", s);
#ifdef DEBUGIO
	 fprintf (stderr, errview[0]);
#endif
         run = 0;
         return -1;
      }
      sysio[dev].iorw = READ;
      return 0;
   }

   if (sysio[dev].iofd != NULL)
   {
      fclose(sysio[dev].iofd);
      sysio[dev].iofd = NULL;
      sysio[dev].iostr[0] = '\0';
   }
   if (*s == '\0' || *s == '\n')
   {
      return 0;
   }

   dot = 0;
   i = 0;

doname:
   for (; i < MAXNAME; i++)
   {
      if ((sysio[dev].iostr[i] = *s++) == '\0')
         goto nlong;
      if (sysio[dev].iostr[i] == '\n')
      {
         sysio[dev].iostr[i] = '\0';
         goto nlong;
      }
      if (sysio[dev].iostr[i] == '@')
      {
         sysio[dev].iostr[i] = '\0';
         goto nlong;
      }
      if (sysio[dev].iostr[i] == '#')
      {
         if (dev <= 2)
	 {
            iochk = 1;
            sprintf (errview[0],
		     "opendev: I/O check: s = %s, iostr = %s\n",
		     s, sysio[dev].iostr);
#ifdef DEBUGIO
	    fprintf (stderr, errview[0]);
#endif
            run = 0;
            sysio[dev].iostr[0] = '\0';
            return -1;
         }
         sysio[dev].iorw = READ;
         sysio[dev].iostr[i] = '\0';
         goto nlong;
      }
      if (sysio[dev].iostr[i] == '.')
      {
         dot = 1;
      }
      if (sysio[dev].iostr[i] == '\\')
      {
         dot = 0;
      }
   }
   iochk = 1;
   sprintf (errview[0],
	    "opendev: I/O check: s = %s, iostr = %s\n",
	    s, sysio[dev].iostr);
#ifdef DEBUGIO
   fprintf (stderr, errview[0]);
#endif
   run = 0;
   sysio[dev].iostr[0] = '\0';
   return -1;

nlong:
   if (!dot && dev <= 2)
   {
      if (dev < 2)
         s = ".cbn";
      else
         s = ".bcd";
      goto doname;
   }

   switch (sysio[dev].iorw)
   {
   case READ:
      sysio[dev].iofd = fopen(sysio[dev].iostr, "rb");
      break;

   case WRITE:
      sysio[dev].iofd = fopen(sysio[dev].iostr, "wb");
      break;

   case RDWRT:
      sysio[dev].iofd = fopen(sysio[dev].iostr, "r+b");
      if (sysio[dev].iofd == NULL)
         sysio[dev].iofd = fopen(sysio[dev].iostr, "w+b");
      break;

   }
   if (sysio[dev].iofd == NULL)
   {
      sprintf (errview[0], "%s: open failed: %s",
	       devstr(dev), strerror(errno));
      sprintf (errview[1], "filename: %s", sysio[dev].iostr);
      iochk = 1;
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
      fprintf (stderr, errview[1]);
#endif
      sysio[dev].iostr[0] = '\0';
      run = 0;
      return -1;
   }
   sysio[dev].iopos = 0;
   sysio[dev].iobin = 0;
   return 0;
}

int
mount(char *s)
{
   int dev;

   if (*s == '\n' || *s == '\0')
      return 0;
   if ((s = parsedev(s, &dev)) == (char *)0)
   {
      mounterr();
      return -1;
   }
   return opendev(s, dev);
}

void
mounterr()
{
    strcpy (errview[0], "mount file on unit: r=cr, u=pch, p=prt");
    strcpy (errview[1], "                    a-h=chan + 1-10=tape");
    strcpy (errview[2], "                    # fpt, @ rwd");
}

void
listmount()
{
   register i, j;
   int line;

   if (panelmode)
   {
      screenposition(17,1);
      clearline();
   }
   printf("\nChannel  Unit     R  File\n");
   line = 0;
   if (sysio[0].iofd)
   {
      printf("   A  Card Reader # %s\n", sysio[0].iostr);
      line++;
   }
   if (sysio[1].iofd)
   {
      printf("   A  Card Punch  * %s\n", sysio[1].iostr);
      line++;
   }
   if (sysio[2].iofd)
   {
      printf("   A  Printer     * %s\n", sysio[2].iostr);
      line ++;
   }
   for (i = 0; i < NUMCHAN; i++)
   {
      for (j = 1; j < 10; j++)
      {
         if (sysio[i*10+j+2].iofd != NULL)
         {
            clearline();
            printf("   %c  Tape %2d     %c %s\n",
		    'A' + i, j,
		    sysio[10*i+j+2].iorw == 1? '#' : ' ',
		    sysio[i*10+j+2].iostr);
            line++;
         }
         if (panelmode && line == prtviewlen)
         {
            printf ("press enter to continue:");
            fgetc (stdin);
            screenposition (17,1);
            clearline();
            printf("\nChannel  Unit     R  File\n");
            line = 0;
         }
      }
   }
   for (; line < 6; line++) { clearline(); printf ("\n"); }
}

int
readrec(int dev, char *buf, int len)
{
   register i, j;
   int n;

   if (sysio[dev].iofd == NULL)
   {
      iochk = 1;
      sprintf(errview[0], "%s: No file mounted", devstr(dev));
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }
   if ((sysio[dev].iorw & 1) == 0)
   {
      iochk = 1;
      sprintf(errview[0], "%s(%s): File is write-only",
         devstr(dev), sysio[dev].iostr);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }

readmore:
   n = fread(buf, 1, len, sysio[dev].iofd);
   if (n == 0)
   {
      buf[0] = (char)0217;
      for (i = 1; i < len; i++)
         buf[i] = 0;
      if (dev == 0 && feof(sysio[dev].iofd))
      {
#ifdef DEBUGCHAN
         fprintf (stderr, "EOF on dev %d, chan %d\n",
		  dev, sysio[dev].iochn);
#endif
         channel[sysio[dev].iochn].ceof = 1;
      }
      return;
   }
   else if (n < 0)
   {
      iochk = 1;
      sprintf(errview[0], "%s(%s): Read error %d",
	      devstr(dev), sysio[dev].iostr, errno);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }

   sysio[dev].iopos += n;
   for (j = n; j < len; j++)
      buf[j] = 0;
   for (i = 0; i < n; i++)
   {
      if (buf[i] & 0200)
         break;
   }
   if (i == n)
      goto readmore;
   if (i > 0)
   {
      for (j = 0; i < n; )
         buf[j++] = buf[i++];
      n = fread(&buf[j], 1, len - j, sysio[dev].iofd);
      if (n < 0)
      {
         iochk = 1;
         sprintf(errview[0], "%s(%s): Read error %d",
		 devstr(dev), sysio[dev].iostr, errno);
#ifdef DEBUGIO
	 fprintf (stderr, errview[0]);
#endif
         run = 0;
         return;
      }
      sysio[dev].iopos += n;
      for (j = j + n; j < len; j++)
         buf[j] = 0;
   }
   for (i = 1; i < n; i++)
   {
      if (buf[i] & 0200)
      {
         for (j = i; j < len; j++)
            buf[j] = 0;
         sysio[dev].iopos = sysio[dev].iopos - n + i;
         fseek(sysio[dev].iofd, sysio[dev].iopos, 0);
         break;
      }
   }
   return (i);
}

void
bsr(int ch)
{
   int   dev;
   register i;
   int n;

   dev = whatdev(ch);
   if (sysio[dev].iofd == NULL)
   {
      iochk = 1;
      sprintf(errview[0], "%s: No file mounted", devstr(dev));
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }
   if ((sysio[dev].iorw & 1) == 0)
   {
      iochk = 1;
      sprintf(errview[0], "%s(%s): File is write-only",
	      devstr(dev), sysio[dev].iostr);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }
#ifdef DEBUGCHAN
   sprintf(s, "bsr   %s                                %10ld",
	   devstr(dev), sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif

   while (sysio[dev].iopos > 0)
   {
      if (sysio[dev].iopos > sizeof bsbuf)
      {
         n = sizeof bsbuf;
         sysio[dev].iopos -= sizeof bsbuf;
      }
      else
      {
         n = sysio[dev].iopos;
         sysio[dev].iopos = 0;
      }
      fseek(sysio[dev].iofd, sysio[dev].iopos, 0);
      fread(bsbuf, 1, n, sysio[dev].iofd);
      for (i = n - 1; i >= 0; i--)
      {
         if (bsbuf[i] & 0200)
	 {
            sysio[dev].iopos += i;
            fseek(sysio[dev].iofd, sysio[dev].iopos, 0);
            goto done;
         }
      }
   }
   fseek(sysio[dev].iofd, 0L, 0);
done:

   if (sysio[dev].iopos == 0)
      channel[ch].cbot = 1;
}

void
bsf(int ch)
{
   int   dev;
   register i;
   int n;

   dev = whatdev(ch);
   if (sysio[dev].iofd == NULL)
   {
      iochk = 1;
      sprintf(errview[0], "%s: No file mounted", devstr(dev));
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }
   if ((sysio[dev].iorw & 1) == 0)
   {
      iochk = 1;
      sprintf(errview[0], "%s(%s): File is write-only",
	      devstr(dev), sysio[dev].iostr);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }

#ifdef DEBUGCHAN
   sprintf(s, "bsf   %s                                %10ld",
	   devstr(dev), sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif

   while (sysio[dev].iopos > 0)
   {
      if (sysio[dev].iopos > sizeof bsbuf)
      {
         n = sizeof bsbuf;
         sysio[dev].iopos -= sizeof bsbuf;
      }
      else
      {
         n = sysio[dev].iopos;
         sysio[dev].iopos = 0;
      }
      fseek(sysio[dev].iofd, sysio[dev].iopos, 0);
      fread(bsbuf, 1, n, sysio[dev].iofd);
      for (i = n - 1; i >= 0; i--)
      {
         if (bsbuf[i] == 0217 && (i == n - 1 || bsbuf[i + 1] & 0200))
	 {
            sysio[dev].iopos += i;
            fseek(sysio[dev].iofd, sysio[dev].iopos, 0);
            goto done;
         }
      }
   }
   fseek(sysio[dev].iofd, 0L, 0);
done:

   if (sysio[dev].iopos == 0)
      channel[ch].cbot = 1;
}

void
writerec(int dev, char *buf, int len)
{
   int n;

   if (sysio[dev].iofd == NULL)
   {
      if (dev == 2)
         return;
      iochk = 1;
      sprintf(errview[0], "%s: No file mounted", devstr(dev));
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }
   if ((sysio[dev].iorw & 2) == 0)
   {
      iochk = 1;
      sprintf(errview[0], "%s(%s): File is read-only",
	      devstr(dev), sysio[dev].iostr);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }

   buf[0] |= 0200;
   buf[len] = (char)0200;
   n = fwrite(buf, 1, len + 1, sysio[dev].iofd);
   if (n != len + 1)
   {
      iochk = 1;
      if (n < 0)
         sprintf(errview[0], "%s(%s): Write error %d",
		 devstr(dev), sysio[dev].iostr, errno);
      else
         sprintf(errview[0],
		 "%s(%s): Out of disk space, wrote %d of %d bytes",
		 devstr(dev), sysio[dev].iostr, n, len);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      run = 0;
      return;
   }
   sysio[dev].iopos += len;
   fseek(sysio[dev].iofd, sysio[dev].iopos, 0);
   if (sysio[dev].iopos > TAPESIZE)
      channel[(dev - 3)/10].ceot = 1;
}

void
dorewind(int dev)
{

#ifdef DEBUGCHAN
   sprintf(s, "rew   %s                                %10ld",
	   devstr(dev), sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif

   if (dev == 1 || dev == 2)
   {
      fflush(sysio[dev].iofd);
      return;
   }
   if (sysio[dev].iofd == NULL)
   {
      iochk = 1;
      run = 0;
      sprintf (errview[0], "rewind: dev %d is not open", dev);
#ifdef DEBUGIO
      fprintf (stderr, errview[0]);
#endif
      return;
   }
   fseek(sysio[dev].iofd, 0L, 0);
   sysio[dev].iopos = 0;
   sysio[dev].iobin = 0;

}

void
unload(int dev)
{
#ifdef DEBUGCHAN
   sprintf(s, "rwunl %s                                %10ld",
	   devstr(dev), sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif

   if (sysio[dev].iofd == NULL)
   {
      return;
   }
   fclose(sysio[dev].iofd);
   sysio[dev].iofd = NULL;

}

int
dismount (char *s)
{
   int dev;

  if (*s == '\n' || *s == '\0')
     return 1;
  if (parsedev(s, &dev) == (char *)0)
     return -1;

   unload(dev);
   return 0;
}

void
wef(int ch)
{
   int dev;
   unsigned char tapemark[2];

   dev = whatdev(ch);
#ifdef DEBUGCHAN
   sprintf(s, "wef   %s                                %10ld",
	   devstr(dev), sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif

   if (dev < 3)
      return;

   tapemark[0] = 017;
   writerec(dev, tapemark, 1);
}


void
bincard(char *cnvbuf, unsigned short *crcol)
{
   register i;

   for (i = 0; i < 80; i++)
      crcol[i] = ((unsigned short)(cnvbuf[2*i] & 077) << 6) |
            (cnvbuf[2*i + 1] & 077);
}

void
cardbin(unsigned short *crcol, char *cnvbuf)
{
   register i;

   for (i = 0; i < 80; i++)
   {
      cnvbuf[2*i] = oddpar[(crcol[i] >> 6) & 077];
      cnvbuf[2*i + 1] = oddpar[crcol[i] & 077];
   }
   cnvbuf[0] |= 0200;
}

void
cardbcd(unsigned short *cbuf, unsigned char *bbuf, int len)
{
   register num;
   register row;

   for (; len; len--)
   {
      row = 00001;
      for (num = 10; --num; )
      {
         if (*cbuf & row)
            break;
         row <<= 1;
      }
      if (num == 8 && (*cbuf & 00174) != 0)
      {
         row = 00004;
         for (num = 16; --num > 10; )
	 {
            if (*cbuf & row)
               break;
            row <<= 1;
         }
      }
      else if (num == 0 && *cbuf & 01000)
         num = 10;
      if ((*cbuf & 01000) && num != 10)
         num |= 060;
      else if (*cbuf & 02000)
         num |= 040;
      else if (*cbuf & 04000)
         num |= 020;
      else if (num == 10)
         num = 0;
      else if (num == 0)
         num = 060;
      cbuf++;
      *bbuf++ = evenpar[num];
   }
}

void
logstr(char *s)
{

   fprintf(logfd, "%s\n", s);
}

void
ioinit()
{
   register i;

   for (i = 0; i < IODEV; i++)
   {
      memset (&sysio[i], '\0', sizeof (IO_t));
   }
   sysio[0].iorw = READ;
   sysio[1].iorw = WRITE;
   sysio[2].iorw = WRITE;
   logfd = fopen("printlog.lst", "w");
}

void
iofin()
{
   register i;

   for (i = 0; i < IODEV; i++)
   {
      if (sysio[i].iofd != NULL)
         fclose(sysio[i].iofd);
      sysio[i].iofd = NULL;
   }
   fclose(logfd);
}
