/***********************************************************************
*
* chan.c - IBM 7090 emulator channel routines.
*
* Changes:
*   ??/??/??   PRP   Original.
*   01/20/05   DGP   Changes for correct channel operation.
*   01/28/05   DGP   Revamped channel and tape controls.
*   02/07/05   DGP   Fixed max tape on a channel access.
*   
***********************************************************************/

#define EXTERN extern

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

#include "sysdef.h"
#include "regs.h"
#include "parity.h"
#include "io.h"
#include "trace.h"
#include "chan.h"
#include "console.h"
#include "lights.h"

extern char tonative[64];
extern char toaltnative[64];
extern char prtview[100][82];
extern int prtviewlen;
extern char errview[5][81];
extern char *devstr();

#ifdef DEBUGCHAN
static char s[100];
#endif

/*
 * Card machine states:
 *      0:  9 left      1:  9 right
 *      2:  8 left      3:  8 right
 *      4:  7 left      5:  7 right
 *      6:  6 left      7:  6 right
 *      8:  5 left      9:  5 right
 *     10:  4 left     11:  4 right
 *     12:  3 left     13:  3 right
 *     14:  2 left     15:  2 right
 *     16:  1 left     17:  1 right
 *     18:  0 left     19:  0 right
 *     20: 11 left     21: 11 right
 *     22: 12 left     23: 12 right
 *     24: End of cycle
 *     47: Beginning of new cycle
 *
 * Read printer states:
 *      0:  9 left      1:  9 right
 *      2:  8 left      3:  8 right
 *      4:  7 left      5:  7 right
 *      6:  6 left      7:  6 right
 *      8:  5 left      9:  5 right
 *     10:  4 left     11:  4 right
 *     12:  3 left     13:  3 right
 *     14:  2 left     15:  2 right
 *     16:  1 left     17:  1 right
 *     18: 84 left     19: 84 right echo
 *     20:  0 left     21:  0 right
 *     22: 83 left     23: 83 right echo
 *     24: 11 left     25: 11 right
 *     26:  9 left     27:  9 right echo
 *     28: 12 left     29: 12 right
 *     30:  8 left     31:  8 right echo
 *     32:  7 left     33:  7 right echo
 *     34:  6 left     35:  6 right echo
 *     36:  5 left     37:  5 right echo
 *     38:  4 left     39:  4 right echo
 *     40:  3 left     41:  3 right echo
 *     42:  2 left     43:  2 right echo
 *     44:  1 left     45:  1 right echo
 *     46: End of cycle
 *     47: Beginning of new cycle
 */

uint16 crstate, crcol[80];	/* Card reader card code */
uint16 cpstate, cpcol[80];	/* Card punch card code */
uint16 prstate, prcol[120];	/* Printer card code */
uint8 cnvbuf[161];		/* Card data conversion buffer */
uint8 prtbuf[73];		/* Printer data conversion buffer */
uint16 pr_ws_rs[] = {		/* Convert from read state to write */
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
   16, 17, 18, 18, 18, 19, 20, 20, 20, 21, 22, 22, 22, 23,
   24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 47
};

void
do_chan(int ch)
{
#ifdef DEBUGCHAN1
   fprintf (stderr, "do_chan: chan = %d\n", ch);
#endif
   run_chan(ch);
   if (channel[ch].csel)
   {
      channel[ch].csel = 0;
      channel[ch].ctrp = 1;
   }
}

void
run_chan(int ch)
{
#ifdef DEBUGCHAN1
   fprintf (stderr, "run_chan: chan = %d\n", ch);
#endif
   if (channel[ch].ccyc > cycle_count)
   {
      cycle_count = channel[ch].ccyc;
   }
   while (channel[ch].cact)
   {
      cycle_chan(ch);
   }
}

void
active_chan(int ch)
{
#ifdef DEBUGCHAN1
   fprintf (stderr, "active_chan: chan = %d\n", ch);
#endif
   cycle_chan(ch);
   while (channel[ch].cact > 1)
   {
      cycle_chan(ch);
   }
}

