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.
Symblicity
Symblicity is a low-level esoteric programming language with five 8-bit registers, a unified 65,536-byte address space, a double-ended data stack, a dedicated nested return stack, bidirectional execution, direction-sensitive letter jumps, stateful binary file I/O, and optional asynchronous audio.
Every non-formatting character in a .sym file has a meaning. Newline,
carriage-return, form-feed, and vertical-tab characters are ignored as formatting.
A literal TAB begins a comment and ignores the rest of that physical source line.
A literal ASCII space is an instruction that skips the next instruction.
Instruction set
Registers
Symblicity operates on five 8-bit registers: R0 through R4.
There are two selectable register banks:
| Set | A | B | Shared result |
|---|---|---|---|
| 0 | R0
|
R1
|
R2
|
| 1 | R3
|
R4
|
The \ instruction switches between the two register banks. Unless otherwise
specified, instructions referring to A or B operate on the
currently selected bank.
All register arithmetic uses 8-bit wraparound.
Constants
| Instruction | Operation | Description |
|---|---|---|
0
|
A += 1
|
Adds 20 to A. |
1
|
A += 2
|
Adds 21 to A. |
2
|
A += 4
|
Adds 22 to A. |
3
|
A += 8
|
Adds 23 to A. |
4
|
A += 16
|
Adds 24 to A. |
5
|
A += 32
|
Adds 25 to A. |
6
|
A += 64
|
Adds 26 to A. |
7
|
A += 128
|
Adds 27 to A. |
8
|
A -= 1
|
Subtracts 1 from A. |
9
|
A -= 2
|
Subtracts 2 from A. |
For example:
4320
adds:
16 + 8 + 4 + 1 = 29
to A.
Arithmetic and bitwise operations
Arithmetic and bitwise operations use the selected A and B registers and store their
result in the shared R2 register.
| Instruction | Operation | Description |
|---|---|---|
+
|
R2 = A + B
|
Addition. |
-
|
R2 = A - B
|
Subtraction. |
*
|
R2 = A * B
|
Multiplication. |
/
|
R2 = A / B
|
Unsigned integer division. Division by zero produces 0. |
%
|
R2 = A % B
|
Unsigned modulo. Modulo by zero produces 0. |
^
|
R2 = A ^ B
|
Bitwise XOR. |
|
|
R2 = A | B
|
Bitwise OR. |
&
|
R2 = A & B
|
Bitwise AND. |
Register operations
| Instruction | Operation | Description |
|---|---|---|
\
|
Switch register bank | Toggles between R0/R1 and R3/R4.
|
:
|
Swap A and B | Exchanges the selected A and B registers. |
;
|
Swap B and R2 | Exchanges the selected B register with the shared R2 register.
|
`
|
Clear A | Sets the selected A register to zero. |
Conditional skipping
Conditional instructions test the current register state. If their condition is satisfied, the next instruction in the current execution direction is skipped.
| Instruction | Condition | Description |
|---|---|---|
=
|
A == B
|
Skip the next instruction if A equals B. |
<
|
A < B
|
Skip the next instruction if A is less than B. |
>
|
A > B
|
Skip the next instruction if A is greater than B. |
!
|
A != B
|
Skip the next instruction if A does not equal B. |
?
|
R2 == 0
|
Skip the next instruction if the shared result register is zero. |
| Space | Always | Unconditionally skips the next instruction in the current direction. |
A literal ASCII space is therefore executable and should not be used merely for source formatting.
Input and output
Character and numeric I/O use the selected primary register A:
R0while register bank 0 is active.R3while register bank 1 is active.
| Instruction | Operation | Description |
|---|---|---|
'
|
Byte input → A | Reads one byte of input into A. |
"
|
Output A | Outputs A as one byte/character. |
.
|
Decimal input → A | Reads a decimal integer and stores its low 8 bits in A. |
,
|
Print A | Outputs A as an unsigned decimal number. |
The reference interpreter supports a configurable input wait for ' and
.:
--input-wait infiniteor--input-wait -1blocks indefinitely and is the default.--input-wait 0performs a nonblocking poll.--input-wait Nwaits for up to N milliseconds, where N is 0 through 60000.--input-timeoutis a compatibility alias for--input-wait.
Infinite mode preserves the original input behavior and leaves R2
unchanged. In finite or nonblocking mode, successful input stores the value in A
and sets R2 = 1. Timeout or EOF sets R2 = 0 and leaves A
unchanged. A partial decimal token is discarded if numeric input times out.
Stack operations
Symblicity's stack is a double-ended byte stack/deque. Push and pop operate on the currently selected end.
| Instruction | Operation | Description |
|---|---|---|
@
|
Push A | Pushes A onto the active end of the stack. |
$
|
Pop → A | Pops from the active end into A. An empty pop produces 0. |
_
|
Switch stack end | Toggles which end is used by both push and pop. |
The reference interpreter currently allocates 4096 stack bytes; stack overflow is an error.
Direction and structured control flow
Symblicity execution can proceed in either source direction.
| Instruction | Operation | Description |
|---|---|---|
{
|
Forward direction | Forces execution to continue forward through the source. |
}
|
Backward direction | Forces execution to continue backward through the source. |
[
|
Loop boundary | One side of a repeating [] block.
|
]
|
Loop boundary | The other side of a repeating [] block.
|
~
|
Break loop | Breaks only the innermost enclosing [] loop. Outside a loop it is a no-op.
|
#
|
Relative jump | Advances A + 1 source positions in the current execution direction.
|
The behavior of these instructions is relative to the current direction rather than being
inherently left-to-right. In particular, ~ does not break a
{} bidirectional region; it only exits an enclosing square-bracket loop.
Parenthesis calls
Parentheses provide a dedicated nested return mechanism separate from the byte stack.
| Instruction | Operation | Description |
|---|---|---|
(
|
Push/return | Pushes its position when it is the active caller symbol; otherwise returns from the most recent saved position. |
)
|
Push/return | Pushes its position when it is the active caller symbol; otherwise returns from the most recent saved position. |
The reference interpreter maintains a 4096-entry return stack containing source
positions only. One shared caller character, either ( or
), applies to the whole active stack:
- When the return stack is empty, the first encountered parenthesis becomes the caller character and its current source position is pushed.
- While the stack is non-empty, encountering that same caller character pushes another current position.
- Encountering the opposite parenthesis pops the top saved position and immediately resumes at
saved_position + current_direction. - When the final saved position is popped, the shared caller character is cleared.
The current execution direction is used at return time; direction is not stored in the return stack. Both orientations are valid:
( ... ) ) ... (
Nested calls and recursion are therefore supported. The parenthesis return stack is independent of the double-ended byte data stack.
Letter jumps
Ordinary alphabetic characters form uppercase/lowercase directional jump pairs.
For example, A searches in the current execution direction for the next
a, while g searches for the next G.
If no opposite-case occurrence exists in the current direction, execution simply continues.
Some letters are reserved by other parts of the VM:
W/wandY/yare the low/high memory-address registers.X/xare unified-memory read/write operations.Z/zare stateful binary file read/write operations in the normal memory model.U/uandV/vbecome audio operations when at least one sound is loaded.T/tbecome second-channel audio operations only when sounds are loaded and dual-audio mode is enabled.- When those optional audio modes are not active,
T/t/U/u/V/vretain normal opposite-case jump behavior.
The eight memory/file letters w/W/x/X/y/Y/z/Z are always reserved and
never perform ordinary case-pair jumps.
Memory and file I/O
Symblicity exposes one unified 65,536-byte memory space. Two 8-bit address registers form a 16-bit address:
address = (high << 8) | low
| Instruction | Operation | Description |
|---|---|---|
w
|
low address = A | Stores A in the low address byte. |
W
|
A = low address | Loads the low address byte into A. |
y
|
high address = A | Stores A in the high address byte. |
Y
|
A = high address | Loads the high address byte into A. |
x
|
memory[address] = A | Writes A to unified memory, except that writes to 0xFEFF are ignored.
|
X
|
A = memory[address] | Reads unified memory; 0xFEFF returns the wrapped system-time byte.
|
System-time byte
Address 0xFEFF, immediately before the file-name buffer, is a
read-only memory-mapped clock byte:
memory[0xFEFF] = Unix time in milliseconds mod 256
Reading it with X returns the current low 8 bits of system time in
milliseconds. The value wraps every 256 milliseconds. Writing it with
x is ignored.
The top 256 bytes, 0xFF00-0xFFFF, remain ordinary memory and also
serve as the file-name buffer for z/Z.
File I/O
z/Z operate on one stateful binary file stream. The file name is a
NUL-terminated byte string beginning at memory address 0xFF00, with a
maximum length of 255 bytes.
| Instruction | Operation | Description |
|---|---|---|
z
|
Write A | Opens the named file if necessary, creating it if it does not exist, then writes A at the current file position. |
Z
|
Read → A | Opens the named existing file if necessary and reads the next byte into A. |
Reads and writes share one advancing file position. If the bytes of the file name
change, the current file is closed and the newly named file is opened from
position 0 on the next z or Z.
Successful reads and writes set R2 = 0. Reading at EOF sets
R2 = 1 and leaves A unchanged. File-system, open, read, and write
failures are VM errors. The native interpreter closes the file automatically when
it exits.
The native VM accesses host files directly. The browser VM provides the same language operations through a persistent origin-local sandbox rather than arbitrary host paths.
Legacy two-bank compatibility
Programs written for the original two independent 256-byte memory banks can opt into a compatibility map with a TAB-comment directive:
<TAB>@memory legacy-2x256
The directive is detected before normal TAB-comment preprocessing and contributes zero executable source positions.
In this mode:
w/W + x/X -> unified page 0x00 (0x0000-0x00FF) y/Y + z/Z -> unified page 0x01 (0x0100-0x01FF)
Thus legacy z/Z regain their historical second-memory-bank behavior.
Programs without the directive use the normal 16-bit address model and stateful
file I/O.
Optional asynchronous audio
The reference interpreter can load an alphabetically sorted array of sound files:
sym --sounds ./sounds program.sym
When one or more sounds are loaded:
| Instruction | Operation | Description |
|---|---|---|
U
|
Next sound | Selects the next sound in the shared sound array, wrapping at the end. |
u
|
Previous sound | Selects the previous sound, wrapping at the beginning. |
V
|
Play channel 1 | Starts the currently selected sound asynchronously on channel 1. |
v
|
Stop channel 1 | Stops channel 1. |
A second simultaneous playback channel can be enabled with:
sym --sounds ./sounds --dual-audio program.sym
or:
sym --sounds ./sounds -2 program.sym
With dual audio enabled:
| Instruction | Operation | Description |
|---|---|---|
T
|
Play channel 2 | Starts the currently selected sound asynchronously on channel 2. |
t
|
Stop channel 2 | Stops channel 2. |
Both channels use the same shared sound selection controlled by U/u, but
their playback is independent. Therefore VT can play the same selected
sound on both channels, while VUT starts the current sound on channel 1,
moves the shared address to the next sound, then starts that sound on channel 2.
The reference interpreter uses ffplay for playback, so FFmpeg is required
only when using the audio extension.
If no sounds are loaded, U/u/V/v behave as ordinary letter jumps. If
dual-audio mode is not enabled, T/t also remain ordinary letter jumps.
Comments and formatting
| Character | Meaning |
|---|---|
| TAB | Begins a comment through the physical end of the line. |
| Newline / CR / form-feed / vertical-tab | Ignored as source formatting. |
| Space | Executable instruction: unconditionally skip the next instruction. |
Program execution normally ends when the program counter runs off either end of the source.
Compact reference
| Symbol | Meaning |
|---|---|
0-7
|
Add 2N to A |
8
|
A -= 1 |
9
|
A -= 2 |
+ - * / % ^ | &
|
A/B arithmetic or bitwise operation → R2 |
= < > !
|
Conditional skip using A and B |
?
|
Skip next if R2 == 0 |
| Space | Unconditionally skip next instruction |
\
|
Toggle R0/R1 ↔ R3/R4 |
:
|
Swap A and B |
;
|
Swap B and R2 |
`
|
A = 0 |
@
|
Push A |
$
|
Pop into A |
_
|
Switch active stack end |
'
|
Read byte into A |
"
|
Output A as byte |
.
|
Read decimal into A |
,
|
Output A as unsigned decimal |
#
|
PC += (A+1) × direction |
{
|
Force forward execution |
}
|
Force backward execution |
[ ]
|
Repeating loop |
~
|
Break innermost enclosing [] loop only
|
( )
|
4096-entry nested parenthesis return stack |
| Ordinary letters | Jump to next opposite-case partner in the current direction |
w/W
|
Set/read low 8-bit memory address byte |
y/Y
|
Set/read high 8-bit memory address byte |
x/X
|
Unified 64 KiB memory write/read |
z/Z
|
Stateful binary file write/read; legacy page-1 memory under @memory legacy-2x256
|
U/u
|
Next/previous sound when sounds are loaded; otherwise ordinary jumps |
V/v
|
Play/stop channel 1 when sounds are loaded; otherwise ordinary jumps |
T/t
|
Play/stop channel 2 in dual-audio mode; otherwise ordinary jumps |
| TAB | Comment through physical end of line |
Special memory locations:
| Address | Meaning |
|---|---|
0xFEFF
|
Read-only Unix-time-in-milliseconds modulo 256 |
0xFF00-0xFFFF
|
Ordinary memory and NUL-terminated file-name buffer for z/Z
|
Examples
Hello World
36"589"38""01"3567"34"01"999"9999"6789"
Output:
HelloWorld!
Functions
Symblicity can build reusable function-like regions from directional letter jumps,
#, bidirectional execution, and the dedicated parenthesis return stack.
Nested calls and recursion are supported by the 4096-entry return stack.
One canonical backward-entered convention used by larger Symblicity programs
defines a function pair A/a with:
{( 1`<reversed-body>a
and calls it with:
`)#A}
The literal space in the definition is an executable Symblicity instruction, not formatting. The function body is stored physically reversed because this convention enters and executes it right-to-left.
A second compact convention uses A as a one-bit call/return state with
#: A = 0 selects the call path and A = 1 selects the post-return path.
The current project conventions reserve m/M for the main-entry guard
and e/E for an exit guard in larger programs.
Online Simulator
Try Symblicity without installing anything:
The browser VM implements the same core register, stack, memory, nested return, timed-input, file-I/O, ANSI-terminal, and optional audio behavior. Browser file I/O uses a persistent origin-local sandbox. The playground includes example programs such as Battleship and Terminal Zero.
Reference interpreter
The maintained native interpreter, language documentation, installers, and examples are kept on GitHub:
Useful input modes include:
sym --input-wait infinite program.sym sym --input-wait 0 program.sym sym --input-wait 80 program.sym
-n is shorthand for nonblocking input and -b restores
infinite blocking input. --input-timeout is retained as a compatibility
alias for --input-wait.
Optional native audio is enabled with:
sym --sounds ./sounds program.sym sym --sounds ./sounds --dual-audio program.sym
The native audio extension uses ffplay, so FFmpeg is required only
when that extension is used.
The repository is dedicated to the public domain under CC0 1.0 Universal.