undar-lang/src/vm.c

334 lines
8.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}