WalterC WalterC - 2 months ago 8
C Question

Simulating virtual machine, have trouble with incrementing pc versus jumps

I'm writing a virtual machine in C and I have all the various functions working, however I'm having trouble putting them together. Specifically I'm running into the problem that I need a way to increment the program counter without it interfering with the instructions that change what pc is pointing at like JMP, JPC, CAL, and RET. When I try to put in measures to cancel out pc++ like a PCvalueAfterJmp - 1 or

if
statements to not increment on those cases, it suddenly goes into an infinite loop which appears to be repeatedly going through the instructions.

This program reads in an input file and prints to the screen what instructions are being processed and the current state of the stack

int main(int argc, char* argv[]){
int running = 1;
int numInstructions = 0;
int lineRun;
int arcntr = 0;

//Memory
int stack[MAX_STACK_HEIGHT];
instruction code[MAX_CODE_LENGTH];
int arlist[MAX_STACK_HEIGHT];

//Registers
int sp=0;
int bp=1;
int pc=0;
instruction ir;

//Initializing ir
ir.op = 0;
ir.l = 0;
ir.m = 0;

//Initializing stack
stack[1] = 0;
stack[2] = 0;
stack[3] = 0;

//Reading the input file
numInstructions = readFile(argc, argv, code);
if(numInstructions < 0) //Exit with error if readFile returns invalid
return 1;

//show input code
printFile(code, numInstructions);

//setup and labeling
printState(-1, ir, pc, bp, sp, stack, arlist);



//Execution loop
while(running)
{
lineRun = pc;

//Fetch cycle
ir = code[pc];

//Execution cycle returns a nonzero to keep program running until end
if(!execOp(&sp, &bp, &pc, ir, code, stack, arlist, &arcntr))
running = 0;

//if statement didn't work
printState(lineRun, ir, pc, bp, sp, stack, arlist);
//if (!(ir.op == 5 || ir.op == 7 || ir.op == 8 || (ir.op == 2 && ir.m == 0)))
pc++;
}

return 0;
}


Here is my execution cycle

int execOp(int* sp, int* bp, int* pc, instruction ir, instruction code[],
int stack[], int arlist[], int* arcntr){

switch((opcode)ir.op){
case LIT:
stack[++(*sp)] = ir.m;
break;

case OPR: //Operators
switch((operator)ir.m){

case RET:
if(*bp == 1) //Kill the simulation if we're at the base level
return 0;
arlist[--(*arcntr)] = 0;
*sp = *bp - 1;
*pc = stack[*sp+3];
*bp = stack[*sp+2];
break;

case NEG:
stack[*sp] = -stack[*sp];
break;

case ADD:
(*sp)--;
stack[*sp] = stack[*sp] + stack[*sp+1];
break;

case SUB:
(*sp)--;
stack[*sp] = stack[*sp] - stack[*sp+1];
break;

case MUL:
(*sp)--;
stack[*sp] = stack[*sp] * stack[*sp+1];
break;

case DIV:
(*sp)--;
stack[*sp] = stack[*sp] / stack[*sp+1];
break;

case ODD:
stack[*sp] = stack[*sp] % 2;
break;

case MOD:
(*sp)--;
stack[*sp] = stack[*sp] % stack[(*sp)+1];
break;

case EQL:
(*sp)--;
stack[*sp] = stack[*sp] == stack[*sp+1];
break;

case NEQ:
(*sp)--;
stack[*sp] = stack[*sp] != stack[*sp+1];
break;

case LSS:
(*sp)--;
stack[*sp] = stack[*sp] < stack[*sp+1];
break;

case LEQ:
(*sp)--;
stack[*sp] = stack[*sp] <= stack[*sp+1];
break;

case GTR:
(*sp)--;
stack[*sp] = stack[*sp] > stack[*sp+1];
break;

case GEQ:
(*sp)--;
stack[*sp] = stack[*sp] >= stack[*sp+1];
break;
}
break;

case LOD:
stack[++*sp] = stack[base(ir.l, *bp, stack) + ir.m];
break;

case STO:
stack[base(ir.l, *bp, stack) + ir.m] = stack[(*sp)--];
break;

case CAL:
arlist[(*arcntr)++] = *sp + 1;
stack[*sp + 1] = base(ir.l, *bp, stack);
stack[*sp + 2] = *bp;
stack[*sp + 3] = *pc - 1;
*bp = *sp + 1;
*pc = ir.m;
break;

case INC:
*sp = *sp + ir.m;
break;

case JMP:
*pc = ir.m;
break;

case JPC:
if(!stack[(*sp)--])
*pc = ir.m;
break;

case SOI:
printf("%d\n", stack[(*sp)--]);
break;

case SIO:
scanf("%d", &stack[++(*sp)]);
break;
}

return 1; //A non-zero return value keeps the machine running
}

Answer

This part of your instruction decode select statement seems wrong

  case CAL:
    arlist[(*arcntr)++] = *sp + 1;
    stack[*sp + 1] = base(ir.l, *bp, stack);
    stack[*sp + 2] = *bp;
    stack[*sp + 3] = *pc - 1;
    *bp = *sp + 1;
    *pc = ir.m;
    break;

Normally you'd want to return to the NEXT instruction on a return.

stack[*sp + 3] = *pc - 1;

The *pc-1 part will probably will bring you back to your call instruction on return

I'd expect you'd want to push the address of the next instruction instead.

You'll probably want to update the stack pointer by 3 after pushing all that too, as well as checking your BP logic too

Comments