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

#define TRUE 1
#define FALSE 0
#define MAX_PROCESSES 4
#define MAX_REGISTERS 2
#define MAX_PAGES 64
#define PAGE_SIZE 32
#define TLB_SIZE 16

// Output file
FILE* output_file;

// TLB replacement strategy (FIFO or LRU)
char* strategy;

// State variables
int define_called = FALSE;
int current_pid = 0;
int registers[MAX_PROCESSES][MAX_REGISTERS];
int page_table[MAX_PROCESSES][MAX_PAGES];
int memory[1024];
int process_tlb_index[MAX_PROCESSES] = {0, 0, 0, 0};
int tlb_entry_number[MAX_PROCESSES][MAX_PAGES];
int off_bits = 0;

// TLB structure
typedef struct {
int vpn;
int pfn;
int valid;
int timestamp;
} TLBEntry;

TLBEntry tlb[MAX_PROCESSES][TLB_SIZE];
int tlb_index[MAX_PROCESSES] = {0};

// Function to tokenize input
char** tokenize_input(char* input) {
char** tokens = NULL;
char* token = strtok(input, " ");
int num_tokens = 0;

while (token != NULL) {
    num_tokens++;
    tokens = realloc(tokens, num_tokens * sizeof(char*));
    tokens[num_tokens - 1] = malloc(strlen(token) + 1);
    strcpy(tokens[num_tokens - 1], token);
    token = strtok(NULL, " ");
}

num_tokens++;
tokens = realloc(tokens, num_tokens * sizeof(char*));
tokens[num_tokens - 1] = NULL;

return tokens;
}

//TLB lookup
int tlb_lookup(int vpn, int* pfn) {
for (int i = 0; i < TLB_SIZE; i++) {
if (tlb[current_pid][i].valid && tlb[current_pid][i].vpn == vpn) {
*pfn = tlb[current_pid][i].pfn;
fprintf(output_file, "Current PID: %d. Translating. Lookup for VPN %d hit in TLB entry %d. PFN is %d\n",
current_pid, vpn, i, *pfn);
return TRUE;
}
}

// On TLB miss, output miss message and do not update TLB here directly
fprintf(output_file, "Current PID: %d. Translating. Lookup for VPN %d caused a TLB miss\n", current_pid, vpn);
return FALSE;
}

int global_tlb_index = 0;

void tlb_update(int vpn, int pfn) {
int entry_index = global_tlb_index % TLB_SIZE;
tlb[current_pid][entry_index].vpn = vpn;
tlb[current_pid][entry_index].pfn = pfn;
tlb[current_pid][entry_index].valid = TRUE;
tlb_entry_number[current_pid][vpn] = entry_index;
global_tlb_index++;
}

// Function to handle loading an immediate into a register
void handle_load(char** tokens) {
if (strcmp(tokens[1], "r1") == 0 || strcmp(tokens[1], "r2") == 0) {
int reg_index = (strcmp(tokens[1], "r1") == 0) ? 0 : 1;
int value = atoi(tokens[2] + 1);
registers[current_pid][reg_index] = value;
fprintf(output_file, "Current PID: %d. Loaded immediate %d into register %s\n", current_pid, value, tokens[1]);
} else {
fprintf(output_file, "Current PID: %d. Error: invalid register operand %s\n", current_pid, tokens[1]);
}
}

// Function to handle add operation
void handle_add() {
int result = registers[current_pid][0] + registers[current_pid][1];
fprintf(output_file, "Current PID: %d. Added contents of registers r1 (%d) and r2 (%d). Result: %d\n",
current_pid, registers[current_pid][0], registers[current_pid][1], result);
registers[current_pid][0] = result;
}

// Function to map a virtual page to a physical frame
void handle_map(char** tokens) {
int vpn = atoi(tokens[1]);
int pfn = atoi(tokens[2]);

// Update the page table
page_table[current_pid][vpn] = pfn;

// Update the TLB entry with the new mapping
tlb_update(vpn, pfn);

fprintf(output_file, "Current PID: %d. Mapped virtual page number %d to physical frame number %d\n", current_pid, vpn, pfn);
}

// Function to unmap a virtual page
void handle_unmap(char** tokens) {
int vpn = atoi(tokens[1]);

// Invalidate any TLB entry for this VPN
for (int i = 0; i < TLB_SIZE; i++) {
    if (tlb[current_pid][i].vpn == vpn && tlb[current_pid][i].valid) {
        tlb[current_pid][i].valid = FALSE;
    }
}

// Invalidate in the page table
page_table[current_pid][vpn] = -1;
fprintf(output_file, "Current PID: %d. Unmapped virtual page number %d\n", current_pid, vpn);
}