void
man_chan(int ch)
{
#ifdef DEBUGCHAN1
   fprintf (stderr, "man_chan: chan = %d\n", ch);
#endif
   if (channel[ch].cwr && channel[ch].csel == WRITE_SEL)
   {
      channel[ch].cdrl = getmeml(channel[ch].car);
      channel[ch].cdrh = memh[channel[ch].car];
      channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
   }
   channel[ch].cact = 1;
   chan_in_op |= 1 << ch;
}

void
load_chan(int ch, int autoload)
{
#ifdef DEBUGCHAN
   fprintf (stderr, "load_chan: chan = %d, autoload = %d\n", ch, autoload);
#endif
   cycle_count++;
   load_chan_cycle(ch, autoload);
   while (channel[ch].cact > 1)
   {
      cycle_chan(ch);
   } 
   if (channel[ch].csel && channel[ch].ccyc < next_steal)
   {
      next_steal = channel[ch].ccyc;
   }
}

void
load_chan_cycle(int ch, int autoload)
{
   int addr;
   uint8 chsrh; uint32 chsrl;

   addr = 0;
   if (autoload == 1)
   {
      chsrh = (char)SIGN + 002;
      chsrl = 000003000000;
      channel[ch].clr = 0;
   }
   else if (autoload == 2)
   {
      chsrh = kyh;
      chsrl = kyl;
   }
   else
   {
      addr = channel[ch].clr;
      chsrh = memh[channel[ch].clr];
      chsrl = getmeml(channel[ch].clr);
      channel[ch].clr = (channel[ch].clr + 1) & MEMLIMIT;

      itrc_h[itrc_idx] = chsrh;
      itrc_l[itrc_idx] = chsrl;
      itrc_buf[itrc_idx++] = channel[ch].clr + ((long)(ch+1) << 16);
      if (itrc_idx == 32)
         itrc_idx = 0;
   }
   channel[ch].cop = ((chsrh & SIGN) >> 5) | ((chsrh & 06) >> 1) |
        ((chsrl & 000000200000) >> 13);
   channel[ch].cwr = ((chsrh & 1) << 14) | ((chsrl & 037777000000) >> 18);
   channel[ch].car = chsrl & 000000077777 & MEMLIMIT;
#ifdef DEBUGCHAN
   fprintf (stderr, "load_chan_cycle: op = %c%02o%010lo\n",
	    (chsrh & SIGN)? '-' : ' ',
	    ((chsrh & 017) << 2) | (short)(chsrl >> 30),
	    chsrl & 07777777777);
   fprintf (stderr,
	"   cop = %02o, cwr = %05o, car = %05o, clr = %05o, unit = %02o%03o\n",
	    channel[ch].cop, channel[ch].cwr, channel[ch].car, addr,
	    ch+1, channel[ch].cunit);
#endif
   if (chsrl & 000000400000)
   {
      channel[ch].car = getmeml(channel[ch].car) & 000000077777 & MEMLIMIT;
      cycle_count++;
   }
   if (autoload == 2)
      return;
   if ((channel[ch].cop & 07) == 01)	/* TCH */
   {
      channel[ch].clr = channel[ch].car;
      channel[ch].cact = 2;
   }
   else if (channel[ch].cwr && channel[ch].csel == WRITE_SEL)
   {
      channel[ch].cact = 3;
   }
   else
   {
      channel[ch].cact = 1;
   }
   chan_in_op |= 1 << ch;
}

void
cycle_chan(int ch)
{
#ifdef DEBUGCHAN1
   fprintf (stderr, "cycle_chan: chan = %d\n", ch);
#endif
   cycle_count++;
   if (cycle_count >= next_lights)
   {
      lights();
      next_lights = cycle_count + NEXTLIGHTS;
      next_steal = next_lights;
      check_intr();
   }

   switch (channel[ch].cact)
   {

   case 1:
      switch (channel[ch].csel)
      {

      case READ_SEL:
      case WRITE_SEL:
         rw_chan(ch);
         break;

      case BSR_SEL:
         bsr(ch);
         channel[ch].csel = 0;
         channel[ch].cact = 0;
         chan_in_op &= ~(1 << ch);
         break;

      case BSF_SEL:
         bsf(ch);
         channel[ch].csel = 0;
         channel[ch].cact = 0;
         chan_in_op &= ~(1 << ch);
         break;

      case WEF_SEL:
         wef(ch);
         channel[ch].csel = 0;
         channel[ch].cact = 0;
         chan_in_op &= ~(1 << ch);
         break;

      }
      break;

   case 2:
      load_chan_cycle(ch, 0);
      break;

   case 3:
      channel[ch].cdrl = getmeml(channel[ch].car);
      channel[ch].cdrh = memh[channel[ch].car];
      channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
      channel[ch].cact = 1;
      break;
   }
}

