/***********************************************************************
*
* negop.c - IBM 7090 emulator negative opcode routines.
*
* Changes:
*   ??/??/??   PRP   Original.
*   10/20/03   DGP   Added 7094 instructions.
*   01/28/05   DGP   Revamped channel and tape controls.
*   
***********************************************************************/

#define EXTERN extern

#include <stdio.h>

#include "sysdef.h"
#include "regs.h"
#include "s709.h"
#include "arith.h"
#include "io.h"
#include "chan.h"
#include "negop.h"

extern char errview[5][81];

void
negop()
{
   int16 chan;
   int16 dev;

   switch(op)
   {

   case 0100000:   /* HTR */
      stop = 1;
      cycle_count++;
      ic--;
      break;

   case 0100020:   /* TRA */
      if (trap)
      {
         traptrace();
         ic = 00001;
      }
      else
      {
         ic = y;
      }
      break;

   case 0100021:   /* ESNT */
      if (cpumode != 7094) goto ILLINST;
      break;

   case 0100022:   /* TRCB */
      chan = 1;
      goto trcx;
   case 0100024:   /* TRCD */
      chan = 3;
      goto trcx;
   case 0100026:   /* TRCF */
      chan = 5;
      goto trcx;
   case 0100027:   /* TRCH */
      chan = 7;
   trcx:
#ifdef DEBUGIO1
      fprintf (stderr,
	       "TRC Channel %c, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + chan, ctrap_enb, trap, channel[chan].ceof);
#endif
      if (ctrap_enb & (01000000 << chan))
         break;
      if (trap)
         traptrace();
      if (channel[chan].cchk)
      {
         channel[chan].cchk = 0;
         if (trap)
            ic = 00001;
         else
            ic = y;
      }
      break;

   case 0100030:   /* TEFB */
   case 0100031:   /* TEFD */
   case 0100032:   /* TEFF */
   case 0100033:   /* TEFH */
      chan = ((op & 03) << 1) + 1;
#ifdef DEBUGIO
      fprintf (stderr,
	    "TEF Channel %c, ic = %05o, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + chan, ic, ctrap_enb, trap, channel[chan].ceof);
#endif
      if (ctrap_enb & (1 << chan))
         break;
      if (trap)
         traptrace();
      if (channel[chan].ceof)
      {
         channel[chan].ceof = 0;
         if (trap)
            ic = 00001;
         else
            ic = y;
      }
      break;

   case 0100042:   /* RIA */
      sih &= ~ach;
      sil &= ~acl;
      break;

   case 0100046:   /* PIA */
      ach = sih;
      acl = sil;
      break;

   case 0100051:   /* IIL */
      sih ^= (srl & 0740000) >> 14;
      sil ^= (srl & 0037777) << 18;
      break;

   case 0100054:   /* LFT */
      adderh = (srl & 0740000) >> 14;
      adderl = (srl & 0037777) << 18;
      if ( (adderh & ~sih) == adderh &&
           (adderl & ~sil) == adderl )
         ic++;
      DCYCLE();
      DCYCLE();
      break;

   case 0100055:   /* SIL */
      sih |= (srl & 0740000) >> 14;
      sil |= (srl & 0037777) << 18;
      break;

   case 0100056:   /* LNT */
      adderh = (srl & 0740000) >> 14;
      adderl = (srl & 0037777) << 18;
      if ( (adderh & sih) == adderh &&
           (adderl & sil) == adderl )
         ic++;
      DCYCLE();
      DCYCLE();
      break;

   case 0100057:   /* RIL */
      sih &= ~((srl & 0740000) >> 14);
      sil &= ~((srl & 0037777) << 18);
      break;

   case 0100060:   /* TCNA */
   case 0100061:   /* TCNB */
   case 0100062:   /* TCNC */
   case 0100063:   /* TCND */
   case 0100064:   /* TCNE */
   case 0100065:   /* TCNF */
   case 0100066:   /* TCNG */
   case 0100067:   /* TCNH */
      if (trap)
         traptrace();
      chan = op & 07;
      if (!channel[chan].csel)
      {
         if (trap)
            ic = 00001;
         else
            ic = y;
      }
      break;

   case 0100100:   /* TNZ */
      if (trap)
         traptrace();
      if ((ach & (Q|P|HMSK)) != 0 || acl != 0)
      {
         if (trap)
            ic = 00001;
         else
            ic = y;
      }
      break;

   case 0100114:   /* CAQ */
   case 0100115:
   case 0100116:
   case 0100117:
      caq();
      break;

   case 0100120:   /* TMI */
      if (trap)
         traptrace();
      if ((ach & SIGN) != 0)
      {
         if (trap)
            ic = 00001;
         else
            ic = y;
      }
      break;

   case 0100130:   /* XCL */
      srh = ((ach << 4) & SIGN) | (ach & HMSK);
      srl = acl;
      ach = ((mqh & SIGN) >> 4) | (mqh & HMSK);
      acl = mql;
      mqh = srh;
      mql = srl;
      break;

   case 0100140:   /* TNO */
      if (trap)
         traptrace();
      if (acoflo == 0)
      {
         if (trap)
            ic = 00001;
         else
            ic = y;
      }
      else
      {
         acoflo = 0;
      }
      break;

   case 0100154:   /* CRQ */
   case 0100155:
   case 0100156:
   case 0100157:
      crq();
      break;

   case 0100200:   /* MPR */
      ACCESS(y);
      shcnt = 043;
      mpy();
      rnd();
      break;

   case 0100240:   /* DFDH */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      dfdiv();
      if (divchk)
         run = 0;
      break;

   case 0100241:   /* DFDP */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      dfdiv();
      break;

   case 0100260:   /* UFM */
      ACCESS(y);
      fmpy(0);
      break;

   case 0100261:   /* DUFM */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      dfmpy(0);
      break;

   case 0100300:   /* UFA */
      ACCESS(y);
      fadd(0);
      break;

   case 0100301:   /* DUFA */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      dfadd(0);
      break;

   case 0100302:   /* UFS */
      ACCESS(y);
      srh ^= SIGN;
      fadd(0);
      break;

   case 0100303:   /* DUFS */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      srh ^= SIGN;
      dfadd(0);
      break;

   case 0100304:   /* UAM */
      ACCESS(y);
      srh &= ~SIGN;
      fadd(0);
      break;

   case 0100305:   /* DUAM */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      srh &= ~SIGN;
      dfadd(0);
      break;

   case 0100306:   /* USM */
      ACCESS(y);
      srh |= SIGN;
      fadd(0);
      break;

   case 0100307:   /* DUSM */
      if (cpumode != 7094) goto ILLINST;
      ACCESS(y);
      srh |= SIGN;
      dfadd(0);
      break;

   case 0100320:   /* ANA */
      ACCESS(y);
      if (srh & SIGN)
         srh -= SIGN - P;
      ach = ach & srh;
      acl = acl & srl;
      if (srh & P)
         srh += SIGN - P;
      CYCLE();
      break;

   case 0100340:   /* LAS */
      ACCESS(y);
      if (srh & SIGN)
         srh -= SIGN - P;
      if ( (ach & (Q|P|HMSK)) > (srh & 017) )
         goto skip0;
      if ( (ach & (Q|P|HMSK)) < (srh & 017) )
         goto skip2;
      if ( acl > srl )
         goto skip0;
      if ( acl < srl )
         goto skip2;
      goto skip1;
skip2: ic++;
skip1: ic++;
skip0: if (srh & P)
         srh += SIGN - P;
      break;

   case 0100400:   /* SBM */
      ACCESS(y);
      srh |= SIGN;
      add();
      CYCLE();
      break;

   case 0100500:   /* CAL */
      ACCESS(y);
      ach = ((srh & SIGN) >> 4) | (srh & HMSK);
      acl = srl;
      break;

   case 0100501:   /* ORA */
      ACCESS(y);
      if (srh & SIGN)
         srh -= SIGN - P;
      ach = ach | srh;
      acl = acl | srl;
      if (srh & P)
         srh += SIGN - P;
      CYCLE();
      break;

   case 0100520:   /* NZT */
      ACCESS(y);
      if ((srh & HMSK) != 0 || srl != 0)
         ic++;
      break;

   case 0100534:   /* LXD */
      ACCESS(addr);
      setxr(((srh & 1) << 14) |
           ((srl & 037777000000) >> 18));
      break;

   case 0100535:   /* LDC */
      ACCESS(addr);
      setxr(0100000 - (((srh & 1) << 14) |
                       ((srl & 037777000000) >> 18)));
      break;

   case 0100540:   /* RCHB */
   case 0100541:   /* RCHD */
   case 0100542:   /* RCHF */
   case 0100543:   /* RCHH */
      chan = ((op & 03) << 1) + 1;
#ifdef DEBUGIO
      fprintf (stderr,
	    "RCH Channel %c, ic = %05o, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + chan, ic, ctrap_enb, trap, channel[chan].ceof);
#endif
      CYCLE();
      channel[chan].ctrp = 0;
      channel[chan].cchk = 0;
      if (channel[chan].csel == READ_SEL || channel[chan].csel == WRITE_SEL)
      {
         channel[chan].clr = y;
         channel[chan].ceor = 0;
         load_chan(chan, 0);
      }
      else
      {
         iochk = 1;
#ifdef DEBUGIO
         fprintf (stderr, "RCH iochk\n");
#endif
      }
      break;

   case 0100544:   /* LCHB */
   case 0100545:   /* LCHD */
   case 0100546:   /* LCHF */
   case 0100547:   /* LCHH */
      chan = ((op & 03) << 1) + 1;
#ifdef DEBUGIO
      fprintf (stderr,
	    "LCH Channel %c, ic = %05o, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + chan, ic, ctrap_enb, trap, channel[chan].ceof);
#endif
      CYCLE();
      if ((channel[chan].csel == READ_SEL || channel[chan].csel == WRITE_SEL)
	    && channel[chan].cact)
      {
         run_chan(chan);
         if (channel[chan].csel == READ_SEL || channel[chan].csel == WRITE_SEL)
	 {
            channel[chan].clr = y;
            load_chan(chan, 0);
         }
	 else
	 {
            iochk = 1;
#ifdef DEBUGIO
	    fprintf (stderr, "LCH iochk\n");
#endif
         }
      }
      else
      {
         iochk = 1;
#ifdef DEBUGIO
         fprintf (stderr, "LCH iochk\n");
#endif
      }
      break;

   case 0100600:   /* STQ */
      memh[y] = mqh;
      setmeml(y, mql);
      CYCLE();
      break;

   case 0100602:   /* ORS */
      ACCESS(y);
      if (srh & SIGN)
         srh -= SIGN - P;
      srh = (ach & (P|HMSK)) | srh;
      srl = acl | srl;
      if (srh & P)
         srh += SIGN - P;
      memh[y] = srh;
      setmeml(y, srl);
      CYCLE();
      break;

   case 0100603:   /* DST */
      if (cpumode != 7094) goto ILLINST;
      memh[y] = ach & (SIGN|HMSK);
      setmeml(y, acl);
      memh[y+1] = mqh;
      setmeml(y+1, mql);
      CYCLE();
      break;

   case 0100620:   /* SLQ */
      memh[y] = mqh;
      setmeml(y, (getmeml(y) & 000000777777) |
            (mql &     037777000000));
      CYCLE();
      break;

   case 0100625:   /* STL */
      setmeml(y, (getmeml(y) & 037777700000) | ic);
      CYCLE();
      break;

   case 0100634:   /* SXD */
      memh[addr] = (memh[addr] & 0376) | (getxr() >> 14);
      setmeml(addr, (getmeml(addr) &      000000777777) |
            (((uint32)getxr() << 18) & 037777000000));
      CYCLE();
      break;

   case 0100636:   /* SCD */
      if (cpumode != 7094) goto ILLINST;
      {
	 uint16 x = 0100000 - getxr();
	 if (tag == 0) x = 0;
	 memh[addr] = (memh[addr] & 0376) | (x >> 14);
	 setmeml(addr, (getmeml(addr) &      000000777777) |
	       (((uint32 )x << 18) & 037777000000));
	 CYCLE();
      }
      break;

   case 0100640:   /* SCHB */
   case 0100641:   /* SCHD */
   case 0100642:   /* SCHF */
   case 0100643:   /* SCHH */
      chan = ((op & 03) << 1) + 1;
#ifdef DEBUGIO
      fprintf (stderr,
	    "SCH Channel %c, ic = %05o, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + chan, ic, ctrap_enb, trap, channel[chan].ceof);
#endif
      CYCLE();
      srh = ((channel[chan].cop << 5) & SIGN) |
            ((channel[chan].cop << 1) & 06) |
            ((channel[chan].clr >> 14) & 1);
      srl = ((channel[chan].cop << 13) & 000000200000) |
            (((uint32)channel[chan].clr << 18) & 037777000000) |
             (channel[chan].car & 000000077777);
      setmeml(y, srl);
      memh[y] = srh;
      break;

   case 0100734:   /* PDX */
      setxr(((ach & 1) << 14) |
           ((acl & 037777000000) >> 18));
      break;

   case 0100737:   /* PDC */
      setxr(0100000 - (((ach & 1) << 14) |
                       ((acl & 037777000000) >> 18)));
      break;

   case 0100754:   /* PXD */
      if (tag == 0)
      {
         ach = 0;
         acl = 0;
      }
      else
      {
         ach = (getxr() >> 14) & 1;
         acl = ((uint32)getxr() << 18) & 037777000000;
      }
      break;

   case 0100756:   /* PCD */
      if (cpumode != 7094) goto ILLINST;
      if (tag == 0)
      {
         ach = 0;
         acl = 0;
      }
      else
      {
         uint16 x = 0100000 - getxr();
         ach = x >> 14 ;
         acl = (x << 18) & 037777000000;
      }
      break;

   case 0100763:   /* LGL */
      for (y &= 0377; y; y--)
      {
         ach = (ach & SIGN) | ((ach << 1) & (Q|P|HMSK));
         if (ach & P)
            acoflo = 1;
         if (acl & 020000000000)
            ach++;
         acl <<= 1;
         if (mqh & SIGN)
            acl++;
         mqh = mqh << 1;
         if (mqh & P)
            mqh += (SIGN - P);
         if (mql & 020000000000)
            mqh++;
         mql <<= 1;
      }
      break;

   case 0100764:   /* BSF */
      chan = (addr & 017000) >> 9;
#ifdef DEBUGIO
      fprintf (stderr,
	    "BSF Channel %c, ic = %05o, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + (chan-1), ic, ctrap_enb, trap, channel[chan].ceof);
#endif
      if (chan && chan <= NUMCHAN)
      {
         chan--;
         if (channel[chan].cact)
            do_chan(chan);
         channel[chan].cunit = (addr - getxr()) & 0777;
         dev = unitcheck(chan, -1);
         if (dev != -1)
	 {
            channel[chan].csel = BSF_SEL;
            channel[chan].cact = 1;
            chan_in_op |= 1 << chan;
         }
      }
      else
      {
         machchk = 1;
         sprintf (errview[0], "BSF MACHINE check: chan = %o\n",
		  chan);
         run = 0;
      }
      CYCLE();
      break;

   case 0100765:   /* LGR */
      for (y &= 0377; y; y--)
      {
         mql >>= 1;
         if (mqh & 1)
            mql |= 020000000000;
         if (mqh & SIGN)
            mqh -= (SIGN - P);
         mqh >>= 1;
         if (acl & 1)
            mqh |= SIGN;
         acl >>= 1;
         if (ach & 1)
            acl |= 020000000000;
         ach = (ach & SIGN) | ((ach >> 1) & (P|HMSK));
      }
      break;

   case 0100772:   /* RUN */
      chan = (addr & 017000) >> 9;
#ifdef DEBUGIO
      fprintf (stderr,
	    "RUN Channel %c, ic = %05o, ctrap_enb = %o, trap = %d, eof = %d\n",
	       'A' + (chan-1), ic, ctrap_enb, trap, channel[chan].ceof);
#endif
      if (chan && chan <= NUMCHAN)
      {
         chan--;
         if (channel[chan].cact)
            do_chan(chan);
         channel[chan].cunit = (addr - getxr()) & 0777;
         dev = unitcheck(chan, -1);
         if (dev != -1)
	 {
            unload(dev);
         }
      }
      else
      {
         machchk = 1;
         sprintf (errview[0], "RUN MACHINE check: chan = %o\n",
		  chan);
         run = 0;
      }
      CYCLE();
      break;

   case 0100773:   /* RQL */
      for (y &= 0377; y; y--)
      {
         adderh = mqh;
         mqh <<= 1;
         if (mqh & P)
            mqh += (SIGN - P);
         if (mql & 020000000000)
            mqh++;
         mql <<= 1;
         if (adderh & SIGN)
            mql++;
      }
      break;

   case 0100774:   /* AXC */
      setxr(0100000 - (srl & 077777));
      break;

   default:
ILLINST:
      machchk = 1;
      sprintf (errview[0], "Illegal instruction: op = %o\n", op);
      run = 0;
      break;
   }
}

void
crq()
{
   uint32 i;

   srl = (srl & 037777700000) | addr;
   shcnt = ((srl & 000377000000) >> 18);
   while (shcnt)
   {
      i = (mqh & SIGN) >> 2 | (mqh & HMSK) << 2 | mql >> 30;
      i = (srl + i) & 077777;
      ACCESS(i);
      for (i = 0; i < 6; i++)
      {
         mqh = (mqh << 1) & (P|HMSK);
         if (mqh & P)
            mqh += SIGN - P;
         if (mql & 020000000000)
            mqh++;
         mql <<= 1;
      }
      mql |= (srh & SIGN) >> 2 | (srh & HMSK) << 2 | srl >> 30;
      shcnt--;
   }
   tag &= 1;
   if (tag)
      setxr(srl);
}

void
caq()
{
   uint32 i;
   int32 t;

   srl = (srl & 037777700000) | addr;
   shcnt = ((srl & 000377000000) >> 18);
   while (shcnt)
   {
      i = (mqh & SIGN) >> 2 | (mqh & HMSK) << 2 | mql >> 30;
      i = (srl + i) & 077777;
      ACCESS(i);
      for (i = 0; i < 6; i++)
      {
         t = mqh & SIGN;
         mqh = (mqh << 1) & (P|HMSK);
         if (mqh & P)
            mqh += SIGN - P;
         if (mql & 020000000000)
            mqh++;
         mql <<= 1;
         mql |= t >> 7;
      }
      if (srh & SIGN)
         srh -= SIGN - P;
      adderl = srl + acl;
      adderh = (srh & (P|HMSK)) + (ach & (Q|P|HMSK));
      if (adderl < srl)
         adderh++;
      if (srh & P)
         srh += SIGN - P;
      acl = adderl;
      ach = (adderh & (Q|P|HMSK)) | (ach & SIGN);
      shcnt--;
   }
   tag &= 1;
   if (tag)
      setxr(srl);
}
