Back to Portfolio
vm.c
PL/0 Compiler — PM/0 Virtual Machine
C COP3402 Fall 2025 488 lines
vm.c

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


#define PAS_SIZE 500

// Define opcodes
typedef enum {
    LIT = 1,
    OPR = 2,
    LOD = 3,
    STO = 4,
    CAL = 5,
    INC = 6,
    JMP = 7,
    JPC = 8,
    SYS = 9
} opcode;

int pas[PAS_SIZE] = {0}; // process address space all initialized to 0

// Function to find base L levels down
int base(int BP, int L){
    int arb = BP;   // arb = activation record base
    while (L-- > 0) arb = pas[arb]; // follow static link
    return arb;
}
 // Print current state of the VM
void print_state(int OP, int L, int M, int PC, int BP, int SP, int textEnd) {
    char left[4]; // Buffer to hold instruction name
    
    // Decode OP and M
    if(OP == OPR){
        switch(M){
            case 0: 
                strcpy(left, "RTN"); 
                break;
            case 1: {
                strcpy(left, "ADD"); 
                break;
            }
            case 2: {
                strcpy(left, "SUB"); 
                break;
            }
            case 3: {
                strcpy(left, "MUL"); 
                break;
            }
            case 4: {
                strcpy(left, "DIV"); 
                break;
            }
            case 5: {
                strcpy(left, "EQL"); 
                break;
            }
            case 6: {
                strcpy(left, "NEQ"); 
                break;
            }
            case 7: {
                strcpy(left, "LSS"); 
                break;
            }
            case 8: {
                strcpy(left, "LEQ"); 
                break;
            }

            case 9: {
                strcpy(left, "GTR"); 
                break;
            }
            case 10: {
                strcpy(left, "GEQ"); 
                break;
            }
            default: {
                strcpy(left, "OPR"); 
                break;
            }
        }
    }
    else{
        switch(OP){
            case LIT: {
                strcpy(left, "LIT"); 
                break;
            }
            case LOD: {
                strcpy(left, "LOD"); 
                break;
            }
            case STO: {
                strcpy(left, "STO"); 
                break;
            }
            case CAL: {
                strcpy(left, "CAL"); 
                break;
            }
            case INC: {
                strcpy(left, "INC"); 
                break;
            }
            case JMP: {
                strcpy(left, "JMP"); 
                break;
            }
            case JPC: {
                strcpy(left, "JPC"); 
                break;
            }
            case SYS: {
                strcpy(left, "SYS"); 
                break;
            }
            default: {
                strcpy(left, "UNK"); 
                break;
            }
        }
    }

    int bars[20];           // to hold indices of activation record bars
    int nbars = 0;          // number of bars found

    // Find activation record bars
    int p = BP;                                     // Start from current frame base
    while (p >= 0 && p < PAS_SIZE && nbars < 20) { 
        bars[nbars++] = p;                          // Store bar position
        int next = pas[p - 1];
        if (next <= 0 || next >= PAS_SIZE) {        // Invalid next base
            break;
        }                                  
        if (next == p) break;
        p = next;                                   // Move to next frame base
    }

    // Print current instruction and registers
    printf(" %3s      %3d   %3d   %3d   %3d   %3d\t", left, L, M, PC, BP, SP);

    // Print stack contents with bars
    int bottom = textEnd - 1;                       // last data cell under text
    for (int i = bottom; i >= SP; i--) {
        

        for (int k = 0; k < nbars; k++) {           // Check if a bar should be printed here and print it
            if (i == bars[k] && bars[k] < bottom) { //
                printf(" | "); 
                break; 
            }
        }   
        printf(" %2d ", pas[i]);                     // Print stack value

    }
    printf("\n");
}

