# Build the Docker image
docker build ./ -t vas
# Run the container
# Linux/MacOS:
docker run --rm -it -v "$(pwd)":/root/env vas
# Windows (CMD):
docker run --rm -it -v "%cd%":/root/env vas
# Windows (PowerShell):
docker run --rm -it -v "${pwd}:/root/env" vasRequires the V compiler to be installed.
v . -prodBasic usage:
vas [options] <input_file>.sOptions:
-o <filename>: Set output file name (default: input_file.o)-f <format>: Output format:elf,macho, orpe(default: auto-detect from OS)--keep-locals: Keep local symbols (e.g., those starting with.L)
The examples/ directory contains ready-to-run programs organized by platform:
examples/
linux/ — ELF examples (assemble with vas, link with ld/gcc)
macos/ — Mach-O examples (assemble with vas -f macho, link with clang)
windows/ — PE/COFF examples (assemble with vas -f pe, link with gcc/MinGW)
Linux (ELF):
./vas examples/linux/hello.s && ld -o hello.out examples/linux/hello.o && ./hello.out
# > Hello, world!macOS (Mach-O, x86-64):
./vas -f macho examples/macos/hello.s && clang -arch x86_64 examples/macos/hello.o -o hello.out && arch -x86_64 ./hello.out
# > Hello, world!Windows (PE/COFF, x86-64):
Windows uses the Microsoft x64 ABI: the first four integer arguments are passed
in %rcx, %rdx, %r8, %r9 (instead of %rdi, %rsi, %rdx, %rcx
on Linux/macOS), and the caller must reserve 32 bytes of shadow space before
every call.
Link with MinGW/GCC:
vas -f pe examples/windows/hello.s && gcc -o hello.exe examples/windows/hello.o && hello.exe
# > Hello, world!Or with the MSVC linker:
vas -f pe examples\windows\hello.s
link /out:hello.exe /subsystem:console /defaultlib:ucrt examples\windows\hello.o
hello.exeCross-compile from Linux (requires x86_64-w64-mingw32-gcc):
./vas -f pe examples/windows/hello.s && x86_64-w64-mingw32-gcc -o hello.exe examples/windows/hello.oRegression tests live under tests/cases/, split by output format:
tests/cases/
elf/ — ELF test cases: <name>.s + <name>.expected.md5
macho/ — Mach-O test cases: <name>.s + <name>.expected.md5
pe/ — PE test cases: <name>.s + <name>.expected.md5
The runner is a V _test.v file:
v test tests/This rebuilds vas, assembles every case in all three directories, and asserts
the output bytes match the recorded MD5. To add a case:
# ELF example
$EDITOR tests/cases/elf/my_feature.s
./vas -f elf tests/cases/elf/my_feature.s
md5 -q tests/cases/elf/my_feature.o > tests/cases/elf/my_feature.expected.md5
rm tests/cases/elf/my_feature.o
v test tests/
# Mach-O example
$EDITOR tests/cases/macho/my_feature.s
./vas -f macho tests/cases/macho/my_feature.s
md5 -q tests/cases/macho/my_feature.o > tests/cases/macho/my_feature.expected.md5
rm tests/cases/macho/my_feature.o
v test tests/
# PE example
$EDITOR tests/cases/pe/my_feature.s
./vas -f pe tests/cases/pe/my_feature.s
md5 -q tests/cases/pe/my_feature.o > tests/cases/pe/my_feature.expected.md5
rm tests/cases/pe/my_feature.o
v test tests/The assembler's instruction set is data-driven: rows are generated from
NASM's third_party/insns.dat by tools/gen_insns.v and committed to
encoder/insns_table.gen.v. Regenerate after editing the parser:
v run tools/gen_insns.vSee LICENSE-NASM for the BSD-2-clause notice covering the bundled
insns.dat and the rows derived from it.
This project is licensed under the MIT License - see the LICENSE file for details.