#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "atlas.h"
#include "io.h"

// console interpreter
int main() {
  char *myline;		 // console input line from "readline" library
  char command[80];
  int count;

  printf("Atlas 1103A emulator version %s\n",VERSION);
  io_open();			// start up I/O devices
  printf("type \"help\" for command list\n");
  pak = PAK_START;
  nextop();
  signal(SIGINT, catch_int);	// trap Ctrl-C 

  // console interpreter loop, exit with Ctrl-D
  while (1) { 

    myline = rl_gets("1103A> ");
    if (myline == NULL) {	// if user indicates end of input
      io_close(0);
    }
    count = sscanf(myline,"%s",command);

    if (count > 0) {
      if (isalpha(command[0])) {
	if (strcmp("start",command) == 0) {
	  printf("starting... send SIGINT (Ctrl-C) to stop\n");
	  run_flag = 1;
	  signal(SIGINT, halt); // catch Ctrl-C to halt execution
	  signal(SIGUSR1, prog_int); // catch USR1 for program interrupt
	  run();
	  signal(SIGUSR1, SIG_DFL);
	  signal(SIGINT, catch_int);
	} else if (strcmp("step",command) == 0) {
	  cycle();
	  pstate();
	} else if (strcmp("state",command) == 0) {
	  pstate();
	} else if (strcmp("f1",command) == 0) { // toggle f1
	  if (f1 == 00) {
	    f1 = 040001;
	  } else {
	    f1 = 00;
	  }
	} else if (strcmp("mj",command) == 0) { // toggle manual jump
	  unsigned int i;
	  sscanf(myline,"%s %d",command,&i);
	  if ((i >=1) && (i <= 3)) {
	    if (mj[i]) {
	      mj[i] = 0;
	    } else {
	      mj[i] = 1;
	    }
	    pstate();
	  } else {
	    printf("\aunknown manual jump number %d\n",i);
	  }
	} else if (strcmp("ms",command) == 0) { // toggle manual stop
	  unsigned int i;
	  sscanf(myline,"%s %d",command,&i);
	  if ((i >=1) && (i <= 3)) {
	    if (ms[i]) {
	      ms[i] = 0;
	    } else {
	      ms[i] = 1;
	    }
	    pstate();
	  } else {
	    printf("\aunknown manual stop number %d\n",i);
	  }
	} else if (strcmp("pak",command) == 0) {
	  sscanf(myline,"%s %o",command,&pak);
	  vhigh_flag = 0;
	  a71borrow_flag = 0;
	  repeat_flag = 0;
	  prog_int_flag = 0;
	  pak_flag = 0;
	  nextop();
	  pstate();
	} else if (strcmp("al",command) == 0) {
	  sscanf(myline,"%s %llo",command,&al);
	  pstate();
	} else if (strcmp("ar",command) == 0) {
	  sscanf(myline,"%s %llo",command,&ar);
	  pstate();
	} else if (strcmp("q",command) == 0) {
	  sscanf(myline,"%s %llo",command,&q);
	  pstate();
	} else if (strcmp("help",command) == 0) {
	    help();
	} else if (strcmp("bp",command) == 0) {
	  int addr;
	  sscanf(myline,"%s %o",command,&addr);
	  bp[addr] = !bp[addr];
	} else if (strcmp("exit",command) == 0) {
	  io_close(0);
	} else if (strcmp("rew",command) == 0) {
	  fseek(ptr,0,SEEK_SET);
	} else if (strcmp("show",command) == 0) {
	  unsigned int addr;
	  sscanf(myline,"%s %o",command,&addr);
	  diss(addr);
	} else {
	  printf("\aunknown command: %s\n",command);
	}
      } else if (isdigit(command[0])) {	// poke a value into memory
	unsigned int addr;
	unsigned long long val;
	sscanf(myline,"%o %llo",&addr,&val);
	writeaddr(addr,val,WORD_MASK);
	diss(addr);		// disassemble the area written to
      } else {
	printf("\aunknown command: %s\n",command);
      }
    }
  }
}

