/*******************************************************************
   Program :  Recursive Descent Compiler for PL/0
   Module  :  PL0INT - Interpreter of itermediate code
   File    :  pl0int.c
   Compiler:  Borland C 3.1 - 4.5, GNU C 2.7.1
   Author  :  H. Weber
   Revision:  Aug. 1998
********************************************************************/

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

#define codemax  5000
#define stackmax 10000

int pc;                    /* program counter */
int base;                  /* base pointer */
int sptr;                  /* stack pointer */
int jumpswitch;            /* indicates jumps */
int stack[stackmax];       /* runtime stack */

FILE *intcode;
struct instr {
     int o;     /* opcode */
     int l;     /* level of declaration */
     int a;     /* rel. address in DSA */
   };
struct instr code[codemax];


void readcode(int *count)
/* read intermediate code */
{
   int  no, o, k;
   char oc[4];

   *count = 0;
   while (!feof(intcode) && *count < codemax) {
      k = fscanf(intcode, "%3d  %3s  %2d %5d",
		 &no, oc, &code[*count].l,
		 &code[*count].a);
      if (k < 4) break;
      if      (strcmp(oc, "NUL") == 0) o = NUL;
      else if (strcmp(oc, "ADD") == 0) o = ADD;
      else if (strcmp(oc, "SUB") == 0) o = SUB;
      else if (strcmp(oc, "MPY") == 0) o = MPY;
      else if (strcmp(oc, "DVD") == 0) o = DVD;
      else if (strcmp(oc, "NEG") == 0) o = NEG;
      else if (strcmp(oc, "ODD") == 0) o = ODD;
      else if (strcmp(oc, "EQL") == 0) o = EQL;
      else if (strcmp(oc, "NEQ") == 0) o = NEQ;
      else if (strcmp(oc, "LSS") == 0) o = LSS;
      else if (strcmp(oc, "GTR") == 0) o = GTR;
      else if (strcmp(oc, "LEQ") == 0) o = LEQ;
      else if (strcmp(oc, "GEQ") == 0) o = GEQ;
      else if (strcmp(oc, "LIT") == 0) o = LIT;
      else if (strcmp(oc, "RET") == 0) o = RET;
      else if (strcmp(oc, "CAL") == 0) o = CAL;
      else if (strcmp(oc, "LOD") == 0) o = LOD;
      else if (strcmp(oc, "STO") == 0) o = STO;
      else if (strcmp(oc, "INT") == 0) o = INT;
      else if (strcmp(oc, "JPU") == 0) o = JPU;
      else if (strcmp(oc, "JPC") == 0) o = JPC;
      else if (strcmp(oc, "INP") == 0) o = INP;
      else if (strcmp(oc, "PRN") == 0) o = PRN;
      else if (strcmp(oc, "ASC") == 0) o = ASC;
      else if (strcmp(oc, "PRS") == 0) o = PRS;
      else if (strcmp(oc, "PRL") == 0) o = PRL;
      else {
	 fprintf(stderr, "Unknown opcode symbol %s\n",
		 oc);
	 exit(1);
      }
      code[(*count)++].o = o;
   }
   if (*count == codemax) {
      fprintf(stderr, "Program too long.");
      exit(1);
   }
   (*count)--;
}

int sbase(int l)
/* sbase determines the address of a DSA which is l levels
   down the runtime stack */
{
   int b;

   b = base;
   while (l != 0) {
      b = stack[b];
      l--;
   }
   return b;
}

main(int argc, char *argv[])
/*------------------------------------------------------------------
   The main function of the interpreter reads the intermediate
   code file and interpretes itc content.
------------------------------------------------------------------*/
{
   int h1, h2, count;
   char cname[20];
   char *p;

   /* check arguments */
   if (argc == 1) {
      fprintf(stderr, "Usage: %s pcode\n", argv[0]);
      exit(1);
   }

   /* set up filenames */
   strcpy(cname, argv[1]);
   p = strchr(cname, '.');
   if (p) *p = 0;
   strcat(cname, ".cod");

   /* open file */
   if ((intcode = fopen(cname, "r")) == NULL) {
      fprintf(stderr, "Cannot open intermed. code file %s\n",
	      cname);
      exit(1);
   }

   /* read code into array */
   readcode(&count);

   /* initialization of stack */
   stack[0] = 0;
   stack[1] = 0;
   stack[2] = 0;

   /* initialization of registers */
   base = 0;
   sptr = 2;       /* neu */
   pc = 0;
   jumpswitch = 1;

   /* main loop */
   do {
      if (!jumpswitch) pc++;
      jumpswitch = 0;
      switch (code[pc].o) {

	 case ADD :
	    sptr--;
	    stack[sptr] = stack[sptr] + stack[sptr+1];
	    break;

	 case SUB :
	    sptr--;
	    stack[sptr] = stack[sptr] - stack[sptr+1];
	    break;

	 case MPY :
	    sptr--;
	    stack[sptr] = stack[sptr] * stack[sptr+1];
	    break;

	 case DVD :
	    sptr--;
	    stack[sptr] = stack[sptr] / stack[sptr+1];
	    break;

	 case NEG :
	    stack[sptr] = -stack[sptr];
	    break;

	 case ODD :
	    stack[sptr] = stack[sptr] % 2;
	    break;

	 case EQL :
	    sptr--;
	    stack[sptr] = stack[sptr] == stack[sptr+1] ? 1 : 0;
	    break;

	 case NEQ :
	    sptr--;
	    stack[sptr] = stack[sptr] != stack[sptr+1] ? 1 : 0;
	    break;

	 case LSS :
	    sptr--;
	    stack[sptr] = stack[sptr] < stack[sptr+1] ? 1 : 0;
	    break;

	 case GTR :
	    sptr--;
	    stack[sptr] = stack[sptr] > stack[sptr+1] ? 1 : 0;
	    break;

	 case LEQ :
	    sptr--;
	    stack[sptr] = stack[sptr] <= stack[sptr+1] ? 1 : 0;
	    break;

	 case GEQ :
	    sptr--;
	    stack[sptr] = stack[sptr] >= stack[sptr+1] ? 1 : 0;
	    break;

	 case LIT :
	    stack[++sptr] = code[pc].a;
	    break;

	 case RET :
	    sptr = base - 1;
	    pc = stack[sptr+3];
	    base = stack[sptr+2];
	    break;

	 case CAL :
	    stack[sptr+1] = sbase(code[pc].l);
	    stack[sptr+2] = base;
	    stack[sptr+3] = pc;
	    base = sptr + 1;
	    sptr += 3;          /* neu */
	    pc = code[pc].a;
	    jumpswitch = 1;
	    break;

	 case LOD :
	    stack[++sptr] = stack[sbase(code[pc].l)+code[pc].a];
	    break;

	 case STO :
	    stack[sbase(code[pc].l)+code[pc].a] = stack[sptr--];
	    break;

	 case INT :
	    sptr += code[pc].a;
	    break;

	 case JPU :
	    jumpswitch = 1;
	    pc = code[pc].a;
	    break;

	 case JPC :
	    if (stack[sptr] == 0) {
	       jumpswitch = 1;
	       pc = code[pc].a;
	    }
	    sptr--;
	    break;

	 case INP :
	    scanf("%d", &stack[++sptr]);
	    break;

	 case PRN :
	    printf("  %6d", stack[sptr--]);
	    break;

	 case PRS :
	    for (h1=1; h1<=code[pc].a; h1++)
	       printf("%c", code[pc+h1].a);
	    pc += code[pc].a;
	    break;

	 case PRL :
	    printf("\n");
	    break;

     default  :
	    fprintf(stderr, "Unknown opcode %d\n", code[pc].o);
	    exit(1);
      }
   } while (pc != 0);

   /* close files */
   fclose(intcode);
   return;
}
