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.
Vimasm
| Designed by | User:Ayazumi |
|---|---|
| Appeared in | 2026 |
| Computational class | Turing complete |
| Reference implementation | https://github.com/diyiliumin/vimasm |
| File extension(s) | .txt |
Vimasm is a parasitic register-machine assembly dialect that runs on Vim's Normal mode. It uses the Vim buffer as its entire address space: registers, memory, stack, heap, and I/O sections are all plain text lines. The program counter is the cursor's current line number, and yy@0 performs instruction fetch and execution by yanking the current line into a register and replaying it as a macro.
Unlike prior proofs that Vim is Turing-complete (which simulate Turing machines or interpret existing esolangs), Vimasm is a register-based assembly language with an explicit instruction set, register model, calling convention, stack, heap, and I/O sections.
Usage
- Open any
.txtfile with a plain, unconfigured Vim (no plugins, no vimrc needed). - Read the usage instructions written inside the file.
- For the latest full build:
127Gyy@0to generate the binary-to-decimal lookup table- wait for the red error message
135Gyy@0to run the test program (takes about a minute)
- A red Vim error message means "instruction completed" — this is intentional.
- To write your own program, edit the region below
please write asm below here.
If you see no red message but the program stops, it has derailed. Either your syntax does not match the assembler's strict format, or you hit an untested edge case.
Execution Model
- Host: Vim (Normal mode, no plugins or configuration required)
- Instruction fetch:
yyyanks the current line,@0executes it as a macro - Program counter: cursor line number, changed via
Gand search motions - Control transfer: the executing line writes a new jump command into a fixed buffer line, then falls through to it
Memory Model
- Registers: fixed lines in the buffer, addressed by
Gand line number - Stack: a dedicated
STACKsection, pushed and popped manually - Heap: a
HEAPsection, resized bymallocandfree - I/O: a
STDOUTsection, written byoutanddout - There is no separate data section: all values are plain text lines
Registers
| Register | Number |
|---|---|
| rax | 37 |
| rbx | 38 |
| rcx | 39 |
| rdx | 40 |
| r5 | 41 |
| r6 | 42 |
| r7 | 43 |
| r8 | 44 |
| r9 | 87 |
| r10 | 88 |
| r11 | 89 |
| r12 | 90 |
| r13 | 91 |
| r14 | 92 |
| r15 | 93 |
| r16 | 94 |
Instruction Set
| Opcode | Semantics |
|---|---|
mov rX,YYYY |
Write YYYY into register rX |
dmov rX,d<number> |
Convert decimal number to binary and write into rX |
add rX,rY |
rX = rX + rY |
sub rX,rY |
rX = rX - rY |
and rX,rY |
rX = rX AND rY |
or rX,rY |
rX = rX OR rY |
xor rX,rY |
rX = rX XOR rY |
not rX |
rX = NOT rX |
shl rX |
rX = rX << 1 |
sar rX |
rX = rX >> 1 (arithmetic) |
slr rX |
rX = rX >> 1 (logical) |
cmp rX,rY |
Compare rX and rY, set flags |
jmp __flag |
Jump to flag |
jz __flag |
Jump to flag if zero flag is set |
jnc __flag |
Jump to flag if carry flag is clear |
call __aim,__ret |
Write return address, jump to aim (return address auto-set in newer versions) |
ret |
Return to written address |
push rX |
Push rX onto stack |
pop rX |
Pop stack into rX |
load rX,[rY] |
Load memory at address rY into rX |
store [rX],rY |
Store rY into memory at address rX |
malloc rX |
Resize heap |
free rX |
Shrink heap |
insert rX,string |
Insert string into heap |
out rX |
Output rX as binary to STDOUT |
dout rX |
Output rX as decimal to STDOUT |
end |
Halt |
Control Flow
- Flags are defined with
__<space>nameand referenced with__name. - The program starts at the last
__start(code before it is ignored). - Comments begin with
;;. For a full-line comment, no space before;;; for an end-of-line comment, the format iscode<space>;;<space>comment. - Indentation must use tabs.
- Only one blank line is allowed between codes.
- A Vim red error message is used as the "instruction completed" signal; this is intentional.
callandretnow support automatic return-address handling, so no unique return flag is needed.
Decimal Support
Vimasm supports decimal numbers, but you must declare the required number of digits at the start of the program. Modify the number of leading zeros in the "please set bit width here" region, then execute yy@0 on that line to generate the conversion table.
Use dmov <register>,d<number> to convert a decimal number to binary and store it. dout outputs in decimal.
Heap
Vimasm supports heap operations, but malloc and free mean changing the size of the heap (not traditional allocation/deallocation). The binary-to-decimal lookup table must be generated with 127Gyy@0 before using the heap.
Evolution: From Logic Gates to Self-Hosting Assembler
Vimasm was developed in stages, each adding a new capability. The reference repository contains one commit per stage; reading git log --oneline from the bottom up gives the language's development history.
| Stage | File | What it added |
|---|---|---|
| 0 | t.txt | test yy@0 macro execution
|
| 1 | bj.txt | half adder |
| 2 | qj.txt | full adder |
| 3 | alu.txt, alu_more.txt | ALU, 10 operations |
| 4 | asm.txt, asm_jnc.txt, asm_jnc_fix_if.txt | assembler + jump-no-carry |
| 5 | asm_cmt_tab.txt | comments + tab indentation |
| 6 | asm_bout.txt, asm_jz_jmp.txt, asm_varabled.txt | jmp, jz, decimal support |
| 7 | asm_call_ret.txt | call / ret |
| 8 | asm_pop_push.txt | stack push / pop |
| 9 | asm_heap.txt | heap + store / load |
| 10 | asm_insert.txt | insert string into heap |
| 11 | asm_auto_set_addr_for_call*.txt | auto address for call |
| 12 | asm_auto_set_addr_for_call_bk_d_buildin_for_slow_show.txt | slow-show debug build |
The key self-hosting transition happens at Stage 4: in Stages 0–3, the author manually implemented logic gates and the ALU using Vim key sequences. From Stage 4 onward, these hand-built ALUs are used to parse and execute a new assembly syntax. In other words, the assembler itself is written in lower-level Vim microcode, and it in turn parses higher-level guest assembly programs. This is a clear self-hosting chain:
Vim Normal-mode key sequences (host primitives)
↓ hand-built
Logic gates / ALU (Stages 1–3)
↓ built using the ALU
Assembler (Stage 4+)
↓ parses
Guest assembly programs (user-written)
The full machine is asm_insert.txt (or the latest asm_auto_set_addr_for_call* build, if you want call/ret with automatic return-address handling).
Programming Restrictions
movonly supports themov register,numberform. If you needmov rax,rbx, writemov rax,0000followed byadd rax,rbx.add,sub,xor, etc. only support theregister,registerform.load/storeonly support theregister,registerform.- The bit width must be set before using decimal.
Performance
Executing a single add may require searching ASM_manual, copying tokens, parsing operands, jumping across the buffer, modifying text bit by bit, propagating carries, writing back the result, and restoring the guest cursor. The full test program takes about a minute to run, which is entirely consistent with this style of implementation.
Computational Class
Vimasm is Turing-complete: it has unbounded memory (the buffer can be extended), conditional branching, and a call stack.
Security Note
Although the extension is .txt, this is not passive text. yy@0 actively executes the key sequences in the file, and Vim macros can in principle trigger Ex commands, shell commands, file writes, and other operations. When experimenting, use an isolated environment, a clean configuration, and first audit the file for :, shell escapes, or file-writing behavior.
Documents
how_does_this_work.txt— the author's own description of how the ALU works, explaining in detail the boot primitive, the adder, the subtractor, and how to build logic gates out of Vim text operations.vim_vm_principle_analysis.txt— a third-party analysis of the assembler, written by reverse-engineering the code, containing detailed speculation on the fetch–decode–execute flow, the register model, the call/ret mechanism, and the automatic address-preprocessing macros at the end of the file.