void help(void) {
  printf("\n");
  printf("state -- show machine state\n");
  printf("start -- start machine\n");
  printf("step  -- step machine one instruction\n");
  printf("pak <addr> -- set program counter to octal address <addr>\n");
  printf("al  <val>  -- set accumulator left half to octal value <val>\n");
  printf("ar  <val>  -- set accumulator right half to octal value <val>\n");
  printf("q   <val>  -- set q register to octal value <val>\n");
  printf("mj  <val>  -- toggle manual jump switch, <val> = 1|2|3\n");
  printf("ms  <val>  -- toggle manual stop switch, <val> = 1|2|3\n");
  printf("<addr> <val> -- set octal address <addr> to octal value <val>\n");
  printf("show <addr>  -- disassemble memory at octal address <addr>\n");
  printf("bp   <addr>  -- toggle breakpoint at octal address <addr>\n");
  printf("rew  -- rewind the paper tape reader\n");
  printf("f1   -- toggle F1 switch between 00000 (default) and 40001\n");
  printf("help -- print help information\n");
  printf("exit -- leave program (also Ctrl-D)\n");
}
  
// catch_int -- catch SIGINT
void catch_int(int sig_num) {
  printf("type Ctrl-D to exit\n");
}

// prog_int -- Program Interrupt
void prog_int(int sig_num) {
  prog_int_flag = 1;
}

// run the machine
void run(void) {
  while (run_flag) {
    cycle();
  }
}

// print machine state
void pstate(void) {
  unsigned int pak_tmp;

  // registers
  printf("%17s   q: %012llo\n","",q);
  //  printf("atl: %012llo atr: %012llo\n",atl,atr);
  printf(" al: %012llo  ar: %012llo\n",al,ar);
  printf("%17s   x: %012llo\n","",x);
  printf("%5s mcr: %02o uak: %05o vak: %05o\n","",mcr,uak,vak);
  printf("%13s pak: %05o sar: %05o\n","",pak,sar);
  printf("%3s f1: %05o  ms: %1d %1d %1d  mj: %1d %1d %1d\n",
	 "",f1,ms[1],ms[2],ms[3],mj[1],mj[2],mj[3]);
  //printf("%8s ioa: %03o iob: %012llo\n","",ioa,iob);

  // disassemble address space
  if (pak_flag) {	       // special program counter for debugger
    pak_tmp = pak_repeat;
  } else {		      // otherwise use machine program counter
    pak_tmp = pak;
  }
  dec_pak(&pak_tmp);
  diss(pak_tmp);
}

// disassemble and print a range of memory
void diss(unsigned int addr) {
  int i;
  for (i = 0; i < 3; i++) {	// print 3 addresses before addr
    dec_pak(&addr);
  }
  printf("\naddr    value           op\n");
  for (i = 0; i < 8; i++) {	// print 8 addresses total
    paddr(addr);
    printf("\n");
    inc_pak(&addr);
  }
}
  

// print disassembly of an address
// flag current instruction with "->"
void paddr(unsigned int addr) {

  unsigned long long word;
  unsigned int opcode, u, v;
  unsigned int pak_test;

  word = readaddr(addr);
  opcode = (unsigned int) ((word & OP_MASK) >> 30);
  u = (unsigned int) ((word & U_MASK) >> 15);
  v = (unsigned int) (word & V_MASK);
  printf("%05o",addr);
  if (pak_flag) {		// use modified program counter
    pak_test = pak_repeat;
  } else {			// standard program counter
    pak_test = pak;
    dec_pak(&pak_test);
  }
  if (pak_test == addr) {	// print an arrow pointing to
    printf(" ->");		// current instruction
  } else {
    printf("   ");
  }
  printf("%02o %05o %05o  %-6s",opcode,u,v,opc[opcode]);
} 

// standard machine cycle
void cycle(void) {
  decode();		      // stage 1 -- decode current instruction
  if (repeat_flag) {	       // RPjnw (075) instruction is in effect
    repeat_cycle();
  } else {			// otherwise continue as normal
    nextop();			// stage 2 -- fetch next operation
  }
}