void
rw_chan(int ch)
{

   channel[ch].ccyc = cycle_count + 33;

#ifdef DEBUGCHANRW
   sprintf(s, "rw_chan %-18.18s   %s     op = %o len = %o",
	   devstr(whatdev(ch)),
	   channel[ch].csel == READ_SEL ?  "READ " : "WRITE",
	   channel[ch].cop, channel[ch].cwr);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif
   switch(channel[ch].cop & 07)
   {

   case 00:   /* IOCD */
      if (channel[ch].cwr)
      {
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done1;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr > 0)
            return;
      }
done1:
      channel[ch].csel = 0;
      endrecord(ch, 0);
      channel[ch].cact = 0;
      chan_in_op &= ~(1 << ch);
      return;

   case 02:   /* IORP */
      if (channel[ch].cwr)
      {
         if (channel[ch].ceor)
            goto done2;
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done2;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr)
            return;
      }
done2:
      endrecord(ch, 1);
      if (channel[ch].ctm)
      {
         channel[ch].csel = 0;
         channel[ch].cact = 0;
         chan_in_op &= ~(1 << ch);
         return;
      }
      break;

   case 03:   /* IORT */
      if (channel[ch].cwr)
      {
         if (channel[ch].ceor)
            goto done3;
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done3;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr)
            return;
      }
done3:
      endrecord(ch, 1);
      channel[ch].cact = 0;
      chan_in_op &= ~(1 << ch);
      if (channel[ch].ctm)
      {
         channel[ch].csel = 0;
      }
      else
      {
         channel[ch].ccyc = cycle_count + 600;
      }
      return;

   case 04:   /* IOCP */
      if (channel[ch].cwr)
      {
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done4;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr)
            return;
      }
done4:
      if (channel[ch].ctm)
      {
         channel[ch].csel = 0;
         channel[ch].cact = 0;
         chan_in_op &= ~(1 << ch);
         return;
      }
      break;

   case 05:   /* IOCT */
      if (channel[ch].cwr)
      {
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done5;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr)
            return;
      }
done5:
      if (channel[ch].csel == WRITE_SEL && (channel[ch].cunit & 0700) == 0200)
	 endrecord(ch, 0);
      channel[ch].cact = 0;
      chan_in_op &= ~(1 << ch);
      if (channel[ch].ctm)
      {
         channel[ch].csel = 0;
      }
      else
      {
         channel[ch].ccyc = cycle_count + 600;
      }
      return;

   case 06:   /* IOSP */
      if (channel[ch].cwr)
      {
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done6;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr && !channel[ch].ceor)
            return;
      }
done6:
      if (channel[ch].ctm)
      {
         channel[ch].csel = 0;
         channel[ch].cact = 0;
         chan_in_op &= ~(1 << ch);
         return;
      }
      break;

   case 07:   /* IOST */
      if (channel[ch].cwr)
      {
         if (channel[ch].csel == READ_SEL)
	 {
            readword(ch);
            if (channel[ch].ctm)
               goto done7;
            if ((channel[ch].cop & 010) == 0)
	    {
               setmeml(channel[ch].car, channel[ch].cdrl);
               memh[channel[ch].car] = channel[ch].cdrh;
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
            }
         }
	 else
	 {
            writeword(ch);
            channel[ch].cdrl = getmeml(channel[ch].car);
            channel[ch].cdrh = memh[channel[ch].car];
            if (channel[ch].cwr > 1)
               channel[ch].car = (channel[ch].car + 1) & MEMLIMIT;
         }
         channel[ch].cwr--;
         if (channel[ch].cwr && !channel[ch].ceor)
            return;
      }
