
#include "Program.h"
#include "Symbol.h"
#include "Frame.h"
#include <stdlib.h>
#include <stdio.h>



Program::Program (Program *enclosing = 0) : Obj(ProgramType)
{
    alloced = 64;
    code = (unsigned char *) malloc(alloced);
    size = 0;
    params = enclosing ? new Frame(enclosing->params) : 0;
}

void Program::AddOp (int op)
{
    if (size == alloced) {
	alloced <<= 1;
	code = (unsigned char *) realloc(code, alloced);
    }
    code[size++] = op;
}

void Program::AddL (long l)		// My little-endian roots show
{
    AddOp(l & 0xff);
    AddOp((l >> 8) & 0xff);
    AddOp((l >> 16) & 0xff);
    AddOp((l >> 24) & 0xff);
}

void Program::AddS (short s)
{
    AddOp(s & 0xff);
    AddOp((s >> 8) & 0xff);
}

void Program::SetL (long where, long l)
{
    code[where] = l & 0xff;
    code[where + 1] = (l >> 8) & 0xff;
    code[where + 2] = (l >> 16) & 0xff;
    code[where + 3] = (l >> 24) & 0xff;
}

int Program::FetchOp (long &ip)
{
    return code[ip++];
}

long Program::FetchL (long &ip)
{
    unsigned char *c = code + ip;
    ip += 4;
    return c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
}

short Program::FetchS (long &ip)
{
    unsigned char *c = code + ip;
    ip += 2;
    return c[0] | (c[1] << 8);
}

void *Program::FetchP (long &ip)
{
    return (void *) FetchL(ip);
}

void Program::Dump (int indent)
{
    long n = 0, t;
    int x;
    printf ("%*sPROGRAM %08x (", indent, "", this);
    if (params)
	params->Dump();
    else
	printf ("<top-level>");
    printf (")\n");
    while (n < size) {
	printf ("%*s%4d: ", indent, "", n);
	switch (x = FetchOp(n)) {
	  case O_PLUS:
	    printf ("add\n"); break;
	  case O_MINUS:
	    printf ("sub\n"); break;
	  case O_TIMES:
	    printf ("mul\n"); break;
	  case O_DIV:
	    printf ("div\n"); break;
	  case O_EQ:
	    printf ("equal?\n"); break;
	  case O_NE:
	    printf ("not-equal?\n"); break;
	  case O_LE:
	    printf ("less-equal?\n"); break;
	  case O_GE:
	    printf ("greater-equal?\n"); break;
	  case O_LT:
	    printf ("less-than?\n"); break;
	  case O_GT:
	    printf ("greater-than?\n"); break;
	  case O_ASSIGN:
	    printf ("assign\n"); break;
	  case O_CALL:
	    printf ("call\n"); break;
	  case O_MAKEFRAME:
	    printf ("make-frame\n"); break;
	  case O_PARAM:
	    printf ("add-param\n"); break;
	  case O_GOTO:
	    t = n;
	    printf ("goto %ld\n", t + FetchL(n));
	    break;
	  case O_BRANCH:
	    t = n;
	    printf ("branch-if-false %ld\n", t + FetchL(n));
	    break;
	  case O_LOCALADDROF:
	  case O_LOCALVALUEOF:
	    printf ("fetch-local-%s ",
		    x == O_LOCALADDROF ? "addr" : "value");
	    int levels = FetchS(n);
	    int offset = FetchS(n);
	    printf ("%d,%d (", levels, offset);
	    params->FindSymbol(levels, offset)->Dump();
	    printf (")\n");
	    break;
	  case O_ADDROF:
	  case O_VALUEOF:
	    printf ("fetch-%s ", x == O_ADDROF ? "addr" : "value");
	    Symbol *sym = (Symbol *) FetchP(n);
	    sym->Dump();
	    printf ("\n");
	    break;
	  case O_NUM:
	    printf ("push %ld\n", FetchL(n));
	    break;
	  case O_POP:
	    printf ("pop\n");
	    break;
	  case O_FUNC:
	    printf ("push-function ");
	    Program *p = (Program *) FetchP(n);
	    printf ("%08x\n", p);
	    p->Dump(indent + 6);
	    break;
	  default:
	    printf ("Unknown opcode %d\n", code[n-1]);
	    return;
	}
    }
}
