# SX VM extensions Each `*.ml` file here is a VM extension — a first-class OCaml module that registers specialized bytecode opcodes with `Sx_vm_extensions`. See [`plans/sx-vm-opcode-extension.md`](../../../../plans/sx-vm-opcode-extension.md) for the design. ## Pattern ```ocaml (* lib/extensions/myport.ml *) open Sx_types type Sx_vm_extension.extension_state += MyportState of { ... } module M : Sx_vm_extension.EXTENSION = struct let name = "myport" let init () = MyportState { ... } let opcodes _st = [ (id, "myport.OP_NAME", handler); ... ] end let register () = Sx_vm_extensions.register (module M) ``` Then call `Myport.register ()` once at startup from any binary that should have the extension loaded. ## Opcode-ID allocation Range 200-247 (per `Sx_vm_extensions.extension_min` / `extension_max`). Conventions: | Range | Use | |---------|-------------------------------------------------------------------------| | 200-209 | reserved for `lib/guest/vm/` shared opcodes (chiselled out on 2nd use) | | 210-219 | inline test extensions defined in `bin/run_tests.ml` | | 220-229 | this directory's `test_ext` (the canonical template) | | 230-247 | first-come-first-served by language ports (Erlang first) | When a port claims a contiguous block, document it in the table above. The registry rejects collisions at startup with a loud error — there is no silent shadowing. ## Naming Always prefix opcode names with the extension name plus a dot: `myport.OP_`. The prefix is a hard convention so that multiple extensions can share the global opcode-name namespace cleanly. ## State `extension_state` is an extensible variant. Add your case (e.g. `MyportState of { ... }`) at the top of your file, return it from `init`, and pattern-match it inside your handlers. Other extensions cannot see your state — the variant case is private to your module. ## Testing `test_ext.ml` is the canonical worked example. `bin/run_tests.ml` calls `Test_ext.register ()`, then drives bytecode that exercises the opcodes end-to-end (push, double, dispatch, disassemble, invocation counter). Mirror this shape when adding a real port's extension. ## Build wiring `lib/dune` has `(include_subdirs unqualified)`, so any `.ml` you drop in here is automatically part of the `sx` library. Module name follows the filename verbatim (`test_ext.ml` → `Test_ext`).