From 5bf3b12f62ccfd85e342b4fe233a7d446688c141 Mon Sep 17 00:00:00 2001 From: zongor Date: Thu, 7 May 2026 22:25:55 -0700 Subject: [PATCH] Add examples for fixing locals to be dynamically allocated from the end of memory. This should allow for recursion and better handling in general. --- docs/LANG_VM.org | 137 ++++++++++++++++++++++++++++++++++++++ test/add-hardcoded.tal | 86 ++++++++++++++++++++++++ test/add.ul | 4 +- test/bang-hardcoded.tal | 82 +++++++++++++++++++++++ test/fib-hardcoded.tal | 89 +++++++++++++++++++++++++ test/fib.ul | 4 +- test/global-hardcoded.tal | 14 ++++ test/hardcoded-while.tal | 13 ---- test/hello-hardcoded.tal | 20 ++++++ test/if-hardcoded.tal | 27 ++++++++ test/str-hardcoded.tal | 85 +++++++++++++++++++++++ test/while-hardcoded.tal | 17 +++++ 12 files changed, 562 insertions(+), 16 deletions(-) create mode 100644 docs/LANG_VM.org create mode 100755 test/add-hardcoded.tal create mode 100644 test/bang-hardcoded.tal create mode 100644 test/fib-hardcoded.tal create mode 100644 test/global-hardcoded.tal delete mode 100644 test/hardcoded-while.tal create mode 100644 test/hello-hardcoded.tal create mode 100644 test/if-hardcoded.tal create mode 100644 test/str-hardcoded.tal create mode 100644 test/while-hardcoded.tal diff --git a/docs/LANG_VM.org b/docs/LANG_VM.org new file mode 100644 index 0000000..2ca99c2 --- /dev/null +++ b/docs/LANG_VM.org @@ -0,0 +1,137 @@ +* Languages / VM Notes about implmementations +** Instruction Order +*** Prefix +- Lisp like feel +**** Pros +- Easily parses into AST +- Easy to generate macros +**** Cons +- Unintuitive to beginners +- Hard to code read for begineers +*** Postfix +- Forth like feel +**** Pros +- Trivial to parse +- Generally small codebase +**** Cons +- Unintuitive to beginners +- Hard to code read for begineers +*** Infix +- Pretty much all others +**** Pros +- Intuitive to beginners (math like) +**** Cons +- More complex to parse +** Virtual Machine abstraction +*** Accululator +A single slot where all intermediate calculations get stored to +**** Pros +- Very simple +- Easy to implement +- Trivial to abstract +**** Cons +- Very slow due to instruction bottlenecks +*** Stack +A Last in First out, usually 2 stacks 1 operation and 1 return stack +**** Pros +- Simple to implement +- Parser trivial to build +**** Cons +- generally a stacic size, so recursion is usually unsafe. +*** Register +A set of very fast accumulators +**** Pros +- Very fast +- Closest to modern hardware +- Easy to JIT +**** Cons +- Extremly complicated +- Very tied to 'modern' hardware so hard to abstract +*** Memory to Memory +Operates on pointers to memory, everything exists in memory. +**** Pros +- Most flexable for dynamic allocation +- Easy to understand conceptually +**** Cons +- Likely slower than stack and register +** Memory abstractions +*** Manual +Manual memory management is where the language gives the developer tools to +allocate, deallocate, and move memory but does not manage logic. +**** Pros +- Simpler compiler +- Fastest +**** Cons +- Cognitive load on developer +- Higher chance of bugs due to mistakes +*** Garbage Collected +GC usually uses a 'mark and sweep' method where the programs 'vm' searches for unused blocks of memory and reclaims them. +**** Pros +- Developer does not have to worry about memory +**** Cons +- Non-deterministic pauses +*** RAII +Resource acquisition is initialization +Heap values are tied to the scope they are created in, complex rules allow for moving, coping, and borrowing memory allocations. +**** Pros +- If rules are followed creates much safer code than manual memory +- Deterministic +**** Cons +- Extremly complex memory rules that need to be followed by developer. +- Slow compilation due to static analysis of memory rules. +*** Arena +An arena is a block of memory where a series of allocations get grouped together. +It is a logical extension of manual memory managment where you allocate to an arena. +When an arena is freed all allocations are freed as a single block. +**** Pros +- Deterministic +**** Cons +- Developer still needs to think about memory +*** Stack based +All values are either pre-allocated at compile time or they grow with the stack being free'd when the scope ends for that scope. +This is how 'mission critical' software works like at NASA. +**** Pros +- Deterministic allocations +- Memory is freed when a scope ends +**** Cons +- Cannot allocate dynamic memory +- Very strict rules +*** Frame based +All memory used by the program exists in one big bump allocator. +When a function gets called, it creates a new arena in memory starting at the end of the previous frame. (or at the beginning of memory if no function has been called) +Variables allocated in the frame's arena are owned by default by the frame they are allocated in. +When variables get modified within the current frame they will get updated in that frame in their current location in the arena. +If the size of the variable changes (like adding a value to the end of a array), a link is added to the internal list that points to a new block is allocated +at the end of the arena of the size of that value, so they are variable sized blocks +When a variable is passed to a child function primitive types are pass by value, complex types (Arrays, Plexes, Strings) are pass by reference. +Variables passed as arguments to the child follow the same rules except when the size of a complex type changes, then a new value is added as a link +from the parent to the child and the new value is allocated at the end of the childs arena (but the owner is still the parent). +When a function "returns" the frame is deallocated and all values allocated by it is freed; thus freeing memory deterministically. +Only a single variable may be returned from a function. When a variable is returned if it is primitive it is just passed back to the parent. +If it is complex the value is coalesced into a single contiguous values and the link in the parent is updated to point at the "new" end of the arena. +If the parents block is at the end the entire block is coalesced into a single contiguous block. +**** Pros +- Deterministic +- "simple" from the computers POV +- Allows for dynamic memory allocation +**** Cons +- Not flexable in memory method +- Likely slower than RAII +** Computer abstraction +*** Von Neumann +Code and data exist in the same block +Example is the Uxn/Varvara VM +**** Pros +- The most "free" compiler +- Very simple to emit commands +**** Cons +- Need to "jump" over data to avoid it being run as commands +- Functions need to be declared after the "main" +*** Harvard +Seperate code and data +Most other VMs use this +**** Pros +- Safer because code is not operatable +**** Cons +- More complicated compiler for one pass +- Not as flexable diff --git a/test/add-hardcoded.tal b/test/add-hardcoded.tal new file mode 100755 index 0000000..eeb333a --- /dev/null +++ b/test/add-hardcoded.tal @@ -0,0 +1,86 @@ +|100 + LIT2r 0000 main POP2r BRK + +@main ( -- ) + #0001 #0001 add_ nat_to_str_ str/ + + &return + POP2r JMP2r + +@add_ ( a b -- res ) + OVR2r LIT2r 0004 SUB2r + STH2kr STA2 + STH2kr #0002 ADD2 STA2 + + STH2kr LDA2 STH2kr #0002 ADD2 LDA2 ADD2 + + &return + POP2r JMP2r + +@sext + #80 ANDk EQU #ff MUL SWP JMP2r + +@aalloc_ ( size* -- result* ) + OVR2r LIT2r 0004 SUB2r STH2kr INC2 INC2 STA2 + ;mem_length_ LDA2 STH2kr STA2 + ;mem_length_ LDA2k STH2kr INC2 INC2 LDA2 ADD2 SWP2 STA2 + ;mem_ STH2kr LDA2 ADD2 !&return + #0000 + + &return + POP2r JMP2r + +@amcpy_ ( length* from* -- result* ) + OVR2r LIT2r 0008 SUB2r STH2kr #0006 ADD2 STA2 + STH2kr #0004 ADD2 STA2 + STH2kr #0004 ADD2 LDA2 aalloc_ + STH2kr INC2 INC2 STA2 + #0000 STH2kr STA2 + + &begin.1 + STH2kr LDA2 STH2kr #0004 ADD2 LDA2 LTH2 #00 EQU ?&break.1 + STH2kr #0006 ADD2 LDA2 STH2kr LDA2 ADD2 LDA sext + STH2kr INC2 INC2 LDA2 STH2kr LDA2 ADD2 STA + POP + + &continue.1 + STH2kr LDA2k INC2k ROT2 STA2 + POP2 !&begin.1 + + &break.1 + STH2kr INC2 INC2 LDA2 !&return + #0000 + + &return + POP2r JMP2r + +@nat_to_str_ ( n* -- result* ) + OVR2r LIT2r 000a SUB2r STH2kr #0008 ADD2 STA2 + #0005 STH2kr STA2 + + &begin.1 + STH2kr #0008 ADD2 LDA2 #000a OVR2 OVR2 DIV2 MUL2 SUB2 #0030 ADD2 STH2kr INC2 INC2 STH2kr LDA2k #0001 SUB2 SWP2 STA2k + POP2 ADD2 STA + POP STH2kr #0008 ADD2 LDA2k #000a DIV2 SWP2 STA2 + + &continue.1 + #0000 STH2kr #0008 ADD2 LDA2 LTH2 ?&begin.1 + + &break.1 + #0005 STH2kr LDA2 SUB2 STH2kr INC2 INC2 STH2kr LDA2 ADD2 amcpy_ + !&return + #0000 + + &return + POP2r JMP2r + +@str/ ( str* -- ) + LDAk DUP ?{ POP POP2 JMP2r } + #18 DEO + INC2 !/ + +@str/ ( str* -- ) + str/ #0a #18 DEO + +@mem_length_ #0000 +@mem_ diff --git a/test/add.ul b/test/add.ul index 228444f..5298c74 100755 --- a/test/add.ul +++ b/test/add.ul @@ -5,4 +5,6 @@ function add(int a, int b) int { return a + b; } -print(add(1, 1) as str); +function main() { + print(add(1, 1) as str); +} diff --git a/test/bang-hardcoded.tal b/test/bang-hardcoded.tal new file mode 100644 index 0000000..536fcdc --- /dev/null +++ b/test/bang-hardcoded.tal @@ -0,0 +1,82 @@ +|100 + +LIT2r 0000 main POP2r BRK + +@const_str_0 "flag 20 "is 20 "false 0a + +@main ( -- ) + OVR2r LIT2r 0001 SUB2r + #0000 STH2kr STA2 + STH2kr LDA2 #0000 EQU2 #03 JCN ?{ ;const_str_0 str/ + !&if_end.0 } &if_end.0 BRK + + &return + afree_ POP2r JMP2r + +@sext + #80 ANDk EQU #ff MUL SWP JMP2r + +%afree_ ( -- ) { + #0000 ;mem_length_ STA2 } + +@aalloc_ ( size* -- result* ) + OVR2r LIT2r 0004 SUB2r STH2kr INC2 INC2 STA2 + ;mem_length_ LDA2 STH2kr STA2 + ;mem_length_ LDA2k STH2kr INC2 INC2 LDA2 ADD2 SWP2 STA2 + ;mem_ STH2kr LDA2 ADD2 !&return + #0000 + + &return + POP2r JMP2r + +@amcpy_ ( length* from* -- result* ) + OVR2r LIT2r 0008 SUB2r STH2kr #0006 ADD2 STA2 + STH2kr #0004 ADD2 STA2 + STH2kr #0004 ADD2 LDA2 aalloc_ + STH2kr INC2 INC2 STA2 + #0000 STH2kr STA2 + + &begin.1 + STH2kr LDA2 STH2kr #0004 ADD2 LDA2 LTH2 #00 EQU ?&break.1 + STH2kr #0006 ADD2 LDA2 STH2kr LDA2 ADD2 LDA sext + STH2kr INC2 INC2 LDA2 STH2kr LDA2 ADD2 STA + POP + + &continue.1 + STH2kr LDA2k INC2k ROT2 STA2 + POP2 !&begin.1 + + &break.1 + STH2kr INC2 INC2 LDA2 !&return + #0000 + + &return + POP2r JMP2r + +@nat_to_str_ ( n* -- result* ) + OVR2r LIT2r 000a SUB2r STH2kr #0008 ADD2 STA2 + #0005 STH2kr STA2 + + &begin.1 + STH2kr #0008 ADD2 LDA2 #000a OVR2 OVR2 DIV2 MUL2 SUB2 #0030 ADD2 STH2kr INC2 INC2 STH2kr LDA2k #0001 SUB2 SWP2 STA2k + POP2 ADD2 STA + POP STH2kr #0008 ADD2 LDA2k #000a DIV2 SWP2 STA2 + + &continue.1 + #0000 STH2kr #0008 ADD2 LDA2 LTH2 ?&begin.1 + + &break.1 + #0005 STH2kr LDA2 SUB2 STH2kr INC2 INC2 STH2kr LDA2 ADD2 amcpy_ + !&return + #0000 + + &return + POP2r JMP2r + +@str/ ( str* -- ) + LDAk DUP ?{ POP POP2 JMP2r } + #18 DEO + INC2 !/ + +@mem_length_ #0000 +@mem_ diff --git a/test/fib-hardcoded.tal b/test/fib-hardcoded.tal new file mode 100644 index 0000000..909eb73 --- /dev/null +++ b/test/fib-hardcoded.tal @@ -0,0 +1,89 @@ +|100 + LIT2r 0000 main POP2r BRK + +@main ( -- ) + #0017 fib nat_to_str_ str/ + + &return + POP2r JMP2r + +@fib ( n -- n ) + OVR2r LIT2r 0002 SUB2r STH2kr STA2 + + STH2kr LDA2 #0002 LTH2 #03 JCN !{ STH2kr LDA2 !&return !&if_end.0 } + &if_end.0 + + STH2kr LDA2 #0002 SUB2 fib + STH2kr LDA2 #0001 SUB2 fib + ADD2 + + &return + POP2r JMP2r + +@sext + #80 ANDk EQU #ff MUL SWP JMP2r + +@aalloc_ ( size* -- result* ) + OVR2r LIT2r 0004 SUB2r STH2kr INC2 INC2 STA2 + ;mem_length_ LDA2 STH2kr STA2 + ;mem_length_ LDA2k STH2kr INC2 INC2 LDA2 ADD2 SWP2 STA2 + ;mem_ STH2kr LDA2 ADD2 !&return + #0000 + + &return + POP2r JMP2r + +@amcpy_ ( length* from* -- result* ) + OVR2r LIT2r 0008 SUB2r STH2kr #0006 ADD2 STA2 + STH2kr #0004 ADD2 STA2 + STH2kr #0004 ADD2 LDA2 aalloc_ + STH2kr INC2 INC2 STA2 + #0000 STH2kr STA2 + + &begin.1 + STH2kr LDA2 STH2kr #0004 ADD2 LDA2 LTH2 #00 EQU ?&break.1 + STH2kr #0006 ADD2 LDA2 STH2kr LDA2 ADD2 LDA sext + STH2kr INC2 INC2 LDA2 STH2kr LDA2 ADD2 STA + POP + + &continue.1 + STH2kr LDA2k INC2k ROT2 STA2 + POP2 !&begin.1 + + &break.1 + STH2kr INC2 INC2 LDA2 !&return + #0000 + + &return + POP2r JMP2r + +@nat_to_str_ ( n* -- result* ) + OVR2r LIT2r 000a SUB2r STH2kr #0008 ADD2 STA2 + #0005 STH2kr STA2 + + &begin.1 + STH2kr #0008 ADD2 LDA2 #000a OVR2 OVR2 DIV2 MUL2 SUB2 #0030 ADD2 STH2kr INC2 INC2 STH2kr LDA2k #0001 SUB2 SWP2 STA2k + POP2 ADD2 STA + POP STH2kr #0008 ADD2 LDA2k #000a DIV2 SWP2 STA2 + + &continue.1 + #0000 STH2kr #0008 ADD2 LDA2 LTH2 ?&begin.1 + + &break.1 + #0005 STH2kr LDA2 SUB2 STH2kr INC2 INC2 STH2kr LDA2 ADD2 amcpy_ + !&return + #0000 + + &return + POP2r JMP2r + +@str/ ( str* -- ) + LDAk DUP ?{ POP POP2 JMP2r } + #18 DEO + INC2 !/ + +@str/ ( str* -- ) + str/ #0a #18 DEO + +@mem_length_ #0000 +@mem_ diff --git a/test/fib.ul b/test/fib.ul index a6cb166..d05e733 100755 --- a/test/fib.ul +++ b/test/fib.ul @@ -1,9 +1,9 @@ /** * Recursively calculate fibonacci */ -function fib(int n) int { +function fib(nat n) nat { if (n < 2) { return n; } return fib(n - 2) + fib(n - 1); } -print(fib(35) as str); \ No newline at end of file +print(fib(23) as str); \ No newline at end of file diff --git a/test/global-hardcoded.tal b/test/global-hardcoded.tal new file mode 100644 index 0000000..eb7b6f3 --- /dev/null +++ b/test/global-hardcoded.tal @@ -0,0 +1,14 @@ +|0100 + LIT2r 0000 main POP2r BRK + +%putchar ( -- ) { + #18 DEO } + +@main ( -- ) + OVR2r LIT2r 0004 SUB2r + #007a STH2kr ( #0000 ADD2 ) STA2 + #0020 STH2kr #0002 ADD2 STA2 + STH2kr ( #0000 ADD2 ) LDA2 STH2kr #0002 ADD2 LDA2 SUB2 putchar + + &return + POP2r JMP2r diff --git a/test/hardcoded-while.tal b/test/hardcoded-while.tal deleted file mode 100644 index 96daf28..0000000 --- a/test/hardcoded-while.tal +++ /dev/null @@ -1,13 +0,0 @@ -|100 -!{ @i $2 } #0000 ;i STA2 - -&while.1 [ ;i LDA2 #0008 LTH2 ] #03 JCN !{ ( Loop while iterator is less than limit. ) - ;i LDA2 print-num ( Run function to print number ) - ;i LDA2 INC2 ;i STA2 ( incriment counter ) -!&while.1 } -BRK ( Halt. ) - -@print-num ( int -- ) - LIT "0 ADD ( Add number to ascii character 0 ) - #18 DEO ( Send to Console/write ) - JMP2r diff --git a/test/hello-hardcoded.tal b/test/hello-hardcoded.tal new file mode 100644 index 0000000..a0f8602 --- /dev/null +++ b/test/hello-hardcoded.tal @@ -0,0 +1,20 @@ +|0100 + LIT2r 0000 main POP2r BRK + +@msg 6e 75 71 6e 65 48 20 27 75 27 3f 00 + +@main ( -- ) + OVR2r LIT2r 0002 SUB2r + ;msg STH2kr STA2 + STH2kr LDA2 str/ + + &return + POP2r JMP2r + +@str/ ( str* -- ) + LDAk DUP ?{ POP POP2 JMP2r } + #18 DEO + INC2 !/ + +@str/ ( str* -- ) + str/ #0a #18 DEO \ No newline at end of file diff --git a/test/if-hardcoded.tal b/test/if-hardcoded.tal new file mode 100644 index 0000000..b84af91 --- /dev/null +++ b/test/if-hardcoded.tal @@ -0,0 +1,27 @@ +|100 + LIT2r 0000 main POP2r BRK + +@const_str_0 78 20 69 73 20 31 30 00 +@const_str_1 78 20 69 73 20 32 30 00 +@const_str_2 78 20 69 73 20 73 6f 6d 65 74 68 69 6e 67 20 65 6c 73 65 00 + +@main ( -- ) + OVR2r LIT2r 0002 SUB2r + + #0014 STH2kr STA2 + + STH2kr LDA2 #000a EQU2 #03 JCN !{ ;const_str_0 str/ !&if_end.0 } + STH2kr LDA2 #0014 EQU2 #03 JCN !{ ;const_str_1 str/ !&if_end.0 } + ;const_str_2 str/ + &if_end.0 + + &return + POP2r JMP2r + +@str/ ( str* -- ) + LDAk DUP ?{ POP POP2 JMP2r } + #18 DEO + INC2 !/ + +@str/ ( str* -- ) + str/ #0a #18 DEO diff --git a/test/str-hardcoded.tal b/test/str-hardcoded.tal new file mode 100644 index 0000000..4a5fb47 --- /dev/null +++ b/test/str-hardcoded.tal @@ -0,0 +1,85 @@ +|0100 + LIT2r 0000 main POP2r BRK + +@sext + #80 ANDk EQU #ff MUL SWP JMP2r + +%afree_ ( -- ) { + #0000 ;mem_length_ STA2 } + +@msg 20 64 61 6d 61 67 65 20 69 6e 66 6c 69 63 74 65 64 21 0a 00 + +@main ( -- ) + OVR2r LIT2r 0006 SUB2r + #000e STH2kr ( #0000 ADD2 ) STA2 + #0096 STH2kr #0002 ADD2 STA2 + STH2kr ( #0000 ADD2 ) LDA2 STH2kr #0002 ADD2 LDA2 MUL2 #0014 DIV2 #0003 SUB2 STH2kr #0006 ADD2 STA2 + + STH2kr #0006 ADD2 LDA2 nat_to_str_ str/ + + ;msg str/ + + &return + afree_ POP2r JMP2r + +@aalloc_ ( size* -- result* ) + OVR2r LIT2r 0004 SUB2r STH2kr INC2 INC2 STA2 + ;mem_length_ LDA2 STH2kr STA2 + ;mem_length_ LDA2k STH2kr INC2 INC2 LDA2 ADD2 SWP2 STA2 + ;mem_ STH2kr LDA2 ADD2 !&return + #0000 + + &return + POP2r JMP2r + +@amcpy_ ( length* from* -- result* ) + OVR2r LIT2r 0008 SUB2r STH2kr #0006 ADD2 STA2 + STH2kr #0004 ADD2 STA2 + STH2kr #0004 ADD2 LDA2 aalloc_ + STH2kr INC2 INC2 STA2 + #0000 STH2kr STA2 + + &begin.1 + STH2kr LDA2 STH2kr #0004 ADD2 LDA2 LTH2 #00 EQU ?&break.1 + STH2kr #0006 ADD2 LDA2 STH2kr LDA2 ADD2 LDA sext + STH2kr INC2 INC2 LDA2 STH2kr LDA2 ADD2 STA + POP + + &continue.1 + STH2kr LDA2k INC2k ROT2 STA2 + POP2 !&begin.1 + + &break.1 + STH2kr INC2 INC2 LDA2 !&return + #0000 + + &return + POP2r JMP2r + +@nat_to_str_ ( n* -- result* ) + OVR2r LIT2r 000a SUB2r STH2kr #0008 ADD2 STA2 + #0005 STH2kr STA2 + + &begin.1 + STH2kr #0008 ADD2 LDA2 #000a OVR2 OVR2 DIV2 MUL2 SUB2 #0030 ADD2 STH2kr INC2 INC2 STH2kr LDA2k #0001 SUB2 SWP2 STA2k + POP2 ADD2 STA + POP STH2kr #0008 ADD2 LDA2k #000a DIV2 SWP2 STA2 + + &continue.1 + #0000 STH2kr #0008 ADD2 LDA2 LTH2 ?&begin.1 + + &break.1 + #0005 STH2kr LDA2 SUB2 STH2kr INC2 INC2 STH2kr LDA2 ADD2 amcpy_ + !&return + #0000 + + &return + POP2r JMP2r + +@str/ ( str* -- ) + LDAk DUP ?{ POP POP2 JMP2r } + #18 DEO + INC2 !/ + +@mem_length_ #0000 +@mem_ diff --git a/test/while-hardcoded.tal b/test/while-hardcoded.tal new file mode 100644 index 0000000..6b576d3 --- /dev/null +++ b/test/while-hardcoded.tal @@ -0,0 +1,17 @@ +|100 + LIT2r 0000 main POP2r BRK + +@main ( -- ) + OVR2r LIT2r 0002 SUB2r ( setup number of locals; in this case 1 u16 named 'i' ) + + #0000 STH2kr STA2 ( store the intial value into i ) + + &while.1 [ STH2kr LDA2 #0008 LTH2 ] #03 JCN !{ ( Loop while iterator is less than limit. ) + STH2kr LDA2 print-num ( Run function to print number ) + STH2kr LDA2 INC2 STH2kr STA2 ( incriment counter ) + !&while.1 } + +@print-num ( int -- ) + LIT "0 ADD ( Add number to ascii character 0 ) + #18 DEO ( Send to Console/write ) + JMP2r