add Q16.16 real, uint32, and int32 opcodes
This commit is contained in:
		
							parent
							
								
									0cd92bb919
								
							
						
					
					
						commit
						e8d0cd5e96
					
				| 
						 | 
				
			
			@ -2,13 +2,13 @@
 | 
			
		|||
# -----------------------
 | 
			
		||||
# Native build (gcc)
 | 
			
		||||
CC_NATIVE = gcc
 | 
			
		||||
CFLAGS_NATIVE = -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter
 | 
			
		||||
CFLAGS_NATIVE = -g -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter
 | 
			
		||||
LDFLAGS_NATIVE = 
 | 
			
		||||
LDLIBS_NATIVE = 
 | 
			
		||||
 | 
			
		||||
# WASM build (emscripten)
 | 
			
		||||
CC_WASM = emcc
 | 
			
		||||
CFLAGS_WASM = -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter #-s WASM=1
 | 
			
		||||
CFLAGS_WASM = -g -std=c89 -Wall -Wextra -Werror -Wno-unused-parameter #-s WASM=1
 | 
			
		||||
LDFLAGS_WASM = #-s WASM=1
 | 
			
		||||
LDLIBS_WASM = 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
#include "debug.h"
 | 
			
		||||
 | 
			
		||||
int core_dump(Data *memory, uint32_t memory_size) {
 | 
			
		||||
int core_dump(Word *memory, uint32_t memory_size) {
 | 
			
		||||
  FILE *file = fopen("memory_dump.bin", "wb");
 | 
			
		||||
  if (!file) {
 | 
			
		||||
    perror("Failed to open file");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,6 @@
 | 
			
		|||
 | 
			
		||||
#include "vm.h"
 | 
			
		||||
 | 
			
		||||
int core_dump(Data *memory, uint32_t memory_size);
 | 
			
		||||
int core_dump(Word *memory, uint32_t memory_size);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								src/main.c
								
								
								
								
							
							
						
						
									
										20
									
								
								src/main.c
								
								
								
								
							| 
						 | 
				
			
			@ -7,7 +7,7 @@
 | 
			
		|||
/* #define MEMORY_SIZE 65536 /\* 64KB memory (adjustable) *\/ */
 | 
			
		||||
#define MEMORY_SIZE 1024
 | 
			
		||||
 | 
			
		||||
Data memory[MEMORY_SIZE] = {0}; /* Memory array */
 | 
			
		||||
Word memory[MEMORY_SIZE] = {0}; /* Memory array */
 | 
			
		||||
uint32_t pc = 1;                /* Program counter */
 | 
			
		||||
 | 
			
		||||
void mainloop() {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,15 +33,23 @@ int main() {
 | 
			
		|||
  memory[i++].u = 102;
 | 
			
		||||
  memory[i++].u = 103;
 | 
			
		||||
  memory[i++].u = 103;
 | 
			
		||||
  memory[i++].u = OP_SUB;
 | 
			
		||||
  memory[i++].u = OP_SUB_UINT;
 | 
			
		||||
  memory[i++].u = 100;
 | 
			
		||||
  memory[i++].u = 101;
 | 
			
		||||
  memory[i++].u = 100;
 | 
			
		||||
  memory[i++].u = OP_JGT_INT;
 | 
			
		||||
  memory[i++].u = OP_JGT_UINT;
 | 
			
		||||
  memory[i++].u = 100;
 | 
			
		||||
  memory[i++].u = 99;
 | 
			
		||||
  memory[i++].u = 1;
 | 
			
		||||
  memory[i++].u = OP_REAL_TO_INT;
 | 
			
		||||
  memory[i++].u = OP_REAL_TO_STRING;
 | 
			
		||||
  memory[i++].u = 103;
 | 
			
		||||
  memory[i++].u = 1;
 | 
			
		||||
  memory[i++].u = 200;
 | 
			
		||||
  memory[i++].u = OP_PRINT_STRING;
 | 
			
		||||
  memory[i++].u = 201;
 | 
			
		||||
  memory[i++].u = 1;
 | 
			
		||||
  memory[i++].u = 1;
 | 
			
		||||
  memory[i++].u = OP_REAL_TO_UINT;
 | 
			
		||||
  memory[i++].u = 103;
 | 
			
		||||
  memory[i++].u = 1;
 | 
			
		||||
  memory[i++].u = 103;
 | 
			
		||||
| 
						 | 
				
			
			@ -68,8 +76,8 @@ int main() {
 | 
			
		|||
  memory[99].u = 0;
 | 
			
		||||
  memory[100].u = 5;
 | 
			
		||||
  memory[101].u = 1;
 | 
			
		||||
  memory[102].f = 5.f;
 | 
			
		||||
  memory[103].f = 5.f;
 | 
			
		||||
  memory[102].q = FLOAT_TO_Q16_16(5.0f);
 | 
			
		||||
  memory[103].q = FLOAT_TO_Q16_16(5.0f);
 | 
			
		||||
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
  emscripten_set_main_loop(mainloop, 0, 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										284
									
								
								src/vm.c
								
								
								
								
							
							
						
						
									
										284
									
								
								src/vm.c
								
								
								
								
							| 
						 | 
				
			
			@ -1,11 +1,23 @@
 | 
			
		|||
#include "vm.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.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(Data *memory, const char *str, uint32_t length,
 | 
			
		||||
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;
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +30,7 @@ void mem_strcpy(Data *memory, const char *str, uint32_t length,
 | 
			
		|||
/**
 | 
			
		||||
 * Step to the next opcode in the vm.
 | 
			
		||||
 */
 | 
			
		||||
uint32_t step_vm(Data *memory, uint32_t memory_size, uint32_t pc) {
 | 
			
		||||
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;
 | 
			
		||||
| 
						 | 
				
			
			@ -34,147 +46,233 @@ uint32_t step_vm(Data *memory, uint32_t memory_size, uint32_t pc) {
 | 
			
		|||
  switch (opcode) {
 | 
			
		||||
  case OP_HALT:
 | 
			
		||||
    return 0;
 | 
			
		||||
  case OP_ADD:
 | 
			
		||||
  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:
 | 
			
		||||
  case OP_SUB_UINT:
 | 
			
		||||
    memory[dest_addr].u = memory[src1_addr].u - memory[src2_addr].u;
 | 
			
		||||
    return pc;
 | 
			
		||||
 | 
			
		||||
  case OP_MUL:
 | 
			
		||||
  case OP_MUL_UINT:
 | 
			
		||||
    memory[dest_addr].u = memory[src1_addr].u * memory[src2_addr].u;
 | 
			
		||||
    return pc;
 | 
			
		||||
 | 
			
		||||
  case OP_DIV:
 | 
			
		||||
  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].f = memory[src1_addr].f + memory[src2_addr].f;
 | 
			
		||||
    memory[dest_addr].q = memory[src1_addr].q + memory[src2_addr].q;
 | 
			
		||||
    return pc;
 | 
			
		||||
 | 
			
		||||
  case OP_SUB_REAL:
 | 
			
		||||
    memory[dest_addr].f = memory[src1_addr].f - memory[src2_addr].f;
 | 
			
		||||
    memory[dest_addr].q = memory[src1_addr].q - memory[src2_addr].q;
 | 
			
		||||
    return pc;
 | 
			
		||||
 | 
			
		||||
  case OP_MUL_REAL:
 | 
			
		||||
    memory[dest_addr].f = memory[src1_addr].f * memory[src2_addr].f;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
  case OP_DIV_REAL:
 | 
			
		||||
    if (memory[src2_addr].f == 0.0f) {
 | 
			
		||||
    /* 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;
 | 
			
		||||
    }
 | 
			
		||||
    memory[dest_addr].f = memory[src1_addr].f / memory[src2_addr].f;
 | 
			
		||||
    return pc;
 | 
			
		||||
  case OP_REAL_TO_INT: {
 | 
			
		||||
    memory[dest_addr].u = (uint32_t)memory[src1_addr].f;
 | 
			
		||||
 | 
			
		||||
    /* 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_INT_TO_REAL: {
 | 
			
		||||
    memory[dest_addr].f = (float)memory[src1_addr].u;
 | 
			
		||||
 | 
			
		||||
  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: {
 | 
			
		||||
    uint32_t value = memory[src1_addr].u;
 | 
			
		||||
    uint32_t value2 = memory[src2_addr].u;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value == value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, i, ==);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JGT_INT: {
 | 
			
		||||
    uint32_t value = memory[src1_addr].u;
 | 
			
		||||
    uint32_t value2 = memory[src2_addr].u;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value > value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, i, >);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JLT_INT: {
 | 
			
		||||
    uint32_t value = memory[src1_addr].u;
 | 
			
		||||
    uint32_t value2 = memory[src2_addr].u;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value < value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, i, <);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JLE_INT: {
 | 
			
		||||
    uint32_t value = memory[src1_addr].u;
 | 
			
		||||
    uint32_t value2 = memory[src2_addr].u;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value <= value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, i, <=);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JGE_INT: {
 | 
			
		||||
    uint32_t value = memory[src1_addr].u;
 | 
			
		||||
    uint32_t value2 = memory[src2_addr].u;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value >= value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, i, >=);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JEQ_REAL: {
 | 
			
		||||
    float value = memory[src1_addr].f;
 | 
			
		||||
    float value2 = memory[src2_addr].f;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value == value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, i, ==);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JGT_REAL: {
 | 
			
		||||
    float value = memory[src1_addr].f;
 | 
			
		||||
    float value2 = memory[src2_addr].f;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value > value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, u, >);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JLT_REAL: {
 | 
			
		||||
    float value = memory[src1_addr].f;
 | 
			
		||||
    float value2 = memory[src2_addr].f;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value < value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, u, <);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JGE_REAL: {
 | 
			
		||||
    float value = memory[src1_addr].f;
 | 
			
		||||
    float value2 = memory[src2_addr].f;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value >= value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, u, >=);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_JLE_REAL: {
 | 
			
		||||
    float value = memory[src1_addr].f;
 | 
			
		||||
    float value2 = memory[src2_addr].f;
 | 
			
		||||
    uint32_t jump_target = dest_addr;
 | 
			
		||||
 | 
			
		||||
    pc = (value <= value2) ? jump_target : pc;
 | 
			
		||||
    return pc;
 | 
			
		||||
    COMPARE_AND_JUMP(int32_t, u, <=);
 | 
			
		||||
  }
 | 
			
		||||
  case OP_INT_TO_STRING: {
 | 
			
		||||
    int32_t a = (int32_t)memory[src1_addr].u;
 | 
			
		||||
    char buffer[32];
 | 
			
		||||
    sprintf(buffer, "%d", a);
 | 
			
		||||
    mem_strcpy(memory, buffer, strlen(buffer), dest_addr);
 | 
			
		||||
    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: {
 | 
			
		||||
    float a = memory[src1_addr].f;
 | 
			
		||||
    char buffer[32];
 | 
			
		||||
    sprintf(buffer, "%f", a);
 | 
			
		||||
    mem_strcpy(memory, buffer, strlen(buffer), dest_addr);
 | 
			
		||||
    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: {
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +282,7 @@ uint32_t step_vm(Data *memory, uint32_t memory_size, uint32_t pc) {
 | 
			
		|||
      int ch = getchar();
 | 
			
		||||
      if (ch == '\n' || ch == EOF) {
 | 
			
		||||
        memory[buffer_addr + (length / 4)].c[length % 4] = '\0';
 | 
			
		||||
	break;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      memory[buffer_addr + (length / 4)].c[length % 4] = ch;
 | 
			
		||||
      length++;
 | 
			
		||||
| 
						 | 
				
			
			@ -199,7 +297,7 @@ uint32_t step_vm(Data *memory, uint32_t memory_size, uint32_t pc) {
 | 
			
		|||
    for (i = 0; i < length; i++) {
 | 
			
		||||
      uint8_t ch = memory[string_addr + (i / 4)].c[i % 4];
 | 
			
		||||
      if (ch == '\0')
 | 
			
		||||
	break;
 | 
			
		||||
        break;
 | 
			
		||||
      putchar(ch);
 | 
			
		||||
    }
 | 
			
		||||
    putchar('\n');
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +319,7 @@ uint32_t step_vm(Data *memory, uint32_t memory_size, uint32_t pc) {
 | 
			
		|||
        uint32_t char2 = memory[addr2 + i].u;
 | 
			
		||||
        if (char1 != char2) {
 | 
			
		||||
          equal = 0;
 | 
			
		||||
	  break;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        if ((char1 & 0xFF) == '\0' && (char2 & 0xFF) == '\0')
 | 
			
		||||
          return pc;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										41
									
								
								src/vm.h
								
								
								
								
							
							
						
						
									
										41
									
								
								src/vm.h
								
								
								
								
							| 
						 | 
				
			
			@ -3,24 +3,46 @@
 | 
			
		|||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
 | 
			
		||||
/* Constants for Q16.16 */
 | 
			
		||||
#define Q16_16_SCALE (1 << 16)
 | 
			
		||||
#define Q16_16_SCALE_FLOAT 65536.0f
 | 
			
		||||
#define Q16_16_FRACTION_MASK 0x0000FFFF
 | 
			
		||||
#define Q16_16_INT_MASK 0xFFFF0000
 | 
			
		||||
 | 
			
		||||
/* Convert float to Q16.16 (with rounding) */
 | 
			
		||||
#define FLOAT_TO_Q16_16(f) ((int32_t)((f) * Q16_16_SCALE_FLOAT + 0.5f))
 | 
			
		||||
/* Convert Q16.16 to float */
 | 
			
		||||
#define Q16_16_TO_FLOAT(q) ((float)(q) / Q16_16_SCALE_FLOAT)
 | 
			
		||||
 | 
			
		||||
typedef union {
 | 
			
		||||
  float f;
 | 
			
		||||
  uint32_t u;
 | 
			
		||||
  char c[4];
 | 
			
		||||
} Data;
 | 
			
		||||
  int32_t i;  /* Integers */
 | 
			
		||||
  int32_t q;  /* Q16.16 (32-bit fixed-point) */
 | 
			
		||||
  uint32_t u; /* Unsigned integers */
 | 
			
		||||
  char c[4];  /* 4 Byte char array for string packing */
 | 
			
		||||
} Word;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
  OP_HALT,           /* terminate execution */
 | 
			
		||||
  OP_ADD,            /* dest = src1 + src2  */
 | 
			
		||||
  OP_SUB,            /* dest = src1 - src2  */
 | 
			
		||||
  OP_MUL,            /* dest = src1 * src2  */
 | 
			
		||||
  OP_DIV,            /* dest = src1 / src2  */
 | 
			
		||||
  OP_ADD_INT,        /* dest = src1 + src2  */
 | 
			
		||||
  OP_SUB_INT,        /* dest = src1 - src2  */
 | 
			
		||||
  OP_MUL_INT,        /* dest = src1 * src2  */
 | 
			
		||||
  OP_DIV_INT,        /* dest = src1 / src2  */
 | 
			
		||||
  OP_JEQ_INT,        /* jump to address dest if src1 as int == src2 as int */
 | 
			
		||||
  OP_JGT_INT,        /* jump to address dest if src1 as int > src2 as int*/
 | 
			
		||||
  OP_JLT_INT,        /* jump to address dest if src1 as int < src2 as int */
 | 
			
		||||
  OP_JLE_INT,        /* jump to address dest if src1 as int <= src2 as int */
 | 
			
		||||
  OP_JGE_INT,        /* jump to address dest if src1 as int >= src2 as int*/
 | 
			
		||||
  OP_INT_TO_REAL,    /* dest = src1 as f32  */
 | 
			
		||||
  OP_ADD_UINT,       /* dest = src1 + src2  */
 | 
			
		||||
  OP_SUB_UINT,       /* dest = src1 - src2  */
 | 
			
		||||
  OP_MUL_UINT,       /* dest = src1 * src2  */
 | 
			
		||||
  OP_DIV_UINT,       /* dest = src1 / src2  */
 | 
			
		||||
  OP_JEQ_UINT,       /* jump to address dest if src1 as int == src2 as uint */
 | 
			
		||||
  OP_JGT_UINT,       /* jump to address dest if src1 as int > src2 as uint*/
 | 
			
		||||
  OP_JLT_UINT,       /* jump to address dest if src1 as int < src2 as uint */
 | 
			
		||||
  OP_JLE_UINT,       /* jump to address dest if src1 as int <= src2 as uint */
 | 
			
		||||
  OP_JGE_UINT,       /* jump to address dest if src1 as int >= src2 as uint*/
 | 
			
		||||
  OP_UINT_TO_REAL,   /* dest = src1 as f32  */
 | 
			
		||||
  OP_ADD_REAL,       /* dest = src1 + src2  */
 | 
			
		||||
  OP_SUB_REAL,       /* dest = src1 - src2  */
 | 
			
		||||
  OP_MUL_REAL,       /* dest = src1 * src2  */
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +53,7 @@ typedef enum {
 | 
			
		|||
  OP_JLT_REAL,       /* jump to address dest if src1 as real < src2 as real */
 | 
			
		||||
  OP_JLE_REAL,       /* jump to address dest if src1 as real <= src2 as real */
 | 
			
		||||
  OP_REAL_TO_INT,    /* dest = src1 as int  */
 | 
			
		||||
  OP_REAL_TO_UINT,   /* dest = src1 as int  */
 | 
			
		||||
  OP_MOV,            /* dest = src1	   */
 | 
			
		||||
  OP_JMP,            /* jump to address src1 unconditionally */
 | 
			
		||||
  OP_INT_TO_STRING,  /* dest = src1 as str */
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +63,6 @@ typedef enum {
 | 
			
		|||
  OP_CMP_STRING,     /* dest = src1 */
 | 
			
		||||
} Opcode;
 | 
			
		||||
 | 
			
		||||
uint32_t step_vm(Data *memory, uint32_t memory_size, uint32_t pc);
 | 
			
		||||
uint32_t step_vm(Word *memory, uint32_t memory_size, uint32_t pc);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue