6.9 KiB
Undâr Programming Language
[ᚢ ᛫ ᛫ ᛫
᛫ ᚱ ᛫ ᛫
᛫ ᛫ ᛋ ᛫
᛫ ᛫ ᛫ ᚾ]
Undâr
Undâr is a programming language for the purpose of creating three dimensional video games and graphical user interfaces that work on constrained systems, microcontrollers, retro consoles, and the using emscripten. The language emphasizes hardware longevity, energy efficiency, and the preservation of digital art and games for future generations.
It has an internal REPL that allows for quick development as well as the ability to dump the program to a binary rom for preserving that program/game/etc.
It runs on the Reality Engine
, a 32 bit stack based VM written in freestanding C89.
Philosophy
Undâr it conforms to permacomputing principles.
"permacomputing encourages the maximization of hardware lifespan, minimization of energy usage and focuses on the use of already available computational resources. it values maintenance and refactoring of systems to keep them efficient, instead of planned obsolescence, permacomputing practices planned longevity. it is about using computation only when it has a strengthening effect on ecosystems." source
Undâr is designed to ensure that programs created today will remain executable for a very long time, even through technological collapse.
This is achieved through:
- A standardized bytecode format that maps 1:1 to human-readable assembly (Sċieppan)
- A VM specification that can be implemented easily
- Hardware abstractions for the VM implementation
- ROM files that contain all necessary information for execution
- A friendly syntax which focuses on maintaining code without obscuring functionality.
Getting Started
Build the Reality Engine
git clone https://git.alfrescocavern.com/zongor/undar-lang.git
cd undar-lang && make
Sċieppan
is a minimal assembler that uses s-expressions.
You can view some examples in the .asm.lisp
files in /test
The Undâr compiler will be written in Sċieppan, as well as core VM tests.
Sample Program: hello.asm.lisp
((code
(label main
(load &terminal-namespace) ; load terminal namespace
(load &hello-str) ; load hello string ptr
(string-length) ; get length to write to stdout
(syscall WRITE) ; do the write syscall
(halt))) ; done
(data
(label terminal-namespace "/dev/term/0")
(label hello-str "nuqneH 'u'?\n")))
./build/linux/undar-linux-debug ./test/hello.asm.lisp
Running the compiler without arguments will put it in "REPL" mode. It will function similar to a LISP repl.
Memory Management
memory is managed via frame based arenas. function scopes defines a memory frame.
heap allocations using the internal malloc opcode push pointers within this frame. when a frame exits, the pointer is reset like stack based gc.
((code
(label main ; this example adds 2 numbers together
(load-immediate $0 1) ; pushes 1 onto the stack for the function call
(push $0)
(load-immediate $0 1)
(push $0)
(call &add) ; here a new frame is generated
(pop $0) ; the element is returned and the memory for the pln is "freed" automatically because the child frame is done
(halt))
(label add
(pop $0)
(pop $1)
(add-int $2 $1 $0) ; add the arguments
(int-to-string $3 $2) ; convert to a string (heap allocation)
(push $3)
(call &pln) ; call print function
(push $2)
(return)) ; return to main function
(label pln
(load-immediate $0 &terminal-namespace) ; load the namespace for the terminal
(load-immediate $3 &new-line) ; and a newline char
(pop $1) ; pointer to string
(string-length $2 $1) ; get the length
(syscall WRITE $0 $1 $2) ; write the string
(string-length $4 $3)
(syscall WRITE $0 $3 $4)
(return))) ; return back to add function
(data ; allocates strings at compile time
(label terminal-namespace "/dev/term/0")
(label new-line "\n")))
values passed to functions must be explicitly returned to propagate. heap values are copy on write, so if a value is modified in a child function it will change the parents value, unless the size of the structure changes then it will copy the parents value and append it to its own frame with the modification. this allows for the low resource usage of a C but the convenience of a Java/Go without the garbage collection.
Core Types
Type | Description |
---|---|
int |
32-bit signed integer |
nat |
32-bit natural number |
real |
Q16.16 fixed-point real number |
str |
fat pointer [length + data] string |
bool |
true/false |
char |
Character |
ref |
Reference prefix for passing by reference |
A ref type allows pass by reference similar to a pointer in c. most types will be pass by value with some types being explicitly pass by reference.
primitive types like int, nat, real, etc. will always be safe to change in child frames.
Roadmap
License
MIT-0
Inspirations
- Uxn - The ideal system for permacomputing
- Plan 9 / 9P - Unified I/O, Tunnels
- Forth - Shadowing
- Lisp - Live coding, REPL, introspection
- Fortran - Array semantics, scientific clarity
- C / Zig - Control, minimalism
- Lua - Languages can be portable and expressive without being complicated.
- Lox - The start of my programming language creation journey
- Permacomputing wiki - Core ideology
- Dusk OS - A much better system for doing permacomputing
- Dis VM - CISC VM structure
- Retro Systems - N64, PS1, Mac Classic, Windows 95 - UI esthetics