We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
VoidPtr
| Paradigm(s) | imperative |
|---|---|
| Designed by | User:Basilisk |
| Appeared in | 2026 |
| Memory system | Cell-based |
| Computational class | Unknown |
| Major implementations | Original |
| Influenced by | brainfuck |
| File extension(s) | .vptr |
VoidPtr (pronounced void pointer) operates on single byte sized cells using only moves and bit-wise logical operations.
The name was inspired by the C data type 'void*' hinting at the language's lack of data differentiation.
Overview
This overview is written based on the VoidPtr specification v.1.2.0.
Comments
Comments are enclosed in semicolons.
;This is a comment;
Operations
Operations are performed on individual bytes in memory.
Bytes are addressed as decimal numbers and can be addressed in an infinite range depending on allocated memory.
| Operation | Description |
|---|---|
a -> b
|
Move value at a into byte b. |
a & b -> c
|
Apply bit-wise AND and move result to c. |
a | b -> c
|
Apply bit-wise OR and move result to c. |
a ^ b -> c
|
Apply bit-wise XOR and move result to c. |
a < b -> c
|
Apply bit shift left of b to a and move result to c. |
a > b -> c
|
Apply bit shift right of b to a and move result to c. |
! a -> b
|
Apply bit-wise not on a and move result to c. |
? a
|
Compares a to 0 and skips the next instruction if the value at the specified address is not zero. |
'label_name
|
Redirects execution to specified label. |
Note: double input operations (e.g. and, or) expect a and c to always be an address
Addressing modes
VoidPtr allows constants, direct addressing and indirect addressing.
3 -> 4 ;Direct - Direct. Moves value from address 3 into address 4; [3] -> 4 ;Indirect - Direct. Moves value from address stored in 3 to address 4; $65 -> 4 ;Constant - Direct. Writes value 65 to address 4;
Note: Allowed address modes depend on the instruction type. Single input instructions allow constants as first input, double input instructions allow only the second operand to be a constant. All instructions allow indirect and direct addressing at every operand.
Constants
Constant values are denoted by '$' and can range from 0-255.
Strings
Characters and strings enclosed in " are compiled to their ascii representations.
"A" -> 2
Compiles to:
$65 -> 2
Multiple characters:
"ABC"
Compiles to:
$65 $66 $67
Labels
Labels mark program addresses and are used for control flow.
A label is defined by an underscore followed by its name.
_label_name _another_label
Macros
Macros allow the user to interact with the compiler and ask for text substitution.
All macros start with the '#' character.
def
The 'def' macro is similar to the '#define' macro in C. It allows the user to name code segments and tell the compiler to instantiate them later. Structure of def: #def,macro_name,contents,#end
#def reg5 5 #end
#def print2 $2 -> 1 $1 -> 0 #end
Instantiating a defined name works by writing it at the selected location.
$65 -> 2 print2 ;pastes contents of print2 at this location;
Define macros can be used inside of each other if the used macro is defined before its usage.
#def print2_3 print2 ;print2 will be instantiated at macro resolution; $3 -> 1 $1 -> 0 #end
Note: Code inside define blocks does not get executed on its own.
stream
The stream macro is a shortcut to writing many values contiguously.
Structure of stream: #stream,direct_start_address,values,#end
#stream 20 $65 $66 $67 #end
Compiles to:
$65 -> 20 $66 -> 21 $67 -> 22
paste
Pastes the contents of a specified file at the macro location (similar to #include in C).
Structure of paste: #paste,file_path
#paste directory/filename.vptr
Note: the file path is provided as raw text
System Calls
System calls are used to communicate with the interpreter or runtime. Addresses 0 and 1 are special addresses used to issue system calls.
Writing a value into 0 automatically triggers a system call. 0 is reset to $0 after every call.
Some system calls expect address 1 to hold arguments or a pointer to arguments for the system call.
Note: System calls and their exact functionality depend highly on the capabilities of the implementation and supported specification version.
Exact system calls are not part of the core language features.
System calls for spec v.1.2.0
n is the byte referenced by address 1
| Call number | Description |
|---|---|
$1
|
Print byte n as a number. |
$2
|
Print byte n as a character. |
$3
|
Read one character from stdin into byte n. |
$4
|
Allocate n additional bytes. |
$5
|
Deallocate n bytes. |
$6
|
Read a 32-bit address from bytes n through n+3 and load its value into address 1. |
$7
|
Store the value at n into the 32-bit address stored in the next four bytes. |
$8
|
Store the current program counter into four bytes beginning at n. |
$9
|
Read four bytes at n as a 32-bit integer and set the program counter. |
$10
|
Load a file into memory starting at n |
Examples
8 bit addition:
#def result 4 #end #def carry 5 #end #def byte_a 2 #end #def byte_b 3 #end $3 -> byte_a $1 -> byte_b _add_loop ? byte_b '_end byte_a & byte_b -> carry byte_a ^ byte_b -> result carry < $1 -> byte_b result -> byte_a '_add_loop _end $result -> 1 $1 -> 0
Hello World:
#stream 10 "Hello World!" $10 $0 #end
#def counter 3 #end
#def data 4 #end
#def bit 5 #end
#def tmp 6 #end
$10 -> counter
_loop
[counter] -> data
$ data -> 1
$2 -> 0
$1 -> bit
_add
counter & bit -> tmp
counter ^ bit -> counter
tmp < $1 -> bit
? bit '_done
'_add
_done
? data '_exit
'_loop
_exit
brainfuck interpreter running a hello world program:
Note: This interpreter is not Turing complete for simplicity. It has a limited program, loop and address space.
;
This is a Brainfuck implementation written in Voidptr.
This version only uses the first 255 bytes for simplicity,
make sure not to load any programms which exceed programm space.
;
;input program;
#stream 28 "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++." $0 #end
;general purpose registers;
#def r0 2 #end
#def r1 3 #end
#def r2 4 #end
#def r3 5 #end
#def r4 6 #end
#def r5 7 #end
;add registers;
#def a0 10 #end
#def a1 11 #end
#def a2 12 #end
;sub register;
#def s0 13 #end
;register mem space end;
#def le 20 #end
;system call;
#def syscall -> 0 #end
#def printNum $1 #end
#def printChar $2 #end
#def getChar $3 #end
;8 bit add;
#def add8
;load args;
r0 -> a0
r1 -> a1
__add_loop
? a1 '__end ;check if carry is 0;
a0 & a1 -> a2
a0 ^ a1 -> a0
a2 < $1 -> a1
'__add_loop
__end
a0 -> r0
#end
;8 bit subtract;
#def sub8
! r1 -> r1
r0 -> s0
$1 -> r0
add8
r0 -> r1
s0 -> r0
add8
#end
#def pointer r3 #end ;byte holding the bf pointer;
#def pc r4 #end
#def stack_ptr r5 #end
;mem layout: 28 - 160 code, 161 - 170 loop stack, 171 - 255 bf memory;
$171 -> pointer
$161 -> stack_ptr
$27 -> pc
#def reset_r1 $1 -> r1 #end
reset_r1
'_main
; ----- OP Labels ----- ;
;> op;
_inc_ptr
pointer -> r0
reset_r1
add8
r0 -> pointer
'_main
;< op;
_dec_ptr
pointer -> r0
reset_r1
sub8
r0 -> pointer
'_main
;+ op;
_plus
[pointer] -> r0
reset_r1
add8
r0 -> [pointer]
'_main
;- op;
_minus
[pointer] -> r0
reset_r1
sub8
r0 -> [pointer]
'_main
;. op;
_out
pointer -> 1
printChar syscall
'_main
;, op;
_in
pointer -> 1
getChar syscall
'_main
;[ op;
_start_loop
? [pointer] '__skip_loop
;enter loop;
stack_ptr -> r0
reset_r1
add8
r0 -> stack_ptr
pc -> [stack_ptr] ;push current pc to loop stack;
'_main
;skip loop, (skip to next ]);
__skip_loop
;increase pc;
pc -> r0
reset_r1
add8
r0 -> pc
? [pc] '_end ;end if we reach 0;
[pc] -> r0
"]" -> r1
sub8
? r0 '_main
'__skip_loop
;] op;
_end_loop
? [pointer] '__pop_loop ;check if current is 0;
[stack_ptr] -> pc
'_main
__pop_loop
stack_ptr -> r0
reset_r1
sub8
r0 -> stack_ptr
'_main
__loop_back
'_main
; ----- Main Loop ----- ;
_main
;increase pc;
pc -> r0
reset_r1
add8
r0 -> pc
;print vlaue at r4 ptr (current BF character)
pc -> 1
printChar syscall
;
[pc] -> r0
">" -> r1
sub8
? r0 '_inc_ptr
[pc] -> r0
"<" -> r1
sub8
? r0 '_dec_ptr
[pc] -> r0
"+" -> r1
sub8
? r0 '_plus
[pc] -> r0
"-" -> r1
sub8
? r0 '_minus
[pc] -> r0
"." -> r1
sub8
? r0 '_out
[pc] -> r0
"," -> r1
sub8
? r0 '_in
[pc] -> r0
"[" -> r1
sub8
? r0 '_start_loop
[pc] -> r0
"]" -> r1
sub8
? r0 '_end_loop
? [pc] '_end
_end