// repeated machine sub-cycle for executing RPjnw (075) instr
void repeat_cycle(void) {
  unsigned int n_rpt;	   // local value used by repeated instruction
  // pick up from repeat_flag = 2; in decode(075);
  inc_pak(&pak);
  n_rpt = pak & N_MASK;
  if ((repeat_flag > 1) && (n_rpt > 0)) { // skip u,v increment our
    repeat_flag = 1;		// first time through
  } else {
    if (n_rpt) { // test for completion of repeat, n_rpt = 0 if we are done
      switch (j_rpt) {		// address modification switch 
      case 1:
	vak = (vak + 1) & 077777;
	break;
      case 2:
	uak = (uak + 1) & 077777;
	break;
      case 3:
	vak = (vak + 1) & 077777;
	uak = (uak + 1) & 077777;
	break;
      default:
	;			// no address modification
      }
    } else {			// normal termination sequence
      pak_repeat = f1;		// set debugger program counter
      sar = f1;			// now get next instruction
      x = readaddr(sar);
      pcr = x;			// and load it into PCR
      split_pcr();
      repeat_flag = 0;		// terminate RPjnw
    }
  }
}


// 2nd stage of machine cycle
void nextop(void) {
  pak_flag = 0; // clear alternate program counter

  // this ugly conditional provides for address modification for
  // the "split" instructions 031 thru 034
  if (vhigh_flag) {		// if PAK address modification needed
    sar = pak | (vak & VHIGH_MASK);
    vhigh_flag = 0;		// reset flag
  } else {
    sar = pak;			// standard address source
  }

  if (prog_int_flag) {	     // take next instruction from f3 if intr.
    sar = f3;
    x = readaddr(sar);
    prog_int_flag = 0;
  } else {
    x = readaddr(sar);		// get instruction
    inc_pak(&pak);		// increment program address counter
  }
  pcr = x;
  split_pcr();			// load instruction

  if (bp[sar]) {		// is emulator breakpoint set here?
    printf("breakpoint at addr: %05o\n",sar);
    run_flag = 0;		// halt machine if breakpoint
  }
}


//   inc_pak -- increment program counter
//   if pak on drum (>= 40000), stay on drum
//   if in a core bank, stay in that core bank
void inc_pak(unsigned int *pak) {
  if (*pak & 040000) {		// drum address
    *pak = (*pak & 040000) | ((*pak + 1) & 037777);
  } else {
    *pak = (*pak & 070000) | ((*pak + 1) & 07777);
  }
}

// decrement program counter
void dec_pak(unsigned int *pak) {
  if (*pak & 040000) {		// drum address
    *pak = (*pak & 040000) | ((*pak - 1) & 037777);
  } else {
    *pak = (*pak & 070000) | ((*pak - 1) & 07777);
  }
} 

// separate various parts of PCR
void split_pcr(void) {

  // standard instruction parts
  mcr = (pcr & OP_MASK) >> 30;	// opcode
  uak = (pcr & U_MASK) >> 15;	// address of U
  vak = (pcr & V_MASK);		// address of V

  // various parts of some instructions
  j = (uak & 070000) >> 12;	// j is bits u_14 .. u_12
  n = uak & 007777;		// n is bits u_11 ... u_0
  k = vak & 000177;             // k is usually v_6 .. v_0 
				// except in LTjkv (opcode 022)
}

