#include "duffle.amd64.win32.h" // --- Semantic Tags (Using X-Macros & Enum_) --- #define Tag_Entries() \ X(Define, "Define", 0x0018AEFF, ":") \ X(Call, "Call", 0x00D6A454, "~") \ X(Data, "Data", 0x0094BAA1, "$") \ X(Imm, "Imm", 0x004AA4C2, "^") \ X(Comment, "Comment", 0x00AAAAAA, ".") \ X(Format, "Format", 0x003A2F3B, " ") \ X(Lambda, "Lambda", 0x00D675A4, "%") typedef Enum_(U4, STag) { #define X(n, s, c, p) tmpl(STag, n), Tag_Entries() #undef X STag_Count, }; global U4 tag_colors[] = { #define X(n, s, c, p) c, Tag_Entries() #undef X }; global const char* tag_prefixes[] = { #define X(n, s, c, p) p, Tag_Entries() #undef X }; global const char* tag_names[] = { #define X(n, s, c, p) s, Tag_Entries() #undef X }; #define pack_token(tag, val) ((u4_(tag) << 28) | (u4_(val) & 0x0FFFFFFF)) #define unpack_tag(token) ( ((token) >> 28) & 0x0F) #define unpack_val(token) ( (token) & 0x0FFFFFFF) #define TOKENS_PER_ROW 8 #define MODE_NAV 0 #define MODE_EDIT 1 global FArena tape_arena; global FArena anno_arena; global U8 cursor_idx = 0; global U4 editor_mode = MODE_NAV; global B4 mode_switch_now = false; global FArena code_arena; global U8 vm_rax = 0; global U8 vm_rdx = 0; global U8 vm_globals[16] = {0}; global B4 run_full = false; global U8 log_buffer[16] = {0}; global U4 log_count = 0; global S4 scroll_y_offset = 0; // New GDI log #define GDI_LOG_MAX_LINES 10 #define GDI_LOG_MAX_LINE_LEN 128 global char gdi_log_buffer[GDI_LOG_MAX_LINES][GDI_LOG_MAX_LINE_LEN] = {0}; global U4 gdi_log_count = 0; internal void debug_log(Str8 fmt, KTL_Str8 table) { // A static buffer for our log lines. LP_ UTF8 console_log_buffer[1024]; mem_zero(u8_(console_log_buffer), 1024); // Format the string. Str8 result = str8_fmt_ktl_buf(slice_ut_arr(console_log_buffer), table, fmt); // Also write to our GDI log buffer if (gdi_log_count < GDI_LOG_MAX_LINES) { U4 len_to_copy = result.len < GDI_LOG_MAX_LINE_LEN - 1 ? result.len : GDI_LOG_MAX_LINE_LEN - 1; mem_copy(u8_(gdi_log_buffer[gdi_log_count]), u8_(result.ptr), len_to_copy); gdi_log_buffer[gdi_log_count][len_to_copy] = '\0'; gdi_log_count++; } // Get stdout handle. MS_Handle stdout_handle = ms_get_std_handle(MS_STD_OUTPUT); // Write the formatted string. ms_write_console(stdout_handle, result.ptr, (U4)result.len, nullptr, 0); // Write a newline. ms_write_console(stdout_handle, (UTF8 const*r)"\n", 1, nullptr, 0); } U8 ms_builtin_print(U8 val, U8 rdx_val, U8 r8_val, U8 r9_val) { char hex1[9], hex2[9], hex3[9], hex4[9]; u64_to_hex(val, hex1, 8); hex1[8] = '\0'; u64_to_hex(rdx_val, hex2, 8); hex2[8] = '\0'; u64_to_hex(r8_val, hex3, 8); hex3[8] = '\0'; u64_to_hex(r9_val, hex4, 8); hex4[8] = '\0'; KTL_Slot_Str8 log_table[] = { { ktl_str8_key("v1"), str8(hex1) }, { ktl_str8_key("v2"), str8(hex2) }, { ktl_str8_key("v3"), str8(hex3) }, { ktl_str8_key("v4"), str8(hex4) }, }; debug_log(str8("FFI PRINT -> RCX: RDX: R8: R9:"), ktl_str8_from_arr(log_table)); if (log_count < 16) log_buffer[log_count++] = val; return val; } // Visual Linker & O(1) Dictionary global U4 tape_to_code_offset[65536] = {0}; // --- WinAPI Persistence --- #define MS_GENERIC_READ 0x80000000 #define MS_GENERIC_WRITE 0x40000000 #define MS_CREATE_ALWAYS 2 #define MS_OPEN_EXISTING 3 #define MS_FILE_ATTRIBUTE_NORMAL 0x80 #define MS_VK_F1 0x70 #define MS_VK_F2 0x71 WinAPI void* ms_create_file_a(char const* lpFileName, U4 dwDesiredAccess, U4 dwShareMode, void* lpSecurityAttributes, U4 dwCreationDisposition, U4 dwFlagsAndAttributes, void* hTemplateFile) asm("CreateFileA"); WinAPI B4 ms_write_file(void* hFile, void const* lpBuffer, U4 nNumberOfBytesToWrite, U4* lpNumberOfBytesWritten, void* lpOverlapped) asm("WriteFile"); WinAPI B4 ms_read_file(void* hFile, void* lpBuffer, U4 nNumberOfBytesToRead, U4* lpNumberOfBytesRead, void* lpOverlapped) asm("ReadFile"); WinAPI B4 ms_close_handle(void* hObject) asm("CloseHandle"); #define PRIM_SWAP 1 #define PRIM_MULT 2 #define PRIM_ADD 3 #define PRIM_FETCH 4 #define PRIM_DEC 5 #define PRIM_STORE 6 #define PRIM_RET_Z 7 #define PRIM_RET 8 #define PRIM_PRINT 9 #define PRIM_RET_S 10 #define PRIM_DUP 11 #define PRIM_DROP 12 #define PRIM_SUB 13 #define PRIM_EXECUTE 14 global const char* prim_names[] = { "", "SWAP ", "MULT ", "ADD ", "FETCH ", "DEC ", "STORE ", "RET_IF_Z", "RETURN ", "PRINT ", "RET_IF_S", "DUP ", "DROP ", "SUB ", "EXECUTE " }; internal U4 resolve_name_to_index(const char* ref_name); internal void relink_tape(void); IA_ void compile_and_run_tape(void); internal void save_cartridge(void) { void* hFile = ms_create_file_a("cartridge.bin", MS_GENERIC_WRITE, 0, nullptr, MS_CREATE_ALWAYS, MS_FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != (void*)-1) { U4 written = 0; ms_write_file(hFile, & tape_arena.used, 8, & written, nullptr); ms_write_file(hFile, & anno_arena.used, 8, & written, nullptr); ms_write_file(hFile, & cursor_idx, 8, & written, nullptr); ms_write_file(hFile, (void*)tape_arena.start, (U4)tape_arena.used, & written, nullptr); ms_write_file(hFile, (void*)anno_arena.start, (U4)anno_arena.used, & written, nullptr); ms_close_handle(hFile); } } internal void load_cartridge(void) { void* hFile = ms_create_file_a("cartridge.bin", MS_GENERIC_READ, 0, nullptr, MS_OPEN_EXISTING, MS_FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile != (void*)-1) { U4 read = 0; ms_read_file(hFile, & tape_arena.used, 8, & read, nullptr); ms_read_file(hFile, & anno_arena.used, 8, & read, nullptr); ms_read_file(hFile, & cursor_idx, 8, & read, nullptr); ms_read_file(hFile, (void*)tape_arena.start, (U4)tape_arena.used, & read, nullptr); ms_read_file(hFile, (void*)anno_arena.start, (U4)anno_arena.used, & read, nullptr); ms_close_handle(hFile); relink_tape(); compile_and_run_tape(); } } IA_ void scatter(U4 token, const char* anno_str) { if (tape_arena.used + sizeof(U4) <= tape_arena.capacity && anno_arena.used + sizeof(U8) <= anno_arena.capacity) { U4 tag = unpack_tag(token); U4 val = unpack_val(token); if (anno_str && (tag == STag_Call || tag == STag_Imm)) { val = resolve_name_to_index(anno_str); } U4*r ptr = u4_r(tape_arena.start + tape_arena.used); ptr[0] = pack_token(tag, val); tape_arena.used += sizeof(U4); U8*r aptr = u8_r(anno_arena.start + anno_arena.used); aptr[0] = 0; if (anno_str) { char* dest = (char*)aptr; int i = 0; while(i < 8 && anno_str[i]) { dest[i] = anno_str[i]; i ++; } } anno_arena.used += sizeof(U8); } } internal void emit8(U1 b) { if (code_arena.used + 1 <= code_arena.capacity) { u1_r(code_arena.start + code_arena.used)[0] = b; code_arena.used += 1; } } internal void emit32(U4 val) { if (code_arena.used + 4 <= code_arena.capacity) { u4_r(code_arena.start + code_arena.used)[0] = val; code_arena.used += 4; } } internal void emit64(U8 val) { if (code_arena.used + 8 <= code_arena.capacity) { u8_r(code_arena.start+ code_arena.used)[0] = val; code_arena.used += 8; } } internal void pad32(void) { while ((code_arena.used % 4) != 0) emit8(0x90); } internal U4 resolve_name_to_index(const char* ref_name) { U8 tape_count = tape_arena.used / sizeof(U4); U4*r tape_ptr = u4_r(tape_arena.start); U8*r anno_ptr = u8_r(anno_arena.start); U8 prim_count = array_len(prim_names); for (int p = 1; p < prim_count; p++) { int match = 1; for (int c = 0; c < 8; c++) { char c1 = ref_name[c] ? ref_name[c] : ' '; char c2 = prim_names[p][c] ? prim_names[p][c] : ' '; if (c1 != c2) { match = 0; break; } } if (match) return p + 0x10000; } for (U8 j = 0; j < tape_count; j++) { if (unpack_tag(tape_ptr[j]) == STag_Define) { char* def_name = (char*)&anno_ptr[j]; int match = 1; for (int c = 0; c < 8; c++) { char c1 = ref_name[c] ? ref_name[c] : ' '; char c2 = def_name[c] ? def_name[c] : ' '; if (c1 != c2) { match = 0; break; } } if (match) return j; } } return 0; } internal void relink_tape(void) { U8 tape_count = tape_arena.used / sizeof(U4); U4*r tape_ptr = u4_r(tape_arena.start); U8*r anno_ptr = u8_r(anno_arena.start); for (U8 i = 0; i < tape_count; i++) { U4 t = tape_ptr[i]; U4 tag = unpack_tag(t); if (tag == STag_Call || tag == STag_Imm) { char* ref_name = (char*)&anno_ptr[i]; U4 new_val = resolve_name_to_index(ref_name); tape_ptr[i] = pack_token(tag, new_val); } } } #pragma region x64 Emission DSL // =================================================================================================================== // x64 Emission DSL // Follows the same bit-field composition pattern as the GP command macros. // =================================================================================================================== // --- REX Prefix Composition --- // REX byte: 0100 W R X B // W = 64-bit operand width // R = extends ModRM.reg field to reach R8-R15 // X = extends SIB.index field to reach R8-R15 // B = extends ModRM.r/m field to reach R8-R15 #define x64_rex_offset_W 3 #define x64_rex_offset_R 2 #define x64_rex_offset_X 1 #define x64_rex_offset_B 0 #define x64_rex_base 0x40 #define x64_rex_W (0x1 << x64_rex_offset_W) // 64-bit operand size #define x64_rex_R (0x1 << x64_rex_offset_R) // Extend reg field #define x64_rex_X (0x1 << x64_rex_offset_X) // Extend index field #define x64_rex_B (0x1 << x64_rex_offset_B) // Extend r/m field #define x64_rex(flags) (x64_rex_base | (flags)) #define x64_REX x64_rex(x64_rex_W) // 0x48 - 64-bit, standard regs #define x64_REX_R x64_rex(x64_rex_W | x64_rex_R) // 0x4C - 64-bit, extended reg field #define x64_REX_B x64_rex(x64_rex_W | x64_rex_B) // 0x49 - 64-bit, extended r/m field #define x64_REX_RB x64_rex(x64_rex_W | x64_rex_R | x64_rex_B) // 0x4D // --- Register Encoding --- // These are the 3-bit register IDs used in ModRM and SIB fields. #define x64_reg_RAX 0x0 // 000 #define x64_reg_RCX 0x1 // 001 #define x64_reg_RDX 0x2 // 010 #define x64_reg_RBX 0x3 // 011 #define x64_reg_RSP 0x4 // 100 (also: SIB follows when in r/m with Mod != 11) #define x64_reg_RBP 0x5 // 101 (also: disp32 no base when Mod = 00) #define x64_reg_RSI 0x6 // 110 #define x64_reg_RDI 0x7 // 111 // Extended registers (require REX.R or REX.B) #define x64_reg_R8 0x0 // 000 + REX.R/B #define x64_reg_R9 0x1 // 001 + REX.R/B #define x64_reg_R10 0x2 // 010 + REX.R/B #define x64_reg_R11 0x3 // 011 + REX.R/B // --- ModRM Composition --- // ModRM byte: [Mod:2][Reg:3][R/M:3] // Mod=11 -> both operands are registers (no memory) // Mod=00 -> r/m is a memory address, no displacement // Mod=01 -> r/m is a memory address + 8-bit displacement // Mod=10 -> r/m is a memory address + 32-bit displacement #define x64_mod_mem 0x0 // 00 - memory, no displacement #define x64_mod_mem_disp8 0x1 // 01 - memory + 8-bit displacement #define x64_mod_mem_disp32 0x2 // 10 - memory + 32-bit displacement #define x64_mod_reg 0x3 // 11 - register direct (no memory) #define x64_modrm_offset_mod 6 #define x64_modrm_offset_reg 3 #define x64_modrm_offset_rm 0 #define x64_modrm(mod, reg, rm) \ (((mod) << x64_modrm_offset_mod) | ((reg) << x64_modrm_offset_reg) | ((rm) << x64_modrm_offset_rm)) // Register-to-register ModRM shortcuts (Mod=11, the common case) #define x64_modrm_rr(reg, rm) x64_modrm(x64_mod_reg, reg, rm) // Commonly used ModRM bytes in this runtime (reg direct) #define x64_modrm_RAX_RAX x64_modrm_rr(x64_reg_RAX, x64_reg_RAX) // 0xC0 #define x64_modrm_RAX_RCX x64_modrm_rr(x64_reg_RAX, x64_reg_RCX) // 0xC1 #define x64_modrm_RAX_RDX x64_modrm_rr(x64_reg_RAX, x64_reg_RDX) // 0xC2 #define x64_modrm_RAX_RBX x64_modrm_rr(x64_reg_RAX, x64_reg_RBX) // 0xC3 #define x64_modrm_RCX_RAX x64_modrm_rr(x64_reg_RCX, x64_reg_RAX) // 0xC8 #define x64_modrm_RCX_RBX x64_modrm_rr(x64_reg_RCX, x64_reg_RBX) // 0xCB #define x64_modrm_RDX_RAX x64_modrm_rr(x64_reg_RDX, x64_reg_RAX) // 0xD0 // Memory + disp8 ModRM shortcuts (Mod=01) #define x64_modrm_RAX_mem_disp8_RBX x64_modrm(x64_mod_mem_disp8, x64_reg_RAX, x64_reg_RBX) // 0x43 #define x64_modrm_RDX_mem_disp8_RBX x64_modrm(x64_mod_mem_disp8, x64_reg_RDX, x64_reg_RBX) // 0x53 // SIB-addressed ModRM (Mod=00, R/M=RSP signals SIB follows) #define x64_modrm_RAX_sib x64_modrm(x64_mod_mem, x64_reg_RAX, x64_reg_RSP) // 0x04 #define x64_modrm_RDX_sib x64_modrm(x64_mod_mem, x64_reg_RDX, x64_reg_RSP) // 0x14 // --- SIB Composition --- // SIB byte: [Scale:2][Index:3][Base:3] // Scale: 00=*1, 01=*2, 10=*4, 11=*8 #define x64_sib_scale_1 0x0 // 00 #define x64_sib_scale_2 0x1 // 01 #define x64_sib_scale_4 0x2 // 10 #define x64_sib_scale_8 0x3 // 11 #define x64_sib_offset_scale 6 #define x64_sib_offset_index 3 #define x64_sib_offset_base 0 #define x64_sib(scale, index, base) \ (((scale) << x64_sib_offset_scale) | ((index) << x64_sib_offset_index) | ((base) << x64_sib_offset_base)) // Tape drive SIB: [rbx + rax*8] // Scale=8, Index=RAX, Base=RBX #define x64_sib_tape x64_sib(x64_sib_scale_8, x64_reg_RAX, x64_reg_RBX) // 0xC3 // --- Opcodes --- #define x64_op_MOV_rm_reg 0x89 // mov r/m, reg (store: reg -> memory or register) #define x64_op_MOV_reg_rm 0x8B // mov reg, r/m (load: memory or register -> reg) #define x64_op_MOV_rm_imm32 0xC7 // mov r/m, imm32 (sign-extended to 64-bit) #define x64_op_MOV_rax_imm64 0xB8 // mov rax, imm64 (register baked into opcode) #define x64_op_MOV_r10_imm64 0xBA // mov r10, imm64 (B8 + r10_id=2, needs REX.B) #define x64_op_XCHG_rm_reg 0x87 // xchg r/m, reg #define x64_op_ADD_rm_reg 0x01 // add r/m, reg #define x64_op_SUB_rm_reg 0x29 // sub r/m, reg #define x64_op_IMUL_reg_rm 0x0F // imul prefix (followed by 0xAF) #define x64_op_IMUL_reg_rm2 0xAF // imul reg, r/m (second byte) #define x64_op_TEST_rm_reg 0x85 // test r/m, reg (sets ZF and SF) #define x64_op_UNARY 0xFF // inc/dec/call-indirect (Reg field = opcode extension) #define x64_op_ARITH_imm8 0x83 // add/sub/etc with sign-extended 8-bit immediate (Reg = extension) #define x64_op_ARITH_imm32 0x81 // add/sub/etc with 32-bit immediate (Reg = extension) // Opcode extensions (used as the Reg field of ModRM with 0xFF and 0x83) #define x64_ext_INC 0x0 // /0 #define x64_ext_DEC 0x1 // /1 #define x64_ext_CALL 0x2 // /2 (used with 0xFF for indirect call) #define x64_ext_ADD 0x0 // /0 (used with 0x83/0x81) #define x64_ext_SUB 0x5 // /5 (used with 0x83/0x81) #define x64_op_CALL_rel32 0xE8 // call rel32 #define x64_op_JMP_rel32 0xE9 // jmp rel32 #define x64_op_JNZ_rel8 0x75 // jnz rel8 (jump if Zero Flag not set) #define x64_op_JNS_rel8 0x79 // jns rel8 (jump if Sign Flag not set) #define x64_op_RET 0xC3 // ret #define x64_op_NOP 0x90 // nop (used for padding to 32-bit alignment) // Push/Pop (opcode encodes register directly, no ModRM) #define x64_op_PUSH_RBX 0x53 // push rbx (50 + rbx_id=3) #define x64_op_POP_RBX 0x5B // pop rbx (58 + rbx_id=3) #define x64_op_PUSH_RDX 0x52 // push rdx (50 + rdx_id=2) #define x64_op_POP_RDX 0x5A // pop rdx (58 + rdx_id=2) // --- Composite Instruction Macros --- // Each maps directly to the emit8/emit32/emit64 calls in compile_action. // Stack Machine Operations #define x64_XCHG_RAX_RDX() do { emit8(x64_REX); emit8(x64_op_XCHG_rm_reg); emit8(x64_modrm_RAX_RDX); } while(0) #define x64_MOV_RDX_RAX() do { emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RAX_RDX); } while(0) // DUP #define x64_MOV_RAX_RDX() do { emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RDX_RAX); } while(0) // DROP // Arithmetic (2-register stack: op RAX with RDX, result in RAX) #define x64_ADD_RAX_RDX() do { emit8(x64_REX); emit8(x64_op_ADD_rm_reg); emit8(x64_modrm_RAX_RDX); } while(0) #define x64_SUB_RAX_RDX() do { emit8(x64_REX); emit8(x64_op_SUB_rm_reg); emit8(x64_modrm_RAX_RDX); } while(0) #define x64_IMUL_RAX_RDX() do { emit8(x64_REX); emit8(x64_op_IMUL_reg_rm); emit8(x64_op_IMUL_reg_rm2); emit8(x64_modrm_RAX_RDX); } while(0) #define x64_DEC_RAX() do { emit8(x64_REX); emit8(x64_op_UNARY); emit8(x64_modrm(x64_mod_reg, x64_ext_DEC, x64_reg_RAX)); } while(0) // Flag Operations (for conditional returns) #define x64_TEST_RAX_RAX() do { emit8(x64_REX); emit8(x64_op_TEST_rm_reg); emit8(x64_modrm_RAX_RAX); } while(0) // Conditional Returns (TEST must precede these) // JNZ skips the RET if RAX != 0, so RET only fires when RAX == 0 #define x64_RET_IF_ZERO() do { x64_TEST_RAX_RAX(); emit8(x64_op_JNZ_rel8); emit8(0x01); emit8(x64_op_RET); } while(0) // JNS skips the RET if RAX >= 0, so RET only fires when RAX < 0 #define x64_RET_IF_SIGN() do { x64_TEST_RAX_RAX(); emit8(x64_op_JNS_rel8); emit8(0x01); emit8(x64_op_RET); } while(0) // Tape Drive Memory (Preemptive Scatter via RBX base pointer) #define x64_FETCH() do { emit8(x64_REX); emit8(x #pragma endregion x64 Emission DSL internal void compile_action(U4 val) { if (val >= 0x10000) { U4 p = val - 0x10000; if (p == PRIM_SWAP) { emit8(0x48); emit8(0x87); emit8(0xC2); pad32(); return; } else if (p == PRIM_MULT) { emit8(0x48); emit8(0x0F); emit8(0xAF); emit8(0xC2); pad32(); return; } else if (p == PRIM_ADD) { emit8(0x48); emit8(0x01); emit8(0xD0); pad32(); return; } else if (p == PRIM_SUB) { emit8(0x48); emit8(0x29); emit8(0xD0); pad32(); return; } else if (p == PRIM_FETCH) { emit8(0x48); emit8(0x8B); emit8(0x04); emit8(0xC3); // mov rax, [rbx + rax*8] pad32(); return; } else if (p == PRIM_DEC) { emit8(0x48); emit8(0xFF); emit8(0xC8); pad32(); return; } else if (p == PRIM_STORE) { emit8(0x48); emit8(0x89); emit8(0x14); emit8(0xC3); // mov [rbx + rax*8], rdx pad32(); return; } else if (p == PRIM_RET_Z) { emit8(0x48); emit8(0x85); emit8(0xC0); emit8(0x75); emit8(0x01); emit8(0xC3); pad32(); return; } else if (p == PRIM_RET_S) { emit8(0x48); emit8(0x85); emit8(0xC0); emit8(0x79); emit8(0x01); emit8(0xC3); pad32(); return; } else if (p == PRIM_RET) { emit8(0xC3); pad32(); return; } else if (p == PRIM_DUP) { emit8(0x48); emit8(0x89); emit8(0xC2); pad32(); return; } else if (p == PRIM_DROP) { emit8(0x48); emit8(0x89); emit8(0xD0); pad32(); return; } else if (p == PRIM_EXECUTE) { emit8(0XFF); emit8(0XD0); pad32(); return; } else if (p == PRIM_PRINT) { // FFI Dance: Save RDX, Align RSP (32 shadow + 8 align = 40) emit8(0x52); // push rdx emit8(0x48); emit8(0x83); emit8(0xEC); emit8(0x28); // sub rsp, 40 // Map arguments: RCX=RAX, RDX=RDX(already loaded), R8=Globals[0], R9=Globals[1] emit8(0x48); emit8(0x89); emit8(0xC1); // mov rcx, rax emit8(0x4C); emit8(0x8B); emit8(0x03); // mov r8, [rbx] emit8(0x4C); emit8(0x8B); emit8(0x4B); emit8(0x08); // mov r9, [rbx+8] // Load func ptr and call emit8(0x49); emit8(0xBA); // mov r10, ... U8 addr = u8_(& ms_builtin_print); emit32(u4_(addr & 0xFFFFFFFF)); emit32(u4_(addr >> 32)); emit8(0x41); emit8(0xFF); emit8(0xD2); // call r10 // Restore emit8(0x48); emit8(0x83); emit8(0xC4); emit8(0x28); // add rsp, 40 emit8(0x5A); // pop rdx pad32(); return; } } if (val > 0 && val < 0x10000) { U4 target = tape_to_code_offset[val]; pad32(); S4 rel32 = s4_(target) - s4_(code_arena.used + 5); emit8(0xE8); emit32(u4_(rel32)); pad32(); } } IA_ void compile_and_run_tape(void) { farena_reset(& code_arena); log_count = 0; gdi_log_count = 0; emit8(0x53); // push rbx emit8(0x48); emit8(0x89); emit8(0xCB); // mov rbx, rcx emit8(0x48); emit8(0x8B); emit8(0x43); emit8(0x70); // mov rax, [rbx+0x70] emit8(0x48); emit8(0x8B); emit8(0x53); emit8(0x78); // mov rdx, [rbx+0x78] U4*r tape_ptr = u4_r(tape_arena.start); U8*r anno_ptr = u8_r(anno_arena.start); B4 in_def = false; U4 def_jmp_offset = 0; B4 in_lambda = false; U4 lambda_jmp_offset = 0; U8 end_idx = run_full ? (tape_arena.used / sizeof(U4)) : (cursor_idx + 1); for (U8 i = 0; i < end_idx; i++) { U4 tag = unpack_tag(tape_ptr[i]); U4 val = unpack_val(tape_ptr[i]); // NUDGE: Define what terminates blocks. B4 is_terminator = (tag == STag_Define || tag == STag_Imm); // Terminate lambdas first if needed if (in_lambda && (is_terminator || tag == STag_Lambda)) { emit8(0xC3); pad32(); // Terminate lambda with RET U4 current = code_arena.used; u4_r(code_arena.start + lambda_jmp_offset)[0] = current - (lambda_jmp_offset + 4); in_lambda = false; } // Terminate definitions if (in_def && is_terminator) { emit8(0xC3); pad32(); // Terminate definition with RET U4 current = code_arena.used; u4_r(code_arena.start + def_jmp_offset)[0] = current - (def_jmp_offset + 4); in_def = false; } if (tag == STag_Define) { pad32(); emit8(0xE9); def_jmp_offset = code_arena.used; emit32(0); // Placeholder for jump distance pad32(); in_def = true; tape_to_code_offset[i] = code_arena.used; emit8(0x48); emit8(0x87); emit8(0xC2); // xchg rax, rdx pad32(); } // NUDGE: Handle the new Lambda tag. else if (tag == STag_Lambda) { char* name = (char*)&anno_ptr[i]; char val_hex[9]; u64_to_hex(val, val_hex, 8); val_hex[8] = '\0'; KTL_Slot_Str8 call_log_table[] = { { ktl_str8_key("name"), str8(name) }, { ktl_str8_key("val"), str8(val_hex) }, }; debug_log(str8("Compiling lambda: (val: )"), ktl_str8_from_arr(call_log_table)); // Outer function: Push lambda address into RAX emit8(0x48); emit8(0x89); emit8(0xC2); // mov rdx, rax (save old rax) emit8(0x48); emit8(0xB8); // mov rax, ... (64-bit immediate) U4 rax_imm_offset = code_arena.used; emit64(0); // Placeholder for lambda address pad32(); // Outer function: Jump over lambda body emit8(0xE9); lambda_jmp_offset = code_arena.used; emit32(0); // Placeholder for jump distance pad32(); in_lambda = true; // Patch the mov rax, ... with the actual lambda body address U8 lambda_addr = u8_(code_arena.start + code_arena.used); u8_r(code_arena.start + rax_imm_offset)[0] = lambda_addr; } else if (tag == STag_Call || tag == STag_Imm) { compile_action(val); } else if (tag == STag_Data) { emit8(0x48); emit8(0x89); emit8(0xC2); emit8(0x48); emit8(0xC7); emit8(0xC0); emit32(val); pad32(); } } if (in_lambda) { emit8(0xC3); pad32(); U4 current = code_arena.used; u4_r(code_arena.start + lambda_jmp_offset)[0] = current - (lambda_jmp_offset + 4); } if (in_def) { emit8(0xC3); pad32(); U4 current = code_arena.used; u4_r(code_arena.start + def_jmp_offset)[0] = current - (def_jmp_offset + 4); } emit8(0x48); emit8(0x89); emit8(0x43); emit8(0x70); // mov [rbx+0x70], rax emit8(0x48); emit8(0x89); emit8(0x53); emit8(0x78); // mov [rbx+0x78], rdx emit8(0x5B); // pop rbx emit8(0xC3); // ret typedef void JIT_Func(U8* globals_ptr); JIT_Func* func = (JIT_Func*)code_arena.start; func(vm_globals); vm_rax = vm_globals[14]; vm_rdx = vm_globals[15]; char rax_hex[9]; u64_to_hex(vm_rax, rax_hex, 8); rax_hex[8] = '\0'; char rdx_hex[9]; u64_to_hex(vm_rdx, rdx_hex, 8); rdx_hex[8] = '\0'; KTL_Slot_Str8 post_jit_log_table[] = { { ktl_str8_key("rax"), str8(rax_hex) }, { ktl_str8_key("rdx"), str8(rdx_hex) }, }; debug_log(str8("JIT finished. RAX: , RDX: "), ktl_str8_from_arr(post_jit_log_table)); } #undef r #undef v #undef expect #include "microui.c" #undef expect #define expect(x,y) __builtin_expect(x, y) // so compiler knows the common path #define r restrict #define v volatile global mu_Context mu_ctx; internal int text_width_cb(mu_Font font, const char *str, int len) { if (len == -1) { len = 0; while (str[len]) len++; } return len * 11; // Approx 11px per char for Consolas 20 } internal int text_height_cb(mu_Font font) { return 20; // Consolas 20 height } internal void gdi_draw_rect(void* hdc, mu_Rect rect, mu_Color color) { U1 red = ((U1*)&color)[0]; U1 green = ((U1*)&color)[1]; U1 blue = ((U1*)&color)[2]; void* hBrush = ms_create_solid_brush((red) | (green << 8) | (blue << 16)); void* hOldBrush = ms_select_object(hdc, hBrush); ms_rectangle(hdc, rect.x - 1, rect.y - 1, rect.x + rect.w + 1, rect.y + rect.h + 1); ms_select_object(hdc, hOldBrush); ms_delete_object(hBrush); } internal void render_microui(void* hdc) { mu_Command *cmd = NULL; while (mu_next_command(&mu_ctx, &cmd)) { switch (cmd->type) { case MU_COMMAND_TEXT: { U1 red = ((U1*)&cmd->text.color)[0]; U1 green = ((U1*)&cmd->text.color)[1]; U1 blue = ((U1*)&cmd->text.color)[2]; ms_set_text_color(hdc, (red) | (green << 8) | (blue << 16)); int len = 0; while (cmd->text.str[len]) len++; ms_text_out_a(hdc, cmd->text.pos.x, cmd->text.pos.y, cmd->text.str, len); break; } case MU_COMMAND_RECT: { gdi_draw_rect(hdc, cmd->rect.rect, cmd->rect.color); break; } case MU_COMMAND_ICON: { gdi_draw_rect(hdc, cmd->icon.rect, cmd->icon.color); break; } case MU_COMMAND_CLIP: { break; } } } } S8 win_proc(void* hwnd, U4 msg, U8 wparam, S8 lparam) { U8 tape_count = tape_arena.used / sizeof(U4); U4*r tape_ptr = u4_r(tape_arena.start); switch (msg) { case MS_WM_CHAR: { char buf[2] = { (char)wparam, 0 }; mu_input_text(&mu_ctx, buf); if (editor_mode != MODE_EDIT) { ms_invalidate_rect(hwnd, nullptr, true); return 0; } U4 t = tape_ptr[cursor_idx]; U4 tag = unpack_tag(t); U4 val = unpack_val(t); U1 c = u1_(wparam); B4 should_skip = c < 32 || (c == 'e' && mode_switch_now); if (should_skip) { mode_switch_now = false; ms_invalidate_rect(hwnd, nullptr, true); return 0; } if (tag == STag_Data) { U4 digit = 16; if (c >= '0' && c <= '9') digit = c - '0'; if (c >= 'a' && c <= 'f') digit = c - 'a' + 10; if (c >= 'A' && c <= 'F') digit = c - 'A' + 10; if (digit < 16) { val = ((val << 4) | digit) & 0x0FFFFFFF; tape_ptr[cursor_idx] = pack_token(tag, val); } } else if (tag != STag_Format) { U8*r anno_ptr = u8_r(anno_arena.start); char* anno_str = (char*) & anno_ptr[cursor_idx]; int len = 0; while (len < 8 && anno_str[len] != '\0' && anno_str[len] != ' ') len ++; if (len < 8) { anno_str[len] = (char)c; for (int i = len + 1; i < 8; i++) anno_str[i] = '\0'; if (tag == STag_Call || tag == STag_Imm || tag == STag_Define) { U4 new_val = resolve_name_to_index(anno_str); tape_ptr[cursor_idx] = pack_token(tag, new_val); if (tag == STag_Define) relink_tape(); } } } vm_rax = 0; vm_rdx = 0; mem_zero(u8_(vm_globals), sizeof(vm_globals)); compile_and_run_tape(); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_MOUSEMOVE: { mu_input_mousemove(&mu_ctx, (S2)(lparam & 0xFFFF), (S2)((lparam >> 16) & 0xFFFF)); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_LBUTTONDOWN: { mu_input_mousedown(&mu_ctx, (S2)(lparam & 0xFFFF), (S2)((lparam >> 16) & 0xFFFF), MU_MOUSE_LEFT); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_LBUTTONUP: { mu_input_mouseup(&mu_ctx, (S2)(lparam & 0xFFFF), (S2)((lparam >> 16) & 0xFFFF), MU_MOUSE_LEFT); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_MOUSEWHEEL: { mu_input_scroll(&mu_ctx, 0, ((S2)((wparam >> 16) & 0xFFFF)) / -30); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_KEYDOWN: { int key = 0; if (wparam == MS_VK_BACK) key = MU_KEY_BACKSPACE; if (wparam == MS_VK_RETURN) key = MU_KEY_RETURN; if (wparam == 0x10) key = MU_KEY_SHIFT; if (wparam == 0x11) key = MU_KEY_CTRL; if (wparam == 0x12) key = MU_KEY_ALT; if (key) mu_input_keydown(&mu_ctx, key); if (wparam == 0x45 && editor_mode == MODE_NAV) { editor_mode = MODE_EDIT; mode_switch_now = true; ms_invalidate_rect(hwnd, nullptr, true); return 0; } if (wparam == 0x1B && editor_mode == MODE_EDIT) { editor_mode = MODE_NAV; relink_tape(); ms_invalidate_rect(hwnd, nullptr, true); return 0; } if (editor_mode == MODE_EDIT) { if (wparam == MS_VK_BACK) { U4 t = tape_ptr[cursor_idx]; U4 tag = unpack_tag(t); U4 val = unpack_val(t); if (tag == STag_Data) { val = val >> 4; tape_ptr[cursor_idx] = pack_token(tag, val); } else if (tag != STag_Format) { U8*r anno_ptr = u8_r(anno_arena.start); char* anno_str = (char*) & anno_ptr[cursor_idx]; int len = 0; while (len < 8 && anno_str[len] != '\0' && anno_str[len] != ' ') len ++; if (len > 0) { anno_str[len - 1] = '\0'; if (tag == STag_Call || tag == STag_Imm || tag == STag_Define) { U4 new_val = resolve_name_to_index(anno_str); tape_ptr[cursor_idx] = pack_token(tag, new_val); if (tag == STag_Define) relink_tape(); } } } vm_rax = 0; vm_rdx = 0; mem_zero(u8_(vm_globals), sizeof(vm_globals)); compile_and_run_tape(); ms_invalidate_rect(hwnd, nullptr, true); } return 0; } if (wparam == MS_VK_RIGHT && cursor_idx < tape_count - 1) cursor_idx ++; if (wparam == MS_VK_LEFT && cursor_idx > 0) cursor_idx --; if (wparam == MS_VK_UP) { U8 line_start = cursor_idx; while (line_start > 0 && unpack_tag(tape_ptr[line_start - 1]) != STag_Format) line_start--; if (line_start > 0) { U8 col = cursor_idx - line_start; U8 prev_line_start = line_start - 1; while (prev_line_start > 0 && unpack_tag(tape_ptr[prev_line_start - 1]) != STag_Format) prev_line_start--; U8 prev_line_len = (line_start - 1) - prev_line_start; cursor_idx = prev_line_start + (col < prev_line_len ? col : prev_line_len); } } if (wparam == MS_VK_DOWN) { U8 line_start = cursor_idx; while (line_start > 0 && unpack_tag(tape_ptr[line_start - 1]) != STag_Format) line_start --; U8 col = cursor_idx - line_start; U8 next_line_start = cursor_idx; while (next_line_start < tape_count && unpack_tag(tape_ptr[next_line_start]) != STag_Format) next_line_start ++; if (next_line_start < tape_count) { next_line_start ++; U8 next_line_end = next_line_start; while (next_line_end < tape_count && unpack_tag(tape_ptr[next_line_end]) != STag_Format) next_line_end ++; U8 next_line_len = next_line_end - next_line_start; cursor_idx = next_line_start + (col < next_line_len ? col : next_line_len); } } if (wparam == MS_VK_PRIOR) { scroll_y_offset -= 100; if (scroll_y_offset < 0) scroll_y_offset = 0; } if (wparam == MS_VK_NEXT) { scroll_y_offset += 100; } if (wparam == MS_VK_F5) { run_full = !run_full; } if (wparam == MS_VK_F1) { save_cartridge(); } if (wparam == MS_VK_F2) { load_cartridge(); ms_invalidate_rect(hwnd, nullptr, true); } if (wparam == MS_VK_TAB) { U4 t = tape_ptr[cursor_idx]; U4 tag = (unpack_tag(t) + 1) % STag_Count; tape_ptr[cursor_idx] = pack_token(tag, unpack_val(t)); } else if (wparam == MS_VK_BACK) { U8 delete_idx = cursor_idx; B4 is_shift = (ms_get_async_key_state(MS_VK_SHIFT) & 0x8000) != 0; if (is_shift == false) { if (cursor_idx > 0) { delete_idx = cursor_idx - 1; cursor_idx--; } else return 0; } if (tape_count > 0) { U8*r anno_ptr = u8_r(anno_arena.start); for (U8 i = delete_idx; i < tape_count - 1; i ++) { tape_ptr[i] = tape_ptr[i + 1]; anno_ptr[i] = anno_ptr[i + 1]; } tape_arena.used -= sizeof(U4); anno_arena.used -= sizeof(U8); } relink_tape(); } else if (wparam == MS_VK_SPACE || wparam == MS_VK_RETURN) { B4 is_shift = (ms_get_async_key_state(MS_VK_SHIFT) & 0x8000) != 0; U8 insert_idx = cursor_idx; if (is_shift) insert_idx ++; if (tape_arena.used + sizeof(U4) <= tape_arena.capacity && anno_arena.used + sizeof(U8) <= anno_arena.capacity) { U8*r anno_ptr = u8_r(anno_arena.start); for (U8 i = tape_count; i > insert_idx; i --) { tape_ptr[i] = tape_ptr[i-1]; anno_ptr[i] = anno_ptr[i-1]; } if (wparam == MS_VK_RETURN) { tape_ptr[insert_idx] = pack_token(STag_Format, 0xA); anno_ptr[insert_idx] = 0; } else { tape_ptr[insert_idx] = pack_token(STag_Comment, 0); anno_ptr[insert_idx] = 0; } if (is_shift) cursor_idx ++; tape_arena.used += sizeof(U4); anno_arena.used += sizeof(U8); } } vm_rax = 0; vm_rdx = 0; mem_zero(u8_(vm_globals), sizeof(vm_globals)); compile_and_run_tape(); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_KEYUP: { int key = 0; if (wparam == MS_VK_BACK) key = MU_KEY_BACKSPACE; if (wparam == MS_VK_RETURN) key = MU_KEY_RETURN; if (wparam == 0x10) key = MU_KEY_SHIFT; if (wparam == 0x11) key = MU_KEY_CTRL; if (wparam == 0x12) key = MU_KEY_ALT; if (key) mu_input_keyup(&mu_ctx, key); ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_SIZE: { ms_invalidate_rect(hwnd, nullptr, true); return 0; } case MS_WM_ERASEBKGND: { return 1; } case MS_WM_PAINT: { mu_begin(&mu_ctx); if (mu_begin_window(&mu_ctx, "ColorForth Source Tape", mu_rect(10, 10, 900, 480))) { U4*r tape_ptr = u4_r(tape_arena.start); U8*r anno_ptr = u8_r(anno_arena.start); S4 start_x = 5, start_y = 5, spacing_x = 6, spacing_y = 26; S4 x = start_x, y = start_y; for (U8 i = 0; i < tape_count; i++) { U4 t = tape_ptr[i]; U4 tag = unpack_tag(t); U4 val = unpack_val(t); U8 anno = anno_ptr[i]; if (tag == STag_Format && val == 0xA) { x = start_x; y += spacing_y; continue; } U4 color_u32 = tag_colors[tag]; const char* prefix = tag_prefixes[tag]; char val_str[9]; if (tag == STag_Data) { u64_to_hex(val, val_str, 6); val_str[6] = '\0'; } else { char* a_str = (char*) & anno; for(int c=0; c<8; c++) { val_str[c] = a_str[c] ? a_str[c] : ' '; } val_str[8] = '\0'; } char out_buf[12]; out_buf[0] = prefix[0]; out_buf[1] = ' '; mem_copy(u8_(out_buf + 2), u8_(val_str), 8); out_buf[10] = '\0'; int btn_w = 20 + text_width_cb(NULL, out_buf, 10); // auto-wrap mu_Container* current_window = mu_get_current_container(&mu_ctx); if (x + btn_w > current_window->body.w - 15) { x = start_x; y += spacing_y; } mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(color_u32 & 0xFF, (color_u32 >> 8) & 0xFF, (color_u32 >> 16) & 0xFF, 255); if (i == cursor_idx && editor_mode == MODE_EDIT) { mu_ctx.style->colors[MU_COLOR_BUTTON] = mu_color(0x56, 0x3B, 0x1E, 255); // Dark Orange } else if (i == cursor_idx && editor_mode == MODE_NAV) { mu_ctx.style->colors[MU_COLOR_BUTTON] = mu_color(0x3B, 0x46, 0x56, 255); // Dark Blue } else { mu_ctx.style->colors[MU_COLOR_BUTTON] = mu_color(0x1E, 0x1E, 0x1E, 255); // Dark Gray } mu_layout_set_next(&mu_ctx, mu_rect(x, y, btn_w, 22), 1); if (mu_button(&mu_ctx, out_buf)) { cursor_idx = i; editor_mode = MODE_NAV; } x += btn_w + spacing_x; mu_ctx.style->colors[MU_COLOR_BUTTON] = mu_color(75, 75, 75, 255); mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(230, 230, 230, 255); } // Dummy element to ensure scrolling bounds are correct mu_layout_set_next(&mu_ctx, mu_rect(start_x, y + spacing_y, 10, 10), 1); mu_draw_rect(&mu_ctx, mu_layout_next(&mu_ctx), mu_color(0,0,0,0)); mu_end_window(&mu_ctx); } if (mu_begin_window(&mu_ctx, "Compiler & Status", mu_rect(10, 500, 350, 200))) { char jit_str[64] = "Mode: Incremental | JIT Size: 0x000 bytes"; if (run_full) mem_copy(u8_(jit_str + 6), u8_("Full "), 11); u64_to_hex(code_arena.used, jit_str + 32, 3); mu_layout_row(&mu_ctx, 1, (int[]){-1}, 0); mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(170, 170, 170, 255); mu_text(&mu_ctx, "2-Reg Stack x86-64 Emitter"); mu_text(&mu_ctx, "[F5] Toggle Run | [PgUp/PgDn] Scroll"); mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(255, 255, 255, 255); mu_text(&mu_ctx, jit_str); if (tape_count > 0 && cursor_idx < tape_count) { U4 cur_tag = unpack_tag(tape_ptr[cursor_idx]); const char* tag_name = tag_names [cur_tag]; U4 cur_color = tag_colors[cur_tag]; char semantics_str[64] = "Current Tag: "; U4 name_len = 0; while (tag_name[name_len]) { semantics_str[13 + name_len] = tag_name[name_len]; name_len ++; } semantics_str[13 + name_len] = '\0'; mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(cur_color & 0xFF, (cur_color >> 8) & 0xFF, (cur_color >> 16) & 0xFF, 255); mu_text(&mu_ctx, semantics_str); } mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(230, 230, 230, 255); mu_end_window(&mu_ctx); } if (mu_begin_window(&mu_ctx, "Registers & Globals", mu_rect(370, 500, 350, 200))) { char state_str[64] = "RAX: 00000000 | RDX: 00000000"; u64_to_hex(vm_rax, state_str + 5, 8); u64_to_hex(vm_rdx, state_str + 21, 8); mu_layout_row(&mu_ctx, 1, (int[]){-1}, 0); mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(161, 186, 148, 255); // #94BAA1 mapped roughly mu_text(&mu_ctx, state_str); for (int i=0; i < 4; i ++) { char glob_str[32] = "[0]: 00000000"; glob_str[1] = '0' + i; u64_to_hex(vm_globals[i], glob_str + 5, 8); mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(84, 164, 214, 255); // #D6A454 rough mapping to bgr? // actually #D6A454 is R=D6(214) G=A4(164) B=54(84) but tag colors are 0x00BBGGRR, so 54(R) A4(G) D6(B). // It was orange before, so 54, 164, 214. mu_text(&mu_ctx, glob_str); } mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(230, 230, 230, 255); mu_end_window(&mu_ctx); } if (mu_begin_window(&mu_ctx, "Print Log", mu_rect(730, 500, 250, 200))) { mu_layout_row(&mu_ctx, 1, (int[]){-1}, 0); mu_ctx.style->colors[MU_COLOR_TEXT] = mu_color(161, 186, 148, 255); for (int i = 0; icolors[MU_COLOR_TEXT] = mu_color(230, 230, 230, 255); mu_end_window(&mu_ctx); } mu_end(&mu_ctx); MS_PAINTSTRUCT ps; void* hdc = ms_begin_paint(hwnd, & ps); MS_RECT rect; ms_get_client_rect(hwnd, &rect); S4 width = rect.right - rect.left; S4 height = rect.bottom - rect.top; void* memDC = ms_create_compatible_dc(hdc); void* memBitmap = ms_create_compatible_bitmap(hdc, width, height); void* oldBitmap = ms_select_object(memDC, memBitmap); void* hFont = ms_create_font_a(20, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, "Consolas"); void* hOldFont = ms_select_object(memDC, hFont); ms_set_bk_mode(memDC, 1); void* hBgBrush = ms_create_solid_brush(0x00222222); ms_select_object(memDC, hBgBrush); ms_rectangle(memDC, -1, -1, width + 2, height + 2); render_microui(memDC); ms_bit_blt(hdc, 0, 0, width, height, memDC, 0, 0, MS_SRCCOPY); ms_select_object(memDC, hOldFont); ms_delete_object(hBgBrush); ms_delete_object(hFont); ms_select_object(memDC, oldBitmap); ms_delete_object(memBitmap); ms_delete_dc(memDC); ms_end_paint(hwnd, & ps); return 0; } case MS_WM_DESTROY: { ms_post_quit_message(0); return 0; } } return ms_def_window_proc_a(hwnd, msg, wparam, lparam); } int main(void) { Slice tape_mem = slice_ut_(u8_(ms_virtual_alloc(nullptr, 64 * 1024, MS_MEM_COMMIT | MS_MEM_RESERVE, MS_PAGE_READWRITE)), 64 * 1024); Slice anno_mem = slice_ut_(u8_(ms_virtual_alloc(nullptr, 64 * 1024, MS_MEM_COMMIT | MS_MEM_RESERVE, MS_PAGE_READWRITE)), 64 * 1024); Slice code_mem = slice_ut_(u8_(ms_virtual_alloc(nullptr, 64 * 1024, MS_MEM_COMMIT | MS_MEM_RESERVE, MS_PAGE_EXECUTE_READWRITE)), 64 * 1024); if (! tape_mem.ptr || ! anno_mem.ptr || ! code_mem.ptr) ms_exit_process(1); farena_init(& tape_arena, tape_mem); farena_init(& anno_arena, anno_mem); farena_init(& code_arena, code_mem); mu_init(&mu_ctx); mu_ctx.text_width = text_width_cb; mu_ctx.text_height = text_height_cb; { scatter(pack_token(STag_Comment, 0), "INIT "); scatter(pack_token(STag_Data, 5), 0); scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Imm, 0), "STORE "); scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Imm, 0), "STORE "); scatter(pack_token(STag_Format, 0xA), 0); scatter(pack_token(STag_Define, 0), "F_STEP "); scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Call, 0), "FETCH "); scatter(pack_token(STag_Call, 0), "RET_IF_Z"); scatter(pack_token(STag_Format, 0xA), 0); scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Call, 0), "FETCH "); scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Call, 0), "FETCH "); scatter(pack_token(STag_Call, 0), "MULT "); scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Call, 0), "STORE "); scatter(pack_token(STag_Format, 0xA), 0); scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Call, 0), "FETCH "); scatter(pack_token(STag_Call, 0), "DEC "); scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Call, 0), "STORE "); scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Call, 0), "FETCH "); scatter(pack_token(STag_Call, 0), "PRINT "); scatter(pack_token(STag_Format, 0xA), 0); scatter(pack_token(STag_Imm, 0), "F_STEP "); scatter(pack_token(STag_Imm, 0), "F_STEP "); scatter(pack_token(STag_Imm, 0), "F_STEP "); scatter(pack_token(STag_Imm, 0), "F_STEP "); scatter(pack_token(STag_Imm, 0), "F_STEP "); } { scatter(pack_token(STag_Comment, 0), "LAMBDAS "); scatter(pack_token(STag_Format, 0xA), 0); // --- Store Lambda 1 (Square) in Global[0] --- scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Lambda, 0), "L_SQUARE"); // Lambda Body: scatter(pack_token(STag_Call, 0), "SWAP "); // Get the argument into RAX scatter(pack_token(STag_Call, 0), "DUP "); scatter(pack_token(STag_Call, 0), "MULT "); // Terminate Lambda, return to main scope, and prepare for STORE scatter(pack_token(STag_Imm, 0), "SWAP "); // RAX = 0, RDX = L_SQUARE_addr scatter(pack_token(STag_Call, 0), "STORE "); // Global[0] = L_SQUARE_addr scatter(pack_token(STag_Format, 0xA), 0); // --- Store Lambda 2 (Double) in Global[1] --- scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Lambda, 0), "L_DOUBLE"); // Lambda Body: scatter(pack_token(STag_Call, 0), "SWAP "); // Get the argument into RAX scatter(pack_token(STag_Call, 0), "DUP "); scatter(pack_token(STag_Call, 0), "ADD "); // Terminate Lambda, return to main scope, and prepare for STORE scatter(pack_token(STag_Imm, 0), "SWAP "); // RAX = 1, RDX = L_DOUBLE_addr scatter(pack_token(STag_Call, 0), "STORE "); // Global[1] = L_DOUBLE_addr scatter(pack_token(STag_Format, 0xA), 0); // --- Execute Lambda 1 (Square of 5) --- scatter(pack_token(STag_Comment, 0), "USE L1 "); scatter(pack_token(STag_Data, 5), 0); // Argument for lambda scatter(pack_token(STag_Data, 0), 0); scatter(pack_token(STag_Call, 0), "FETCH "); // RAX = Global[0] (L_SQUARE_addr), RDX = 5 scatter(pack_token(STag_Call, 0), "EXECUTE "); // Calls L_SQUARE. Returns RAX = 25 scatter(pack_token(STag_Call, 0), "PRINT "); // Prints 0x19 (25) scatter(pack_token(STag_Format, 0xA), 0); // --- Execute Lambda 2 (Double of 5) --- scatter(pack_token(STag_Comment, 0), "USE L2 "); scatter(pack_token(STag_Data, 5), 0); // Argument for lambda scatter(pack_token(STag_Data, 1), 0); scatter(pack_token(STag_Call, 0), "FETCH "); // RAX = Global[1] (L_DOUBLE_addr), RDX = 5 scatter(pack_token(STag_Call, 0), "EXECUTE "); // Calls L_DOUBLE. Returns RAX = 10 scatter(pack_token(STag_Call, 0), "PRINT "); // Prints 0xA (10) } relink_tape(); run_full = true; compile_and_run_tape(); run_full = false; MS_WNDCLASSA wc; mem_fill(u8_(& wc), 0, sizeof(wc)); wc.lpfnWndProc = win_proc; wc.hInstance = ms_get_stock_object(0); wc.lpszClassName = "ColorForthWindow"; wc.hbrBackground = ms_get_stock_object(4); ms_register_class_a(& wc); void* hwnd = ms_create_window_ex_a(0, wc.lpszClassName, "Sourceless Global Memory Explorer", MS_WS_OVERLAPPEDWINDOW | MS_WS_VISIBLE, 100, 100, 1100, 750, nullptr, nullptr, wc.hInstance, nullptr); MS_MSG msg; while (ms_get_message_a(& msg, nullptr, 0, 0)) { ms_translate_message(& msg); ms_dispatch_message_a(& msg); } ms_exit_process(0); return 0; }