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

From Esolang
Jump to navigation Jump to search
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 .txt file with a plain, unconfigured Vim (no plugins, no vimrc needed).
  • Read the usage instructions written inside the file.
  • For the latest full build:
    • 127Gyy@0 to generate the binary-to-decimal lookup table
    • wait for the red error message
    • 135Gyy@0 to 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: yy yanks the current line, @0 executes it as a macro
  • Program counter: cursor line number, changed via G and 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 G and line number
  • Stack: a dedicated STACK section, pushed and popped manually
  • Heap: a HEAP section, resized by malloc and free
  • I/O: a STDOUT section, written by out and dout
  • 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>name and 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 is code<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.
  • call and ret now 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

  • mov only supports the mov register,number form. If you need mov rax,rbx, write mov rax,0000 followed by add rax,rbx.
  • add, sub, xor, etc. only support the register,register form.
  • load/store only support the register,register form.
  • 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.

External Links