// instruction decoding
void decode(void) {

  switch(mcr) {

  case 011:			// Transmit Positive, TPuv
  case 012:			// Transmit Absolute Magnitude, TMuv
  case 013:			// Transmit Negative, TNuv
    sar = uak;
    x = readaddr(sar);
    if (mcr == 012) {		// if transmit magnitude
      if (x & BIT35) {		// complement if negative
	x = x ^ WORD_MASK;
      }
    }
    if (mcr == 013) {		// if transmit negative
	x = x ^ WORD_MASK;
    }
    sar = vak;
    if (is_a(sar)) {		// if desination is accumulator
      cleara();
      dx();
      add();
    } else {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 014:			// Interpret, IP--
    x = 00ULL;
    x = (unsigned long long) pak;
    sar = f1;
    writeaddr(sar,x,V_MASK);
    pak = f2;			// jump to location f2
    break;

  case 015:			// Transmit U Address, TUuv
  case 016:			// Transmit V Address, TVuv
    sar = uak;
    x = readaddr(sar);
    sar = vak;
    if (is_q(sar) || is_a(sar)) { // check for illegal Q/A assignment
      printf("\aTUuv/TVuv: invalid address: %05o\n",sar);
      run_flag = 0;
    } else {
      if (mcr == 015) {
	writeaddr(sar,x,U_MASK);
      } else {
	writeaddr(sar,x,V_MASK);
      }
    }
    break;

  case 017:			// External Function, EF-v
    sar = vak;
    x = readaddr(sar);
    iob = x;
    switch (iob) {
    case 0100000200000ULL:	// start paper tape reader
      //printf("starting paper tape reader...\n");
      a_dev = ptr;
      break;
    case 0100000100000ULL:	// stop paper tape reader
      //printf("stopping paper tape reader...\n");
      //a_dev = NULL;
      break;
    case 0100000300000ULL:	// step paper tape reader
      //printf("stepping paper tape reader...\n");
      a_dev = ptr;
      break;
    default:
      printf("\aEF-v: unknown operation: %012llo\n",iob);
    }
    break;

  case 021:			// Replace Add, RAuv
    sar = uak;
    x = readaddr(sar);
    cleara();
    dx();
    add();
    sar = vak;
    x = readaddr(sar);
    dx();
    add();
    x = ar;
    sar = uak;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 022:			// Left Transmit, LTjkv
    k = uak & 000177;		// exception to k notation
    shifta(k);
    if (j == 0) {
      x = al;
    } else if (j == 1) {
      x = ar;
    } else {
      printf("\aLTjkv: unknown j value %d\n",j);
    }
    sar = vak;
    if (is_a(sar)) {
      cleara();
      dx();
      add();
    } else {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 023:			// Replace Subtract, RSuv
    sar = uak;
    x = readaddr(sar);
    cleara();
    dx();
    add();
    sar = vak;
    x = readaddr(sar);
    dx();
    sub();
    x = ar;
    sar = uak;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 027:			// Controlled Complement, CCuv
    sar = uak;
    x = readaddr(sar);
    ar = 00ULL;
    x = x ^ WORD_MASK;
    ar = ar ^ (x ^ WORD_MASK);
    sar = vak;
    x = readaddr(sar);
    x = x ^ WORD_MASK;
    ar = ar ^ (x ^ WORD_MASK);
    x = ar;
    sar = uak;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 031:			// Split Positive Entry, SPuk
  case 032:			// Split Add, SAuk
  case 033:			// Split Negative Entry, SNuk
  case 034:			// Split Subtract, SSuk
    sar = uak;
    x = readaddr(uak);
    if ((mcr == 031) || (mcr == 033)) {  // if we have SPuk or SNuk
      cleara();			// clear accumulator
    }
    sx();			// zero-extend X
    if ((mcr == 031) || (mcr == 032)) { // if we hve SAuk or SSuk
      add();
    } else {
      sub();
    }
    shifta(k);			// perform shifting of accumulator
    if (vak & VHIGH_MASK) {
      vhigh_flag = 1;	      // flag need to modify next instr. addr.
    }
    break;

  case 035:			// Add and Transmit, ATuv
  case 036:			// Subtract and Transmit, STuv
    sar = uak;
    x = readaddr(sar);

    dx();			// sign extend

    if (mcr == 035) {		// Add
      add();
    } else {			// Subtract
      sub();
    }

    x = ar;
    sar = vak;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 037:			// Return Jump, RJuv
    if (is_q(uak) || is_a(uak)) {
      printf("\aRJuv: invalid u address: %05o\n",uak);
      run_flag = 0;
    } else {
      x = 00ULL;
      x = (unsigned long long) pak;
      pak = vak;
      sar = uak;
      writeaddr(sar,x,V_MASK);
    }
    break;

  case 041:			// Index Jump, IJuv
    x = 00ULL;
    sar = uak;
    if (!is_a(sar)) {
      x = readaddr(sar);
      cleara();
    }
    dx();
    add();
    x = 01ULL;
    dx();
    sub();
    x = ar;
    if (!(al & BIT35)) {	// if accumulator bit 71 is 0
      pak = vak;		// set program counter to v
      sar = uak;
      if (!is_a(sar)) {		// store if u is not accumulator
	writeaddr(sar,x,WORD_MASK);
      }
    }
    break;

  case 042:			// Threshhold Jump, TJuv
    sar = uak;
    x = readaddr(sar);
    dx();
    sub();
    if (al & BIT35) {		// if accumulator bit 71 is 1
      if (repeat_flag) {
	pak = pak % 077777;	// complement PAK
	q = pak;		// save PAK in Q
	repeat_flag = 0;	// terminate repeat
      }
      pak = vak;		// set program counter to v
    }
    add();			// restore accumulator
    break;

  case 043:			// Equality Jump, EJuv
    sar = uak;
    x = readaddr(sar);
    dx();
    sub();
    atl = 00ULL;		// subtract 1 from accumulator
    atr = 01ULL;
    sub();

    if (a71borrow_flag) {	// jump if end-around borrow from A71
      if (repeat_flag) {	// if RPjnw (075) in effect
	pak = pak ^ 077777;	// complement PAK
	q = pak;		// save PAK in Q
	repeat_flag = 0;	// terminate repeat
      }
      pak = vak;		// jump to V
    }

    // now restore accumulator
    dx();
    add();
    x = 01ULL;
    dx();
    add();
    break;

  case 044:			// Q-Jump, QJuv
    if (q & BIT35) {
      pak = uak;
    } else {
      pak = vak;
    }
    shiftq1();
    break;

  case 045:			// Manually Selective Jump, MJjv
    if (j > 3) {
      printf("\aMJjv: error: j out of range: %d\n",j);
      run_flag = 0;
    }
    if ((j == 0) || (mj[j])) {
      pak = vak;
    }
    break;

  case 046:			// Sign Jump, SJuv
    if (al & BIT35) {
      pak = uak;
    } else {
      pak = vak;
    }
    break;

  case 047:			// Zero Jump, ZJuv
    atl = 00ULL;
    atr = 01ULL;
    sub();
    if (a71borrow_flag) {
      pak = vak;
    } else {
      pak = uak;
    }
    x = 01ULL;
    dx();
    add();
    break;

  case 051:			// Q-Controlled Transmit, QTuv
  case 052:			// Q-Controlled Add, QAuv
    sar = uak;
    x = readaddr(sar);
    if (mcr == 051) {
      cleara();
    }
    x = q & x;
    sx();
    add();
    if (mcr == 052) {
      x = ar;
    }
    sar = vak;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 053:			// Q-Controlled Substitute, QSuv
    sar = uak;
    x = readaddr(sar);
    cleara();
    x = x & q;
    sx();
    add();
    q = q ^ WORD_MASK;
    sar = vak;
    x = readaddr(sar);
    x = x & q;
    sx();
    add();
    q = q ^ WORD_MASK;
    x = ar;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 054:			// Left Shift in A, LAuk
    x = 00ULL;
    sar = uak;
    if (!is_a(sar)) {
      x = readaddr(sar);
      cleara();
    }
    dx();
    add();
    shifta(k);
    x = ar;
    sar = (vak & VHIGH_MASK) | uak;
    if (!is_a(sar)) {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 055:			// Left shift in Q, LQuk
    sar = uak;
    x = readaddr(sar);
    q = x;
    shiftq(k);
    x = q;
    sar = (vak & VHIGH_MASK) | uak;
    if (!is_a(sar)) {		// if U is not A
      writeaddr(sar,x,WORD_MASK);
    } else {		 // if U is A, put double-extended result in A
      cleara();
      dx();
      add();
    }
    break;
   
  case 056:			// Manually Selective Stop, MSjv
    if (j > 3) {
      printf("\aMSjv: error: j out of range: %d\n",j);
      run_flag = 0;
    }
    if ((j == 0) || (ms[j])) {
     printf("\aMSjv: manually selective stop j = %d\n",j);
     run_flag = 0;
    }
    pak = vak;
    break;

  case 057:			// Program Stop, PS--
    run_flag = 0;
    printf("\aPS--: program stop\n");
    break;

  case 061:			// Print, PR-v
    sar = vak;
    x = readaddr(sar);
    twr = (unsigned char) (x & 077ULL);
    //printf("twr: %03o\n",twr);
    if (twr == 047) {		// shift type bars up
      twshft_flag = 1;
    } else if (twr == 057) {	// shift type bars down
      twshft_flag = 0;
    } else {			// output character
      if (tc[twshft_flag][twr] != '\0') {
	fputc(tc[twshft_flag][twr],tw);
	putchar(tc[twshft_flag][twr]);
      } else {
	printf("\aPR-v: unknown typewriter code: %02o\n",
	       twr);
      }
    }
    break;

  case 063:			// Punch, PUjv
    sar = vak;
    x = readaddr(sar);
    hpr = (unsigned char) (x & 077ULL);
    if (j & 01) {		// turn on bit 7 on tape if j = 1
      hpr = hpr | '\100';
    }
    fputc(hpr,ptp);
    break;

  case 071:			// Multiply, MPuv
    sar = uak;
    x = readaddr(sar);
    cleara();
    q = x;
    sar = vak;
    x = readaddr(sar);
    mul();
    break;

  case 072:			// Multiply Add, MAuv
    sar = uak;
    x = readaddr(sar);
    q = x;
    shifta(36);
    sar = vak;
    x = readaddr(sar);
    mul();
    break;

  case 073:			// Divide, DVuv
    sar = uak;
    x = readaddr(sar);
    q = 00ULL;
    div();
    x = q;
    sar = vak;
    if (is_a(sar)) {
      cleara();
      dx();
      add();
    } else {
      writeaddr(sar,x,WORD_MASK);
    }
    break;

  case 074:			// Scale Factor, SFuv
    x = 00ULL;
    sar = uak;
    if (!is_a(sar)) {
      x = readaddr(sar);
      cleara();
    }
    dx();
    add();
    shifta(36);
    sar = 36; // rightmost 7 bits of SAR is shift counter SK in this case
    while ( ((ar & BIT35) >> 35) == ((ar & BIT34) >> 34)) {
      if (sar == 0) {
	shifta1();
	sar = 71;
	cycle;
      } else if (sar == 38) {
	shifta1();
	sar = sar - 1;
	break;
      } else {
	shifta1();
	sar = sar - 1;
      }
    }

    x = 00ULL;
    x = sar;
    sar = vak;
    if (is_q(sar) || is_a(sar)) {
      printf("\aSFuv: error: invalid address v: %05o\n",vak);
      run_flag = 0;
    } else {
      writeaddr(sar,x,V_MASK);
    }
    break;

  case 075:			// Repeat, RPjnw
    pak_repeat = pak;		// store where we come from
    pak_flag = 1;	// use alternate program counter for debugging
    x = 00ULL;
    w = vak;
    x = w;
    sar = f1;
    writeaddr(sar,x,V_MASK);
    sar = pak;
    x = readaddr(sar);
    pak = uak;		    // jn -> PAK, where jn is contained in uak
    j_rpt = (pak & J_MASK) >> 12; // save address modification code
    pak = pak ^ 077777;		// complement jn
    pcr = x;
    split_pcr();
    repeat_flag = 2;
    // rest of repeat operation is handled by a special machine loop
    break;

  case 076:			// External Read, ERjv
    x = 00ULL;
    if (j) {			// j = 1, select iob
      x = iob;
      iob = 00ULL;
    } else {			// j = 0, slect ioa
      ioa = (unsigned char) fgetc(a_dev);
      x = (unsigned long long) ioa;
      ioa = '\0';
    }
    sar = vak;
    if (is_a(sar)) {
      cleara();
      dx();
      add();
    } else {
      writeaddr(sar,x,WORD_MASK);
    } 
    break;

  case 077:			// External Write, EWjv
    sar = vak;
    x = readaddr(sar);
    if (j) {
      iob = x;
    } else {
      ioa = (unsigned char) x;
    }
    break;

  default:
    printf("\aunknown opcode: %02o\n",mcr);
    run_flag = 0;
  }
}

// divide (36 stage non-restoring with final restore if necessary)
void div(void) {
  int i;
  int dividend_sign, divisor_sign;

  divisor_sign = (x >> 35) & 01ULL;
  shifta(36);
  for (i = 0; i < 36; i++) {
    shifta1();
    dx();
    dividend_sign = (al >> i) & 01ULL;
    if ((!dividend_sign && !divisor_sign) || // dividend & divisor have
	(dividend_sign && divisor_sign)) {   // the same sign
      q = q | 01ULL;
      sub();
    } else {		    // dividend & divisor have different signs
      add();
    }
    shiftq1();
  }

  // final restore -- check and correct for negative remainder
  if (al & BIT35) {
    //printf("negative remainder\n");
    dx();
    if (divisor_sign) {
      //printf("negative divisor\n");
      sub();
      q = q + 01ULL;
    } else {
      //printf("positive divisor\n");
      add();
      q = q - 01ULL;
    }
  }
}

// multiply
void mul(void) {
  int i;
  int mult_flag;
  mult_flag = 0;
  // procedure modification if Q negative and X positive or
  // both Q and X negative
  if ( ((q & BIT35) && (!(x & BIT35)) )
      || ((q & BIT35) && (x & BIT35))) { 
    mult_flag = 1; 
    dx();
    sub();
  }
  for (i = 0; i < 36; i++) {
    shifta1();
    if (q & BIT35) {
      dx();
      add();
    }
    shiftq1();
  }
  // cleanup procedure
  if (mult_flag) {
    dx();
    add();
  }
}

// clear accumulator
void cleara(void) {
  ar = 00ULL;
  al = 00ULL;
}

// double length extension of X (with sign extension)
void dx(void) {
  atr = x;
  if (atr & BIT35) { // sign extend
    atl = WORD_MASK;
  } else {
    atl = 00ULL;
  }
}

// split double length extension of X (no sign extension)
void sx(void) {
  atr = x;
  atl = 00ULL;
}

// circular shift A left by count bits
void shifta(unsigned int count) {
  unsigned int i;
  for (i = 0; i < count; i++) {
    shifta1();
  }
}
  
// circular shift A left by 1 bit
void shifta1(void) {
  ar = (ar << 1) | ((al & BIT35) >> 35);
  al = (al << 1) | ((ar & BIT36) >> 36);
  ar = ar & WORD_MASK;
  al = al & WORD_MASK;
}

// circular shift Q left by count bits
void shiftq(unsigned int count) {
  unsigned int i;
  for (i = 0; i < count; i++) {
    shiftq1();
  }
}

// circular shift Q left by 1 bit
void shiftq1(void) {
  q = (q << 1) | ((q & BIT35) >> 35);
  q = q & WORD_MASK;
}

// add
void add(void) {
  atr = atr ^ WORD_MASK;	// form 1's complement
  atl = atl ^ WORD_MASK;
  sub();			// do arithmetic
 }

// subtract (the fundamental machine operation)
void sub(void) {
  a71borrow_flag = 0;
  //al = al | ((ar & 01ULL) << 36); // grab bit for wraparound borrow

  // 1st stage of sub() (do accumulator right half)
  ar = ar - atr;
  if (ar & BIT63) {		// 1st stage borrow
    /// propagate 1st stage borrow to 2nd stage
    al = al - 01ULL;
    ar = ar & WORD_MASK;	// reset ar
  } else if (ar & BIT36) {	// 1st stage carry
    al = al + 01ULL;		// propagate carry to 2nd stage
  } else {
    ;
  }

  // 2nd stage of sub() (do accumulator left half)
  al = al - atl;
  if (al & BIT63) {		// borrow in 2nd stage
    ar = ar - 01ULL;   // propagate 2nd stage borrow back to 1st stage
    a71borrow_flag = 1;
    if (ar & BIT63) {		// another 1st stage borrow
      al = al - 01ULL;		// propagate borrow to 2nd stage
      ar = ar & WORD_MASK;	// reset ar
    }
  } else if (al & BIT36) {	// propagate carry to 1st stage
    ar = ar + 01ULL;
    // propagate carry from 1st to 2nd here??
  } else {
    ;
  }
  ar = ar & WORD_MASK;
  al = al & WORD_MASK;
}

// halt - catch interrupt, set run_flag to halt machine
void halt(int sig_num) {
  run_flag = 0;
}

// test if address is A (accumulator)
int is_a(unsigned int addr) {
  if ((addr >= 032000) && (addr <= 032777)) {
    return 1;
  } else {
    return 0;
  }
}

// test if address is Q
int is_q(unsigned int addr) {
  if ((addr >= 031000) && (addr <= 031777)) {
    return 1;
  } else {
    return 0;
  }
}

// readaddr - read a memory address
unsigned long long readaddr(unsigned int addr) {
  if (addr <= 027777) {		// core
    return mcs[addr];
  } else if (is_q(addr)) {	// Q
    return q;
  } else if (is_a(addr)) {	// A accumulator
    return ar;
  } else if ((addr >= 040000) && (addr <= 077777)) { // drum
    return md[addr-040000];
  } else {
    printf("\areadaddr: invalid read address: %05o\n",addr);
    run_flag = 0;
    return 0;
  }
}

// writeaddr - write masked bits to an address
void writeaddr(unsigned int addr, unsigned long long val,
	       unsigned long long bitflag) {
  unsigned long long flaginv;
  flaginv = bitflag ^ WORD_MASK;
  if (addr <= 027777) {		// core
    mcs[addr] = (mcs[addr] & flaginv) | (val & bitflag);
  } else if (is_q(addr)) {	// Q
    q = (q & flaginv) | (val & bitflag);
  } else if (is_a(addr)) {	// A accumulator
    ar = (ar & flaginv) | (val & bitflag);
  } else if ((addr >= 040000) && (addr <= 077777)) { // drum
    md[addr-040000] = (md[addr-040000] & flaginv) | (val & bitflag);
  } else {
    printf("\awriteaddr: invalid write address: %05o\n",addr);
    run_flag = 0;
  }
} 

// disconnect I/O and store drum to disk
void io_close(int sig_num) {
  int i;
  FILE *fid;

  if (ptr != NULL) {
    printf("disconnecting paper tape reader... ");
    fclose(ptr);
  }
  printf("done\n");

  printf("disconnecting paper tape punch... ");
  fclose(ptp);
  printf("done\n");  

  printf("disconnecting typewriter... ");
  fclose(tw);
  printf("done\n");  

  printf("storing drum... ");
  fid = fopen("md","w");
  fwrite(md,sizeof(md[0]),MAX_DRUM,fid);
  fclose(fid);
  printf("done\n");
  exit(0);
}

// load drum memory image from disk and connect I/O
void io_open(void) {
  int i;
  FILE *fid;

  printf("loading drum... ");
  fid = fopen("md","r");
  if (fid != NULL) {
    fread(md,sizeof(md[0]),MAX_DRUM,fid);
    fclose(fid);
    printf("done\n");
  } else {
    printf("none found\n");
  }

  printf("connecting typewriter... ");
  tw = fopen("tw","w");
  printf("done\n");

  printf("connecting paper tape punch... ");
  ptp = fopen("ptp","w");
  printf("done\n");

  printf("connecting paper tape reader... ");
  ptr = fopen("ptr","r");
  if (ptr != NULL) {
    printf("done\n");
  } else {
    printf("none found\n");
  }
}
    
// This is straight out of the GNU Readline Library manual
/* Read a string, and return a pointer to it.
   Returns NULL on EOF. */
char *
rl_gets (char * p)
{
  /* If the buffer has already been allocated,
     return the memory to the free pool. */
  if (line_read)
    {
      free (line_read);
      line_read = (char *)NULL;
    }

  /* Get a line from the user. */
  line_read = readline (p);

  /* If the line has any text in it,
     save it on the history. */
  if (line_read && *line_read)
    add_history (line_read);
  return (line_read);
}