done7:
      if (channel[ch].csel == WRITE_SEL && (channel[ch].cunit & 0700) == 0200)
	 endrecord(ch, 0);
      channel[ch].cact = 0;
      chan_in_op &= ~(1 << ch);
      if (channel[ch].ctm)
      {
         channel[ch].csel = 0;
      }
      else
      {
         channel[ch].ccyc = cycle_count + 600;
      }
      return;


   default:   /* invalid ops, TCH handled in load_chan */
      machchk = 1;
      run = 0;
      channel[ch].cact = 0;
      chan_in_op &= ~(1 << ch);
      sprintf (errview[0], "I/O MACHINE check: cop = %o\n",
	       channel[ch].cop & 07);
      return;
   }

   channel[ch].cact = 2;
   channel[ch].ccyc = cycle_count + 1;
}

int
unitcheck(int ch, int wr)
{
   uint16 unit;

   channel[ch].ctm = 0;
   unit = channel[ch].cunit;
   if (ch == 0)
   {
      if (unit == 0321 && wr == 1) /* Chan A Card reader */
      {
         sysio[0].iochn = ch;
         readcard();
         channel[ch].ccyc = cycle_count + 4600;
         return 0;
      }
      if (unit == 0341 && wr == 0) /* Chan A Card punch */
      {
         initpunch();
         channel[ch].ccyc = cycle_count + 8000;
         return 1;
      }
      if (unit == 0361)		/* Chan A BCD printer */
      {
         initprint();
         channel[ch].ccyc = cycle_count + 5000;
         return 2;
      }
      if (unit == 0362)		/* Chan A Binary printer */
      {
         initprint();
         channel[ch].ccyc = cycle_count + 5000;
         return 2;
      }
   }

   if (channel[ch].cbot)
      channel[ch].ccyc = cycle_count + 4000;
   else
      channel[ch].ccyc = cycle_count + 600;
   if (unit >= 0201 && unit <= 0212)	/* BCD tape */
      return unit - 0201 + 3 + 10*ch;
   if (unit >= 0221 && unit <= 0232)	/* Binary tape */
      return unit - 0221 + 3 + 10*ch;

   iochk = 1;
   sprintf (errview[0],
	    "unitcheck: I/O check: ch = %d, cop = %o, cunit = %o\n",
	    ch+1, channel[ch].cop & 07, channel[ch].cunit);
#ifdef DEBUGCHAN
   fprintf (stderr, errview[0]);
#endif
   run = 0;
   return -1;
}

int
whatdev(int ch)
{
   uint16 unit;

   unit = channel[ch].cunit;
   if (ch == 0)
   {
      if (unit == 0321)		/* Chan A Card reader */
      {
         return 0;
      }
      if (unit == 0341)		/* Chan A Card punch */
      {
         return 1;
      }
      if (unit == 0361)		/* Chan A BCD printer */
      {
         return 2;
      }
      if (unit == 0362)		/* Chan A Binary printer */
      {
         return 2;
      }
   }

   if (unit >= 0201 && unit <= 0212) /* BCD tape */
      return unit - 0201 + 3 + 10*ch;
   if (unit >= 0221 && unit <= 0232) /* Binary tape */
      return unit - 0221 + 3 + 10*ch;

   iochk = 1;
   sprintf (errview[0],
	    "whatdev: I/O check: ch = %d, cop = %o, cunit = %o\n",
	    ch+1, channel[ch].cop & 07, channel[ch].cunit);
#ifdef DEBUGCHAN
   fprintf (stderr, errview[0]);
#endif
   run = 0;
   return -1;
}

void
readcard()
{
   register i;

   crstate = 0;
   for (i = 0; i < 80; i++)
      crcol[i] = 0;
   i = readrec(0, cnvbuf, sizeof cnvbuf - 1);
#ifdef DEBUGCHAN
   fprintf (stderr, "readcard: ceof = %d(%d)\n",
	    channel[sysio[0].iochn].ceof, channel[0].ceof);
#endif
   if (channel[sysio[0].iochn].ceof)
      return;
   if (iochk)
      return;
   bincard(cnvbuf, crcol);
}

