Files
whitemagic/openspec/changes/inject-and-assemble/specs/text-assembler/spec.md
T
2026-07-21 22:30:10 +02:00

3.5 KiB

ADDED Requirements

Requirement: AsmBuilder assembles x86 instruction text to byte array

AsmBuilder.Assemble(string source) parses x86 assembly text and returns the corresponding byte[] machine code.

Scenario: single instruction

  • WHEN AsmBuilder.Assemble("nop") is called
  • THEN the result is [0x90]

Scenario: multiple instructions

  • WHEN AsmBuilder.Assemble("pushad\npopad") is called
  • THEN the result is [0x60, 0x61]

Scenario: instruction with immediate operand

  • WHEN AsmBuilder.Assemble("mov eax, 1") is called
  • THEN the result is [0xB8, 0x01, 0x00, 0x00, 0x00]

Requirement: AsmBuilder supports register operands

Supported registers: eax, ecx, edx, ebx, esp, ebp, esi, edi (and 8-bit: al, cl, dl, bl, ah, ch, dh, bh).

Scenario: register-to-register move

  • WHEN AsmBuilder.Assemble("mov eax, ecx") is called
  • THEN the result is [0x89, 0xC8] (mov eax, ecx encoding)

Scenario: register encoding

  • WHEN registers are used in instructions
  • THEN each register maps to its correct 3-bit encoding (eax=0, ecx=1, edx=2, ebx=3, esp=4, ebp=5, esi=6, edi=7)

Requirement: AsmBuilder supports labels and jumps

Labels are defined with @name: and referenced with jmp @name or je @name. Forward and backward references are resolved in a second pass.

Scenario: forward jump

  • WHEN AsmBuilder.Assemble("jmp @skip\nnop\n@skip:\nret") is called
  • THEN the jump skips exactly over the nop (2 bytes) and lands on ret

Scenario: backward jump

  • WHEN AsmBuilder.Assemble("@loop:\nnop\njmp @loop") is called
  • THEN the jump targets the earlier label correctly

Scenario: multiple labels

  • WHEN multiple labels are used in one source
  • THEN each label resolves to its correct byte offset

Requirement: AsmBuilder SetPassLimit controls iteration

AsmBuilder.SetPassLimit(int limit) sets the maximum number of assembly passes for label resolution. Default is 10. If the limit is exceeded before all labels resolve, InvalidOperationException is thrown.

Scenario: default pass limit

  • WHEN no SetPassLimit is called
  • THEN the assembler uses 10 passes maximum

Scenario: custom pass limit

  • WHEN SetPassLimit(20) is called
  • THEN the assembler uses 20 passes maximum

Scenario: pass limit exceeded

  • WHEN forward references cannot resolve within the pass limit
  • THEN InvalidOperationException is thrown with label resolution details

Requirement: AsmBuilder reports clear errors

Unknown instructions, missing operands, and invalid register names produce ArgumentException with the line number and offending text.

Scenario: unknown instruction

  • WHEN AsmBuilder.Assemble("xyzw") is called
  • THEN ArgumentException is thrown mentioning line 1 and "xyzw"

Scenario: missing operand

  • WHEN AsmBuilder.Assemble("mov") is called (no operands)
  • THEN ArgumentException is thrown mentioning missing operand

Requirement: AsmBuilder supported instruction set

The following x86 instructions are supported:

  • Data movement: mov, push, pop, pushad, popad, lea
  • Arithmetic: add, sub, inc, dec, xor, and, or, cmp, test
  • Control flow: jmp, je, jne, call, ret, nop, hlt

Scenario: all instructions produce valid bytes

  • WHEN each supported instruction is assembled individually
  • THEN it produces the correct x86 machine code encoding