Getting Started

This page walks you through building armvm, running the test suite, and executing your first ARM32 program.

Prerequisites

Tool Notes
GCC or Clang Any recent version for the host
GNU Make Standard make
clang -arch armv7 macOS only — required only for the simple-app example

Building

# Clone the repository
git clone https://github.com/corepunch/armvm
cd armvm

# Build the armvm-compiler executable
make

# Run all unit tests
make test

# Remove build artifacts
make clean

The default make target produces the armvm-compiler executable in the repository root.

Manual build

cd armvm
gcc -Wall -O2 -o ../armvm-compiler \
    armvm.c compiler.c armcomp.c expr.c memory.c libpvm.c -lm

Using Xcode

xcodebuild -project armvm.xcodeproj -scheme armvm

Your first program

Step 1 — write ARM assembly

cat > hello.s << 'EOF'
.globl _main
_main:
    mov r0, #42
    bx lr
EOF

Step 2 — compile to bytecode

./armvm-compiler -o hello.bin hello.s

The compiler produces two files:

File Contents
hello.bin Executable bytecode with a 12-byte ORCA header
hello.bin_d Debug symbol table (line-number ↔ bytecode offset mapping)

Step 3 — run it

#include <stdio.h>
#include <string.h>
#include "avm.h"

int main(void) {
    const char *src =
        ".globl _main\n"
        "_main:\n"
        "    mov r0, #42\n"
        "    bx lr\n";

    avm_State *L = avm_newstate(64*1024, 64*1024);
    avm_loadbuffer(L, src, strlen(src));
    avm_call(L, L->entry_point);
    printf("r0 = %d\n", avm_tointeger(L, 1));  /* r0 = 42 */
    avm_close(L);
    return 0;
}

Using the low-level API

#include <stdio.h>
#include <stdlib.h>
#include "vm.h"

static DWORD no_syscall(LPVM vm, DWORD id) { (void)vm; (void)id; return 0; }

int main(void) {
    FILE *fp = fopen("hello.bin", "rb");
    fseek(fp, 0, SEEK_END);
    DWORD size = (DWORD)ftell(fp);
    fseek(fp, 0, SEEK_SET);
    BYTE *prog = malloc(size);
    fread(prog, size, 1, fp);
    fclose(fp);

    LPVM vm = vm_create(no_syscall, 64*1024, 64*1024, prog, size);
    free(prog);

    execute(vm, 0);                              /* start at offset 0 */
    printf("r0 = %u\n", vm->r[0]);              /* prints: r0 = 42  */
    vm_shutdown(vm);
    return 0;
}

Running the tests

make test

The test binary (build/armtest) runs 11 unit tests and prints a summary:

ARM VM Test Suite
=================

✓ testMOV
✓ testLSL
✓ testLSR
✓ testPUSH
✓ testCMP
✓ testCMP2
✓ testBL
✓ testLDR
✓ testLDR2
✓ testADD
✓ testPopPC

=================
Test Results: 11/11 passed

Generating compatible assembly from C

macOS / Xcode

# Compile a C file to ARMv7 assembly
clang -S -arch armv7 -target armv7-apple-darwin -O1 input.c -o input.s

GCC cross-compiler (Linux)

arm-linux-gnueabi-gcc -S -march=armv7-a -marm -O2 input.c -o input.s

Note: GCC may generate directives that armvm does not recognise. Unknown directives are ignored, so this is usually harmless, but verify the output.

simple-app example (macOS)

The examples/simple-app/ directory contains a full end-to-end demonstration that cross-compiles a C source file, injects custom host functions via the syscall interface, and runs the result in the VM.

# Build armvm first, then run the example
make
make -C examples/simple-app run

See Examples for a detailed walkthrough.

Next steps

  • Lua-like API — full reference for avm_newstate, avm_register, avm_loadbuffer, avm_call, etc.
  • API Reference — low-level vm_create / execute interface
  • Assembly Reference — complete instruction and directive reference
  • Architecture — internals, adding new instructions, adding new directives