int main (int argc, char **argv){
    // Exit if improper argument
    if (argc != 2) {
        printf("Error improper argument\n");
        return 1;
    }

    // Open file
    FILE *fp = fopen(argv[1], "r");
    
    // Exit program if file not found
    if (fp == NULL) {
        printf("Error file not found\n");
        return 1;
    }

    int addr = PAS_SIZE - 1;        // Starting address at top of memory
    int line_number = 0;            // Count number of lines in file
    
    // Read instructions from file into pas
    while(1){
        int OP, L, M;
        int r = fscanf(fp, "%d %d %d", &OP, &L, &M); // Read 3 integers from file into OP, L, M
        if (r == EOF)                                // r holds number of items successfully scanned (unless)
            break;
        
        // Exit if improper format or file too big
        if (r != 3) {                
            printf("Error improper format\n");
            return 1;
        }
        if (addr - 2 < 0) {
            printf("Error file too big\n");
            return 1;
        }

        // Store instructions 
        pas[addr--] = OP;
        pas[addr--] = L;
        pas[addr--] = M;
        
        line_number++;
    }

    // Close file
    fclose(fp);
    
    // Exit if empty file
    if(line_number == 0){
        printf("Empty file\n");
        return 0;
    }
    

    int textEnd = addr + 1;             // Address of last memory cell used by text segment (for printing)
    int PC = PAS_SIZE - 1;              // Program counter starts at the last address
    int SP = textEnd;                   // Stack pointer starts below text segment
    int BP = SP - 1;                    // Base pointer


    // Print table header and initial values
    printf("            L     M    PC    BP    SP    STACK\n");
    printf("Initial values:       %d   %d   %d\n", PC, BP, SP);


    // Main Loop
    while(1){
        // Test for PC out of bounds
        if (PC < textEnd + 2 || PC > PAS_SIZE - 1){
            printf("Error: PC out of bounds\n");
            return 1;
        }
        int OP = pas[PC];           // Current opcode
        int L = pas[PC - 1];        // Current L
        int M = pas[PC - 2];        // Current M
        int nextPC = PC - 3;        // Next program counter
        
        // Execute instruction based on OP
        switch(OP){
            case LIT: {          // Pushes a constant value M onto the stack
                if (SP - 1 < 0){
                    printf("Error: Stack overflow\n");
                    return 1;
                }
                SP--;               // Decrement stack pointer
                pas[SP] = M;        // Push M onto stack
                break;
            }
            case OPR: {                      // Perform operation specified by M
                switch(M){
                    case 0: {                   // Return from subroutine
                        int RA = pas[BP - 2];   // Return address
                        int DL = pas[BP - 1];   // Dynamic link
                        SP = BP + 1;            // Restore stack pointer
                        BP = DL;                // Restore base pointer
                        nextPC = RA;            // Jump to return address
                        break;
                    }
                    case 1: {                   // Addition
                        int a = pas[SP + 1];    
                        int b = pas[SP];
                        pas[SP + 1] = a + b;
                        SP++;
                        break;
                    }
                    case 2: {                   // Subtraction
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = a - b;
                        SP++;  
                        break;
                    }
                    case 3: {                   // Multiplication
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = a * b;
                        SP++;  
                        break;
                    }
                    case 4: {                   // Division
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        if (b == 0){            // Check for division by zero
                            printf("Error: Division by zero\n");
                            return 1;
                        }
                        pas[SP + 1] = a / b;
                        SP++;  
                        break;
                    }
                    case 5: {                   // Equality check
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = (a == b);
                        SP++;  
                        break;
                    }
                    case 6: {                   // Inequality check
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = (a != b);
                        SP++;  
                        break;
                    }
                    case 7: {                   // Less than check
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = (a < b);
                        SP++;  
                        break;
                    }
                    case 8: {                   // Less than or equal check
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = (a <= b);
                        SP++;  
                        break;
                    }
                    case 9: {                   // Greater than check
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = (a > b);
                        SP++;  
                        break;
                    }
                    case 10: {                  // Greater than or equal check
                        int a = pas[SP + 1];
                        int b = pas[SP];
                        pas[SP + 1] = (a >= b);
                        SP++;  
                        break;
                    }
                    default:{                   // Invalid M for OPR
                        printf("Error unknown OPR");
                        return 1;
                    }

                }
                break;

            }
            case LOD: {                   // Load value to stack from offset M in L levels down
                int b = base(BP, L);         // Find base L levels doww
                int index = b - M;
                if (SP - 1 < 0 || index < 0 || index >= PAS_SIZE){      // Check for stack overflow and invalid memory access
                    printf("Error Stack overflow\n");
                    return 1;
                }
                SP--;
                pas[SP] = pas[index];
                break;
            }
            case STO: {              // Store value from stack to offset M in L levels down
                int value = pas[SP];
                SP++;
                int b = base(BP, L);                    // Find base L levels down
                int index = b - M;                      // Calculate target index
                if (index < 0 || index >= PAS_SIZE){    //check for invalid memory access
                    printf("Error Invalid memory access STO\n");
                    return 1;
                }
                pas[index] = value;
                break;
            }
            case CAL: {             // Call procedure at M
                if (SP - 3 < 0){    // Check for stack overflow
                    printf("Error Stack overflow\n");
                    return 1;
                }

                pas[SP - 1] = base(BP, L);    // Static link
                pas[SP - 2] = BP;             // Dynamic link
                pas[SP - 3] = PC - 3;         // Return address
                BP = SP - 1;                  // New base pointer
                int target = (PAS_SIZE - 1) - M;
                if (target < textEnd + 2 || target > PAS_SIZE - 1){     // Check for valid call target
                    printf("Error target out of bounds\n");
                    return 1;
                }
                nextPC = target;
                break;
            }
            case INC: {          // Allocate M locals on stack
                if (SP - M < 0){    // Check for stack overflow
                    printf("Error: Stack overflow\n");
                    return 1;
                }
                SP -= M;
                for (int i = 0; i < M; ++i){    // Initialize new locals to 0
                    pas[SP - i] = 0;
                }
                break;
            }
            case JMP: { // Jump to M
                int target = (PAS_SIZE - 1) - M;                    // Calculate target address
                if (target < textEnd+2 || target > PAS_SIZE - 1){   // Check for valid jump target
                    printf("Error: Jump target out of bounds\n");
                    return 1;
                }
                nextPC = target; 
                break;
            }
            case JPC: { // Jump to M if top stack value is 0
                int c = pas[SP];
                SP++;
                if (c == 0){
                    int target = (PAS_SIZE - 1) - M;                    // Calculate target address
                    if (target < textEnd + 2 || target > PAS_SIZE - 1){ // Check for valid jump target
                        printf("Error: Jump target out of bounds\n");
                        return 1;
                    }
                    nextPC = target; 
                }
                break;
            }
            case SYS: {          // System call
                if (M == 1){                                // Print top stack value
                    int output = pas[SP];
                    SP++;
                    printf("Output result is: %d\n", output);
                }
                
                else if (M == 2){                           // Read input to top of stack
                    int input;
                    printf("Please enter an integer: ");
                    if (scanf("%d", &input) != 1){          // Check for only 1 integer input
                        printf("Error: Invalid input\n");
                        return 1;
                    }
                    SP--;
                    pas[SP] = input;
                }
                else if (M == 3){                           // Stop program and exit
                    print_state(OP, L, M, nextPC, BP, SP, textEnd);
                    return 0;
                }
                else{
                    printf("Error: Unknown SYS M=%d\n", M); // Invalid M for SYS
                    return 1;
                }
                break;
            }
            default: {
                printf("Error: Unknown OP=%d\n", OP);       // Invalid OP
                return 1; 
            }  
        }
        PC = nextPC;
        print_state(OP, L, M, PC, BP, SP, textEnd);         // Print current state

        if (SP < 0 || SP >= PAS_SIZE) {                     // Check for stack pointer out of bounds
            printf("Error: Stack pointer out of bounds\n");
            return 1;
        }
    }

    return 0;

}