RandomizeMemory=false;
CheckUninit=false;
IgnoreInvalidOpcode=false;

// Setup opExec
opExec=new Array();
opExec[0x00]=new Opcode(ExecuteHT);
opExec[0x01]=new Opcode(ExecuteCA);
opExec[0x02]=new Opcode(ExecuteSR);
opExec[0x04]=new Opcode(ExecuteNO);
opExec[0x08]=new Opcode(ExecuteSL);
opExec[0x10]=new Opcode(ExecuteSE);
opExec[0x20]=new Opcode(ExecuteJC);
opExec[0x40]=new Opcode(ExecuteStartSub);
opExec[0x60]=new Opcode(ExecuteSU);
opExec[0x80]=new Opcode(ExecuteJU);
opExec[0xa0]=new Opcode(ExecuteSA);
opExec[0xc0]=new Opcode(ExecuteAN);
opExec[0xe0]=new Opcode(ExecuteAD);

function ParseLineMC(line) {
    line=line.replace(/#.*$/,"");
    if (/^\s*$/.test(line))
        return;
    var reLine=/(0x[\da-f][\da-f]):\s+([\da-f\s]+)\s*/i;
    if (!reLine.test(line)) {
        DoError("Line is invalid: "+sLine+"\n"+line);
        return;
    }
    reLine.exec(line);
    var strLoc=RegExp.$1;
    var loc=parseInt(strLoc,16);
    var strMachineCode=RegExp.$2;
    strMachineCode=strMachineCode.replace(/\s+$/,"");
    strMachineCode=strMachineCode.replace(/\s\s+/g,"\s");
    var arrMachineCode=strMachineCode.split(/\s/);
    for (var i=0;i<arrMachineCode.length;i++) {
        bytecode[loc+i]=parseInt(arrMachineCode[i],16);
        //WScript.Echo(loc+i+": "+bytecode[loc+i]+" "+arrMachineCode[i]);
    }
}

function Opcode(opfunc) {
    this.exec=opfunc;
    //this.print=printfn;
}

function Reset() {
    pc=new Object();
    pc.pc=0;
    pc.rac=0xff;
    A=0;
    halt=false;
    instCount=0;
}

function ExecuteInstruction(bytecode,pc) {
    opcode=bytecode[pc.pc];
    //print_byte(bytecode,pc);
    if (opcode.unused && CheckUninit) {
        //Executing uninitialized memory - panic...
        halt=true;
        DoError("Attempted to execute uninitialized memory at location 0x"+ByteString(pc.pc)+" - program halted.");
        return pc;
    }
    if (opExec[opcode]) {
        pc=opExec[opcode].exec(bytecode,pc);
        PrintState(bytecode,A,pc);
    }
    else {
        if (!IgnoreInvalidOpcode) {
            halt=true;
            //WScript.Echo("pc="+pc.pc);
            print_byte(bytecode,pc.pc);
            DoError("Invalid opcode encountered :(");
        }
        else {
            WScript.Echo("Invalid opcode at location 0x"+ByteString(pc.pc));
            pc.pc++;
            PrintState(bytecode,A,pc);
        }
    }
    if (halt)
        WScript.Echo("Program halted.");
    return pc;
}

function PrintState(bytecode,A,pcO) {
    var str="State: A="+ByteString(A)+" ("+A+")\t";
    str+="PC="+ByteString(pcO.pc)+" ("+pcO.pc+")\t";
    str+="OR="+ByteString(bytecode[255])+" ("+bytecode[255]+")\t";
    str+="RAC="+ByteString(pcO.rac)+" ("+pcO.rac+")";
    WScript.Echo(str);
}

function DoError(msg) {
    WScript.Echo("Error: "+msg);
}

function ExecuteCA(bytecode,pcO) {
    var pc=pcO.pc;
    print_CA(bytecode,pc);
    changeA(0);
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteHT(bytecode,pcO) {
    var pc=pcO.pc;
    print_HT(bytecode,pc);
    halt=true;
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteNO(bytecode,pcO) {
    var pc=pcO.pc;
    print_NO(bytecode,pc);
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteSL(bytecode,pcO) {
    var pc=pcO.pc;
    print_SL(bytecode,pc);
    pc++;
    var x=(A<<1)&255;
    changeA(x);
    pcO.pc=pc;
    return pcO;
}

function ExecuteSR(bytecode,pcO) {
    var pc=pcO.pc;
    print_SR(bytecode,pc);
    pc++;
    var x=(A>>1)&255;
    changeA(x);
    pcO.pc=pc;
    return pcO;
}

function ExecuteAD(bytecode,pcO) {
    var pc=pcO.pc;
    print_AD(bytecode,pc);
    var loc=bytecode[pc+1];
    if (bytecode[loc].unused && CheckUninit) {
        DoError("Added contents of uninitialized memory location 0x"+ByteString(loc)+" to accumulator.");
        halt=true;
        return pcO;
    }
    pc++;
    changeA(A+bytecode[loc]);
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteSU(bytecode,pcO) {
    var pc=pcO.pc;
    print_SU(bytecode,pc);
    var loc=bytecode[pc+1];
    if (bytecode[loc].unused && CheckUninit) {
        DoError("Subtracted contents of uninitialized memory location 0x"+ByteString(loc)+" to accumulator.");
        halt=true;
        return pcO;
    }
    pc++;
    changeA(A-bytecode[bytecode[pc]]);
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteSA(bytecode,pcO) {
    var pc=pcO.pc;
    print_SA(bytecode,pc);
    pc++;
    setLoc(bytecode[pc],A);
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteSE(bytecode,pcO) {
    var pc=pcO.pc;
    print_SE(bytecode,pc);
    pc++;
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteAN(bytecode,pcO) {
    var pc=pcO.pc;
    print_AN(bytecode,pc);
    var loc=bytecode[pc+1];
    if (bytecode[loc].unused && CheckUninit) {
        DoError("Performed bitwise AND with contents of uninitialized memory location 0x"+ByteString(loc)+" to accumulator.");
        halt=true;
        return pcO;
    }
    pc++;
    changeA(A&bytecode[bytecode[pc]]);
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteStartSub(bytecode,pcO) {
    var pc=pcO.pc;
    var old_pc=pcO.rac;
    print_SS(bytecode,pc);
    pc++;
    setLoc(bytecode[pc],old_pc);//Need to store return address somehow
    pc++;
    pcO.pc=pc;
    return pcO;
}

function ExecuteJU(bytecode,pcO) {
    var pc=pcO.pc;
    print_JU(bytecode,pc);
    pc++;
    var new_pc=bytecode[pc];//Need to store return address somehow
    pc++;
    pcO.pc=new_pc;
    pcO.rac=pc;
    return pcO;
}

function ExecuteJC(bytecode,pcO) {
    var pc=pcO.pc;
    print_JC(bytecode,pc);
    pc++;
    var new_pc=bytecode[pc];//Need to store return address somehow
    pc++;
    pcO.pc=pc;
    if (A==0)
     return pcO;// No jump
    pcO.pc=new_pc;
    pcO.rac=pc;
    return pcO;
}

function print_inst(pc,a) {
    var args=new Array();
    for (var i=0;i<arguments.length;i++) {args[i]=arguments[i];}
    var pc=args.shift();
    var str=args.shift();
    var bytes=args.pop();
    var mc="";
    if (false) {
        // Show machine code in disassembly
        var bstr="";
        for (var i=0;i<bytes.length;i++) {
            bstr+=ByteString(bytes[i])+" ";
        }
        mc=bstr+"\t# ";
    }
    if (args.length>0) {
        for (var i=0;i<args.length;i++) {
            var loc=bytes[i+1];
            //WScript.Echo("- Loc 0x"+ByteString(loc)+" ("+loc+") "+" = 0x"+ByteString(bytecode[loc])+" ("+bytecode[loc]+")");
            str+=" "+args[i]+" (->"+ByteString(bytecode[loc])+")";
        }
    }
    WScript.Echo("0x"+ByteString(pc)+":\t"+str);
}

function changeA(nA) {
    var newA=nA%256;
    WScript.Echo("\tA: "+ByteString(A)+"->"+ByteString(newA)+" ("+A+"->"+newA+") ");
    A=newA;
}

function setLoc(loc,v) {
    var value=v%256;
    WScript.Echo("\t0x"+ByteString(loc)+": "+ByteString(bytecode[loc])+"->"+ByteString(value)+" ("+bytecode[loc]+"->"+value+")");
    bytecode[loc]=value;
}