void
initpunch()
{
   register i;

   cpstate = 0;
   for (i = 0; i < 80; i++)
      cpcol[i] = 0;
}

void
writepunch()
{

   cardbin(cpcol, cnvbuf);
   writerec(1, cnvbuf, sizeof cnvbuf - 1);
   initpunch();
   return;
}

void
initprint()
{
   register i;

   prstate = 0;
   for (i = 0; i < 120; i++)
      prcol[i] = 0;
}

void
writeprint()
{
   int i, j;
   int maxline = prtviewlen - 1;

   cardbcd(prcol, prtbuf, 72);
   writerec(2, prtbuf, sizeof prtbuf - 1);

   for (i = 0; i < maxline; i++)
      strcpy (prtview[i], prtview[i + 1]);

   for (j = 0; j < 72; j++)
      prtview[maxline][j] = tonative[prtbuf[j] & 077];
   prtview[maxline][72] = 0;

   logstr(prtview[maxline]);
   lights();
   pview();
   initprint();
   return;
}

void
printerror(char *s)
{
   int i, j;
   int maxline = prtviewlen - 1;

   for (i = 0; i < maxline; i++)
      strcpy (prtview[i], prtview[i + 1]);

   for (j = 0; s[j] && j < 80; j++)
      prtview[maxline][j] = s[j];
   prtview[maxline][80] = 0;

   logstr(prtview[maxline]);
   lights();
   pview();
}

