#include "vm.h" #define MAX_LEN_INT32 11 #define MAX_INT32 2147483647 #define MIN_INT32 -2147483648 const char radix_set[11] = "0123456789"; #define COMPARE_AND_JUMP(type, accessor, op) \ do { \ type value = memory[src1_addr].accessor; \ type value2 = memory[src2_addr].accessor; \ uint32_t jump_target = dest_addr; \ pc = (value op value2) ? jump_target : pc; \ return pc; \ } while (0) /** * String copy in data memory. */ void mem_strcpy(Word *memory, const char *str, uint32_t length, uint32_t dest_addr) { memory[dest_addr].u = length; uint32_t buffer_addr = dest_addr + 1; uint32_t i; for (i = 0; i < length; i++) { memory[buffer_addr + (i / 4)].c[i % 4] = str[i]; } } /** * Step to the next opcode in the vm. */ uint32_t step_vm(Word *memory, uint32_t memory_size, uint32_t pc) { Opcode opcode = memory[pc].u; uint32_t src1_addr = memory[pc + 1].u; uint32_t src2_addr = memory[pc + 2].u; uint32_t dest_addr = memory[pc + 3].u; pc += 4; /* Advance to next instruction */ if (src1_addr >= memory_size || src2_addr >= memory_size || dest_addr >= memory_size) { printf("Invalid memory address!\n"); return 0; } switch (opcode) { case OP_HALT: return 0; case OP_ADD_INT: memory[dest_addr].u = memory[src1_addr].i + memory[src2_addr].i; return pc; case OP_SUB_INT: memory[dest_addr].u = memory[src1_addr].i - memory[src2_addr].i; return pc; case OP_MUL_INT: memory[dest_addr].u = memory[src1_addr].i * memory[src2_addr].i; return pc; case OP_DIV_INT: memory[dest_addr].u = memory[src1_addr].i / memory[src2_addr].i; return pc; case OP_ADD_UINT: memory[dest_addr].u = memory[src1_addr].u + memory[src2_addr].u; return pc; case OP_SUB_UINT: memory[dest_addr].u = memory[src1_addr].u - memory[src2_addr].u; return pc; case OP_MUL_UINT: memory[dest_addr].u = memory[src1_addr].u * memory[src2_addr].u; return pc; case OP_DIV_UINT: memory[dest_addr].u = memory[src1_addr].u / memory[src2_addr].u; return pc; case OP_ADD_REAL: memory[dest_addr].q = memory[src1_addr].q + memory[src2_addr].q; return pc; case OP_SUB_REAL: memory[dest_addr].q = memory[src1_addr].q - memory[src2_addr].q; return pc; case OP_MUL_REAL: { int32_t a = memory[src1_addr].q; int32_t b = memory[src2_addr].q; /* Extract integer and fractional parts */ int32_t a_int = a & Q16_16_INT_MASK; int32_t a_frac = a & Q16_16_FRACTION_MASK; int32_t b_int = b & Q16_16_INT_MASK; int32_t b_frac = b & Q16_16_FRACTION_MASK; /* Compute terms with explicit casting to prevent overflow */ int32_t term1 = a_int * b_int; /* Integer × Integer */ int32_t term2 = a_int * b_frac; /* Integer × Fractional */ int32_t term3 = a_frac * b_int; /* Fractional × Integer */ int32_t term4 = a_frac * b_frac; /* Fractional × Fractional */ /* Scale terms back to Q16.16 (avoid shifting negative values) */ int32_t scaled_term1 = term1; int32_t scaled_term2 = (term2 >> 16); int32_t scaled_term3 = (term3 >> 16); int32_t scaled_term4 = (term4 >> 16); /* 16-bit shift for 1/65536 scaling */ /* Combine scaled terms with overflow checks */ int32_t result = scaled_term1 + scaled_term2 + scaled_term3 + scaled_term4; memory[dest_addr].q = result; return pc; } case OP_DIV_REAL: { int32_t a = memory[src1_addr].q; int32_t b = memory[src2_addr].q; if (b == 0) { printf("Division by zero error at address %d\n", pc - 4); return 0; } /* Check for overflow */ int32_t a_int = a >> 16; int32_t b_int = b >> 16; /* If a_int / b_int would overflow, clamp */ if (b_int == 0 || (a_int > 0 && b_int < 0 && a_int > (MAX_INT32 / b_int)) || (a_int < 0 && b_int > 0 && a_int < (MIN_INT32 / b_int))) { return (a < 0) ? MIN_INT32 : MAX_INT32; } /* Scale numerator and divide */ int32_t scaled_a = a << 16; int32_t result = scaled_a / b; memory[dest_addr].q = result; return pc; } case OP_REAL_TO_INT: memory[dest_addr].i = (int32_t)(memory[src1_addr].q >> 16); return pc; case OP_INT_TO_REAL: memory[dest_addr].q = (int32_t)(memory[src1_addr].i << 16); return pc; case OP_REAL_TO_UINT: memory[dest_addr].u = (int32_t)(memory[src1_addr].q >> 16); return pc; case OP_UINT_TO_REAL: memory[dest_addr].q = (uint32_t)(memory[src1_addr].u << 16); return pc; case OP_MOV: memory[dest_addr] = memory[src1_addr]; return pc; case OP_JMP: pc = src1_addr; /* Jump to address */ return pc; case OP_JEQ_UINT: { COMPARE_AND_JUMP(uint32_t, u, ==); } case OP_JGT_UINT: { COMPARE_AND_JUMP(uint32_t, u, >); } case OP_JLT_UINT: { COMPARE_AND_JUMP(uint32_t, u, <); } case OP_JLE_UINT: { COMPARE_AND_JUMP(uint32_t, u, <=); } case OP_JGE_UINT: { COMPARE_AND_JUMP(uint32_t, u, >=); } case OP_JEQ_INT: { COMPARE_AND_JUMP(int32_t, i, ==); } case OP_JGT_INT: { COMPARE_AND_JUMP(int32_t, i, >); } case OP_JLT_INT: { COMPARE_AND_JUMP(int32_t, i, <); } case OP_JLE_INT: { COMPARE_AND_JUMP(int32_t, i, <=); } case OP_JGE_INT: { COMPARE_AND_JUMP(int32_t, i, >=); } case OP_JEQ_REAL: { COMPARE_AND_JUMP(int32_t, i, ==); } case OP_JGT_REAL: { COMPARE_AND_JUMP(int32_t, u, >); } case OP_JLT_REAL: { COMPARE_AND_JUMP(int32_t, u, <); } case OP_JGE_REAL: { COMPARE_AND_JUMP(int32_t, u, >=); } case OP_JLE_REAL: { COMPARE_AND_JUMP(int32_t, u, <=); } case OP_INT_TO_STRING: { int32_t v = memory[src1_addr].i; char buffer[MAX_LEN_INT32]; int64_t n = v; bool neg = n < 0; if (neg) n = -n; int i = MAX_LEN_INT32; do { buffer[--i] = radix_set[n % 10]; n /= 10; } while (n > 0); if (neg) buffer[--i] = '-'; /* Ensure at least one digit is written for 0 */ if (v == 0) buffer[--i] = '0'; /* Copy from buffer[i] to buffer + MAX_LEN_INT32 */ mem_strcpy(memory, buffer + i, MAX_LEN_INT32 - i, dest_addr); return pc; } case OP_REAL_TO_STRING: { int32_t q = memory[src1_addr].q; char buffer[32]; /* Max 10 digits for integer part + 6 for fractional + sign + '.' + null */ int i = 0, j = 0; /* Handle negative numbers */ if (q < 0) { buffer[i++] = '-'; q = -q; } /* Extract integer part (top 16 bits) */ uint32_t int_part = q >> 16; /* Extract fractional part (bottom 16 bits) */ uint32_t frac_part = q & 0xFFFF; /* Convert integer part to string (reverse order) */ if (int_part == 0) { buffer[i++] = radix_set[0]; } else { char tmp[16]; int tmp_i = 0; while (int_part > 0) { tmp[tmp_i++] = radix_set[int_part % 10]; int_part /= 10; } while (tmp_i > 0) { buffer[i++] = tmp[--tmp_i]; } } /* Convert fractional part to 6 decimal digits */ buffer[i++] = '.'; for (j = 0; j < 6; j++) { frac_part *= 10; buffer[i++] = radix_set[frac_part >> 16]; /* Get integer part of (frac_part * 10) */ frac_part &= 0xFFFF; /* Keep fractional part for next digit */ } /* Null-terminate */ buffer[i] = '\0'; /* Copy to memory */ mem_strcpy(memory, buffer, i, dest_addr); return pc; } case OP_READ_STRING: { uint32_t buffer_addr = dest_addr + 1; uint32_t length = 0; while (1) { int ch = getchar(); if (ch == '\n' || ch == EOF) { memory[buffer_addr + (length / 4)].c[length % 4] = '\0'; break; } memory[buffer_addr + (length / 4)].c[length % 4] = ch; length++; } memory[dest_addr].u = length; return pc; } case OP_PRINT_STRING: { uint32_t string_addr = src1_addr; uint32_t length = memory[src1_addr - 1].u; uint32_t i; for (i = 0; i < length; i++) { uint8_t ch = memory[string_addr + (i / 4)].c[i % 4]; if (ch == '\0') break; putchar(ch); } putchar('\n'); return pc; } case OP_CMP_STRING: { uint32_t addr1 = src1_addr; uint32_t addr2 = src2_addr; uint32_t length1 = memory[src1_addr - 1].u; uint32_t length2 = memory[src2_addr - 1].u; uint32_t equal = 1; if (length1 != length2) { equal = 0; } else { uint32_t i = 0; while (i < length1) { uint32_t char1 = memory[addr1 + i].u; uint32_t char2 = memory[addr2 + i].u; if (char1 != char2) { equal = 0; break; } if ((char1 & 0xFF) == '\0' && (char2 & 0xFF) == '\0') return pc; } } memory[dest_addr].u = equal; return pc; } } return 0; }