MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode_exec.cpp
Go to the documentation of this file.
1
6#include "mxvm/icode.hpp"
7#include <cstring>
8#include <iomanip>
9#include <iostream>
10
11namespace mxvm {
12
13 static inline void releaseOwnedPointer(Variable &v) {
15 std::free(v.var_value.ptr_value);
16 v.var_value.ptr_value = nullptr;
17 v.var_value.owns = false;
18 v.var_value.ptr_size = 0;
19 v.var_value.ptr_count = 0;
20 }
21 }
22
24 running = false;
25 }
26
28 if (root != this) {
29 auto qualifyVar = [&](Operand &op) {
30 if (op.op.empty())
31 return;
32 if (op.type != OperandType::OP_VARIABLE)
33 return;
34 if (!op.object.empty())
35 return;
36 if (op.op.find('.') != std::string::npos)
37 return;
38
39 std::string qualified = this->name + "." + op.op;
40 if (vars.find(qualified) != vars.end()) {
41 op.object = this->name;
42 op.label = op.op;
43 op.op = qualified;
44 }
45 };
46 auto qualifyLabel = [&](Operand &op) {
47 if (op.op.empty())
48 return;
49 if (op.op.find('.') != std::string::npos)
50 return;
51 if (labels.find(op.op) != labels.end()) {
52 op.object = this->name;
53 op.label = op.op;
54 op.op = this->name + "." + op.op;
55 }
56 };
57 Instruction ci = i;
58 qualifyVar(ci.op1);
59 qualifyVar(ci.op2);
60 qualifyVar(ci.op3);
61 for (auto &vo : ci.vop)
62 qualifyVar(vo);
63 switch (ci.instruction) {
64 case CALL:
65 case JMP:
66 case JE:
67 case JNE:
68 case JL:
69 case JLE:
70 case JG:
71 case JGE:
72 case JZ:
73 case JNZ:
74 case JA:
75 case JB:
76 qualifyLabel(ci.op1);
77 break;
78 default:
79 break;
80 }
81 root->add_instruction(ci);
82 return;
83 }
84 root->add_instruction(i);
85 }
86
87 void Program::flatten_label(Program *root, int64_t offset, const std::string &label, bool func) {
88
89 if (root != this) {
90 root->add_label(this->name + "." + label, offset, func);
91 return;
92 }
93 root->add_label(label, offset, func);
94 }
95
96 void Program::flatten_external(Program *root, const std::string &e, RuntimeFunction &r) {
97 if (root->external_functions.find(e) == root->external_functions.end()) {
98 root->external_functions[e] = r;
99 }
100 }
101
102 void Program::flatten(Program *program) {
103 if (program != this) {
104 int64_t size = program->inc.size();
105 for (auto &i : inc) {
106 flatten_inc(program, i);
107 }
108 for (auto &lbl : labels) {
109 flatten_label(program, lbl.second.first + size, lbl.first, lbl.second.second);
110 }
111 for (auto &e : external_functions) {
112 flatten_external(program, e.first, e.second);
113 }
114 }
115
116 for (auto &obj : objects) {
117 if (obj.get() != program)
118 obj->flatten(program);
119 }
120 }
121
123
124 this->add_standard();
125
126 if (inc.empty()) {
127 std::cerr << "No instructions to execute\n";
128 return EXIT_FAILURE;
129 }
130 pc = 0;
131 running = true;
132
133 while (running && pc < inc.size()) {
134 const Instruction &instr = inc.at(pc);
136 std::cout << instr << "\n";
137
138 switch (instr.instruction) {
139 case MOV:
140 exec_mov(instr);
141 break;
142 case ADD:
143 exec_add(instr);
144 break;
145 case SUB:
146 exec_sub(instr);
147 break;
148 case MUL:
149 exec_mul(instr);
150 break;
151 case DIV:
152 exec_div(instr);
153 break;
154 case CMP:
155 exec_cmp(instr);
156 break;
157 case FCMP:
158 exec_fcmp(instr);
159 break;
160 case JMP:
161 exec_jmp(instr);
162 continue;
163 case JE:
164 exec_je(instr);
165 continue;
166 case JNE:
167 exec_jne(instr);
168 continue;
169 case JL:
170 exec_jl(instr);
171 continue;
172 case JLE:
173 exec_jle(instr);
174 continue;
175 case JG:
176 exec_jg(instr);
177 continue;
178 case JGE:
179 exec_jge(instr);
180 continue;
181 case JZ:
182 exec_jz(instr);
183 continue;
184 case JNZ:
185 exec_jnz(instr);
186 continue;
187 case JA:
188 exec_ja(instr);
189 continue;
190 case JB:
191 exec_jb(instr);
192 continue;
193 case JAE:
194 exec_jae(instr);
195 continue;
196 case JBE:
197 exec_jbe(instr);
198 continue;
199 case JC:
200 exec_jc(instr);
201 continue;
202 case JNC:
203 exec_jnc(instr);
204 continue;
205 case JP:
206 exec_jp(instr);
207 continue;
208 case JNP:
209 exec_jnp(instr);
210 continue;
211 case JO:
212 exec_jo(instr);
213 continue;
214 case JNO:
215 exec_jno(instr);
216 continue;
217 case JS:
218 exec_js(instr);
219 continue;
220 case JNS:
221 exec_jns(instr);
222 continue;
223 case LOAD:
224 exec_load(instr);
225 break;
226 case STORE:
227 exec_store(instr);
228 break;
229 case OR:
230 exec_or(instr);
231 break;
232 case AND:
233 exec_and(instr);
234 break;
235 case XOR:
236 exec_xor(instr);
237 break;
238 case NOT:
239 exec_not(instr);
240 break;
241 case MOD:
242 exec_mod(instr);
243 break;
244 case PRINT:
245 exec_print(instr);
246 break;
247 case ALLOC:
248 exec_alloc(instr);
249 break;
250 case FREE:
251 exec_free(instr);
252 break;
253 case EXIT:
254 exec_exit(instr);
255 return getExitCode();
256 case GETLINE:
257 exec_getline(instr);
258 break;
259 case PUSH:
260 exec_push(instr);
261 break;
262 case POP:
263 exec_pop(instr);
264 break;
265 case STACK_LOAD:
266 exec_stack_load(instr);
267 break;
268 case STACK_STORE:
269 exec_stack_store(instr);
270 break;
271 case STACK_SUB:
272 exec_stack_sub(instr);
273 break;
274 case CALL:
275 exec_call(instr);
276 continue;
277 case RET:
278 exec_ret(instr);
279 break;
280 case STRING_PRINT:
281 exec_string_print(instr);
282 break;
283 case DONE:
284 exec_done(instr);
285 return EXIT_SUCCESS;
286 case TO_INT:
287 exec_to_int(instr);
288 break;
289 case TO_FLOAT:
290 exec_to_float(instr);
291 break;
292 case INVOKE:
293 exec_invoke(instr);
294 break;
295 case RETURN:
296 exec_return(instr);
297 break;
298 case NEG:
299 exec_neg(instr);
300 break;
301 case LEA:
302 exec_lea(instr);
303 break;
304 case REALLOC:
305 exec_realloc(instr);
306 break;
307 default:
308 throw mx::Exception("Unknown instruction: " + std::to_string(instr.instruction));
309 break;
310 }
311 pc++;
312 }
313 return getExitCode();
314 }
315
316 void Program::exec_mov(const Instruction &instr) {
317 if (!isVariable(instr.op1.op)) {
318 std::cerr << "Error: MOV destination: " + instr.op1.op + " must be a variable, not a constant\n";
319 return;
320 }
321 Variable &dest = getVariable(instr.op1.op);
322
323 // Before freeing dest's old pointer, transfer ownership to any alias
324 if (dest.type == VarType::VAR_POINTER && dest.var_value.ptr_value && dest.var_value.owns) {
325 void *old_ptr = dest.var_value.ptr_value;
326 bool transferred = false;
327 for (auto &kv : vars) {
328 if (&kv.second != &dest &&
329 kv.second.type == VarType::VAR_POINTER &&
330 kv.second.var_value.ptr_value == old_ptr) {
331 kv.second.var_value.owns = true;
332 transferred = true;
333 break;
334 }
335 }
336 if (!transferred) {
337 std::free(old_ptr);
338 }
339 dest.var_value.ptr_value = nullptr;
340 dest.var_value.owns = false;
341 dest.var_value.ptr_size = 0;
342 dest.var_value.ptr_count = 0;
343 } else {
345 }
346
347 if (isVariable(instr.op2.op)) {
348 Variable &src = getVariable(instr.op2.op);
349
350 if (dest.type == VarType::VAR_INTEGER && src.type == VarType::VAR_FLOAT) {
351 dest.var_value.int_value = static_cast<int64_t>(src.var_value.float_value);
353 return;
354 } else if (dest.type == VarType::VAR_FLOAT && src.type == VarType::VAR_INTEGER) {
355 dest.var_value.float_value = static_cast<double>(src.var_value.int_value);
357 return;
358 }
359
360 dest.var_value = src.var_value;
361 dest.type = src.type;
362 if (dest.type == VarType::VAR_POINTER || dest.type == VarType::VAR_EXTERN) {
363 dest.var_value.owns = false;
364 }
365 } else {
366
367 if (dest.type == VarType::VAR_POINTER) {
368 std::string v = instr.op2.op;
369 if (v == "null" || v == "NULL" || v == "0") {
370 dest.var_value.ptr_value = nullptr;
371 } else {
372 uintptr_t addr = std::stoull(v, nullptr, 0);
373 dest.var_value.ptr_value = reinterpret_cast<void *>(addr);
374 }
376 dest.var_value.owns = false;
377 } else {
378 setVariableFromConstant(dest, instr.op2.op);
379 }
380 }
381 }
382
383 void Program::exec_add(const Instruction &instr) {
384 if (!isVariable(instr.op1.op)) {
385 std::cerr << "Error: ADD destination: " + instr.op1.op + " must be a variable, not a constant\n";
386 return;
387 }
388 Variable &dest = getVariable(instr.op1.op);
389 Variable *src1 = nullptr, *src2 = nullptr;
390 Variable temp1, temp2;
391
392 {
393 VarType constType = (dest.type == VarType::VAR_POINTER) ? VarType::VAR_INTEGER : dest.type;
394 if (instr.op3.op.empty()) {
395 src1 = &dest;
396 if (isVariable(instr.op2.op)) {
397 src2 = &getVariable(instr.op2.op);
398 } else {
399 temp2 = createTempVariable(constType, instr.op2.op);
400 src2 = &temp2;
401 }
402 } else {
403 if (isVariable(instr.op2.op)) {
404 src1 = &getVariable(instr.op2.op);
405 } else {
406 temp1 = createTempVariable(constType, instr.op2.op);
407 src1 = &temp1;
408 }
409 if (isVariable(instr.op3.op)) {
410 src2 = &getVariable(instr.op3.op);
411 } else {
412 temp2 = createTempVariable(constType, instr.op3.op);
413 src2 = &temp2;
414 }
415 }
416 }
417
418 addVariables(dest, *src1, *src2);
419 }
420
421 void Program::exec_sub(const Instruction &instr) {
422 if (!isVariable(instr.op1.op)) {
423 std::cerr << "Error: SUB destination must be a variable, not a constant\n";
424 return;
425 }
426 Variable &dest = getVariable(instr.op1.op);
427
428 Variable *src1 = nullptr, *src2 = nullptr;
429 Variable temp1, temp2;
430
431 {
432 VarType constType = (dest.type == VarType::VAR_POINTER) ? VarType::VAR_INTEGER : dest.type;
433 if (instr.op3.op.empty()) {
434 src1 = &dest;
435 if (isVariable(instr.op2.op)) {
436 src2 = &getVariable(instr.op2.op);
437 } else {
438 temp2 = createTempVariable(constType, instr.op2.op);
439 src2 = &temp2;
440 }
441 } else {
442 if (isVariable(instr.op2.op)) {
443 src1 = &getVariable(instr.op2.op);
444 } else {
445 temp1 = createTempVariable(constType, instr.op2.op);
446 src1 = &temp1;
447 }
448 if (isVariable(instr.op3.op)) {
449 src2 = &getVariable(instr.op3.op);
450 } else {
451 temp2 = createTempVariable(constType, instr.op3.op);
452 src2 = &temp2;
453 }
454 }
455 }
456
457 subVariables(dest, *src1, *src2);
458 }
459
460 void Program::exec_mul(const Instruction &instr) {
461 if (!isVariable(instr.op1.op)) {
462 std::cerr << "Error: MUL destination must be a variable, not a constant\n";
463 return;
464 }
465 Variable &dest = getVariable(instr.op1.op);
466
467 Variable *src1 = nullptr, *src2 = nullptr;
468 Variable temp1, temp2;
469
470 if (instr.op3.op.empty()) {
471 src1 = &dest;
472 if (isVariable(instr.op2.op)) {
473 src2 = &getVariable(instr.op2.op);
474 } else {
475 temp2 = createTempVariable(dest.type, instr.op2.op);
476 src2 = &temp2;
477 }
478 } else {
479 if (isVariable(instr.op2.op)) {
480 src1 = &getVariable(instr.op2.op);
481 } else {
482 temp1 = createTempVariable(dest.type, instr.op2.op);
483 src1 = &temp1;
484 }
485 if (isVariable(instr.op3.op)) {
486 src2 = &getVariable(instr.op3.op);
487 } else {
488 temp2 = createTempVariable(dest.type, instr.op3.op);
489 src2 = &temp2;
490 }
491 }
492
493 mulVariables(dest, *src1, *src2);
494 }
495
496 void Program::exec_div(const Instruction &instr) {
497 if (!isVariable(instr.op1.op)) {
498 std::cerr << "Error: DIV destination must be a variable, not a constant\n";
499 return;
500 }
501 Variable &dest = getVariable(instr.op1.op);
502
503 Variable *src1 = nullptr, *src2 = nullptr;
504 Variable temp1, temp2;
505
506 if (instr.op3.op.empty()) {
507 src1 = &dest;
508 if (isVariable(instr.op2.op)) {
509 src2 = &getVariable(instr.op2.op);
510 } else {
511 temp2 = createTempVariable(dest.type, instr.op2.op);
512 src2 = &temp2;
513 }
514 } else {
515 if (isVariable(instr.op2.op)) {
516 src1 = &getVariable(instr.op2.op);
517 } else {
518 temp1 = createTempVariable(dest.type, instr.op2.op);
519 src1 = &temp1;
520 }
521 if (isVariable(instr.op3.op)) {
522 src2 = &getVariable(instr.op3.op);
523 } else {
524 temp2 = createTempVariable(dest.type, instr.op3.op);
525 src2 = &temp2;
526 }
527 }
528
529 divVariables(dest, *src1, *src2);
530 }
531
532 void Program::exec_cmp(const Instruction &instr) {
533 Variable *var1 = nullptr;
534 Variable *var2 = nullptr;
535 Variable temp1, temp2;
536
537 if (isVariable(instr.op1.op)) {
538 var1 = &getVariable(instr.op1.op);
539 } else {
541 var1 = &temp1;
542 }
543 if (isVariable(instr.op2.op)) {
544 var2 = &getVariable(instr.op2.op);
545 } else {
547 var2 = &temp2;
548 }
549 zero_flag = false;
550 less_flag = false;
551 greater_flag = false;
552 if ((var1->type == VarType::VAR_INTEGER || var1->type == VarType::VAR_BYTE) &&
553 (var2->type == VarType::VAR_INTEGER || var2->type == VarType::VAR_BYTE)) {
554 int64_t val1 = var1->var_value.int_value;
555 int64_t val2 = var2->var_value.int_value;
556 if (val1 == val2)
557 zero_flag = true;
558 else if (val1 < val2)
559 less_flag = true;
560 else
561 greater_flag = true;
562 } else if (var1->type == VarType::VAR_FLOAT && var2->type == VarType::VAR_FLOAT) {
563 double val1 = var1->var_value.float_value;
564 double val2 = var2->var_value.float_value;
565 if (val1 == val2)
566 zero_flag = true;
567 else if (val1 < val2)
568 less_flag = true;
569 else
570 greater_flag = true;
571 } else if (var1->type == VarType::VAR_FLOAT && (var2->type == VarType::VAR_INTEGER || var2->type == VarType::VAR_BYTE)) {
572 double val1 = var1->var_value.float_value;
573 double val2 = static_cast<double>(var2->var_value.int_value);
574 if (val1 == val2)
575 zero_flag = true;
576 else if (val1 < val2)
577 less_flag = true;
578 else
579 greater_flag = true;
580 } else if ((var1->type == VarType::VAR_INTEGER || var1->type == VarType::VAR_BYTE) && var2->type == VarType::VAR_FLOAT) {
581 double val1 = static_cast<double>(var1->var_value.int_value);
582 double val2 = var2->var_value.float_value;
583 if (val1 == val2)
584 zero_flag = true;
585 else if (val1 < val2)
586 less_flag = true;
587 else
588 greater_flag = true;
589 } else if (var1->type == VarType::VAR_POINTER && var2->type == VarType::VAR_POINTER) {
590 uintptr_t val1 = reinterpret_cast<uintptr_t>(var1->var_value.ptr_value);
591 uintptr_t val2 = reinterpret_cast<uintptr_t>(var2->var_value.ptr_value);
592 if (val1 == val2)
593 zero_flag = true;
594 else if (val1 < val2)
595 less_flag = true;
596 else
597 greater_flag = true;
598 } else if (var1->type == VarType::VAR_POINTER && (var2->type == VarType::VAR_INTEGER || var2->type == VarType::VAR_BYTE)) {
599 uintptr_t val1 = reinterpret_cast<uintptr_t>(var1->var_value.ptr_value);
600 uint64_t val2 = var2->var_value.int_value;
601 if (val1 == val2)
602 zero_flag = true;
603 else if (val1 < val2)
604 less_flag = true;
605 else
606 greater_flag = true;
607 } else if ((var1->type == VarType::VAR_INTEGER || var1->type == VarType::VAR_BYTE) && var2->type == VarType::VAR_POINTER) {
608 uint64_t val1 = var1->var_value.int_value;
609 uintptr_t val2 = reinterpret_cast<uintptr_t>(var2->var_value.ptr_value);
610 if (val1 == val2)
611 zero_flag = true;
612 else if (val1 < val2)
613 less_flag = true;
614 else
615 greater_flag = true;
616 } else {
617 throw mx::Exception("cmp: unsupported type combination: " +
618 var1->toString() + " vs " + var2->toString());
619 }
620 }
621
622 void Program::exec_jmp(const Instruction &instr) {
623 auto it = labels.find(instr.op1.op);
624 if (it != labels.end()) {
625 pc = it->second.first;
626 } else {
627 throw mx::Exception("Label not found: " + instr.op1.op);
628 }
629 }
630
631 void Program::exec_print(const Instruction &instr) {
632 std::string format;
633 std::vector<Variable> tempArgs;
634 std::vector<Variable *> args;
635 if (isVariable(instr.op1.op)) {
636 Variable &fmt = getVariable(instr.op1.op);
637 if (fmt.type != VarType::VAR_STRING)
638 throw mx::Exception("PRINT format must be a string variable");
639 format = fmt.var_value.str_value;
640 } else {
641 format = instr.op1.op;
642 }
643 auto getArgVariable = [&](const Operand &op, VarType type = VarType::VAR_INTEGER) -> Variable * {
644 if (isVariable(op.op)) {
645 return &getVariable(op.op);
646 } else {
647 if (op.type == OperandType::OP_VARIABLE) {
648 throw mx::Exception("Instruction variable not defined: " + op.op);
649 }
650 tempArgs.push_back(createTempVariable(type, op.op));
651 return &tempArgs.back();
652 }
653 };
654 if (!instr.op2.op.empty()) {
655 args.push_back(getArgVariable(instr.op2));
656 }
657 if (!instr.op3.op.empty()) {
658 args.push_back(getArgVariable(instr.op3));
659 }
660 for (const auto &vop : instr.vop) {
661 if (!vop.op.empty()) {
662 args.push_back(getArgVariable(vop));
663 }
664 }
665 printFormatted(format, args);
666 }
667
668 void Program::exec_exit(const Instruction &instr) {
669 int exit_code = 0;
670 if (!instr.op1.op.empty()) {
671 if (isVariable(instr.op1.op)) {
672 exit_code = getVariable(instr.op1.op).var_value.int_value;
673 } else {
674 exit_code = std::stoll(instr.op1.op, nullptr, 0);
675 }
676 }
677 exitCode = exit_code;
678 stop();
679 }
680
681 Variable &Program::getVariable(const std::string &n) {
682 auto rax_pos = n.find("%");
683 if (rax_pos != std::string::npos) {
684 auto dot = n.find(".");
685 if (dot != std::string::npos) {
686 std::string n_ = n.substr(dot + 1);
687 auto e = vars.find(n_);
688 if (e != vars.end())
689 return e->second;
690 } else {
691 auto e = vars.find(n);
692 if (e != vars.end())
693 return e->second;
694 }
695 }
696 auto pos = n.find(".");
697 if (pos == std::string::npos) {
698 auto it = vars.find(name + "." + n);
699 if (it != vars.end()) {
700 return it->second;
701 }
702 } else {
703 auto it = vars.find(n);
704 if (it != vars.end())
705 return it->second;
706 }
707
708 if (Program::base != nullptr) {
709 for (auto &obj : Base::base->object_map) {
710 if (pos == std::string::npos) {
711 auto it = obj.second->vars.find(name + "." + n);
712 if (it != obj.second->vars.end())
713 return it->second;
714 } else {
715 auto it = obj.second->vars.find(n);
716 if (it != obj.second->vars.end())
717 return it->second;
718 }
719 }
720 }
721 throw mx::Exception("Variable not found: " + name + "." + n);
722 }
723
724 bool Program::isVariable(const std::string &n) {
725
726 auto rax_pos = n.find("%");
727
728 if (rax_pos != std::string::npos) {
729 return true;
730 }
731
732 if (Program::base != nullptr) {
733 for (auto &obj : Base::base->object_map) {
734 auto it = obj.second->vars.find(n);
735 if (it != obj.second->vars.end()) {
736 return true;
737 }
738
739 it = obj.second->vars.find(name + "." + n);
740 if (it != obj.second->vars.end())
741 return true;
742 }
743 }
744
745 if (vars.find(name + "." + n) != vars.end())
746 return true;
747
748 if (vars.find(n) != vars.end())
749 return true;
750
751 return false;
752 }
753
755 for (auto &variable : v.var_names) {
756 auto it = vars.find(variable.second->getTokenValue());
757 if (it == vars.end()) {
758 for (auto &o : objects) {
759 if (o->name == variable.first && !o->isVariable(variable.second->getTokenValue())) {
760 throw mx::Exception("Syntax Error: Argument variable not defined: Object: " +
761 variable.first + " variable: " +
762 variable.second->getTokenValue() +
763 " at line " + std::to_string(variable.second->getLine()));
764 }
765 }
766 }
767 }
768 return true;
769 }
770
771 void Program::setVariableFromString(Variable &var, const std::string &value) {
772 if (var.type == VarType::VAR_INTEGER) {
773 if (value.starts_with("0x") || value.starts_with("0X")) {
774 var.var_value.int_value = std::stoll(value, nullptr, 16);
775 } else {
776 var.var_value.int_value = std::stoll(value);
777 }
779 } else if (var.type == VarType::VAR_FLOAT) {
780 var.var_value.float_value = std::stod(value);
782 } else if (var.type == VarType::VAR_STRING) {
783 var.var_value.str_value = value;
785 }
786 }
788 if (dest.type == VarType::VAR_POINTER) {
789 char *base = static_cast<char *>(src1.var_value.ptr_value);
790 int64_t offset = (src2.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2.var_value.float_value) : src2.var_value.int_value;
791 dest.var_value.ptr_value = base + offset;
793 dest.var_value.owns = false;
794 } else if (dest.type == VarType::VAR_INTEGER) {
795 int64_t v1 = (src1.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src1.var_value.float_value) : src1.var_value.int_value;
796 int64_t v2 = (src2.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2.var_value.float_value) : src2.var_value.int_value;
797 dest.var_value.int_value = v1 + v2;
799 } else if (dest.type == VarType::VAR_FLOAT) {
800 double v1 = (src1.type == VarType::VAR_INTEGER) ? static_cast<double>(src1.var_value.int_value) : src1.var_value.float_value;
801 double v2 = (src2.type == VarType::VAR_INTEGER) ? static_cast<double>(src2.var_value.int_value) : src2.var_value.float_value;
802 dest.var_value.float_value = v1 + v2;
804 }
805 }
806
808 if (dest.type == VarType::VAR_POINTER) {
809 char *base = static_cast<char *>(src1.var_value.ptr_value);
810 int64_t offset = (src2.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2.var_value.float_value) : src2.var_value.int_value;
811 dest.var_value.ptr_value = base - offset;
813 dest.var_value.owns = false;
814 } else if (dest.type == VarType::VAR_INTEGER) {
815 int64_t v1 = (src1.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src1.var_value.float_value) : src1.var_value.int_value;
816 int64_t v2 = (src2.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2.var_value.float_value) : src2.var_value.int_value;
817 dest.var_value.int_value = v1 - v2;
819 } else if (dest.type == VarType::VAR_FLOAT) {
820 double v1 = (src1.type == VarType::VAR_INTEGER) ? static_cast<double>(src1.var_value.int_value) : src1.var_value.float_value;
821 double v2 = (src2.type == VarType::VAR_INTEGER) ? static_cast<double>(src2.var_value.int_value) : src2.var_value.float_value;
822 dest.var_value.float_value = v1 - v2;
824 }
825 }
826
828 if (dest.type == VarType::VAR_INTEGER) {
829 int64_t v1 = (src1.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src1.var_value.float_value) : src1.var_value.int_value;
830 int64_t v2 = (src2.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2.var_value.float_value) : src2.var_value.int_value;
831 dest.var_value.int_value = v1 * v2;
833 } else if (dest.type == VarType::VAR_FLOAT) {
834 double v1 = (src1.type == VarType::VAR_INTEGER) ? static_cast<double>(src1.var_value.int_value) : src1.var_value.float_value;
835 double v2 = (src2.type == VarType::VAR_INTEGER) ? static_cast<double>(src2.var_value.int_value) : src2.var_value.float_value;
836 dest.var_value.float_value = v1 * v2;
838 }
839 }
840
842 if (dest.type == VarType::VAR_INTEGER) {
843 int64_t v1 = (src1.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src1.var_value.float_value) : src1.var_value.int_value;
844 int64_t v2 = (src2.type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2.var_value.float_value) : src2.var_value.int_value;
845 if (v2 == 0) {
846 throw mx::Exception("DIV: integer division by zero");
847 }
848 if (v1 == INT64_MIN && v2 == -1) {
849 throw mx::Exception("DIV: signed integer overflow (INT64_MIN / -1)");
850 }
851 dest.var_value.int_value = v1 / v2;
853 } else if (dest.type == VarType::VAR_FLOAT) {
854 double v1 = (src1.type == VarType::VAR_INTEGER) ? static_cast<double>(src1.var_value.int_value) : src1.var_value.float_value;
855 double v2 = (src2.type == VarType::VAR_INTEGER) ? static_cast<double>(src2.var_value.int_value) : src2.var_value.float_value;
856 if (v2 == 0.0) {
857 throw mx::Exception("DIV: floating-point division by zero");
858 }
859 dest.var_value.float_value = v1 / v2;
861 }
862 }
863
864 void Program::exec_load(const Instruction &instr) {
865 if (!isVariable(instr.op1.op)) {
866 throw mx::Exception("LOAD destination must be a variable");
867 }
868 Variable &dest = getVariable(instr.op1.op);
869 void *ptr = nullptr;
870 size_t allocated_size = 0;
871
872 bool is_string_source = false;
873
874 if (isVariable(instr.op2.op)) {
875 Variable &ptrVar = getVariable(instr.op2.op);
876 if (ptrVar.type != VarType::VAR_POINTER && ptrVar.type != VarType::VAR_STRING) {
877 throw mx::Exception("LOAD source must be a valid pointer/string buffer");
878 }
879 if (ptrVar.type == VarType::VAR_POINTER) {
880 ptr = ptrVar.var_value.ptr_value;
881 allocated_size = ptrVar.var_value.ptr_size * ptrVar.var_value.ptr_count;
882 } else {
883
884 ptr = (void *)ptrVar.var_value.str_value.data();
885 allocated_size = ptrVar.var_value.str_value.size() + 1;
886 is_string_source = true;
887 }
888 } else {
889 throw mx::Exception("LOAD source must be a pointer variable");
890 }
891
892 if (ptr == nullptr) {
893
894 switch (dest.type) {
897 dest.var_value.int_value = 0;
898 break;
900 dest.var_value.float_value = 0.0;
901 break;
903 dest.var_value.ptr_value = nullptr;
904 break;
906 dest.var_value.str_value = "";
907 break;
908 default:
909
910 break;
911 }
912 dest.var_value.type = dest.type;
913 return;
914 }
915
916 size_t index = 0;
917 if (!instr.op3.op.empty()) {
918 if (isVariable(instr.op3.op)) {
919 index = static_cast<size_t>(getVariable(instr.op3.op).var_value.int_value);
920 } else {
921 index = static_cast<size_t>(std::stoll(instr.op3.op, nullptr, 0));
922 }
923 }
924
925 size_t stride = 8;
926 if (!instr.vop.empty() && !instr.vop[0].op.empty()) {
927 const auto &sizeOp = instr.vop[0];
928 if (isVariable(sizeOp.op)) {
929 stride = static_cast<size_t>(getVariable(sizeOp.op).var_value.int_value);
930 } else {
931 stride = static_cast<size_t>(std::stoll(sizeOp.op, nullptr, 0));
932 }
933 }
934
935 if (stride == 0) {
936 throw mx::Exception("LOAD: stride must be non-zero");
937 }
938 if (index > SIZE_MAX / stride) {
939 throw mx::Exception("LOAD: index * stride overflow");
940 }
941 size_t offset = index * stride;
942 if (allocated_size > 0 && (offset + stride > allocated_size)) {
943 throw mx::Exception("LOAD: index out of bounds, offset " + std::to_string(offset) +
944 " + stride " + std::to_string(stride) +
945 " exceeds allocated size " + std::to_string(allocated_size));
946 }
947
948 try {
949 char *base = static_cast<char *>(ptr) + offset;
950
951 switch (dest.type) {
953 std::memcpy(&dest.var_value.int_value, base, sizeof(int64_t));
955 break;
957 dest.var_value.int_value = 0;
958 std::memcpy(&dest.var_value.int_value, base, 1);
960 break;
962 std::memcpy(&dest.var_value.float_value, base, sizeof(double));
964 break;
966 std::memcpy(&dest.var_value.ptr_value, base, sizeof(void *));
968 dest.var_value.owns = false;
969 break;
971 if (is_string_source && index < allocated_size) {
972
973 dest.var_value.str_value = std::string(1, *(base));
974 } else {
975
976 const char *str_ptr = nullptr;
977 std::memcpy(&str_ptr, base, sizeof(const char *));
978 if (str_ptr != nullptr) {
979
980 dest.var_value.str_value = str_ptr;
981 } else {
982 dest.var_value.str_value = "";
983 }
984 }
986 break;
987 default:
988 throw mx::Exception("LOAD: unsupported destination type");
989 }
990 } catch (const std::exception &e) {
991 throw mx::Exception(std::string("LOAD: memory access error: ") + e.what());
992 } catch (...) {
993 throw mx::Exception("LOAD: unknown memory access error");
994 }
995 }
996
1009 void *ptr = nullptr;
1010 size_t allocated_size = 0;
1011 bool is_owned = false;
1012
1013 if (!isVariable(instr.op2.op)) {
1014 throw mx::Exception("STORE destination must be a variable or register");
1015 }
1016
1017 Variable &ptrVar = getVariable(instr.op2.op);
1018
1019 if (ptrVar.type == VarType::VAR_POINTER) {
1020 if (ptrVar.var_value.ptr_value == nullptr) {
1021 throw mx::Exception("STORE destination pointer is null: " + ptrVar.var_name);
1022 }
1023 ptr = ptrVar.var_value.ptr_value;
1024 allocated_size = ptrVar.var_value.ptr_size * ptrVar.var_value.ptr_count;
1025 is_owned = ptrVar.var_value.owns;
1026 } else if (ptrVar.type == VarType::VAR_INTEGER) {
1027 if (ptrVar.var_value.int_value == 0) {
1028 throw mx::Exception("STORE destination contains null pointer: " + ptrVar.var_name);
1029 }
1030 ptr = reinterpret_cast<void *>(static_cast<uintptr_t>(ptrVar.var_value.int_value));
1031
1032 } else {
1033 throw mx::Exception("STORE destination must be a pointer or integer containing a pointer address");
1034 }
1035
1036 size_t index = 0;
1037 if (!instr.op3.op.empty()) {
1038 if (isVariable(instr.op3.op)) {
1039 index = static_cast<size_t>(getVariable(instr.op3.op).var_value.int_value);
1040 } else {
1041 index = static_cast<size_t>(std::stoll(instr.op3.op, nullptr, 0));
1042 }
1043 }
1044
1045 size_t stride = 8;
1046 if (!instr.vop.empty() && !instr.vop[0].op.empty()) {
1047 const auto &sizeOp = instr.vop[0];
1048 if (isVariable(sizeOp.op)) {
1049 stride = static_cast<size_t>(getVariable(sizeOp.op).var_value.int_value);
1050 } else {
1051 stride = static_cast<size_t>(std::stoll(sizeOp.op, nullptr, 0));
1052 }
1053 }
1054
1055 if (stride == 0) {
1056 throw mx::Exception("STORE: stride must be non-zero");
1057 }
1058 if (index > SIZE_MAX / stride) {
1059 throw mx::Exception("STORE: index * stride overflow");
1060 }
1061 size_t offset = index * stride;
1062 if (is_owned && allocated_size > 0 && (offset + stride > allocated_size)) {
1063 throw mx::Exception("STORE: index out of bounds, offset " + std::to_string(offset) +
1064 " + stride " + std::to_string(stride) +
1065 " exceeds allocated size " + std::to_string(allocated_size));
1066 }
1067
1068 char *base = static_cast<char *>(ptr) + offset;
1069
1070 if (instr.op1.type == OperandType::OP_CONSTANT) {
1071 int64_t cval = std::stoll(instr.op1.op, nullptr, 0);
1072 std::memcpy(base, &cval, sizeof(int64_t));
1073 } else {
1074 Variable &src = getVariable(instr.op1.op);
1075 switch (src.type) {
1077 std::memcpy(base, &src.var_value.int_value, sizeof(int64_t));
1078 break;
1079 case VarType::VAR_BYTE: {
1080 uint8_t byte_val = static_cast<uint8_t>(src.var_value.int_value & 0xFF);
1081 std::memcpy(base, &byte_val, 1);
1082 break;
1083 }
1084 case VarType::VAR_FLOAT:
1085 std::memcpy(base, &src.var_value.float_value, sizeof(double));
1086 break;
1088 std::memcpy(base, &src.var_value.ptr_value, sizeof(void *));
1089 break;
1090 case VarType::VAR_STRING: {
1091
1092 if (is_owned && stride >= sizeof(char *)) {
1093 char *old_str = nullptr;
1094 std::memcpy(&old_str, base, sizeof(char *));
1095 size_t str_len = src.var_value.str_value.size() + 1;
1096
1097 if (old_str != nullptr) {
1098 std::free(old_str);
1099 }
1100
1101 char *new_str = static_cast<char *>(std::malloc(str_len));
1102 if (new_str == nullptr) {
1103 throw mx::Exception("STORE: failed to allocate memory for string copy");
1104 }
1105 std::strncpy(new_str, src.var_value.str_value.c_str(), str_len);
1106 new_str[str_len - 1] = '\0';
1107 std::memcpy(base, &new_str, sizeof(char *));
1108 } else {
1109 const char *cstr = src.var_value.str_value.c_str();
1110 std::memcpy(base, &cstr, sizeof(const char *));
1111 }
1112 break;
1113 }
1114 default:
1115 throw mx::Exception("STORE: unsupported source type");
1116 }
1117 }
1118 }
1119 void Program::exec_and(const Instruction &instr) {
1120 if (!isVariable(instr.op1.op)) {
1121 throw mx::Exception("AND destination must be a variable");
1122 }
1123 Variable &dest = getVariable(instr.op1.op);
1124 int64_t v1, v2;
1125 if (instr.op3.op.empty()) {
1126 v1 = dest.var_value.int_value;
1127 v2 = isVariable(instr.op2.op) ? getVariable(instr.op2.op).var_value.int_value : std::stoll(instr.op2.op, nullptr, 0);
1128 } else {
1129 v1 = isVariable(instr.op2.op) ? getVariable(instr.op2.op).var_value.int_value : std::stoll(instr.op2.op, nullptr, 0);
1130 v2 = isVariable(instr.op3.op) ? getVariable(instr.op3.op).var_value.int_value : std::stoll(instr.op3.op, nullptr, 0);
1131 }
1132 dest.var_value.int_value = v1 & v2;
1134 }
1135
1136 void Program::exec_or(const Instruction &instr) {
1137 if (!isVariable(instr.op1.op)) {
1138 throw mx::Exception("OR destination must be a variable");
1139 }
1140 Variable &dest = getVariable(instr.op1.op);
1141 int64_t v1, v2;
1142 if (instr.op3.op.empty()) {
1143 v1 = dest.var_value.int_value;
1144 v2 = isVariable(instr.op2.op) ? getVariable(instr.op2.op).var_value.int_value : std::stoll(instr.op2.op, nullptr, 0);
1145 } else {
1146 v1 = isVariable(instr.op2.op) ? getVariable(instr.op2.op).var_value.int_value : std::stoll(instr.op2.op, nullptr, 0);
1147 v2 = isVariable(instr.op3.op) ? getVariable(instr.op3.op).var_value.int_value : std::stoll(instr.op3.op, nullptr, 0);
1148 }
1149 dest.var_value.int_value = v1 | v2;
1151 }
1152
1153 void Program::exec_xor(const Instruction &instr) {
1154 if (!isVariable(instr.op1.op)) {
1155 throw mx::Exception("XOR destination must be a variable");
1156 }
1157 Variable &dest = getVariable(instr.op1.op);
1158 int64_t v1, v2;
1159 if (instr.op3.op.empty()) {
1160 v1 = dest.var_value.int_value;
1161 v2 = isVariable(instr.op2.op) ? getVariable(instr.op2.op).var_value.int_value : std::stoll(instr.op2.op, nullptr, 0);
1162 } else {
1163 v1 = isVariable(instr.op2.op) ? getVariable(instr.op2.op).var_value.int_value : std::stoll(instr.op2.op, nullptr, 0);
1164 v2 = isVariable(instr.op3.op) ? getVariable(instr.op3.op).var_value.int_value : std::stoll(instr.op3.op, nullptr, 0);
1165 }
1166 dest.var_value.int_value = v1 ^ v2;
1168 }
1169
1171 if (!isVariable(instr.op1.op)) {
1172 throw mx::Exception("ALLOC destination must be a variable");
1173 }
1174 Variable &dest = getVariable(instr.op1.op);
1175 int64_t size = 0;
1176 int64_t count = 1;
1177
1178 if (!instr.op2.op.empty()) {
1179 if (isVariable(instr.op2.op)) {
1180 size = getVariable(instr.op2.op).var_value.int_value;
1181 } else {
1182 size = std::stoll(instr.op2.op, nullptr, 0);
1183 }
1184 }
1185
1186 if (!instr.op3.op.empty()) {
1187 if (isVariable(instr.op3.op)) {
1188 count = getVariable(instr.op3.op).var_value.int_value;
1189 } else {
1190 count = std::stoll(instr.op3.op, nullptr, 0);
1191 }
1192 }
1193 if (size <= 0 || count <= 0) {
1194 throw mx::Exception("ALLOC: size and count must be positive");
1195 }
1196 releaseOwnedPointer(dest);
1199 dest.var_value.ptr_value = calloc(static_cast<size_t>(count), static_cast<size_t>(size));
1200 if (dest.var_value.ptr_value == nullptr) {
1201 throw mx::Exception("ALLOC failed: calloc returned nullptr");
1202 }
1203 dest.var_value.ptr_size = size;
1204 dest.var_value.ptr_count = count;
1205 dest.var_value.owns = true;
1206 }
1207 void Program::exec_free(const Instruction &instr) {
1208 if (!isVariable(instr.op1.op)) {
1209 throw mx::Exception("FREE argument must be a variable");
1210 }
1211 Variable &var = getVariable(instr.op1.op);
1212 if (var.type == VarType::VAR_POINTER && var.var_value.ptr_value != nullptr) {
1213 void *ptr_to_free = var.var_value.ptr_value;
1214 std::free(ptr_to_free);
1215 // Clear this variable
1216 var.var_value.ptr_value = nullptr;
1217 var.var_value.owns = false;
1218 var.var_value.ptr_size = 0;
1219 var.var_value.ptr_count = 0;
1220 // Clear any other variables pointing to the same address to prevent double-free
1221 for (auto &kv : vars) {
1222 if (&kv.second != &var &&
1223 kv.second.type == VarType::VAR_POINTER &&
1224 kv.second.var_value.ptr_value == ptr_to_free) {
1225 kv.second.var_value.ptr_value = nullptr;
1226 kv.second.var_value.owns = false;
1227 kv.second.var_value.ptr_size = 0;
1228 kv.second.var_value.ptr_count = 0;
1229 }
1230 }
1231 }
1232 }
1233
1234 void Program::exec_lea(const Instruction &instr) {
1235 if (!isVariable(instr.op1.op))
1236 throw mx::Exception("LEA destination must be a variable");
1237 if (!isVariable(instr.op2.op))
1238 throw mx::Exception("LEA source must be a variable");
1239
1240 Variable &dest = getVariable(instr.op1.op);
1241 Variable &src = getVariable(instr.op2.op);
1242
1245
1246 switch (src.type) {
1248 case VarType::VAR_BYTE:
1250 dest.var_value.ptr_size = sizeof(int64_t);
1251 break;
1252 case VarType::VAR_FLOAT:
1254 dest.var_value.ptr_size = sizeof(double);
1255 break;
1258 dest.var_value.ptr_size = sizeof(void *);
1259 break;
1260 default:
1262 dest.var_value.ptr_size = sizeof(int64_t);
1263 break;
1264 }
1265 dest.var_value.ptr_count = 1;
1266 dest.var_value.owns = false;
1267 }
1268
1269 void Program::exec_not(const Instruction &instr) {
1270 if (!isVariable(instr.op1.op)) {
1271 throw mx::Exception("NOT destination must be a variable");
1272 }
1273 Variable &dest = getVariable(instr.op1.op);
1274 if (dest.var_value.type != VarType::VAR_INTEGER) {
1275 throw mx::Exception("Error NOT bitwise operation must be on integer value");
1276 }
1277 dest.var_value.int_value = (dest.var_value.int_value == 0) ? 1 : 0;
1279 }
1280
1281 void Program::exec_mod(const Instruction &instr) {
1282 if (!isVariable(instr.op1.op)) {
1283 std::cerr << "Error: MOD destination must be a variable, not a constant\n";
1284 return;
1285 }
1286 Variable &dest = getVariable(instr.op1.op);
1287
1288 Variable *src1 = nullptr, *src2 = nullptr;
1289 Variable temp1, temp2;
1290
1291 if (instr.op3.op.empty()) {
1292 src1 = &dest;
1293 if (isVariable(instr.op2.op)) {
1294 src2 = &getVariable(instr.op2.op);
1295 } else {
1296 temp2 = createTempVariable(dest.type, instr.op2.op);
1297 src2 = &temp2;
1298 }
1299 } else {
1300 if (isVariable(instr.op2.op)) {
1301 src1 = &getVariable(instr.op2.op);
1302 } else {
1303 temp1 = createTempVariable(dest.type, instr.op2.op);
1304 src1 = &temp1;
1305 }
1306 if (isVariable(instr.op3.op)) {
1307 src2 = &getVariable(instr.op3.op);
1308 } else {
1309 temp2 = createTempVariable(dest.type, instr.op3.op);
1310 src2 = &temp2;
1311 }
1312 }
1313
1314 if (dest.type == VarType::VAR_INTEGER || dest.type == VarType::VAR_BYTE) {
1315 int64_t v1 = (src1->type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src1->var_value.float_value) : src1->var_value.int_value;
1316 int64_t v2 = (src2->type == VarType::VAR_FLOAT) ? static_cast<int64_t>(src2->var_value.float_value) : src2->var_value.int_value;
1317 if (v2 == 0) {
1318 throw mx::Exception("MOD: integer modulo by zero");
1319 } else {
1320 dest.var_value.int_value = v1 % v2;
1321 }
1323 } else {
1324 std::cerr << "MOD only supports integer types\n";
1325 }
1326 }
1327
1328 void Program::exec_je(const Instruction &instr) {
1329 if (zero_flag)
1330 exec_jmp(instr);
1331 else
1332 pc++;
1333 }
1334
1335 void Program::exec_jne(const Instruction &instr) {
1336 if (!zero_flag)
1337 exec_jmp(instr);
1338 else
1339 pc++;
1340 }
1341
1342 void Program::exec_jl(const Instruction &instr) {
1343 if (less_flag)
1344 exec_jmp(instr);
1345 else
1346 pc++;
1347 }
1348
1349 void Program::exec_jle(const Instruction &instr) {
1350 if (less_flag || zero_flag)
1351 exec_jmp(instr);
1352 else
1353 pc++;
1354 }
1355
1356 void Program::exec_jg(const Instruction &instr) {
1357 if (greater_flag)
1358 exec_jmp(instr);
1359 else
1360 pc++;
1361 }
1362
1363 void Program::exec_jge(const Instruction &instr) {
1364 if (greater_flag || zero_flag)
1365 exec_jmp(instr);
1366 else
1367 pc++;
1368 }
1369
1370 void Program::exec_jz(const Instruction &instr) {
1371 if (zero_flag)
1372 exec_jmp(instr);
1373 else
1374 pc++;
1375 }
1376
1377 void Program::exec_jnz(const Instruction &instr) {
1378 if (!zero_flag)
1379 exec_jmp(instr);
1380 else
1381 pc++;
1382 }
1383
1384 void Program::exec_ja(const Instruction &instr) {
1385 if (greater_flag)
1386 exec_jmp(instr);
1387 else
1388 pc++;
1389 }
1390
1391 void Program::exec_jb(const Instruction &instr) {
1392 if (less_flag)
1393 exec_jmp(instr);
1394 else
1395 pc++;
1396 }
1397
1399 if (!isVariable(instr.op1.op)) {
1400 throw mx::Exception("STRING_PRINT destination must be a variable");
1401 }
1402 Variable &dest = getVariable(instr.op1.op);
1403
1404 if (dest.type != VarType::VAR_STRING) {
1405 throw mx::Exception("STRING_PRINT destination must be a string variable");
1406 }
1407
1408 std::string format;
1409 std::vector<Variable> tempArgs;
1410 std::vector<Variable *> args;
1411 if (isVariable(instr.op2.op)) {
1412 Variable &fmtVar = getVariable(instr.op2.op);
1413 if (fmtVar.type != VarType::VAR_STRING)
1414 throw mx::Exception("STRING_PRINT format must be a string variable");
1415 format = fmtVar.var_value.str_value;
1416 } else {
1417 format = instr.op2.op;
1418 }
1419
1420 auto getArgVariable = [&](const Operand &op, VarType type = VarType::VAR_INTEGER) -> Variable * {
1421 if (isVariable(op.op)) {
1422 return &getVariable(op.op);
1423 } else {
1424 if (op.type == OperandType::OP_VARIABLE) {
1425 throw mx::Exception("string_print instruction variable not defined: " + op.op);
1426 }
1427 tempArgs.push_back(createTempVariable(type, op.op));
1428 return &tempArgs.back();
1429 }
1430 };
1431
1432 if (!instr.op3.op.empty()) {
1433 args.push_back(getArgVariable(instr.op3));
1434 }
1435 for (const auto &vop : instr.vop) {
1436 if (!vop.op.empty()) {
1437 args.push_back(getArgVariable(vop));
1438 }
1439 }
1440
1441 std::ostringstream oss;
1442 size_t argIndex = 0;
1443 const char *fmt = format.c_str();
1444 char buffer[4096];
1445
1446 for (size_t i = 0; i < format.length(); ++i) {
1447 if (fmt[i] == '%' && i + 1 < format.length()) {
1448 size_t start = i;
1449 size_t j = i + 1;
1450 while (j < format.length() &&
1451 (fmt[j] == '-' || fmt[j] == '+' || fmt[j] == ' ' || fmt[j] == '#' || fmt[j] == '0' ||
1452 (fmt[j] >= '0' && fmt[j] <= '9') || fmt[j] == '.' ||
1453 fmt[j] == 'l' || fmt[j] == 'h' || fmt[j] == 'z' || fmt[j] == 'j' || fmt[j] == 't')) {
1454 ++j;
1455 }
1456 if (j < format.length() && fmt[j] == '%') {
1457 oss << '%';
1458 i = j;
1459 continue;
1460 }
1461 if (j >= format.length())
1462 break;
1463 if (!std::isalpha(fmt[j])) {
1464 oss << fmt[i];
1465 continue;
1466 }
1467 std::string spec(fmt + start, fmt + j + 1);
1468 if (argIndex < args.size()) {
1469 Variable *arg = args[argIndex++];
1470 if (arg->type == VarType::VAR_INTEGER) {
1471 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.int_value);
1472 } else if (arg->type == VarType::VAR_POINTER || arg->var_value.type == VarType::VAR_EXTERN) {
1473 if (spec.find("%s") != std::string::npos) {
1474 std::snprintf(buffer, sizeof(buffer), spec.c_str(), static_cast<char *>(arg->var_value.ptr_value));
1475 } else {
1476 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.ptr_value);
1477 }
1478 } else if (arg->type == VarType::VAR_FLOAT) {
1479 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.float_value);
1480 } else if (arg->type == VarType::VAR_STRING) {
1481 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.str_value.c_str());
1482 } else if (arg->type == VarType::VAR_BYTE) {
1483 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.int_value);
1484 } else {
1485 std::snprintf(buffer, sizeof(buffer), "%s", "(unsupported)");
1486 }
1487 oss << buffer;
1488 } else {
1489 oss << spec;
1490 }
1491 i = j;
1492 } else {
1493 oss << fmt[i];
1494 }
1495 }
1496
1497 dest.var_value.str_value = oss.str();
1499 }
1500
1501 std::string Program::printFormatted(const std::string &format, const std::vector<Variable *> &args, bool output) {
1502 std::ostringstream oss;
1503 size_t argIndex = 0;
1504 const char *fmt = format.c_str();
1505 char buffer[4096];
1506 for (size_t i = 0; i < format.length(); ++i) {
1507 if (fmt[i] == '%' && i + 1 < format.length()) {
1508 size_t start = i;
1509 size_t j = i + 1;
1510 while (j < format.length() &&
1511 (fmt[j] == '-' || fmt[j] == '+' || fmt[j] == ' ' || fmt[j] == '#' || fmt[j] == '0' ||
1512 (fmt[j] >= '0' && fmt[j] <= '9') || fmt[j] == '.' ||
1513 fmt[j] == 'l' || fmt[j] == 'h' || fmt[j] == 'z' || fmt[j] == 'j' || fmt[j] == 't')) {
1514 ++j;
1515 }
1516 if (j < format.length() && fmt[j] == '%') {
1517 oss << '%';
1518 i = j;
1519 continue;
1520 }
1521 if (j >= format.length())
1522 break;
1523 if (!std::isalpha(fmt[j])) {
1524 oss << fmt[i];
1525 continue;
1526 }
1527 std::string spec(fmt + start, fmt + j + 1);
1528 if (argIndex < args.size()) {
1529
1530 Variable *arg = args[argIndex++];
1531 if (arg->type == VarType::VAR_INTEGER) {
1532 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.int_value);
1533 } else if (arg->type == VarType::VAR_POINTER || arg->var_value.type == VarType::VAR_EXTERN) {
1534 if (spec.find("%s") != std::string::npos) {
1535 std::snprintf(buffer, sizeof(buffer), spec.c_str(), static_cast<char *>(arg->var_value.ptr_value));
1536 } else {
1537 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.ptr_value);
1538 }
1539 } else if (arg->type == VarType::VAR_FLOAT) {
1540 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.float_value);
1541 } else if (arg->type == VarType::VAR_STRING) {
1542 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.str_value.c_str());
1543 } else if (arg->type == VarType::VAR_BYTE) {
1544 std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg->var_value.int_value);
1545 } else {
1546 std::snprintf(buffer, sizeof(buffer), "%s", "(unsupported)");
1547 }
1548 oss << buffer;
1549 } else {
1550 oss << spec;
1551 }
1552 i = j;
1553 } else {
1554 oss << fmt[i];
1555 }
1556 }
1557 if (output)
1558 std::cout << oss.str();
1559 return oss.str();
1560 }
1562 if (!isVariable(instr.op1.op)) {
1563 throw mx::Exception("GETLINE destination must be a variable");
1564 }
1565 Variable &dest = getVariable(instr.op1.op);
1566 std::string input;
1567 std::getline(std::cin, input);
1568
1569 switch (dest.type) {
1571 dest.var_value.str_value = input;
1573 break;
1574 default:
1575 throw mx::Exception("GETLINE: unsupported variable type");
1576 }
1577 }
1578
1588 void Program::exec_push(const Instruction &instr) {
1589 if (!isVariable(instr.op1.op)) {
1590 try {
1591 int64_t int_val = std::stoll(instr.op1.op, nullptr, 0);
1592 stack.push(int_val);
1593 return;
1594 } catch (...) {
1595 throw mx::Exception("PUSH argument must be a variable or integer constant");
1596 }
1597 }
1598 Variable &var = getVariable(instr.op1.op);
1600 stack.push(var.var_value.int_value);
1602 stack.push(var.var_value.ptr_value);
1603 } else if (var.var_value.type == VarType::VAR_FLOAT) {
1604 stack.push(var.var_value.float_value);
1605 } else if (var.var_value.type == VarType::VAR_STRING) {
1606 stack.push(var.var_value.str_value);
1607 } else {
1608 throw mx::Exception("PUSH: unsupported variable type");
1609 }
1610 }
1611
1621 void Program::exec_pop(const Instruction &instr) {
1622 if (!isVariable(instr.op1.op)) {
1623 throw mx::Exception("POP destination must be a variable");
1624 }
1625 Variable &var = getVariable(instr.op1.op);
1626
1627 if (stack.empty()) {
1628 throw mx::Exception("POP from empty stack");
1629 }
1630
1631 StackValue value = stack.pop();
1632 if (var.type == VarType::VAR_INTEGER || var.type == VarType::VAR_BYTE) {
1633 if (!std::holds_alternative<int64_t>(value)) {
1634 throw mx::Exception("POP type mismatch: expected integer");
1635 }
1636 var.var_value.int_value = std::get<int64_t>(value);
1637 var.var_value.type = var.type;
1638 } else if (var.type == VarType::VAR_POINTER || var.type == VarType::VAR_EXTERN) {
1639 if (!std::holds_alternative<void *>(value)) {
1640 throw mx::Exception("POP type mismatch: expected pointer");
1641 }
1642 var.var_value.ptr_value = std::get<void *>(value);
1643 var.var_value.type = var.type;
1644 } else if (var.type == VarType::VAR_FLOAT) {
1645 if (!std::holds_alternative<double>(value)) {
1646 throw mx::Exception("POP type mismatch: expected float");
1647 }
1648 var.var_value.float_value = std::get<double>(value);
1650 } else if (var.type == VarType::VAR_STRING) {
1651 if (!std::holds_alternative<std::string>(value)) {
1652 throw mx::Exception("POP type mismatch: expected string");
1653 }
1654 var.var_value.str_value = std::get<std::string>(value);
1656 } else {
1657 throw mx::Exception("POP: unsupported variable type");
1658 }
1659 }
1660
1662 if (!isVariable(instr.op1.op)) {
1663 throw mx::Exception("STACK_LOAD destination must be a variable");
1664 }
1665 Variable &dest = getVariable(instr.op1.op);
1666
1667 size_t index = 0;
1668 if (!instr.op2.op.empty()) {
1669 if (isVariable(instr.op2.op)) {
1670 index = static_cast<size_t>(getVariable(instr.op2.op).var_value.int_value);
1671 } else {
1672 index = static_cast<size_t>(std::stoll(instr.op2.op, nullptr, 0));
1673 }
1674 }
1675
1676 if (index >= stack.size()) {
1677 throw mx::Exception("STACK_LOAD: index out of bounds");
1678 }
1679
1680 StackValue &value = stack[index];
1681 if (dest.type == VarType::VAR_INTEGER || dest.type == VarType::VAR_BYTE) {
1682 if (!std::holds_alternative<int64_t>(value)) {
1683 throw mx::Exception("STACK_LOAD type mismatch: expected integer");
1684 }
1685 dest.var_value.int_value = std::get<int64_t>(value);
1686 dest.var_value.type = dest.type;
1687 } else if (dest.type == VarType::VAR_POINTER || dest.type == VarType::VAR_EXTERN) {
1688 if (!std::holds_alternative<void *>(value)) {
1689 throw mx::Exception("STACK_LOAD type mismatch: expected pointer");
1690 }
1691 dest.var_value.ptr_value = std::get<void *>(value);
1692 dest.var_value.type = dest.type;
1693 } else if (dest.type == VarType::VAR_FLOAT) {
1694 if (!std::holds_alternative<double>(value)) {
1695 throw mx::Exception("STACK_LOAD type mismatch: expected float");
1696 }
1697 dest.var_value.float_value = std::get<double>(value);
1699 } else if (dest.type == VarType::VAR_STRING) {
1700 if (!std::holds_alternative<std::string>(value)) {
1701 throw mx::Exception("STACK_LOAD type mismatch: expected string");
1702 }
1703 dest.var_value.str_value = std::get<std::string>(value);
1705 } else {
1706 throw mx::Exception("STACK_LOAD: unsupported variable type");
1707 }
1708 }
1709
1711 if (!isVariable(instr.op1.op)) {
1712 throw mx::Exception("STACK_STORE source must be a variable");
1713 }
1714 Variable &src = getVariable(instr.op1.op);
1715
1716 size_t index = 0;
1717 if (!instr.op2.op.empty()) {
1718 if (isVariable(instr.op2.op)) {
1719 index = static_cast<size_t>(getVariable(instr.op2.op).var_value.int_value);
1720 } else {
1721 index = static_cast<size_t>(std::stoll(instr.op2.op, nullptr, 0));
1722 }
1723 }
1724
1725 if (index >= stack.size()) {
1726 throw mx::Exception("STACK_STORE: index out of bounds");
1727 }
1728 StackValue &value = stack[index];
1729 if (src.type == VarType::VAR_INTEGER || src.type == VarType::VAR_BYTE) {
1730 value = src.var_value.int_value;
1731 } else if (src.type == VarType::VAR_POINTER || src.type == VarType::VAR_EXTERN) {
1732 value = src.var_value.ptr_value;
1733 } else if (src.type == VarType::VAR_FLOAT) {
1734 value = src.var_value.float_value;
1735 } else if (src.type == VarType::VAR_STRING) {
1736 value = src.var_value.str_value;
1737 } else {
1738 throw mx::Exception("STACK_STORE: unsupported variable type");
1739 }
1740 }
1741
1743 size_t count = 1;
1744 if (!instr.op1.op.empty()) {
1745 if (isVariable(instr.op1.op)) {
1746 count = static_cast<size_t>(getVariable(instr.op1.op).var_value.int_value);
1747 } else {
1748 count = static_cast<size_t>(std::stoll(instr.op1.op, nullptr, 0));
1749 }
1750 }
1751 if (count > stack.size()) {
1752 throw mx::Exception("STACK_SUB: not enough values on stack to pop " + std::to_string(count));
1753 }
1754 for (size_t i = 0; i < count; ++i) {
1755 (void)stack.pop();
1756 }
1757 }
1758
1759 void Program::exec_call(const Instruction &instr) {
1760 if (instr.op1.op.empty()) {
1761 throw mx::Exception("CALL requires a label operand");
1762 }
1763 auto it = labels.find(instr.op1.op);
1764 if (it != labels.end()) {
1765 stack.push(static_cast<int64_t>(pc + 1));
1766 pc = it->second.first;
1767 } else {
1768 throw mx::Exception("CALL Label not found: " + instr.op1.op);
1769 }
1770 }
1771
1772 void Program::exec_ret(const Instruction &instr) {
1773 if (stack.empty()) {
1774 throw mx::Exception("RET: stack is empty, no return address");
1775 }
1776 StackValue value = stack.pop();
1777 if (!std::holds_alternative<int64_t>(value)) {
1778 throw mx::Exception("RET: return address on stack is not an integer");
1779 }
1780 pc = static_cast<size_t>(std::get<int64_t>(value)) - 1;
1781 }
1782
1784 exitCode = 0;
1785 stop();
1786 }
1787
1789 if (!instr.op1.op.empty() && isVariable(instr.op1.op)) {
1790 Variable &v = getVariable(instr.op1.op);
1791 if (v.type == VarType::VAR_INTEGER) {
1792 if (isVariable(instr.op2.op)) {
1793 Variable &s = getVariable(instr.op2.op);
1794 if (s.type == VarType::VAR_STRING) {
1795 try {
1796 v.var_value.int_value = std::stoll(s.var_value.str_value, nullptr, 0);
1797 } catch (...) {
1798 v.var_value.int_value = 0;
1799 }
1800 } else if (s.type == VarType::VAR_FLOAT) {
1801 v.var_value.int_value = static_cast<int64_t>(s.var_value.float_value);
1802 }
1803 } else {
1804 throw mx::Exception("to_int second argument must be a variable");
1805 }
1806 } else {
1807 throw mx::Exception("to_int first argument must be an integer variable");
1808 }
1809 } else {
1810 throw mx::Exception("to_int first argument must be a variable");
1811 }
1812 }
1813
1815 if (!instr.op1.op.empty() && isVariable(instr.op1.op)) {
1816 Variable &v = getVariable(instr.op1.op);
1817 if (v.type == VarType::VAR_FLOAT) {
1818 if (isVariable(instr.op2.op)) {
1819 Variable &s = getVariable(instr.op2.op);
1820 if (s.type == VarType::VAR_STRING) {
1821 try {
1822 v.var_value.float_value = std::stod(s.var_value.str_value);
1824 } catch (...) {
1825 v.var_value.float_value = 0.0;
1827 }
1828 } else if (s.type == VarType::VAR_INTEGER || s.type == VarType::VAR_BYTE) {
1829 v.var_value.float_value = static_cast<double>(s.var_value.int_value);
1831 } else if (s.type == VarType::VAR_FLOAT) {
1834 } else if (s.type == VarType::VAR_POINTER || s.type == VarType::VAR_EXTERN) {
1835 v.var_value.float_value = static_cast<double>(reinterpret_cast<uintptr_t>(s.var_value.ptr_value));
1837 } else {
1838 throw mx::Exception("to_float second argument must be a variable");
1839 }
1840 } else {
1841 v.var_value.float_value = static_cast<double>(std::stoll(instr.op2.op, nullptr, 0));
1843 }
1844 } else {
1845 throw mx::Exception("to_float first argument must be a float variable");
1846 }
1847 } else {
1848 throw mx::Exception("to_float first argument must be a variable");
1849 }
1850 }
1851
1852 std::string toStringFromVarType(const VarType &v) {
1853 switch (v) {
1854 case VarType::VAR_BYTE:
1855 return "Byte";
1857 return "Integer";
1858 case VarType::VAR_FLOAT:
1859 return "Float";
1861 return "String";
1863 return "Pointer";
1865 return "Extern";
1866 default:
1867 return "Unknown";
1868 }
1869 return "Unknown";
1870 }
1871
1873 if (!instr.op1.op.empty() && isVariable(instr.op1.op)) {
1874 Variable &v = getVariable(instr.op1.op);
1875 std::string name = v.var_name;
1876 Variable &r = getVariable(result.op);
1878 v = r;
1879 v.var_name = name;
1880 if (r.type == VarType::VAR_POINTER) {
1881 r.var_value.owns = false;
1882 }
1883 }
1884 }
1885 void Program::exec_neg(const Instruction &instr) {
1886 if (!instr.op1.op.empty() && isVariable(instr.op1.op)) {
1887 Variable &v = getVariable(instr.op1.op);
1888 switch (v.type) {
1890 case VarType::VAR_BYTE:
1892 break;
1893 case VarType::VAR_FLOAT:
1895 break;
1896 default:
1897 throw mx::Exception("NEG: unsupported variable type");
1898 }
1899 }
1900 }
1901
1911 if (!isVariable(instr.op1.op)) {
1912 throw mx::Exception("REALLOC destination must be a variable");
1913 }
1914 Variable &dest = getVariable(instr.op1.op);
1915 int64_t size = 0;
1916 int64_t count = 1;
1917
1918 if (!instr.op2.op.empty()) {
1919 if (isVariable(instr.op2.op)) {
1920 size = getVariable(instr.op2.op).var_value.int_value;
1921 } else {
1922 size = std::stoll(instr.op2.op, nullptr, 0);
1923 }
1924 }
1925
1926 if (!instr.op3.op.empty()) {
1927 if (isVariable(instr.op3.op)) {
1928 count = getVariable(instr.op3.op).var_value.int_value;
1929 } else {
1930 count = std::stoll(instr.op3.op, nullptr, 0);
1931 }
1932 }
1933 if (size <= 0 || count < 0) {
1934 throw mx::Exception("REALLOC: size must be positive, count must be non-negative");
1935 }
1936
1937 size_t newBytes = static_cast<size_t>(count) * static_cast<size_t>(size);
1938 size_t oldBytes = static_cast<size_t>(dest.var_value.ptr_count) * static_cast<size_t>(dest.var_value.ptr_size);
1939
1940 if (count == 0) {
1941 // SetLength(arr, 0) — free the memory
1942 if (dest.var_value.ptr_value != nullptr && dest.var_value.owns) {
1943 std::free(dest.var_value.ptr_value);
1944 }
1945 dest.var_value.ptr_value = nullptr;
1946 dest.var_value.ptr_size = size;
1947 dest.var_value.ptr_count = 0;
1948 dest.var_value.owns = true;
1951 return;
1952 }
1953
1954 void *newPtr = std::realloc(dest.var_value.ptr_value, newBytes);
1955 if (newPtr == nullptr) {
1956 throw mx::Exception("REALLOC failed: realloc returned nullptr");
1957 }
1958
1959 // Zero-initialize newly allocated portion
1960 if (newBytes > oldBytes) {
1961 std::memset(static_cast<char *>(newPtr) + oldBytes, 0, newBytes - oldBytes);
1962 }
1963
1966 dest.var_value.ptr_value = newPtr;
1967 dest.var_value.ptr_size = size;
1968 dest.var_value.ptr_count = count;
1969 dest.var_value.owns = true;
1970 }
1971
1972 Variable Program::createTempVariable(VarType type, const std::string &value) {
1973 Variable temp;
1974 temp.type = type;
1975 temp.var_name = "";
1976 if (type == VarType::VAR_INTEGER) {
1977 if (value.empty())
1978 throw mx::Exception("empty integer constant: " + value);
1979 temp.var_value.int_value = std::stoll(value, nullptr, 0);
1981 } else if (type == VarType::VAR_FLOAT) {
1982 temp.var_value.float_value = std::stod(value);
1984 } else if (type == VarType::VAR_STRING) {
1985 temp.var_value.str_value = value;
1987 }
1988 return temp;
1989 }
1990
1991 void Program::setVariableFromConstant(Variable &var, const std::string &value) {
1992 if (var.type == VarType::VAR_INTEGER) {
1993 var.var_value.int_value = std::stoll(value, nullptr, 0);
1995 } else if (var.type == VarType::VAR_FLOAT) {
1996 var.var_value.float_value = std::stod(value);
1998 } else if (var.type == VarType::VAR_STRING) {
1999 var.var_value.str_value = value;
2001 } else if (var.type == VarType::VAR_POINTER) {
2002 if (value == "null" || value == "NULL" || value == "0") {
2003 var.var_value.ptr_value = nullptr;
2004 } else {
2005 uintptr_t addr = std::stoull(value, nullptr, 0);
2006 var.var_value.ptr_value = reinterpret_cast<void *>(addr);
2007 }
2009 var.var_value.owns = false;
2010 }
2011 }
2012
2013 void Program::print(std::ostream &out) {
2014 out << "Debug Information..\n";
2015 out << "=== INSTRUCTIONS ===\n";
2016 if (inc.empty()) {
2017 out << " (no instructions)\n";
2018 } else {
2019 out << std::left << std::setw(10) << "Addr"
2020 << std::setw(20) << "Instruction"
2021 << std::setw(25) << "Operand1"
2022 << std::setw(25) << "Operand2"
2023 << std::setw(25) << "Operand3"
2024 << std::setw(30) << "Extra Operands" << "\n";
2025 out << std::string(130, '-') << "\n";
2026
2027 for (size_t i = 0; i < inc.size(); ++i) {
2028 const auto &instr = inc[i];
2029 out << std::setw(10) << i;
2030 if (instr.instruction >= 0 && instr.instruction < static_cast<int>(IncType.size())) {
2031 out << std::setw(12) << IncType[instr.instruction];
2032 } else {
2033 out << std::setw(12) << "UNKNOWN";
2034 }
2035 out << std::setw(25) << (instr.op1.op.empty() ? "-" : instr.op1.op);
2036 out << std::setw(25) << (instr.op2.op.empty() ? "-" : instr.op2.op);
2037 out << std::setw(25) << (instr.op3.op.empty() ? "-" : instr.op3.op);
2038 if (!instr.vop.empty()) {
2039 std::string extraOps;
2040 for (size_t j = 0; j < instr.vop.size(); ++j) {
2041 if (j > 0)
2042 extraOps += ", ";
2043 extraOps += instr.vop[j].op;
2044 }
2045 out << std::setw(30) << extraOps;
2046 } else {
2047 out << std::setw(30) << "-";
2048 }
2049
2050 out << "\n";
2051 }
2052 }
2053 out << "\n";
2054 }
2055
2057 auto it = external_functions.find(instr.op1.op);
2058 if (it == external_functions.end()) {
2059 throw mx::Exception("INVOKE: external function not found: " + instr.op1.op);
2060 }
2061
2062 std::vector<Operand> args;
2063
2064 auto process_operand = [&](Operand op) {
2065 if (op.op.empty())
2066 return;
2067
2068 if (isVariable(op.op)) {
2069 Variable &v = getVariable(op.op);
2071 if (v.var_value.ptr_value == nullptr) {
2072 if (instr.op1.op == "strlen" || instr.op1.op == "strncpy" ||
2073 instr.op1.op == "strncat" || instr.op1.op == "draw_text") {
2074 op.op = "";
2075 op.type = OperandType::OP_CONSTANT;
2076 }
2077 }
2078 }
2079 }
2080 args.push_back(op);
2081 };
2082
2083 process_operand(instr.op2);
2084 process_operand(instr.op3);
2085 for (const auto &vop : instr.vop) {
2086 process_operand(vop);
2087 }
2088
2089 it->second.call(this, args);
2090 result.op = "%rax";
2091 }
2092
2093 void Program::post(std::ostream &out) {
2094 if (stack.empty()) {
2095 out << "Stack aligned.\n";
2096 } else {
2097 out << "Stack unaligned. {\n";
2098 for (size_t i = 0; i < stack.size(); ++i) {
2099 out << "\tAddress: [" << i << "] = ";
2100 const StackValue &value = stack[i];
2101 if (std::holds_alternative<int64_t>(value)) {
2102 out << std::get<int64_t>(value);
2103 } else if (std::holds_alternative<void *>(value)) {
2104 void *ptr = std::get<void *>(value);
2105 if (ptr == nullptr) {
2106 out << "null";
2107 } else {
2108 out << "0x" << std::hex << reinterpret_cast<uintptr_t>(ptr) << std::dec;
2109 }
2110 } else if (std::holds_alternative<double>(value)) {
2111 out << std::get<double>(value);
2112 } else if (std::holds_alternative<std::string>(value)) {
2113 out << "\"" << std::get<std::string>(value) << "\"";
2114 } else {
2115 out << "(unknown)";
2116 }
2117 out << "\n";
2118 }
2119 out << "}\n";
2120 }
2121 }
2122
2124 if (isVariable(op.op)) {
2125 return getVariable(op.op);
2126 } else if (op.type == OperandType::OP_CONSTANT) {
2128 }
2129 throw mx::Exception("Could not create variable from operand: " + op.op);
2130 }
2131
2132 void Program::exec_fcmp(const Instruction &instr) {
2133 Variable *var1 = nullptr;
2134 Variable *var2 = nullptr;
2135 Variable temp1, temp2;
2136
2137 if (isVariable(instr.op1.op)) {
2138 var1 = &getVariable(instr.op1.op);
2139 } else {
2141 var1 = &temp1;
2142 }
2143 if (isVariable(instr.op2.op)) {
2144 var2 = &getVariable(instr.op2.op);
2145 } else {
2147 var2 = &temp2;
2148 }
2149
2150 zero_flag = false;
2151 less_flag = false;
2152 greater_flag = false;
2153
2154 double val1, val2;
2155 if (var1->type == VarType::VAR_FLOAT) {
2156 val1 = var1->var_value.float_value;
2157 } else if (var1->type == VarType::VAR_INTEGER || var1->type == VarType::VAR_BYTE) {
2158 val1 = static_cast<double>(var1->var_value.int_value);
2159 } else {
2160 throw mx::Exception("FCMP: unsupported operand type for var1");
2161 }
2162
2163 if (var2->type == VarType::VAR_FLOAT) {
2164 val2 = var2->var_value.float_value;
2165 } else if (var2->type == VarType::VAR_INTEGER || var2->type == VarType::VAR_BYTE) {
2166 val2 = static_cast<double>(var2->var_value.int_value);
2167 } else {
2168 throw mx::Exception("FCMP: unsupported operand type for var2");
2169 }
2170
2171 if (val1 == val2) {
2172 zero_flag = true;
2173 } else if (val1 < val2) {
2174 less_flag = true;
2175 } else {
2176 greater_flag = true;
2177 }
2178 }
2179
2180 void Program::exec_jae(const Instruction &instr) {
2181 if (greater_flag || zero_flag)
2182 exec_jmp(instr);
2183 else
2184 pc++;
2185 }
2186
2187 void Program::exec_jbe(const Instruction &instr) {
2188 if (less_flag || zero_flag)
2189 exec_jmp(instr);
2190 else
2191 pc++;
2192 }
2193
2194 void Program::exec_jc(const Instruction &instr) {
2195 if (carry_flag)
2196 exec_jmp(instr);
2197 else
2198 pc++;
2199 }
2200
2201 void Program::exec_jnc(const Instruction &instr) {
2202 if (!carry_flag)
2203 exec_jmp(instr);
2204 else
2205 pc++;
2206 }
2207
2208 void Program::exec_jp(const Instruction &instr) {
2209 pc++;
2210 }
2211
2212 void Program::exec_jnp(const Instruction &instr) {
2213 exec_jmp(instr);
2214 }
2215
2216 void Program::exec_jo(const Instruction &instr) {
2217 pc++;
2218 }
2219
2220 void Program::exec_jno(const Instruction &instr) {
2221 exec_jmp(instr);
2222 }
2223
2224 void Program::exec_js(const Instruction &instr) {
2225 pc++;
2226 }
2227
2228 void Program::exec_jns(const Instruction &instr) {
2229 exec_jmp(instr);
2230 }
2231
2232} // namespace mxvm
General-purpose exception with errno-aware factory method.
Definition exception.hpp:38
static std::unordered_map< std::string, Program * > object_map
object name -> Program mapping
Definition icode.hpp:201
std::unordered_map< std::string, std::pair< uint64_t, bool > > labels
label -> (address, is_function)
Definition icode.hpp:194
std::vector< Instruction > inc
instruction stream
Definition icode.hpp:192
std::unordered_map< std::string, Variable > vars
variable symbol table
Definition icode.hpp:193
void add_label(const std::string &name, uint64_t address, bool f)
Register a label at the given instruction address.
Definition icode.cpp:241
static Base * base
pointer to the main program base
Definition icode.hpp:197
std::unordered_map< std::string, RuntimeFunction > external_functions
resolved runtime function bindings
Definition icode.hpp:196
void add_instruction(const Instruction &i)
Append an instruction to the instruction stream.
Definition icode.cpp:237
Variable createTempVariable(VarType type, const std::string &value)
Create a temporary unnamed variable with the given type and value.
void setVariableFromConstant(Variable &var, const std::string &value)
Set a variable's value from a constant string (integer, float, or string literal).
void exec_jns(const Instruction &instr)
void exec_jae(const Instruction &instr)
Variable & getVariable(const std::string &name)
Look up a variable by name, searching local scope then global.
void exec_mul(const Instruction &instr)
void exec_jc(const Instruction &instr)
void exec_free(const Instruction &instr)
void exec_load(const Instruction &instr)
int exec()
Execute the program via the interpreter.
void exec_exit(const Instruction &instr)
void flatten_label(Program *root, int64_t offset, const std::string &label, bool func)
Copy a label from a child object into the root label map with an address offset.
void divVariables(Variable &dest, Variable &src1, Variable &src2)
Divide src1 by src2 and store the result in dest.
void exec_push(const Instruction &instr)
Execute a PUSH instruction — save a variable or constant onto the VM stack.
void stop()
Halt the interpreter loop.
Stack stack
Definition icode.hpp:858
size_t pc
program counter
Definition icode.hpp:399
bool less_flag
comparison less-than flag
Definition icode.hpp:403
void exec_mod(const Instruction &instr)
void exec_jg(const Instruction &instr)
void add_standard()
Register standard library runtime functions.
Definition icode.cpp:107
void exec_jl(const Instruction &instr)
void exec_stack_load(const Instruction &instr)
bool isVariable(const std::string &name)
Check whether a variable exists in local or global scope.
void exec_to_int(const Instruction &instr)
void exec_jz(const Instruction &instr)
void exec_fcmp(const Instruction &instr)
std::vector< std::string > args
Definition icode.hpp:407
void flatten_external(Program *root, const std::string &e, RuntimeFunction &r)
Copy an external function binding from a child object into the root.
void print(std::ostream &out)
Print all instructions and data to an output stream.
void post(std::ostream &out)
Generate native data section (variable declarations) into the output stream.
void exec_to_float(const Instruction &instr)
void exec_done(const Instruction &instr)
void exec_store(const Instruction &instr)
Execute a STORE instruction — write a value into a pointer at a given offset.
void exec_sub(const Instruction &instr)
void exec_or(const Instruction &instr)
std::vector< std::unique_ptr< Program > > objects
Definition icode.hpp:340
void exec_mov(const Instruction &instr)
void exec_return(const Instruction &instr)
void exec_not(const Instruction &instr)
bool carry_flag
comparison carry flag
Definition icode.hpp:405
void exec_cmp(const Instruction &instr)
void exec_je(const Instruction &instr)
void exec_invoke(const Instruction &instr)
void exec_js(const Instruction &instr)
void exec_jo(const Instruction &instr)
Variable variableFromOperand(const Operand &op)
Resolve an Operand to its corresponding Variable.
void exec_realloc(const Instruction &instr)
Execute the REALLOC instruction in the interpreter.
void exec_add(const Instruction &instr)
void exec_and(const Instruction &instr)
void exec_xor(const Instruction &instr)
void exec_jnz(const Instruction &instr)
std::string name
Definition icode.hpp:306
void flatten(Program *program)
Flatten child object programs into the root instruction stream.
void exec_neg(const Instruction &instr)
void exec_stack_sub(const Instruction &instr)
void exec_call(const Instruction &instr)
void exec_string_print(const Instruction &instr)
void exec_jne(const Instruction &instr)
void exec_jge(const Instruction &instr)
void exec_div(const Instruction &instr)
void exec_jmp(const Instruction &instr)
bool zero_flag
comparison zero flag
Definition icode.hpp:402
void exec_jle(const Instruction &instr)
void exec_getline(const Instruction &instr)
void exec_alloc(const Instruction &instr)
void exec_jbe(const Instruction &instr)
int getExitCode() const
Get the program's exit code after execution.
Definition icode.hpp:305
void exec_jb(const Instruction &instr)
void exec_jnp(const Instruction &instr)
bool greater_flag
comparison greater-than flag
Definition icode.hpp:404
void exec_lea(const Instruction &instr)
void addVariables(Variable &dest, Variable &src1, Variable &src2)
Add two variables and store the result in dest.
void flatten_inc(Program *root, Instruction &i)
Rewrite object-qualified operand references in a single instruction.
bool validateNames(Validator &v)
Validate all variable and label names against the Validator.
Operand result
return value operand from last invoke
Definition icode.hpp:859
void exec_jno(const Instruction &instr)
void exec_ja(const Instruction &instr)
void exec_pop(const Instruction &instr)
Execute a POP instruction — restore a value from the VM stack into a variable.
void exec_jp(const Instruction &instr)
void mulVariables(Variable &dest, Variable &src1, Variable &src2)
Multiply two variables and store the result in dest.
Program()
Default constructor — initializes execution state (pc, flags, etc.).
Definition icode.cpp:96
void setVariableFromString(Variable &var, const std::string &value)
Set a variable's value from a string representation, coercing types.
void exec_jnc(const Instruction &instr)
std::string printFormatted(const std::string &format, const std::vector< Variable * > &args, bool output=true)
Format and optionally print a printf-style format string with variable arguments.
void exec_print(const Instruction &instr)
void exec_ret(const Instruction &instr)
bool running
interpreter running flag
Definition icode.hpp:400
void exec_stack_store(const Instruction &instr)
void subVariables(Variable &dest, Variable &src1, Variable &src2)
Subtract src2 from src1 and store the result in dest.
Wraps a dynamically loaded native function from a shared library module.
Definition icode.hpp:78
Validates MXVM source for correct variable/label usage and instruction operands.
Definition valid.hpp:69
std::vector< VarNamePair > var_names
Definition valid.hpp:90
int64_t pos(const char *substr, const char *s)
Definition cstring.c:51
Intermediate code representation, execution engine, and native x64 code generation (SysV + Win64).
std::vector< std::string > IncType
String representations of Inc opcodes, indexed by enum value.
Definition instruct.hpp:82
@ JLE
Definition instruct.hpp:40
@ NOT
Definition instruct.hpp:33
@ POP
Definition instruct.hpp:53
@ JNO
Definition instruct.hpp:74
@ MUL
Definition instruct.hpp:28
@ JNZ
Definition instruct.hpp:44
@ SUB
Definition instruct.hpp:27
@ MOD
Definition instruct.hpp:34
@ ALLOC
Definition instruct.hpp:49
@ JMP
Definition instruct.hpp:36
@ GETLINE
Definition instruct.hpp:51
@ STRING_PRINT
Definition instruct.hpp:59
@ JBE
Definition instruct.hpp:68
@ JE
Definition instruct.hpp:37
@ LEA
Definition instruct.hpp:77
@ RETURN
Definition instruct.hpp:64
@ JNP
Definition instruct.hpp:72
@ JO
Definition instruct.hpp:73
@ JA
Definition instruct.hpp:45
@ JS
Definition instruct.hpp:75
@ STACK_LOAD
Definition instruct.hpp:54
@ EXIT
Definition instruct.hpp:48
@ CMP
Definition instruct.hpp:35
@ DIV
Definition instruct.hpp:29
@ AND
Definition instruct.hpp:31
@ JGE
Definition instruct.hpp:42
@ JL
Definition instruct.hpp:39
@ OR
Definition instruct.hpp:30
@ LOAD
Definition instruct.hpp:24
@ DONE
Definition instruct.hpp:60
@ JZ
Definition instruct.hpp:43
@ STACK_SUB
Definition instruct.hpp:56
@ MOV
Definition instruct.hpp:23
@ JNC
Definition instruct.hpp:70
@ JC
Definition instruct.hpp:69
@ PRINT
Definition instruct.hpp:47
@ RET
Definition instruct.hpp:58
@ JAE
Definition instruct.hpp:67
@ STACK_STORE
Definition instruct.hpp:55
@ CALL
Definition instruct.hpp:57
@ XOR
Definition instruct.hpp:32
@ INVOKE
Definition instruct.hpp:63
@ FREE
Definition instruct.hpp:50
@ TO_INT
Definition instruct.hpp:61
@ ADD
Definition instruct.hpp:26
@ PUSH
Definition instruct.hpp:52
@ JNS
Definition instruct.hpp:76
@ JG
Definition instruct.hpp:41
@ JB
Definition instruct.hpp:46
@ REALLOC
Reallocate a dynamic memory block: realloc dest, elemSize, count.
Definition instruct.hpp:78
@ TO_FLOAT
Definition instruct.hpp:62
@ JP
Definition instruct.hpp:71
@ JNE
Definition instruct.hpp:38
@ NEG
Definition instruct.hpp:65
@ FCMP
Definition instruct.hpp:66
@ STORE
Definition instruct.hpp:25
Definition ast.hpp:14
bool instruct_mode
enable instruction trace mode
Definition parser.cpp:97
static void releaseOwnedPointer(Variable &v)
std::string toStringFromVarType(const VarType &v)
std::variant< int64_t, void *, double, std::string > StackValue
Stack element: integer, pointer, double, or string.
Definition icode.hpp:20
VarType
MXVM variable type discriminator.
Definition instruct.hpp:180
A complete MXVM instruction with opcode, operands, and optional label.
Definition instruct.hpp:168
std::vector< Operand > vop
variable-length operand list (for print, invoke, etc.)
Definition instruct.hpp:171
Inc instruction
the opcode
Definition instruct.hpp:169
Operand op3
up to three fixed operands
Definition instruct.hpp:170
A single instruction operand (constant value or variable reference).
Definition instruct.hpp:159
std::string op
textual operand value
Definition instruct.hpp:161
OperandType type
Definition instruct.hpp:163
uint64_t ptr_size
size of each element in allocated memory
Definition instruct.hpp:218
bool owns
true if this value owns the allocated memory
Definition instruct.hpp:222
std::string str_value
Definition instruct.hpp:211
uint64_t ptr_count
number of elements allocated
Definition instruct.hpp:219
A named variable with type, value, and optional object association.
Definition instruct.hpp:292
std::string var_name
Definition instruct.hpp:294
Variable_Value var_value
Definition instruct.hpp:295
std::string toString() const
Convert this variable to a human-readable string.
Definition instruct.cpp:90