

#include "Machine.h"
#include "Func.h"

Machine::Machine(void)
{
    stackSize = 256;
    stack = (Value *) malloc(sizeof(Value) * stackSize);
    stackPointer = 0;
    prog = 0;
    env = 0;
}

Value Machine::Run (Program *p, Frame *e)
{
    stackPointer = 0;			// Reset stack when starting a program
    prog = p;
    env = e;
    insPointer = 0;
    while (insPointer < prog->End()) {
	RunOne();
    }
    return Pop();
}

void Machine::RunOne (void)
{
    int op = prog->FetchOp(insPointer);
    Value a, b;
    Value *vp;
    Symbol *sym;
    long t;
    Frame *e;
    Func *f;
    Program *p;
    short levels, offset;
    
    switch (op) {
      case O_NUM:
	Push(Value(FetchL()));
	break;
      case O_PLUS:
	a = Pop(); b = Pop(); Push (Value(b.Number() + a.Number())); break;
      case O_MINUS:
	a = Pop(); b = Pop(); Push (Value(b.Number() - a.Number())); break;
      case O_TIMES:
	a = Pop(); b = Pop(); Push (Value(b.Number() * a.Number())); break;
      case O_DIV:
	a = Pop(); b = Pop(); Push (Value(b.Number() / a.Number())); break;
      case O_EQ:
	a = Pop(); b = Pop(); Push (Value(b.Number() == a.Number())); break;
      case O_NE:
	a = Pop(); b = Pop(); Push (Value(b.Number() != a.Number())); break;
      case O_LE:
	a = Pop(); b = Pop(); Push (Value(b.Number() <= a.Number())); break;
      case O_GE:
	a = Pop(); b = Pop(); Push (Value(b.Number() >= a.Number())); break;
      case O_LT:
	a = Pop(); b = Pop(); Push (Value(b.Number() < a.Number())); break;
      case O_GT:
	a = Pop(); b = Pop(); Push (Value(b.Number() > a.Number())); break;
      case O_ASSIGN:
	a = Pop();
	vp = Pop().ValuePtr();
	*vp = a;
	Push(a);
	break;
      case O_VALUEOF:
	sym = (Symbol *) FetchP();
	Push (sym->Value());
	break;
      case O_ADDROF:
	sym = (Symbol *) FetchP();
	Push (Value(&sym->Value()));
	break;
      case O_LOCALVALUEOF:
	levels = FetchS();
	offset = FetchS();
	Push (env->FindValue(levels, offset));
	break;
      case O_LOCALADDROF:
	levels = FetchS();
	offset = FetchS();
	Push (Value(&(env->FindValue(levels, offset))));
	break;
      case O_POP:
	Pop();
	break;
      case O_GOTO:
	t = insPointer;
	insPointer = t + FetchL(); 
	break;
      case O_BRANCH:
	t = insPointer;
	t += FetchL();
	if (Pop().False())
	    insPointer = t;
	break;
      case O_FUNC:
	p = (Program *) FetchP();
	f = new Func (p, env);
	Push(Value(f));
	break;
      case O_MAKEFRAME:
	a = TOS();
	if (a.ValueType() != FuncType) {
	    printf ("Attempt to call non-function\n");
	    exit(1);
	}
	f = (Func *) a.ObjPtr();
	e = new Frame(f->Env());
	Push(Value(e));
	break;
      case O_PARAM:
	a = Pop();
	e = (Frame *) TOS().ObjPtr();
	e->AddBinding (NULL, a);
	break;
      case O_CALL:
	e = (Frame *) Pop().ObjPtr();
	f = (Func *) Pop().ObjPtr();
	e->IsArgsFor (f);
	Push(Value(prog));
	Push(Value(env));
	Push(Value(insPointer));
	env = e;
	prog = f->Prog();
	insPointer = 0;
	break;
      default:
	printf ("***  ERROR   Unknown operator %d in machine %08x location %d\n", op, prog, insPointer - 1);
	prog->Dump();
	exit(1);
    }
    if (insPointer >= prog->End() && stackPointer > 1) {
	a = Pop();
	insPointer = Pop().Number();
	env = (Frame *) Pop().ObjPtr();
	prog = (Program *) Pop().ObjPtr();
	Push(a);
    }
}

void Machine::Push (Value v)
{
    if (stackPointer == stackSize) {
	stackSize <<= 1;
	stack = (Value *) realloc (stack, stackSize * sizeof(Value));
    }
    stack[stackPointer++] = v;
}

Value Machine::Pop (void)
{
    return stack[--stackPointer];
}

