From 672b184c869f6e488a8fbfc10a1c893af1356b9a Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 21 Feb 2026 14:26:41 -0500 Subject: [PATCH] slop continues --- .gitignore | 1 + ai_client.py | 110 + colorforth_bootslop_001.md | 4323 ------------------------------------ config.toml | 30 +- gui.py | 330 +-- pyproject.toml | 3 +- 6 files changed, 328 insertions(+), 4469 deletions(-) create mode 100644 ai_client.py delete mode 100644 colorforth_bootslop_001.md diff --git a/.gitignore b/.gitignore index a20e18c..3df2aee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ credentials.toml __pycache__ uv.lock +colorforth_bootslop_002.md diff --git a/ai_client.py b/ai_client.py new file mode 100644 index 0000000..143034f --- /dev/null +++ b/ai_client.py @@ -0,0 +1,110 @@ +# ai_client.py +import tomllib +from pathlib import Path + +_provider: str = "gemini" +_model: str = "gemini-2.0-flash" + +_gemini_client = None +_gemini_chat = None + +_anthropic_client = None +_anthropic_history: list[dict] = [] + +def _load_credentials() -> dict: + with open("credentials.toml", "rb") as f: + return tomllib.load(f) + +# ------------------------------------------------------------------ provider setup + +def set_provider(provider: str, model: str): + global _provider, _model + _provider = provider + _model = model + +def reset_session(): + global _gemini_client, _gemini_chat + global _anthropic_client, _anthropic_history + _gemini_client = None + _gemini_chat = None + _anthropic_client = None + _anthropic_history = [] + +# ------------------------------------------------------------------ model listing + +def list_models(provider: str) -> list[str]: + creds = _load_credentials() + if provider == "gemini": + return _list_gemini_models(creds["gemini"]["api_key"]) + elif provider == "anthropic": + return _list_anthropic_models() + return [] + +def _list_gemini_models(api_key: str) -> list[str]: + from google import genai + client = genai.Client(api_key=api_key) + models = [] + for m in client.models.list(): + name = m.name + if name.startswith("models/"): + name = name[len("models/"):] + if "gemini" in name.lower(): + models.append(name) + return sorted(models) + +def _list_anthropic_models() -> list[str]: + import anthropic + creds = _load_credentials() + client = anthropic.Anthropic(api_key=creds["anthropic"]["api_key"]) + models = [] + for m in client.models.list(): + models.append(m.id) + return sorted(models) + +# ------------------------------------------------------------------ gemini + +def _ensure_gemini_chat(): + global _gemini_client, _gemini_chat + if _gemini_chat is None: + from google import genai + creds = _load_credentials() + _gemini_client = genai.Client(api_key=creds["gemini"]["api_key"]) + _gemini_chat = _gemini_client.chats.create(model=_model) + +def _send_gemini(md_content: str, user_message: str) -> str: + _ensure_gemini_chat() + full_message = f"\n{md_content}\n\n\n{user_message}" + response = _gemini_chat.send_message(full_message) + return response.text + +# ------------------------------------------------------------------ anthropic + +def _ensure_anthropic_client(): + global _anthropic_client + if _anthropic_client is None: + import anthropic + creds = _load_credentials() + _anthropic_client = anthropic.Anthropic(api_key=creds["anthropic"]["api_key"]) + +def _send_anthropic(md_content: str, user_message: str) -> str: + global _anthropic_history + _ensure_anthropic_client() + full_message = f"\n{md_content}\n\n\n{user_message}" + _anthropic_history.append({"role": "user", "content": full_message}) + response = _anthropic_client.messages.create( + model=_model, + max_tokens=8096, + messages=_anthropic_history + ) + reply = response.content[0].text + _anthropic_history.append({"role": "assistant", "content": reply}) + return reply + +# ------------------------------------------------------------------ unified send + +def send(md_content: str, user_message: str) -> str: + if _provider == "gemini": + return _send_gemini(md_content, user_message) + elif _provider == "anthropic": + return _send_anthropic(md_content, user_message) + raise ValueError(f"unknown provider: {_provider}") diff --git a/colorforth_bootslop_001.md b/colorforth_bootslop_001.md deleted file mode 100644 index 15816f3..0000000 --- a/colorforth_bootslop_001.md +++ /dev/null @@ -1,4323 +0,0 @@ -### `C:\projects\forth\bootslop\attempt_1\attempt_1.md` - -```md -# Technical Outline: Attempt 1 - -## Overview -`attempt_1` is a minimal C program that serves as a proof-of-concept for the "Lottes/Onat" sourceless ColorForth paradigm. It successfully integrates a visual editor, a live JIT compiler, and an execution environment into a single, cohesive Win32 application that links against the C runtime but avoids direct includes of standard headers, using manually declared functions instead. - -The application presents a visual grid of 32-bit tokens and allows the user to navigate and edit them directly. On every keypress, the token array is re-compiled into x86-64 machine code and executed, with the results (register states and global memory) displayed instantly in the HUD. - -## Core Concepts Implemented - -1. **Sourceless Token Array (`FArena` tape):** - * The "source code" is a contiguous block of `U4` (32-bit) integers allocated by `VirtualAlloc` and managed by the `FArena` from `duffle.h`. - * Each token is packed with a 4-bit "Color" tag and a 28-bit payload, adhering to the core design. - -2. **Annotation Layer (`FArena` anno):** - * A parallel `FArena` of `U8` (64-bit) integers stores an 8-character string for each corresponding token on the tape. - * The UI renderer prioritizes displaying this string, but the compiler only ever sees the indices packed into the 32-bit token. - -3. **2-Register Stack & Global Memory:** - * The JIT compiler emits x86-64 that strictly adheres to Onat's `RAX`/`RDX` register stack. - * A `vm_globals` array is passed by pointer into the JIT'd code (via `RCX` on Win64), allowing instructions like `FETCH` and `STORE` to simulate the "tape drive" memory model. - -4. **Handmade x86-64 JIT Emitter:** - * A small set of `emit8`/`emit32` functions write raw x86-64 opcodes into a `VirtualAlloc` block marked as executable (`PAGE_EXECUTE_READWRITE`). - * This buffer is cast to a C function pointer and called directly, bypassing the need for an external assembler like NASM or a complex library like Zydis for this prototype stage. - -5. **Modal Editor (Win32 GDI):** - * The UI is built with raw Win32 GDI calls defined in `duffle.h`. - * It features two modes: `Navigation` (gray cursor, arrow key movement) and `Edit` (orange cursor, text input). - * The editor correctly handles token insertion, deletion (Vim-style backspace), tag cycling (Tab), and value editing, all while re-compiling and re-executing on every keystroke. - -6. **O(1) Dictionary & Visual Linking:** - * The dictionary relies on an edit-time visual linker. When the tape is modified, `relink_tape` resolves names to absolute source memory indices. - * The compiler resolves references in `O(1)` time instantly by indexing into an offset mapping table (`tape_to_code_offset`). - -7. **Implicit Definition Boundaries (Magenta Pipe):** - * Definitions implicitly cause the JIT to emit a `RET` to close the prior block, and an `xchg rax, rdx` to rotate the stack for the new block. - -8. **x68 Instruction Padding:** - * The JIT pads every logical block/instruction to exact 32-bit multiples using `0x90` (NOPs) to perfectly align with the visual token grid logic. - -9. **The FFI Bridge:** - * The system uses an FFI macro (`emit_ffi_dance`) to align the `RSP` stack to 16 bytes, allocate 32 bytes of shadow space, and map the 2-register data stack/globals into the Windows C-ABI (`RCX`, `RDX`, `R8`, `R9`) to safely call WinAPI functions (like `MessageBoxA`). - -## What's Missing (TODO) - -* **Saving/Loading (Persistence):** The tape and annotation arenas are purely in-memory and are lost when the program closes. Need to implement the self-modifying OS cartridge concept. -* **Expanded Instruction Set:** The JIT only knows a handful of primitives. It has no support for floating point or more complex branches. -* **Annotation Editing & Comments:** Typing into an annotation just appends characters up to 8 bytes. A proper text-editing cursor within the token is needed, and support for arbitrarily long comments should be implemented. -* **Tape Drive / Preemptive Scatter Logic:** Improve the FFI argument mapping to properly read from the "tape drive" memory slots instead of just mapping RAX/RDX to the first parameters. - -## References Utilized -* **Heavily Utilized:** - * **Onat's Talks:** The core architecture (2-register stack, global memory tape, JIT philosophy) is a direct implementation of the concepts from his VAMP/KYRA presentations. - * **Lottes' Twitter Notes:** The 2-character mapped dictionary, `ret-if-signed` (`RET_IF_ZERO`), and annotation layer concepts were taken directly from his tweets. - * **User's `duffle.h` & `fortish-study`:** The C coding conventions (X-Macros, `FArena`, byte-width types, `ms_` prefixes) were adopted from these sources. -* **Lightly Utilized:** - * **Lottes' Blog:** Provided the high-level "sourceless" philosophy and inspiration. - * **Grok Searches:** Served to validate our understanding and provide parallels (like Wasm's linear memory), but did not provide direct implementation details. - -``` - ---- - -### `C:\projects\forth\bootslop\attempt_1\duffle.amd64.win32.h` - -```h -/* -C DSL Duffle -ISA: amd64 -Sandbox: Windows 11 -Compiler: clang -Standard: c23 -*/ -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Wunused-variable" -#pragma clang diagnostic ignored "-Wswitch" -#pragma clang diagnostic ignored "-Wuninitialized" -#pragma comment(lib, "Advapi32.lib") -#pragma comment(lib, "gdi32.lib") -#pragma comment(lib, "Kernel32.lib") -#pragma comment(lib, "msvcrt.lib") -#pragma comment(lib, "user32.lib") -#pragma comment(lib, "ucrt.lib") -#pragma comment(lib, "vcruntime.lib") -#define WinAPI __attribute((__stdcall__)) __attribute__((__force_align_arg_pointer__)) // Win32 Syscall FFI - -#pragma region DSL -#define m_expand(...) __VA_ARGS__ -#define glue_impl(A, B) A ## B -#define glue(A, B) glue_impl(A, B) -#define tmpl(prefix, type) prefix ## _ ## type - -#define VA_Sel_1( _1, ... ) _1 // <-- Of all th args passed pick _1. -#define VA_Sel_2( _1, _2, ... ) _2 // <-- Of all the args passed pick _2. -#define VA_Sel_3( _1, _2, _3, ... ) _3 // etc.. - -#define global static // Mark global data -#define gknown // Mark global data used in procedure - -#define LT_ thread_local -#define LP_ static // static data within procedure scope -#define internal static // internal - -#define asm __asm__ -#define align(value) __attribute__(aligned (value)) // for easy alignment -#define C_(type,data) ((type)(data)) // for enforced precedence -#define expect(x,y) __builtin_expect(x, y) // so compiler knows the common path -#define I_ internal inline -#define IA_ I_ __attribute__((always_inline)) // inline always -#define N_ internal __attribute__((noinline)) // inline never -#define RO_ __attribute__((section(".rodata"))) // Read only data allocation -#define r restrict // pointers are either restricted or volatile and nothing else -#define v volatile // pointers are either restricted or volatile and nothing else -#define T_ typeof -#define T_same(a,b) _Generic((a), typeof((b)): 1, default: 0) - -#define r_(ptr) C_(T_(ptr[0])*r, ptr) -#define v_(ptr) C_(T_(ptr[0])*v, ptr) -#define tr_(type, ptr) C_(type*r, ptr) -#define tv_(type, ptr) C_(type*v, ptr) - -#define array_len(a) (U8)(sizeof(a) / sizeof(typeof((a)[0]))) -#define array_decl(type, ...) (type[]){__VA_ARGS__} -#define Array_sym(type,len) type ## _ ## A ## len -#define Array_expand(type,len) type Array_sym(type, len)[len]; -#define Array_(type,len) Array_expand(type,len) -#define Bit_(id,b) id = (1 << b), tmpl(id,pos) = b -#define Enum_(underlying_type,symbol) enum symbol: underlying_type symbol; enum symbol: underlying_type -#define Struct_(symbol) struct symbol symbol; struct symbol -#define Union_(symbol) union symbol symbol; union symbol - -#define Opt_(proc) Struct_(tmpl(Opt,proc)) -#define opt_(symbol, ...) (tmpl(Opt,symbol)){__VA_ARGS__} -#define Ret_(proc) Struct_(tmpl(Ret,proc)) -#define ret_(proc) tmpl(Ret,proc) proc - -// Generally unused, allows force inlining of procedures at the discretion of the call-site. -#if 0 -#define IC_(name) inline_ ## name -#define I_proc(name, params, args) \ - IA_ void IC_(name) params; \ - I_ void name params { IC_(name)args; } \ - IA_ void IC_(name) params -#define I_proc_r(type_ret, name, params, args) \ - IA_ type_ret IC_(name) params; \ - I_ type_ret name params { return IC_(name)args; } -#endif - -// Using Byte-Width convention for the fundamental types. -typedef __UINT8_TYPE__ U1; typedef __UINT16_TYPE__ U2; typedef __UINT32_TYPE__ U4; typedef __UINT64_TYPE__ U8; -typedef __INT8_TYPE__ S1; typedef __INT16_TYPE__ S2; typedef __INT32_TYPE__ S4; typedef __INT64_TYPE__ S8; -typedef unsigned char B1; typedef __UINT16_TYPE__ B2; typedef __UINT32_TYPE__ B4; typedef __UINT64_TYPE__ B8; -typedef float F4; typedef double F8; -typedef float F4_2 __attribute__((vector_size(16))); - -#define u1_(value) C_(U1, value) -#define u2_(value) C_(U2, value) -#define u4_(value) C_(U4, value) -#define u8_(value) C_(U8, value) -#define s1_(value) C_(S1, value) -#define s2_(value) C_(S2, value) -#define s4_(value) C_(S4, value) -#define s8_(value) C_(S8, value) -#define f4_(value) C_(F4, value) -#define f8_(value) C_(F8, value) - -#define u1_r(value) C_(U1*r, value) -#define u2_r(value) C_(U2*r, value) -#define u4_r(value) C_(U4*r, value) -#define u8_r(value) C_(U8*r, value) -#define u1_v(value) C_(U1*v, value) -#define u2_v(value) C_(U2*v, value) -#define u4_v(value) C_(U4*v, value) -#define u8_v(value) C_(U8*v, value) - -#define kilo(n) (C_(U8, n) << 10) -#define mega(n) (C_(U8, n) << 20) -#define giga(n) (C_(U8, n) << 30) -#define tera(n) (C_(U8, n) << 40) -#define null C_(U8, 0) -#define nullptr C_(void*, 0) -#define O_(type,member) C_(U8,__builtin_offsetof(type,member)) -#define S_(data) C_(U8, sizeof(data)) - -#define sop_1(op,a,b) C_(U1, s1_(a) op s1_(b)) -#define sop_2(op,a,b) C_(U2, s2_(a) op s2_(b)) -#define sop_4(op,a,b) C_(U4, s4_(a) op s4_(b)) -#define sop_8(op,a,b) C_(U8, s8_(a) op s8_(b)) - -#undef def_signed_op -#define def_signed_op(id,op,width) IA_ U ## width id ## _s ## width(U ## width a, U ## width b) {return sop_ ## width(op, a, b); } -#define def_signed_ops(id,op) def_signed_op(id, op, 1) def_signed_op(id, op, 2) def_signed_op(id, op, 4) def_signed_op(id, op, 8) -def_signed_ops(add, +) -def_signed_ops(sub, -) -def_signed_ops(mut, *) -def_signed_ops(div, /) -def_signed_ops(gt, >) -def_signed_ops(lt, <) -def_signed_ops(ge, >=) -def_signed_ops(le, <=) -#undef def_signed_ops -#undef def_signed_op - -#define def_generic_sop(op, a, ...) _Generic((a), U1: op ## _s1, U2: op ## _s2, U4: op ## _s4, U8: op ## _s8) (a, __VA_ARGS__) -#define add_s(a,b) def_generic_sop(add,a,b) -#define sub_s(a,b) def_generic_sop(sub,a,b) -#define mut_s(a,b) def_generic_sop(mut,a,b) -#define gt_s(a,b) def_generic_sop(gt, a,b) -#define lt_s(a,b) def_generic_sop(lt, a,b) -#define ge_s(a,b) def_generic_sop(ge, a,b) -#define le_s(a,b) def_generic_sop(le, a,b) -#undef def_generic_sop -#pragma endregion DSL - -#pragma region Thread Coherence -IA_ void barrier_compiler(void){asm volatile("::""memory");} // Compiler Barrier -IA_ void barrier_memory (void){__builtin_ia32_mfence();} // Memory Barrier -IA_ void barrier_read (void){__builtin_ia32_lfence();} // Read Barrier -IA_ void barrier_write (void){__builtin_ia32_sfence();} // Write Barrier - -IA_ U4 atm_add_u4 (U4*r addr, U4 value){asm volatile("lock xaddl %0,%1":"=r"(value),"=m"(addr[0]):"0"(value),"m"(addr[0]):"memory","cc");return value;} -IA_ U8 atm_add_u8 (U8*r addr, U8 value){asm volatile("lock xaddq %0,%1":"=r"(value),"=m"(addr[0]):"0"(value),"m"(addr[0]):"memory","cc");return value;} -IA_ U4 atm_swap_u4(U4*r addr, U4 value){asm volatile("lock xchgl %0,%1":"=r"(value),"=m"(addr[0]):"0"(value),"m"(addr[0]):"memory","cc");return value;} -IA_ U8 atm_swap_u8(U8*r addr, U8 value){asm volatile("lock xchgq %0,%1":"=r"(value),"=m"(addr[0]):"0"(value),"m"(addr[0]):"memory","cc");return value;} -#pragma endregion Thread Coherence - -#pragma region Debug -WinAPI void ms_exit_process(U4 uExitCode) asm("ExitProcess"); // Kernel 32 - -#define debug_trap() __builtin_debugtrap() -#if BUILD_DEBUG -IA_ void assert(U8 cond) { if(cond){return;} else{debug_trap(); ms_exit_process(1);} } -#else -#define assert(cond) -#endif -#pragma endregion Debug - -#pragma region Memory -#define MEM_ALIGNMENT_DEFAULT (2 * S_(void*)) - -#define assert_bounds(point, start, end) for(;0;){ \ - assert((start) <= (point)); \ - assert((point) <= (end)); \ -} while(0) - -IA_ U8 align_pow2(U8 x, U8 b) { - assert(b != 0); - assert((b & (b - 1)) == 0); // Check power of 2 - return ((x + b - 1) & (~(b - 1))); -} - -#if 0 -#pragma clang optimize off -// TODO(Ed): Replace these later (only matters if CRT is not used) -void* memset(void* dest, int c, U8 count) { - U1* bytes = (U1*)dest; - while (count--) *bytes++ = (U1)c; - return dest; -} - -void* memcpy(void* dest, const void* src, U8 count) { - U1* d = (U1*)dest; - const U1* s = (const U1*)src; - while (count--) *d++ = *s++; - return dest; -} -#pragma clang optimize on -#endif - -IA_ U8 mem_copy (U8 dest, U8 src, U8 len) { return (U8)(__builtin_memcpy ((void*)dest, (void const*)src, len)); } -IA_ U8 mem_copy_overlapping(U8 dest, U8 src, U8 len) { return (U8)(__builtin_memmove((void*)dest, (void const*)src, len)); } -IA_ U8 mem_fill (U8 dest, U8 value, U8 len) { return (U8)(__builtin_memset ((void*)dest, (int) value, len)); } -IA_ B4 mem_zero (U8 dest, U8 len) { if(dest == 0){return false;} mem_fill(dest, 0, len); return true; } - -typedef Struct_(Slice) { U8 ptr, len; }; // Untyped Slice -IA_ Slice slice_ut_(U8 ptr, U8 len) { return (Slice){ptr, len}; } - -#define Slice_(type) Struct_(tmpl(Slice,type)) { type* ptr; U8 len; } -typedef Slice_(B1); -#define slice_assert(s) do { assert((s).ptr != 0); assert((s).len > 0); } while(0) -#define slice_end(slice) ((slice).ptr + (slice).len) -#define S_slice(s) ((s).len * S_((s).ptr[0])) - -#define slice_ut(ptr,len) slice_ut_(u8_(ptr), u8_(len)) -#define slice_ut_arr(a) slice_ut_(u8_(a), S_(a)) -#define slice_to_ut(s) slice_ut_(u8_((s).ptr), S_slice(s)) - -#define slice_iter(container, iter) (T_((container).ptr) iter = (container).ptr; iter != slice_end(container); ++ iter) -#define slice_arg_from_array(type, ...) & (tmpl(Slice,type)) { .ptr = array_decl(type,__VA_ARGS__), .len = array_len( array_decl(type,__VA_ARGS__)) } - -IA_ void slice_zero_(Slice s) { slice_assert(s); mem_zero(s.ptr, s.len); } -#define slice_zero(s) slice_zero_(slice_to_ut(s)) - -IA_ void slice_copy_(Slice dest, Slice src) { - assert(dest.len >= src.len); - slice_assert(dest); - slice_assert(src); - mem_copy(dest.ptr, src.ptr, src.len); -} -#define slice_copy(dest, src) do { \ - static_assert(T_same(dest, src)); \ - slice_copy_(slice_to_ut(dest), slice_to_ut(src)); \ -} while(0) - -IA_ Slice mem_bump(U8 start, U8 cap, U8*r used, U8 amount) { - assert(amount <= (cap - used[0])); - used[0] += amount; - return (Slice){start + used[0], amount}; -} -#pragma endregion Memory - -#pragma region Encoding -IA_ void u64_to_hex(U8 val, char* buf, S4 chars) { - static const char hex_chars[] = "0123456789ABCDEF"; - for(S1 i = chars - 1; i >= 0; --i) { buf[i] = hex_chars[val & 0xF]; val >>= 4; } -} -#pragma endregion Encoding - -#pragma region Math -#define u8_max 0xffffffffffffffffull - -#define min(A,B) (((A) < (B)) ? (A) : (B)) -#define max(A,B) (((A) > (B)) ? (A) : (B)) -#define clamp_bot(X,B) max(X, B) // Clamp "X" by "B" - -#define clamp_decrement(X) (((X) > 0) ? ((X) - 1) : 0) - -typedef Struct_(R1_U1){ U1 p0; U1 p1; }; -typedef Struct_(R1_U2){ U2 p0; U2 p1; }; -typedef Struct_(R1_U4){ U4 p0; U2 p4; }; -typedef Struct_(R1_U8){ U8 p0; U8 p4; }; - -typedef Struct_(V2_U1){ U1 x; U1 y;}; - -IA_ B8 add_of (U8 a, U8 b, U8*r res) { return __builtin_uaddll_overflow(a, b, res); } -IA_ B8 sub_of (U8 a, U8 b, U8*r res) { return __builtin_usubll_overflow(a, b, res); } -IA_ B8 mul_of (U8 a, U8 b, U8*r res) { return __builtin_umulll_overflow(a, b, res); } -IA_ B8 add_s_of(S8 a, S8 b, S8*r res) { return __builtin_saddll_overflow(a, b, res); } -IA_ B8 sub_s_of(S8 a, S8 b, S8*r res) { return __builtin_ssubll_overflow(a, b, res); } -IA_ B8 mul_s_of(S8 a, S8 b, S8*r res) { return __builtin_smulll_overflow(a, b, res); } -#pragma endregion Math - -#pragma region Control Flow & Iteration -#define each_iter(type, iter, end) (type iter = 0; iter < end; ++ iter) -#define index_iter(type, iter, begin, op, end) (type iter = begin; iter op end; (begin < end ? ++ iter : -- iter)) -#define range_iter(iter,op,range) (T_((range).p0) iter = (range).p0; iter op (range).p1; ((range).p0 < (range).p1 ? ++ iter : -- iter)) - -#define defer(expr) for(U4 once= 1; once!=1;++ once,(expr)) // Basic do something after body -#define scope(begin,end) for(U4 once=(1,(begin)); once!=1;++ once,(end )) // Do things before or after a scope -#define defer_rewind(cursor) for(T_(cursor) sp=cursor,once=0; once!=1;++ once,cursor=sp) // Used with arenas/stacks -#define defer_info(type,expr, ...) for(type info= {__VA_ARGS__}; info.once!=1;++info.once,(expr)) // Defer with tracked state - -#define do_while(cond) for (U8 once=0; once!=1 || (cond); ++once) -#pragma endregion Control Flow & Iteration - -#pragma region FArena -typedef Opt_(farena) { U8 alignment, type_width; }; -typedef Struct_(FArena) { U8 start, capacity, used; }; -IA_ void farena_init(FArena*r arena, Slice mem) { assert(arena != nullptr); - arena->start = mem.ptr; - arena->capacity = mem.len; - arena->used = 0; -} -IA_ FArena farena_make(Slice mem) { FArena a; farena_init(& a, mem); return a; } -I_ Slice farena_push(FArena*r arena, U8 amount, Opt_farena o) { - if (amount == 0) { return (Slice){}; } - U8 desired = amount * (o.type_width == 0 ? 1 : o.type_width); - U8 to_commit = align_pow2(desired, o.alignment ? o.alignment : MEM_ALIGNMENT_DEFAULT); - return mem_bump(arena->start, arena->capacity, & arena->used, to_commit); -} -IA_ void farena_reset(FArena*r arena) { arena->used = 0; } -IA_ void farena_rewind(FArena*r arena, U8 save_point) { - U8 end = arena->start + arena->used; assert_bounds(save_point, arena->start, end); - arena->used -= save_point - arena->start; -} -IA_ U8 farena_save(FArena arena) { return arena.used; } -#define farena_push_(arena, amount, ...) farena_push((arena), (amount), opt_(farena, __VA_ARGS__)) -#define farena_push_type(arena, type, ...) C_(type*, farena_push((arena), 1, opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr) -#define farena_push_array(arena, type, amount, ...) (tmpl(Slice,type)){ C_(type*, farena_push((arena), (amount), opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr), (amount) } -#pragma endregion FArena - -#pragma region FStack -#define FStack_(name, type, width) Struct_(name) { U8 top; type arr[width]; } - -IA_ Slice fstack_push(Slice mem, U8* top, U8 amount, Opt_farena o) { - FArena a = { mem.ptr, mem.len, top[0] }; Slice s = farena_push(& a, amount, o); - top[0] = a.used; return s; -}; - -// This is here more for annotation than anything else. -#define fstack_save(stack) stack.top -#define fstack_rewind(stack, sp) do{stack.top = sp;}while(0) -#define fstack_reset(stack) do{stack.top = 0; }while(0) - -#define fstack_slice(stack) slice_ut_arr((stack).arr) -#define fstack_push_(stk, amount, ...) fstack_push(fstack_slice(stk), & (stk).top, (amount), opt_(farena, __VA_ARGS__)) -#define fstack_push_array(stk, type, amount, ...) \ -(tmpl(Slice,type)){ C_(type*, fstack_push(fstack_slice(stk), & (stk).top, (amount), opt_(farena, .type_width=S_(type), __VA_ARGS__)).ptr), (amount) } -#pragma endregion FStack - -#pragma region Text -// Using Bit-Width convention for the Unicode Encoding. -typedef unsigned char UTF8; -typedef Struct_(Str8) { UTF8* ptr; U8 len; }; -typedef Str8 Slice_UTF8; -typedef Slice_(Str8); -#define str8_comp(ptr, len) ((Str8){(UTF8*)ptr, len}) -#define str8(literal) ((Str8){(UTF8*)literal, S_(literal) - 1}) -#pragma endregion Text - -#pragma region Hashing -IA_ void hash64_fnv1a(U8*r hash, Slice data, U8 seed) { - LP_ U8 const default_seed = 0xcbf29ce484222325; - if (seed == 0) seed = default_seed; - hash[0] = seed; for (U8 elem = data.ptr; elem != slice_end(data); elem += 1) { - hash[0] ^= u1_r(elem)[0]; - hash[0] *= 0x100000001b3; - } -} -IA_ U8 hash64_fnv1a_ret(Slice data, U8 seed) { U8 h = 0; hash64_fnv1a(& h, data, seed); return h; } -#pragma endregion Hashing - -#pragma region IO -#define MS_STD_INPUT u4_(-10) -#define MS_STD_OUTPUT u4_(-11) -typedef Struct_(MS_Handle){U8 id;}; -#pragma endregion IO - -#pragma region Key Table Linear (KTL) -enum { KT_SLot_value = S_(U8), }; -#define KTL_Slot_(type) Struct_(tmpl(KTL_Slot,type)) { \ - U8 key; \ - type value; \ -} -#define KTL_(type) Slice_(tmpl(KTL_Slot,type)); \ - typedef tmpl(Slice_KTL_Slot,type) tmpl(KTL,type) -typedef Slice KTL_Byte; -typedef Struct_(KTL_Meta) { - U8 slot_size; - U8 type_width; -}; - -typedef Array_(Str8, 2); -typedef Slice_(Str8_A2); -typedef KTL_Slot_(Str8); -typedef KTL_(Str8); -IA_ void ktl_populate_slice_a2_str8(KTL_Str8* kt, Slice_Str8_A2 values) { - assert(kt != null); slice_assert(* kt); - if (values.len == 0) return; - assert(kt->len == values.len); - for index_iter(U4, id, 0, <, values.len) { - hash64_fnv1a(& kt->ptr[id].key, slice_to_ut(values.ptr[id][0]), 0); - mem_copy(u8_(& kt->ptr[id].value), u8_(& values.ptr[id][1]), S_(Str8)); - } -} -#define ktl_str8_key(str) hash64_fnv1a_ret(slice_to_ut(str8(str)), 0) -#define ktl_str8_from_arr(arr) (KTL_Str8){arr, array_len(arr)} -#pragma endregion KTL - -#pragma region Text Ops -// NOTE(rjf): Includes reverses for uppercase and lowercase hex. -RO_ global U8 integer_symbol_reverse[128] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, -}; - -IA_ B4 char_is_upper(UTF8 c) { return('A' <= c && c <= 'Z'); } -IA_ UTF8 char_to_lower(UTF8 c) { if (char_is_upper(c)) { c += ('a' - 'A'); } return(c); } -IA_ B4 char_is_digit(UTF8 c, U4 base) { - B4 result = 0; if (0 < base && base <= 16) { - if (integer_symbol_reverse[c] < base) result = 1; - } - return result; -} -IA_ UTF8 integer_symbols(UTF8 value) { - LP_ UTF8 lookup_table[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', }; - return lookup_table[C_(UTF8, value)]; -} -IA_ U8 u8_from_str8(Str8 str, U4 radix) { - U8 x = 0; if(1 < radix && radix <= 16) { - for each_iter(U8, cursor, str.len) { - x *= radix; - x += integer_symbol_reverse[str.ptr[cursor] & 0x7F]; - } - } - return x; -} - -typedef Struct_(Info_str8_from_u4) { - Str8 prefix; - U4 digit_group_size; - U4 needed_leading_zeros; - U4 size_required; -}; -I_ Info_str8_from_u4 str8_from_u4_info(U4 num, U4 radix, U4 min_digits, U4 digit_group_separator) -{ - Info_str8_from_u4 info = {0}; - LP_ Str8 tbl_prefix[] = { str8("0x"), str8("0o"), str8("0b") }; - switch (radix) { - case 16: { info.prefix = tbl_prefix[0]; } break; - case 8: { info.prefix = tbl_prefix[1]; } break; - case 2: { info.prefix = tbl_prefix[2]; } break; - } - info.digit_group_size = 3; - switch (radix) { - default: break; - case 2: - case 8: - case 16: { - info.digit_group_size = 4; - } - break; - } - info.needed_leading_zeros = 0; - { - U4 needed_digits = 1; - { - U4 u32_reduce = num; - for(;;) - { - u32_reduce /= radix; - if (u32_reduce == 0) { - break; - } - needed_digits += 1; - } - } - info.needed_leading_zeros = (min_digits > needed_digits) ? min_digits - needed_digits : 0; - U4 needed_separators = 0; - if (digit_group_separator != 0) - { - needed_separators = (needed_digits + info.needed_leading_zeros) / info.digit_group_size; - if (needed_separators > 0 && (needed_digits + info.needed_leading_zeros) % info.digit_group_size == 0) { - needed_separators -= 1; - } - } - info.size_required = info.prefix.len + info.needed_leading_zeros + needed_separators + needed_digits; - } - return info; -} -I_ Str8 str8_from_u4_buf(Slice buf, U4 num, U4 radix, U4 min_digits, U4 digit_group_separator, Info_str8_from_u4 info) -{ - assert(buf.len >= info.size_required); - Str8 result = { C_(UTF8*, buf.ptr), info.size_required }; - /*Fill Content*/ { - U4 num_reduce = num; - U4 digits_until_separator = info.digit_group_size; - for (U8 idx = 0; idx < result.len; idx += 1) - { - U8 separator_pos = result.len - idx - 1; - if (digits_until_separator == 0 && digit_group_separator != 0) { - result.ptr[separator_pos] = u1_(digit_group_separator); - digits_until_separator = info.digit_group_size + 1; - } - else { - result.ptr[separator_pos] = (U1) char_to_lower(integer_symbols(u1_(num_reduce % radix))); - num_reduce /= radix; - } - digits_until_separator -= 1; - if (num_reduce == 0) break; - } - for (U8 leading_0_idx = 0; leading_0_idx < info.needed_leading_zeros; leading_0_idx += 1) { - result.ptr[info.prefix.len + leading_0_idx] = '0'; - } - } - /*Fill Prefix*/ if (info.prefix.len > 0) { slice_copy(result, info.prefix); } - return result; -} -I_ Str8 str8_fmt_ktl_buf(Slice buffer, KTL_Str8 table, Str8 fmt_template) -{ - slice_assert(buffer); - slice_assert(table); - slice_assert(fmt_template); - UTF8*r cursor_buffer = C_(UTF8*r, buffer.ptr); - U8 buffer_remaining = buffer.len; - UTF8*r cursor_fmt = fmt_template.ptr; - U8 left_fmt = fmt_template.len; - while (left_fmt && buffer_remaining) - { - // Forward until we hit the delimiter '<' or the template's contents are exhausted. - U8 copy_offset = 0; - if (cursor_fmt[0] == '<') - { - UTF8*r potential_token_cursor = cursor_fmt + 1; // Skip '<' - U8 potential_token_len = 0; - B4 fmt_overflow = false; - while(true) { - UTF8*r cursor = potential_token_cursor + potential_token_len; - fmt_overflow = cursor >= slice_end(fmt_template); - B4 found_terminator = potential_token_cursor[potential_token_len] == '>'; - if (fmt_overflow || found_terminator) { break; } - ++ potential_token_len; - } - if (fmt_overflow) { - // Failed to find a subst and we're at end of fmt, just copy segment. - copy_offset = 1 + potential_token_len; // '<' + token - goto write_to_buffer; - } - // Hashing the potential token and cross checking it with our token table - U8 key = hash64_fnv1a_ret(slice_ut(u8_(potential_token_cursor), potential_token_len), 0); - Str8*r value = nullptr; for slice_iter(table, token) { - // We do a linear iteration instead of a hash table lookup because the user should never subst with more than 100 unqiue tokens.. - if (token->key == key) { value = & token->value; break; } - } - if (value) - { - // We're going to appending the string, make sure we have enough space in our buffer. - // NOTE(Ed): this version doesn't support growing the buffer (No Allocator Interface) - assert((buffer_remaining - potential_token_len) > 0); - copy_offset = min(buffer_remaining, value->len); // Prevent Buffer overflow. - mem_copy(u8_(cursor_buffer), u8_(value->ptr), buffer_remaining); - // Sync cursor format to after the processed token - cursor_buffer += copy_offset; - buffer_remaining -= copy_offset; - cursor_fmt = potential_token_cursor + 1 + potential_token_len; // '<' + token - left_fmt -= potential_token_len + 2; // The 2 here are the '<' & '>' delimiters being omitted. - continue; - } - // If not a subsitution, we copy the segment and continue. - copy_offset = 1 + potential_token_len; // '<' + token - goto write_to_buffer; - } - else do { - ++ copy_offset; - } - while ( (cursor_fmt[copy_offset] != '<' && (cursor_fmt + copy_offset) < slice_end(fmt_template)) ); - write_to_buffer: - assert((buffer_remaining - copy_offset) > 0); - copy_offset = min(buffer_remaining, copy_offset); // Prevent buffer overflow. - mem_copy(u8_(cursor_buffer), u8_(cursor_fmt), copy_offset); - buffer_remaining -= copy_offset; - left_fmt -= copy_offset; - cursor_buffer += copy_offset; - cursor_fmt += copy_offset; - } - return (Str8){C_(UTF8*, buffer.ptr), buffer.len - buffer_remaining}; -} - -typedef Struct_(Str8Gen) { UTF8* ptr; U8 cap, len; }; -IA_ Slice str8gen_buf(Str8Gen*r gen) { return (Slice){u8_(gen->ptr) + gen->len, gen->cap - gen->len}; } - -IA_ void str8gen_append_str8(Str8Gen*r gen, Str8 str) { assert(gen != nullptr); - U8 ptr = mem_bump(u8_(gen->ptr), gen->cap, & gen->len, str.len).ptr; - mem_copy(ptr, u8_(str.ptr), str.len); -} -IA_ void str8gen_append_fmt(Str8Gen*r gen, Str8 fmt, KTL_Str8 tbl) { - Str8 result = str8_fmt_ktl_buf(str8gen_buf(gen), tbl, fmt); - gen->len += result.len; -} -#define str8gen_append_str8_(gen, s) str8gen_append_str8(gen, str8(s)) -#pragma endregion Text Ops - -#pragma region OS_GDI_And_Minimal -// --- WinAPI Minimal Definitions --- -typedef struct MS_WNDCLASSA { - U4 style; - S8 (*lpfnWndProc)(void*, U4, U8, S8); - S4 cbClsExtra; - S4 cbWndExtra; - void* hInstance; - void* hIcon; - void* hCursor; - void* hbrBackground; - char const* lpszMenuName; - char const* lpszClassName; -} MS_WNDCLASSA; -typedef struct MS_POINT { S4 x, y; } MS_POINT; -typedef struct MS_MSG { void* hwnd; U4 message; U8 wParam; S8 lParam; U4 time; MS_POINT pt; } MS_MSG; -typedef struct MS_RECT { S4 left, top, right, bottom; } MS_RECT; -typedef struct MS_PAINTSTRUCT { void* hdc; S4 fErase; MS_RECT rcPaint; S4 fRestore; S4 fIncUpdate; U1 rgbReserved[32]; } MS_PAINTSTRUCT; - -// --- Kernel32 --- -WinAPI void ms_exit_process(U4 uExitCode) asm("ExitProcess"); -WinAPI MS_Handle ms_get_std_handle(U4 handle_type) asm("GetStdHandle"); -WinAPI void* ms_virtual_alloc(void* lpAddress, U8 dwSize, U4 flAllocationType, U4 flProtect) asm("VirtualAlloc"); -WinAPI B4 ms_read_console( - MS_Handle handle, - UTF8*r buffer, - U4 to_read, - U4*r num_read, - U8 reserved_input_control -) asm("ReadConsoleA"); -WinAPI B4 ms_write_console( - MS_Handle handle, - UTF8 const*r buffer, - U4 chars_to_write, - U4*v chars_written, - U8 reserved -) asm("WriteConsoleA"); - -// --- User32 --- -WinAPI U2 ms_register_class_a(MS_WNDCLASSA const* lpWndClass) asm("RegisterClassA"); -WinAPI void* ms_create_window_ex_a( - U4 dwExStyle, - char const* lpClassName, - char const* lpWindowName, - U4 dwStyle, - S4 X, - S4 Y, - S4 nWidth, - S4 nHeight, - void* hWndParent, - void* hMenu, - void* hInstance, - void* lpParam -) asm("CreateWindowExA"); -WinAPI S4 ms_show_window(void* hWnd, S4 nCmdShow) asm("ShowWindow"); -WinAPI S4 ms_get_message_a(MS_MSG* lpMsg, void* hWnd, U4 wMsgFilterMin, U4 wMsgFilterMax) asm("GetMessageA"); -WinAPI S4 ms_translate_message(MS_MSG const* lpMsg) asm("TranslateMessage"); -WinAPI S8 ms_dispatch_message_a(MS_MSG const* lpMsg) asm("DispatchMessageA"); -WinAPI S8 ms_def_window_proc_a(void* hWnd, U4 Msg, U8 wParam, S8 lParam) asm("DefWindowProcA"); -WinAPI void ms_post_quit_message(S4 nExitCode) asm("PostQuitMessage"); -WinAPI S4 ms_invalidate_rect(void* hWnd, MS_RECT const* lpRect, S4 bErase) asm("InvalidateRect"); -WinAPI S2 ms_get_async_key_state(S4 vKey) asm("GetAsyncKeyState"); - -// --- GDI32 --- -WinAPI void* ms_begin_paint(void* hWnd, MS_PAINTSTRUCT* lpPaint) asm("BeginPaint"); -WinAPI S4 ms_end_paint(void* hWnd, MS_PAINTSTRUCT const* lpPaint) asm("EndPaint"); -WinAPI U4 ms_set_text_color(void* hdc, U4 color) asm("SetTextColor"); -WinAPI U4 ms_set_bk_color(void* hdc, U4 color) asm("SetBkColor"); -WinAPI S4 ms_text_out_a(void* hdc, S4 x, S4 y, char const* lpString, S4 c) asm("TextOutA"); -WinAPI void* ms_get_stock_object(S4 i) asm("GetStockObject"); -WinAPI void* ms_create_font_a( - S4 cHeight, - S4 cWidth, - S4 cEscapement, - S4 cOrientation, - S4 cWeight, - U4 bItalic, - U4 bUnderline, - U4 bStrikeOut, - U4 iCharSet, - U4 iOutPrecision, - U4 iClipPrecision, - U4 iQuality, - U4 iPitchAndFamily, - char const* pszFaceName -) asm("CreateFontA"); -WinAPI void* ms_create_compatible_dc(void* hdc) asm("CreateCompatibleDC"); -WinAPI void* ms_create_compatible_bitmap(void* hdc, S4 cx, S4 cy) asm("CreateCompatibleBitmap"); -WinAPI B4 ms_bit_blt(void* hdcDest, S4 x, S4 y, S4 w, S4 h, void* hdcSrc, S4 xSrc, S4 ySrc, U4 rop) asm("BitBlt"); -WinAPI B4 ms_delete_dc(void* hdc) asm("DeleteDC"); -WinAPI B4 ms_get_client_rect(void* hwnd, MS_RECT* lpRect) asm("GetClientRect"); -WinAPI void* ms_select_object(void* hdc, void* h) asm("SelectObject"); -WinAPI S4 ms_rectangle(void* hdc, S4 left, S4 top, S4 right, S4 bottom) asm("Rectangle"); -WinAPI S4 ms_set_bk_mode(void* hdc, S4 mode) asm("SetBkMode"); -WinAPI void* ms_create_solid_brush(U4 color) asm("CreateSolidBrush"); -WinAPI S4 ms_delete_object(void* ho) asm("DeleteObject"); - -#define MS_MEM_COMMIT 0x00001000 -#define MS_MEM_RESERVE 0x00002000 -#define MS_PAGE_READWRITE 0x04 -#define MS_SRCCOPY 0x00CC0020 -#define MS_WM_DESTROY 0x0002 -#define MS_WM_SIZE 0x0005 -#define MS_WM_PAINT 0x000F -#define MS_WM_ERASEBKGND 0x0014 -#define MS_WM_KEYDOWN 0x0100 -#define MS_WM_KEYUP 0x0101 -#define MS_WM_MOUSEMOVE 0x0200 -#define MS_WM_LBUTTONDOWN 0x0201 -#define MS_WM_LBUTTONUP 0x0202 -#define MS_WM_RBUTTONDOWN 0x0204 -#define MS_WM_RBUTTONUP 0x0205 -#define MS_WM_MBUTTONDOWN 0x0207 -#define MS_WM_MBUTTONUP 0x0208 -#define MS_WM_MOUSEWHEEL 0x020A -#define MS_WS_OVERLAPPEDWINDOW 0x00CF0000 -#define MS_WS_VISIBLE 0x10000000 -#define MS_VK_LEFT 0x25 -#define MS_VK_UP 0x26 -#define MS_VK_RIGHT 0x27 -#define MS_VK_DOWN 0x28 - -#define MS_PAGE_EXECUTE_READWRITE 0x40 - -#define MS_WM_CHAR 0x0102 -#define MS_VK_RETURN 0x0D -#define MS_VK_BACK 0x08 -#define MS_VK_TAB 0x09 -#define MS_VK_SPACE 0x20 -#define MS_VK_F5 0x74 -#define MS_VK_PRIOR 0x21 -#define MS_VK_NEXT 0x22 - -#define MS_VK_SHIFT 0x10 -#pragma endregion OS_GDI_And_Minimal - -``` - ---- - -### `C:\projects\forth\bootslop\attempt_1\main.c` - -```c -#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 -IA_ void x64_XCHG_RAX_RDX() { emit8(x64_REX); emit8(x64_op_XCHG_rm_reg); emit8(x64_modrm_RAX_RDX); } -IA_ void x64_MOV_RDX_RAX() { emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RAX_RDX); } // DUP -IA_ void x64_MOV_RAX_RDX() { emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RDX_RAX); } // DROP -// Arithmetic (2-register stack: op RAX with RDX, result in RAX) -IA_ void x64_ADD_RAX_RDX() { emit8(x64_REX); emit8(x64_op_ADD_rm_reg); emit8(x64_modrm_RAX_RDX); } -IA_ void x64_SUB_RAX_RDX() { emit8(x64_REX); emit8(x64_op_SUB_rm_reg); emit8(x64_modrm_RAX_RDX); } -IA_ void x64_IMUL_RAX_RDX() { emit8(x64_REX); emit8(x64_op_IMUL_reg_rm); emit8(x64_op_IMUL_reg_rm2); emit8(x64_modrm_RAX_RDX); } -IA_ void x64_DEC_RAX() { emit8(x64_REX); emit8(x64_op_UNARY); emit8(x64_modrm(x64_mod_reg, x64_ext_DEC, x64_reg_RAX)); } -// Flag Operations (for conditional returns) -IA_ void x64_TEST_RAX_RAX() { emit8(x64_REX); emit8(x64_op_TEST_rm_reg); emit8(x64_modrm_RAX_RAX); } -// Conditional Returns (TEST must precede these) -IA_ void x64_RET_IF_ZERO() { x64_TEST_RAX_RAX(); emit8(x64_op_JNZ_rel8); emit8(0x01); emit8(x64_op_RET); } // JNZ skips the RET if RAX != 0, so RET only fires when RAX == 0 -IA_ void x64_RET_IF_SIGN() { x64_TEST_RAX_RAX(); emit8(x64_op_JNS_rel8); emit8(0x01); emit8(x64_op_RET); } // JNS skips the RET if RAX >= 0, so RET only fires when RAX < 0 - -// Tape Drive Memory (Preemptive Scatter via RBX base pointer) -IA_ void x64_FETCH() { - emit8(x64_REX); - emit8(x64_op_MOV_reg_rm); - emit8(x64_modrm_RAX_sib); - emit8(x64_sib_tape); -} -IA_ void x64_STORE() { - emit8(x64_REX); - emit8(x64_op_MOV_rm_reg); - emit8(x64_modrm_RDX_sib); - emit8(x64_sib_tape); -} -// Indirect call through RAX (EXECUTE primitive) -IA_ void x64_CALL_RAX() { - emit8(x64_op_UNARY); - emit8(x64_modrm(x64_mod_reg, x64_ext_CALL, x64_reg_RAX)); -} -IA_ void x64_RET() { emit8(x64_op_RET); } // RET - -// JIT Entry Prologue: save RBX, load vm_globals ptr from RCX, restore RAX/RDX state -// vm_globals[14] = RAX save slot (14 * 8 = 0x70) -// vm_globals[15] = RDX save slot (15 * 8 = 0x78) -#define x64_vm_rax_slot 0x70 -#define x64_vm_rdx_slot 0x78 - -IA_ void x64_JIT_PROLOGUE() { - emit8(x64_op_PUSH_RBX); - emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RCX_RBX); - emit8(x64_REX); emit8(x64_op_MOV_reg_rm); emit8(x64_modrm_RAX_mem_disp8_RBX); emit8(x64_vm_rax_slot); - emit8(x64_REX); emit8(x64_op_MOV_reg_rm); emit8(x64_modrm_RDX_mem_disp8_RBX); emit8(x64_vm_rdx_slot); -} -// JIT Exit Epilogue: save RAX/RDX state back, restore RBX, return -IA_ void x64_JIT_EPILOGUE() { - emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RAX_mem_disp8_RBX); emit8(x64_vm_rax_slot); - emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RDX_mem_disp8_RBX); emit8(x64_vm_rdx_slot); - emit8(x64_op_POP_RBX); - emit8(x64_op_RET); -} -// Win64 FFI Dance: align RSP, allocate 32 bytes shadow space -// sub rsp, 40 (32 shadow + 8 to realign since we pushed RDX) -#define x64_ffi_shadow_space 0x28 -IA_ void x64_FFI_PROLOGUE() { - emit8(x64_op_PUSH_RDX); - emit8(x64_REX); emit8(x64_op_ARITH_imm8); - emit8(x64_modrm(x64_mod_reg, x64_ext_SUB, x64_reg_RSP)); - emit8(x64_ffi_shadow_space); -} -// Map 2-register stack and tape drive into Win64 ABI argument registers -// RCX = RAX (arg1 = Top of Stack) -// RDX = RDX (arg2 = Next of Stack, already in place) -// R8 = vm_globals[0] -// R9 = vm_globals[1] -#define x64_FFI_MAP_ARGS() \ - do { \ - emit8(x64_REX); emit8(x64_op_MOV_rm_reg); emit8(x64_modrm_RAX_RCX); \ - emit8(x64_REX_R); emit8(x64_op_MOV_reg_rm); emit8(x64_modrm(x64_mod_mem, x64_reg_R8, x64_reg_RBX)); \ - emit8(x64_REX_R); emit8(x64_op_MOV_reg_rm); emit8(x64_modrm(x64_mod_mem_disp8, x64_reg_R9, x64_reg_RBX)); emit8(0x08); \ - } while(0) - -// Load absolute 64-bit function address into R10 and call it -#define x64_FFI_CALL_ABS(abs_addr) \ - do { \ - emit8(x64_REX_B); emit8(x64_op_MOV_r10_imm64); \ - emit32(u4_(u8_(abs_addr) & 0xFFFFFFFF)); \ - emit32(u4_(u8_(abs_addr) >> 32)); \ - emit8(0x41); emit8(x64_op_UNARY); \ - emit8(x64_modrm(x64_mod_reg, x64_ext_CALL, x64_reg_R10)); \ - } while(0) - -// Restore RSP and RDX after FFI call -#define x64_FFI_EPILOGUE() \ - do { \ - emit8(x64_REX); emit8(x64_op_ARITH_imm8); \ - emit8(x64_modrm(x64_mod_reg, x64_ext_ADD, x64_reg_RSP)); \ - emit8(x64_ffi_shadow_space); \ - emit8(x64_op_POP_RDX); \ - } while(0) - -#pragma endregion x64 Emission DSL - -internal void compile_action(U4 val) -{ - if (val >= 0x10000) { - U4 p = val - 0x10000; - if (p == PRIM_SWAP) { - x64_XCHG_RAX_RDX(); - pad32(); - return; - } - else if (p == PRIM_MULT) { - x64_IMUL_RAX_RDX(); - pad32(); - return; - } - else if (p == PRIM_ADD) { - x64_ADD_RAX_RDX(); - pad32(); - return; - } - else if (p == PRIM_SUB) { - x64_SUB_RAX_RDX(); - pad32(); - return; - } - else if (p == PRIM_FETCH) { - x64_FETCH(); - pad32(); - return; - } - else if (p == PRIM_DEC) { - x64_DEC_RAX(); - pad32(); - return; - } - else if (p == PRIM_STORE) { - x64_STORE(); - pad32(); - return; - } - else if (p == PRIM_RET_Z) { - x64_RET_IF_ZERO(); - pad32(); - return; - } - else if (p == PRIM_RET_S) { - x64_RET_IF_SIGN(); - pad32(); - return; - } - else if (p == PRIM_RET) { - emit8(x64_op_RET); - pad32(); - return; - } - else if (p == PRIM_DUP) { - x64_MOV_RDX_RAX(); - pad32(); - return; - } - else if (p == PRIM_DROP) { - x64_MOV_RAX_RDX(); - pad32(); - return; - } - else if (p == PRIM_EXECUTE) { - x64_CALL_RAX(); - pad32(); - return; - } - else if (p == PRIM_PRINT) { - x64_FFI_PROLOGUE(); - x64_FFI_MAP_ARGS(); - x64_FFI_CALL_ABS(u8_(& ms_builtin_print)); - x64_FFI_EPILOGUE(); - 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(x64_op_CALL_rel32); - 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; - - // Factorial - { - 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 "); - } - - // Lambda test - { - 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; -} - -``` - ---- - -### `C:\projects\forth\bootslop\attempt_1\microui.c` - -```c -/* -** Copyright (c) 2024 rxi -** -** Permission is hereby granted, free of charge, to any person obtaining a copy -** of this software and associated documentation files (the "Software"), to -** deal in the Software without restriction, including without limitation the -** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -** sell copies of the Software, and to permit persons to whom the Software is -** furnished to do so, subject to the following conditions: -** -** The above copyright notice and this permission notice shall be included in -** all copies or substantial portions of the Software. -** -** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -** IN THE SOFTWARE. -*/ - -#include -#include -#include -#include "microui.h" - -#define unused(x) ((void) (x)) - -#define expect(x) do { \ - if (!(x)) { \ - fprintf(stderr, "Fatal error: %s:%d: assertion '%s' failed\n", \ - __FILE__, __LINE__, #x); \ - abort(); \ - } \ - } while (0) - -#define push(stk, val) do { \ - expect((stk).idx < (int) (sizeof((stk).items) / sizeof(*(stk).items))); \ - (stk).items[(stk).idx] = (val); \ - (stk).idx++; /* incremented after incase `val` uses this value */ \ - } while (0) - -#define pop(stk) do { \ - expect((stk).idx > 0); \ - (stk).idx--; \ - } while (0) - - -static mu_Rect unclipped_rect = { 0, 0, 0x1000000, 0x1000000 }; - -static mu_Style default_style = { - /* font | size | padding | spacing | indent */ - NULL, { 68, 10 }, 5, 4, 24, - /* title_height | scrollbar_size | thumb_size */ - 24, 12, 8, - { - { 230, 230, 230, 255 }, /* MU_COLOR_TEXT */ - { 25, 25, 25, 255 }, /* MU_COLOR_BORDER */ - { 50, 50, 50, 255 }, /* MU_COLOR_WINDOWBG */ - { 25, 25, 25, 255 }, /* MU_COLOR_TITLEBG */ - { 240, 240, 240, 255 }, /* MU_COLOR_TITLETEXT */ - { 0, 0, 0, 0 }, /* MU_COLOR_PANELBG */ - { 75, 75, 75, 255 }, /* MU_COLOR_BUTTON */ - { 95, 95, 95, 255 }, /* MU_COLOR_BUTTONHOVER */ - { 115, 115, 115, 255 }, /* MU_COLOR_BUTTONFOCUS */ - { 30, 30, 30, 255 }, /* MU_COLOR_BASE */ - { 35, 35, 35, 255 }, /* MU_COLOR_BASEHOVER */ - { 40, 40, 40, 255 }, /* MU_COLOR_BASEFOCUS */ - { 43, 43, 43, 255 }, /* MU_COLOR_SCROLLBASE */ - { 30, 30, 30, 255 } /* MU_COLOR_SCROLLTHUMB */ - } -}; - - -mu_Vec2 mu_vec2(int x, int y) { - mu_Vec2 res; - res.x = x; res.y = y; - return res; -} - - -mu_Rect mu_rect(int x, int y, int w, int h) { - mu_Rect res; - res.x = x; res.y = y; res.w = w; res.h = h; - return res; -} - - -mu_Color mu_color(int r, int g, int b, int a) { - mu_Color res; - res.r = r; res.g = g; res.b = b; res.a = a; - return res; -} - - -static mu_Rect expand_rect(mu_Rect rect, int n) { - return mu_rect(rect.x - n, rect.y - n, rect.w + n * 2, rect.h + n * 2); -} - - -static mu_Rect intersect_rects(mu_Rect r1, mu_Rect r2) { - int x1 = mu_max(r1.x, r2.x); - int y1 = mu_max(r1.y, r2.y); - int x2 = mu_min(r1.x + r1.w, r2.x + r2.w); - int y2 = mu_min(r1.y + r1.h, r2.y + r2.h); - if (x2 < x1) { x2 = x1; } - if (y2 < y1) { y2 = y1; } - return mu_rect(x1, y1, x2 - x1, y2 - y1); -} - - -static int rect_overlaps_vec2(mu_Rect r, mu_Vec2 p) { - return p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h; -} - - -static void draw_frame(mu_Context *ctx, mu_Rect rect, int colorid) { - mu_draw_rect(ctx, rect, ctx->style->colors[colorid]); - if (colorid == MU_COLOR_SCROLLBASE || - colorid == MU_COLOR_SCROLLTHUMB || - colorid == MU_COLOR_TITLEBG) { return; } - /* draw border */ - if (ctx->style->colors[MU_COLOR_BORDER].a) { - mu_draw_box(ctx, expand_rect(rect, 1), ctx->style->colors[MU_COLOR_BORDER]); - } -} - - -void mu_init(mu_Context *ctx) { - memset(ctx, 0, sizeof(*ctx)); - ctx->draw_frame = draw_frame; - ctx->_style = default_style; - ctx->style = &ctx->_style; -} - - -void mu_begin(mu_Context *ctx) { - expect(ctx->text_width && ctx->text_height); - ctx->command_list.idx = 0; - ctx->root_list.idx = 0; - ctx->scroll_target = NULL; - ctx->hover_root = ctx->next_hover_root; - ctx->next_hover_root = NULL; - ctx->mouse_delta.x = ctx->mouse_pos.x - ctx->last_mouse_pos.x; - ctx->mouse_delta.y = ctx->mouse_pos.y - ctx->last_mouse_pos.y; - ctx->frame++; -} - - -static int compare_zindex(const void *a, const void *b) { - return (*(mu_Container**) a)->zindex - (*(mu_Container**) b)->zindex; -} - - -void mu_end(mu_Context *ctx) { - int i, n; - /* check stacks */ - expect(ctx->container_stack.idx == 0); - expect(ctx->clip_stack.idx == 0); - expect(ctx->id_stack.idx == 0); - expect(ctx->layout_stack.idx == 0); - - /* handle scroll input */ - if (ctx->scroll_target) { - ctx->scroll_target->scroll.x += ctx->scroll_delta.x; - ctx->scroll_target->scroll.y += ctx->scroll_delta.y; - } - - /* unset focus if focus id was not touched this frame */ - if (!ctx->updated_focus) { ctx->focus = 0; } - ctx->updated_focus = 0; - - /* bring hover root to front if mouse was pressed */ - if (ctx->mouse_pressed && ctx->next_hover_root && - ctx->next_hover_root->zindex < ctx->last_zindex && - ctx->next_hover_root->zindex >= 0 - ) { - mu_bring_to_front(ctx, ctx->next_hover_root); - } - - /* reset input state */ - ctx->key_pressed = 0; - ctx->input_text[0] = '\0'; - ctx->mouse_pressed = 0; - ctx->scroll_delta = mu_vec2(0, 0); - ctx->last_mouse_pos = ctx->mouse_pos; - - /* sort root containers by zindex */ - n = ctx->root_list.idx; - qsort(ctx->root_list.items, n, sizeof(mu_Container*), compare_zindex); - - /* set root container jump commands */ - for (i = 0; i < n; i++) { - mu_Container *cnt = ctx->root_list.items[i]; - /* if this is the first container then make the first command jump to it. - ** otherwise set the previous container's tail to jump to this one */ - if (i == 0) { - mu_Command *cmd = (mu_Command*) ctx->command_list.items; - cmd->jump.dst = (char*) cnt->head + sizeof(mu_JumpCommand); - } else { - mu_Container *prev = ctx->root_list.items[i - 1]; - prev->tail->jump.dst = (char*) cnt->head + sizeof(mu_JumpCommand); - } - /* make the last container's tail jump to the end of command list */ - if (i == n - 1) { - cnt->tail->jump.dst = ctx->command_list.items + ctx->command_list.idx; - } - } -} - - -void mu_set_focus(mu_Context *ctx, mu_Id id) { - ctx->focus = id; - ctx->updated_focus = 1; -} - - -/* 32bit fnv-1a hash */ -#define HASH_INITIAL 2166136261 - -static void hash(mu_Id *hash, const void *data, int size) { - const unsigned char *p = data; - while (size--) { - *hash = (*hash ^ *p++) * 16777619; - } -} - - -mu_Id mu_get_id(mu_Context *ctx, const void *data, int size) { - int idx = ctx->id_stack.idx; - mu_Id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : HASH_INITIAL; - hash(&res, data, size); - ctx->last_id = res; - return res; -} - - -void mu_push_id(mu_Context *ctx, const void *data, int size) { - push(ctx->id_stack, mu_get_id(ctx, data, size)); -} - - -void mu_pop_id(mu_Context *ctx) { - pop(ctx->id_stack); -} - - -void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect) { - mu_Rect last = mu_get_clip_rect(ctx); - push(ctx->clip_stack, intersect_rects(rect, last)); -} - - -void mu_pop_clip_rect(mu_Context *ctx) { - pop(ctx->clip_stack); -} - - -mu_Rect mu_get_clip_rect(mu_Context *ctx) { - expect(ctx->clip_stack.idx > 0); - return ctx->clip_stack.items[ctx->clip_stack.idx - 1]; -} - - -int mu_check_clip(mu_Context *ctx, mu_Rect r) { - mu_Rect cr = mu_get_clip_rect(ctx); - if (r.x > cr.x + cr.w || r.x + r.w < cr.x || - r.y > cr.y + cr.h || r.y + r.h < cr.y ) { return MU_CLIP_ALL; } - if (r.x >= cr.x && r.x + r.w <= cr.x + cr.w && - r.y >= cr.y && r.y + r.h <= cr.y + cr.h ) { return 0; } - return MU_CLIP_PART; -} - - -static void push_layout(mu_Context *ctx, mu_Rect body, mu_Vec2 scroll) { - mu_Layout layout; - int width = 0; - memset(&layout, 0, sizeof(layout)); - layout.body = mu_rect(body.x - scroll.x, body.y - scroll.y, body.w, body.h); - layout.max = mu_vec2(-0x1000000, -0x1000000); - push(ctx->layout_stack, layout); - mu_layout_row(ctx, 1, &width, 0); -} - - -static mu_Layout* get_layout(mu_Context *ctx) { - return &ctx->layout_stack.items[ctx->layout_stack.idx - 1]; -} - - -static void pop_container(mu_Context *ctx) { - mu_Container *cnt = mu_get_current_container(ctx); - mu_Layout *layout = get_layout(ctx); - cnt->content_size.x = layout->max.x - layout->body.x; - cnt->content_size.y = layout->max.y - layout->body.y; - /* pop container, layout and id */ - pop(ctx->container_stack); - pop(ctx->layout_stack); - mu_pop_id(ctx); -} - - -mu_Container* mu_get_current_container(mu_Context *ctx) { - expect(ctx->container_stack.idx > 0); - return ctx->container_stack.items[ ctx->container_stack.idx - 1 ]; -} - - -static mu_Container* get_container(mu_Context *ctx, mu_Id id, int opt) { - mu_Container *cnt; - /* try to get existing container from pool */ - int idx = mu_pool_get(ctx, ctx->container_pool, MU_CONTAINERPOOL_SIZE, id); - if (idx >= 0) { - if (ctx->containers[idx].open || ~opt & MU_OPT_CLOSED) { - mu_pool_update(ctx, ctx->container_pool, idx); - } - return &ctx->containers[idx]; - } - if (opt & MU_OPT_CLOSED) { return NULL; } - /* container not found in pool: init new container */ - idx = mu_pool_init(ctx, ctx->container_pool, MU_CONTAINERPOOL_SIZE, id); - cnt = &ctx->containers[idx]; - memset(cnt, 0, sizeof(*cnt)); - cnt->open = 1; - mu_bring_to_front(ctx, cnt); - return cnt; -} - - -mu_Container* mu_get_container(mu_Context *ctx, const char *name) { - mu_Id id = mu_get_id(ctx, name, strlen(name)); - return get_container(ctx, id, 0); -} - - -void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt) { - cnt->zindex = ++ctx->last_zindex; -} - - -/*============================================================================ -** pool -**============================================================================*/ - -int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id) { - int i, n = -1, f = ctx->frame; - for (i = 0; i < len; i++) { - if (items[i].last_update < f) { - f = items[i].last_update; - n = i; - } - } - expect(n > -1); - items[n].id = id; - mu_pool_update(ctx, items, n); - return n; -} - - -int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id) { - int i; - unused(ctx); - for (i = 0; i < len; i++) { - if (items[i].id == id) { return i; } - } - return -1; -} - - -void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx) { - items[idx].last_update = ctx->frame; -} - - -/*============================================================================ -** input handlers -**============================================================================*/ - -void mu_input_mousemove(mu_Context *ctx, int x, int y) { - ctx->mouse_pos = mu_vec2(x, y); -} - - -void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn) { - mu_input_mousemove(ctx, x, y); - ctx->mouse_down |= btn; - ctx->mouse_pressed |= btn; -} - - -void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn) { - mu_input_mousemove(ctx, x, y); - ctx->mouse_down &= ~btn; -} - - -void mu_input_scroll(mu_Context *ctx, int x, int y) { - ctx->scroll_delta.x += x; - ctx->scroll_delta.y += y; -} - - -void mu_input_keydown(mu_Context *ctx, int key) { - ctx->key_pressed |= key; - ctx->key_down |= key; -} - - -void mu_input_keyup(mu_Context *ctx, int key) { - ctx->key_down &= ~key; -} - - -void mu_input_text(mu_Context *ctx, const char *text) { - int len = strlen(ctx->input_text); - int size = strlen(text) + 1; - expect(len + size <= (int) sizeof(ctx->input_text)); - memcpy(ctx->input_text + len, text, size); -} - - -/*============================================================================ -** commandlist -**============================================================================*/ - -mu_Command* mu_push_command(mu_Context *ctx, int type, int size) { - mu_Command *cmd = (mu_Command*) (ctx->command_list.items + ctx->command_list.idx); - expect(ctx->command_list.idx + size < MU_COMMANDLIST_SIZE); - cmd->base.type = type; - cmd->base.size = size; - ctx->command_list.idx += size; - return cmd; -} - - -int mu_next_command(mu_Context *ctx, mu_Command **cmd) { - if (*cmd) { - *cmd = (mu_Command*) (((char*) *cmd) + (*cmd)->base.size); - } else { - *cmd = (mu_Command*) ctx->command_list.items; - } - while ((char*) *cmd != ctx->command_list.items + ctx->command_list.idx) { - if ((*cmd)->type != MU_COMMAND_JUMP) { return 1; } - *cmd = (*cmd)->jump.dst; - } - return 0; -} - - -static mu_Command* push_jump(mu_Context *ctx, mu_Command *dst) { - mu_Command *cmd; - cmd = mu_push_command(ctx, MU_COMMAND_JUMP, sizeof(mu_JumpCommand)); - cmd->jump.dst = dst; - return cmd; -} - - -void mu_set_clip(mu_Context *ctx, mu_Rect rect) { - mu_Command *cmd; - cmd = mu_push_command(ctx, MU_COMMAND_CLIP, sizeof(mu_ClipCommand)); - cmd->clip.rect = rect; -} - - -void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color) { - mu_Command *cmd; - rect = intersect_rects(rect, mu_get_clip_rect(ctx)); - if (rect.w > 0 && rect.h > 0) { - cmd = mu_push_command(ctx, MU_COMMAND_RECT, sizeof(mu_RectCommand)); - cmd->rect.rect = rect; - cmd->rect.color = color; - } -} - - -void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color) { - mu_draw_rect(ctx, mu_rect(rect.x + 1, rect.y, rect.w - 2, 1), color); - mu_draw_rect(ctx, mu_rect(rect.x + 1, rect.y + rect.h - 1, rect.w - 2, 1), color); - mu_draw_rect(ctx, mu_rect(rect.x, rect.y, 1, rect.h), color); - mu_draw_rect(ctx, mu_rect(rect.x + rect.w - 1, rect.y, 1, rect.h), color); -} - - -void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len, - mu_Vec2 pos, mu_Color color) -{ - mu_Command *cmd; - mu_Rect rect = mu_rect( - pos.x, pos.y, ctx->text_width(font, str, len), ctx->text_height(font)); - int clipped = mu_check_clip(ctx, rect); - if (clipped == MU_CLIP_ALL ) { return; } - if (clipped == MU_CLIP_PART) { mu_set_clip(ctx, mu_get_clip_rect(ctx)); } - /* add command */ - if (len < 0) { len = strlen(str); } - cmd = mu_push_command(ctx, MU_COMMAND_TEXT, sizeof(mu_TextCommand) + len); - memcpy(cmd->text.str, str, len); - cmd->text.str[len] = '\0'; - cmd->text.pos = pos; - cmd->text.color = color; - cmd->text.font = font; - /* reset clipping if it was set */ - if (clipped) { mu_set_clip(ctx, unclipped_rect); } -} - - -void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color) { - mu_Command *cmd; - /* do clip command if the rect isn't fully contained within the cliprect */ - int clipped = mu_check_clip(ctx, rect); - if (clipped == MU_CLIP_ALL ) { return; } - if (clipped == MU_CLIP_PART) { mu_set_clip(ctx, mu_get_clip_rect(ctx)); } - /* do icon command */ - cmd = mu_push_command(ctx, MU_COMMAND_ICON, sizeof(mu_IconCommand)); - cmd->icon.id = id; - cmd->icon.rect = rect; - cmd->icon.color = color; - /* reset clipping if it was set */ - if (clipped) { mu_set_clip(ctx, unclipped_rect); } -} - - -/*============================================================================ -** layout -**============================================================================*/ - -enum { RELATIVE = 1, ABSOLUTE = 2 }; - - -void mu_layout_begin_column(mu_Context *ctx) { - push_layout(ctx, mu_layout_next(ctx), mu_vec2(0, 0)); -} - - -void mu_layout_end_column(mu_Context *ctx) { - mu_Layout *a, *b; - b = get_layout(ctx); - pop(ctx->layout_stack); - /* inherit position/next_row/max from child layout if they are greater */ - a = get_layout(ctx); - a->position.x = mu_max(a->position.x, b->position.x + b->body.x - a->body.x); - a->next_row = mu_max(a->next_row, b->next_row + b->body.y - a->body.y); - a->max.x = mu_max(a->max.x, b->max.x); - a->max.y = mu_max(a->max.y, b->max.y); -} - - -void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height) { - mu_Layout *layout = get_layout(ctx); - if (widths) { - expect(items <= MU_MAX_WIDTHS); - memcpy(layout->widths, widths, items * sizeof(widths[0])); - } - layout->items = items; - layout->position = mu_vec2(layout->indent, layout->next_row); - layout->size.y = height; - layout->item_index = 0; -} - - -void mu_layout_width(mu_Context *ctx, int width) { - get_layout(ctx)->size.x = width; -} - - -void mu_layout_height(mu_Context *ctx, int height) { - get_layout(ctx)->size.y = height; -} - - -void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative) { - mu_Layout *layout = get_layout(ctx); - layout->next = r; - layout->next_type = relative ? RELATIVE : ABSOLUTE; -} - - -mu_Rect mu_layout_next(mu_Context *ctx) { - mu_Layout *layout = get_layout(ctx); - mu_Style *style = ctx->style; - mu_Rect res; - - if (layout->next_type) { - /* handle rect set by `mu_layout_set_next` */ - int type = layout->next_type; - layout->next_type = 0; - res = layout->next; - if (type == ABSOLUTE) { return (ctx->last_rect = res); } - - } else { - /* handle next row */ - if (layout->item_index == layout->items) { - mu_layout_row(ctx, layout->items, NULL, layout->size.y); - } - - /* position */ - res.x = layout->position.x; - res.y = layout->position.y; - - /* size */ - res.w = layout->items > 0 ? layout->widths[layout->item_index] : layout->size.x; - res.h = layout->size.y; - if (res.w == 0) { res.w = style->size.x + style->padding * 2; } - if (res.h == 0) { res.h = style->size.y + style->padding * 2; } - if (res.w < 0) { res.w += layout->body.w - res.x + 1; } - if (res.h < 0) { res.h += layout->body.h - res.y + 1; } - - layout->item_index++; - } - - /* update position */ - layout->position.x += res.w + style->spacing; - layout->next_row = mu_max(layout->next_row, res.y + res.h + style->spacing); - - /* apply body offset */ - res.x += layout->body.x; - res.y += layout->body.y; - - /* update max position */ - layout->max.x = mu_max(layout->max.x, res.x + res.w); - layout->max.y = mu_max(layout->max.y, res.y + res.h); - - return (ctx->last_rect = res); -} - - -/*============================================================================ -** controls -**============================================================================*/ - -static int in_hover_root(mu_Context *ctx) { - int i = ctx->container_stack.idx; - while (i--) { - if (ctx->container_stack.items[i] == ctx->hover_root) { return 1; } - /* only root containers have their `head` field set; stop searching if we've - ** reached the current root container */ - if (ctx->container_stack.items[i]->head) { break; } - } - return 0; -} - - -void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect, - int colorid, int opt) -{ - if (opt & MU_OPT_NOFRAME) { return; } - colorid += (ctx->focus == id) ? 2 : (ctx->hover == id) ? 1 : 0; - ctx->draw_frame(ctx, rect, colorid); -} - - -void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect, - int colorid, int opt) -{ - mu_Vec2 pos; - mu_Font font = ctx->style->font; - int tw = ctx->text_width(font, str, -1); - mu_push_clip_rect(ctx, rect); - pos.y = rect.y + (rect.h - ctx->text_height(font)) / 2; - if (opt & MU_OPT_ALIGNCENTER) { - pos.x = rect.x + (rect.w - tw) / 2; - } else if (opt & MU_OPT_ALIGNRIGHT) { - pos.x = rect.x + rect.w - tw - ctx->style->padding; - } else { - pos.x = rect.x + ctx->style->padding; - } - mu_draw_text(ctx, font, str, -1, pos, ctx->style->colors[colorid]); - mu_pop_clip_rect(ctx); -} - - -int mu_mouse_over(mu_Context *ctx, mu_Rect rect) { - return rect_overlaps_vec2(rect, ctx->mouse_pos) && - rect_overlaps_vec2(mu_get_clip_rect(ctx), ctx->mouse_pos) && - in_hover_root(ctx); -} - - -void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt) { - int mouseover = mu_mouse_over(ctx, rect); - - if (ctx->focus == id) { ctx->updated_focus = 1; } - if (opt & MU_OPT_NOINTERACT) { return; } - if (mouseover && !ctx->mouse_down) { ctx->hover = id; } - - if (ctx->focus == id) { - if (ctx->mouse_pressed && !mouseover) { mu_set_focus(ctx, 0); } - if (!ctx->mouse_down && ~opt & MU_OPT_HOLDFOCUS) { mu_set_focus(ctx, 0); } - } - - if (ctx->hover == id) { - if (ctx->mouse_pressed) { - mu_set_focus(ctx, id); - } else if (!mouseover) { - ctx->hover = 0; - } - } -} - - -void mu_text(mu_Context *ctx, const char *text) { - const char *start, *end, *p = text; - int width = -1; - mu_Font font = ctx->style->font; - mu_Color color = ctx->style->colors[MU_COLOR_TEXT]; - mu_layout_begin_column(ctx); - mu_layout_row(ctx, 1, &width, ctx->text_height(font)); - do { - mu_Rect r = mu_layout_next(ctx); - int w = 0; - start = end = p; - do { - const char* word = p; - while (*p && *p != ' ' && *p != '\n') { p++; } - w += ctx->text_width(font, word, p - word); - if (w > r.w && end != start) { break; } - w += ctx->text_width(font, p, 1); - end = p++; - } while (*end && *end != '\n'); - mu_draw_text(ctx, font, start, end - start, mu_vec2(r.x, r.y), color); - p = end + 1; - } while (*end); - mu_layout_end_column(ctx); -} - - -void mu_label(mu_Context *ctx, const char *text) { - mu_draw_control_text(ctx, text, mu_layout_next(ctx), MU_COLOR_TEXT, 0); -} - - -int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt) { - int res = 0; - mu_Id id = label ? mu_get_id(ctx, label, strlen(label)) - : mu_get_id(ctx, &icon, sizeof(icon)); - mu_Rect r = mu_layout_next(ctx); - mu_update_control(ctx, id, r, opt); - /* handle click */ - if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) { - res |= MU_RES_SUBMIT; - } - /* draw */ - mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, opt); - if (label) { mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, opt); } - if (icon) { mu_draw_icon(ctx, icon, r, ctx->style->colors[MU_COLOR_TEXT]); } - return res; -} - - -int mu_checkbox(mu_Context *ctx, const char *label, int *state) { - int res = 0; - mu_Id id = mu_get_id(ctx, &state, sizeof(state)); - mu_Rect r = mu_layout_next(ctx); - mu_Rect box = mu_rect(r.x, r.y, r.h, r.h); - mu_update_control(ctx, id, r, 0); - /* handle click */ - if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) { - res |= MU_RES_CHANGE; - *state = !*state; - } - /* draw */ - mu_draw_control_frame(ctx, id, box, MU_COLOR_BASE, 0); - if (*state) { - mu_draw_icon(ctx, MU_ICON_CHECK, box, ctx->style->colors[MU_COLOR_TEXT]); - } - r = mu_rect(r.x + box.w, r.y, r.w - box.w, r.h); - mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0); - return res; -} - - -int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r, - int opt) -{ - int res = 0; - mu_update_control(ctx, id, r, opt | MU_OPT_HOLDFOCUS); - - if (ctx->focus == id) { - /* handle text input */ - int len = strlen(buf); - int n = mu_min(bufsz - len - 1, (int) strlen(ctx->input_text)); - if (n > 0) { - memcpy(buf + len, ctx->input_text, n); - len += n; - buf[len] = '\0'; - res |= MU_RES_CHANGE; - } - /* handle backspace */ - if (ctx->key_pressed & MU_KEY_BACKSPACE && len > 0) { - /* skip utf-8 continuation bytes */ - while ((buf[--len] & 0xc0) == 0x80 && len > 0); - buf[len] = '\0'; - res |= MU_RES_CHANGE; - } - /* handle return */ - if (ctx->key_pressed & MU_KEY_RETURN) { - mu_set_focus(ctx, 0); - res |= MU_RES_SUBMIT; - } - } - - /* draw */ - mu_draw_control_frame(ctx, id, r, MU_COLOR_BASE, opt); - if (ctx->focus == id) { - mu_Color color = ctx->style->colors[MU_COLOR_TEXT]; - mu_Font font = ctx->style->font; - int textw = ctx->text_width(font, buf, -1); - int texth = ctx->text_height(font); - int ofx = r.w - ctx->style->padding - textw - 1; - int textx = r.x + mu_min(ofx, ctx->style->padding); - int texty = r.y + (r.h - texth) / 2; - mu_push_clip_rect(ctx, r); - mu_draw_text(ctx, font, buf, -1, mu_vec2(textx, texty), color); - mu_draw_rect(ctx, mu_rect(textx + textw, texty, 1, texth), color); - mu_pop_clip_rect(ctx); - } else { - mu_draw_control_text(ctx, buf, r, MU_COLOR_TEXT, opt); - } - - return res; -} - - -static int number_textbox(mu_Context *ctx, mu_Real *value, mu_Rect r, mu_Id id) { - if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->key_down & MU_KEY_SHIFT && - ctx->hover == id - ) { - ctx->number_edit = id; - sprintf(ctx->number_edit_buf, MU_REAL_FMT, *value); - } - if (ctx->number_edit == id) { - int res = mu_textbox_raw( - ctx, ctx->number_edit_buf, sizeof(ctx->number_edit_buf), id, r, 0); - if (res & MU_RES_SUBMIT || ctx->focus != id) { - *value = strtod(ctx->number_edit_buf, NULL); - ctx->number_edit = 0; - } else { - return 1; - } - } - return 0; -} - - -int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt) { - mu_Id id = mu_get_id(ctx, &buf, sizeof(buf)); - mu_Rect r = mu_layout_next(ctx); - return mu_textbox_raw(ctx, buf, bufsz, id, r, opt); -} - - -int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high, - mu_Real step, const char *fmt, int opt) -{ - char buf[MU_MAX_FMT + 1]; - mu_Rect thumb; - int x, w, res = 0; - mu_Real last = *value, v = last; - mu_Id id = mu_get_id(ctx, &value, sizeof(value)); - mu_Rect base = mu_layout_next(ctx); - - /* handle text input mode */ - if (number_textbox(ctx, &v, base, id)) { return res; } - - /* handle normal mode */ - mu_update_control(ctx, id, base, opt); - - /* handle input */ - if (ctx->focus == id && - (ctx->mouse_down | ctx->mouse_pressed) == MU_MOUSE_LEFT) - { - v = low + (ctx->mouse_pos.x - base.x) * (high - low) / base.w; - if (step) { v = ((long long)((v + step / 2) / step)) * step; } - } - /* clamp and store value, update res */ - *value = v = mu_clamp(v, low, high); - if (last != v) { res |= MU_RES_CHANGE; } - - /* draw base */ - mu_draw_control_frame(ctx, id, base, MU_COLOR_BASE, opt); - /* draw thumb */ - w = ctx->style->thumb_size; - x = (v - low) * (base.w - w) / (high - low); - thumb = mu_rect(base.x + x, base.y, w, base.h); - mu_draw_control_frame(ctx, id, thumb, MU_COLOR_BUTTON, opt); - /* draw text */ - sprintf(buf, fmt, v); - mu_draw_control_text(ctx, buf, base, MU_COLOR_TEXT, opt); - - return res; -} - - -int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step, - const char *fmt, int opt) -{ - char buf[MU_MAX_FMT + 1]; - int res = 0; - mu_Id id = mu_get_id(ctx, &value, sizeof(value)); - mu_Rect base = mu_layout_next(ctx); - mu_Real last = *value; - - /* handle text input mode */ - if (number_textbox(ctx, value, base, id)) { return res; } - - /* handle normal mode */ - mu_update_control(ctx, id, base, opt); - - /* handle input */ - if (ctx->focus == id && ctx->mouse_down == MU_MOUSE_LEFT) { - *value += ctx->mouse_delta.x * step; - } - /* set flag if value changed */ - if (*value != last) { res |= MU_RES_CHANGE; } - - /* draw base */ - mu_draw_control_frame(ctx, id, base, MU_COLOR_BASE, opt); - /* draw text */ - sprintf(buf, fmt, *value); - mu_draw_control_text(ctx, buf, base, MU_COLOR_TEXT, opt); - - return res; -} - - -static int header(mu_Context *ctx, const char *label, int istreenode, int opt) { - mu_Rect r; - int active, expanded; - mu_Id id = mu_get_id(ctx, label, strlen(label)); - int idx = mu_pool_get(ctx, ctx->treenode_pool, MU_TREENODEPOOL_SIZE, id); - int width = -1; - mu_layout_row(ctx, 1, &width, 0); - - active = (idx >= 0); - expanded = (opt & MU_OPT_EXPANDED) ? !active : active; - r = mu_layout_next(ctx); - mu_update_control(ctx, id, r, 0); - - /* handle click */ - active ^= (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id); - - /* update pool ref */ - if (idx >= 0) { - if (active) { mu_pool_update(ctx, ctx->treenode_pool, idx); } - else { memset(&ctx->treenode_pool[idx], 0, sizeof(mu_PoolItem)); } - } else if (active) { - mu_pool_init(ctx, ctx->treenode_pool, MU_TREENODEPOOL_SIZE, id); - } - - /* draw */ - if (istreenode) { - if (ctx->hover == id) { ctx->draw_frame(ctx, r, MU_COLOR_BUTTONHOVER); } - } else { - mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, 0); - } - mu_draw_icon( - ctx, expanded ? MU_ICON_EXPANDED : MU_ICON_COLLAPSED, - mu_rect(r.x, r.y, r.h, r.h), ctx->style->colors[MU_COLOR_TEXT]); - r.x += r.h - ctx->style->padding; - r.w -= r.h - ctx->style->padding; - mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0); - - return expanded ? MU_RES_ACTIVE : 0; -} - - -int mu_header_ex(mu_Context *ctx, const char *label, int opt) { - return header(ctx, label, 0, opt); -} - - -int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt) { - int res = header(ctx, label, 1, opt); - if (res & MU_RES_ACTIVE) { - get_layout(ctx)->indent += ctx->style->indent; - push(ctx->id_stack, ctx->last_id); - } - return res; -} - - -void mu_end_treenode(mu_Context *ctx) { - get_layout(ctx)->indent -= ctx->style->indent; - mu_pop_id(ctx); -} - - -#define scrollbar(ctx, cnt, b, cs, x, y, w, h) \ - do { \ - /* only add scrollbar if content size is larger than body */ \ - int maxscroll = cs.y - b->h; \ - \ - if (maxscroll > 0 && b->h > 0) { \ - mu_Rect base, thumb; \ - mu_Id id = mu_get_id(ctx, "!scrollbar" #y, 11); \ - \ - /* get sizing / positioning */ \ - base = *b; \ - base.x = b->x + b->w; \ - base.w = ctx->style->scrollbar_size; \ - \ - /* handle input */ \ - mu_update_control(ctx, id, base, 0); \ - if (ctx->focus == id && ctx->mouse_down == MU_MOUSE_LEFT) { \ - cnt->scroll.y += ctx->mouse_delta.y * cs.y / base.h; \ - } \ - /* clamp scroll to limits */ \ - cnt->scroll.y = mu_clamp(cnt->scroll.y, 0, maxscroll); \ - \ - /* draw base and thumb */ \ - ctx->draw_frame(ctx, base, MU_COLOR_SCROLLBASE); \ - thumb = base; \ - thumb.h = mu_max(ctx->style->thumb_size, base.h * b->h / cs.y); \ - thumb.y += cnt->scroll.y * (base.h - thumb.h) / maxscroll; \ - ctx->draw_frame(ctx, thumb, MU_COLOR_SCROLLTHUMB); \ - \ - /* set this as the scroll_target (will get scrolled on mousewheel) */ \ - /* if the mouse is over it */ \ - if (mu_mouse_over(ctx, *b)) { ctx->scroll_target = cnt; } \ - } else { \ - cnt->scroll.y = 0; \ - } \ - } while (0) - - -static void scrollbars(mu_Context *ctx, mu_Container *cnt, mu_Rect *body) { - int sz = ctx->style->scrollbar_size; - mu_Vec2 cs = cnt->content_size; - cs.x += ctx->style->padding * 2; - cs.y += ctx->style->padding * 2; - mu_push_clip_rect(ctx, *body); - /* resize body to make room for scrollbars */ - if (cs.y > cnt->body.h) { body->w -= sz; } - if (cs.x > cnt->body.w) { body->h -= sz; } - /* to create a horizontal or vertical scrollbar almost-identical code is - ** used; only the references to `x|y` `w|h` need to be switched */ - scrollbar(ctx, cnt, body, cs, x, y, w, h); - scrollbar(ctx, cnt, body, cs, y, x, h, w); - mu_pop_clip_rect(ctx); -} - - -static void push_container_body( - mu_Context *ctx, mu_Container *cnt, mu_Rect body, int opt -) { - if (~opt & MU_OPT_NOSCROLL) { scrollbars(ctx, cnt, &body); } - push_layout(ctx, expand_rect(body, -ctx->style->padding), cnt->scroll); - cnt->body = body; -} - - -static void begin_root_container(mu_Context *ctx, mu_Container *cnt) { - push(ctx->container_stack, cnt); - /* push container to roots list and push head command */ - push(ctx->root_list, cnt); - cnt->head = push_jump(ctx, NULL); - /* set as hover root if the mouse is overlapping this container and it has a - ** higher zindex than the current hover root */ - if (rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) && - (!ctx->next_hover_root || cnt->zindex > ctx->next_hover_root->zindex) - ) { - ctx->next_hover_root = cnt; - } - /* clipping is reset here in case a root-container is made within - ** another root-containers's begin/end block; this prevents the inner - ** root-container being clipped to the outer */ - push(ctx->clip_stack, unclipped_rect); -} - - -static void end_root_container(mu_Context *ctx) { - /* push tail 'goto' jump command and set head 'skip' command. the final steps - ** on initing these are done in mu_end() */ - mu_Container *cnt = mu_get_current_container(ctx); - cnt->tail = push_jump(ctx, NULL); - cnt->head->jump.dst = ctx->command_list.items + ctx->command_list.idx; - /* pop base clip rect and container */ - mu_pop_clip_rect(ctx); - pop_container(ctx); -} - - -int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt) { - mu_Rect body; - mu_Id id = mu_get_id(ctx, title, strlen(title)); - mu_Container *cnt = get_container(ctx, id, opt); - if (!cnt || !cnt->open) { return 0; } - push(ctx->id_stack, id); - - if (cnt->rect.w == 0) { cnt->rect = rect; } - begin_root_container(ctx, cnt); - rect = body = cnt->rect; - - /* draw frame */ - if (~opt & MU_OPT_NOFRAME) { - ctx->draw_frame(ctx, rect, MU_COLOR_WINDOWBG); - } - - /* do title bar */ - if (~opt & MU_OPT_NOTITLE) { - mu_Rect tr = rect; - tr.h = ctx->style->title_height; - ctx->draw_frame(ctx, tr, MU_COLOR_TITLEBG); - - /* do title text */ - if (~opt & MU_OPT_NOTITLE) { - mu_Id id = mu_get_id(ctx, "!title", 6); - mu_update_control(ctx, id, tr, opt); - mu_draw_control_text(ctx, title, tr, MU_COLOR_TITLETEXT, opt); - if (id == ctx->focus && ctx->mouse_down == MU_MOUSE_LEFT) { - cnt->rect.x += ctx->mouse_delta.x; - cnt->rect.y += ctx->mouse_delta.y; - } - body.y += tr.h; - body.h -= tr.h; - } - - /* do `close` button */ - if (~opt & MU_OPT_NOCLOSE) { - mu_Id id = mu_get_id(ctx, "!close", 6); - mu_Rect r = mu_rect(tr.x + tr.w - tr.h, tr.y, tr.h, tr.h); - tr.w -= r.w; - mu_draw_icon(ctx, MU_ICON_CLOSE, r, ctx->style->colors[MU_COLOR_TITLETEXT]); - mu_update_control(ctx, id, r, opt); - if (ctx->mouse_pressed == MU_MOUSE_LEFT && id == ctx->focus) { - cnt->open = 0; - } - } - } - - push_container_body(ctx, cnt, body, opt); - - /* do `resize` handle */ - if (~opt & MU_OPT_NORESIZE) { - int sz = ctx->style->title_height; - mu_Id id = mu_get_id(ctx, "!resize", 7); - mu_Rect r = mu_rect(rect.x + rect.w - sz, rect.y + rect.h - sz, sz, sz); - mu_update_control(ctx, id, r, opt); - if (id == ctx->focus && ctx->mouse_down == MU_MOUSE_LEFT) { - cnt->rect.w = mu_max(96, cnt->rect.w + ctx->mouse_delta.x); - cnt->rect.h = mu_max(64, cnt->rect.h + ctx->mouse_delta.y); - } - } - - /* resize to content size */ - if (opt & MU_OPT_AUTOSIZE) { - mu_Rect r = get_layout(ctx)->body; - cnt->rect.w = cnt->content_size.x + (cnt->rect.w - r.w); - cnt->rect.h = cnt->content_size.y + (cnt->rect.h - r.h); - } - - /* close if this is a popup window and elsewhere was clicked */ - if (opt & MU_OPT_POPUP && ctx->mouse_pressed && ctx->hover_root != cnt) { - cnt->open = 0; - } - - mu_push_clip_rect(ctx, cnt->body); - return MU_RES_ACTIVE; -} - - -void mu_end_window(mu_Context *ctx) { - mu_pop_clip_rect(ctx); - end_root_container(ctx); -} - - -void mu_open_popup(mu_Context *ctx, const char *name) { - mu_Container *cnt = mu_get_container(ctx, name); - /* set as hover root so popup isn't closed in begin_window_ex() */ - ctx->hover_root = ctx->next_hover_root = cnt; - /* position at mouse cursor, open and bring-to-front */ - cnt->rect = mu_rect(ctx->mouse_pos.x, ctx->mouse_pos.y, 1, 1); - cnt->open = 1; - mu_bring_to_front(ctx, cnt); -} - - -int mu_begin_popup(mu_Context *ctx, const char *name) { - int opt = MU_OPT_POPUP | MU_OPT_AUTOSIZE | MU_OPT_NORESIZE | - MU_OPT_NOSCROLL | MU_OPT_NOTITLE | MU_OPT_CLOSED; - return mu_begin_window_ex(ctx, name, mu_rect(0, 0, 0, 0), opt); -} - - -void mu_end_popup(mu_Context *ctx) { - mu_end_window(ctx); -} - - -void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt) { - mu_Container *cnt; - mu_push_id(ctx, name, strlen(name)); - cnt = get_container(ctx, ctx->last_id, opt); - cnt->rect = mu_layout_next(ctx); - if (~opt & MU_OPT_NOFRAME) { - ctx->draw_frame(ctx, cnt->rect, MU_COLOR_PANELBG); - } - push(ctx->container_stack, cnt); - push_container_body(ctx, cnt, cnt->rect, opt); - mu_push_clip_rect(ctx, cnt->body); -} - - -void mu_end_panel(mu_Context *ctx) { - mu_pop_clip_rect(ctx); - pop_container(ctx); -} - -``` - ---- - -### `C:\projects\forth\bootslop\attempt_1\microui.h` - -```h -/* -** Copyright (c) 2024 rxi -** -** This library is free software; you can redistribute it and/or modify it -** under the terms of the MIT license. See `microui.c` for details. -*/ - -#ifndef MICROUI_H -#define MICROUI_H - -#define MU_VERSION "2.02" - -#define MU_COMMANDLIST_SIZE (256 * 1024) -#define MU_ROOTLIST_SIZE 32 -#define MU_CONTAINERSTACK_SIZE 32 -#define MU_CLIPSTACK_SIZE 32 -#define MU_IDSTACK_SIZE 32 -#define MU_LAYOUTSTACK_SIZE 16 -#define MU_CONTAINERPOOL_SIZE 48 -#define MU_TREENODEPOOL_SIZE 48 -#define MU_MAX_WIDTHS 16 -#define MU_REAL float -#define MU_REAL_FMT "%.3g" -#define MU_SLIDER_FMT "%.2f" -#define MU_MAX_FMT 127 - -#define mu_stack(T, n) struct { int idx; T items[n]; } -#define mu_min(a, b) ((a) < (b) ? (a) : (b)) -#define mu_max(a, b) ((a) > (b) ? (a) : (b)) -#define mu_clamp(x, a, b) mu_min(b, mu_max(a, x)) - -enum { - MU_CLIP_PART = 1, - MU_CLIP_ALL -}; - -enum { - MU_COMMAND_JUMP = 1, - MU_COMMAND_CLIP, - MU_COMMAND_RECT, - MU_COMMAND_TEXT, - MU_COMMAND_ICON, - MU_COMMAND_MAX -}; - -enum { - MU_COLOR_TEXT, - MU_COLOR_BORDER, - MU_COLOR_WINDOWBG, - MU_COLOR_TITLEBG, - MU_COLOR_TITLETEXT, - MU_COLOR_PANELBG, - MU_COLOR_BUTTON, - MU_COLOR_BUTTONHOVER, - MU_COLOR_BUTTONFOCUS, - MU_COLOR_BASE, - MU_COLOR_BASEHOVER, - MU_COLOR_BASEFOCUS, - MU_COLOR_SCROLLBASE, - MU_COLOR_SCROLLTHUMB, - MU_COLOR_MAX -}; - -enum { - MU_ICON_CLOSE = 1, - MU_ICON_CHECK, - MU_ICON_COLLAPSED, - MU_ICON_EXPANDED, - MU_ICON_MAX -}; - -enum { - MU_RES_ACTIVE = (1 << 0), - MU_RES_SUBMIT = (1 << 1), - MU_RES_CHANGE = (1 << 2) -}; - -enum { - MU_OPT_ALIGNCENTER = (1 << 0), - MU_OPT_ALIGNRIGHT = (1 << 1), - MU_OPT_NOINTERACT = (1 << 2), - MU_OPT_NOFRAME = (1 << 3), - MU_OPT_NORESIZE = (1 << 4), - MU_OPT_NOSCROLL = (1 << 5), - MU_OPT_NOCLOSE = (1 << 6), - MU_OPT_NOTITLE = (1 << 7), - MU_OPT_HOLDFOCUS = (1 << 8), - MU_OPT_AUTOSIZE = (1 << 9), - MU_OPT_POPUP = (1 << 10), - MU_OPT_CLOSED = (1 << 11), - MU_OPT_EXPANDED = (1 << 12) -}; - -enum { - MU_MOUSE_LEFT = (1 << 0), - MU_MOUSE_RIGHT = (1 << 1), - MU_MOUSE_MIDDLE = (1 << 2) -}; - -enum { - MU_KEY_SHIFT = (1 << 0), - MU_KEY_CTRL = (1 << 1), - MU_KEY_ALT = (1 << 2), - MU_KEY_BACKSPACE = (1 << 3), - MU_KEY_RETURN = (1 << 4) -}; - - -typedef struct mu_Context mu_Context; -typedef unsigned mu_Id; -typedef MU_REAL mu_Real; -typedef void* mu_Font; - -typedef struct { int x, y; } mu_Vec2; -typedef struct { int x, y, w, h; } mu_Rect; -typedef struct { unsigned char r, g, b, a; } mu_Color; -typedef struct { mu_Id id; int last_update; } mu_PoolItem; - -typedef struct { int type, size; } mu_BaseCommand; -typedef struct { mu_BaseCommand base; void *dst; } mu_JumpCommand; -typedef struct { mu_BaseCommand base; mu_Rect rect; } mu_ClipCommand; -typedef struct { mu_BaseCommand base; mu_Rect rect; mu_Color color; } mu_RectCommand; -typedef struct { mu_BaseCommand base; mu_Font font; mu_Vec2 pos; mu_Color color; char str[1]; } mu_TextCommand; -typedef struct { mu_BaseCommand base; mu_Rect rect; int id; mu_Color color; } mu_IconCommand; - -typedef union { - int type; - mu_BaseCommand base; - mu_JumpCommand jump; - mu_ClipCommand clip; - mu_RectCommand rect; - mu_TextCommand text; - mu_IconCommand icon; -} mu_Command; - -typedef struct { - mu_Rect body; - mu_Rect next; - mu_Vec2 position; - mu_Vec2 size; - mu_Vec2 max; - int widths[MU_MAX_WIDTHS]; - int items; - int item_index; - int next_row; - int next_type; - int indent; -} mu_Layout; - -typedef struct { - mu_Command *head, *tail; - mu_Rect rect; - mu_Rect body; - mu_Vec2 content_size; - mu_Vec2 scroll; - int zindex; - int open; -} mu_Container; - -typedef struct { - mu_Font font; - mu_Vec2 size; - int padding; - int spacing; - int indent; - int title_height; - int scrollbar_size; - int thumb_size; - mu_Color colors[MU_COLOR_MAX]; -} mu_Style; - -struct mu_Context { - /* callbacks */ - int (*text_width)(mu_Font font, const char *str, int len); - int (*text_height)(mu_Font font); - void (*draw_frame)(mu_Context *ctx, mu_Rect rect, int colorid); - /* core state */ - mu_Style _style; - mu_Style *style; - mu_Id hover; - mu_Id focus; - mu_Id last_id; - mu_Rect last_rect; - int last_zindex; - int updated_focus; - int frame; - mu_Container *hover_root; - mu_Container *next_hover_root; - mu_Container *scroll_target; - char number_edit_buf[MU_MAX_FMT]; - mu_Id number_edit; - /* stacks */ - mu_stack(char, MU_COMMANDLIST_SIZE) command_list; - mu_stack(mu_Container*, MU_ROOTLIST_SIZE) root_list; - mu_stack(mu_Container*, MU_CONTAINERSTACK_SIZE) container_stack; - mu_stack(mu_Rect, MU_CLIPSTACK_SIZE) clip_stack; - mu_stack(mu_Id, MU_IDSTACK_SIZE) id_stack; - mu_stack(mu_Layout, MU_LAYOUTSTACK_SIZE) layout_stack; - /* retained state pools */ - mu_PoolItem container_pool[MU_CONTAINERPOOL_SIZE]; - mu_Container containers[MU_CONTAINERPOOL_SIZE]; - mu_PoolItem treenode_pool[MU_TREENODEPOOL_SIZE]; - /* input state */ - mu_Vec2 mouse_pos; - mu_Vec2 last_mouse_pos; - mu_Vec2 mouse_delta; - mu_Vec2 scroll_delta; - int mouse_down; - int mouse_pressed; - int key_down; - int key_pressed; - char input_text[32]; -}; - - -mu_Vec2 mu_vec2(int x, int y); -mu_Rect mu_rect(int x, int y, int w, int h); -mu_Color mu_color(int r, int g, int b, int a); - -void mu_init(mu_Context *ctx); -void mu_begin(mu_Context *ctx); -void mu_end(mu_Context *ctx); -void mu_set_focus(mu_Context *ctx, mu_Id id); -mu_Id mu_get_id(mu_Context *ctx, const void *data, int size); -void mu_push_id(mu_Context *ctx, const void *data, int size); -void mu_pop_id(mu_Context *ctx); -void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect); -void mu_pop_clip_rect(mu_Context *ctx); -mu_Rect mu_get_clip_rect(mu_Context *ctx); -int mu_check_clip(mu_Context *ctx, mu_Rect r); -mu_Container* mu_get_current_container(mu_Context *ctx); -mu_Container* mu_get_container(mu_Context *ctx, const char *name); -void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt); - -int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id); -int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id); -void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx); - -void mu_input_mousemove(mu_Context *ctx, int x, int y); -void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn); -void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn); -void mu_input_scroll(mu_Context *ctx, int x, int y); -void mu_input_keydown(mu_Context *ctx, int key); -void mu_input_keyup(mu_Context *ctx, int key); -void mu_input_text(mu_Context *ctx, const char *text); - -mu_Command* mu_push_command(mu_Context *ctx, int type, int size); -int mu_next_command(mu_Context *ctx, mu_Command **cmd); -void mu_set_clip(mu_Context *ctx, mu_Rect rect); -void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color); -void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color); -void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len, mu_Vec2 pos, mu_Color color); -void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color); - -void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height); -void mu_layout_width(mu_Context *ctx, int width); -void mu_layout_height(mu_Context *ctx, int height); -void mu_layout_begin_column(mu_Context *ctx); -void mu_layout_end_column(mu_Context *ctx); -void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative); -mu_Rect mu_layout_next(mu_Context *ctx); - -void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect, int colorid, int opt); -void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect, int colorid, int opt); -int mu_mouse_over(mu_Context *ctx, mu_Rect rect); -void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt); - -#define mu_button(ctx, label) mu_button_ex(ctx, label, 0, MU_OPT_ALIGNCENTER) -#define mu_textbox(ctx, buf, bufsz) mu_textbox_ex(ctx, buf, bufsz, 0) -#define mu_slider(ctx, value, lo, hi) mu_slider_ex(ctx, value, lo, hi, 0, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER) -#define mu_number(ctx, value, step) mu_number_ex(ctx, value, step, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER) -#define mu_header(ctx, label) mu_header_ex(ctx, label, 0) -#define mu_begin_treenode(ctx, label) mu_begin_treenode_ex(ctx, label, 0) -#define mu_begin_window(ctx, title, rect) mu_begin_window_ex(ctx, title, rect, 0) -#define mu_begin_panel(ctx, name) mu_begin_panel_ex(ctx, name, 0) - -void mu_text(mu_Context *ctx, const char *text); -void mu_label(mu_Context *ctx, const char *text); -int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt); -int mu_checkbox(mu_Context *ctx, const char *label, int *state); -int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r, int opt); -int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt); -int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high, mu_Real step, const char *fmt, int opt); -int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step, const char *fmt, int opt); -int mu_header_ex(mu_Context *ctx, const char *label, int opt); -int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt); -void mu_end_treenode(mu_Context *ctx); -int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt); -void mu_end_window(mu_Context *ctx); -void mu_open_popup(mu_Context *ctx, const char *name); -int mu_begin_popup(mu_Context *ctx, const char *name); -void mu_end_popup(mu_Context *ctx); -void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt); -void mu_end_panel(mu_Context *ctx); - -#endif - -``` - ---- - -### `C:\projects\forth\bootslop\scripts\build.attempt_1.c.ps1` - -```ps1 -$path_root = split-path -Path $PSScriptRoot -Parent - -# --- Toolchain Executable Paths --- -$compiler = 'clang' -$linker = 'lld-link.exe' - -# https://clang.llvm.org/docs/ClangCommandLineReference.html -$flag_c23 = '-std=c23' -$flag_compile = '-c' -$flag_debug = '-g' -$flag_define = '-D' -$flag_exceptions_disabled = '-fno-exceptions' -$flag_diagnostics_absolute_paths = '-fdiagnostics-absolute-paths' -$flag_include = '-I' -$flag_linker = '-Wl,' -$flag_link_mapfile = '/MAP:' -$flag_link_win_subsystem_console = '/SUBSYSTEM:CONSOLE' -$flag_link_win_machine_64 = '/MACHINE:X64' -$flag_link_win_debug = '/DEBUG' -$flag_link_win_pdb = '/PDB:' -$flag_link_win_path_output = '/OUT:' -$flag_link_no_incremental = '/INCREMENTAL:NO' -$flag_no_optimization = '-O0' -$flag_path_output = '-o' -$flag_wall = '-Wall' -$flag_nologo = '/nologo' - -$path_build = join-path $path_root 'build' -if ( -not(test-path -Path $path_build) ) { - new-item -ItemType Directory -Path $path_build -} - -push-location $path_build - -# --- File Paths --- -$unit_name = "attempt_1" -$unit_source = join-path $path_root "attempt_1\main.c" -$object = join-path $path_build "$unit_name.obj" -$binary = join-path $path_build "$unit_name.exe" -$pdb = join-path $path_build "$unit_name.pdb" -$map = join-path $path_build "$unit_name.map" - -# --- Stage 1: Compile C to Object --- -write-host "Stage 1: Compiling C to Object" -$compiler_args = @() -$compiler_args += ($flag_define + 'BUILD_DEBUG=1') -$compiler_args += $flag_debug -$compiler_args += $flag_wall -$compiler_args += $flag_c23 -$compiler_args += $flag_no_optimization -$compiler_args += $flag_diagnostics_absolute_paths -$compiler_args += $flag_exceptions_disabled -$compiler_args += ($flag_include + (join-path $path_root "attempt_1")) -# $compiler_args += "-nostdlib" -# $compiler_args += "-ffreestanding" -$compiler_args += $flag_compile -$compiler_args += $flag_path_output, $object -$compiler_args += $unit_source - -$compiler_args | ForEach-Object { Write-Host $_ } -$stage1_time = Measure-Command { & $compiler $compiler_args } -write-host "Compilation took $($stage1_time.TotalMilliseconds)ms" -write-host - -# --- Stage 2: Linking --- -write-host "Stage 2: Linking" -$linker_args = @() -$linker_args += $flag_nologo -$linker_args += $flag_link_win_machine_64 -$linker_args += $flag_link_no_incremental -$linker_args += ($flag_link_win_path_output + $binary) -$linker_args += $flag_link_win_debug -$linker_args += $flag_link_win_pdb + $pdb -$linker_args += $flag_link_mapfile + $map -$linker_args += $flag_link_win_subsystem_console -# $linker_args += "/nodefaultlib" -$linker_args += "kernel32.lib" -$linker_args += "user32.lib" -$linker_args += "gdi32.lib" -$linker_args += $object - -$linker_args | ForEach-Object { Write-Host $_ } -$linking_time = Measure-Command { & $linker $linker_args } -write-host "Linking took $($linking_time.TotalMilliseconds)ms" -write-host - -if ($LASTEXITCODE -eq 0) { - write-host "Build Successful! Run $binary manually to see the GUI." -ForegroundColor Green -} - -Pop-Location - -``` - ---- - -### `./references/Architectural_Consolidation.md` - -```md -# Architectural Consolidation: Zero-Overhead Sourceless ColorForth - -This document serves as the master blueprint for the research and curation phase, synthesizing the findings from Timothy Lottes, Onat Türkçüoğlu, and related high-performance minimalist systems. - -## 1. Core Philosophy -* **Sourceless:** The "source of truth" is a 32-bit token array, not a text file. No string parsing occurs at runtime. -* **Zero-Overhead:** Instant iteration (<5ms compilation) by emitting machine code directly from tokens. -* **Bounded Complexity:** Force complexity into data structures rather than code logic. -* **Hardware Locality:** Treat the register file as a global namespace; minimize or eliminate the data stack. - -## 2. Lottes' x68 Architecture (The Frontend/Editor) -* **32-Bit Instruction Granularity:** Every x86-64 instruction is padded to exactly 4 bytes (or multiples thereof) using ignored prefixes and multi-byte NOPs. - * *Example:* `RET` (0xC3) -> `C3 90 90 90`. -* **Token Format:** 32-bit words consisting of: - * **28 Bits:** Compressed name/string or value. - * **4 Bits:** Semantic Tag (Opcode, Abs Addr, Rel Addr, Immediate, etc.). -* **Annotation Overlay:** A parallel memory layer (e.g., 64-bit per token) stores metadata for the editor (colors, names, formatting tags) without polluting the executable. -* **Tooling Recommendation:** **ImHex** with a custom `.hexpat` pattern language can serve as the visual frontend for this annotation overlay. - -## 3. Onat's VAMP/KYRA Architecture (The Runtime/Codegen) -* **2-Item Register Stack:** Uses `RAX` and `RDX` as a tiny, hardware-resident stack. - * **The Swap / Magenta Pipe:** A definition boundary implicitly emits `RET` (to close the last block) followed by `xchg rax, rdx` (1-byte: `48 87 C2` or `48 92`) to rotate the "top of stack" for the new block. -* **Aliased Global Namespace:** The CPU register file is treated as a shared, aliased memory space for functions. -* **Functions as Blocks:** Words are "free of arguments and returns" in the traditional sense. -* **Preemptive Scatter ("Tape Drive"):** Arguments are pre-placed into fixed, contiguous memory slots ("the tape") by the compiler/loader before execution. This eliminates "argument gathering" during function calls. -* **The FFI Dance (C-ABI Integration):** To call OS APIs (like WinAPI or Vulkan), the hardware stack pointer (`RSP`) must be strictly 16-byte aligned. Custom macros (like `CCALL`) must save state, align `RSP`, map the 2-register stack into C-ABI registers (`RCX`, `RDX`, `R8`, `R9`), execute the `CALL`, and restore `RSP`. - -## 4. Implementation Components -* **Emitter:** **Zydis Encoder API**. Zero-allocation, sub-5ms instruction generation. -* **Live Reload:** **Hot Runtime Linking** (Fredriksson style). Atomic pointer swapping at main-loop "safe points" to patch code in-place. -* **Threading Model:** **Direct Threaded Code (DTC)** for the initial dictionary/execution token (`xt`) baseline. -* **Wasm Parallels:** WebAssembly's linear memory and binary sectioning provide a modern reference for the "tape drive" and fixed-offset load/store model. - -## 5. Visual Semantics (ColorForth Mapping) -* **RED:** Define new word (Dictionary entry). -* **GREEN:** Compile word into current definition. -* **YELLOW/ORANGE:** Immediate execution (Macros/Editor commands). -* **CYAN/BLUE:** Variables, Addresses, Layout. -* **WHITE/DIM:** Comments, Annotations, UI. -* **MAGENTA:** Pointers, State modifiers. - ---- -*Curation Phase Status: COMPLETE* -*Ready for Strategy Phase: Pending Directive* - -``` - ---- - -### `./references/neokineogfx_in-depth.md` - -```md -# In-Depth Analysis: Neokineogfx - 4th And Beyond (Timothy Lottes) - -This document synthesizes the insights extracted from the transcript and OCR analysis of Timothy Lottes's "4th And Beyond" presentation video (released under his Neokineogfx channel in 2026). It details the evolution of his Forth derivatives, the specifics of his "x68" encoding, and the mechanics of his "5th" system. - ---- - -## 1. Evolution from Calculator to Forth -Lottes traces the ideal interactive tool back to Reverse Polish Notation (RPN) calculators like the HP48. -* **The Baseline:** Start with simple RPN math on a stack. -* **The Dictionary:** Introduce a dictionary that points to positions on the data stack or to executable code. -* **Color Semantics (ColorForth Inspired):** - * **Yellow (Execute):** Push numbers to the stack, or execute dictionary words. - * **Red (Define):** Define a word. - * **Green (Compile):** Compile words or push values during compilation. - * **Magenta (Variable):** Define a variable. - -## 2. The Branch Misprediction Problem -Standard Forth causes severe CPU pipeline stalls (averaging 16-clock stalls on architectures like Zen 2) due to constant branch misprediction when interpreting tags or navigating the dictionary lookup loop. - -* **Solution - The Folded Interpreter:** Lottes mitigates this by folding a tiny (5-byte) interpreter directly into the end of every compiled word. -* By ending every word with its own fetch/dispatch logic (e.g., `LODSD`, lookup, `JMP`), the CPU's branch predictor gets unique slots for every transition, drastically improving execution speed. - -## 3. The Architecture of "Source-Less" (x68) -To make manipulating binary data as easy as text, Lottes invented "x68"—a subset of x86-64 designed purely around 32-bit boundaries. - -* **32-Bit Instruction Granularity:** Every x86-64 instruction is padded to exactly 4 bytes (or multiples of 4). -* **Prefix Padding:** x86-64 allows ignored prefixes (like `3E`, the DS segment override) and multi-byte NOPs to pad instructions. - * *Example (RET):* `C3` padded to `f0f c3` or `C3 90 90 90` (RET + NOPs). - * *Example (Inline Data):* Moving a 32-bit immediate is padded with `3E`s to ensure the immediate value is perfectly 32-bit aligned in the next memory slot. -* **Why?** This removes the complexity of variable-length instructions, turning compilation into an edit-time operation where the user simply copies and pastes 32-bit words. - -## 4. Editor Mechanics & Annotation Overlay -The editor is an "Advanced 32-bit Hex Editor". The source code is literally the binary array. - -* **Structure:** The file is split into blocks. For every 32-bit source word, there are 64 bits of annotation memory. -* **64-bit Annotation Layout:** - * 8 characters encoded in 7 bits each (56 bits total) acting as the human-readable Label/Note. - * 8-bit Tag. This tag dictates how the 32-bit value in memory is formatted in the editor (e.g., Hex Data, Absolute Address, Relative Address). -* **Visual Layout:** The editor displays lines with two elements per cell: - * Top: The Annotation string (color-coded by tag). - * Bottom: The 32-bit interpreted value. -* **Auto-Relinking:** The editor dynamically recalculates `CALL`/`JMP` 32-bit relative offsets and 8-bit conditional jump offsets when tokens are inserted or deleted. The editor is the linker. - -## 5. Free-Form Source & Argument Fetching -Lottes diverges from strict zero-operand Forth by introducing "preemptive scatter" arguments directly in the source stream. - -* **Source is the Dictionary:** The 32-bit words are direct absolute memory pointers into the binary. -* **Argument Fetching:** Instead of pushing to a data stack before calling, words can read ahead in the instruction stream. - * `[RSI]` points to the current word. - * `[RSI+4]`, `[RSI+8]` can be fetched directly into registers (like `RCX`, `RDX`) within the word's implementation. -* **Benefits:** This reduces branch granularity and eliminates stack shuffling overhead, making it much faster for heavy code-generation tasks (like JITing GPU shaders). - -## 6. The Self-Modifying OS Cartridge -To handle persistent storage and live updates without complex OS APIs, Lottes leverages Linux's memory mapping and dirty page writeback. - -* **The Execution Loop:** - 1. Launch `cart` (the binary). - 2. The binary copies itself to `cart.bck` and launches `cart.bck`. - 3. `cart.bck` maps the original `cart` file into memory (e.g., at the 6MiB mark) with Read/Write/Execute (RWE) permissions. - 4. It maps an adjustable zero-fill memory space immediately following it. - 5. It jumps into the interpreter. -* **Persistence:** Because the file is mapped into memory, any changes made in the editor modify the file in RAM. Linux's kernel automatically flushes "dirty pages" to the physical disk (e.g., every 30 seconds on SteamOS/SteamDeck). There is no "Save File" code required; data and code reside together and persist implicitly. -``` - ---- - -### `./references/blog_in-depth.md` - -```md -# In-Depth Analysis: Timothy Lottes's Development Blogs (2007 - 2016) - -This document synthesizes the architectural paradigms, implementation details, and philosophical shifts documented in Timothy Lottes's blogs over a decade of building minimal, high-performance Forth-like operating environments. This knowledge is crucial for understanding the "Lottes/Onat Paradigm" and successfully implementing the `bootslop` project. - ---- - -## 1. The Core Philosophy: "Vintage Programming" - -Lottes advocates for returning to a "stone-age" development methodology reminiscent of the Commodore 64 or HP48, but applied to modern x86-64 hardware and GPUs. - -* **Rejection of Modern Complexity:** He explicitly rejects the "NO" of modern operating systems—compilers, linkers, debuggers, memory protection, paging, and bloated ABIs. He aims for an environment that says "YES" to direct hardware access. -* **The OS IS the Editor:** The system boots directly into a visual editor. This editor functions simultaneously as an IDE, assembler, disassembler, hex editor, debugger, and live-coding environment. -* **Instant Iteration:** The primary goal is a sub-5ms edit-compile-run loop. Debugging is done via instant visual feedback and "printf" style memory peeking within the editor itself, rendering traditional debuggers obsolete. -* **Extreme Minimalism:** His compilers and core runtimes often fit within 1.5KB to 4KB (e.g., the 1536-byte bootloader/interpreter project). - -## 2. The Evolution to "Source-Less" Programming - -The most critical architectural shift in Lottes's work is the move from text-based source files (like his 2014 "A" language) to **Source-Less Programming** (2015). - -### Why Source-Less? -Parsing text (lexical analysis, string hashing, AST generation) is slow and complex. In a source-less model, the "source code" *is* the binary executable image (or a direct structured representation of it). - -### The Architecture of Source-Less (x68) -1. **32-Bit Granularity:** Every token in the system is exactly 32 bits (4 bytes). - * To accommodate variable-length x86-64 instructions, Lottes invented "x68". - * **Padding:** Standard x86 instructions are padded to exactly 32 bits (or multiples of 32 bits) using ignored segment override prefixes (like `2E` or `3E`) and multi-byte NOPs. - * Example: A `RET` instruction (`C3`) becomes `C3 90 90 90`. - * *Why?* This keeps immediate values (like 32-bit addresses or constants) 32-bit aligned, drastically simplifying the editor and the assembler. - -2. **The Token Types:** A 32-bit word in memory represents one of four things: - * **DAT (Data):** Hexadecimal data or an immediate value. - * **OP (Opcode):** A padded 32-bit x86-64 machine instruction. - * **ABS (Absolute Address):** A direct 32-bit memory pointer. - * **REL (Relative Address):** An `[RIP + imm32]` relative offset used for branching. - -3. **The Annotation Overlay (The "Shadow" Memory):** - * Because raw 32-bit hex values are unreadable to humans, the editor maintains a *parallel array* of 64-bit annotations for every 32-bit token. - * **Annotation Layout (64-bit):** - * `Tag` (4 to 8 bits): Defines how the editor should display and treat the 32-bit value (e.g., display as a signed int, an opcode name, a relative address, or a specific color). - * `Label / Name`: A short string (e.g., 5 to 8 characters, often compressed using 6-bit or 7-bit encodings to fit) that acts as the human-readable name for the memory address. - * *The Magic:* The editor reads the binary array and the annotation array. It uses the tags to dynamically format the screen. There is **zero string parsing** at runtime. - -4. **Edit-Time Relinking (The Visual Linker):** - * When you insert or delete a token in the editor, all tokens tagged as `ABS` or `REL` (addresses) are automatically recalculated and updated in real-time. The editor *is* the linker. - -5. **Live State vs. Edit State:** - * Memory is split: The live running program, and the edit buffer. - * When edits are made and confirmed (e.g., hitting ESC or Enter), the editor atomically swaps or patches the live image with the edited image. - -## 3. Language Paradigms: "Ear" and "Toe" - -In his "Random Holiday 2015" post, Lottes solidifies the specific DSLs used within this source-less framework: - -* **"Toe" (The Low-Level Assembler):** This is the subset of x86-64 with 32-bit padded opcodes. It is heavily macro-driven to assemble machine code. -* **"Ear" (The High-Level Macro/Forth Language):** A zero-operand, Forth-like language embedded directly into the binary form. - * Instead of a traditional Forth interpreter searching a dictionary at runtime, the dictionary is resolved at *edit-time* or *import-time*. - * A token is just an index or a direct `CALL` instruction to the compiled word. - -### The 2-Item Stack (Implicit Registers) -While early experiments used a traditional Forth data stack in memory, Lottes's later architectures (and Onat's derived work) map the stack directly to hardware registers to eliminate memory overhead: -* `RAX` = Top of Stack (TOS) -* `RBX` (or `RDX` in Onat's VAMP) = Second item on stack (NOS) -* **The xchg Trick:** Stack rotation is often handled by `xchg rax, rbx` (or `rdx`), which compiles to a tiny 2-3 byte instruction, keeping execution entirely within the CPU cache. - -## 4. Bootstrapping "The Chicken Without an Egg" - -How do you build a system that requires a custom binary editor to write code, when you don't have the editor yet? - -1. **C Prototype First:** Lottes explicitly states he builds the first iteration of the visual editor and virtual machine in C (using WinAPI or standard libraries). This allows rapid iteration of the visual layout and the memory arena logic. -2. **Hand-Assembling Bootstraps:** He uses standard assemblers (like NASM) or hexadecimal byte-banging (using tools like `objdump -d`) to figure out the exact padded 32-bit opcode bytes. -3. **Embed Opcode Definitions:** The C prototype includes hardcoded arrays of bytes that represent the base opcodes (e.g., `MOV`, `ADD`, `CALL`, `RET`). -4. **Self-Hosting:** Once the C editor is stable and can generate binary code into an arena, he rewrites the editor *inside* the custom language within the C editor, eventually discarding the C host. - -## 5. UI and Visual Design - -The UI is not an afterthought; it is integral to the architecture. - -* **The Grid:** The editor displays memory as a strict grid. Typical layout: 8 tokens per line (fitting half a 64-byte cache line). -* **Two Rows per Token:** - * Top Row: The Annotation (Label/Name), color-coded. - * Bottom Row: The 32-bit Data (Hex value, or a resolved symbol name if tagged as an address). -* **Colors (ColorForth Inspired):** - * Colors dictate semantic meaning (e.g., Red = Define, Green = Compile, Yellow = Execute/Immediate, White/Grey = Comment/Format). This visual syntax replaces traditional language keywords. -* **Pixel-Perfect Fonts:** Lottes builds custom, fixed-width raster fonts (e.g., 6x11 or 8x8) to ensure perfect readability without anti-aliasing blurring, often treating specific characters (like `_`, `-`, `=`) as line-drawing characters to structure the UI. - -## Summary for the `bootslop` Implementation - -Our current `attempt_1/main.c` is perfectly aligned with Phase 1 of the Lottes bootstrapping process: -1. We have a C-based WinAPI editor. -2. We have a token array (`tape_arena`) and an annotation array (`anno_arena`). -3. We have 32-bit tokens packed with a 4-bit semantic tag and a 28-bit payload. -4. We have a basic JIT emitter targeting a 2-register (`RAX`/`RDX`) virtual machine. - -**Next Immediate Priorities based on Lottes's path:** -* Move away from string-based dictionary lookups at runtime to **Edit-Time Relinking** (resolving addresses when the token is typed or modified in the UI). -* Implement the **Padding Strategy** for the x86-64 JIT emitter to ensure all emitted logical blocks align cleanly, paving the way for 1:1 token-to-machine-code mapping. -* Refine the Editor Grid to show the two-row (Annotation / Data) layout clearly. -``` - ---- - -### `./references/kyra_in-depth.md` - -```md -# In-Depth Analysis: Metaprogramming KYRA in KYRA (Onat Türkçüoğlu) - -This document provides a comprehensive synthesis of the "Metaprogramming KYRA in KYRA" presentation given by Onat Türkçüoğlu at the Silicon Valley Forth Interest Group (SVFIG) on April 26, 2025. It integrates insights from the video transcript and the extensive OCR analysis of his visual editor. - -This presentation is the most explicit, hardcore low-level deep dive into Onat's binary-encoded compiler (KYRA) and serves as the definitive mechanical blueprint for our `bootslop` project. - ---- - -## 1. Performance and "Runtime-Opinionated" Languages - -Onat's primary critique of traditional Forth (and languages like C or Rust) is that they are "runtime opinionated." Standard Forth dictates a memory-based data stack and return stack. This makes it fundamentally incompatible with environments like GPU compute shaders. - -* **Compilation Speed:** KYRA compiles its entire program (including a custom editor, Vulkan renderers, and FFMPEG integrations) in **8.24 milliseconds** natively on Windows/Linux. -* **The 2-Item Hardware Stack:** To achieve hardware locality and GPU compatibility, KYRA strictly restricts the data stack to exactly two CPU registers: **`RAX` (Top of Stack)** and **`RDX` (Next on Stack)**. -* **Zero Stack Overhead:** By having no memory data stack, KYRA eliminates the push/pop overhead that plagues standard Forth implementations. - -## 2. The Mechanics of the KYRA Emitter - -KYRA is not an interpreter; it is a high-level macro assembler that generates direct x86-64 machine code via JIT compilation. - -### The `xchg` Trick (The Magenta Pipe `|`) -* Because the stack is just `RAX` and `RDX`, ensuring `RAX` is the active "Top of Stack" before executing a word is vital. -* The `xchg rax, rdx` instruction compiles to a tiny 2-byte opcode: `48 92`. -* **Definitions:** There are no `begin` or `end` words. A magenta pipe token (`|`) implicitly signals the start of a new definition. The JIT reacts to this by: - 1. Emitting a `RET` (`C3`) to close the *previous* definition. - 2. Emitting `48 92` (`xchg rax, rdx`) to ensure proper stack alignment for the *new* definition. - -### Color Semantics and Code Generation (From Transcript & OCR) -* **Magenta (`|`):** Definition boundary (`RET` + `xchg rax, rdx`). -* **White (Call):** A compile-time call. Emits a direct `CALL` instruction or a `JMP RAX` (e.g., `FFE0`) if optimizing a tail call. -* **Green (Load):** Emits a read from memory: `mov rax, [global_offset]`. -* **Red (Store):** Emits a write to memory: `mov [global_offset], rax`. -* **Yellow (Execute/Immediate):** A highly overloaded color used for runtime execution, immediate invocation of lambdas, or prefix accessors (like struct member reading). -* **Cyan (Literal):** Compiles an immediate value load: `mov rax, imm`. -* **Blue (Comment):** Stored directly in the token payload (3 characters per 24-bit payload) without polluting the global dictionary. - -## 3. Global Memory vs. Local Variables - -Onat heavily critiques the conventional wisdom of avoiding global variables, specifically calling out Rust for forcing developers to pass state through 30 layers of call stacks. - -* **Implicit Register Passing:** For passing transient state (like the active UI element's `slot ID`), he implicitly passes the value in a dedicated register (e.g., `R12D`) across functions, completely bypassing any need to push it to a stack. -* **Single-Register Memory Base:** He dedicates a single CPU register to act as the base pointer for all program memory. This gives instant `[BASE_REG + offset]` access to "gigabytes of state." -* **The "Tape Drive" in Practice:** Instead of a stack, data needed for complex API calls (like Vulkan initialization) is pre-scattered into these known global offsets using Red (Store) words, and then passed via a single pointer. - -## 4. Dictionary Management and The "Deck" - -Unlike text-based Forths that require hashing, KYRA uses a pure binary index map. - -* **24-Bit Indices:** Words are stored as 24-bit indices pointing to 8-byte cells. (Onat notes his next iteration moves to 32-bit indices + a separate 1-byte tag array, exactly matching Lottes's `x68` annotation model). -* **Visual Organization (The "Scrolls"):** The dictionary is explicitly organized by the programmer into 16-word horizontal "scrolls" (e.g., one scroll for "Vulkan API", another for "Math"). -* **IP Protection:** Because the dictionary mapping is separate from the source array, you can ship the binary source indices without the dictionary symbols, effectively stripping the symbols while retaining the executable structure. - -## 5. Control Flow: Basic Blocks `[ ]` and Lambdas `{ }` - -KYRA eliminates standard Abstract Syntax Trees (ASTs) and `if/else/then` branching. - -* **Basic Blocks `[ ]`:** These visually constrain the assembly output. They provide implicit begin, link (else), and end jump targets for the JIT to resolve relative offsets within a limited scope. -* **Lambdas `{ }`:** A lambda (colored Yellow `{`) does not execute inline. The JIT compiles the block of code elsewhere in the arena and leaves its executable memory address in `RAX`. -* **Conditionals:** To perform an `IF`: - 1. Evaluate a condition (e.g., `luma > 0.6`). - 2. Write the boolean result to a dedicated global `condition` variable. - 3. Define a lambda block containing the "true" branch (leaving its address in `RAX`). - 4. Call an execution word that reads the `condition` variable, emits a `cmp condition, 0`, and executes a `jz` (jump if zero) to skip the lambda address stored in `RAX`. - -## 6. FFI: Bridging to C and Vulkan (WinAPI equivalent) - -Dealing with OS APIs and standard C libraries (like Vulkan and FFMPEG) requires satisfying the C Application Binary Interface (ABI). - -* **RSP Alignment:** The hardware stack pointer (`RSP`) is exclusively used for the call stack (return addresses), eliminating buffer overflow vulnerabilities. -* **The FFI Dance:** When calling external C functions, Onat's macros explicitly read `RSP` into a temporary variable, align `RSP` to 16-bytes (a strict requirement for Windows/Linux x64 C ABI), execute the `CALL`, and then restore `RSP`. -* *(Note for Bootslop: We saw `CCALL1`, `CCALL2`, etc., in the OCR, confirming he uses specialized macro words to map the `RAX`/`RDX` stack and global variables into the standard `RCX`, `RDX`, `R8`, `R9` C-ABI registers before triggering the OS call).* - -## 7. Development Workflow - -* **Bug Triage over Asserts:** There are no unit tests or assertions. Bugs are found by commenting out blocks of code (disabling them) and hitting compile. Because compilation takes 8ms, binary searching for the crash point is faster than writing tests. -* **Free Printf / Data Flow:** By hovering over a word in the editor, the system automatically injects code to record `RAX` and `RDX` at that exact execution step, allowing the programmer to step through the data flow visually without running traditional debuggers. - ---- -### Conclusion for `bootslop` - -The "Metaprogramming KYRA" talk confirms that our 2-register stack and "preemptive scatter" global memory model in `attempt_1/main.c` is the exact correct path. - -The next major hurdles for `bootslop` will be: -1. Implementing the `xchg rax, rdx` definition boundary logic. -2. Creating an FFI bridge (like Onat's `CCALL`) that aligns `RSP` to 16 bytes and maps globals to WinAPI registers, allowing our minimal Forth to summon full OS windows and graphics. -3. Transitioning dictionary definitions from string-parsing to direct array index resolution. -``` - ---- - -### `.editorconfig` - -```text -root = true - -[*.s] -indent_style = tab -indent_size = 2 - -[*.asm] -indent_style = tab -indent_size = 2 - -[*.refactor] -indent_style = space -indent_size = 4 - -[*.md] -indent_style = space -indent_size = 4 - -[*.c] -indent_style = tab -indent_size = 2 -charset = utf-8 - -[*.cpp] -indent_style = tab -indent_size = 2 -charset = utf-8 - -[*.h] -indent_style = tab -indent_size = 2 -charset = utf-8 - -[*.hpp] -indent_style = tab -indent_size = 2 -charset = utf-8 - -[*.{ps1, psm1}] -indent_style = tab -indent_size = 4 - -[*.odin] -indent_style = tab -indent_size = 2 -charset = utf-8 - -[*.{natvis, natstepfilter}] -indent_style = tab -indent_size = 4 - -``` - ---- - -### `GEMINI.md` - -```md -# System Prompt - -## Baseline - -DO NOT EVER make a shell script unless told to. DO NOT EVER make a readme or a file describing your changes unless your are told to. If you have commands I should be entering into the command line or if you have something to explain to me, please just use code blocks or normal text output. DO NOT DO ANYTHING OTHER THAN WHAT YOU WERE TOLD TODO. DO NOT EVER, EVER DO ANYTHING OTHER THAN WHAT YOU WERE TOLD TO DO. IF YOU WANT TO DO OTHER THINGS, SIMPLY SUGGEST THEM, AND THEN I WILL REVIEW YOUR CHANGES, AND MAKE THE DECISION ON HOW TO PROCEED. WHEN WRITING SCRIPTS USE A 120-160 character limit per line. I don't want to see scrunched code. -The user will often screenshot various aspects of the development with ShareX, which will be available in the current months directory: 'C:\Users\Ed\scoop\apps\sharex\current\ShareX\Screenshots\2026-02' -You may read fromt his and the user will let you know (by last modified) which of the last screenshots are the most relevant. Otherwise they manually paste relevant content in the './gallery' directory. -Do not use the .gitignore as a reference for WHAT YOU SHOULD IGNORE. THAT IS STRICT FOR THE GIT REPO, NOT FOR INFERENCING FILE RELEVANCE. -If a task is very heavy, use sub-agents (such as a codebase/docs/references investiagor, code editor, specifc pattern or nuance analyzer, etc). - -## Coding Conventions - -Before writing any C code in this workspace, you MUST review the strict stylistic and architectural guidelines defined in [CONVENTIONS.md](./CONVENTIONS.md). These dictate the usage of byte-width types, X-Macros, WinAPI FFI mapping, and memory arenas. - -## Necessary Background for Goal - -Watch or read the following: - -* [Forth Day 2020 - Preview of x64 & ColorForth & SPIR V - Onat](https://youtu.be/ajZAECYdJvE) -* [Metaprogramming VAMP in KYRA, a Next-gen Forth-like language](https://youtu.be/J9U_5tjdegY) -* [Neokineogfx - 4th And Beyond](https://youtu.be/Awkdt30Ruvk) - -There are transcripts for each of these videos in the [references](./references/) directory, along with a comprehensive curation of Lottes's blogs, Onat's tweets, and architectural consolidations. - -## Goal - -Learn ColorForth and be able to build a ColorForth derivative from scratch similar to Timothy Lottes and Onatt. - -**Critical Clarification:** The goal is *not* for the AI to auto-generate a novelty solution or dump a finished codebase. The objective is for me (the user) to *learn* how to build this architecture from scratch. The AI must act as a highly contextualized mentor, providing guided nudges, architectural validation, and specific tactical assistance when requested. We are at the cusp of implementation. The AI should lean on the extensive curation in `./references/` to ensure its advice remains strictly aligned with the Lottes/Onat "sourceless, zero-overhead" paradigm, minimizing generic LLM hallucinations. - -## Architectural Constraints (The "Lottes/Onat" Paradigm) - -Based on the curation in `./references/`, the resulting system MUST adhere to these non-standard rules: - -1. **Sourceless Environment (x68):** No string parsing at runtime. Code exists purely as an array of 32-bit tokens. - - **Token Layout:** 28 bits of payload (compressed name/index/value) + 4 bits for the semantic "Color" Tag. -2. **Visual Editor as the OS:** The editor directly maps to the token array. It does not read text files. It uses the 4-bit tags to colorize the tokens live. -3. **Register-Only Stack:** The traditional Forth data stack in memory is completely eliminated. - - We strictly use a **2-item register stack** (`RAX` and `RDX`). - - Stack rotation is handled via the `xchg rax, rdx` instruction. -4. **Preemptive Scatter ("Tape Drive"):** Function arguments are not pushed to a stack before a call. They are "scattered" into pre-allocated, contiguous global memory slots during compilation/initialization. The function simply reads from these known offsets, eliminating argument gathering overhead. -5. **No `if/then` branches:** Rely on hardware-level flags like conditional returns (`ret-if-signed`) combined with factored calls to avoid writing complex AST parsers. -6. **No Dependencies:** C implementation must be minimal (`-nostdlib`), ideally running directly against OS APIs (e.g., WinAPI `VirtualAlloc`, `ExitProcess`, `GDI32` for rendering). - -## Current Development Roadmap (attempt_1) - -The prototype currently implements a functional WinAPI modal editor, a 2-register (`RAX`/`RDX`) JIT compiler with an `O(1)` visual linker, x68 32-bit instruction padding, implicit definition boundaries (Magenta Pipe), and an initial FFI Bridge (`emit_ffi_dance`). - -Here is a breakdown of the next steps to advance the `attempt_1` implementation towards a complete ColorForth derivative: - -1. ~~**Refine the FFI / Tape Drive Argument Scatter:**~~ (Completed via `PRIM_PRINT` updating to load R8/R9 from `vm_globals`) - * Currently, the FFI bridge only maps `RAX` and `RDX` to the C-ABI `RCX` and `RDX`. - * Implement "Preemptive Scatter" logic so the FFI bridge correctly reads subsequent arguments (e.g., `R8`, `R9`) directly from pre-defined offsets in the `vm_globals` tape drive instead of just zeroing them out. - -2. **Expanded Annotation Layer (Variable-Length Comments):** - * The current `anno_arena` strictly allocates 8 bytes (a `U8`) per token. - * Refactor the visual editor and annotation memory management to allow for arbitrarily long text blocks (comments) to be attached to specific tokens without disrupting the `O(1)` compilation mapping. - -3. ~~**Implement the Self-Modifying Cartridge (Persistence):**~~ (Completed via F1/F2 save/load) - * The tape and annotations are currently lost when the program closes. - * Move away from purely transient `VirtualAlloc` buffers to a memory-mapped file approach (or a manual Save/Load equivalent in WinAPI) to allow the "executable as source" to persist between sessions. - -4. ~~**Refine Visual Editor Interactions:**~~ (Completed via `microui` integration) - * Implement a proper internal text-editing cursor within the `STag_Data` and `STag_Format` (annotation) tokens, rather than relying on backspace-truncation and appendage. - * Migrated to `microui` for immediate mode GUI floating panels, auto-layout token sizing (for a natural text look), and window resizing. - -5. **Continuous Validation & Complex Control Flow:** - * Expand the primitive set to allow for more complex, AST-less control flow (e.g., handling Lambdas or specific Basic Block jumps). - -``` - ---- - -### `CONVENTIONS.md` - -```md -# C DSL Conventions (duffle) - -This document outlines the strict C style and architectural conventions expected in this workspace. It is based on the `duffle.amd64.win32.h` header, the user's `fortish-study` samples, and iterative feedback. - -## 1. Type Conventions (Byte-Width Fundamentals) -* **Never use standard C types** (e.g., `int`, `long`, `unsigned`, `short`, `float`, `double`) directly in application code. -* **Always use the byte-width typedefs:** - * Unsigned: `U1`, `U2`, `U4`, `U8` - * Signed: `S1`, `S2`, `S4`, `S8` - * Float: `F4`, `F8` - * Boolean: `B1`, `B2`, `B4`, `B8` (use `true`/`false` primitives) - * Strings/Chars: `UTF8` (for characters), `Str8` (for string slices) -* **Fundamental Type Casts:** Strictly use the provided casting macros (e.g., `u8_(val)`, `u4_r(ptr)`, `s4_(val)`) instead of standard C-style cast syntax like `(U8)val`. Standard casts should only be used for complex types or when an appropriate macro isn't available. -* **WinAPI Structs:** Only use `MS_` prefixed fundamental types (e.g., `MS_LONG`, `MS_DWORD`) *inside* WinAPI struct definitions (`MS_WNDCLASSA`, etc.) to maintain FFI compatibility. Do not use them in general application logic. - -## 2. Declaration Wrappers & X-Macros -* **Structs and Enums:** Always use the macro wrappers for defining compound types to enforce clean namespacing. - * `typedef Struct_(Name) { ... };` - * `typedef Enum_(UnderlyingType, Name) { ... };` -* **X-Macros:** Use X-Macros to tightly couple Enums with their corresponding string representations or metadata. - ```c - #define My_Tag_Entries() \ - X(Define, "Define") \ - X(Call, "Call") - ``` - -## 3. Function & Symbol Naming -* **Case:** Strictly use `lower_snake_case` for all functions and variables. -* **Types:** Use `PascalCase` for type names (`FArena`, `SWord_Tag`). -* **WinAPI Symbols:** When declaring foreign Win32 symbols, prefix the C function name with `ms_` (using `lower_snake_case`) and use the `asm("SymbolName")` attribute to link it to the actual DLL export. - * *Correct:* `WinAPI U2 ms_register_class_a(MS_WNDCLASSA const* lpWndClass) asm("RegisterClassA");` - * *Incorrect:* `WinAPI U2 RegisterClassA(...);` - -## 4. Formatting & Layout -* **Vertical Alignment:** Align related variable declarations, struct fields, and function prototypes into columns to create a "sheet-like" layout. This improves visual parsing. - * Example Struct: - ```c - typedef struct MS_WNDCLASSA { - U4 style; - S8 (*lpfnWndProc)(void*, U4, U8, S8); - S4 cbClsExtra; - // ... - char const* lpszClassName; - } MS_WNDCLASSA; - ``` -* **Multi-line Argument Alignment:** For long function signatures, place one argument per line with a single 4-space indent. -* **WinAPI Grouping:** Group foreign procedure declarations by their originating OS library (e.g., Kernel32, User32, GDI32) using comment headers. -* **Brace Style:** Use Allman style (braces on a new line) for function bodies or control blocks (`if`, `for`, `switch`, etc.) that are large or complex. Smaller blocks may use K&R style. -* **Conditionals & Control Flow:** Always place `else if` and `else` statements on a new line. Align control flow parentheses (e.g., between consecutive `while` and `if` blocks) vertically when possible for aesthetic uniformity: - ```c - while (len < 8) len ++; - if (len > 0) { ... } - ``` -* **Address-Of Operator:** Do insert a space between the address-of operator (`&`) and the variable name. - * **Correct:** `& my_var` - * **Incorrect:** `&my_var` - -## 5. Memory Management -* **Standard Library:** The C standard library is linked, but headers like `` or `` should not be included directly. Required functions should be declared manually if needed, or accessed via compiler builtins. -* **Arenas over Malloc:** Use `FArena` and its associated macros (`farena_push`, `farena_push_type`, `farena_reset`) for all dynamic memory allocations. Do not use raw pointers with manual arithmetic when an arena can handle it. -* **Memory Ops:** Use `mem_fill` and `mem_copy` instead of standard `memset`/`memcpy` within the application logic. - -## 6. Type Qualifiers -* **`const` Placement:** The `const` keyword must always be placed to the right of the type it modifies. This maintains a consistent right-to-left reading of type declarations. - * **Correct:** `char const* my_ptr;` (Pointer to a constant character) - * **Correct:** `U4 const* const my_ptr;` (Constant pointer to a constant U4) - * **Incorrect:** `const char* my_ptr;` - -## 7. Metadata Coupling (X-Macros) -* **Metadata Enums:** Use X-Macros to define Enums that are tightly coupled with static metadata (colors, prefixes, names). - * Example: - ```c - #define Tag_Entries() \ - X(Define, "Define", 0x0018AEFF, ":") \ - X(Call, "Call", 0x00D6A454, "~") - ``` -* **Naming Conventions:** When using X-Macros for Tags, entry names should be PascalCase, and the Enum symbols should be prefixed with the Enum type name (e.g., `tmpl(STag, Define)` -> `STag_Define`). - - -``` \ No newline at end of file diff --git a/config.toml b/config.toml index b5b5075..9d30c4f 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,3 @@ -# config.toml [output] namespace = "colorforth_bootslop" output_dir = "." @@ -6,24 +5,25 @@ output_dir = "." [files] base_dir = "C:/projects/forth/bootslop" paths = [ - "./attempt_1/*", - "./scripts/*", - "./references/Architectural_Consolidation.md", - "./references/neokineogfx_in-depth.md", - "./references/blog_in-depth.md", - "./references/kyra_in-depth.md", - ".editorconfig", - "GEMINI.md", - "CONVENTIONS.md" + "./attempt_1/*", + "./scripts/*", + "./references/Architectural_Consolidation.md", + "./references/neokineogfx_in-depth.md", + "./references/blog_in-depth.md", + "./references/kyra_in-depth.md", + ".editorconfig", + "GEMINI.md", + "CONVENTIONS.md", ] [screenshots] base_dir = "C:/Users/Ed/scoop/apps/sharex/current/ShareX/Screenshots/2026-02" -paths = [ -] - +paths = [] [discussion] -history = [ -] +history = [] + +[ai] +provider = "gemini" +model = "gemini-2.0-flash" diff --git a/gui.py b/gui.py index c9c6c16..856e863 100644 --- a/gui.py +++ b/gui.py @@ -6,9 +6,10 @@ import threading from pathlib import Path from tkinter import filedialog, Tk import aggregate -import gemini +import ai_client CONFIG_PATH = Path("config.toml") +PROVIDERS = ["gemini", "anthropic"] def load_config() -> dict: with open(CONFIG_PATH, "rb") as f: @@ -31,14 +32,19 @@ class App: self.screenshots: list[str] = list(self.config.get("screenshots", {}).get("paths", [])) self.history: list[str] = list(self.config.get("discussion", {}).get("history", [])) + ai_cfg = self.config.get("ai", {}) + self.current_provider: str = ai_cfg.get("provider", "gemini") + self.current_model: str = ai_cfg.get("model", "gemini-2.0-flash") + self.available_models: list[str] = [] + self.gemini_status = "idle" self.gemini_response = "" self.last_md = "" self.last_md_path: Path | None = None self.send_thread: threading.Thread | None = None + self.models_thread: threading.Thread | None = None - self.file_rows: list[str] = [] - self.shot_rows: list[str] = [] + ai_client.set_provider(self.current_provider, self.current_model) # ------------------------------------------------------------------ helpers @@ -54,6 +60,10 @@ class App: raw = dpg.get_value("discussion_box") self.history = [s.strip() for s in raw.split("---") if s.strip()] self.config["discussion"] = {"history": self.history} + self.config["ai"] = { + "provider": self.current_provider, + "model": self.current_model + } def _do_generate(self) -> tuple[str, Path]: self._flush_to_config() @@ -62,48 +72,76 @@ class App: def _update_status(self, status: str): self.gemini_status = status - dpg.set_value("gemini_status", f"Status: {status}") + if dpg.does_item_exist("ai_status"): + dpg.set_value("ai_status", f"Status: {status}") def _update_response(self, text: str): self.gemini_response = text - dpg.set_value("gemini_response", text) + if dpg.does_item_exist("gemini_response"): + dpg.set_value("gemini_response", text) - def _rebuild_files_table(self): - dpg.delete_item("files_table", children_only=True) - for i, f in enumerate(self.files): - with dpg.table_row(parent="files_table"): - dpg.add_text(f) - dpg.add_button( - label="x", - callback=self._make_remove_file_cb(i), - width=24 - ) + def _rebuild_files_list(self): + if dpg.does_item_exist("files_scroll"): + dpg.delete_item("files_scroll", children_only=True) + for i, f in enumerate(self.files): + with dpg.group(horizontal=True, parent="files_scroll"): + dpg.add_button( + label="x", + width=24, + callback=self._make_remove_file_cb(i) + ) + dpg.add_text(f) - def _rebuild_shots_table(self): - dpg.delete_item("shots_table", children_only=True) - for i, s in enumerate(self.screenshots): - with dpg.table_row(parent="shots_table"): - dpg.add_text(s) - dpg.add_button( - label="x", - callback=self._make_remove_shot_cb(i), - width=24 - ) + def _rebuild_shots_list(self): + if dpg.does_item_exist("shots_scroll"): + dpg.delete_item("shots_scroll", children_only=True) + for i, s in enumerate(self.screenshots): + with dpg.group(horizontal=True, parent="shots_scroll"): + dpg.add_button( + label="x", + width=24, + callback=self._make_remove_shot_cb(i) + ) + dpg.add_text(s) + + def _rebuild_models_list(self): + if not dpg.does_item_exist("model_listbox"): + return + dpg.configure_item("model_listbox", items=self.available_models) + if self.current_model in self.available_models: + dpg.set_value("model_listbox", self.current_model) + elif self.available_models: + dpg.set_value("model_listbox", self.available_models[0]) + self.current_model = self.available_models[0] + ai_client.set_provider(self.current_provider, self.current_model) def _make_remove_file_cb(self, idx: int): def cb(): if idx < len(self.files): self.files.pop(idx) - self._rebuild_files_table() + self._rebuild_files_list() return cb def _make_remove_shot_cb(self, idx: int): def cb(): if idx < len(self.screenshots): self.screenshots.pop(idx) - self._rebuild_shots_table() + self._rebuild_shots_list() return cb + def _fetch_models(self, provider: str): + self._update_status("fetching models...") + def do_fetch(): + try: + models = ai_client.list_models(provider) + self.available_models = models + self._rebuild_models_list() + self._update_status(f"models loaded: {len(models)}") + except Exception as e: + self._update_status(f"model fetch error: {e}") + self.models_thread = threading.Thread(target=do_fetch, daemon=True) + self.models_thread.start() + # ---------------------------------------------------------------- callbacks def cb_browse_output(self): @@ -132,7 +170,7 @@ class App: for p in paths: if p not in self.files: self.files.append(p) - self._rebuild_files_table() + self._rebuild_files_list() def cb_add_wildcard(self): root = hide_tk_root() @@ -140,7 +178,7 @@ class App: root.destroy() if d: self.files.append(str(Path(d) / "**" / "*")) - self._rebuild_files_table() + self._rebuild_files_list() def cb_browse_shots_base(self): root = hide_tk_root() @@ -159,7 +197,7 @@ class App: for p in paths: if p not in self.screenshots: self.screenshots.append(p) - self._rebuild_shots_table() + self._rebuild_shots_list() def cb_add_excerpt(self): current = dpg.get_value("discussion_box") @@ -183,7 +221,7 @@ class App: self._update_status(f"error: {e}") def cb_reset_session(self): - gemini.reset_session() + ai_client.reset_session() self._update_status("session reset") self._update_response("") @@ -199,11 +237,11 @@ class App: return self._update_status("sending...") - user_msg = dpg.get_value("gemini_input") + user_msg = dpg.get_value("ai_input") def do_send(): try: - response = gemini.send(self.last_md, user_msg) + response = ai_client.send(self.last_md, user_msg) self._update_response(response) self._update_status("done") except Exception as e: @@ -213,117 +251,116 @@ class App: self.send_thread = threading.Thread(target=do_send, daemon=True) self.send_thread.start() + def cb_provider_changed(self, sender, app_data): + self.current_provider = app_data + ai_client.reset_session() + ai_client.set_provider(self.current_provider, self.current_model) + self.available_models = [] + self._rebuild_models_list() + self._fetch_models(self.current_provider) + + def cb_model_changed(self, sender, app_data): + if app_data: + self.current_model = app_data + ai_client.reset_session() + ai_client.set_provider(self.current_provider, self.current_model) + self._update_status(f"model set: {self.current_model}") + + def cb_fetch_models(self): + self._fetch_models(self.current_provider) + # ---------------------------------------------------------------- build ui def _build_ui(self): + with dpg.window( label="Config", tag="win_config", pos=(8, 8), - width=340, - height=220, + width=400, + height=200, no_close=True ): + dpg.add_text("Namespace") dpg.add_input_text( - label="Namespace", tag="namespace", default_value=self.config["output"]["namespace"], width=-1 ) + dpg.add_text("Output Dir") dpg.add_input_text( - label="Output Dir", tag="output_dir", default_value=self.config["output"]["output_dir"], width=-1 ) - dpg.add_button(label="Browse Output Dir", callback=self.cb_browse_output, width=-1) - dpg.add_separator() - dpg.add_button(label="Save Config", callback=self.cb_save_config, width=-1) + with dpg.group(horizontal=True): + dpg.add_button(label="Browse Output Dir", callback=self.cb_browse_output) + dpg.add_button(label="Save Config", callback=self.cb_save_config) with dpg.window( label="Files", tag="win_files", - pos=(8, 236), - width=340, - height=460, + pos=(8, 216), + width=400, + height=500, no_close=True ): - dpg.add_input_text( - label="Base Dir", - tag="files_base_dir", - default_value=self.config["files"]["base_dir"], - width=-1 - ) - dpg.add_button(label="Browse Base Dir##files", callback=self.cb_browse_files_base, width=-1) + dpg.add_text("Base Dir") + with dpg.group(horizontal=True): + dpg.add_input_text( + tag="files_base_dir", + default_value=self.config["files"]["base_dir"], + width=-220 + ) + dpg.add_button(label="Browse##filesbase", callback=self.cb_browse_files_base) dpg.add_separator() - - with dpg.table( - tag="files_table", - header_row=False, - resizable=True, - borders_innerV=True, - scrollY=True, - height=280 - ): - dpg.add_table_column(label="Path", width_stretch=True) - dpg.add_table_column(label="", width_fixed=True, init_width_or_weight=28) - - self._rebuild_files_table() - + dpg.add_text("Paths") + with dpg.child_window(tag="files_scroll", height=-64, border=True): + pass + self._rebuild_files_list() dpg.add_separator() with dpg.group(horizontal=True): - dpg.add_button(label="Add File(s)", callback=self.cb_add_files, width=-1) - with dpg.group(horizontal=True): - dpg.add_button(label="Add Wildcard", callback=self.cb_add_wildcard, width=-1) + dpg.add_button(label="Add File(s)", callback=self.cb_add_files) + dpg.add_button(label="Add Wildcard", callback=self.cb_add_wildcard) with dpg.window( label="Screenshots", tag="win_screenshots", - pos=(356, 8), - width=340, - height=460, + pos=(416, 8), + width=400, + height=500, no_close=True ): - dpg.add_input_text( - label="Base Dir", - tag="shots_base_dir", - default_value=self.config.get("screenshots", {}).get("base_dir", "."), - width=-1 - ) - dpg.add_button(label="Browse Base Dir##shots", callback=self.cb_browse_shots_base, width=-1) + dpg.add_text("Base Dir") + with dpg.group(horizontal=True): + dpg.add_input_text( + tag="shots_base_dir", + default_value=self.config.get("screenshots", {}).get("base_dir", "."), + width=-220 + ) + dpg.add_button(label="Browse##shotsbase", callback=self.cb_browse_shots_base) dpg.add_separator() - - with dpg.table( - tag="shots_table", - header_row=False, - resizable=True, - borders_innerV=True, - scrollY=True, - height=280 - ): - dpg.add_table_column(label="Path", width_stretch=True) - dpg.add_table_column(label="", width_fixed=True, init_width_or_weight=28) - - self._rebuild_shots_table() - + dpg.add_text("Paths") + with dpg.child_window(tag="shots_scroll", height=-48, border=True): + pass + self._rebuild_shots_list() dpg.add_separator() - dpg.add_button(label="Add Screenshot(s)", callback=self.cb_add_shots, width=-1) + dpg.add_button(label="Add Screenshot(s)", callback=self.cb_add_shots) with dpg.window( label="Discussion History", tag="win_discussion", - pos=(704, 8), - width=340, - height=460, + pos=(824, 8), + width=400, + height=500, no_close=True ): dpg.add_input_text( - label="##discussion_box", tag="discussion_box", default_value="\n---\n".join(self.history), multiline=True, width=-1, - height=340 + height=-64 ) dpg.add_separator() with dpg.group(horizontal=True): @@ -332,31 +369,66 @@ class App: dpg.add_button(label="Save", callback=self.cb_save_discussion) with dpg.window( - label="Gemini", - tag="win_gemini", - pos=(1052, 8), - width=340, - height=700, + label="AI Client", + tag="win_ai", + pos=(1232, 8), + width=420, + height=900, no_close=True ): - dpg.add_text("Status: idle", tag="gemini_status") + # provider + dpg.add_text("Provider") + dpg.add_combo( + tag="provider_combo", + items=PROVIDERS, + default_value=self.current_provider, + width=-1, + callback=self.cb_provider_changed + ) + dpg.add_separator() + + # model + with dpg.group(horizontal=True): + dpg.add_text("Model") + dpg.add_button( + label="Fetch Models", + callback=self.cb_fetch_models + ) + dpg.add_listbox( + tag="model_listbox", + items=self.available_models, + default_value=self.current_model, + width=-1, + num_items=6, + callback=self.cb_model_changed + ) + + dpg.add_separator() + + dpg.add_text("Status: idle", tag="ai_status") + + dpg.add_separator() + + dpg.add_text("Message") dpg.add_input_text( - label="##gemini_input", - tag="gemini_input", + tag="ai_input", multiline=True, width=-1, - height=120 + height=140 ) + dpg.add_separator() + with dpg.group(horizontal=True): dpg.add_button(label="Gen + Send", callback=self.cb_generate_send) dpg.add_button(label="MD Only", callback=self.cb_md_only) dpg.add_button(label="Reset", callback=self.cb_reset_session) + dpg.add_separator() - dpg.add_text("Response:") + + dpg.add_text("Response") dpg.add_input_text( - label="##gemini_response", tag="gemini_response", multiline=True, readonly=True, @@ -364,31 +436,29 @@ class App: height=-1 ) - def run(self): - dpg.create_context() - dpg.configure_app(docking=True, docking_space=True) - - dpg.create_viewport( - title="manual slop", - width=1600, - height=900 - ) - - dpg.setup_dearpygui() - dpg.show_viewport() - dpg.maximize_viewport() - - self._build_ui() - - while dpg.is_dearpygui_running(): - dpg.render_dearpygui_frame() - - dpg.destroy_context() +def run(config: dict) -> tuple[str, Path]: + namespace = config["output"]["namespace"] + output_dir = Path(config["output"]["output_dir"]) / "md_gen" + base_dir = Path(config["files"]["base_dir"]) + files = config["files"].get("paths", []) + screenshot_base_dir = Path(config.get("screenshots", {}).get("base_dir", ".")) + screenshots = config.get("screenshots", {}).get("paths", []) + history = config.get("discussion", {}).get("history", []) + output_dir.mkdir(parents=True, exist_ok=True) + increment = find_next_increment(output_dir, namespace) + output_file = output_dir / f"{namespace}_{increment:03d}.md" + markdown = build_markdown(base_dir, files, screenshot_base_dir, screenshots, history) + output_file.write_text(markdown, encoding="utf-8") + return markdown, output_file def main(): - app = App() - app.run() + with open("config.toml", "rb") as f: + import tomllib + config = tomllib.load(f) + markdown, output_file = run(config) + print(f"Written: {output_file}") + if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml index cff310c..1b14241 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,4 @@ +# pyproject.toml [project] name = "manual_slop" version = "0.1.0" @@ -5,6 +6,6 @@ requires-python = ">=3.11" dependencies = [ "dearpygui", "google-genai", + "anthropic", "tomli-w" ] -