
ASM: examples explained
There are two workflows: #ASM blocks inside BASIC and standalone .asm source files. The first example uses a BASIC wrapper; the others use the separate ASM input.
Twelve short examples and one larger program. Choose a topic or work through the examples in order.
1. 1 + 2 + 3 + 4 + 5 = 15
The numbers 1 through 5 are added to 15. Return value 15 is checked as the process exit code.
FUNCTION PBMAIN() AS LONG
LOCAL sum AS LONG
#ASM
xor eax, eax ; sum = 0
mov ecx, 5 ; counter = 5
add_loop:
add eax, ecx ; sum += counter
dec ecx ; counter--
jnz add_loop ; loop until 0
mov %sum, eax ; store result
#ENDASM
FUNCTION = sum ' = 15
END FUNCTIONExpected exit code: 15
PBXB64.exe sum15-asm.pb -O2 -o example.exe .\example.exe $LASTEXITCODE
Executed natively with the V22 candidate from 2026-09-30; final-compiler verification pending.
2. Standalone ASM: return a value in EAX
global main marks the entry point. mov eax, 42 sets the result register; ret returns. This is a .asm file, not an embedded #ASM block.
// Test: Basic ASM syntax and compilation
section .data
msg db "Test", 0
text section .text
global main
main:
mov eax, 42
retExpected exit code: 42
PBXB64.exe test_basic_asm.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Canonical test case with an expected result; no new native V22 check here. Final execution pending.
3. Load a value from the data section
value dd 123 defines a 32-bit value. mov eax, [value] reads memory, while mov eax, 123 would use an immediate number. The expected exit code is 123.
// Test: ASM data sections and global symbols
section .data
value dd 123
msg db "Hello", 0
text section .text
global main
main:
mov eax, [value]
retExpected exit code: 123
PBXB64.exe test_data_sections.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Canonical test case with an expected result; no new native V22 check here. Final execution pending.
4. Macro parameters and assembly-time repetition
Step-by-step flow
- MAGIC is the constant 40; SETEAX loads that value into eax.
- .rep 4 repeats the add instruction during assembly, bringing eax to 44.
- Parameterized ADDI32 subtracts 2. PROLOG and leave surround the return value 42.
// Wave-6 slice 6-assembler: macro assembler (.macro/.endm with params,
// .equ equates, .rep/.endr repeat) — asm_macro.c port
// Expected exit code: 42
.equ MAGIC, 40
.macro PROLOG
push rbp
mov rbp, rsp
.endm
.macro SETEAX val
mov eax, \val
.endm
.macro ADDI32 reg, amount
add \reg, \amount
.endm
section .text
global main
main:
PROLOG
SETEAX MAGIC
.rep 4
add eax, 1
.endr
ADDI32 eax, -2
leave
ret
Expected exit code: 42
PBXB64.exe asm-w6_macro.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
5. Conditional copying with CMOV
Step-by-step flow
- cmp sets flags; cmovl copies 20 because 10 is less than 20.
- On the following equality check, cmovne does not move, while cmove reads DWORD value 7 from the data section.
- 7 + 35 gives 42. Each CMOV condition depends on its preceding comparison.
// Wave-6 slice 6-assembler: cmovcc + [rel] data access (encoder expansion group)
// Expected exit code: 42
section .data
seven: dd 7
section .text
global main
main:
mov eax, 10
mov ecx, 20
cmp eax, ecx
cmovl eax, ecx ; 10 < 20 -> eax = 20
cmp eax, ecx
cmovne eax, dword [rel seven] ; equal -> no move, eax stays 20
cmp eax, ecx
cmove eax, dword [rel seven] ; equal -> eax = 7
add eax, 35 ; 7 + 35 = 42
ret
Expected exit code: 42
PBXB64.exe asm-w6_enc_cmovcc.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
6. Counted loop, call and labels
Step-by-step flow
- loop decreases the counter and performs ten increments of eax.
- call add_seven changes 10 to 17; jrcxz takes its branch when rcx is 0.
- Adding 9 and 16 produces 42; unexpected intermediate values enter the failure path.
// Wave-6 slice 6-assembler: branch group (loop/loope/loopne, jrcxz/jecxz,
// short jcc, call rel32, jcxz) — encoder expansion group
// Expected exit code: 42
section .text
global main
add_seven:
add eax, 7
ret
main:
xor eax, eax
; loop: count down from 10, eax += 1 each time
mov ecx, 10
count_loop:
add eax, 1
loop count_loop ; 10 iterations -> eax = 10
cmp eax, 10
jne fail
; call rel32 through a local function
call add_seven ; eax = 17
; jrcxz: rcx is 0 here -> taken
jrcxz rcxz_taken
jmp fail
rcxz_taken:
add eax, 9 ; eax = 26
; short forward jump (rel8)
cmp eax, 26
je short_target
jmp fail
short_target:
add eax, 16 ; eax = 42
ret
fail:
mov eax, 1
ret
Expected exit code: 42
PBXB64.exe asm-w6_enc_branch.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
7. Bytes, string terminators and UTF-16 data
Step-by-step flow
- Directives store AB without a terminator and HI and YO with null terminators; .unicode emits UTF-16LE data.
- movzx reads individual bytes without sign extension; checked null bytes contribute 0.
- 65 + 66 + 77 + 78 gives 286. Only after this comparison is success value 42 returned.
// Wave-6 slice 6-assembler: .ascii/.asciz/.string/.unicode directives
// Expected exit code: 42
section .data
a_bytes: .ascii "AB" // 2 bytes, no terminator
z_bytes: .asciz "HI" // 3 bytes incl NUL
s_bytes: .string "YO" // 3 bytes incl NUL
u_bytes: .unicode "MN" // UTF-16LE: 4D 00 4E 00 00 00
section .text
global main
main:
xor ebx, ebx
movzx eax, byte [rel a_bytes] ; 'A' = 65
add ebx, eax
movzx eax, byte [rel a_bytes+1] ; 'B' = 66
add ebx, eax
movzx eax, byte [rel z_bytes+2] ; NUL terminator of .asciz
add ebx, eax ; +0
movzx eax, byte [rel s_bytes+2] ; NUL terminator of .string
add ebx, eax ; +0
movzx eax, byte [rel u_bytes] ; 0x4D = 77 ('M')
add ebx, eax
movzx eax, byte [rel u_bytes+1] ; 0x00 high byte
add ebx, eax ; +0
movzx eax, byte [rel u_bytes+2] ; 0x4E = 78 ('N')
add ebx, eax
movzx eax, byte [rel u_bytes+4] ; NUL16 low byte
add ebx, eax ; +0
; ebx = 65 + 66 + 77 + 78 = 286
cmp ebx, 286
jne fail
mov eax, 42
ret
fail:
mov eax, 1
ret
Expected exit code: 42
PBXB64.exe asm-w6_directives.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
8. Bit positions, population count and XADD
Step-by-step flow
- BTS, BT, BTR and BTC set, test, clear and toggle bits; BSF/BSR find position 3.
- POPCNT counts four set bits in 0xF0; SHRD and SHLD each produce 16 here.
- XADD changes 2 and 3 into 5 and 2. Later additions produce 42; this program requires a CPU supporting POPCNT.
// Wave-6 slice 6-assembler: bit-manipulation group (bt/bts/btr/btc, bsf/bsr,
// popcnt, shld/shrd, xadd) — encoder expansion group
// Expected exit code: 42
section .text
global main
main:
xor eax, eax
bts rax, 5 ; rax = 32
bt rax, 5 ; CF = 1
setc cl ; cl = 1
btr rax, 5 ; rax = 0, CF = 1
btc rax, 3 ; rax = 8
bsf rdx, rax ; rdx = 3 (index of lowest set bit)
bsr rdx, rax ; rdx = 3 (index of highest set bit)
mov r8, 0xF0
popcnt r9, r8 ; r9 = 4
; shrd dest, src, n: dest = (dest >> n) | (src << (64-n))
mov r10, 0
mov r11, 1
shrd r10, r11, 60 ; r10 = 0 | (1 << 4) = 16
; shld dest, src, n: dest = (dest << n) | (src >> (64-n))
mov r14, 1
xor r15, r15
shld r14, r15, 4 ; r14 = 16 | 0 = 16
cmp r14, 16
jne fail
mov r12, 2
mov r13, 3
xadd r12, r13 ; r12 = 5, r13 = 2
add eax, edx ; 8 + 3 = 11
add eax, r9d ; 15
add eax, r10d ; 31
add eax, r12d ; 36
add eax, r13d ; 38
movzx ecx, cl ; ecx = 1 (from setc cl)
add eax, ecx ; 39
add eax, 3 ; 42
ret
fail:
mov eax, 1
ret
Expected exit code: 42
PBXB64.exe asm-w6_enc_bitops.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
9. Five arguments using the Win64 convention
Step-by-step flow
- INVOKE passes the first four integer arguments through ecx, edx, r8d and r9d.
- The called code checks the fifth argument at [rsp + 40].
- main reserves 40 bytes and releases them after the call; check_args returns 42 for the expected values.
; Help-backed INVOKE contract: Win64 register and stack argument placement.
section .text
global main
check_args:
cmp ecx, 1
jne fail
cmp edx, 2
jne fail
cmp r8d, 3
jne fail
cmp r9d, 4
jne fail
cmp dword [rsp + 40], 5
jne fail
mov eax, 42
ret
fail:
mov eax, 1
ret
main:
sub rsp, 40
invoke check_args, 1, 2, 3, 4, 5
add rsp, 40
ret
Expected exit code: 42
PBXB64.exe asm-w7_invoke_abi.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
10. GAS directives with Intel operands
Step-by-step flow
- .intel_syntax noprefix combines Intel operands with GAS directives and COFF symbol declarations.
- Four RIP-relative byte loads read 65, 66, 77 and 78; the sum must be 286.
- A semicolon separates mov eax, 40 from add eax, 2. The resulting exit code is 42.
.intel_syntax noprefix
/* Wave-9 GAS Intel-syntax acceptance contract for the runtime .S closure
work: block comments, ';' statement separators, the COFF symbol
definition family, and quoted .section flags. The same file assembles
under host GNU as, so the script lane compares emitted COFF symbol
records against the host reference. Expected exit code: 42. */
.section .rdata,"dr"
table_bytes: .byte 65, 66, 77, 78
.text
.globl main
.def main; .scl 2; .type 32; .endef
main:
xor ebx, ebx
movzx eax, byte ptr [rip+table_bytes]
add ebx, eax
movzx eax, byte ptr [rip+table_bytes+1]
add ebx, eax
movzx eax, byte ptr [rip+table_bytes+2]
add ebx, eax
movzx eax, byte ptr [rip+table_bytes+3]
add ebx, eax
# 65 + 66 + 77 + 78 = 286
cmp ebx, 286
jne fail
mov eax, 40; add eax, 2
ret
fail:
mov eax, 1; ret
Expected exit code: 42
PBXB64.exe asm-w9_gas_intel_syntax.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
11. Branch within a resumed code section
Step-by-step flow
- jmp targets a label even though a data section appears between two text sections.
- The data byte belongs to .data and is never executed as code.
- After resuming .text, target sets eax to 48 and returns.
.intel_syntax noprefix
/* A branch resumes in .text after intervening .data bytes. The object
builder owns the section-relative fixup. Expected exit code: 48. */
.text
.globl main
main:
jmp target
.data
.byte 1
.text
target:
mov eax, 48
ret
Expected exit code: 48
PBXB64.exe asm-w9_interleaved_section_relax.asm -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
12. Describe an ASM block's effects
Step-by-step flow
- #ASM EFFECTS explicitly describes register, flag and memory effects.
- STACK PRESERVE and CONTROL FALLTHROUGH describe the block's expected behavior.
- mov rax, rax leaves the value unchanged. These declarations do not automatically prove arbitrary ASM code correct.
' Explicit ASM EFFECTS metadata is opt-in. Ordinary #ASM remains opaque.
FUNCTION PBMAIN() AS LONG
#ASM EFFECTS
READ RAX
WRITE RAX
CLOBBER R10
FLAGS_WRITE ZF, CF
MEMORY READWRITE
STACK PRESERVE
CONTROL FALLTHROUGH
FEATURES SSE2
CODE
mov rax, rax
#ENDASM
FUNCTION = 0
END FUNCTION
Expected exit code: 0
PBXB64.exe asm-test_inline_asm_effects.pb -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
Larger example: Two ASM blocks using BASIC variables
Step-by-step flow
- {a}, {b} and {c} bind memory operands to addresses of local BASIC variables.
- The first block computes 7 + 100 + 5 = 112 into c; the second doubles c into a = 224.
- DWORD operations affect 32 bits and QUAD access affects 64 bits. These values are positive and the LONG variable was fully initialized; BASIC checks c = 112, a = 224 and b = 100.
' Wave-1 inline-asm slice: `{ var }` binding plus multi-block temp-name race.
' Two inline asm blocks read and write LOCAL variables via { var }. Each
' bound variable's address is passed in a Win64 argument register (rcx, rdx,
' r8, r9 in order of first appearance within the block) and { var } becomes
' a memory operand on that register. Both blocks are assembled to per-block
' unique temp objects (_inline_asm_0.obj, _inline_asm_1.obj, ...) so neither
' overwrites the other. Expected exit code: 0
FUNCTION PBMAIN() AS LONG
LOCAL a AS LONG
LOCAL b AS QUAD
LOCAL c AS DWORD
a = 7
b = 100
c = 5
' Block 1 (#ASM): c = a + b + c = 7 + 100 + 5 = 112
#ASM
mov eax, {a}
add rax, {b}
add eax, {c}
mov {c}, eax
#ENDASM
' Block 2 (#ASM): a = c * 2 = 224
#ASM
mov eax, {c}
add eax, eax
mov {a}, eax
#ENDASM
IF c = 112 AND a = 224 AND b = 100 THEN
PRINT "inline asm binding OK"
PBMAIN = 0
ELSE
PRINT "inline asm binding FAIL: a="; a; " b="; b; " c="; c
PBMAIN = 1
END IF
END FUNCTION
Expected exit code: 0
Canonical output verification pattern (regex)
inline asm binding OK
PBXB64.exe asm-test_inline_asm_binding.pb -O2 -o example.exe .\example.exe $LASTEXITCODE
Expected-result contract from tests/validate.toml. This website expansion adds no new native compiler verification. Execution with the qualified final compiler remains pending.
All sources for this tutorial
Each example has its own folder; the README lists compiler commands. Included support files retain matching paths.
Download all 13 examples as ZIPBridging
global exposes a symbol; extern refers to a symbol defined elsewhere. A joint compiler invocation connects ASM and BASIC. Data width and memory layout remain part of the contract.
Explore complete source pairs