tuxanci2-engine
Game engine for tuxanci2
Log | Files | Refs | README | LICENSE

vm.c (13833B)


      1 #include <stdint.h>
      2 #include <stdlib.h>
      3 #include "vm.h"
      4 #include "syscall.h"
      5 #include "abi.h"
      6 #include "api.h"
      7 #include "tab.h"
      8 #include "screen.h"
      9 #include "scene.h"
     10 
     11 #define OP_LOAD     0x03
     12 #define OP_MISC_MEM 0x0F
     13 #define OP_OP_IMM   0x13
     14 #define OP_AUIPC    0x17
     15 #define OP_STORE    0x23
     16 #define OP_OP       0x33
     17 #define OP_LUI      0x37
     18 #define OP_BRANCH   0x63
     19 #define OP_JALR     0x67
     20 #define OP_JAL      0x6F
     21 #define OP_SYSTEM   0x73
     22 
     23 static int32_t sign_extend(uint32_t value, uint32_t bits) {
     24 	uint32_t mask = (uint32_t)1 << (bits - 1);
     25 
     26 	return (int32_t)((value ^ mask) - mask);
     27 }
     28 
     29 static uint32_t shift_right_arith(uint32_t value, uint32_t amount) {
     30 	uint32_t result = value >> amount;
     31 
     32 	if (amount != 0 && (value & 0x80000000u) != 0) {
     33 		result |= ~(0xFFFFFFFFu >> amount);
     34 	}
     35 
     36 	return result;
     37 }
     38 
     39 static uint32_t mul_high_unsigned(uint32_t a, uint32_t b) {
     40 	uint32_t a_lo = a & 0xFFFFu;
     41 	uint32_t a_hi = a >> 16;
     42 	uint32_t b_lo = b & 0xFFFFu;
     43 	uint32_t b_hi = b >> 16;
     44 	uint32_t lo_lo = a_lo * b_lo;
     45 	uint32_t hi_lo = a_hi * b_lo;
     46 	uint32_t lo_hi = a_lo * b_hi;
     47 	uint32_t hi_hi = a_hi * b_hi;
     48 	uint32_t cross = (lo_lo >> 16) + (hi_lo & 0xFFFFu) + lo_hi;
     49 
     50 	return hi_hi + (hi_lo >> 16) + (cross >> 16);
     51 }
     52 
     53 static uint32_t mul_high_signed(uint32_t a, uint32_t b) {
     54 	uint32_t result = mul_high_unsigned(a, b);
     55 
     56 	if (a & 0x80000000u) {
     57 		result -= b;
     58 	}
     59 
     60 	if (b & 0x80000000u) {
     61 		result -= a;
     62 	}
     63 
     64 	return result;
     65 }
     66 
     67 static uint32_t mul_high_signed_unsigned(uint32_t a, uint32_t b) {
     68 	uint32_t result = mul_high_unsigned(a, b);
     69 
     70 	if (a & 0x80000000u) {
     71 		result -= b;
     72 	}
     73 
     74 	return result;
     75 }
     76 
     77 static uint32_t div_signed(uint32_t a, uint32_t b) {
     78 	if (b == 0) {
     79 		return 0xFFFFFFFFu;
     80 	}
     81 
     82 	if (a == 0x80000000u && b == 0xFFFFFFFFu) {
     83 		return a;
     84 	}
     85 
     86 	return (uint32_t)((int32_t)a / (int32_t)b);
     87 }
     88 
     89 static uint32_t rem_signed(uint32_t a, uint32_t b) {
     90 	if (b == 0) {
     91 		return a;
     92 	}
     93 
     94 	if (a == 0x80000000u && b == 0xFFFFFFFFu) {
     95 		return 0;
     96 	}
     97 
     98 	return (uint32_t)((int32_t)a % (int32_t)b);
     99 }
    100 
    101 static bool in_bounds(const ta_vm *vm, uint32_t addr, uint32_t size) {
    102 	return size <= vm->mem_size && addr <= vm->mem_size - size;
    103 }
    104 
    105 bool ta_vm_read8(const ta_vm *vm, uint32_t addr, uint8_t *out) {
    106 	if (!in_bounds(vm, addr, 1)) {
    107 		return false;
    108 	}
    109 
    110 	*out = vm->mem[addr];
    111 
    112 	return true;
    113 }
    114 
    115 bool ta_vm_read32(const ta_vm *vm, uint32_t addr, uint32_t *out) {
    116 	if (!in_bounds(vm, addr, 4)) {
    117 		return false;
    118 	}
    119 
    120 	const uint8_t *p = vm->mem + addr;
    121 
    122 	*out = (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
    123 
    124 	return true;
    125 }
    126 
    127 bool ta_vm_write32(ta_vm *vm, uint32_t addr, uint32_t value) {
    128 	if (!in_bounds(vm, addr, 4)) {
    129 		return false;
    130 	}
    131 
    132 	uint8_t *p = vm->mem + addr;
    133 
    134 	p[0] = (uint8_t)value;
    135 	p[1] = (uint8_t)(value >> 8);
    136 	p[2] = (uint8_t)(value >> 16);
    137 	p[3] = (uint8_t)(value >> 24);
    138 
    139 	return true;
    140 }
    141 
    142 static bool load_value(const ta_vm *vm, uint32_t addr, uint32_t size, uint32_t *out) {
    143 	if (!in_bounds(vm, addr, size)) {
    144 		return false;
    145 	}
    146 
    147 	const uint8_t *p = vm->mem + addr;
    148 	uint32_t value = 0;
    149 
    150 	for (uint32_t i = 0; i < size; i++) {
    151 		value |= (uint32_t)p[i] << (i * 8);
    152 	}
    153 
    154 	*out = value;
    155 
    156 	return true;
    157 }
    158 
    159 static bool store_value(ta_vm *vm, uint32_t addr, uint32_t size, uint32_t value) {
    160 	if (!in_bounds(vm, addr, size)) {
    161 		return false;
    162 	}
    163 
    164 	uint8_t *p = vm->mem + addr;
    165 
    166 	for (uint32_t i = 0; i < size; i++) {
    167 		p[i] = (uint8_t)(value >> (i * 8));
    168 	}
    169 
    170 	return true;
    171 }
    172 
    173 void *ta_vm_translate_pointer(const ta_vm *vm, uint32_t addr, uint32_t size) {
    174 	if (size == 0 || !in_bounds(vm, addr, size)) {
    175 		return NULL;
    176 	}
    177 
    178 	return vm->mem + addr;
    179 }
    180 
    181 const char *ta_vm_cstr(const ta_vm *vm, uint32_t addr) {
    182 	const char *string = (const char *)ta_vm_translate_pointer(vm, addr, 1);
    183 
    184 	if (!string) {
    185 		return NULL;
    186 	}
    187 
    188 	for (uint32_t offset = addr; offset < vm->mem_size; offset++) {
    189 		if (vm->mem[offset] == 0) {
    190 			return string;
    191 		}
    192 	}
    193 
    194 	return NULL;
    195 }
    196 
    197 void ta_vm_init(ta_vm *vm, uint8_t *mem, uint32_t mem_size, ta_vm_ecall ecall, void *user) {
    198 	vm->mem = mem;
    199 	vm->mem_size = mem_size;
    200 	vm->ecall = ecall;
    201 	vm->user = user;
    202 	ta_vm_reset(vm, 0, mem_size);
    203 }
    204 
    205 void ta_vm_reset(ta_vm *vm, uint32_t entry, uint32_t stack_top) {
    206 	for (uint32_t i = 0; i < 32; i++) {
    207 		vm->regs[i] = 0;
    208 	}
    209 
    210 	vm->regs[TA_VM_REG_SP] = stack_top & ~(uint32_t)15;
    211 	vm->pc = entry;
    212 	vm->steps = 0;
    213 }
    214 
    215 ta_vm_status ta_vm_run(ta_vm *vm, uint32_t max_steps) {
    216 	uint32_t *x = vm->regs;
    217 	uint32_t pc = vm->pc;
    218 	uint32_t steps = 0;
    219 	ta_vm_status status = TA_VM_RUNNING;
    220 
    221 	while (steps < max_steps) {
    222 		uint32_t inst;
    223 
    224 		if ((pc & 3) != 0) {
    225 			status = TA_VM_FAULT_MISALIGNED;
    226 			break;
    227 		}
    228 
    229 		if (!ta_vm_read32(vm, pc, &inst)) {
    230 			status = TA_VM_FAULT_MEMORY;
    231 			break;
    232 		}
    233 
    234 		uint32_t opcode = inst & 0x7F;
    235 		uint32_t rd = (inst >> 7) & 31;
    236 		uint32_t funct3 = (inst >> 12) & 7;
    237 		uint32_t rs1 = (inst >> 15) & 31;
    238 		uint32_t rs2 = (inst >> 20) & 31;
    239 		uint32_t funct7 = inst >> 25;
    240 		uint32_t a = x[rs1];
    241 		uint32_t b = x[rs2];
    242 		uint32_t next = pc + 4;
    243 		uint32_t result = 0;
    244 		bool write = false;
    245 
    246 		switch (opcode) {
    247 			case OP_LUI: {
    248 				result = inst & 0xFFFFF000u;
    249 				write = true;
    250 				break;
    251 			}
    252 			case OP_AUIPC: {
    253 				result = pc + (inst & 0xFFFFF000u);
    254 				write = true;
    255 				break;
    256 			}
    257 			case OP_JAL: {
    258 				uint32_t imm = ((inst >> 31) << 20) | (((inst >> 12) & 0xFF) << 12) | (((inst >> 20) & 1) << 11) | (((inst >> 21) & 0x3FF) << 1);
    259 
    260 				result = next;
    261 				write = true;
    262 				next = pc + (uint32_t)sign_extend(imm, 21);
    263 				break;
    264 			}
    265 			case OP_JALR: {
    266 				if (funct3 != 0) {
    267 					status = TA_VM_FAULT_ILLEGAL;
    268 					break;
    269 				}
    270 
    271 				result = next;
    272 				write = true;
    273 				next = (a + (uint32_t)sign_extend(inst >> 20, 12)) & ~(uint32_t)1;
    274 				break;
    275 			}
    276 			case OP_BRANCH: {
    277 				uint32_t imm = ((inst >> 31) << 12) | (((inst >> 7) & 1) << 11) | (((inst >> 25) & 0x3F) << 5) | (((inst >> 8) & 0xF) << 1);
    278 				bool taken = false;
    279 
    280 				switch (funct3) {
    281 					case 0: taken = a == b; break;
    282 					case 1: taken = a != b; break;
    283 					case 4: taken = (int32_t)a < (int32_t)b; break;
    284 					case 5: taken = (int32_t)a >= (int32_t)b; break;
    285 					case 6: taken = a < b; break;
    286 					case 7: taken = a >= b; break;
    287 					default: status = TA_VM_FAULT_ILLEGAL; break;
    288 				}
    289 
    290 				if (taken) {
    291 					next = pc + (uint32_t)sign_extend(imm, 13);
    292 				}
    293 
    294 				break;
    295 			}
    296 			case OP_LOAD: {
    297 				uint32_t addr = a + (uint32_t)sign_extend(inst >> 20, 12);
    298 				uint32_t value = 0;
    299 				uint32_t size = 0;
    300 
    301 				switch (funct3) {
    302 					case 0: case 4: size = 1; break;
    303 					case 1: case 5: size = 2; break;
    304 					case 2: size = 4; break;
    305 					default: status = TA_VM_FAULT_ILLEGAL; break;
    306 				}
    307 
    308 				if (status != TA_VM_RUNNING) {
    309 					break;
    310 				}
    311 
    312 				if (!load_value(vm, addr, size, &value)) {
    313 					status = TA_VM_FAULT_MEMORY;
    314 					break;
    315 				}
    316 
    317 				if (funct3 == 0) {
    318 					value = (uint32_t)sign_extend(value, 8);
    319 				} else if (funct3 == 1) {
    320 					value = (uint32_t)sign_extend(value, 16);
    321 				}
    322 
    323 				result = value;
    324 				write = true;
    325 				break;
    326 			}
    327 			case OP_STORE: {
    328 				uint32_t imm = ((inst >> 25) << 5) | ((inst >> 7) & 31);
    329 				uint32_t addr = a + (uint32_t)sign_extend(imm, 12);
    330 				uint32_t size = 0;
    331 
    332 				switch (funct3) {
    333 					case 0: size = 1; break;
    334 					case 1: size = 2; break;
    335 					case 2: size = 4; break;
    336 					default: status = TA_VM_FAULT_ILLEGAL; break;
    337 				}
    338 
    339 				if (status != TA_VM_RUNNING) {
    340 					break;
    341 				}
    342 
    343 				if (!store_value(vm, addr, size, b)) {
    344 					status = TA_VM_FAULT_MEMORY;
    345 				}
    346 
    347 				break;
    348 			}
    349 			case OP_OP_IMM: {
    350 				uint32_t imm = (uint32_t)sign_extend(inst >> 20, 12);
    351 				uint32_t shamt = rs2;
    352 
    353 				switch (funct3) {
    354 					case 0: result = a + imm; break;
    355 					case 2: result = (int32_t)a < (int32_t)imm; break;
    356 					case 3: result = a < imm; break;
    357 					case 4: result = a ^ imm; break;
    358 					case 6: result = a | imm; break;
    359 					case 7: result = a & imm; break;
    360 					case 1: {
    361 						if (funct7 != 0x00) {
    362 							status = TA_VM_FAULT_ILLEGAL;
    363 						}
    364 
    365 						result = a << shamt;
    366 						break;
    367 					}
    368 					default: {
    369 						if (funct7 == 0x00) {
    370 							result = a >> shamt;
    371 						} else if (funct7 == 0x20) {
    372 							result = shift_right_arith(a, shamt);
    373 						} else {
    374 							status = TA_VM_FAULT_ILLEGAL;
    375 						}
    376 
    377 						break;
    378 					}
    379 				}
    380 
    381 				write = true;
    382 				break;
    383 			}
    384 			case OP_OP: {
    385 				if (funct7 == 0x01) {
    386 					switch (funct3) {
    387 						case 0: result = a * b; break;
    388 						case 1: result = mul_high_signed(a, b); break;
    389 						case 2: result = mul_high_signed_unsigned(a, b); break;
    390 						case 3: result = mul_high_unsigned(a, b); break;
    391 						case 4: result = div_signed(a, b); break;
    392 						case 5: result = b == 0 ? 0xFFFFFFFFu : a / b; break;
    393 						case 6: result = rem_signed(a, b); break;
    394 						default: result = b == 0 ? a : a % b; break;
    395 					}
    396 				} else if (funct7 == 0x00 || funct7 == 0x20) {
    397 					bool alt = funct7 == 0x20;
    398 
    399 					if (alt && funct3 != 0 && funct3 != 5) {
    400 						status = TA_VM_FAULT_ILLEGAL;
    401 						break;
    402 					}
    403 
    404 					switch (funct3) {
    405 						case 0: result = alt ? a - b : a + b; break;
    406 						case 1: result = a << (b & 31); break;
    407 						case 2: result = (int32_t)a < (int32_t)b; break;
    408 						case 3: result = a < b; break;
    409 						case 4: result = a ^ b; break;
    410 						case 5: result = alt ? shift_right_arith(a, b & 31) : a >> (b & 31); break;
    411 						case 6: result = a | b; break;
    412 						default: result = a & b; break;
    413 					}
    414 				} else {
    415 					status = TA_VM_FAULT_ILLEGAL;
    416 					break;
    417 				}
    418 
    419 				write = true;
    420 				break;
    421 			}
    422 			case OP_MISC_MEM: {
    423 				break;
    424 			}
    425 			case OP_SYSTEM: {
    426 				uint32_t imm = inst >> 20;
    427 
    428 				if (funct3 != 0 || rd != 0 || rs1 != 0) {
    429 					status = TA_VM_FAULT_ILLEGAL;
    430 				} else if (imm == 0) {
    431 					vm->pc = pc;
    432 
    433 					if (vm->ecall) {
    434 						status = vm->ecall(vm, vm->user);
    435 					} else {
    436 						status = TA_VM_FAULT_ILLEGAL;
    437 					}
    438 				} else if (imm == 1) {
    439 					status = TA_VM_FAULT_EBREAK;
    440 				} else {
    441 					status = TA_VM_FAULT_ILLEGAL;
    442 				}
    443 
    444 				break;
    445 			}
    446 			default: {
    447 				status = TA_VM_FAULT_ILLEGAL;
    448 				break;
    449 			}
    450 		}
    451 
    452 		if (status != TA_VM_RUNNING && status != TA_VM_HALTED) {
    453 			break;
    454 		}
    455 
    456 		if (write && rd != 0) {
    457 			x[rd] = result;
    458 		}
    459 
    460 		x[0] = 0;
    461 		pc = next;
    462 		steps++;
    463 
    464 		if (status != TA_VM_RUNNING) {
    465 			break;
    466 		}
    467 	}
    468 
    469 	vm->pc = pc;
    470 	vm->steps = steps;
    471 
    472 	return status;
    473 }
    474 
    475 static ta_vm guest;
    476 static uint8_t *ram;
    477 static ta_vm_status guest_status;
    478 static int32_t exit_code;
    479 static uint32_t return_stub;
    480 static uint32_t call_result;
    481 static bool ready;
    482 static bool returned;
    483 static bool exec_pending;
    484 static uint8_t exec_file;
    485 bool ta_vm_running;
    486 
    487 static ta_vm_status guest_ecall(ta_vm *machine, void *user) {
    488 	(void)user;
    489 
    490 	uint32_t *registers = machine->regs;
    491 
    492 	switch (registers[TA_VM_REG_A7]) {
    493 		case TA_SYS_EXIT: {
    494 			exit_code = (int32_t)registers[TA_VM_REG_A0];
    495 
    496 			return TA_VM_HALTED;
    497 		}
    498 		case TA_SYS_READY: {
    499 			return_stub = registers[TA_VM_REG_A0];
    500 			ready = true;
    501 
    502 			return TA_VM_YIELDED;
    503 		}
    504 		case TA_SYS_RETURN: {
    505 			call_result = registers[TA_VM_REG_A0];
    506 			returned = true;
    507 
    508 			return TA_VM_YIELDED;
    509 		}
    510 		default: {
    511 			return ta_syscall_handler(machine);
    512 		}
    513 	}
    514 }
    515 
    516 static void finish_run(bool completed) {
    517 	if (completed) {
    518 		guest_status = TA_VM_RUNNING;
    519 		ta_vm_running = true;
    520 
    521 		return;
    522 	}
    523 
    524 	if (guest_status != TA_VM_HALTED) {
    525 		exit_code = 1;
    526 	}
    527 
    528 	ta_vm_running = false;
    529 }
    530 
    531 bool ta_vm_load(const uint8_t *binary, size_t size) {
    532 	ta_vm_unload();
    533 
    534 	if (!binary || size < 4 || size > TA_VM_RAM_SIZE) {
    535 		return false;
    536 	}
    537 
    538 	ram = (uint8_t *)malloc(TA_VM_RAM_SIZE);
    539 
    540 	if (!ram) {
    541 		return false;
    542 	}
    543 
    544 	for (size_t i = 0; i < TA_VM_RAM_SIZE; i++) {
    545 		ram[i] = i < size ? binary[i] : 0;
    546 	}
    547 
    548 	ta_vm_init(&guest, ram, TA_VM_RAM_SIZE, guest_ecall, NULL);
    549 	guest.pc = 0;
    550 	guest_status = TA_VM_RUNNING;
    551 	exit_code = 0;
    552 	ready = false;
    553 	returned = false;
    554 	ta_vm_running = true;
    555 
    556 	guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
    557 
    558 	if (guest_status == TA_VM_YIELDED && exec_pending) {
    559 		finish_run(true);
    560 
    561 		return true;
    562 	}
    563 
    564 	if (guest_status != TA_VM_YIELDED || !ready) {
    565 		finish_run(false);
    566 		ta_vm_unload();
    567 
    568 		return false;
    569 	}
    570 
    571 	finish_run(true);
    572 
    573 	return true;
    574 }
    575 
    576 bool ta_vm_load_tab(const char *path) {
    577 	if (!ta_tab_load(path)) {
    578 		return false;
    579 	}
    580 
    581 	const uint8_t *data;
    582 	uint32_t size;
    583 
    584 	if (!ta_tab_find(TA_TAB_MAIN_PROGRAM, &data, &size)) {
    585 		return false;
    586 	}
    587 
    588 	return ta_vm_load(data, size);
    589 }
    590 
    591 void ta_vm_request_exec(uint8_t file) {
    592 	exec_file = file;
    593 	exec_pending = true;
    594 }
    595 
    596 bool ta_vm_exec_pending(void) {
    597 	return exec_pending;
    598 }
    599 
    600 bool ta_vm_exec_apply(void) {
    601 	while (exec_pending) {
    602 		const uint8_t *data;
    603 		uint32_t size;
    604 		uint8_t file = exec_file;
    605 
    606 		exec_pending = false;
    607 
    608 		if (!ta_tab_find(file, &data, &size)) {
    609 			ta_vm_unload();
    610 			exit_code = 1;
    611 
    612 			return false;
    613 		}
    614 
    615 		ta_screen_init();
    616 		ta_scene_invalidate();
    617 
    618 		if (!ta_vm_load(data, size)) {
    619 			exit_code = 1;
    620 
    621 			return false;
    622 		}
    623 	}
    624 
    625 	return ta_vm_running;
    626 }
    627 
    628 bool ta_vm_call(uint32_t function, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t *result) {
    629 	if (!ta_vm_running || !ready || function < 4 || (function & 3) != 0 || function >= TA_VM_RAM_SIZE) {
    630 		return false;
    631 	}
    632 
    633 	ta_vm_reset(&guest, function, TA_VM_RAM_SIZE);
    634 	guest.regs[TA_VM_REG_RA] = return_stub;
    635 	guest.regs[TA_VM_REG_A0] = arg0;
    636 	guest.regs[TA_VM_REG_A1] = arg1;
    637 	guest.regs[TA_VM_REG_A2] = arg2;
    638 	returned = false;
    639 
    640 	guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
    641 
    642 	if (guest_status == TA_VM_YIELDED && exec_pending) {
    643 		finish_run(true);
    644 
    645 		return false;
    646 	}
    647 
    648 	bool completed = guest_status == TA_VM_YIELDED && returned;
    649 
    650 	finish_run(completed);
    651 
    652 	if (completed && result) {
    653 		*result = call_result;
    654 	}
    655 
    656 	return completed;
    657 }
    658 
    659 int32_t ta_vm_exit_code(void) {
    660 	return exit_code;
    661 }
    662 
    663 void ta_vm_unload(void) {
    664 	ta_syscall_reset();
    665 
    666 	if (ram) {
    667 		free(ram);
    668 		ram = NULL;
    669 	}
    670 
    671 	ready = false;
    672 	returned = false;
    673 	exec_pending = false;
    674 	guest_status = TA_VM_HALTED;
    675 	ta_vm_running = false;
    676 }