// Function to handle store operation
void handle_store(char** tokens) {
int addr = atoi(tokens[1]);
int reg_value = (tokens[2][0] == '#') ? atoi(tokens[2] + 1) : registers[current_pid][atoi(&tokens[2][1]) - 1];
int vpn = addr >> off_bits; // Calculate VPN by shifting address right by off_bits
int offset = addr & ((1 << off_bits) - 1); // Offset mask based on off_bits

int pfn;
if (!tlb_lookup(vpn, &pfn)) {
    // If TLB miss, fall back to page table
    pfn = page_table[current_pid][vpn];
    if (pfn != -1) {
        tlb_update(vpn, pfn); // Update TLB if mapping is in page table
        fprintf(output_file, "Current PID: %d. Translating. Successfully mapped VPN %d to PFN %d\n", current_pid, vpn, pfn);
    } else {
        fprintf(output_file, "Current PID: %d. Error: attempted to store to unmapped address %d\n", current_pid, addr);
        return;
    }
}

int phys_addr = (pfn << off_bits) | offset;
memory[phys_addr] = reg_value;

if (tokens[2][0] == '#') {
    fprintf(output_file, "Current PID: %d. Stored immediate %d into location %d\n", current_pid, reg_value, addr);
} else {
    fprintf(output_file, "Current PID: %d. Stored value of register %s (%d) into location %d\n", current_pid, tokens[2], reg_value, addr);
}
}

// handle_load_mem function
void handle_load_mem(char** tokens) {
int addr = atoi(tokens[2]);
int vpn = addr >> off_bits; // Calculate VPN by shifting address right by off_bits
int offset = addr & ((1 << off_bits) - 1); // Offset mask based on off_bits

int pfn;
if (!tlb_lookup(vpn, &pfn)) {
    // If TLB miss, fall back to page table
    pfn = page_table[current_pid][vpn];
    if (pfn != -1) {
        tlb_update(vpn, pfn); // Update TLB if mapping is in page table
        fprintf(output_file, "Current PID: %d. Translating. Successfully mapped VPN %d to PFN %d\n", current_pid, vpn, pfn);
    } else {
        fprintf(output_file, "Current PID: %d. Error: attempted to load from unmapped address %d\n", current_pid, addr);
        return;
    }
}

int phys_addr = (pfn << off_bits) | offset;
int reg_index = (strcmp(tokens[1], "r1") == 0) ? 0 : 1;
registers[current_pid][reg_index] = memory[phys_addr];
fprintf(output_file, "Current PID: %d. Loaded value of location %d (%d) into register %s\n", current_pid, addr, memory[phys_addr], tokens[1]);
}

// Function to inspect page table entry
void handle_pinspect(char** tokens) {
int vpn = atoi(tokens[1]);
int pfn = page_table[current_pid][vpn];
fprintf(output_file, "Current PID: %d. Inspected page table entry %d. Physical frame number: %d. Valid: %d\n",
current_pid, vpn, pfn, (pfn != -1));
}

// Function to inspect TLB entry
void handle_tinspect(char** tokens) {
int tlbn = atoi(tokens[1]);
if (tlbn >= 0 && tlbn < TLB_SIZE) {
TLBEntry entry = tlb[current_pid][tlbn];
fprintf(output_file, "Current PID: %d. Inspected TLB entry %d. VPN: %d. PFN: %d. Valid: %d. PID: %d\n",
current_pid, tlbn, entry.vpn, entry.pfn, entry.valid, current_pid);
}
}

// Function to inspect physical memory location
void handle_linspect(char** tokens) {
int pl = atoi(tokens[1]);
if (pl >= 0 && pl < 1024) { // Assuming 1024 is max physical memory size
fprintf(output_file, "Current PID: %d. Inspected physical location %d. Content: %d\n",
current_pid, pl, memory[pl]);
}
}

// Function to inspect register
void handle_rinspect(char** tokens) {
if (strcmp(tokens[1], "r1") == 0 || strcmp(tokens[1], "r2") == 0) {
int reg_index = (strcmp(tokens[1], "r1") == 0) ? 0 : 1;
fprintf(output_file, "Current PID: %d. Inspected register %s. Content: %d\n",
current_pid, tokens[1], registers[current_pid][reg_index]);
} else {
fprintf(output_file, "Current PID: %d. Error: invalid register operand %s\n", current_pid, tokens[1]);
}
}

int main(int argc, char* argv[]) {
const char usage[] = "Usage: memsym.out <strategy> <input trace> <output trace>\n";
char* input_trace;
char* output_trace;
char buffer[1024];

// Parse command line arguments
if (argc != 4) {
    printf("%s", usage);
    return 1;
}
strategy = argv[1];
input_trace = argv[2];
output_trace = argv[3];

// Initialize memory and page tables
memset(memory, 0, sizeof(memory));
for (int i = 0; i < MAX_PROCESSES; i++) {
    for (int j = 0; j < MAX_PAGES; j++) {
        page_table[i][j] = -1;
    }
}
memset(tlb_entry_number, 0, sizeof(tlb_entry_number));

// Open input and output files
FILE* input_file = fopen(input_trace, "r");
output_file = fopen(output_trace, "w");  

while (!feof(input_file)) {
    // Read input file line by line
    char *rez = fgets(buffer, sizeof(buffer), input_file);
    if (!rez) {
        fprintf(stderr, "Reached end of trace. Exiting...\n");
        return -1;
    } else {
        buffer[strlen(buffer) - 1] = '\0';
    }
    char** tokens = tokenize_input(buffer);

    // Ignore comments
    if (tokens[0] && tokens[0][0] == '%') {
        for (int i = 0; tokens[i] != NULL; i++)
            free(tokens[i]);
        free(tokens);
        continue;
    }

    // Implement the memory simulator
    if (tokens[0] != NULL) {
        if (strcmp(tokens[0], "define") == 0) {
            if (define_called) {
                fprintf(output_file, "Current PID: 0. Error: multiple calls to define in the same trace\n");
                break;
            } else {
                define_called = TRUE;
                off_bits = atoi(tokens[1]);  // Capture OFF bits
                int PFN = atoi(tokens[2]);
                int VPN = atoi(tokens[3]);
                fprintf(output_file, "Current PID: 0. Memory instantiation complete. OFF bits: %d. PFN bits: %d. VPN bits: %d\n", off_bits, PFN, VPN);
            }
        } else if (strcmp(tokens[0], "ctxswitch") == 0) {
            if (!define_called) {
                fprintf(output_file, "Current PID: 0. Error: attempt to execute instruction before define\n");
                break;
            }
            int new_pid = atoi(tokens[1]);
            if (new_pid < 0 || new_pid >= MAX_PROCESSES) {
                fprintf(output_file, "Current PID: %d. Invalid context switch to process %d\n", current_pid, new_pid);
                break;
            } else {
                current_pid = new_pid;
                fprintf(output_file, "Current PID: %d. Switched execution context to process: %d\n", current_pid, new_pid);
            }
        } else if (strcmp(tokens[0], "load") == 0) {
            if (!define_called) {
                fprintf(output_file, "Current PID: 0. Error: attempt to execute instruction before define\n");
                break;
            }
            if (tokens[2][0] == '#') {
                handle_load(tokens);
            } else {
                handle_load_mem(tokens);
            }
        } else if (strcmp(tokens[0], "add") == 0) {
            if (!define_called) {
                fprintf(output_file, "Current PID: 0. Error: attempt to execute instruction before define\n");
                break;
            }
            handle_add();
        } else if (strcmp(tokens[0], "map") == 0) {
            handle_map(tokens);
        } else if (strcmp(tokens[0], "unmap") == 0) {
            handle_unmap(tokens);
        } else if (strcmp(tokens[0], "store") == 0) {
            handle_store(tokens);
        } else if (strcmp(tokens[0], "pinspect") == 0) {
            handle_pinspect(tokens);
        } else if (strcmp(tokens[0], "tinspect") == 0) {
            handle_tinspect(tokens);
        } else if (strcmp(tokens[0], "linspect") == 0) {
            handle_linspect(tokens);
        } else if (strcmp(tokens[0], "rinspect") == 0) {
            handle_rinspect(tokens);
        } else {
            fprintf(output_file, "Current PID: 0. Error: attempt to execute instruction before define\n");
            break;
        }
    }

    // Deallocate tokens
    for (int i = 0; tokens[i] != NULL; i++)
        free(tokens[i]);
    free(tokens);
}

// Close input and output files
fclose(input_file);
fclose(output_file);

return 0;
}

fix this

Running test batch 1: basic tests
Running test Test 1.1: empty trace... PASSED
Running test Test 1.2: define... PASSED
Running test Test 1.3: double-defines... PASSED
Running test Test 1.4: context switching... PASSED
Running test Test 1.5: invalid context switch... PASSED
Done!
Running test batch 2: instructions tests
Running test Test 2.1: load immediate into register... PASSED
Running test Test 2.2: load immediate into invalid register... PASSED
Running test Test 2.3: instruction without define... PASSED
Running test Test 2.4: add... PASSED
Running test Test 2.5: add with context switch... PASSED
Done!
Running test batch 3: mapping tests
Running test Test 3.1: basic mapping... PASSED
Running test Test 3.2: basic unmapping... PASSED
Running test Test 3.3: store/load in mapped memory... PASSED
Running test Test 3.4: load from unmapped memory... FAILED
Done!
Running test batch 4: inspection tests
Running test Test 4.1: inspecting registers... PASSED
Running test Test 4.2: inspecting the page table... FAILED
Done!
Running test batch 5: complex tests
Running test Test 5.1: mapping all the thingss... FAILED
Done!