void
readword(int ch)
{
   Tape_t *ptape;
   uint8  *pbuf;
   int32  i, j;
   uint32 m;
   uint16 unit;
   uint16 col;
   uint16 dev;
   uint16 tdev;
   uint8  dchr;

   unit = channel[ch].cunit;
   channel[ch].cdrl = 0;
   channel[ch].cdrh = 0;
   channel[ch].ceor = 0;
   channel[ch].ctm = 0;

   if (unit == 0321)		/* Card reader */
   {
      if (crstate >= 24)
      {
         sysio[0].iochn = ch;
         readcard();
         if (channel[ch].ceof)
         {
            channel[ch].ceor = 1;
            return;
         }
         if (iochk)
            return;
      }

      j = (crstate & 1)? 36 : 0;
      col = 1 << (crstate >> 1);
      if (crcol[j++] & col)
         channel[ch].cdrh |= SIGN;

      for (i = 04; i; i >>= 1)
         if (crcol[j++] & col)
            channel[ch].cdrh |= i;

      for (i = 0; i < 32; i++)
      {
         channel[ch].cdrl <<= 1;
         if (crcol[j++] & col)
            channel[ch].cdrl |= 1;
      }

      crstate++;
      if (crstate == 24)
         channel[ch].ceor = 1;
      return;
   }

   if (unit == 0361)		/* BCD printer */
   {
      if (prstate >= 46)
         initprint();
      j = (prstate & 1)? 36 : 0;

      switch (prstate)
      {
      case  0:   /*  9 left  */
      case  1:   /*  9 right */
      case  2:   /*  8 left  */
      case  3:   /*  8 right */
      case  4:   /*  7 left  */
      case  5:   /*  7 right */
      case  6:   /*  6 left  */
      case  7:   /*  6 right */
      case  8:   /*  5 left  */
      case  9:   /*  5 right */
      case 10:   /*  4 left  */
      case 11:   /*  4 right */
      case 12:   /*  3 left  */
      case 13:   /*  3 right */
      case 14:   /*  2 left  */
      case 15:   /*  2 right */
      case 16:   /*  1 left  */
      case 17:   /*  1 right */
         col = 1 << (prstate >> 1);
         goto write_row;

      case 20:   /*  0 left  */
      case 21:   /*  0 right */
         col = 01000;
         goto write_row;

      case 24:   /* 11 left  */
      case 25:   /* 11 right */
         col = 02000;
         goto write_row;

      case 28:   /* 12 left  */
      case 29:   /* 12 right */
         col = 04000;
write_row:
         channel[ch].cdrl = getmeml(channel[ch].car);
         channel[ch].cdrh = memh[channel[ch].car];
         if (channel[ch].cdrh & SIGN)
            prcol[j] |= col;
         j++;

         for (i = 04; i; i >>= 1)
	 {
            if (channel[ch].cdrh & i)
               prcol[j] |= col;
            j++;
         }
         for (m = 020000000000; m; m >>= 1)
	 {
            if (channel[ch].cdrl & m)
               prcol[j] |= col;
            j++;
         }
         break;

      case 18:   /* 84 left  echo */
      case 19:   /* 84 right echo */
         col = 00042;
         goto read_8row;

      case 22:   /* 83 left  echo */
      case 23:   /* 83 right echo */
         col = 00102;
         goto read_8row;


      case 26:   /*  9 left  echo */
      case 27:   /*  9 right echo */
         col = 00001;
         goto read_row;

      case 30:   /*  8 left  echo */
      case 31:   /*  8 right echo */
         col = 00002;
read_8row:
         if ((prcol[j++] & 00142) == col)
            channel[ch].cdrh |= SIGN;
         for (i = 04; i; i >>= 1)
            if ((prcol[j++] & 00142) == col)
               channel[ch].cdrh |= i;
         for (i = 0; i < 32; i++)
	 {
            channel[ch].cdrl <<= 1;
            if ((prcol[j++] & 00142) == col)
               channel[ch].cdrl |= 1;
         }
         break;

      case 32:   /*  7 left  echo */
      case 33:   /*  7 right echo */
      case 34:   /*  6 left  echo */
      case 35:   /*  6 right echo */
      case 36:   /*  5 left  echo */
      case 37:   /*  5 right echo */
      case 38:   /*  4 left  echo */
      case 39:   /*  4 right echo */
      case 40:   /*  3 left  echo */
      case 41:   /*  3 right echo */
      case 42:   /*  2 left  echo */
      case 43:   /*  2 right echo */
      case 44:   /*  1 left  echo */
      case 45:   /*  1 right echo */
         col = 1 << ((prstate - 28) >> 1);

read_row:
         if (prcol[j++] & col)
            channel[ch].cdrh |= SIGN;
         for (i = 04; i; i >>= 1)
            if (prcol[j++] & col)
               channel[ch].cdrh |= i;
         for (i = 0; i < 32; i++)
	 {
            channel[ch].cdrl <<= 1;
            if (prcol[j++] & col)
               channel[ch].cdrl |= 1;
         }
      }
      prstate++;
      if (prstate == 46)
      {
         channel[ch].ceor = 1;
         writeprint();
      }
      return;
   }

   /* Tape */

   tdev = (unit & 017) - 1;
   dev = tdev + 3 + 10*ch;
   ptape = &channel[ch].tapes[tdev];
   pbuf = ptape->buf;

#ifdef DEBUGCHAN1
   sprintf(s, "read  %s        state = %d, pos = %10ld", devstr(dev),
	   ptape->state, sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif

   if (ptape->state == TAPEIDLE)
   {
      sysio[dev].iochn = ch;
      ptape->reclen = readrec(dev, pbuf, MAXRECLEN);

#ifdef DEBUGCHAN
      sprintf(s, "read  %s  %05d %03o %03o %03o %03o %03o %03o %10ld",
	      devstr(dev), ptape->reclen,
	      pbuf[0], pbuf[1], pbuf[2], pbuf[3], pbuf[5],
	      sysio[dev].iopos);
      /*printerror(s);*/
      fprintf (stderr, "%s\n", s);

#ifdef DEBUGCHANDATA
      j = ptape->reclen;
      for (i = 0; i < j; )
      {
         if (channel[ch].cunit & 020)
         {
	    int k;

	    if (j > 80) j = 80;
	    for (k = 0; k < 12; k++)
	       fprintf (stderr, "%03o ", pbuf[i+k]);
	    fputs ("   ", stderr);
	    for (k = 0; k < 12; k++)
	       fputc (tonative[pbuf[i+k] & 077], stderr);
	    i += 12;
	    fputc ('\n', stderr);
         }
         else
         {
	    fputc (tonative[pbuf[i] & 077], stderr);
	    i++;
         }
      }
      fputc ('\n', stderr);
#endif
#endif

      ptape->curloc = 0;
      ptape->state = TAPEREAD;
      if (pbuf[0] == 0217)	/* end of file marker */
      {
         if (pbuf[1] == 0)
	 {
            channel[ch].ceof = 1;
            channel[ch].ceor = 1;
            channel[ch].ctm = 1;
	    ptape->state = TAPEIDLE;
            return;
         }
         if (pbuf[1] == 017)
	 {
            pbuf[0] = 0100;
            pbuf[1] = 0100;
         }
      }
   }
   for (i = 0; i < 6; i++)
   {
      channel[ch].cdrh = 0;
      if (channel[ch].cdrl &        004000000000)
         channel[ch].cdrh |= SIGN;
      channel[ch].cdrh |= (channel[ch].cdrl & 003400000000) >> 26;
      channel[ch].cdrl =  (channel[ch].cdrl & 000377777777) << 6;

      j = pbuf[ptape->curloc++] & 0177;
      if (unit & 020)
      {
         dchr = j & 077;
         j ^= oddpar[dchr];
      }
      else
      {
         dchr = /*binbcd[j & 077]*/ j & 077;
         j ^= evenpar[j & 077];
      }
      channel[ch].cdrl |= dchr & 077;
#if 0
      if (j)
      {
         channel[ch].cchk = 1;
      }
#endif
   }

   if (ptape->curloc >= ptape->reclen)
   {
      channel[ch].ceor = 1;
      ptape->state = TAPEIDLE;
   }
}

void
writeword(int ch)
{
   Tape_t *ptape;
   uint8  *pbuf;
   int32  i, j;
   uint16 unit;
   uint16 tdev;
   uint16 dev;
   uint16 col;
   uint8  dchr;

   unit = channel[ch].cunit;
   channel[ch].ceor = 0;
   channel[ch].ctm = 0;

   if (unit == 0341)		/* Card punch */
   {
      if (cpstate >= 24)
         initpunch();
      j = (cpstate & 1)? 36 : 0;
      col = 1 << (cpstate >> 1);

      if (channel[ch].cdrh & SIGN)
         cpcol[j] |= col;
      j++;

      for (i = 04; i; i >>= 1)
      {
         if (channel[ch].cdrh & i)
            cpcol[j] |= col;
         j++;
      }
      for (i = 0; i < 32; i++)
      {
         if (channel[ch].cdrl & 020000000000)
            cpcol[j] |= col;
         j++;
         channel[ch].cdrl <<= 1;
      }

      cpstate++;
      if (cpstate == 24)
      {
         channel[ch].ceor = 1;
         writepunch();
      }
      return;
   }

   if (unit == 0361)		/* BCD printer */
   {
      if (prstate >= 24)
         initprint();
      j = (prstate & 1)? 36 : 0;
      col = 1 << (prstate >> 1);

      if (channel[ch].cdrh & SIGN)
         prcol[j] |= col;
      j++;

      for (i = 04; i; i >>= 1)
      {
         if (channel[ch].cdrh & i)
            prcol[j] |= col;
         j++;
      }

      for (i = 0; i < 32; i++)
      {
         if (channel[ch].cdrl & 020000000000)
            prcol[j] |= col;
         j++;
         channel[ch].cdrl <<= 1;
      }

      prstate++;
      if (prstate == 24)
      {
         channel[ch].ceor = 1;
         writeprint();
      }
      return;
   }

   /* Tape */

   tdev = (unit & 017) - 1;
   dev = tdev + 3 + 10*ch;
   ptape = &channel[ch].tapes[tdev];
   pbuf = ptape->buf;

#ifdef DEBUGCHAN1
   sprintf(s, "write  %s        state = %d, pos = %10ld", devstr(dev),
	 ptape->state, sysio[dev].iopos);
   /*printerror(s);*/
   fprintf (stderr, "%s\n", s);
#endif
   if (ptape->state == TAPEIDLE)
   {
      ptape->curloc = 0;
      ptape->state = TAPEWRITE;
   }
   dchr = ((channel[ch].cdrh & SIGN) >> 2) |
          ((channel[ch].cdrh & HMSK) << 2) |
          ((channel[ch].cdrl >> 30) & 03);

   if (unit & 020)
      pbuf[ptape->curloc++] = oddpar[dchr & 077];
   else
      pbuf[ptape->curloc++] = /*bcdbin[dchr & 077]*/ evenpar[dchr & 077];

   for (i = 1; i < 6; i++)
   {
      dchr = ((channel[ch].cdrl >> 24) & 077);
      if (unit & 020)
         pbuf[ptape->curloc++] = oddpar[dchr & 077];
      else
         pbuf[ptape->curloc++] = /*bcdbin[dchr & 077]*/ evenpar[dchr & 077];
      channel[ch].cdrl <<= 6;
   }
}

void
startrec(int ch)
{
   uint16 unit;

   unit = channel[ch].cunit;

   if (unit == 0321)		/* Card reader */
   {
      if (crstate >= 24)
      {
         sysio[0].iochn = ch;
         readcard();
         if (channel[ch].ceof)
         {
            channel[ch].ceor = 1;
            return;
         }
      }

   }

   else if (unit == 0341)	/* Card punch */
   {
      if (cpstate == 0)
         cpstate = 25;

   }

   else if (unit == 0361)	/* BCD printer */
   {
      if (prstate == 0)
         prstate = 47;
   }
}

void
endrecord(int ch, int skiptoend)
{
   Tape_t *ptape;
   uint8  *pbuf;
   uint16 unit;
   uint16 dev;
   uint16 tdev;

   channel[ch].cdrh = 0;
   channel[ch].cdrl = 0;
   unit = channel[ch].cunit;

   if (unit == 0321)		/* Card reader */
   {
      while (crstate < 24)
         readword(ch);
   }
   
   else if (unit == 0341)	/* Card punch */
   {
      if (cpstate != 0)
      {
         if (cpstate == 25)
            cpstate = 0;
         do {
            writeword(ch);
         } while (cpstate != 0);
      }
   }

   else if (unit == 0361)	/* BCD printer */
   {
      if (channel[ch].csel == READ_SEL)
      {
         prstate = pr_ws_rs[prstate];
      }
      if (prstate != 0)
      {
         if (prstate == 47)
            prstate = 0;
         do {
            writeword(ch);
         } while (prstate != 0);
      }
   }

   else				/* Tape */
   {

      tdev = (unit & 017) - 1;
      dev = tdev + 3 + 10*ch;
      ptape = &channel[ch].tapes[tdev];
      pbuf = ptape->buf;

      if (ptape->state == TAPEWRITE)
      {

#ifdef DEBUGCHAN
         sprintf(s, "write %s  %05d %03o %03o %03o %03o %03o %03o %10ld",
		 devstr(dev), ptape->curloc,
		 pbuf[0], pbuf[1], pbuf[2], pbuf[3], pbuf[4], pbuf[5],
		 sysio[dev].iopos);
         /*printerror(s);*/
         fprintf (stderr, "%s\n", s);

#ifdef DEBUGCHANDATA
	 {
	    int32  i, j, k;
	    j = ptape->curloc;
	    for (i = 0; i < j; )
	    {
	       if (unit & 020)
	       {
		  if (j > 80) j = 80;
		  for (k = 0; k < 12; k++)
		     fprintf (stderr, "%03o ", pbuf[i+k]);
		  fputs ("   ", stderr);
		  for (k = 0; k < 12; k++)
		     fputc (tonative[pbuf[i+k] & 077], stderr);
		  i += 12;
		  fputc ('\n', stderr);
	       }
	       else
	       {
		  fputc (tonative[pbuf[i] & 077], stderr);
		  i++;
	       }
	    }
	    fputc ('\n', stderr);
	 }
#endif
#endif

         writerec(dev, pbuf, ptape->curloc);
         ptape->curloc = 0;
      }
      else if (skiptoend)
      {
#ifdef DEBUGCHAN
	 fprintf (stderr, "endrecord: skiptoend \n");
#endif
         while (!channel[ch].ceor)
         {
            readword(ch);
            if (channel[ch].ceof) break;
         }
      }
      ptape->state = TAPEIDLE;
   }
   channel[ch].ceor = 0;
}
