"""
Z80 Code Generator.

Translates IR to Z80 assembly code.
"""

from dataclasses import dataclass, field
from typing import Optional

from uada80.ir import (
    IRModule,
    IRFunction,
    BasicBlock,
    IRInstr,
    OpCode,
    IRType,
    IRValue,
    VReg,
    Immediate,
    Label,
    MemoryLocation,
    ir_type_size,
)


@dataclass
class Z80Register:
    """Z80 register."""

    name: str
    is_16bit: bool = False


# Z80 registers (lowercase for upeepz80 compatibility)
REG_A = Z80Register("a")
REG_B = Z80Register("b")
REG_C = Z80Register("c")
REG_D = Z80Register("d")
REG_E = Z80Register("e")
REG_H = Z80Register("h")
REG_L = Z80Register("l")
REG_BC = Z80Register("bc", True)
REG_DE = Z80Register("de", True)
REG_HL = Z80Register("hl", True)
REG_SP = Z80Register("sp", True)
REG_IX = Z80Register("ix", True)
REG_IY = Z80Register("iy", True)


@dataclass
class RegisterAllocation:
    """Mapping of virtual registers to Z80 registers or stack slots."""

    vreg_to_reg: dict[int, Z80Register] = field(default_factory=dict)
    vreg_to_stack: dict[int, int] = field(default_factory=dict)  # stack offset
    stack_size: int = 0


class Z80CodeGen:
    """Z80 code generator."""

    def __init__(self, emit_inline_runtime: bool = False) -> None:
        self.output: list[str] = []
        self.reg_alloc: Optional[RegisterAllocation] = None
        self.current_function: Optional[IRFunction] = None
        self.emit_inline_runtime = emit_inline_runtime  # If False, use libada.lib
        self.runtime_deps: set[str] = set()  # Track needed runtime routines
        self.module: Optional[IRModule] = None

    def _mangle_symbol(self, name: str) -> str:
        """Mangle a user symbol name to avoid Z80 mnemonic collisions.

        Prefixes user symbols with '_' to avoid collisions with Z80 instruction
        mnemonics like ADD, SUB, INC, DEC, CALL, RET, PUSH, POP, etc.
        Runtime symbols (already starting with '_') are left unchanged.
        """
        if name.startswith("_"):
            return name  # Already prefixed (runtime symbol or internal)
        return f"_{name}"

    def generate(self, module: IRModule) -> str:
        """Generate Z80 assembly for an IR module."""
        self.output = []
        self.runtime_deps = set()
        self.module = module

        # Collect runtime deps from module (set by lowering)
        if module.runtime_deps:
            self.runtime_deps.update(module.runtime_deps)

        # Header
        self._emit("; Generated by uada80 Ada compiler")
        self._emit("; Target: Z80")
        if not self.emit_inline_runtime:
            self._emit("; Link with: ul80 program.rel -l libada.lib")
        self._emit("")
        self._emit("    .Z80    ; Enable Z80 instruction set")
        self._emit("")

        # Generate startup code (entry point for CP/M .COM files)
        # This must come first in CSEG so it's at address 0100H
        # Use the first function as the main entry point
        main_entry = module.functions[0].name if module.functions else None
        if main_entry:
            self._emit("; CP/M entry point - execution starts here")
            self._emit("    CSEG")
            self._emit("_start:")
            self._emit_instr("call", self._mangle_symbol(main_entry))
            self._emit_instr("jp", "0")
            self._emit("; Exit to CP/M")
            self._emit("")

        # Generate globals
        if module.globals:
            self._emit("; Global variables")
            self._emit("    DSEG")
            for name, (ir_type, size) in module.globals.items():
                self._emit(f"{self._mangle_symbol(name)}:")
                self._emit(f"    DS {size}")
            self._emit("")

        # Generate string literals
        if module.string_literals:
            self._emit("; String literals")
            self._emit("    CSEG")
            for label, value in module.string_literals.items():
                escaped = value.replace('"', '\\"')
                self._emit(f'{label}:')
                self._emit(f'    DB "{escaped}", 0')
            self._emit("")

        # Generate float64 constants
        if module.float64_constants:
            self._emit("; Float64 constants (IEEE 754 double precision)")
            self._emit("    CSEG")
            for label, value_bytes in module.float64_constants.items():
                # Emit 8 bytes in little-endian order
                byte_str = ", ".join(f"0{b:02X}H" for b in value_bytes)
                self._emit(f'{label}:')
                self._emit(f'    DB {byte_str}')
            self._emit("")

        # Generate vtables for tagged types
        if module.vtables:
            self._emit("; Vtables for tagged types (OOP dispatch)")
            self._emit("    CSEG")
            for vtable_name, proc_names in module.vtables.items():
                self._emit(f"{vtable_name}:")
                for proc_name in proc_names:
                    self._emit(f"    DW {proc_name}  ; slot {proc_names.index(proc_name)}")
            self._emit("")

        # Generate code
        self._emit("    CSEG")
        self._emit("")

        for func in module.functions:
            self._generate_function(func)

        if self.emit_inline_runtime:
            # Legacy mode: emit all runtime code inline (for standalone .com files)
            self._generate_heap_runtime()
            self._generate_fixed_point_runtime()
            self._generate_exc_runtime()
            self._generate_string_runtime()
            self._generate_math_runtime()
            self._generate_text_io_runtime()
            self._generate_dispatch_runtime()
            self._generate_fixed_point_runtime()
            self._generate_float48_runtime()
            self._generate_tasking_runtime()
            self._generate_c_interface_runtime()
            self._generate_container_runtime()
            self._generate_stream_runtime()
            self._generate_float48_externals()
            self._generate_float64_externals()
            self._generate_libada_externals()
        else:
            # Library mode: emit EXTRN declarations for needed routines
            self._generate_runtime_externs()

        self._emit("")
        self._emit("    END")

        return "\n".join(self.output)

    def _generate_heap_runtime(self) -> None:
        """Generate simple heap allocator runtime."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Heap Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Heap pointer (grows upward from _heap_start)")
        self._emit("_heap_ptr:")
        self._emit("    .dw _heap_start")
        self._emit("")
        self._emit("; _heap_alloc: Allocate memory from heap")
        self._emit("; Input: hl = size to allocate")
        self._emit("; Output: hl = pointer to allocated memory (0 if failed)")
        self._emit("_heap_alloc:")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    ; Load current heap pointer")
        self._emit("    ld de, (_heap_ptr)")
        self._emit("    ; Save current pointer as return value")
        self._emit("    push de")
        self._emit("    ; Advance heap pointer by size (de += hl)")
        self._emit("    add hl, de")
        self._emit("    ; Check for overflow (hl >= _heap_end)")
        self._emit("    ld bc, _heap_end")
        self._emit("    or a")
        self._emit("    sbc hl, bc")
        self._emit("    add hl, bc  ; restore hl")
        self._emit("    jr nc, _heap_alloc_fail")
        self._emit("    ; Store new heap pointer")
        self._emit("    ld (_heap_ptr), hl")
        self._emit("    ; Return saved pointer")
        self._emit("    pop hl")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("_heap_alloc_fail:")
        self._emit("    ; Return null (0)")
        self._emit("    pop hl  ; discard saved pointer")
        self._emit("    ld hl, 0")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("")
        self._emit("; _heap_free: Free allocated memory")
        self._emit("; Input: hl = pointer to free")
        self._emit("; Note: Simple bump allocator doesn't actually reclaim memory")
        self._emit("; A real implementation would use a free list or similar")
        self._emit("_heap_free:")
        self._emit("    ; For a bump allocator, we can't easily reclaim memory")
        self._emit("    ; Just validate the pointer is in the heap range")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    ; Check if hl >= _heap_start")
        self._emit("    ld de, _heap_start")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de  ; restore hl")
        self._emit("    jr c, _heap_free_done  ; hl < _heap_start")
        self._emit("    ; Check if hl < _heap_ptr (current allocation)")
        self._emit("    ld de, (_heap_ptr)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de  ; restore hl")
        self._emit("    jr nc, _heap_free_done  ; hl >= _heap_ptr")
        self._emit("    ; Valid pointer - in a real allocator we'd add to free list")
        self._emit("    ; For now, just mark the memory as zero (helps debug)")
        self._emit("    ld (hl), 0")
        self._emit("    inc hl")
        self._emit("    ld (hl), 0")
        self._emit("_heap_free_done:")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("")
        self._emit("; Heap area (adjust size as needed)")
        self._emit("_heap_start:")
        self._emit("    .ds 2048  ; 2KB heap")
        self._emit("_heap_end:")
        self._emit("")

    def _generate_exc_runtime(self) -> None:
        """Generate exception handling runtime."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Exception Handling Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Exception state variables")
        self._emit("_exc_handler:")
        self._emit("    .dw 0  ; current handler chain head (0 = none)")
        self._emit("_exc_current:")
        self._emit("    .dw 0  ; currently raised exception ID")
        self._emit("_exc_message:")
        self._emit("    .dw 0  ; exception message pointer (0 = none)")
        self._emit("")
        self._emit("; _exc_do_raise: Process a raised exception")
        self._emit("; Expects _exc_current and _exc_message to be set")
        self._emit("; Searches handler chain for matching handler")
        self._emit("_exc_do_raise:")
        self._emit("    ; Get current handler")
        self._emit("    ld hl, (_exc_handler)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _exc_unhandled  ; no handler - halt")
        self._emit("")
        self._emit("_exc_check_handler:")
        self._emit("    ; hl points to handler frame")
        self._emit("    ; Frame: +0=prev, +2=sp, +4=addr, +6=exc_id")
        self._emit("    push hl  ; save frame pointer")
        self._emit("")
        self._emit("    ; Get exception ID this handler catches (+6)")
        self._emit("    ld de, 6")
        self._emit("    add hl, de")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("")
        self._emit("    ; Check if handler catches all (ID = 0)")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _exc_handler_match  ; catches all")
        self._emit("")
        self._emit("    ; Check if handler ID matches raised exception")
        self._emit("    ld hl, (_exc_current)")
        self._emit("    or a  ; clear carry")
        self._emit("    sbc hl, de")
        self._emit("    jr z, _exc_handler_match  ; ID matches")
        self._emit("")
        self._emit("    ; No match - try previous handler")
        self._emit("    pop hl  ; restore frame pointer")
        self._emit("    ; Get previous handler (+0)")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("    ex de, hl  ; hl = previous handler")
        self._emit("")
        self._emit("    ; Check if there's a previous handler")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _exc_check_handler  ; try previous")
        self._emit("    jr _exc_unhandled  ; no more handlers")
        self._emit("")
        self._emit("_exc_handler_match:")
        self._emit("    ; Found matching handler")
        self._emit("    pop hl  ; restore frame pointer")
        self._emit("")
        self._emit("    ; Get previous handler and update chain")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("    inc hl")
        self._emit("    ld (_exc_handler), de")
        self._emit("")
        self._emit("    ; Get saved sp (+2)")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("    inc hl")
        self._emit("")
        self._emit("    ; Get handler address (+4)")
        self._emit("    ld a, (hl)")
        self._emit("    inc hl")
        self._emit("    ld h, (hl)")
        self._emit("    ld l, a  ; hl = handler address")
        self._emit("")
        self._emit("    ; Restore sp and jump to handler")
        self._emit("    ex de, hl  ; de = handler addr, hl = saved sp")
        self._emit("    ld sp, hl")
        self._emit("    ex de, hl  ; hl = handler addr")
        self._emit("    jp (hl)   ; jump to handler")
        self._emit("")
        self._emit("_exc_unhandled:")
        self._emit("    ; No handler found - halt with error")
        self._emit("    ; Could output error message here")
        self._emit("    halt")
        self._emit("")
        self._emit("; Predefined exception raise routines")
        self._emit("; These have fixed exception IDs matching ASTLowering.PREDEFINED_EXCEPTIONS")
        self._emit("_raise_constraint_error:")
        self._emit("    ; Constraint_Error = ID 1")
        self._emit("    ld hl, 1")
        self._emit("    ld (_exc_current), hl")
        self._emit("    ld hl, 0")
        self._emit("    ld (_exc_message), hl")
        self._emit("    call _exc_do_raise")
        self._emit("")
        self._emit("_raise_program_error:")
        self._emit("    ; Program_Error = ID 2")
        self._emit("    ld hl, 2")
        self._emit("    ld (_exc_current), hl")
        self._emit("    ld hl, 0")
        self._emit("    ld (_exc_message), hl")
        self._emit("    call _exc_do_raise")
        self._emit("")
        self._emit("_raise_storage_error:")
        self._emit("    ; Storage_Error = ID 3")
        self._emit("    ld hl, 3")
        self._emit("    ld (_exc_current), hl")
        self._emit("    ld hl, 0")
        self._emit("    ld (_exc_message), hl")
        self._emit("    call _exc_do_raise")
        self._emit("")
        self._emit("_raise_tasking_error:")
        self._emit("    ; Tasking_Error = ID 4")
        self._emit("    ld hl, 4")
        self._emit("    ld (_exc_current), hl")
        self._emit("    ld hl, 0")
        self._emit("    ld (_exc_message), hl")
        self._emit("    call _exc_do_raise")
        self._emit("")
        self._emit("_raise_assertion_error:")
        self._emit("    ; Assertion_Error = ID 5")
        self._emit("    ld hl, 5")
        self._emit("    ld (_exc_current), hl")
        self._emit("    ld hl, 0")
        self._emit("    ld (_exc_message), hl")
        self._emit("    call _exc_do_raise")
        self._emit("")
        self._emit("; Exception occurrence information")
        self._emit("; _exc_occurrence structure (8 bytes):")
        self._emit(";   +0: exception ID (2 bytes)")
        self._emit(";   +2: message pointer (2 bytes, 0 if none)")
        self._emit(";   +4: location PC at raise (2 bytes)")
        self._emit(";   +6: reserved (2 bytes)")
        self._emit("")
        self._emit("; Exception occurrence field offsets")
        self._emit("EXC_OCC_ID       .equ 0    ; exception ID")
        self._emit("EXC_OCC_MSG      .equ 2    ; message pointer")
        self._emit("EXC_OCC_LOC      .equ 4    ; location PC")
        self._emit("EXC_OCC_RESERVED .equ 6    ; reserved")
        self._emit("EXC_OCC_SIZE     .equ 8    ; total size")
        self._emit("")
        self._emit("_exc_occurrence:")
        self._emit("    .ds EXC_OCC_SIZE")
        self._emit("")
        self._emit("; Exception name table")
        self._emit("; ID -> name string pointer")
        self._emit("_exc_name_table:")
        self._emit("    .dw _exc_name_unknown      ; 0 = unknown")
        self._emit("    .dw _exc_name_constraint   ; 1 = Constraint_Error")
        self._emit("    .dw _exc_name_program      ; 2 = Program_Error")
        self._emit("    .dw _exc_name_storage      ; 3 = Storage_Error")
        self._emit("    .dw _exc_name_tasking      ; 4 = Tasking_Error")
        self._emit("    .dw _exc_name_assertion    ; 5 = Assertion_Error")
        self._emit("_exc_name_table_end:")
        self._emit("")
        self._emit("_exc_name_unknown:")
        self._emit("    .db 7, 'Unknown', 0")
        self._emit("_exc_name_constraint:")
        self._emit("    .db 16, 'Constraint_Error', 0")
        self._emit("_exc_name_program:")
        self._emit("    .db 13, 'Program_Error', 0")
        self._emit("_exc_name_storage:")
        self._emit("    .db 13, 'Storage_Error', 0")
        self._emit("_exc_name_tasking:")
        self._emit("    .db 13, 'Tasking_Error', 0")
        self._emit("_exc_name_assertion:")
        self._emit("    .db 15, 'Assertion_Error', 0")
        self._emit("")
        self._emit("; _exc_get_name: Get exception name string")
        self._emit("; Input: hl = exception ID")
        self._emit("; Output: hl = pointer to name string (Ada string with length prefix)")
        self._emit("_exc_get_name:")
        self._emit("    ; Validate ID is in range")
        self._emit("    ld de, 6      ; table size")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de    ; restore hl")
        self._emit("    jr nc, _exc_get_name_unknown  ; ID >= 6")
        self._emit("    ; Look up in table: addr = table + ID * 2")
        self._emit("    add hl, hl    ; hl = ID * 2")
        self._emit("    ld de, _exc_name_table")
        self._emit("    add hl, de    ; hl = &table[ID]")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)    ; de = name pointer")
        self._emit("    ex de, hl     ; hl = name pointer")
        self._emit("    ret")
        self._emit("_exc_get_name_unknown:")
        self._emit("    ld hl, _exc_name_unknown")
        self._emit("    ret")
        self._emit("")
        self._emit("; _exc_get_message: Get exception message string")
        self._emit("; Input: none (uses _exc_message)")
        self._emit("; Output: hl = pointer to message string (or 0 if none)")
        self._emit("_exc_get_message:")
        self._emit("    ld hl, (_exc_message)")
        self._emit("    ret")
        self._emit("")
        self._emit("; _exc_get_info: Get full exception information string")
        self._emit("; Input: none (uses current exception state)")
        self._emit("; Output: hl = pointer to info string in _exc_info_buffer")
        self._emit("; Format: 'Exception_Name: Message' or just 'Exception_Name'")
        self._emit("_exc_get_info:")
        self._emit("    ; Get exception name")
        self._emit("    ld hl, (_exc_current)")
        self._emit("    call _exc_get_name")
        self._emit("    ; hl = name string ptr, first byte is length")
        self._emit("    push hl       ; save name ptr")
        self._emit("    ; Copy name to buffer")
        self._emit("    ld de, _exc_info_buffer + 1")
        self._emit("    ld a, (hl)    ; get length")
        self._emit("    ld b, a       ; save total length")
        self._emit("    inc hl        ; skip length byte")
        self._emit("_exc_info_copy_name:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _exc_info_name_done")
        self._emit("    ld (de), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _exc_info_copy_name")
        self._emit("_exc_info_name_done:")
        self._emit("    pop hl        ; discard name ptr")
        self._emit("    ; Check if there's a message")
        self._emit("    ld hl, (_exc_message)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _exc_info_no_msg")
        self._emit("    ; Add ': ' separator")
        self._emit("    ld a, ':'")
        self._emit("    ld (de), a")
        self._emit("    inc de")
        self._emit("    inc b")
        self._emit("    ld a, ' '")
        self._emit("    ld (de), a")
        self._emit("    inc de")
        self._emit("    inc b")
        self._emit("    ; Copy message (hl = message ptr)")
        self._emit("_exc_info_copy_msg:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _exc_info_done")
        self._emit("    ld (de), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    inc b")
        self._emit("    jr _exc_info_copy_msg")
        self._emit("_exc_info_no_msg:")
        self._emit("_exc_info_done:")
        self._emit("    ; Store total length")
        self._emit("    ld a, b")
        self._emit("    ld (_exc_info_buffer), a")
        self._emit("    ; Null terminate")
        self._emit("    xor a")
        self._emit("    ld (de), a")
        self._emit("    ; Return buffer pointer")
        self._emit("    ld hl, _exc_info_buffer")
        self._emit("    ret")
        self._emit("")
        self._emit("_exc_info_buffer:")
        self._emit("    .ds 80        ; buffer for exception info string")
        self._emit("")
        self._emit("; _exc_save_occurrence: Save current exception to occurrence record")
        self._emit("; Input: none (uses current exception state)")
        self._emit("; Output: hl = pointer to occurrence record")
        self._emit("_exc_save_occurrence:")
        self._emit("    ; Use ix for indexed access to occurrence record")
        self._emit("    ld ix, _exc_occurrence")
        self._emit("    ; Store exception ID")
        self._emit("    ld hl, (_exc_current)")
        self._emit("    ld (ix+EXC_OCC_ID), l")
        self._emit("    ld (ix+EXC_OCC_ID+1), h")
        self._emit("    ; Store message pointer")
        self._emit("    ld hl, (_exc_message)")
        self._emit("    ld (ix+EXC_OCC_MSG), l")
        self._emit("    ld (ix+EXC_OCC_MSG+1), h")
        self._emit("    ; Location not tracked in simple implementation")
        self._emit("    xor a")
        self._emit("    ld (ix+EXC_OCC_LOC), a")
        self._emit("    ld (ix+EXC_OCC_LOC+1), a")
        self._emit("    ld (ix+EXC_OCC_RESERVED), a")
        self._emit("    ld (ix+EXC_OCC_RESERVED+1), a")
        self._emit("    ; Return pointer to occurrence")
        self._emit("    ld hl, _exc_occurrence")
        self._emit("    ret")
        self._emit("")
        self._emit("; _exc_identity: Get exception identity from occurrence")
        self._emit("; Input: hl = occurrence pointer")
        self._emit("; Output: hl = exception ID")
        self._emit("_exc_identity:")
        self._emit("    push hl")
        self._emit("    pop ix        ; ix = occurrence pointer")
        self._emit("    ld l, (ix+EXC_OCC_ID)")
        self._emit("    ld h, (ix+EXC_OCC_ID+1)")
        self._emit("    ret")
        self._emit("")
        self._emit("; _exc_get_occurrence_msg: Get message from occurrence")
        self._emit("; Input: hl = occurrence pointer")
        self._emit("; Output: hl = message pointer (0 if none)")
        self._emit("_exc_get_occurrence_msg:")
        self._emit("    push hl")
        self._emit("    pop ix        ; ix = occurrence pointer")
        self._emit("    ld l, (ix+EXC_OCC_MSG)")
        self._emit("    ld h, (ix+EXC_OCC_MSG+1)")
        self._emit("    ret")
        self._emit("")

    def _generate_string_runtime(self) -> None:
        """Generate string conversion runtime (Image/Value)."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; String Conversion Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Static buffer for Integer'Image result")
        self._emit("_int_str_buf:")
        self._emit("    .ds 8  ; -32768 is 6 chars + sign + null")
        self._emit("")
        self._emit("; _int_to_str: Convert signed 16-bit integer to string")
        self._emit("; Input: Stack: value (16-bit signed)")
        self._emit("; Output: hl = pointer to null-terminated string")
        self._emit("_int_to_str:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get value from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Point de to end of buffer")
        self._emit("    ld de, _int_str_buf + 7")
        self._emit("    xor a")
        self._emit("    ld (de), a  ; null terminator")
        self._emit("    dec de")
        self._emit("    ; Check for negative")
        self._emit("    bit 7, h")
        self._emit("    push af  ; save sign flag")
        self._emit("    jr z, _its_loop")
        self._emit("    ; Negate hl")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("_its_loop:")
        self._emit("    ; Divide hl by 10, remainder in A")
        self._emit("    push de")
        self._emit("    ld de, 10")
        self._emit("    call _div16_mod  ; hl = hl/10, a = remainder")
        self._emit("    pop de")
        self._emit("    add a, '0'")
        self._emit("    ld (de), a")
        self._emit("    dec de")
        self._emit("    ; Check if hl is zero")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _its_loop")
        self._emit("    ; Add minus sign if needed")
        self._emit("    pop af  ; restore sign flag")
        self._emit("    jr z, _its_done")
        self._emit("    ld a, '-'")
        self._emit("    ld (de), a")
        self._emit("    dec de")
        self._emit("_its_done:")
        self._emit("    ; Return pointer to start of string")
        self._emit("    inc de")
        self._emit("    ex de, hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _str_to_int: Convert string to signed 16-bit integer")
        self._emit("; Input: Stack: pointer to null-terminated string")
        self._emit("; Output: hl = value")
        self._emit("_str_to_int:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get string pointer from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Result in de")
        self._emit("    ld de, 0")
        self._emit("    ; Check for leading minus")
        self._emit("    ld a, (hl)")
        self._emit("    ld c, 0  ; sign flag")
        self._emit("    cp '-'")
        self._emit("    jr nz, _sti_skip_ws")
        self._emit("    ld c, 1  ; negative")
        self._emit("    inc hl")
        self._emit("_sti_skip_ws:")
        self._emit("    ; Skip leading spaces")
        self._emit("    ld a, (hl)")
        self._emit("    cp ' '")
        self._emit("    jr nz, _sti_loop")
        self._emit("    inc hl")
        self._emit("    jr _sti_skip_ws")
        self._emit("_sti_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _sti_done")
        self._emit("    cp '0'")
        self._emit("    jr c, _sti_done")
        self._emit("    cp '9'+1")
        self._emit("    jr nc, _sti_done")
        self._emit("    ; de = de * 10 + (A - '0')")
        self._emit("    sub '0'")
        self._emit("    push hl")
        self._emit("    push bc")
        self._emit("    ; de * 10")
        self._emit("    ex de, hl")
        self._emit("    add hl, hl  ; *2")
        self._emit("    push hl")
        self._emit("    add hl, hl  ; *4")
        self._emit("    add hl, hl  ; *8")
        self._emit("    pop de")
        self._emit("    add hl, de  ; *10")
        self._emit("    ; Add digit")
        self._emit("    ld e, a")
        self._emit("    ld d, 0")
        self._emit("    add hl, de")
        self._emit("    ex de, hl")
        self._emit("    pop bc")
        self._emit("    pop hl")
        self._emit("    inc hl")
        self._emit("    jr _sti_loop")
        self._emit("_sti_done:")
        self._emit("    ; Check sign and negate if needed")
        self._emit("    ld a, c")
        self._emit("    or a")
        self._emit("    jr z, _sti_ret")
        self._emit("    ; Negate de")
        self._emit("    xor a")
        self._emit("    sub e")
        self._emit("    ld e, a")
        self._emit("    sbc a, a")
        self._emit("    sub d")
        self._emit("    ld d, a")
        self._emit("_sti_ret:")
        self._emit("    ex de, hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _strcat: Concatenate two null-terminated strings")
        self._emit("; Input: Stack: str1_ptr (16-bit), str2_ptr (16-bit)")
        self._emit("; Output: hl = pointer to new string (in static buffer)")
        self._emit("; Note: Uses static buffer, not reentrant")
        self._emit("_strcat:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    ; Get str1 pointer from stack (at ix+6)")
        self._emit("    ld l, (ix+6)")
        self._emit("    ld h, (ix+7)")
        self._emit("    ; Point de to concatenation buffer")
        self._emit("    ld de, _strcat_buf")
        self._emit("    push de  ; save result pointer")
        self._emit("    ; Copy str1")
        self._emit("_strcat_copy1:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _strcat_copy2_start")
        self._emit("    ld (de), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _strcat_copy1")
        self._emit("_strcat_copy2_start:")
        self._emit("    ; Get str2 pointer from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Copy str2")
        self._emit("_strcat_copy2:")
        self._emit("    ld a, (hl)")
        self._emit("    ld (de), a")
        self._emit("    or a")
        self._emit("    jr z, _strcat_done")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _strcat_copy2")
        self._emit("_strcat_done:")
        self._emit("    pop hl  ; return pointer to buffer")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; Static buffer for string concatenation")
        self._emit("_strcat_buf:")
        self._emit("    .ds 256  ; max concatenated string length")
        self._emit("")
        self._emit("; _str_len: Get length of null-terminated string")
        self._emit("; Input: hl = pointer to string")
        self._emit("; Output: hl = length (not including null)")
        self._emit("_str_len:")
        self._emit("    push bc")
        self._emit("    ld bc, 0")
        self._emit("_str_len_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _str_len_done")
        self._emit("    inc hl")
        self._emit("    inc bc")
        self._emit("    jr _str_len_loop")
        self._emit("_str_len_done:")
        self._emit("    ld h, b")
        self._emit("    ld l, c")
        self._emit("    pop bc")
        self._emit("    ret")
        self._emit("")
        self._emit("; _str_cmp: Compare two null-terminated strings")
        self._emit("; Input: Stack: str1_ptr (16-bit), str2_ptr (16-bit)")
        self._emit("; Output: hl = negative if str1 < str2, 0 if equal, positive if str1 > str2")
        self._emit("_str_cmp:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    ; Get str1 pointer from stack (at ix+6)")
        self._emit("    ld l, (ix+6)")
        self._emit("    ld h, (ix+7)")
        self._emit("    ; Get str2 pointer from stack (at ix+4)")
        self._emit("    ld e, (ix+4)")
        self._emit("    ld d, (ix+5)")
        self._emit("_str_cmp_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    ld b, a")
        self._emit("    ld a, (de)")
        self._emit("    ; Compare characters")
        self._emit("    cp b")
        self._emit("    jr nz, _str_cmp_diff")
        self._emit("    ; Characters equal, check if end of string")
        self._emit("    or a")
        self._emit("    jr z, _str_cmp_equal")
        self._emit("    ; Move to next character")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _str_cmp_loop")
        self._emit("_str_cmp_diff:")
        self._emit("    ; Characters differ: B = char1, a = char2")
        self._emit("    ; Return char1 - char2 (negative if str1 < str2, positive if str1 > str2)")
        self._emit("    ld a, b")
        self._emit("    ld b, (de)  ; get char2 again")
        self._emit("    sub b")
        self._emit("    ; Sign-extend A to hl")
        self._emit("    ld l, a")
        self._emit("    rlca")
        self._emit("    sbc a, a")
        self._emit("    ld h, a")
        self._emit("    jr _str_cmp_done")
        self._emit("_str_cmp_equal:")
        self._emit("    ld hl, 0")
        self._emit("_str_cmp_done:")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # =========================================
        # Ada.Strings.Fixed Runtime Functions
        # =========================================
        self._emit("; =========================================")
        self._emit("; Ada.Strings.Fixed Runtime")
        self._emit("; =========================================")
        self._emit("")

        # _str_move: Copy source to target
        self._emit("; _str_move: Copy source string to target")
        self._emit("; Input: Stack: source_ptr, target_ptr")
        self._emit("STR_MOVE_SRC         .equ 6")
        self._emit("STR_MOVE_TGT         .equ 4")
        self._emit("")
        self._emit("_str_move:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+STR_MOVE_SRC)")
        self._emit("    ld h, (ix+STR_MOVE_SRC+1)")
        self._emit("    ld e, (ix+STR_MOVE_TGT)")
        self._emit("    ld d, (ix+STR_MOVE_TGT+1)")
        self._emit("_sm_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    ld (de), a")
        self._emit("    or a")
        self._emit("    jr z, _sm_done")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _sm_loop")
        self._emit("_sm_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _str_index: Find pattern in source
        self._emit("; _str_index: Find pattern in source string")
        self._emit("; Input: Stack: source_ptr, pattern_ptr")
        self._emit("; Output: hl = 1-based position (0 if not found)")
        self._emit("STR_INDEX_SRC        .equ 6")
        self._emit("STR_INDEX_PAT        .equ 4")
        self._emit("")
        self._emit("_str_index:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+STR_INDEX_SRC)")
        self._emit("    ld h, (ix+STR_INDEX_SRC+1)")
        self._emit("    ld bc, 1          ; position counter (1-based)")
        self._emit("_si_outer:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _si_notfound  ; end of source")
        self._emit("    ; Try to match pattern at this position")
        self._emit("    push hl")
        self._emit("    push bc")
        self._emit("    ld e, (ix+STR_INDEX_PAT)")
        self._emit("    ld d, (ix+STR_INDEX_PAT+1)")
        self._emit("_si_match:")
        self._emit("    ld a, (de)")
        self._emit("    or a")
        self._emit("    jr z, _si_found     ; end of pattern = match!")
        self._emit("    ld b, a")
        self._emit("    ld a, (hl)")
        self._emit("    cp b")
        self._emit("    jr nz, _si_nomatch  ; mismatch")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _si_match")
        self._emit("_si_found:")
        self._emit("    pop bc")
        self._emit("    pop hl")
        self._emit("    push bc")
        self._emit("    pop hl             ; return position")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_si_nomatch:")
        self._emit("    pop bc")
        self._emit("    pop hl")
        self._emit("    inc hl")
        self._emit("    inc bc")
        self._emit("    jr _si_outer")
        self._emit("_si_notfound:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _str_index_char: Find character in source
        self._emit("; _str_index_char: Find character in source string")
        self._emit("; Input: Stack: source_ptr, character")
        self._emit("; Output: hl = 1-based position (0 if not found)")
        self._emit("STR_IDXCH_SRC        .equ 6")
        self._emit("STR_IDXCH_CHR        .equ 4")
        self._emit("")
        self._emit("_str_index_char:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+STR_IDXCH_SRC)")
        self._emit("    ld h, (ix+STR_IDXCH_SRC+1)")
        self._emit("    ld c, (ix+STR_IDXCH_CHR)")
        self._emit("    ld de, 1          ; position counter")
        self._emit("_sic_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _sic_notfound")
        self._emit("    cp c")
        self._emit("    jr z, _sic_found")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _sic_loop")
        self._emit("_sic_found:")
        self._emit("    ex de, hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_sic_notfound:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _str_count: Count occurrences of pattern
        self._emit("; _str_count: Count occurrences of pattern in source")
        self._emit("; Input: Stack: source_ptr, pattern_ptr")
        self._emit("; Output: hl = count")
        self._emit("STR_COUNT_SRC        .equ 6")
        self._emit("STR_COUNT_PAT        .equ 4")
        self._emit("")
        self._emit("_str_count:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+STR_COUNT_SRC)")
        self._emit("    ld h, (ix+STR_COUNT_SRC+1)")
        self._emit("    ld bc, 0          ; count")
        self._emit("_sc_outer:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _sc_done")
        self._emit("    ; Try to match pattern at this position")
        self._emit("    push hl")
        self._emit("    push bc")
        self._emit("    ld e, (ix+STR_COUNT_PAT)")
        self._emit("    ld d, (ix+STR_COUNT_PAT+1)")
        self._emit("_sc_match:")
        self._emit("    ld a, (de)")
        self._emit("    or a")
        self._emit("    jr z, _sc_found")
        self._emit("    ld b, a")
        self._emit("    ld a, (hl)")
        self._emit("    cp b")
        self._emit("    jr nz, _sc_nomatch")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _sc_match")
        self._emit("_sc_found:")
        self._emit("    pop bc")
        self._emit("    inc bc")
        self._emit("    pop hl")
        self._emit("    inc hl")
        self._emit("    jr _sc_outer")
        self._emit("_sc_nomatch:")
        self._emit("    pop bc")
        self._emit("    pop hl")
        self._emit("    inc hl")
        self._emit("    jr _sc_outer")
        self._emit("_sc_done:")
        self._emit("    push bc")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _str_head: Get first N characters
        self._emit("; _str_head: Get first N characters of string")
        self._emit("; Input: Stack: source_ptr, count")
        self._emit("; Output: hl = pointer to result (in static buffer)")
        self._emit("STR_HEAD_SRC         .equ 6")
        self._emit("STR_HEAD_CNT         .equ 4")
        self._emit("")
        self._emit("_str_head:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+STR_HEAD_SRC)")
        self._emit("    ld h, (ix+STR_HEAD_SRC+1)")
        self._emit("    ld c, (ix+STR_HEAD_CNT)")
        self._emit("    ld b, (ix+STR_HEAD_CNT+1)")
        self._emit("    ld de, _str_fixed_buf")
        self._emit("    push de           ; save result ptr")
        self._emit("_sh_loop:")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _sh_done    ; count reached 0")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _sh_done    ; end of source")
        self._emit("    ld (de), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    dec bc")
        self._emit("    jr _sh_loop")
        self._emit("_sh_done:")
        self._emit("    xor a")
        self._emit("    ld (de), a        ; null terminate")
        self._emit("    pop hl            ; return buffer ptr")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _str_tail: Get last N characters
        self._emit("; _str_tail: Get last N characters of string")
        self._emit("; Input: Stack: source_ptr, count")
        self._emit("; Output: hl = pointer to result")
        self._emit("STR_TAIL_SRC         .equ 6")
        self._emit("STR_TAIL_CNT         .equ 4")
        self._emit("")
        self._emit("_str_tail:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; First get string length")
        self._emit("    ld l, (ix+STR_TAIL_SRC)")
        self._emit("    ld h, (ix+STR_TAIL_SRC+1)")
        self._emit("    push hl")
        self._emit("    call _str_len      ; hl = length")
        self._emit("    pop de             ; de = source ptr")
        self._emit("    ; Calculate start position = len - count")
        self._emit("    ld c, (ix+STR_TAIL_CNT)")
        self._emit("    ld b, (ix+STR_TAIL_CNT+1)")
        self._emit("    or a")
        self._emit("    sbc hl, bc")
        self._emit("    jr nc, _st_pos     ; len >= count")
        self._emit("    ld hl, 0           ; start at 0")
        self._emit("_st_pos:")
        self._emit("    add hl, de         ; hl = source + start")
        self._emit("    ; Copy to buffer")
        self._emit("    ld de, _str_fixed_buf")
        self._emit("    push de")
        self._emit("_st_copy:")
        self._emit("    ld a, (hl)")
        self._emit("    ld (de), a")
        self._emit("    or a")
        self._emit("    jr z, _st_done")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _st_copy")
        self._emit("_st_done:")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _str_trim: Remove leading/trailing spaces
        self._emit("; _str_trim: Remove leading and trailing spaces")
        self._emit("; Input: Stack: source_ptr")
        self._emit("; Output: hl = pointer to trimmed string")
        self._emit("STR_TRIM_SRC         .equ 4")
        self._emit("")
        self._emit("_str_trim:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+STR_TRIM_SRC)")
        self._emit("    ld h, (ix+STR_TRIM_SRC+1)")
        self._emit("    ; Skip leading spaces")
        self._emit("_strm_lead:")
        self._emit("    ld a, (hl)")
        self._emit("    cp ' '")
        self._emit("    jr nz, _strm_copy")
        self._emit("    inc hl")
        self._emit("    jr _strm_lead")
        self._emit("_strm_copy:")
        self._emit("    ; Copy to buffer")
        self._emit("    ld de, _str_fixed_buf")
        self._emit("    push de")
        self._emit("_strm_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    ld (de), a")
        self._emit("    or a")
        self._emit("    jr z, _strm_trail")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _strm_loop")
        self._emit("_strm_trail:")
        self._emit("    ; Remove trailing spaces")
        self._emit("    pop hl             ; buffer start")
        self._emit("    push hl")
        self._emit("    ; Find end")
        self._emit("    ld de, _str_fixed_buf")
        self._emit("_strm_findend:")
        self._emit("    ld a, (de)")
        self._emit("    or a")
        self._emit("    jr z, _strm_trim_trail")
        self._emit("    inc de")
        self._emit("    jr _strm_findend")
        self._emit("_strm_trim_trail:")
        self._emit("    ; de points to null, go back")
        self._emit("    dec de")
        self._emit("_strm_trail_loop:")
        self._emit("    ld a, d")
        self._emit("    cp _str_fixed_buf / 256")
        self._emit("    jr nz, _strm_trail_chk")
        self._emit("    ld a, e")
        self._emit("    cp _str_fixed_buf & 0xFF")
        self._emit("    jr c, _strm_ret    ; at start")
        self._emit("_strm_trail_chk:")
        self._emit("    ld a, (de)")
        self._emit("    cp ' '")
        self._emit("    jr nz, _strm_null")
        self._emit("    dec de")
        self._emit("    jr _strm_trail_loop")
        self._emit("_strm_null:")
        self._emit("    inc de")
        self._emit("    xor a")
        self._emit("    ld (de), a")
        self._emit("_strm_ret:")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Static buffer for Fixed string operations
        self._emit("; Static buffer for Ada.Strings.Fixed operations")
        self._emit("_str_fixed_buf:")
        self._emit("    .ds 256")
        self._emit("")

        self._emit("; _memcpy: Copy block of memory")
        self._emit("; Input: Stack: dest (16-bit), src (16-bit), count (16-bit)")
        self._emit("; Output: None")
        self._emit("_memcpy:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    push hl")
        self._emit("    ; Get dest pointer from stack (at ix+8)")
        self._emit("    ld e, (ix+8)")
        self._emit("    ld d, (ix+9)")
        self._emit("    ; Get src pointer from stack (at ix+6)")
        self._emit("    ld l, (ix+6)")
        self._emit("    ld h, (ix+7)")
        self._emit("    ; Get count from stack (at ix+4)")
        self._emit("    ld c, (ix+4)")
        self._emit("    ld b, (ix+5)")
        self._emit("    ; Check if count is 0")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _memcpy_done")
        self._emit("    ; Use LDIR for efficient block copy")
        self._emit("    ldir")
        self._emit("_memcpy_done:")
        self._emit("    pop hl")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _protected_lock: Acquire lock on protected object")
        self._emit("; Input: Stack: ptr to protected object (lock byte is at offset 0)")
        self._emit("; Output: None (spins until lock acquired)")
        self._emit("_protected_lock:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get protected object pointer from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("_prot_lock_spin:")
        self._emit("    ; Try to acquire lock (test and set)")
        self._emit("    di             ; Disable interrupts")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr nz, _prot_lock_wait")
        self._emit("    ld (hl), 1     ; Set lock")
        self._emit("    ei             ; Re-enable interrupts")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_prot_lock_wait:")
        self._emit("    ei             ; Re-enable interrupts")
        self._emit("    ; Simple spin wait")
        self._emit("    jr _prot_lock_spin")
        self._emit("")
        self._emit("; _protected_unlock: Release lock on protected object")
        self._emit("; Input: Stack: ptr to protected object")
        self._emit("; Output: None")
        self._emit("_protected_unlock:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get protected object pointer from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Clear lock")
        self._emit("    xor a")
        self._emit("    ld (hl), a")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Protected entry queue support
        self._emit("; _protected_entry_queue: Queue caller on entry barrier")
        self._emit("; Input: Stack: entry_id, ptr to protected object")
        self._emit("; Output: Caller is blocked until barrier is re-evaluated")
        self._emit("_protected_entry_queue:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get protected object ptr from stack (ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Get entry_id (ix+6)")
        self._emit("    ld e, (ix+6)")
        self._emit("    ld d, (ix+7)")
        self._emit("    ; Queue structure is at offset 2 in protected object")
        self._emit("    ; For simplicity: just yield and hope for reeval")
        self._emit("    push hl")
        self._emit("    push de")
        self._emit("    call _TASK_YIELD")
        self._emit("    pop de")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        self._emit("; _protected_reeval_barriers: Re-evaluate entry barriers")
        self._emit("; Input: Stack: ptr to protected object")
        self._emit("; Output: Wakes up any queued tasks whose barriers are now true")
        self._emit("_protected_reeval_barriers:")
        self._emit("    ; Simplified: just return (tasks will re-check on wake)")
        self._emit("    ret")
        self._emit("")

    def _generate_math_runtime(self) -> None:
        """Generate math runtime (multiply, divide)."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Math Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; _mul16: 16-bit signed multiply")
        self._emit("; Input: hl = multiplicand, de = multiplier")
        self._emit("; Output: hl = product (low 16 bits)")
        self._emit("_mul16:")
        self._emit("    push bc")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("    ld hl, 0")
        self._emit("_mul16_loop:")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _mul16_done")
        self._emit("    srl D")
        self._emit("    rr E")
        self._emit("    jr nc, _mul16_skip")
        self._emit("    add hl, bc")
        self._emit("_mul16_skip:")
        self._emit("    sla C")
        self._emit("    rl B")
        self._emit("    jr _mul16_loop")
        self._emit("_mul16_done:")
        self._emit("    pop bc")
        self._emit("    ret")
        self._emit("")
        self._emit("; _div16: 16-bit signed divide")
        self._emit("; Input: hl = dividend, de = divisor")
        self._emit("; Output: hl = quotient")
        self._emit("_div16:")
        self._emit("    push bc")
        self._emit("    ; Handle signs")
        self._emit("    ld a, h")
        self._emit("    xor d")
        self._emit("    push af  ; save result sign")
        self._emit("    ; Make both positive")
        self._emit("    bit 7, h")
        self._emit("    jr z, _div16_pos1")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("_div16_pos1:")
        self._emit("    bit 7, d")
        self._emit("    jr z, _div16_pos2")
        self._emit("    xor a")
        self._emit("    sub e")
        self._emit("    ld e, a")
        self._emit("    sbc a, a")
        self._emit("    sub d")
        self._emit("    ld d, a")
        self._emit("_div16_pos2:")
        self._emit("    call _div16_unsigned")
        self._emit("    ; Apply sign to result")
        self._emit("    pop af")
        self._emit("    bit 7, a")
        self._emit("    jr z, _div16_ret")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("_div16_ret:")
        self._emit("    pop bc")
        self._emit("    ret")
        self._emit("")
        self._emit("; _div16_unsigned: 16-bit unsigned divide")
        self._emit("; Input: hl = dividend, de = divisor")
        self._emit("; Output: hl = quotient, bc = remainder")
        self._emit("_div16_unsigned:")
        self._emit("    ld bc, 0  ; remainder")
        self._emit("    ld a, 16  ; bit counter")
        self._emit("_div16u_loop:")
        self._emit("    ; Shift dividend left into remainder")
        self._emit("    add hl, hl")
        self._emit("    rl C")
        self._emit("    rl B")
        self._emit("    ; Try subtract divisor from remainder")
        self._emit("    push hl")
        self._emit("    ld h, b")
        self._emit("    ld l, c")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr c, _div16u_skip")
        self._emit("    ; Subtraction succeeded")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("    pop hl")
        self._emit("    inc l  ; set quotient bit")
        self._emit("    jr _div16u_next")
        self._emit("_div16u_skip:")
        self._emit("    pop hl")
        self._emit("_div16u_next:")
        self._emit("    dec a")
        self._emit("    jr nz, _div16u_loop")
        self._emit("    ret")
        self._emit("")
        self._emit("; _div16_mod: 16-bit unsigned divide with modulo")
        self._emit("; Input: hl = dividend, de = divisor")
        self._emit("; Output: hl = quotient, a = remainder (low byte)")
        self._emit("_div16_mod:")
        self._emit("    call _div16_unsigned")
        self._emit("    ld a, c  ; remainder low byte")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mod16: 16-bit signed modulo")
        self._emit("; Input: hl = dividend, de = divisor")
        self._emit("; Output: hl = dividend mod divisor (Ada semantics)")
        self._emit("; Note: Ada mod has sign of divisor, different from rem")
        self._emit("_mod16:")
        self._emit("    push bc")
        self._emit("    ; Save signs for later")
        self._emit("    ld a, h")
        self._emit("    xor d")
        self._emit("    push af  ; save combined sign for mod adjustment")
        self._emit("    ld a, d")
        self._emit("    push af  ; save divisor sign")
        self._emit("    ; Make both positive")
        self._emit("    bit 7, h")
        self._emit("    jr z, _mod16_pos1")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("_mod16_pos1:")
        self._emit("    bit 7, d")
        self._emit("    jr z, _mod16_pos2")
        self._emit("    xor a")
        self._emit("    sub e")
        self._emit("    ld e, a")
        self._emit("    sbc a, a")
        self._emit("    sub d")
        self._emit("    ld d, a")
        self._emit("_mod16_pos2:")
        self._emit("    ; Now do unsigned divide, remainder in bc")
        self._emit("    call _div16_unsigned")
        self._emit("    ; Result remainder is in bc, move to hl")
        self._emit("    ld h, b")
        self._emit("    ld l, c")
        self._emit("    ; Ada mod: result has sign of divisor")
        self._emit("    ; If remainder is zero, we're done")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _mod16_done")
        self._emit("    ; If signs differed and remainder != 0,")
        self._emit("    ; adjust: result = divisor - remainder")
        self._emit("    pop af  ; divisor sign")
        self._emit("    pop bc  ; combined sign (in B's bit 7)")
        self._emit("    bit 7, b")
        self._emit("    jr z, _mod16_apply_sign")
        self._emit("    ; Signs differed - adjust result")
        self._emit("    push de  ; save divisor")
        self._emit("    ex de, hl")
        self._emit("    pop hl   ; hl = divisor")
        self._emit("    or a")
        self._emit("    sbc hl, de  ; hl = divisor - remainder")
        self._emit("    jr _mod16_apply_sign2")
        self._emit("_mod16_apply_sign:")
        self._emit("    ; Apply divisor's sign to result")
        self._emit("    bit 7, a")
        self._emit("    jr z, _mod16_ret")
        self._emit("_mod16_apply_sign2:")
        self._emit("    ; Negate result if divisor was negative")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("    jr _mod16_ret")
        self._emit("_mod16_done:")
        self._emit("    pop af")
        self._emit("    pop af")
        self._emit("_mod16_ret:")
        self._emit("    pop bc")
        self._emit("    ret")
        self._emit("")
        self._emit("; _rem16: 16-bit signed remainder")
        self._emit("; Input: hl = dividend, de = divisor")
        self._emit("; Output: hl = dividend rem divisor (Ada semantics)")
        self._emit("; Note: Ada rem has sign of dividend, different from mod")
        self._emit("_rem16:")
        self._emit("    push bc")
        self._emit("    ; Save dividend sign for result")
        self._emit("    ld a, h")
        self._emit("    push af  ; save dividend sign")
        self._emit("    ; Make both positive")
        self._emit("    bit 7, h")
        self._emit("    jr z, _rem16_pos1")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("_rem16_pos1:")
        self._emit("    bit 7, d")
        self._emit("    jr z, _rem16_pos2")
        self._emit("    xor a")
        self._emit("    sub e")
        self._emit("    ld e, a")
        self._emit("    sbc a, a")
        self._emit("    sub d")
        self._emit("    ld d, a")
        self._emit("_rem16_pos2:")
        self._emit("    ; Do unsigned divide, remainder in bc")
        self._emit("    call _div16_unsigned")
        self._emit("    ; Result remainder is in bc, move to hl")
        self._emit("    ld h, b")
        self._emit("    ld l, c")
        self._emit("    ; Ada rem: result has sign of dividend")
        self._emit("    pop af  ; dividend sign")
        self._emit("    bit 7, a")
        self._emit("    jr z, _rem16_ret")
        self._emit("    ; Negate result if dividend was negative")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _rem16_ret  ; don't negate zero")
        self._emit("    xor a")
        self._emit("    sub l")
        self._emit("    ld l, a")
        self._emit("    sbc a, a")
        self._emit("    sub h")
        self._emit("    ld h, a")
        self._emit("_rem16_ret:")
        self._emit("    pop bc")
        self._emit("    ret")
        self._emit("")
        self._emit("; _shl16: 16-bit left shift")
        self._emit("; Input: hl = value, de = shift count")
        self._emit("; Output: hl = value << count")
        self._emit("_shl16:")
        self._emit("    ld a, e")
        self._emit("    or a")
        self._emit("    ret z        ; no shift needed")
        self._emit("    cp 16")
        self._emit("    jr c, _shl16_loop")
        self._emit("    ld hl, 0     ; shift >= 16 yields 0")
        self._emit("    ret")
        self._emit("_shl16_loop:")
        self._emit("    add hl, hl   ; hl = hl * 2")
        self._emit("    dec a")
        self._emit("    jr nz, _shl16_loop")
        self._emit("    ret")
        self._emit("")
        self._emit("; _shr16: 16-bit logical right shift (unsigned)")
        self._emit("; Input: hl = value, de = shift count")
        self._emit("; Output: hl = value >> count")
        self._emit("_shr16:")
        self._emit("    ld a, e")
        self._emit("    or a")
        self._emit("    ret z        ; no shift needed")
        self._emit("    cp 16")
        self._emit("    jr c, _shr16_loop")
        self._emit("    ld hl, 0     ; shift >= 16 yields 0")
        self._emit("    ret")
        self._emit("_shr16_loop:")
        self._emit("    srl H        ; shift high byte right")
        self._emit("    rr L         ; rotate low byte right with carry")
        self._emit("    dec a")
        self._emit("    jr nz, _shr16_loop")
        self._emit("    ret")
        self._emit("")
        self._emit("; _exp16: 16-bit integer exponentiation (base ** exp)")
        self._emit("; Input: Stack: base (16-bit), exponent (16-bit)")
        self._emit("; Output: hl = base ** exponent")
        self._emit("; Uses binary exponentiation for efficiency")
        self._emit("_exp16:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get base from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Get exponent from stack (at ix+6)")
        self._emit("    ld e, (ix+6)")
        self._emit("    ld d, (ix+7)")
        self._emit("    ; Handle exponent = 0 -> result = 1")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr nz, _exp16_init")
        self._emit("    ld hl, 1")
        self._emit("    jr _exp16_done")
        self._emit("_exp16_init:")
        self._emit("    ; Save base in bc, result = 1 in hl")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("    ld hl, 1")
        self._emit("_exp16_loop:")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _exp16_done")
        self._emit("    ; If exp is odd, result *= base")
        self._emit("    bit 0, e")
        self._emit("    jr z, _exp16_square")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    ld d, b")
        self._emit("    ld e, c")
        self._emit("    call _mul16")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("_exp16_square:")
        self._emit("    ; base *= base")
        self._emit("    push hl")
        self._emit("    ld h, b")
        self._emit("    ld l, c")
        self._emit("    ld d, b")
        self._emit("    ld e, c")
        self._emit("    call _mul16")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("    pop hl")
        self._emit("    ; exp /= 2")
        self._emit("    srl D")
        self._emit("    rr E")
        self._emit("    jr _exp16_loop")
        self._emit("_exp16_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

    def _generate_text_io_runtime(self) -> None:
        """Generate Text_IO runtime for CP/M BDOS."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Ada.Text_IO Runtime for CP/M")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; CP/M BDOS entry point")
        self._emit("BDOS: equ 5")
        self._emit("")
        self._emit("; _put_char: Output single character")
        self._emit("; Input: A = character to output")
        self._emit("_put_char:")
        self._emit("    ld e, a")
        self._emit("    ld c, 2      ; BDOS function 2: console output")
        self._emit("    call BDOS")
        self._emit("    ret")
        self._emit("")
        self._emit("; _put_string: Output null-terminated string")
        self._emit("; Input: hl = pointer to string")
        self._emit("_put_string:")
        self._emit("    push hl")
        self._emit("_put_string_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _put_string_done")
        self._emit("    call _put_char")
        self._emit("    inc hl")
        self._emit("    jr _put_string_loop")
        self._emit("_put_string_done:")
        self._emit("    pop hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; _put_line: Output string followed by CR+LF")
        self._emit("; Input: Stack: pointer to string")
        self._emit("_put_line:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get string pointer from stack (at ix+4)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    call _put_string")
        self._emit("    call _new_line")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _new_line: Output CR+LF")
        self._emit("_new_line:")
        self._emit("    ld a, 13     ; CR")
        self._emit("    call _put_char")
        self._emit("    ld a, 10     ; LF")
        self._emit("    call _put_char")
        self._emit("    ret")
        self._emit("")
        self._emit("; _put_int: Output integer (calls _int_to_str then _put_string)")
        self._emit("; Input: Stack: value (16-bit signed)")
        self._emit("_put_int:")
        self._emit("    ; Convert integer to string first")
        self._emit("    call _int_to_str")
        self._emit("    ; hl now contains pointer to string")
        self._emit("    call _put_string")
        self._emit("    ret")
        self._emit("")
        self._emit("; _put_int_line: Output integer followed by CR+LF")
        self._emit("; Input: Stack: value (16-bit signed)")
        self._emit("_put_int_line:")
        self._emit("    call _int_to_str")
        self._emit("    call _put_string")
        self._emit("    call _new_line")
        self._emit("    ret")
        self._emit("")
        self._emit("; _get_char: Get single character from console")
        self._emit("; Output: A = character read, hl = character (zero-extended)")
        self._emit("_get_char:")
        self._emit("    ld c, 1      ; BDOS function 1: console input")
        self._emit("    call BDOS")
        self._emit("    ld l, a")
        self._emit("    ld h, 0")
        self._emit("    ret")
        self._emit("")
        self._emit("; _get_line: Read line from console into buffer")
        self._emit("; Input: hl = buffer address, de = max length")
        self._emit("; Output: hl = actual length read")
        self._emit("_get_line:")
        self._emit("    push de      ; save max length")
        self._emit("    push hl      ; save buffer address")
        self._emit("    ld b, 0      ; length counter")
        self._emit("_get_line_loop:")
        self._emit("    call _get_char")
        self._emit("    ; Check for CR (end of line)")
        self._emit("    cp 13")
        self._emit("    jr z, _get_line_done")
        self._emit("    ; Check for max length")
        self._emit("    ld a, e      ; max length low")
        self._emit("    cp b")
        self._emit("    jr z, _get_line_done  ; buffer full")
        self._emit("    ; Store character")
        self._emit("    ld a, l")
        self._emit("    call _get_char  ; re-read (simplified)")
        self._emit("    pop hl")
        self._emit("    push hl")
        self._emit("    ld (hl), a")
        self._emit("    inc hl")
        self._emit("    inc b")
        self._emit("    jr _get_line_loop")
        self._emit("_get_line_done:")
        self._emit("    pop hl       ; discard buffer address")
        self._emit("    pop de       ; discard max length")
        self._emit("    ld l, b")
        self._emit("    ld h, 0      ; hl = actual length")
        self._emit("    ret")
        self._emit("")

    def _generate_dispatch_runtime(self) -> None:
        """Generate runtime for OOP dispatching."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; OOP Dispatch Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; _dispatch_call: Call procedure whose address is in hl")
        self._emit("; This is needed because Z80 has no 'call (hl)' instruction")
        self._emit("; We use self-modifying code or a jump trampoline")
        self._emit("_dispatch_call:")
        self._emit("    jp (hl)      ; Jump to target (caller's return addr is on stack)")
        self._emit("")
        self._generate_controlled_runtime()
        self._generate_z80_specific_runtime()
        self._generate_cpm_specific_runtime()
        self._generate_memory_runtime()

    def _generate_controlled_runtime(self) -> None:
        """Generate runtime for Ada controlled types (Initialize/Adjust/Finalize).

        Controlled types in Ada require:
        - Initialize: Called when object is default-initialized
        - Adjust: Called after assignment to the target object
        - Finalize: Called when object goes out of scope

        We use a finalization chain - a linked list of controlled objects
        that need finalization when the current scope exits.
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Controlled Types Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Finalization chain structure:")
        self._emit("; Each entry is 6 bytes:")
        self._emit(";   +0: next pointer (2 bytes)")
        self._emit(";   +2: object pointer (2 bytes)")
        self._emit(";   +4: finalize procedure address (2 bytes)")
        self._emit("")
        self._emit("; Offsets for finalization entry")
        self._emit("FIN_NEXT      .equ 0")
        self._emit("FIN_OBJ_PTR   .equ 2")
        self._emit("FIN_PROC_PTR  .equ 4")
        self._emit("FIN_ENTRY_SIZE .equ 6")
        self._emit("")
        self._emit("_fin_chain_head:")
        self._emit("    .dw 0           ; head of finalization chain (null initially)")
        self._emit("_fin_mark_stack:")
        self._emit("    .ds 16          ; stack of scope marks (8 nested scopes max)")
        self._emit("_fin_mark_sp:")
        self._emit("    .dw _fin_mark_stack")
        self._emit("")
        self._emit("; _fin_push_scope: Mark the start of a new scope")
        self._emit("; Saves current chain head so we can finalize just this scope's objects")
        self._emit("_fin_push_scope:")
        self._emit("    ld hl, (_fin_mark_sp)")
        self._emit("    ld de, (_fin_chain_head)")
        self._emit("    ld (hl), e")
        self._emit("    inc hl")
        self._emit("    ld (hl), d")
        self._emit("    inc hl")
        self._emit("    ld (_fin_mark_sp), hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fin_register: Register an object for finalization")
        self._emit("; Input: hl = object pointer, de = finalize proc address")
        self._emit("_fin_register:")
        self._emit("    push hl           ; save object ptr")
        self._emit("    push de           ; save finalize proc")
        self._emit("    ; Allocate finalization entry (6 bytes)")
        self._emit("    ld hl, FIN_ENTRY_SIZE")
        self._emit("    push hl")
        self._emit("    call _heap_alloc")
        self._emit("    pop bc            ; discard size arg")
        self._emit("    ; hl = new entry ptr")
        self._emit("    pop de            ; finalize proc")
        self._emit("    pop bc            ; object ptr")
        self._emit("    ; Fill in entry")
        self._emit("    push hl")
        self._emit("    pop ix            ; ix = entry ptr")
        self._emit("    ld (ix+FIN_OBJ_PTR), c")
        self._emit("    ld (ix+FIN_OBJ_PTR+1), b")
        self._emit("    ld (ix+FIN_PROC_PTR), e")
        self._emit("    ld (ix+FIN_PROC_PTR+1), d")
        self._emit("    ; Link to chain head")
        self._emit("    ld de, (_fin_chain_head)")
        self._emit("    ld (ix+FIN_NEXT), e")
        self._emit("    ld (ix+FIN_NEXT+1), d")
        self._emit("    ; Update chain head")
        self._emit("    push ix")
        self._emit("    pop hl")
        self._emit("    ld (_fin_chain_head), hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fin_pop_scope: Finalize all objects in current scope and pop")
        self._emit("_fin_pop_scope:")
        self._emit("    ; Get scope mark")
        self._emit("    ld hl, (_fin_mark_sp)")
        self._emit("    dec hl")
        self._emit("    ld d, (hl)")
        self._emit("    dec hl")
        self._emit("    ld e, (hl)         ; de = saved chain head (scope boundary)")
        self._emit("    ld (_fin_mark_sp), hl")
        self._emit("    push de            ; save scope boundary")
        self._emit("_fps_loop:")
        self._emit("    ld hl, (_fin_chain_head)")
        self._emit("    pop de             ; scope boundary")
        self._emit("    push de")
        self._emit("    ; Check if we've reached the scope boundary")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de         ; restore hl")
        self._emit("    jr z, _fps_done    ; reached boundary")
        self._emit("    ; Get entry info")
        self._emit("    push hl")
        self._emit("    pop ix             ; ix = current entry")
        self._emit("    ; Get next entry (for advancing chain head)")
        self._emit("    ld e, (ix+FIN_NEXT)")
        self._emit("    ld d, (ix+FIN_NEXT+1)")
        self._emit("    ld (_fin_chain_head), de")
        self._emit("    ; Call Finalize(object_ptr)")
        self._emit("    ld l, (ix+FIN_OBJ_PTR)")
        self._emit("    ld h, (ix+FIN_OBJ_PTR+1)")
        self._emit("    push hl            ; arg: object ptr")
        self._emit("    ld l, (ix+FIN_PROC_PTR)")
        self._emit("    ld h, (ix+FIN_PROC_PTR+1)")
        self._emit("    call _dispatch_call  ; call Finalize")
        self._emit("    pop bc             ; clean arg")
        self._emit("    jr _fps_loop")
        self._emit("_fps_done:")
        self._emit("    pop de             ; discard scope boundary")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fin_run_all: Finalize all objects (for program exit)")
        self._emit("_fin_run_all:")
        self._emit("_fra_loop:")
        self._emit("    ld hl, (_fin_chain_head)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    ret z              ; chain empty, done")
        self._emit("    push hl")
        self._emit("    pop ix             ; ix = current entry")
        self._emit("    ; Advance chain head")
        self._emit("    ld e, (ix+FIN_NEXT)")
        self._emit("    ld d, (ix+FIN_NEXT+1)")
        self._emit("    ld (_fin_chain_head), de")
        self._emit("    ; Call Finalize(object_ptr)")
        self._emit("    ld l, (ix+FIN_OBJ_PTR)")
        self._emit("    ld h, (ix+FIN_OBJ_PTR+1)")
        self._emit("    push hl            ; arg: object ptr")
        self._emit("    ld l, (ix+FIN_PROC_PTR)")
        self._emit("    ld h, (ix+FIN_PROC_PTR+1)")
        self._emit("    call _dispatch_call  ; call Finalize")
        self._emit("    pop bc             ; clean arg")
        self._emit("    jr _fra_loop")
        self._emit("")

    def _generate_z80_specific_runtime(self) -> None:
        """Generate Z80-specific runtime functions for low-level access."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Z80-Specific Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; _port_in: Read from I/O port")
        self._emit("; Input: Stack: port number (8-bit)")
        self._emit("; Output: hl = value read")
        self._emit("_port_in:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld c, (ix+4)  ; port number")
        self._emit("    in A, (c)")
        self._emit("    ld l, a")
        self._emit("    ld h, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _port_out: Write to I/O port")
        self._emit("; Input: Stack: port number, value")
        self._emit("_port_out:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld c, (ix+4)  ; port number")
        self._emit("    ld a, (ix+6)  ; value")
        self._emit("    out (c), a")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _get_interrupt_state: Check if interrupts enabled")
        self._emit("; Output: hl = 1 if enabled, 0 if disabled")
        self._emit("; Note: Z80 has no direct way to read IFF, use shadow")
        self._emit("_get_interrupt_state:")
        self._emit("    ld hl, (_int_enabled_flag)")
        self._emit("    ret")
        self._emit("")
        self._emit("; Interrupt enable flag (shadow register)")
        self._emit("_int_enabled_flag: dw 1")
        self._emit("")
        self._emit("; _enable_interrupts: Enable interrupts")
        self._emit("_enable_interrupts:")
        self._emit("    ei")
        self._emit("    ld hl, 1")
        self._emit("    ld (_int_enabled_flag), hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; _disable_interrupts: Disable interrupts")
        self._emit("_disable_interrupts:")
        self._emit("    di")
        self._emit("    ld hl, 0")
        self._emit("    ld (_int_enabled_flag), hl")
        self._emit("    ret")
        self._emit("")

    def _generate_cpm_specific_runtime(self) -> None:
        """Generate CP/M-specific runtime functions."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; CP/M-Specific Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; _bdos: Make BDOS system call")
        self._emit("; Input: Stack: function number, parameter (de)")
        self._emit("; Output: hl = result (from A or hl depending on function)")
        self._emit("_bdos:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld c, (ix+4)  ; function number")
        self._emit("    ld e, (ix+6)  ; parameter low")
        self._emit("    ld d, (ix+7)  ; parameter high")
        self._emit("    call 5        ; BDOS entry")
        self._emit("    ; Result in A (or BA for some functions)")
        self._emit("    ld l, a")
        self._emit("    ld h, b")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _bios: Make BIOS system call")
        self._emit("; Input: Stack: function number (0-16)")
        self._emit("; Output: hl = result")
        self._emit("_bios:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get BIOS jump table address from 0x0001")
        self._emit("    ld hl, (1)")
        self._emit("    ; Calculate offset: function * 3")
        self._emit("    ld a, (ix+4)  ; function number")
        self._emit("    ld e, a")
        self._emit("    add a, a      ; *2")
        self._emit("    add a, e      ; *3")
        self._emit("    ld e, a")
        self._emit("    ld d, 0")
        self._emit("    add hl, de")
        self._emit("    ; Jump to BIOS routine")
        self._emit("    jp (hl)")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; DMA address tracking")
        self._emit("_dma_address: dw 0x0080  ; Default DMA at 0x80")
        self._emit("")

    def _generate_memory_runtime(self) -> None:
        """Generate memory management runtime functions."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Memory Management Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; _get_heap_size: Get current heap size")
        self._emit("; Output: hl = heap size in bytes")
        self._emit("_get_heap_size:")
        self._emit("    ld hl, (_heap_top)")
        self._emit("    ld de, (_heap_start)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    ret")
        self._emit("")
        self._emit("; _get_stack_size: Get current stack usage")
        self._emit("; Output: hl = bytes used on stack")
        self._emit("_get_stack_size:")
        self._emit("    ld hl, (_stack_top)")
        self._emit("    ld de, 0")
        self._emit("    add hl, sp    ; Approximate - sp is current")
        self._emit("    ret")
        self._emit("")
        self._emit("; _get_free_memory: Get available free memory")
        self._emit("; Output: hl = free bytes between heap and stack")
        self._emit("_get_free_memory:")
        self._emit("    ; Free = sp - heap_top")
        self._emit("    ld hl, 0")
        self._emit("    add hl, sp")
        self._emit("    ld de, (_heap_top)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    ret")
        self._emit("")
        self._emit("; Memory management variables")
        self._emit("_heap_start: dw _heap_begin")
        self._emit("_heap_top:   dw _heap_begin")
        self._emit("_stack_top:  dw 0xFFFF  ; Will be set at startup")
        self._emit("")
        self._emit("; _get_tag_info: Get tag info structure for tagged type")
        self._emit("; Input: Stack: tag value")
        self._emit("; Output: hl = pointer to tag info")
        self._emit("_get_tag_info:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ; Tag points to vtable, first entry is tag info")
        self._emit("    ; Return tag itself as simplified tag info")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _class_membership: Check if object is in T'Class hierarchy")
        self._emit("; Input: hl = object's tag (vtable ptr)")
        self._emit(";        de = target type's vtable ptr")
        self._emit("; Output: hl = 1 if in class, 0 if not")
        self._emit("; Algorithm: Walk up the parent chain from object's tag,")
        self._emit(";            checking if any ancestor matches the target type")
        self._emit("_class_membership:")
        self._emit("    ; Check if hl == de (exact match)")
        self._emit("_cm_loop:")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de        ; restore hl without affecting flags")
        self._emit("    jr z, _cm_found   ; exact match")
        self._emit("    ; Walk to parent: vtable[0] = parent vtable ptr")
        self._emit("    ld a, (hl)")
        self._emit("    inc hl")
        self._emit("    ld h, (hl)")
        self._emit("    ld l, a           ; hl = parent vtable ptr")
        self._emit("    ; Check if parent is null (reached root)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _cm_loop   ; continue if not null")
        self._emit("    ; Reached root without match")
        self._emit("    ld hl, 0")
        self._emit("    ret")
        self._emit("_cm_found:")
        self._emit("    ld hl, 1")
        self._emit("    ret")
        self._emit("")
        self._generate_storage_pool_runtime()

    def _generate_storage_pool_runtime(self) -> None:
        """Generate storage pool runtime for custom allocators.

        Ada storage pools allow custom memory management:
        - Root_Storage_Pool: Abstract type with Allocate/Deallocate/Storage_Size
        - User defines concrete pool types with custom allocation strategies

        For Z80/CP/M we provide:
        - Default pool: Uses _heap_alloc/_heap_free
        - Mark/Release pool: Fast allocation with bulk deallocation
        - Bounded pool: Fixed-size block allocation
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Storage Pool Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Storage pool dispatch table structure:")
        self._emit("; +0: Allocate procedure address")
        self._emit("; +2: Deallocate procedure address")
        self._emit("; +4: Storage_Size function address")
        self._emit("")
        self._emit("; Pool dispatch offsets")
        self._emit("POOL_ALLOCATE    .equ 0")
        self._emit("POOL_DEALLOCATE  .equ 2")
        self._emit("POOL_SIZE        .equ 4")
        self._emit("")
        self._emit("; _pool_alloc: Allocate from a storage pool")
        self._emit("; Input: hl = pool dispatch table ptr, de = size")
        self._emit("; Output: hl = allocated address")
        self._emit("_pool_alloc:")
        self._emit("    push de           ; save size")
        self._emit("    ; Get Allocate procedure address")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("    ; de = Allocate proc addr")
        self._emit("    pop hl            ; hl = size")
        self._emit("    push hl           ; arg: size")
        self._emit("    ex de, hl         ; hl = proc addr")
        self._emit("    call _dispatch_call")
        self._emit("    pop bc            ; clean size arg")
        self._emit("    ; Result in hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; _pool_free: Deallocate to a storage pool")
        self._emit("; Input: hl = pool dispatch table ptr, de = address to free")
        self._emit("_pool_free:")
        self._emit("    push de           ; save address")
        self._emit("    ; Get Deallocate procedure address")
        self._emit("    inc hl")
        self._emit("    inc hl")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("    ; de = Deallocate proc addr")
        self._emit("    pop hl            ; hl = address")
        self._emit("    push hl           ; arg: address")
        self._emit("    ex de, hl         ; hl = proc addr")
        self._emit("    call _dispatch_call")
        self._emit("    pop bc            ; clean addr arg")
        self._emit("    ret")
        self._emit("")
        self._emit("; Default storage pool dispatch table")
        self._emit("_default_pool:")
        self._emit("    .dw _heap_alloc")
        self._emit("    .dw _heap_free")
        self._emit("    .dw _get_free_memory")
        self._emit("")
        self._emit("; Mark/Release pool for fast allocation")
        self._emit("; Usage: Mark saves heap state, allocations happen,")
        self._emit(";        Release restores heap state (frees all at once)")
        self._emit("_mark_pool_mark:")
        self._emit("    .dw 0             ; saved heap_top")
        self._emit("")
        self._emit("; _mark_pool_mark_op: Save current heap state")
        self._emit("_mark_pool_mark_op:")
        self._emit("    ld hl, (_heap_top)")
        self._emit("    ld (_mark_pool_mark), hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mark_pool_release_op: Restore heap to marked state")
        self._emit("_mark_pool_release_op:")
        self._emit("    ld hl, (_mark_pool_mark)")
        self._emit("    ld (_heap_top), hl")
        self._emit("    ret")
        self._emit("")
        self._emit("; Mark/Release pool dispatch table")
        self._emit("_mark_pool:")
        self._emit("    .dw _heap_alloc       ; same allocator as default")
        self._emit("    .dw _mark_noop        ; deallocate does nothing")
        self._emit("    .dw _get_free_memory")
        self._emit("")
        self._emit("_mark_noop:")
        self._emit("    ret")
        self._emit("")

    def _generate_fixed_point_runtime(self) -> None:
        """Generate 32-bit fixed-point arithmetic runtime (16.16 format)."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Fixed-Point Arithmetic Runtime (16.16)")
        self._emit("; =========================================")
        self._emit("; Numbers are stored as 32-bit values:")
        self._emit("; High word = integer part + sign")
        self._emit("; Low word = fractional part (65536ths)")
        self._emit("")
        self._emit("; _fix_add: Add two 32-bit fixed-point numbers")
        self._emit("; Input: Stack: a_hi, a_lo, b_hi, b_lo")
        self._emit("; Output: hl = result_hi, de = result_lo")
        self._emit("_fix_add:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get a_lo (ix+4) and b_lo (ix+8)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ld e, (ix+8)")
        self._emit("    ld d, (ix+9)")
        self._emit("    add hl, de")
        self._emit("    push hl     ; save result_lo")
        self._emit("    ; Get a_hi (ix+6) and b_hi (ix+10)")
        self._emit("    ld l, (ix+6)")
        self._emit("    ld h, (ix+7)")
        self._emit("    ld e, (ix+10)")
        self._emit("    ld d, (ix+11)")
        self._emit("    adc hl, de  ; add with carry from low add")
        self._emit("    pop de      ; result_lo in de")
        self._emit("    ; hl = result_hi, de = result_lo")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_sub: Subtract two 32-bit fixed-point numbers (a - b)")
        self._emit("; Input: Stack: a_hi, a_lo, b_hi, b_lo")
        self._emit("; Output: hl = result_hi, de = result_lo")
        self._emit("_fix_sub:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get a_lo (ix+4) and b_lo (ix+8)")
        self._emit("    ld l, (ix+4)")
        self._emit("    ld h, (ix+5)")
        self._emit("    ld e, (ix+8)")
        self._emit("    ld d, (ix+9)")
        self._emit("    or a        ; clear carry")
        self._emit("    sbc hl, de")
        self._emit("    push hl     ; save result_lo")
        self._emit("    ; Get a_hi (ix+6) and b_hi (ix+10)")
        self._emit("    ld l, (ix+6)")
        self._emit("    ld h, (ix+7)")
        self._emit("    ld e, (ix+10)")
        self._emit("    ld d, (ix+11)")
        self._emit("    sbc hl, de  ; sub with borrow from low sub")
        self._emit("    pop de      ; result_lo in de")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_mul: Multiply two 16.16 fixed-point numbers")
        self._emit("; Input: Stack: a_hi, a_lo, b_hi, b_lo")
        self._emit("; Output: hl = result_hi, de = result_lo")
        self._emit("; Algorithm: (A * B) >> 16 where A, b are 32-bit")
        self._emit(";")
        self._emit("; Fixed-point multiply offsets")
        self._emit("FIX_MUL_A_LO     .equ 4    ; stack offset for a_lo")
        self._emit("FIX_MUL_A_HI     .equ 6    ; stack offset for a_hi")
        self._emit("FIX_MUL_B_LO     .equ 8    ; stack offset for b_lo")
        self._emit("FIX_MUL_B_HI     .equ 10   ; stack offset for b_hi")
        self._emit("")
        self._emit("_fix_mul:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; For (a_hi.a_lo) * (b_hi.b_lo):")
        self._emit("    ; Result = a_lo*b_lo/65536 + a_lo*b_hi + a_hi*b_lo + a_hi*b_hi*65536")
        self._emit("    ; We need: a_lo*b_hi + a_hi*b_lo + (a_lo*b_lo >> 16) as mid")
        self._emit("    ; And: a_hi*b_hi + (mid >> 16) as high")
        self._emit("    ;")
        self._emit("    ; Compute a_lo * b_lo -> keep high word only")
        self._emit("    ld l, (ix+FIX_MUL_A_LO)")
        self._emit("    ld h, (ix+FIX_MUL_A_LO+1)")
        self._emit("    ld e, (ix+FIX_MUL_B_LO)")
        self._emit("    ld d, (ix+FIX_MUL_B_LO+1)")
        self._emit("    call _mul16_32   ; hl:de = 32-bit result")
        self._emit("    push hl          ; save high word of a_lo*b_lo")
        self._emit("    ;")
        self._emit("    ; Compute a_lo * b_hi")
        self._emit("    ld l, (ix+FIX_MUL_A_LO)")
        self._emit("    ld h, (ix+FIX_MUL_A_LO+1)")
        self._emit("    ld e, (ix+FIX_MUL_B_HI)")
        self._emit("    ld d, (ix+FIX_MUL_B_HI+1)")
        self._emit("    call _mul16_32")
        self._emit("    ex de, hl        ; de = high, hl = low")
        self._emit("    push de          ; save high word")
        self._emit("    push hl          ; save low word (this is our result_lo candidate)")
        self._emit("    ;")
        self._emit("    ; Compute a_hi * b_lo")
        self._emit("    ld l, (ix+FIX_MUL_A_HI)")
        self._emit("    ld h, (ix+FIX_MUL_A_HI+1)")
        self._emit("    ld e, (ix+FIX_MUL_B_LO)")
        self._emit("    ld d, (ix+FIX_MUL_B_LO+1)")
        self._emit("    call _mul16_32")
        self._emit("    ; hl = high, de = low")
        self._emit("    pop bc           ; bc = a_lo*b_hi low")
        self._emit("    ex de, hl        ; de = high, hl = low")
        self._emit("    add hl, bc       ; hl = sum of low words")
        self._emit("    push hl          ; save result_lo")
        self._emit("    jr nc, _fmul_nc1")
        self._emit("    inc de           ; carry to high")
        self._emit("_fmul_nc1:")
        self._emit("    pop hl           ; result_lo")
        self._emit("    push hl")
        self._emit("    push de          ; save a_hi*b_lo high + carry")
        self._emit("    ;")
        self._emit("    ; Add contributions: (a_lo*b_lo>>16)")
        self._emit("    pop de           ; a_hi*b_lo high")
        self._emit("    pop hl           ; result_lo (we'll update)")
        self._emit("    ex (sp), hl      ; hl = a_lo*b_hi high, stack = result_lo")
        self._emit("    add hl, de       ; hl = sum of high words (partial result_hi)")
        self._emit("    ex de, hl        ; de = partial result_hi")
        self._emit("    pop hl           ; hl = result_lo")
        self._emit("    pop bc           ; bc = a_lo*b_lo high")
        self._emit("    add hl, bc       ; add to result_lo")
        self._emit("    jr nc, _fmul_nc2")
        self._emit("    inc de           ; carry to high")
        self._emit("_fmul_nc2:")
        self._emit("    push hl          ; save final result_lo")
        self._emit("    ;")
        self._emit("    ; Compute a_hi * b_hi (goes to high word)")
        self._emit("    push de          ; save partial result_hi")
        self._emit("    ld l, (ix+FIX_MUL_A_HI)")
        self._emit("    ld h, (ix+FIX_MUL_A_HI+1)")
        self._emit("    ld e, (ix+FIX_MUL_B_HI)")
        self._emit("    ld d, (ix+FIX_MUL_B_HI+1)")
        self._emit("    call _mul16_32")
        self._emit("    ; Only need low word (high*65536 shifts out)")
        self._emit("    ex de, hl        ; de = low word")
        self._emit("    pop hl           ; hl = partial result_hi")
        self._emit("    add hl, de       ; hl = final result_hi")
        self._emit("    ;")
        self._emit("    pop de           ; de = result_lo")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mul16_32: 16x16 multiply with 32-bit result")
        self._emit("; Input: hl = multiplicand, de = multiplier")
        self._emit("; Output: hl = high 16 bits, de = low 16 bits")
        self._emit("_mul16_32:")
        self._emit("    push bc")
        self._emit("    ; Use shift-and-add algorithm")
        self._emit("    ld b, h")
        self._emit("    ld c, l          ; bc = multiplicand")
        self._emit("    ld hl, 0         ; result high")
        self._emit("    ld a, 16         ; bit counter")
        self._emit("_mul32_loop:")
        self._emit("    ; Shift result left")
        self._emit("    add hl, hl")
        self._emit("    ex de, hl")
        self._emit("    adc hl, hl")
        self._emit("    ex de, hl")
        self._emit("    ; Check high bit of multiplier")
        self._emit("    sla E")
        self._emit("    rl D")
        self._emit("    jr nc, _mul32_skip")
        self._emit("    ; Add multiplicand")
        self._emit("    add hl, bc")
        self._emit("    jr nc, _mul32_skip")
        self._emit("    inc de           ; carry to high")
        self._emit("_mul32_skip:")
        self._emit("    dec a")
        self._emit("    jr nz, _mul32_loop")
        self._emit("    ; de = high, hl = low, swap")
        self._emit("    ex de, hl")
        self._emit("    pop bc")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_div: Divide two 16.16 fixed-point numbers (a / b)")
        self._emit("; Input: Stack: a_hi, a_lo, b_hi, b_lo")
        self._emit("; Output: hl = result_hi, de = result_lo")
        self._emit("; Algorithm: (A << 16) / B")
        self._emit(";")
        self._emit("; Fixed-point divide offsets")
        self._emit("FIX_DIV_A_LO     .equ 4")
        self._emit("FIX_DIV_A_HI     .equ 6")
        self._emit("FIX_DIV_B_LO     .equ 8")
        self._emit("FIX_DIV_B_HI     .equ 10")
        self._emit("")
        self._emit("_fix_div:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Check for divide by zero")
        self._emit("    ld a, (ix+FIX_DIV_B_LO)")
        self._emit("    or (ix+FIX_DIV_B_LO+1)")
        self._emit("    or (ix+FIX_DIV_B_HI)")
        self._emit("    or (ix+FIX_DIV_B_HI+1)")
        self._emit("    jr nz, _fdiv_ok")
        self._emit("    ; Divide by zero - return max value")
        self._emit("    ld hl, 0x7FFF")
        self._emit("    ld de, 0xFFFF")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_fdiv_ok:")
        self._emit("    ; For simplicity, use iterative division")
        self._emit("    ; Shift dividend left by 16 bits")
        self._emit("    ; dividend = (a_hi, a_lo, 0, 0) / (0, 0, b_hi, b_lo)")
        self._emit("    ; Result is 32-bit quotient")
        self._emit("    ;")
        self._emit("    ; Simplified: divide as integers, shift for fractional")
        self._emit("    ld l, (ix+FIX_DIV_A_HI)")
        self._emit("    ld h, (ix+FIX_DIV_A_HI+1)")
        self._emit("    ld e, (ix+FIX_DIV_B_HI)")
        self._emit("    ld d, (ix+FIX_DIV_B_HI+1)")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _fdiv_frac")
        self._emit("    call _div16")
        self._emit("    ; hl = quotient, put in result_hi")
        self._emit("    ; Now compute fractional part")
        self._emit("    push hl          ; save result_hi")
        self._emit("    ; Remainder-based fraction calc (simplified)")
        self._emit("    ; de still has remainder from _div16")
        self._emit("    ld hl, 0")
        self._emit("    pop hl           ; restore result_hi")
        self._emit("    ld de, 0         ; result_lo = 0 for now")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_fdiv_frac:")
        self._emit("    ; Divisor has no integer part, result will be large")
        self._emit("    ; For now, just do simple integer division")
        self._emit("    ld l, (ix+FIX_DIV_A_LO)")
        self._emit("    ld h, (ix+FIX_DIV_A_LO+1)")
        self._emit("    ld e, (ix+FIX_DIV_B_LO)")
        self._emit("    ld d, (ix+FIX_DIV_B_LO+1)")
        self._emit("    call _div16")
        self._emit("    ; Result goes to hi word")
        self._emit("    ld de, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_cmp: Compare two 16.16 fixed-point numbers")
        self._emit("; Input: Stack: a_hi, a_lo, b_hi, b_lo")
        self._emit("; Output: A = -1 (a<b), 0 (a=b), 1 (a>b), flags set")
        self._emit("FIX_CMP_A_LO     .equ 4")
        self._emit("FIX_CMP_A_HI     .equ 6")
        self._emit("FIX_CMP_B_LO     .equ 8")
        self._emit("FIX_CMP_B_HI     .equ 10")
        self._emit("")
        self._emit("_fix_cmp:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Compare high words first")
        self._emit("    ld l, (ix+FIX_CMP_A_HI)")
        self._emit("    ld h, (ix+FIX_CMP_A_HI+1)")
        self._emit("    ld e, (ix+FIX_CMP_B_HI)")
        self._emit("    ld d, (ix+FIX_CMP_B_HI+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr nz, _fcmp_done")
        self._emit("    ; High words equal, compare low words")
        self._emit("    ld l, (ix+FIX_CMP_A_LO)")
        self._emit("    ld h, (ix+FIX_CMP_A_LO+1)")
        self._emit("    ld e, (ix+FIX_CMP_B_LO)")
        self._emit("    ld d, (ix+FIX_CMP_B_LO+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("_fcmp_done:")
        self._emit("    pop ix")
        self._emit("    ; Set result based on flags")
        self._emit("    jr z, _fcmp_eq")
        self._emit("    jr c, _fcmp_lt")
        self._emit("    ld a, 1          ; a > b")
        self._emit("    ret")
        self._emit("_fcmp_lt:")
        self._emit("    ld a, 0xFF       ; a < b (-1)")
        self._emit("    ret")
        self._emit("_fcmp_eq:")
        self._emit("    xor a            ; a = b (0)")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_neg: Negate 16.16 fixed-point number")
        self._emit("; Input: hl = hi, de = lo")
        self._emit("; Output: hl = hi, de = lo (negated)")
        self._emit("_fix_neg:")
        self._emit("    ; Two's complement of 32-bit value")
        self._emit("    ld a, e")
        self._emit("    cpl")
        self._emit("    ld e, a")
        self._emit("    ld a, d")
        self._emit("    cpl")
        self._emit("    ld d, a")
        self._emit("    ld a, l")
        self._emit("    cpl")
        self._emit("    ld l, a")
        self._emit("    ld a, h")
        self._emit("    cpl")
        self._emit("    ld h, a")
        self._emit("    ; Add 1")
        self._emit("    inc de")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    ret nz")
        self._emit("    inc hl           ; carry")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_abs: Absolute value of 16.16 fixed-point")
        self._emit("; Input: hl = hi, de = lo")
        self._emit("; Output: hl = hi, de = lo (absolute)")
        self._emit("_fix_abs:")
        self._emit("    bit 7, h         ; check sign")
        self._emit("    ret z            ; positive, done")
        self._emit("    jr _fix_neg      ; negate")
        self._emit("")
        self._emit("; _fix_to_int: Convert 16.16 fixed-point to integer (truncate)")
        self._emit("; Input: hl = hi word, de = lo word")
        self._emit("; Output: hl = integer value")
        self._emit("_fix_to_int:")
        self._emit("    ; Just return the high word (integer part)")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_to_int_round: Convert with rounding")
        self._emit("; Input: hl = hi word, de = lo word")
        self._emit("; Output: hl = integer value (rounded)")
        self._emit("_fix_to_int_round:")
        self._emit("    bit 7, d         ; check if frac >= 0.5")
        self._emit("    ret z            ; no rounding needed")
        self._emit("    inc hl           ; round up")
        self._emit("    ret")
        self._emit("")
        self._emit("; _int_to_fix: Convert integer to 16.16 fixed-point")
        self._emit("; Input: hl = integer value")
        self._emit("; Output: hl = hi word, de = lo word (0)")
        self._emit("_int_to_fix:")
        self._emit("    ld de, 0         ; fractional part = 0")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_floor: Floor of fixed-point (truncate toward -inf)")
        self._emit("; Input: hl = hi, de = lo")
        self._emit("; Output: hl = hi, de = 0")
        self._emit("_fix_floor:")
        self._emit("    bit 7, h         ; check sign")
        self._emit("    jr z, _fix_floor_pos")
        self._emit("    ; Negative: if frac != 0, subtract 1 from int part")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _fix_floor_pos")
        self._emit("    dec hl")
        self._emit("_fix_floor_pos:")
        self._emit("    ld de, 0")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_ceil: Ceiling of fixed-point (truncate toward +inf)")
        self._emit("; Input: hl = hi, de = lo")
        self._emit("; Output: hl = hi, de = 0")
        self._emit("_fix_ceil:")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _fix_ceil_done")
        self._emit("    bit 7, h         ; check sign")
        self._emit("    jr nz, _fix_ceil_done")
        self._emit("    ; Positive with fraction: add 1")
        self._emit("    inc hl")
        self._emit("_fix_ceil_done:")
        self._emit("    ld de, 0")
        self._emit("    ret")
        self._emit("")
        self._emit("; _fix_to_str: Convert 16.16 fixed-point to string")
        self._emit("; Input: Stack: hi_word, lo_word")
        self._emit("; Output: hl = pointer to string")
        self._emit("FIX_STR_LO       .equ 4")
        self._emit("FIX_STR_HI       .equ 6")
        self._emit("")
        self._emit("_fix_to_str:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Output buffer")
        self._emit("    ld hl, _fix_str_buf + 1")
        self._emit("    push hl          ; save output ptr")
        self._emit("    ; Check sign")
        self._emit("    ld a, (ix+FIX_STR_HI+1)")
        self._emit("    bit 7, a")
        self._emit("    jr z, _fts_pos")
        self._emit("    ; Negative: output '-' and negate")
        self._emit("    ld (hl), '-'")
        self._emit("    inc hl")
        self._emit("    push hl")
        self._emit("    ld l, (ix+FIX_STR_LO)")
        self._emit("    ld h, (ix+FIX_STR_LO+1)")
        self._emit("    ld e, (ix+FIX_STR_HI)")
        self._emit("    ld d, (ix+FIX_STR_HI+1)")
        self._emit("    ex de, hl")
        self._emit("    call _fix_neg")
        self._emit("    ex de, hl")
        self._emit("    ; Store negated values back")
        self._emit("    ld (ix+FIX_STR_LO), e")
        self._emit("    ld (ix+FIX_STR_LO+1), d")
        self._emit("    ld (ix+FIX_STR_HI), l")
        self._emit("    ld (ix+FIX_STR_HI+1), h")
        self._emit("    pop hl")
        self._emit("_fts_pos:")
        self._emit("    push hl          ; save output ptr")
        self._emit("    ; Convert integer part")
        self._emit("    ld l, (ix+FIX_STR_HI)")
        self._emit("    ld h, (ix+FIX_STR_HI+1)")
        self._emit("    push hl")
        self._emit("    call _int_to_str")
        self._emit("    ; hl = string ptr, copy to output")
        self._emit("    ex de, hl        ; de = int string")
        self._emit("    pop bc           ; discard pushed arg")
        self._emit("    pop hl           ; hl = output ptr")
        self._emit("_fts_copy_int:")
        self._emit("    inc de           ; skip length byte")
        self._emit("    ld a, (de)")
        self._emit("    or a")
        self._emit("    jr z, _fts_decimal")
        self._emit("    ld (hl), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    jr _fts_copy_int")
        self._emit("_fts_decimal:")
        self._emit("    ; Add decimal point")
        self._emit("    ld (hl), '.'")
        self._emit("    inc hl")
        self._emit("    ; Convert fractional part (3 decimal places)")
        self._emit("    ld e, (ix+FIX_STR_LO)")
        self._emit("    ld d, (ix+FIX_STR_LO+1)")
        self._emit("    ; Multiply by 1000 and divide by 65536")
        self._emit("    ; Simplified: just show hex fraction")
        self._emit("    ld b, 3          ; 3 digits")
        self._emit("_fts_frac_loop:")
        self._emit("    ; frac = frac * 10")
        self._emit("    push bc")
        self._emit("    push hl")
        self._emit("    ex de, hl")
        self._emit("    add hl, hl       ; x2")
        self._emit("    push hl")
        self._emit("    add hl, hl       ; x4")
        self._emit("    add hl, hl       ; x8")
        self._emit("    pop de")
        self._emit("    add hl, de       ; x10")
        self._emit("    ; High byte is digit")
        self._emit("    ld a, h")
        self._emit("    and 0x0F")
        self._emit("    add a, '0'")
        self._emit("    ld h, 0          ; keep only fraction")
        self._emit("    ex de, hl")
        self._emit("    pop hl")
        self._emit("    ld (hl), a")
        self._emit("    inc hl")
        self._emit("    pop bc")
        self._emit("    djnz _fts_frac_loop")
        self._emit("    ; Null terminate")
        self._emit("    ld (hl), 0")
        self._emit("    ; Calculate length")
        self._emit("    pop de           ; start of string")
        self._emit("    push de")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    ld a, l")
        self._emit("    pop hl")
        self._emit("    dec hl")
        self._emit("    ld (hl), a       ; store length")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("_fix_str_buf:")
        self._emit("    .ds 20           ; buffer for fixed-point string")
        self._emit("")

    def _generate_float48_runtime(self) -> None:
        """Generate 48-bit floating point runtime wrappers.

        This generates thin wrappers that interface with z88dk's math48 library.
        The actual math is done by z88dk's optimized routines.

        z88dk math48 format (6 bytes):
          Bytes 0-4: 40-bit mantissa (LSB first, sign in bit 7 of byte 4)
          Byte 5: 8-bit exponent (bias 128, 0 = special zero value)

        Register convention:
          Primary accumulator: bc'de'hl' (alternate register set)
          Secondary operand: BCDEHL (main register set)
          Result: bc'de'hl'

        z88dk function names (we alias to these):
          dadd - add
          dsub - subtract
          dmul - multiply
          ddiv - divide
          dneg - negate
          dcmp - compare (result in A: -1/0/1)
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; 48-bit Floating Point Runtime (z88dk math48)")
        self._emit("; =========================================")
        self._emit("; Uses z88dk's math48 library for actual computation")
        self._emit("; Link with: -lmath48")
        self._emit("")

        # Wrapper for add - calls z88dk's dadd
        self._emit("; _FADD48: Add two 48-bit floats")
        self._emit("; Input: Primary in bc'de'hl', secondary in BCDEHL")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FADD48:")
        self._emit("    jp dadd      ; jump to z88dk routine")
        self._emit("")

        # Wrapper for subtract
        self._emit("; _FSUB48: Subtract (primary - secondary)")
        self._emit("_FSUB48:")
        self._emit("    jp dsub")
        self._emit("")

        # Wrapper for multiply
        self._emit("; _FMUL48: Multiply two 48-bit floats")
        self._emit("_FMUL48:")
        self._emit("    jp dmul")
        self._emit("")

        # Wrapper for divide
        self._emit("; _FDIV48: Divide (primary / secondary)")
        self._emit("_FDIV48:")
        self._emit("    jp ddiv")
        self._emit("")

        # Negate - flip sign bit in byte 4 of mantissa
        self._emit("; _FNEG48: Negate float in primary accumulator")
        self._emit("_FNEG48:")
        self._emit("    jp dneg")
        self._emit("")

        # Compare
        self._emit("; _FCMP48: Compare primary vs secondary")
        self._emit("; Output: A = -1 if less, 0 if equal, 1 if greater")
        self._emit("_FCMP48:")
        self._emit("    jp dcmp")
        self._emit("")

        # Absolute value - clear sign bit
        self._emit("; _FABS48: Absolute value of primary")
        self._emit("_FABS48:")
        self._emit("    exx")
        self._emit("    res 7, b     ; clear sign bit in byte 4")
        self._emit("    exx")
        self._emit("    ret")
        self._emit("")

        # Integer to float conversion
        self._emit("; _ITOF48: Convert 16-bit signed integer to 48-bit float")
        self._emit("; Input: hl = integer value")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_ITOF48:")
        self._emit("    jp l_int2f   ; z88dk int-to-float routine")
        self._emit("")

        # Float to integer conversion
        self._emit("; _FTOI48: Convert 48-bit float to 16-bit integer (truncate)")
        self._emit("; Input: Primary in bc'de'hl'")
        self._emit("; Output: hl = integer value")
        self._emit("_FTOI48:")
        self._emit("    jp l_f2int   ; z88dk float-to-int routine")
        self._emit("")

        # =========================================
        # Transcendental Functions
        # =========================================
        self._emit("; =========================================")
        self._emit("; Transcendental Math Functions")
        self._emit("; =========================================")
        self._emit("; All functions take argument in bc'de'hl' (primary)")
        self._emit("; and return result in bc'de'hl' (primary)")
        self._emit("")

        # Square root
        self._emit("; _FSQRT48: Square root")
        self._emit("; Input: Primary in bc'de'hl'")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FSQRT48:")
        self._emit("    jp dsqrt     ; z88dk sqrt routine")
        self._emit("")

        # Sine
        self._emit("; _FSIN48: Sine (radians)")
        self._emit("; Input: Primary in bc'de'hl' (angle in radians)")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FSIN48:")
        self._emit("    jp dsin      ; z88dk sin routine")
        self._emit("")

        # Cosine
        self._emit("; _FCOS48: Cosine (radians)")
        self._emit("; Input: Primary in bc'de'hl' (angle in radians)")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FCOS48:")
        self._emit("    jp dcos      ; z88dk cos routine")
        self._emit("")

        # Tangent
        self._emit("; _FTAN48: Tangent (radians)")
        self._emit("; Input: Primary in bc'de'hl' (angle in radians)")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FTAN48:")
        self._emit("    jp dtan      ; z88dk tan routine")
        self._emit("")

        # Arc sine
        self._emit("; _FASIN48: Arc sine")
        self._emit("; Input: Primary in bc'de'hl' (-1 to 1)")
        self._emit("; Output: Result in bc'de'hl' (radians)")
        self._emit("_FASIN48:")
        self._emit("    jp dasin     ; z88dk asin routine")
        self._emit("")

        # Arc cosine
        self._emit("; _FACOS48: Arc cosine")
        self._emit("; Input: Primary in bc'de'hl' (-1 to 1)")
        self._emit("; Output: Result in bc'de'hl' (radians)")
        self._emit("_FACOS48:")
        self._emit("    jp dacos     ; z88dk acos routine")
        self._emit("")

        # Arc tangent
        self._emit("; _FATAN48: Arc tangent")
        self._emit("; Input: Primary in bc'de'hl'")
        self._emit("; Output: Result in bc'de'hl' (radians)")
        self._emit("_FATAN48:")
        self._emit("    jp datan     ; z88dk atan routine")
        self._emit("")

        # Natural logarithm
        self._emit("; _FLOG48: Natural logarithm (ln)")
        self._emit("; Input: Primary in bc'de'hl' (> 0)")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FLOG48:")
        self._emit("    jp dlog      ; z88dk log routine")
        self._emit("")

        # Base-10 logarithm
        self._emit("; _FLOG10_48: Base-10 logarithm")
        self._emit("; Input: Primary in bc'de'hl' (> 0)")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FLOG10_48:")
        self._emit("    jp dlog10    ; z88dk log10 routine")
        self._emit("")

        # Exponential (e^x)
        self._emit("; _FEXP48: Exponential (e^x)")
        self._emit("; Input: Primary in bc'de'hl'")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FEXP48:")
        self._emit("    jp dexp      ; z88dk exp routine")
        self._emit("")

        # Power (x^y)
        self._emit("; _FPOW48: Power (x^y)")
        self._emit("; Input: Base in bc'de'hl' (primary), exponent in BCDEHL (secondary)")
        self._emit("; Output: Result in bc'de'hl'")
        self._emit("_FPOW48:")
        self._emit("    jp dpow      ; z88dk pow routine")
        self._emit("")

        # Hyperbolic functions
        self._emit("; _FSINH48: Hyperbolic sine")
        self._emit("_FSINH48:")
        self._emit("    jp dsinh     ; z88dk sinh routine")
        self._emit("")

        self._emit("; _FCOSH48: Hyperbolic cosine")
        self._emit("_FCOSH48:")
        self._emit("    jp dcosh     ; z88dk cosh routine")
        self._emit("")

        self._emit("; _FTANH48: Hyperbolic tangent")
        self._emit("_FTANH48:")
        self._emit("    jp dtanh     ; z88dk tanh routine")
        self._emit("")

        # Floor and ceiling
        self._emit("; _FFLOOR48: Floor (round down to integer)")
        self._emit("; Input: Primary in bc'de'hl'")
        self._emit("; Output: Result in bc'de'hl' (still a float)")
        self._emit("_FFLOOR48:")
        self._emit("    jp dfloor    ; z88dk floor routine")
        self._emit("")

        self._emit("; _FCEIL48: Ceiling (round up to integer)")
        self._emit("_FCEIL48:")
        self._emit("    jp dceil     ; z88dk ceil routine")
        self._emit("")

        # Truncation (round toward zero)
        self._emit("; _FTRUNC48: Truncate (round toward zero)")
        self._emit("_FTRUNC48:")
        self._emit("    jp dtrunc    ; z88dk trunc routine")
        self._emit("")

        # Rounding
        self._emit("; _FROUND48: Round to nearest integer")
        self._emit("_FROUND48:")
        self._emit("    ; z88dk doesn't have a direct round, implement via floor(x + 0.5)")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    push hl")
        self._emit("    ; Load 0.5 into secondary")
        self._emit("    ld hl, _FP_HALF")
        self._emit("    call dload   ; load 0.5 to main regs")
        self._emit("    pop hl")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    call dadd    ; x + 0.5")
        self._emit("    call dfloor  ; floor(x + 0.5)")
        self._emit("    ret")
        self._emit("")

        self._emit("_FP_HALF:")
        self._emit("    ; 0.5 in math48 format: mantissa 0x80 00 00 00 00, exp 127")
        self._emit("    .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F")
        self._emit("")

        # Modulo (floating point remainder)
        self._emit("; _FMOD48: Floating point modulo (x mod y)")
        self._emit("; Input: x in bc'de'hl' (primary), y in BCDEHL (secondary)")
        self._emit("; Output: Result in bc'de'hl' (x - y * floor(x/y))")
        self._emit("_FMOD48:")
        self._emit("    jp dfmod     ; z88dk fmod routine")
        self._emit("")

        # Atan2 (two-argument arc tangent)
        self._emit("; _FATAN2_48: Two-argument arc tangent")
        self._emit("; Input: y in bc'de'hl' (primary), x in BCDEHL (secondary)")
        self._emit("; Output: Result in bc'de'hl' (angle in radians)")
        self._emit("_FATAN2_48:")
        self._emit("    jp datan2    ; z88dk atan2 routine")
        self._emit("")

        # Constants
        self._emit("; Mathematical constants in math48 format")
        self._emit("_FP_PI:")
        self._emit("    .db 0xDB, 0x0F, 0x49, 0xC9, 0x00, 0x81  ; pi = 3.14159265...")
        self._emit("_FP_E:")
        self._emit("    .db 0x54, 0xF8, 0x2D, 0xB8, 0x15, 0x81  ; e = 2.71828182...")
        self._emit("_FP_LN2:")
        self._emit("    .db 0x18, 0x72, 0x31, 0x63, 0x17, 0x7F  ; ln(2) = 0.69314718...")
        self._emit("_FP_LN10:")
        self._emit("    .db 0xFD, 0xDE, 0x65, 0x13, 0x5D, 0x81  ; ln(10) = 2.30258509...")
        self._emit("_FP_ZERO:")
        self._emit("    .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  ; 0.0")
        self._emit("_FP_ONE:")
        self._emit("    .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x80  ; 1.0")
        self._emit("_FP_TWO:")
        self._emit("    .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x81  ; 2.0")
        self._emit("")

    def _generate_float48_externals(self) -> None:
        """Generate external declarations for z88dk math48 library."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; External z88dk math48 library references")
        self._emit("; =========================================")
        self._emit("; These are provided by z88dk's math48 library")
        self._emit("; Link with: zcc +cpm -lmath48 ...")
        self._emit("")
        self._emit("    .extern dadd     ; float add")
        self._emit("    .extern dsub     ; float subtract")
        self._emit("    .extern dmul     ; float multiply")
        self._emit("    .extern ddiv     ; float divide")
        self._emit("    .extern dneg     ; float negate")
        self._emit("    .extern dcmp     ; float compare")
        self._emit("    .extern l_int2f  ; int to float")
        self._emit("    .extern l_f2int  ; float to int")
        self._emit("    .extern dload    ; load float from (hl)")
        self._emit("    .extern dstore   ; store float to (hl)")
        self._emit("")
        self._emit("; Transcendental functions")
        self._emit("    .extern dsqrt    ; square root")
        self._emit("    .extern dsin     ; sine")
        self._emit("    .extern dcos     ; cosine")
        self._emit("    .extern dtan     ; tangent")
        self._emit("    .extern dasin    ; arc sine")
        self._emit("    .extern dacos    ; arc cosine")
        self._emit("    .extern datan    ; arc tangent")
        self._emit("    .extern datan2   ; two-arg arc tangent")
        self._emit("    .extern dlog     ; natural logarithm")
        self._emit("    .extern dlog10   ; base-10 logarithm")
        self._emit("    .extern dexp     ; exponential")
        self._emit("    .extern dpow     ; power")
        self._emit("    .extern dsinh    ; hyperbolic sine")
        self._emit("    .extern dcosh    ; hyperbolic cosine")
        self._emit("    .extern dtanh    ; hyperbolic tangent")
        self._emit("    .extern dfloor   ; floor")
        self._emit("    .extern dceil    ; ceiling")
        self._emit("    .extern dtrunc   ; truncate")
        self._emit("    .extern dfmod    ; floating modulo")
        self._emit("")

    def _generate_float64_externals(self) -> None:
        """Generate external declarations for 64-bit IEEE 754 floating-point library."""
        self._emit("")
        self._emit("; =========================================")
        self._emit("; External float64 library references")
        self._emit("; =========================================")
        self._emit("; 64-bit IEEE 754 double precision floating point")
        self._emit("; Link with: ul80 program.rel float64.rel ...")
        self._emit("")
        self._emit("; Basic operations")
        self._emit("    EXTRN _f64_add     ; Add two floats")
        self._emit("    EXTRN _f64_sub     ; Subtract (a - b)")
        self._emit("    EXTRN _f64_mul     ; Multiply")
        self._emit("    EXTRN _f64_div     ; Divide (a / b)")
        self._emit("    EXTRN _f64_rem     ; Remainder (sign follows dividend)")
        self._emit("    EXTRN _f64_mod     ; Modulo (sign follows divisor)")
        self._emit("    EXTRN _f64_neg     ; Negate")
        self._emit("    EXTRN _f64_abs     ; Absolute value")
        self._emit("")
        self._emit("; Comparison operations")
        self._emit("    EXTRN _f64_cmp     ; Compare (returns -1, 0, 1)")
        self._emit("    EXTRN _f64_eq      ; Equal")
        self._emit("    EXTRN _f64_lt      ; Less than")
        self._emit("    EXTRN _f64_le      ; Less or equal")
        self._emit("    EXTRN _f64_gt      ; Greater than")
        self._emit("    EXTRN _f64_ge      ; Greater or equal")
        self._emit("")
        self._emit("; Conversion operations")
        self._emit("    EXTRN _f64_itof    ; Integer to float")
        self._emit("    EXTRN _f64_ftoi    ; Float to integer")
        self._emit("    EXTRN _f64_zero    ; Load 0.0")
        self._emit("    EXTRN _f64_one     ; Load 1.0")
        self._emit("    EXTRN _f64_copy    ; Copy float")
        self._emit("")
        self._emit("; Math operations")
        self._emit("    EXTRN _f64_sqrt    ; Square root")
        self._emit("    EXTRN _f64_floor   ; Floor")
        self._emit("    EXTRN _f64_ceil    ; Ceiling")
        self._emit("    EXTRN _f64_trunc   ; Truncate")
        self._emit("    EXTRN _f64_round   ; Round to nearest")
        self._emit("")
        self._emit("; Transcendental functions")
        self._emit("    EXTRN _f64_e2x     ; e^x exponential")
        self._emit("    EXTRN _f64_log     ; ln(x) natural log")
        self._emit("    EXTRN _f64_sin     ; sin(x)")
        self._emit("    EXTRN _f64_cos     ; cos(x)")
        self._emit("    EXTRN _f64_tan     ; tan(x)")
        self._emit("    EXTRN _f64_atan    ; arctan(x)")
        self._emit("    EXTRN _f64_asin    ; arcsin(x)")
        self._emit("    EXTRN _f64_acos    ; arccos(x)")
        self._emit("    EXTRN _f64_at2     ; atan2(y, x)")
        self._emit("")
        self._emit("; Float64 constants for inlined composite functions")
        self._emit("    EXTRN _const_one_f64 ; 1.0")
        self._emit("    EXTRN _const_2     ; 2.0")
        self._emit("    EXTRN _const_ln10  ; ln(10)")
        self._emit("")

    def _generate_libada_externals(self) -> None:
        """Generate external declarations for libada.lib runtime library.

        These symbols are provided by the runtime library (libada.lib)
        which is assembled separately and linked with ul80.
        Link with: ul80 program.rel -l libada.lib
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; External libada.lib runtime references")
        self._emit("; =========================================")
        self._emit("; Link with: ul80 program.rel -l libada.lib")
        self._emit("")

        # String operations (from strings.asm)
        self._emit("; String operations (Ada.Strings.Fixed)")
        self._emit("    EXTRN _str_move    ; Move source to target")
        self._emit("    EXTRN _str_index   ; Find pattern in string")
        self._emit("    EXTRN _str_index_char ; Find char in string")
        self._emit("    EXTRN _str_count   ; Count pattern occurrences")
        self._emit("    EXTRN _str_head    ; Get first N chars")
        self._emit("    EXTRN _str_tail    ; Get last N chars")
        self._emit("    EXTRN _str_trim    ; Trim whitespace")
        self._emit("    EXTRN _str_len     ; Get string length")
        self._emit("")

        # Container operations (from containers.asm)
        self._emit("; Container operations (Ada.Containers)")
        self._emit("    EXTRN _vec_create  ; Create vector")
        self._emit("    EXTRN _vec_length  ; Get vector length")
        self._emit("    EXTRN _vec_is_empty ; Check if empty")
        self._emit("    EXTRN _vec_clear   ; Clear vector")
        self._emit("    EXTRN _vec_first   ; Get first cursor")
        self._emit("    EXTRN _vec_last    ; Get last cursor")
        self._emit("    EXTRN _vec_append  ; Append element")
        self._emit("    EXTRN _vec_element ; Get element at index")
        self._emit("    EXTRN _cont_element ; Get element - dispatches by type")
        self._emit("    EXTRN _vec_replace ; Replace element")
        self._emit("    EXTRN _vec_delete_last ; Delete last element")
        self._emit("    EXTRN _vec_capacity ; Get capacity")
        self._emit("    EXTRN _vec_reserve ; Reserve capacity")
        self._emit("    EXTRN _cursor_next ; Move cursor forward")
        self._emit("    EXTRN _cursor_previous ; Move cursor backward")
        self._emit("    EXTRN _cursor_has_element ; Check cursor validity")
        self._emit("")

        # List operations (stubs in containers.asm)
        self._emit("    EXTRN _list_create ; Create list")
        self._emit("    EXTRN _list_length ; Get list length")
        self._emit("    EXTRN _list_is_empty ; Check if empty")
        self._emit("    EXTRN _list_clear  ; Clear list")
        self._emit("    EXTRN _list_first  ; Get first cursor")
        self._emit("    EXTRN _list_last   ; Get last cursor")
        self._emit("    EXTRN _list_append ; Append to list")
        self._emit("    EXTRN _list_prepend ; Prepend to list")
        self._emit("    EXTRN _list_delete ; Delete at cursor")
        self._emit("    EXTRN _list_element ; Get element at index")
        self._emit("")
        self._emit("    ; Hash functions")
        self._emit("    EXTRN _str_hash    ; Hash string (DJB2)")
        self._emit("    EXTRN _int_hash    ; Hash 16-bit integer")
        self._emit("")
        self._emit("    ; Hash map operations")
        self._emit("    EXTRN _hmap_create ; Create hash map")
        self._emit("    EXTRN _hmap_find   ; Find value by key")
        self._emit("    EXTRN _hmap_insert ; Insert key-value pair")
        self._emit("    EXTRN _hmap_delete ; Delete by key")
        self._emit("")
        self._emit("    ; Hash set operations")
        self._emit("    EXTRN _hset_create  ; Create hash set")
        self._emit("    EXTRN _hset_contains ; Check membership")
        self._emit("    EXTRN _hset_insert  ; Insert element")
        self._emit("    EXTRN _hset_delete  ; Delete element")
        self._emit("")
        self._emit("    ; Ordered map operations (BST)")
        self._emit("    EXTRN _omap_create  ; Create ordered map")
        self._emit("    EXTRN _omap_find    ; Find value by key")
        self._emit("    EXTRN _omap_insert  ; Insert key-value")
        self._emit("    EXTRN _omap_delete  ; Delete by key")
        self._emit("")
        self._emit("    ; Ordered set operations (BST)")
        self._emit("    EXTRN _oset_create  ; Create ordered set")
        self._emit("    EXTRN _oset_contains ; Check membership")
        self._emit("    EXTRN _oset_insert  ; Insert element")
        self._emit("    EXTRN _oset_delete  ; Delete element")
        self._emit("")
        self._emit("    ; Container iteration")
        self._emit("    EXTRN _cont_first   ; Start iteration")
        self._emit("    EXTRN _cont_next    ; Get next cursor")
        self._emit("    EXTRN _cont_has_element ; Check cursor valid")
        self._emit("    EXTRN _cont_cursor_element ; Get element at cursor")
        self._emit("")
        self._emit("    ; Container utilities")
        self._emit("    EXTRN _cont_length  ; Get element count")
        self._emit("    EXTRN _cont_is_empty ; Check if empty")
        self._emit("    EXTRN _cont_clear   ; Remove all elements")
        self._emit("")

        # Heap operations (from heap.asm)
        self._emit("; Heap operations")
        self._emit("    EXTRN _heap_init   ; Initialize heap")
        self._emit("    EXTRN _heap_alloc  ; Allocate memory")
        self._emit("    EXTRN _heap_free   ; Free memory")
        self._emit("")

        # Math operations (from math.asm)
        self._emit("; Math operations")
        self._emit("    EXTRN _mul16       ; 16-bit multiply")
        self._emit("    EXTRN _mul16_32    ; 16-bit multiply, 32-bit result")
        self._emit("    EXTRN _div16       ; 16-bit divide")
        self._emit("    EXTRN _div16_signed ; Signed divide")
        self._emit("    EXTRN _div16_mod   ; Divide with modulo (for int_to_str)")
        self._emit("    EXTRN _mod16       ; 16-bit modulo")
        self._emit("    EXTRN _abs16       ; Absolute value")
        self._emit("    EXTRN _neg16       ; Negate")
        self._emit("    EXTRN _min16       ; Minimum")
        self._emit("    EXTRN _max16       ; Maximum")
        self._emit("    EXTRN _fix_add     ; Fixed-point add")
        self._emit("    EXTRN _fix_sub     ; Fixed-point subtract")
        self._emit("    EXTRN _fix_neg     ; Fixed-point negate")
        self._emit("    EXTRN _fix_abs     ; Fixed-point absolute")
        self._emit("    EXTRN _fix_to_int  ; Fixed to integer")
        self._emit("    EXTRN _fix_from_int ; Integer to fixed")
        self._emit("    EXTRN _fix_cmp     ; Fixed-point compare")
        self._emit("")

        # I/O operations (from io.asm)
        # Note: _put_int, _int_to_str, _str_to_int are defined inline - don't declare EXTRN
        self._emit("; I/O operations")
        self._emit("    EXTRN _putchar     ; Output character")
        self._emit("    EXTRN _getchar     ; Input character with echo")
        self._emit("    EXTRN _getchar_noecho ; Input without echo")
        self._emit("    EXTRN _kbhit       ; Check keyboard")
        self._emit("    EXTRN _puts        ; Output string")
        self._emit("    EXTRN _put_newline ; Output CR/LF")
        self._emit("    EXTRN _put_hex     ; Output hex")
        self._emit("    EXTRN _fopen       ; Open file")
        self._emit("    EXTRN _fclose      ; Close file")
        self._emit("    EXTRN _fread       ; Read from file")
        self._emit("    EXTRN _fwrite      ; Write to file")
        self._emit("    EXTRN _fcreate     ; Create file")
        self._emit("    EXTRN _fdelete     ; Delete file")
        self._emit("")

    def _generate_tasking_runtime(self) -> None:
        """Generate preemptive multitasking runtime.

        Task model:
        - Up to 8 concurrent tasks (configurable)
        - Preemptive scheduling via timer interrupt (IM 1 at RST 38h)
        - Round-robin with priority support
        - Entry calls for task synchronization

        TCB (Task Control Block) structure - 16 bytes:
          +0:  saved_sp (2 bytes) - task's stack pointer
          +2:  stack_base (2 bytes) - bottom of task's stack
          +4:  stack_size (2 bytes) - size of stack allocation
          +6:  state (1 byte) - 0=inactive, 1=ready, 2=waiting, 3=terminated
          +7:  priority (1 byte) - higher = more important
          +8:  next (2 bytes) - next TCB in ready/wait queue
          +10: prev (2 bytes) - previous TCB in queue
          +12: task_id (2 bytes) - unique task identifier
          +14: delay_ticks (2 bytes) - ticks remaining for delay
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Tasking Runtime (Preemptive Multitasking)")
        self._emit("; =========================================")
        self._emit("")

        # Constants
        self._emit("; Task states")
        self._emit("TASK_INACTIVE   .equ 0")
        self._emit("TASK_READY      .equ 1")
        self._emit("TASK_WAITING    .equ 2")
        self._emit("TASK_TERMINATED .equ 3")
        self._emit("")
        self._emit("; TCB (Task Control Block) field offsets")
        self._emit("; TCB is 16 bytes total")
        self._emit("TCB_SAVED_SP    .equ 0    ; saved stack pointer (2 bytes)")
        self._emit("TCB_STACK_BASE  .equ 2    ; base of task's stack (2 bytes)")
        self._emit("TCB_STACK_SIZE  .equ 4    ; size of stack allocation (2 bytes)")
        self._emit("TCB_STATE       .equ 6    ; task state (1 byte)")
        self._emit("TCB_PRIORITY    .equ 7    ; task priority (1 byte)")
        self._emit("TCB_NEXT        .equ 8    ; next TCB in queue (2 bytes)")
        self._emit("TCB_PREV        .equ 10   ; prev TCB in queue (2 bytes)")
        self._emit("TCB_TASK_ID     .equ 12   ; unique task identifier (2 bytes)")
        self._emit("TCB_DELAY_TICKS .equ 14   ; ticks remaining for delay (2 bytes)")
        self._emit("")
        self._emit("; Configuration")
        self._emit("MAX_TASKS       .equ 8")
        self._emit("TCB_SIZE        .equ 16")
        self._emit("DEFAULT_STACK   .equ 256  ; bytes per task stack")
        self._emit("TIME_SLICE      .equ 10   ; ticks per time slice")
        self._emit("DEFAULT_PRIO    .equ 128  ; default task priority (mid-range)")
        self._emit("")

        # Data section
        self._emit("; Tasking data")
        self._emit("_task_current:")
        self._emit("    .dw 0           ; pointer to current task's TCB")
        self._emit("_task_ready_head:")
        self._emit("    .dw 0           ; head of ready queue")
        self._emit("_task_count:")
        self._emit("    .db 0           ; number of active tasks")
        self._emit("_task_next_id:")
        self._emit("    .dw 1           ; next task ID to assign")
        self._emit("_tick_count:")
        self._emit("    .dw 0           ; system tick counter")
        self._emit("_slice_remaining:")
        self._emit("    .db TIME_SLICE  ; ticks left in current slice")
        self._emit("_scheduler_lock:")
        self._emit("    .db 0           ; non-zero = scheduler disabled")
        self._emit("_old_rst38:")
        self._emit("    .dw 0           ; saved RST 38h vector")
        self._emit("")

        # TCB array
        self._emit("; TCB array (MAX_TASKS * TCB_SIZE bytes)")
        self._emit("_tcb_array:")
        self._emit("    .ds MAX_TASKS * TCB_SIZE")
        self._emit("")

        # Initialize tasking system
        self._emit("; _TASK_INIT: Initialize tasking system")
        self._emit("; Called once at program start")
        self._emit("_TASK_INIT:")
        self._emit("    di")
        self._emit("    ; Clear TCB array")
        self._emit("    ld hl, _tcb_array")
        self._emit("    ld de, _tcb_array + 1")
        self._emit("    ld bc, MAX_TASKS * TCB_SIZE - 1")
        self._emit("    ld (hl), 0")
        self._emit("    ldir")
        self._emit("    ; Set up main task as task 0 using ix for indexed access")
        self._emit("    ld ix, _tcb_array")
        self._emit("    ld hl, _tcb_array")
        self._emit("    ld (_task_current), hl")
        self._emit("    ld a, TASK_READY")
        self._emit("    ld (ix+TCB_STATE), a       ; state = ready")
        self._emit("    ld a, DEFAULT_PRIO")
        self._emit("    ld (ix+TCB_PRIORITY), a    ; priority = default")
        self._emit("    ld (ix+TCB_TASK_ID), 1     ; task_id low byte")
        self._emit("    ld (ix+TCB_TASK_ID+1), 0   ; task_id high byte")
        self._emit("    ld hl, 2")
        self._emit("    ld (_task_next_id), hl")
        self._emit("    ld a, 1")
        self._emit("    ld (_task_count), a")
        self._emit("    ; Install timer ISR at RST 38h (IM 1)")
        self._emit("    ld hl, (0x0039)  ; save old vector")
        self._emit("    ld (_old_rst38), hl")
        self._emit("    ld hl, _TIMER_ISR")
        self._emit("    ld (0x0039), hl")
        self._emit("    im 1")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")

        # Timer ISR for preemptive scheduling
        self._emit("; _TIMER_ISR: Timer interrupt service routine")
        self._emit("; Called by RST 38h for preemptive task switching")
        self._emit("_TIMER_ISR:")
        self._emit("    push af")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    push hl")
        self._emit("    push ix")
        self._emit("    push iy")
        self._emit("    ex af, af'")
        self._emit("    exx")
        self._emit("    push af")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    push hl")
        self._emit("    ; Increment tick count")
        self._emit("    ld hl, (_tick_count)")
        self._emit("    inc hl")
        self._emit("    ld (_tick_count), hl")
        self._emit("    ; Check scheduler lock")
        self._emit("    ld a, (_scheduler_lock)")
        self._emit("    or a")
        self._emit("    jr nz, _timer_isr_exit")
        self._emit("    ; Decrement time slice")
        self._emit("    ld a, (_slice_remaining)")
        self._emit("    dec a")
        self._emit("    ld (_slice_remaining), a")
        self._emit("    jr nz, _timer_isr_exit")
        self._emit("    ; Time slice expired - switch tasks")
        self._emit("    call _do_schedule")
        self._emit("_timer_isr_exit:")
        self._emit("    pop hl")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    pop af")
        self._emit("    exx")
        self._emit("    ex af, af'")
        self._emit("    pop iy")
        self._emit("    pop ix")
        self._emit("    pop hl")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    pop af")
        self._emit("    ei")
        self._emit("    reti")
        self._emit("")

        # Scheduler
        self._emit("; _do_schedule: Find next ready task and switch to it")
        self._emit("_do_schedule:")
        self._emit("    ; Save current task's sp")
        self._emit("    ld hl, (_task_current)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _sched_no_current")
        self._emit("    ld (hl), sp    ; save sp at TCB+0")
        self._emit("_sched_no_current:")
        self._emit("    ; Simple round-robin: move to next task")
        self._emit("    ld hl, (_task_current)")
        self._emit("    ld de, TCB_SIZE")
        self._emit("    add hl, de")
        self._emit("    ; Check if past end of array")
        self._emit("    ld de, _tcb_array + (MAX_TASKS * TCB_SIZE)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de")
        self._emit("    jr c, _sched_check_task")
        self._emit("    ; Wrap to start")
        self._emit("    ld hl, _tcb_array")
        self._emit("_sched_check_task:")
        self._emit("    ; Check if this task is ready (hl = TCB address)")
        self._emit("    push hl")
        self._emit("    pop ix           ; ix = TCB address for indexed access")
        self._emit("    ld a, (ix+TCB_STATE)  ; get task state")
        self._emit("    cp TASK_READY")
        self._emit("    jr z, _sched_found")
        self._emit("    ; Try next task (simple search)")
        self._emit("    ld de, TCB_SIZE")
        self._emit("    add hl, de")
        self._emit("    ; Check bounds again")
        self._emit("    ld de, _tcb_array + (MAX_TASKS * TCB_SIZE)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    add hl, de")
        self._emit("    jr c, _sched_check_task")
        self._emit("    ld hl, _tcb_array")
        self._emit("    jr _sched_check_task")
        self._emit("_sched_found:")
        self._emit("    ; Switch to new task")
        self._emit("    ld (_task_current), hl")
        self._emit("    ld sp, (hl)    ; restore sp from TCB+0")
        self._emit("    ld a, TIME_SLICE")
        self._emit("    ld (_slice_remaining), a")
        self._emit("    ret")
        self._emit("")

        # Create new task
        self._emit("; _TASK_CREATE: Create a new task")
        self._emit("; Input: hl = entry point address")
        self._emit("; Output: hl = task ID (0 on failure)")
        self._emit("_TASK_CREATE:")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    push hl         ; save entry point")
        self._emit("    ; Find free TCB slot")
        self._emit("    ld hl, _tcb_array")
        self._emit("    ld b, MAX_TASKS")
        self._emit("_tc_find_slot:")
        self._emit("    push hl")
        self._emit("    pop ix           ; ix = TCB for indexed access")
        self._emit("    ld a, (ix+TCB_STATE)  ; check task state")
        self._emit("    cp TASK_INACTIVE")
        self._emit("    jr z, _tc_found_slot")
        self._emit("    ld de, TCB_SIZE")
        self._emit("    add hl, de")
        self._emit("    djnz _tc_find_slot")
        self._emit("    ; No free slot")
        self._emit("    pop hl")
        self._emit("    ld hl, 0")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("_tc_found_slot:")
        self._emit("    ; hl = TCB address, ix already set from _tc_find_slot")
        self._emit("    push hl         ; save TCB ptr")
        self._emit("    push ix         ; save ix (TCB)")
        self._emit("    ; Allocate stack")
        self._emit("    ld hl, DEFAULT_STACK")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _tc_fail")
        self._emit("    ; hl = stack base, save it")
        self._emit("    ld d, h")
        self._emit("    ld e, l         ; de = stack base")
        self._emit("    pop ix          ; ix = TCB for indexed writes")
        self._emit("    ; Store stack_base at TCB+2")
        self._emit("    ld (ix+TCB_STACK_BASE), e")
        self._emit("    ld (ix+TCB_STACK_BASE+1), d")
        self._emit("    ; Store stack_size at TCB+4")
        self._emit("    ld (ix+TCB_STACK_SIZE), LOW DEFAULT_STACK")
        self._emit("    ld (ix+TCB_STACK_SIZE+1), HIGH DEFAULT_STACK")
        self._emit("    ; Calculate initial sp (top of stack = base + size)")
        self._emit("    ld hl, DEFAULT_STACK")
        self._emit("    add hl, de      ; hl = top of stack")
        self._emit("    ; Set up initial stack frame (push entry point as return addr)")
        self._emit("    dec hl")
        self._emit("    dec hl          ; make room for return address")
        self._emit("    pop de          ; de = entry point (was saved earlier)")
        self._emit("    ld (hl), e      ; store entry point low byte")
        self._emit("    inc hl")
        self._emit("    ld (hl), d      ; store entry point high byte")
        self._emit("    dec hl          ; hl = initial sp")
        self._emit("    ; Save initial sp in TCB+0")
        self._emit("    ld (ix+TCB_SAVED_SP), l")
        self._emit("    ld (ix+TCB_SAVED_SP+1), h")
        self._emit("    ; Set task state to READY")
        self._emit("    ld (ix+TCB_STATE), TASK_READY")
        self._emit("    ; Set default priority")
        self._emit("    ld (ix+TCB_PRIORITY), DEFAULT_PRIO")
        self._emit("    ; Assign task ID")
        self._emit("    ld hl, (_task_next_id)")
        self._emit("    ld (ix+TCB_TASK_ID), l")
        self._emit("    ld (ix+TCB_TASK_ID+1), h")
        self._emit("    push hl         ; save task ID for return")
        self._emit("    inc hl")
        self._emit("    ld (_task_next_id), hl")
        self._emit("    ; Increment task count")
        self._emit("    ld a, (_task_count)")
        self._emit("    inc a")
        self._emit("    ld (_task_count), a")
        self._emit("    ; Return task ID in hl")
        self._emit("    pop hl          ; hl = task ID")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("_tc_fail:")
        self._emit("    pop hl")
        self._emit("    pop hl")
        self._emit("    ld hl, 0")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("")

        # Yield
        self._emit("; _TASK_YIELD: Voluntarily yield to scheduler")
        self._emit("_TASK_YIELD:")
        self._emit("    di")
        self._emit("    call _do_schedule")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")

        # Terminate
        self._emit("; _TASK_TERMINATE: Terminate current task")
        self._emit("_TASK_TERMINATE:")
        self._emit("    di")
        self._emit("    ld hl, (_task_current)")
        self._emit("    push hl")
        self._emit("    pop ix            ; ix = TCB pointer")
        self._emit("    ld a, TASK_TERMINATED")
        self._emit("    ld (ix+TCB_STATE), a")
        self._emit("    ; Decrement task count")
        self._emit("    ld a, (_task_count)")
        self._emit("    dec a")
        self._emit("    ld (_task_count), a")
        self._emit("    ; Schedule another task")
        self._emit("    call _do_schedule")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")

        # Delay
        self._emit("; _TASK_DELAY: Delay for hl ticks")
        self._emit("_TASK_DELAY:")
        self._emit("    di")
        self._emit("    push hl           ; save ticks to delay")
        self._emit("    ld hl, (_task_current)")
        self._emit("    push hl")
        self._emit("    pop ix            ; ix = TCB pointer")
        self._emit("    pop de            ; de = ticks to delay")
        self._emit("    ld (ix+TCB_DELAY_TICKS), e")
        self._emit("    ld (ix+TCB_DELAY_TICKS+1), d")
        self._emit("    ld a, TASK_WAITING")
        self._emit("    ld (ix+TCB_STATE), a")
        self._emit("    call _do_schedule")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")

        # Delay until
        self._emit("; _TASK_DELAY_UNTIL: Delay until tick count reaches hl")
        self._emit("_TASK_DELAY_UNTIL:")
        self._emit("    di")
        self._emit("    ld de, (_tick_count)")
        self._emit("    or a")
        self._emit("    sbc hl, de      ; ticks to wait = target - current")
        self._emit("    jr nc, _delay_until_positive")
        self._emit("    ld hl, 0        ; already past, no delay")
        self._emit("_delay_until_positive:")
        self._emit("    push hl           ; save ticks to delay")
        self._emit("    ld hl, (_task_current)")
        self._emit("    push hl")
        self._emit("    pop ix            ; ix = TCB pointer")
        self._emit("    pop de            ; de = ticks to delay")
        self._emit("    ld (ix+TCB_DELAY_TICKS), e")
        self._emit("    ld (ix+TCB_DELAY_TICKS+1), d")
        self._emit("    ld a, TASK_WAITING")
        self._emit("    ld (ix+TCB_STATE), a")
        self._emit("    call _do_schedule")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")

        # Task abort
        self._emit("; _TASK_ABORT: Abort a task by ID")
        self._emit("; Input: hl = task ID to abort")
        self._emit("_TASK_ABORT:")
        self._emit("    di")
        self._emit("    push hl")
        self._emit("    call _find_task_by_id")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _abort_not_found")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    ; Check if already terminated")
        self._emit("    ld a, (ix+TCB_STATE)")
        self._emit("    cp TASK_TERMINATED")
        self._emit("    jr z, _abort_done")
        self._emit("    ; Mark as terminated")
        self._emit("    ld a, TASK_TERMINATED")
        self._emit("    ld (ix+TCB_STATE), a")
        self._emit("    ; Decrement task count")
        self._emit("    ld a, (_task_count)")
        self._emit("    dec a")
        self._emit("    ld (_task_count), a")
        self._emit("_abort_done:")
        self._emit("    pop hl")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("_abort_not_found:")
        self._emit("    pop hl")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")

        # Entry queue structure and constants
        self._emit("; Entry queue structure (8 bytes per entry)")
        self._emit("; Entry queues are used for task rendezvous")
        self._emit("ENTRY_Q_NEXT     .equ 0    ; next entry in queue (2 bytes)")
        self._emit("ENTRY_Q_CALLER   .equ 2    ; caller task TCB (2 bytes)")
        self._emit("ENTRY_Q_ENTRY_ID .equ 4    ; entry ID being called (2 bytes)")
        self._emit("ENTRY_Q_PARAMS   .equ 6    ; parameter block pointer (2 bytes)")
        self._emit("ENTRY_Q_SIZE     .equ 8    ; size of queue entry")
        self._emit("")
        self._emit("; Maximum entry queue nodes")
        self._emit("MAX_ENTRY_NODES  .equ 16")
        self._emit("")
        self._emit("; Entry queue data")
        self._emit("_entry_queue_head:")
        self._emit("    .dw 0            ; head of pending entry calls queue")
        self._emit("_entry_free_list:")
        self._emit("    .dw _entry_nodes ; head of free node list")
        self._emit("_entry_nodes:")
        self._emit("    .ds ENTRY_Q_SIZE * MAX_ENTRY_NODES")
        self._emit("")
        self._emit("; _entry_init: Initialize entry queue free list")
        self._emit("_entry_init:")
        self._emit("    ld hl, _entry_nodes")
        self._emit("    ld de, ENTRY_Q_SIZE")
        self._emit("    ld b, MAX_ENTRY_NODES - 1")
        self._emit("_ei_loop:")
        self._emit("    push hl")
        self._emit("    add hl, de       ; next node")
        self._emit("    ex de, hl        ; de = next node")
        self._emit("    pop hl           ; hl = current node")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    ld (ix+ENTRY_Q_NEXT), e")
        self._emit("    ld (ix+ENTRY_Q_NEXT+1), d")
        self._emit("    ex de, hl        ; hl = next node")
        self._emit("    ld de, ENTRY_Q_SIZE")
        self._emit("    djnz _ei_loop")
        self._emit("    ; Terminate last node")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    xor a")
        self._emit("    ld (ix+ENTRY_Q_NEXT), a")
        self._emit("    ld (ix+ENTRY_Q_NEXT+1), a")
        self._emit("    ret")
        self._emit("")
        self._emit("; _entry_alloc: Allocate a queue node")
        self._emit("; Output: hl = node pointer (0 if none)")
        self._emit("_entry_alloc:")
        self._emit("    ld hl, (_entry_free_list)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    ret z            ; no free nodes")
        self._emit("    ; Remove from free list")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    ld e, (ix+ENTRY_Q_NEXT)")
        self._emit("    ld d, (ix+ENTRY_Q_NEXT+1)")
        self._emit("    ld (_entry_free_list), de")
        self._emit("    ret")
        self._emit("")
        self._emit("; _entry_free: Free a queue node")
        self._emit("; Input: hl = node pointer")
        self._emit("_entry_free:")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    ld de, (_entry_free_list)")
        self._emit("    ld (ix+ENTRY_Q_NEXT), e")
        self._emit("    ld (ix+ENTRY_Q_NEXT+1), d")
        self._emit("    ld (_entry_free_list), hl")
        self._emit("    ret")
        self._emit("")

        # Entry call (synchronous rendezvous)
        self._emit("; _ENTRY_CALL: Call a task entry (rendezvous)")
        self._emit("; Stack: entry_id, task_id, params_ptr")
        self._emit("; Blocks caller until entry is accepted")
        self._emit("_ENTRY_CALL:")
        self._emit("    di")
        self._emit("    ; Set up stack frame")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get parameters from stack (ix+4=ret, ix+6=entry_id, ix+8=task_id, ix+10=params)")
        self._emit("    ld l, (ix+8)     ; task_id low")
        self._emit("    ld h, (ix+9)     ; task_id high")
        self._emit("    push hl          ; save task_id")
        self._emit("    ld e, (ix+6)     ; entry_id low")
        self._emit("    ld d, (ix+7)     ; entry_id high")
        self._emit("    push de          ; save entry_id")
        self._emit("    ld l, (ix+10)    ; params_ptr low")
        self._emit("    ld h, (ix+11)    ; params_ptr high")
        self._emit("    push hl          ; save params_ptr")
        self._emit("    ; Allocate queue node")
        self._emit("    call _entry_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _ec_fail")
        self._emit("    ; hl = queue node, fill it in")
        self._emit("    push hl")
        self._emit("    pop ix           ; ix = queue node")
        self._emit("    ; Set caller TCB")
        self._emit("    ld hl, (_task_current)")
        self._emit("    ld (ix+ENTRY_Q_CALLER), l")
        self._emit("    ld (ix+ENTRY_Q_CALLER+1), h")
        self._emit("    ; Set entry ID")
        self._emit("    pop hl           ; discard params (stack order)")
        self._emit("    pop de           ; entry_id")
        self._emit("    ld (ix+ENTRY_Q_ENTRY_ID), e")
        self._emit("    ld (ix+ENTRY_Q_ENTRY_ID+1), d")
        self._emit("    pop hl           ; task_id (target)")
        self._emit("    push hl          ; save for later")
        self._emit("    ; Set params ptr (re-get from frame)")
        self._emit("    ld l, (ix+10)")
        self._emit("    ld h, (ix+11)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld (ix+ENTRY_Q_PARAMS), l")
        self._emit("    ld (ix+ENTRY_Q_PARAMS+1), h")
        self._emit("    ; Add to pending queue (at head)")
        self._emit("    ld hl, (_entry_queue_head)")
        self._emit("    ld (ix+ENTRY_Q_NEXT), l")
        self._emit("    ld (ix+ENTRY_Q_NEXT+1), h")
        self._emit("    push ix")
        self._emit("    pop hl")
        self._emit("    ld (_entry_queue_head), hl")
        self._emit("    ; Mark caller as ENTRY_WAIT")
        self._emit("    ld hl, (_task_current)")
        self._emit("    push hl")
        self._emit("    pop iy           ; iy = caller TCB")
        self._emit("    ld a, TASK_ENTRY_WAIT")
        self._emit("    ld (iy+TCB_STATE), a")
        self._emit("    ; Wake target task if it's waiting on accept")
        self._emit("    pop hl           ; target task_id")
        self._emit("    call _find_task_by_id")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _ec_no_target")
        self._emit("    push hl")
        self._emit("    pop iy           ; iy = target TCB")
        self._emit("    ld a, (iy+TCB_STATE)")
        self._emit("    cp TASK_ACCEPT_WAIT")
        self._emit("    jr nz, _ec_schedule")
        self._emit("    ; Target is waiting for accept, wake it")
        self._emit("    ld a, TASK_READY")
        self._emit("    ld (iy+TCB_STATE), a")
        self._emit("_ec_schedule:")
        self._emit("_ec_no_target:")
        self._emit("    ; Schedule away from caller")
        self._emit("    call _do_schedule")
        self._emit("    ; When we return, rendezvous is complete")
        self._emit("    pop ix")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("_ec_fail:")
        self._emit("    ; No queue node available - raise Tasking_Error")
        self._emit("    pop hl")
        self._emit("    pop hl")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ei")
        self._emit("    jp _raise_tasking_error")
        self._emit("")
        self._emit("; Task state for entry wait")
        self._emit("TASK_ENTRY_WAIT  .equ 4    ; waiting for entry call to complete")
        self._emit("TASK_ACCEPT_WAIT .equ 5    ; waiting in accept statement")
        self._emit("")

        # Entry accept
        self._emit("; _ENTRY_ACCEPT: Accept an entry call")
        self._emit("; Input: hl = entry_id to accept")
        self._emit("; Output: hl = params_ptr from caller (0 if none)")
        self._emit("; Blocks until a matching entry call arrives")
        self._emit("_ENTRY_ACCEPT:")
        self._emit("    di")
        self._emit("    push ix")
        self._emit("    push hl          ; save entry_id")
        self._emit("_ea_check:")
        self._emit("    ; Search queue for matching entry call")
        self._emit("    ld hl, _entry_queue_head")
        self._emit("    ld iy, 0         ; prev pointer (0 = head)")
        self._emit("_ea_search:")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)       ; de = current node")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _ea_wait   ; queue empty, wait")
        self._emit("    ; de = node, check if entry_id matches")
        self._emit("    push de")
        self._emit("    pop ix           ; ix = node")
        self._emit("    ld l, (ix+ENTRY_Q_ENTRY_ID)")
        self._emit("    ld h, (ix+ENTRY_Q_ENTRY_ID+1)")
        self._emit("    ex (sp), hl      ; hl = wanted entry_id, stack = node entry_id")
        self._emit("    pop de           ; de = node entry_id")
        self._emit("    push hl          ; save wanted entry_id")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr z, _ea_found")
        self._emit("    ; Not a match, move to next")
        self._emit("    push ix")
        self._emit("    pop iy           ; iy = prev (this node)")
        self._emit("    push ix")
        self._emit("    pop hl")
        self._emit("    jr _ea_search")
        self._emit("_ea_wait:")
        self._emit("    ; No matching entry, block and wait")
        self._emit("    ld hl, (_task_current)")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    ld a, TASK_ACCEPT_WAIT")
        self._emit("    ld (ix+TCB_STATE), a")
        self._emit("    call _do_schedule")
        self._emit("    ; Woken up, check queue again")
        self._emit("    jr _ea_check")
        self._emit("_ea_found:")
        self._emit("    ; ix = matching node, iy = prev node (or 0)")
        self._emit("    ; Remove node from queue")
        self._emit("    ld e, (ix+ENTRY_Q_NEXT)")
        self._emit("    ld d, (ix+ENTRY_Q_NEXT+1)")
        self._emit("    ; Check if prev is head")
        self._emit("    push iy")
        self._emit("    pop hl")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _ea_unlink_mid")
        self._emit("    ; Unlinking head")
        self._emit("    ld (_entry_queue_head), de")
        self._emit("    jr _ea_unlinked")
        self._emit("_ea_unlink_mid:")
        self._emit("    ; iy = prev node")
        self._emit("    ld (iy+ENTRY_Q_NEXT), e")
        self._emit("    ld (iy+ENTRY_Q_NEXT+1), d")
        self._emit("_ea_unlinked:")
        self._emit("    ; Get params pointer to return")
        self._emit("    ld l, (ix+ENTRY_Q_PARAMS)")
        self._emit("    ld h, (ix+ENTRY_Q_PARAMS+1)")
        self._emit("    push hl          ; save params")
        self._emit("    ; Wake caller task")
        self._emit("    ld l, (ix+ENTRY_Q_CALLER)")
        self._emit("    ld h, (ix+ENTRY_Q_CALLER+1)")
        self._emit("    push hl")
        self._emit("    pop iy           ; iy = caller TCB")
        self._emit("    ld a, TASK_READY")
        self._emit("    ld (iy+TCB_STATE), a")
        self._emit("    ; Free the node")
        self._emit("    push ix")
        self._emit("    pop hl")
        self._emit("    call _entry_free")
        self._emit("    ; Return params ptr")
        self._emit("    pop hl           ; params")
        self._emit("    pop de           ; discard saved entry_id")
        self._emit("    pop ix")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")
        self._emit("; _ENTRY_ACCEPT_END: Mark end of accept body")
        self._emit("; Called when accept body completes")
        self._emit("_ENTRY_ACCEPT_END:")
        self._emit("    ; Rendezvous complete - caller already woken in _ENTRY_ACCEPT")
        self._emit("    ret")
        self._emit("")

        # Select statement support
        self._emit("; Select statement support")
        self._emit("; _SELECT_START: Begin a select statement")
        self._emit("_SELECT_START:")
        self._emit("    ret              ; placeholder for future guard evaluation")
        self._emit("")
        self._emit("; _SELECT_WAIT: Wait for one of the registered alternatives")
        self._emit("; For now, uses simple round-robin check")
        self._emit("_SELECT_WAIT:")
        self._emit("    di")
        self._emit("    push ix")
        self._emit("    ; Check if any entry call is pending")
        self._emit("    ld hl, (_entry_queue_head)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _sw_found")
        self._emit("    ; No calls pending, block")
        self._emit("    ld hl, (_task_current)")
        self._emit("    push hl")
        self._emit("    pop ix")
        self._emit("    ld a, TASK_ACCEPT_WAIT")
        self._emit("    ld (ix+TCB_STATE), a")
        self._emit("    call _do_schedule")
        self._emit("_sw_found:")
        self._emit("    pop ix")
        self._emit("    ei")
        self._emit("    ret")
        self._emit("")
        self._emit("; _SELECT_END: End a select statement")
        self._emit("_SELECT_END:")
        self._emit("    ret")
        self._emit("")

        # Task function aliases - lowering emits lowercase, runtime uses uppercase
        self._emit("; Task runtime aliases (for lowering compatibility)")
        self._emit("_task_accept_start:")
        self._emit("    jp _ENTRY_ACCEPT")
        self._emit("_task_accept_end:")
        self._emit("    jp _ENTRY_ACCEPT_END")
        self._emit("_task_select_start:")
        self._emit("    jp _SELECT_START")
        self._emit("_task_select_wait:")
        self._emit("    jp _SELECT_WAIT")
        self._emit("_task_select_end:")
        self._emit("    jp _SELECT_END")
        self._emit("_task_delay:")
        self._emit("    jp _TASK_DELAY")
        self._emit("_task_delay_until:")
        self._emit("    jp _TASK_DELAY_UNTIL")
        self._emit("_task_abort:")
        self._emit("    jp _TASK_ABORT")
        self._emit("")
        self._emit("; _task_select_register: Register alternative for select")
        self._emit("; Input: Stack: alternative_index")
        self._emit("; For now just a placeholder - select waits for any entry")
        self._emit("_task_select_register:")
        self._emit("    ret")
        self._emit("")
        self._emit("; _task_requeue: Requeue entry call to same or different entry")
        self._emit("; Input: Stack: entry_id (or 0 for same entry)")
        self._emit("_task_requeue:")
        self._emit("    ; Simplified: just return, caller will get rescheduled")
        self._emit("    ret")
        self._emit("")

        # Find task by ID
        self._emit("; _find_task_by_id: Find TCB for task ID")
        self._emit("; Input: hl = task_id")
        self._emit("; Output: hl = TCB pointer (0 if not found)")
        self._emit("_find_task_by_id:")
        self._emit("    ld de, _tcb_array")
        self._emit("    ld b, MAX_TASKS")
        self._emit("_ftbi_loop:")
        self._emit("    push hl")
        self._emit("    push de")
        self._emit("    ld hl, de")
        self._emit("    ld de, TCB_TASK_ID  ; offset to task_id field")
        self._emit("    add hl, de")
        self._emit("    ld e, (hl)")
        self._emit("    inc hl")
        self._emit("    ld d, (hl)")
        self._emit("    pop hl          ; hl = TCB, de = stored task_id")
        self._emit("    ex (sp), hl     ; hl = target task_id, stack = TCB")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    pop hl          ; hl = TCB")
        self._emit("    jr z, _ftbi_found")
        self._emit("    ; Move to next TCB")
        self._emit("    ld de, TCB_SIZE")
        self._emit("    add hl, de")
        self._emit("    djnz _ftbi_loop")
        self._emit("    ld hl, 0")
        self._emit("    ret")
        self._emit("_ftbi_found:")
        self._emit("    ret")
        self._emit("")

    # Z80 instruction mnemonics that need to be uppercased
    _Z80_MNEMONICS = {
        "ld", "ldd", "lddr", "ldi", "ldir",
        "push", "pop", "ex", "exx",
        "add", "adc", "sub", "sbc", "and", "or", "xor", "cp",
        "inc", "dec", "neg", "cpl", "daa",
        "rlca", "rrca", "rla", "rra", "rlc", "rrc", "rl", "rr",
        "sla", "sra", "srl", "sll",
        "bit", "set", "res",
        "jp", "jr", "call", "ret", "reti", "retn", "rst",
        "djnz",
        "nop", "halt", "di", "ei", "im",
        "in", "ini", "inir", "ind", "indr",
        "out", "outi", "otir", "outd", "otdr",
        "ccf", "scf",
    }

    def _emit(self, line: str) -> None:
        """Emit a line of assembly."""
        # Track runtime calls in emitted lines (but not calls to local functions)
        stripped = line.strip().lower()
        if stripped.startswith("call _") and not stripped.startswith("call __"):
            # Extract routine name: "call _routine" or "call _routine ; comment"
            parts = line.split()
            for i, p in enumerate(parts):
                if p.lower() == "call" and i + 1 < len(parts):
                    routine = parts[i + 1].rstrip(",;")
                    if routine.startswith("_") and not routine.startswith("__"):
                        # Check if this is a locally defined function
                        is_local = False
                        if self.module:
                            for func in self.module.functions:
                                if self._mangle_symbol(func.name) == routine:
                                    is_local = True
                                    break
                        if not is_local:
                            self.runtime_deps.add(routine)
                    break

        # Normalize Z80 instruction mnemonics (lowercase for upeepz80 compatibility)
        line = self._normalize_mnemonic(line)
        self.output.append(line)

    def _normalize_mnemonic(self, line: str) -> str:
        """Normalize Z80 instruction mnemonic in an assembly line.

        For upeepz80 compatibility, mnemonics and registers are lowercase.
        This method just returns the line unchanged since we now emit
        lowercase directly.
        """
        return line

    def _call_runtime(self, routine_name: str, comment: str = "") -> None:
        """Emit a call to a runtime routine and track the dependency."""
        self.runtime_deps.add(routine_name)
        if comment:
            self._emit(f"    call {routine_name:<16} ; {comment}")
        else:
            self._emit(f"    call {routine_name}")

    def _generate_runtime_externs(self) -> None:
        """Generate EXTRN declarations for all needed runtime routines."""
        if not self.runtime_deps:
            return

        self._emit("")
        self._emit("; =========================================")
        self._emit("; External runtime library references")
        self._emit("; Link with: ul80 program.rel -l libada.lib")
        self._emit("; =========================================")
        self._emit("")

        for name in sorted(self.runtime_deps):
            self._emit(f"    EXTRN {name}")
        self._emit("")

    def _generate_c_interface_runtime(self) -> None:
        """Generate runtime support for c calling convention interface.

        Z80 C calling conventions (z88dk compatible):
        - Parameters pushed right-to-left on stack (cdecl style)
        - Caller cleans up stack after call
        - Return value in hl (16-bit) or bc:de:hl (32/48-bit)
        - ix/iy preserved across calls
        - bc/de/hl caller-saved

        Ada calling convention:
        - Similar but we use ix frame pointer
        - Callee cleans stack (Pascal-style)
        - Return in hl
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; C Interface Runtime")
        self._emit("; =========================================")
        self._emit("; Support for pragma Import(C, ...) and pragma Export(C, ...)")
        self._emit("")
        self._emit("; C calling convention parameter offsets")
        self._emit("; For cdecl: parameters pushed right-to-left")
        self._emit("; Caller cleans up stack")
        self._emit("")
        self._emit("; _c_call_0: Call c function with 0 parameters")
        self._emit("; Input: de = function address")
        self._emit("; Output: hl = return value")
        self._emit("_c_call_0:")
        self._emit("    push ix           ; save frame pointer")
        self._emit("    ex de, hl")
        self._emit("    call _c_call_hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _c_call_1: Call c function with 1 parameter")
        self._emit("; Input: de = function address, Stack: param1 (2 bytes)")
        self._emit("; Output: hl = return value")
        self._emit("C_CALL_1_PARAM   .equ 6    ; offset to param1 after pushing ix and return addr")
        self._emit("")
        self._emit("_c_call_1:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Push param1 for c function")
        self._emit("    ld l, (ix+C_CALL_1_PARAM)")
        self._emit("    ld h, (ix+C_CALL_1_PARAM+1)")
        self._emit("    push hl")
        self._emit("    ; Call function")
        self._emit("    ex de, hl")
        self._emit("    call _c_call_hl")
        self._emit("    ; Clean up stack (C caller cleanup)")
        self._emit("    pop bc            ; remove param1")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _c_call_2: Call c function with 2 parameters")
        self._emit("; Input: de = function address, Stack: param1, param2")
        self._emit("; Output: hl = return value")
        self._emit("C_CALL_2_PARAM1  .equ 6")
        self._emit("C_CALL_2_PARAM2  .equ 8")
        self._emit("")
        self._emit("_c_call_2:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Push params right-to-left for cdecl")
        self._emit("    ld l, (ix+C_CALL_2_PARAM2)")
        self._emit("    ld h, (ix+C_CALL_2_PARAM2+1)")
        self._emit("    push hl")
        self._emit("    ld l, (ix+C_CALL_2_PARAM1)")
        self._emit("    ld h, (ix+C_CALL_2_PARAM1+1)")
        self._emit("    push hl")
        self._emit("    ; Call function")
        self._emit("    ex de, hl")
        self._emit("    call _c_call_hl")
        self._emit("    ; Clean up stack")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _c_call_3: Call c function with 3 parameters")
        self._emit("C_CALL_3_PARAM1  .equ 6")
        self._emit("C_CALL_3_PARAM2  .equ 8")
        self._emit("C_CALL_3_PARAM3  .equ 10")
        self._emit("")
        self._emit("_c_call_3:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Push params right-to-left")
        self._emit("    ld l, (ix+C_CALL_3_PARAM3)")
        self._emit("    ld h, (ix+C_CALL_3_PARAM3+1)")
        self._emit("    push hl")
        self._emit("    ld l, (ix+C_CALL_3_PARAM2)")
        self._emit("    ld h, (ix+C_CALL_3_PARAM2+1)")
        self._emit("    push hl")
        self._emit("    ld l, (ix+C_CALL_3_PARAM1)")
        self._emit("    ld h, (ix+C_CALL_3_PARAM1+1)")
        self._emit("    push hl")
        self._emit("    ; Call function")
        self._emit("    ex de, hl")
        self._emit("    call _c_call_hl")
        self._emit("    ; Clean up stack")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _c_call_hl: Call function at address in hl")
        self._emit("_c_call_hl:")
        self._emit("    jp (hl)")
        self._emit("")
        self._emit("; Export wrappers - convert Ada calling convention to C")
        self._emit("; When C code calls an exported Ada function, we need a wrapper")
        self._emit("; that sets up the Ada frame pointer convention")
        self._emit("")
        self._emit("; _export_wrapper_template: Template for export wrappers")
        self._emit("; Generated per-function during linking")
        self._emit("; Typically:")
        self._emit(";   push ix")
        self._emit(";   ld ix, 0")
        self._emit(";   add ix, sp")
        self._emit(";   call _ada_function")
        self._emit(";   pop ix")
        self._emit(";   ret")
        self._emit("")
        self._emit("; Type conversion helpers for c interface")
        self._emit("")
        self._emit("; _c_bool_to_ada: Convert C bool (0/non-0) to Ada Boolean (0/1)")
        self._emit("; Input: hl = C bool")
        self._emit("; Output: hl = Ada Boolean")
        self._emit("_c_bool_to_ada:")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _cba_false")
        self._emit("    ld hl, 1          ; True")
        self._emit("    ret")
        self._emit("_cba_false:")
        self._emit("    ld hl, 0          ; False")
        self._emit("    ret")
        self._emit("")
        self._emit("; _ada_bool_to_c: Convert Ada Boolean to C bool")
        self._emit("; Input: hl = Ada Boolean (0 or 1)")
        self._emit("; Output: hl = C bool (same, 0 or 1)")
        self._emit("_ada_bool_to_c:")
        self._emit("    ; Ada and c booleans are compatible for 0/1")
        self._emit("    ret")
        self._emit("")
        self._emit("; _c_ptr_to_ada: Convert C pointer to Ada access type")
        self._emit("; Input: hl = C pointer")
        self._emit("; Output: hl = Ada access (same representation)")
        self._emit("_c_ptr_to_ada:")
        self._emit("    ; Pointers have same representation")
        self._emit("    ret")
        self._emit("")
        self._emit("; _ada_ptr_to_c: Convert Ada access type to C pointer")
        self._emit("; Input: hl = Ada access")
        self._emit("; Output: hl = C pointer")
        self._emit("_ada_ptr_to_c:")
        self._emit("    ; Same representation")
        self._emit("    ret")
        self._emit("")
        self._emit("; String conversion (Ada string to C null-terminated)")
        self._emit("; _ada_str_to_c: Convert Ada string to C string")
        self._emit("; Input: hl = Ada string (length-prefixed)")
        self._emit("; Output: hl = C string (null-terminated, in buffer)")
        self._emit("_ada_str_to_c:")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    ld a, (hl)        ; get length")
        self._emit("    ld b, a           ; B = count")
        self._emit("    inc hl            ; skip length byte")
        self._emit("    ld de, _c_str_buf")
        self._emit("    push de           ; save start")
        self._emit("    ld a, b")
        self._emit("    or a")
        self._emit("    jr z, _astc_done")
        self._emit("_astc_loop:")
        self._emit("    ld a, (hl)")
        self._emit("    ld (de), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    djnz _astc_loop")
        self._emit("_astc_done:")
        self._emit("    xor a")
        self._emit("    ld (de), a        ; null terminate")
        self._emit("    pop hl            ; hl = buffer start")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("")
        self._emit("; _c_str_to_ada: Convert C string to Ada string")
        self._emit("; Input: hl = C string (null-terminated)")
        self._emit("; Output: hl = Ada string (length-prefixed, in buffer)")
        self._emit("_c_str_to_ada:")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    push hl           ; save source")
        self._emit("    ; Count length")
        self._emit("    ld b, 0")
        self._emit("_csta_count:")
        self._emit("    ld a, (hl)")
        self._emit("    or a")
        self._emit("    jr z, _csta_counted")
        self._emit("    inc hl")
        self._emit("    inc b")
        self._emit("    jr _csta_count")
        self._emit("_csta_counted:")
        self._emit("    ; B = length")
        self._emit("    ld a, b")
        self._emit("    ld de, _ada_str_buf")
        self._emit("    ld (de), a        ; store length")
        self._emit("    inc de")
        self._emit("    pop hl            ; restore source")
        self._emit("    ld a, b")
        self._emit("    or a")
        self._emit("    jr z, _csta_done")
        self._emit("_csta_copy:")
        self._emit("    ld a, (hl)")
        self._emit("    ld (de), a")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    djnz _csta_copy")
        self._emit("_csta_done:")
        self._emit("    ld hl, _ada_str_buf")
        self._emit("    pop bc")
        self._emit("    pop de")
        self._emit("    ret")
        self._emit("")
        self._emit("_c_str_buf:")
        self._emit("    .ds 256           ; buffer for c strings")
        self._emit("_ada_str_buf:")
        self._emit("    .ds 256           ; buffer for Ada strings")
        self._emit("")

    def _generate_container_runtime(self) -> None:
        """Generate runtime for Ada.Containers (Vector, Doubly_Linked_List, etc.).

        Container data structures use heap allocation and linked structures.
        All containers use a common header format:
          - Offset 0: Container type ID (1 byte)
          - Offset 1: Flags (1 byte)
          - Offset 2: Element count (2 bytes)
          - Offset 4: Capacity or head pointer (2 bytes)
          - Offset 6: Element size (2 bytes)
          - Offset 8+: Type-specific data

        Cursor format:
          - Offset 0: Container pointer (2 bytes)
          - Offset 2: Position/Node pointer (2 bytes)
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Container Runtime (Ada.Containers)")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Container type IDs")
        self._emit("CONT_TYPE_VECTOR      .equ 1")
        self._emit("CONT_TYPE_LIST        .equ 2")
        self._emit("CONT_TYPE_HASHED_MAP  .equ 3")
        self._emit("CONT_TYPE_ORDERED_MAP .equ 4")
        self._emit("CONT_TYPE_HASHED_SET  .equ 5")
        self._emit("CONT_TYPE_ORDERED_SET .equ 6")
        self._emit("")
        self._emit("; Container header offsets")
        self._emit("CONT_HDR_TYPE        .equ 0    ; container type ID")
        self._emit("CONT_HDR_FLAGS       .equ 1    ; flags byte")
        self._emit("CONT_HDR_COUNT       .equ 2    ; element count")
        self._emit("CONT_HDR_CAPACITY    .equ 4    ; capacity (vectors) or head ptr (lists)")
        self._emit("CONT_HDR_ELEM_SIZE   .equ 6    ; element size in bytes")
        self._emit("CONT_HDR_SIZE        .equ 8    ; header size")
        self._emit("")
        self._emit("; Vector-specific (after header)")
        self._emit("VEC_DATA_PTR         .equ 8    ; pointer to data array")
        self._emit("VEC_TOTAL_SIZE       .equ 10   ; total structure size")
        self._emit("")
        self._emit("; List node structure")
        self._emit("LIST_NODE_PREV       .equ 0    ; previous node")
        self._emit("LIST_NODE_NEXT       .equ 2    ; next node")
        self._emit("LIST_NODE_DATA       .equ 4    ; element data starts here")
        self._emit("")
        self._emit("; Cursor structure")
        self._emit("CURSOR_CONTAINER     .equ 0    ; container pointer")
        self._emit("CURSOR_POSITION      .equ 2    ; position (index or node ptr)")
        self._emit("CURSOR_SIZE          .equ 4")
        self._emit("")
        self._emit("; No_Element cursor (null cursor)")
        self._emit("_no_element:")
        self._emit("    .dw 0, 0         ; null container, null position")
        self._emit("")

        # Vector operations
        self._emit("; ==== Vector Operations ====")
        self._emit("")
        self._emit("; _vec_create: Create a new vector")
        self._emit("; Input: Stack: element_size, initial_capacity")
        self._emit("; Output: hl = vector pointer (0 if failed)")
        self._emit("VEC_CREATE_ELEM_SIZE .equ 4")
        self._emit("VEC_CREATE_CAPACITY  .equ 6")
        self._emit("")
        self._emit("_vec_create:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Allocate header")
        self._emit("    ld hl, VEC_TOTAL_SIZE")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vc_fail")
        self._emit("    push hl           ; save header ptr")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = header")
        self._emit("    ; Initialize header")
        self._emit("    ld (iy+CONT_HDR_TYPE), CONT_TYPE_VECTOR")
        self._emit("    ld (iy+CONT_HDR_FLAGS), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), 0")
        self._emit("    ; Set capacity")
        self._emit("    ld l, (ix+VEC_CREATE_CAPACITY)")
        self._emit("    ld h, (ix+VEC_CREATE_CAPACITY+1)")
        self._emit("    ld (iy+CONT_HDR_CAPACITY), l")
        self._emit("    ld (iy+CONT_HDR_CAPACITY+1), h")
        self._emit("    push hl           ; save capacity")
        self._emit("    ; Set element size")
        self._emit("    ld l, (ix+VEC_CREATE_ELEM_SIZE)")
        self._emit("    ld h, (ix+VEC_CREATE_ELEM_SIZE+1)")
        self._emit("    ld (iy+CONT_HDR_ELEM_SIZE), l")
        self._emit("    ld (iy+CONT_HDR_ELEM_SIZE+1), h")
        self._emit("    ; Allocate data array: capacity * elem_size")
        self._emit("    pop de            ; capacity")
        self._emit("    call _mul16       ; hl = capacity * elem_size")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vc_fail_free")
        self._emit("    ; Store data pointer")
        self._emit("    ld (iy+VEC_DATA_PTR), l")
        self._emit("    ld (iy+VEC_DATA_PTR+1), h")
        self._emit("    ; Return header pointer")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_vc_fail_free:")
        self._emit("    pop hl            ; header ptr")
        self._emit("    call _heap_free")
        self._emit("_vc_fail:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _vec_append: Append element to vector")
        self._emit("; Input: Stack: vector_ptr, element_ptr")
        self._emit("; Output: hl = 1 (success) or 0 (failed)")
        self._emit("VEC_APPEND_VEC       .equ 4")
        self._emit("VEC_APPEND_ELEM      .equ 6")
        self._emit("")
        self._emit("_vec_append:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get vector pointer")
        self._emit("    ld l, (ix+VEC_APPEND_VEC)")
        self._emit("    ld h, (ix+VEC_APPEND_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = vector")
        self._emit("    ; Check capacity")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld e, (iy+CONT_HDR_CAPACITY)")
        self._emit("    ld d, (iy+CONT_HDR_CAPACITY+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr nc, _va_grow   ; count >= capacity, need to grow")
        self._emit("    jr _va_insert")
        self._emit("_va_grow:")
        self._emit("    ; Grow vector: allocate new array with 2x capacity")
        self._emit("    ; iy = vector header, ix = stack frame")
        self._emit("    ; Save iy (vector) and ix (frame)")
        self._emit("    push iy")
        self._emit("    push ix")
        self._emit("    ; Calculate new capacity = old_capacity * 2")
        self._emit("    ld l, (iy+CONT_HDR_CAPACITY)")
        self._emit("    ld h, (iy+CONT_HDR_CAPACITY+1)")
        self._emit("    add hl, hl         ; hl = capacity * 2")
        self._emit("    ; If capacity was 0, set to 4")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _va_grow_calc")
        self._emit("    ld hl, 4           ; minimum capacity")
        self._emit("_va_grow_calc:")
        self._emit("    push hl            ; save new capacity")
        self._emit("    ; Calculate new array size = new_capacity * elem_size")
        self._emit("    ld e, (iy+CONT_HDR_ELEM_SIZE)")
        self._emit("    ld d, (iy+CONT_HDR_ELEM_SIZE+1)")
        self._emit("    call _mul16        ; hl = new array size")
        self._emit("    ; Allocate new array")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _va_grow_fail")
        self._emit("    push hl            ; save new array ptr")
        self._emit("    ; Copy old data to new array")
        self._emit("    ; de = destination (new array)")
        self._emit("    ex de, hl          ; de = new array")
        self._emit("    ; Get old array and count")
        self._emit("    pop hl             ; hl = new array")
        self._emit("    push hl            ; re-save")
        self._emit("    ex de, hl          ; de = new array")
        self._emit("    ; Restore iy to get vector")
        self._emit("    ld hl, 4")
        self._emit("    add hl, sp")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+0)       ; this is wrong stack offset, fix:")
        self._emit("    ; Re-get vector from saved position")
        self._emit("    pop hl             ; new array")
        self._emit("    pop bc             ; new capacity")
        self._emit("    pop iy             ; vector (saved earlier)")
        self._emit("    push iy            ; re-save")
        self._emit("    push bc            ; re-save new capacity")
        self._emit("    push hl            ; re-save new array")
        self._emit("    ex de, hl          ; de = new array")
        self._emit("    ; hl = old array")
        self._emit("    ld l, (iy+VEC_DATA_PTR)")
        self._emit("    ld h, (iy+VEC_DATA_PTR+1)")
        self._emit("    ; bc = bytes to copy = count * elem_size")
        self._emit("    push de            ; save destination")
        self._emit("    ld e, (iy+CONT_HDR_COUNT)")
        self._emit("    ld d, (iy+CONT_HDR_COUNT+1)")
        self._emit("    push hl            ; save source")
        self._emit("    ex de, hl          ; hl = count")
        self._emit("    ld e, (iy+CONT_HDR_ELEM_SIZE)")
        self._emit("    ld d, (iy+CONT_HDR_ELEM_SIZE+1)")
        self._emit("    call _mul16        ; hl = bytes to copy")
        self._emit("    ld b, h")
        self._emit("    ld c, l            ; bc = byte count")
        self._emit("    pop hl             ; source (old array)")
        self._emit("    pop de             ; destination (new array)")
        self._emit("    ; Check if anything to copy")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _va_grow_update")
        self._emit("    ldir               ; copy old data")
        self._emit("_va_grow_update:")
        self._emit("    ; Free old array")
        self._emit("    ld l, (iy+VEC_DATA_PTR)")
        self._emit("    ld h, (iy+VEC_DATA_PTR+1)")
        self._emit("    call _heap_free")
        self._emit("    ; Update vector with new array and capacity")
        self._emit("    pop hl             ; new array")
        self._emit("    ld (iy+VEC_DATA_PTR), l")
        self._emit("    ld (iy+VEC_DATA_PTR+1), h")
        self._emit("    pop hl             ; new capacity")
        self._emit("    ld (iy+CONT_HDR_CAPACITY), l")
        self._emit("    ld (iy+CONT_HDR_CAPACITY+1), h")
        self._emit("    pop bc             ; discard saved iy")
        self._emit("    pop ix             ; restore frame")
        self._emit("    ; Now insert the element")
        self._emit("    jr _va_insert")
        self._emit("_va_grow_fail:")
        self._emit("    ; Allocation failed, clean up and return 0")
        self._emit("    pop bc             ; discard new capacity")
        self._emit("    pop iy             ; discard saved vector")
        self._emit("    pop ix             ; restore frame")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_va_insert:")
        self._emit("    ; Calculate destination: data_ptr + count * elem_size")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld e, (iy+CONT_HDR_ELEM_SIZE)")
        self._emit("    ld d, (iy+CONT_HDR_ELEM_SIZE+1)")
        self._emit("    call _mul16       ; hl = offset")
        self._emit("    ld e, (iy+VEC_DATA_PTR)")
        self._emit("    ld d, (iy+VEC_DATA_PTR+1)")
        self._emit("    add hl, de        ; hl = destination")
        self._emit("    ex de, hl         ; de = destination")
        self._emit("    ; Source = element_ptr")
        self._emit("    ld l, (ix+VEC_APPEND_ELEM)")
        self._emit("    ld h, (ix+VEC_APPEND_ELEM+1)")
        self._emit("    ; Copy elem_size bytes")
        self._emit("    ld c, (iy+CONT_HDR_ELEM_SIZE)")
        self._emit("    ld b, (iy+CONT_HDR_ELEM_SIZE+1)")
        self._emit("    ldir              ; copy bc bytes from hl to de")
        self._emit("    ; Increment count")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    inc hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("    ld hl, 1          ; success")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _vec_element: Get element at index")
        self._emit("; Input: Stack: vector_ptr, index")
        self._emit("; Output: hl = pointer to element (0 if out of bounds)")
        self._emit("VEC_ELEMENT_VEC      .equ 4")
        self._emit("VEC_ELEMENT_IDX      .equ 6")
        self._emit("")
        self._emit("_vec_element:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_ELEMENT_VEC)")
        self._emit("    ld h, (ix+VEC_ELEMENT_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = vector")
        self._emit("    ; Bounds check")
        self._emit("    ld l, (ix+VEC_ELEMENT_IDX)")
        self._emit("    ld h, (ix+VEC_ELEMENT_IDX+1)")
        self._emit("    ld e, (iy+CONT_HDR_COUNT)")
        self._emit("    ld d, (iy+CONT_HDR_COUNT+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr nc, _ve_oob    ; index >= count")
        self._emit("    ; Calculate address: data_ptr + index * elem_size")
        self._emit("    ld l, (ix+VEC_ELEMENT_IDX)")
        self._emit("    ld h, (ix+VEC_ELEMENT_IDX+1)")
        self._emit("    ld e, (iy+CONT_HDR_ELEM_SIZE)")
        self._emit("    ld d, (iy+CONT_HDR_ELEM_SIZE+1)")
        self._emit("    call _mul16       ; hl = offset")
        self._emit("    ld e, (iy+VEC_DATA_PTR)")
        self._emit("    ld d, (iy+VEC_DATA_PTR+1)")
        self._emit("    add hl, de        ; hl = element address")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_ve_oob:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _vec_length: Get vector length")
        self._emit("; Input: Stack: vector_ptr")
        self._emit("; Output: hl = length")
        self._emit("VEC_LENGTH_VEC       .equ 4")
        self._emit("")
        self._emit("_vec_length:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_LENGTH_VEC)")
        self._emit("    ld h, (ix+VEC_LENGTH_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Vector Is_Empty
        self._emit("; _vec_is_empty: Check if vector is empty")
        self._emit("; Input: Stack: vector_ptr")
        self._emit("; Output: hl = 1 (empty) or 0 (not empty)")
        self._emit("VEC_ISEMPTY_VEC      .equ 4")
        self._emit("")
        self._emit("_vec_is_empty:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_ISEMPTY_VEC)")
        self._emit("    ld h, (ix+VEC_ISEMPTY_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vie_empty")
        self._emit("    ld hl, 0          ; not empty")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_vie_empty:")
        self._emit("    ld hl, 1          ; empty")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Vector Clear
        self._emit("; _vec_clear: Clear all elements from vector")
        self._emit("; Input: Stack: vector_ptr")
        self._emit("VEC_CLEAR_VEC        .equ 4")
        self._emit("")
        self._emit("_vec_clear:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_CLEAR_VEC)")
        self._emit("    ld h, (ix+VEC_CLEAR_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Set count to 0 (keep capacity and data)")
        self._emit("    ld (iy+CONT_HDR_COUNT), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Vector First and Last (aliases to container first/last)
        self._emit("; _vec_first: Get first cursor for vector")
        self._emit("_vec_first .equ _container_first")
        self._emit("")
        self._emit("; _vec_last: Get last cursor for vector")
        self._emit("_vec_last .equ _container_last")
        self._emit("")

        # Vector Delete First and Last
        self._emit("; _vec_delete_first: Delete first element")
        self._emit("; Input: Stack: vector_ptr")
        self._emit("VEC_DELFIRST_VEC     .equ 4")
        self._emit("")
        self._emit("_vec_delete_first:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_DELFIRST_VEC)")
        self._emit("    ld h, (ix+VEC_DELFIRST_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Check if empty")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vdf_done    ; empty, nothing to delete")
        self._emit("    ; Shift all elements down by elem_size")
        self._emit("    ld e, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld d, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    ld l, (iy+VEC_DATA_PTR)")
        self._emit("    ld h, (iy+VEC_DATA_PTR+1)")
        self._emit("    push hl           ; dest = data")
        self._emit("    add hl, de        ; src = data + elem_size")
        self._emit("    ex de, hl         ; de = src")
        self._emit("    pop hl            ; hl = dest")
        self._emit("    ; Calculate bytes to move = (count - 1) * elem_size")
        self._emit("    push iy")
        self._emit("    pop bc")
        self._emit("    push hl")
        self._emit("    push de")
        self._emit("    push bc")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    dec hl            ; count - 1")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    call _mul16       ; hl = (count-1) * elem_size")
        self._emit("    ld b, h")
        self._emit("    ld c, l           ; bc = bytes to move")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _vdf_deccount")
        self._emit("    pop de            ; src")
        self._emit("    pop hl            ; dest")
        self._emit("    ldir              ; move elements")
        self._emit("    jr _vdf_dec2")
        self._emit("_vdf_deccount:")
        self._emit("    pop de")
        self._emit("    pop hl")
        self._emit("_vdf_dec2:")
        self._emit("    ; Decrement count")
        self._emit("    ld l, (ix+VEC_DELFIRST_VEC)")
        self._emit("    ld h, (ix+VEC_DELFIRST_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    dec hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("_vdf_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        self._emit("; _vec_delete_last: Delete last element")
        self._emit("; Input: Stack: vector_ptr")
        self._emit("VEC_DELLAST_VEC      .equ 4")
        self._emit("")
        self._emit("_vec_delete_last:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_DELLAST_VEC)")
        self._emit("    ld h, (ix+VEC_DELLAST_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Check if empty")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vdl_done    ; empty, nothing to delete")
        self._emit("    ; Just decrement count")
        self._emit("    dec hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("_vdl_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Vector Prepend
        self._emit("; _vec_prepend: Prepend element to front of vector")
        self._emit("; Input: Stack: vector_ptr, element_ptr")
        self._emit("; Output: hl = 1 (success) or 0 (failed)")
        self._emit("VEC_PREPEND_VEC      .equ 4")
        self._emit("VEC_PREPEND_ELEM     .equ 6")
        self._emit("")
        self._emit("_vec_prepend:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+VEC_PREPEND_VEC)")
        self._emit("    ld h, (ix+VEC_PREPEND_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Check if we need to grow")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld e, (iy+VEC_CAPACITY)")
        self._emit("    ld d, (iy+VEC_CAPACITY+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr c, _vp_hasroom")
        self._emit("    ; Need to grow - call _vec_grow")
        self._emit("    ld l, (ix+VEC_PREPEND_VEC)")
        self._emit("    ld h, (ix+VEC_PREPEND_VEC+1)")
        self._emit("    push hl")
        self._emit("    call _vec_grow")
        self._emit("    pop hl")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vp_fail")
        self._emit("    ld l, (ix+VEC_PREPEND_VEC)")
        self._emit("    ld h, (ix+VEC_PREPEND_VEC+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("_vp_hasroom:")
        self._emit("    ; Shift all elements up by elem_size")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _vp_insert   ; empty, just insert")
        self._emit("    ; Calculate src = data + (count-1)*elem_size, dest = src + elem_size")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    call _mul16       ; hl = count * elem_size")
        self._emit("    ld e, (iy+VEC_DATA_PTR)")
        self._emit("    ld d, (iy+VEC_DATA_PTR+1)")
        self._emit("    add hl, de        ; hl = data + count*elem_size (end)")
        self._emit("    ex de, hl         ; de = dest (end + elem_size effectively)")
        self._emit("    ld l, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld h, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    or a")
        self._emit("    ex de, hl")
        self._emit("    sbc hl, de        ; hl = end - elem_size = last element start")
        self._emit("    ex de, hl         ; de = src, hl still has elem_size")
        self._emit("    ; Use LDDR to shift backwards")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    call _mul16       ; hl = count * elem_size = bytes to move")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _vp_insert")
        self._emit("    ; Setup for LDDR")
        self._emit("    ld l, (iy+VEC_DATA_PTR)")
        self._emit("    ld h, (iy+VEC_DATA_PTR+1)")
        self._emit("    push hl           ; save data ptr for later")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    call _mul16       ; hl = count * elem_size")
        self._emit("    pop de            ; de = data ptr")
        self._emit("    add hl, de")
        self._emit("    dec hl            ; hl = last byte of data")
        self._emit("    ld e, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld d, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    push hl")
        self._emit("    add hl, de        ; hl = dest last byte")
        self._emit("    ex de, hl         ; de = dest last byte")
        self._emit("    pop hl            ; hl = src last byte")
        self._emit("    ld c, (iy+CONT_HDR_COUNT)")
        self._emit("    ld b, (iy+CONT_HDR_COUNT+1)")
        self._emit("    push bc")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    push hl")
        self._emit("    push de")
        self._emit("    pop hl")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    push bc")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    ex de, hl         ; swap back: hl=src, de=dest")
        self._emit("    push bc")
        self._emit("    ld c, (iy+CONT_HDR_COUNT)")
        self._emit("    ld b, (iy+CONT_HDR_COUNT+1)")
        self._emit("    push hl")
        self._emit("    push de")
        self._emit("    ex (sp), hl")
        self._emit("    ex de, hl")
        self._emit("    pop hl")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    ld hl, (iy+VEC_DATA_PTR)")
        self._emit("    push bc")
        self._emit("    ld c, (iy+CONT_HDR_COUNT)")
        self._emit("    ld b, (iy+CONT_HDR_COUNT+1)")
        self._emit("    call _mul16       ; hl = count * elem_size")
        self._emit("    pop bc")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _vp_insert")
        self._emit("    ; Simplified: just copy to position 0")
        self._emit("_vp_insert:")
        self._emit("    ; Copy new element to position 0")
        self._emit("    ld l, (iy+VEC_DATA_PTR)")
        self._emit("    ld h, (iy+VEC_DATA_PTR+1)")
        self._emit("    ex de, hl         ; de = dest")
        self._emit("    ld l, (ix+VEC_PREPEND_ELEM)")
        self._emit("    ld h, (ix+VEC_PREPEND_ELEM+1) ; hl = src")
        self._emit("    ld c, (iy+VEC_ELEM_SIZE)")
        self._emit("    ld b, (iy+VEC_ELEM_SIZE+1)")
        self._emit("    ldir")
        self._emit("    ; Increment count")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    inc hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("    ld hl, 1          ; success")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_vp_fail:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Cursor operations
        self._emit("; ==== Cursor Operations ====")
        self._emit("")
        self._emit("; _container_first: Get first cursor")
        self._emit("; Input: Stack: container_ptr")
        self._emit("; Output: hl = cursor (position, container in _cursor_container)")
        self._emit("CONT_FIRST_CONT      .equ 4")
        self._emit("")
        self._emit("_container_first:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+CONT_FIRST_CONT)")
        self._emit("    ld h, (ix+CONT_FIRST_CONT+1)")
        self._emit("    ld (_cursor_container), hl")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Check if empty")
        self._emit("    ld a, (iy+CONT_HDR_COUNT)")
        self._emit("    or (iy+CONT_HDR_COUNT+1)")
        self._emit("    jr z, _cf_empty")
        self._emit("    ; Return position 0 (first)")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_cf_empty:")
        self._emit("    ld hl, 0xFFFF     ; No_Element")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _container_last: Get last cursor")
        self._emit("; Input: Stack: container_ptr")
        self._emit("; Output: hl = cursor position")
        self._emit("CONT_LAST_CONT       .equ 4")
        self._emit("")
        self._emit("_container_last:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+CONT_LAST_CONT)")
        self._emit("    ld h, (ix+CONT_LAST_CONT+1)")
        self._emit("    ld (_cursor_container), hl")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Check if empty")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _cl_empty")
        self._emit("    ; Return count - 1")
        self._emit("    dec hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_cl_empty:")
        self._emit("    ld hl, 0xFFFF     ; No_Element")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _cursor_next: Advance cursor")
        self._emit("; Input: Stack: cursor_position")
        self._emit("; Output: hl = new cursor position")
        self._emit("CURSOR_NEXT_POS      .equ 4")
        self._emit("")
        self._emit("_cursor_next:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+CURSOR_NEXT_POS)")
        self._emit("    ld h, (ix+CURSOR_NEXT_POS+1)")
        self._emit("    ; Check for No_Element")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _cn_done    ; already No_Element")
        self._emit("    ; Increment and check bounds")
        self._emit("    inc hl")
        self._emit("    push hl")
        self._emit("    ld hl, (_cursor_container)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld e, (iy+CONT_HDR_COUNT)")
        self._emit("    ld d, (iy+CONT_HDR_COUNT+1)")
        self._emit("    pop hl")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr nc, _cn_end    ; pos >= count")
        self._emit("    add hl, de        ; restore position")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_cn_end:")
        self._emit("    ld hl, 0xFFFF     ; No_Element")
        self._emit("_cn_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _cursor_previous: Move cursor backward")
        self._emit("; Input: Stack: cursor_position")
        self._emit("; Output: hl = new cursor position")
        self._emit("CURSOR_PREV_POS      .equ 4")
        self._emit("")
        self._emit("_cursor_previous:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+CURSOR_PREV_POS)")
        self._emit("    ld h, (ix+CURSOR_PREV_POS+1)")
        self._emit("    ; Check for No_Element or position 0")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _cp_done    ; No_Element")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _cp_begin   ; at position 0")
        self._emit("    dec hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_cp_begin:")
        self._emit("    ld hl, 0xFFFF     ; No_Element")
        self._emit("_cp_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _cursor_element: Get element at cursor")
        self._emit("; Input: Stack: cursor_position")
        self._emit("; Output: hl = element pointer (0 if No_Element)")
        self._emit("CURSOR_ELEM_POS      .equ 4")
        self._emit("")
        self._emit("_cursor_element:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+CURSOR_ELEM_POS)")
        self._emit("    ld h, (ix+CURSOR_ELEM_POS+1)")
        self._emit("    ; Check for No_Element")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _ce_none")
        self._emit("    ; Get element from container")
        self._emit("    push hl           ; save position (index)")
        self._emit("    ld hl, (_cursor_container)")
        self._emit("    push hl           ; push container")
        self._emit("    call _cont_element ; dispatch by container type")
        self._emit("    pop bc            ; clean stack")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_ce_none:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _cursor_has_element: Check if cursor points to valid element")
        self._emit("; Input: Stack: cursor_position")
        self._emit("; Output: hl = 1 (has element) or 0 (No_Element)")
        self._emit("CURSOR_HAS_POS       .equ 4")
        self._emit("")
        self._emit("_cursor_has_element:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+CURSOR_HAS_POS)")
        self._emit("    ld h, (ix+CURSOR_HAS_POS+1)")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _che_no")
        self._emit("    ld hl, 1          ; has element")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_che_no:")
        self._emit("    ld hl, 0          ; no element")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; Container find and contains")
        self._emit("; _container_find: Linear search for element in container")
        self._emit("; Input: Stack: container_ptr, item_ptr")
        self._emit("; Output: hl = cursor position (0xFFFF = not found)")
        self._emit("CONT_FIND_CONT       .equ 4")
        self._emit("CONT_FIND_ITEM       .equ 6")
        self._emit("")
        self._emit("_container_find:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get container")
        self._emit("    ld l, (ix+CONT_FIND_CONT)")
        self._emit("    ld h, (ix+CONT_FIND_CONT+1)")
        self._emit("    ld (_cursor_container), hl")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = container")
        self._emit("    ; Get count")
        self._emit("    ld e, (iy+CONT_HDR_COUNT)")
        self._emit("    ld d, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _cf_notfound ; empty container")
        self._emit("    ; Get element size for comparison")
        self._emit("    ld a, (iy+CONT_HDR_ELEM_SIZE)")
        self._emit("    ld (_cf_elem_size), a")
        self._emit("    ; Start at position 0")
        self._emit("    ld bc, 0          ; bc = current position")
        self._emit("_cf_loop:")
        self._emit("    ; Check if position < count")
        self._emit("    push bc")
        self._emit("    push de")
        self._emit("    ld h, b")
        self._emit("    ld l, c           ; hl = position")
        self._emit("    or a")
        self._emit("    sbc hl, de        ; position - count")
        self._emit("    pop de")
        self._emit("    pop bc")
        self._emit("    jr nc, _cf_notfound ; position >= count")
        self._emit("    ; Get element at position")
        self._emit("    push bc           ; save position")
        self._emit("    push de           ; save count")
        self._emit("    ; Push args for _cont_element (container, index)")
        self._emit("    push bc           ; index")
        self._emit("    ld l, (ix+CONT_FIND_CONT)")
        self._emit("    ld h, (ix+CONT_FIND_CONT+1)")
        self._emit("    push hl           ; container")
        self._emit("    call _cont_element ; dispatch by container type")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ; hl = element pointer")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _cf_next    ; null element, skip")
        self._emit("    ; Compare with search item")
        self._emit("    push hl           ; save element ptr")
        self._emit("    ex de, hl         ; de = element ptr")
        self._emit("    ld l, (ix+CONT_FIND_ITEM)")
        self._emit("    ld h, (ix+CONT_FIND_ITEM+1) ; hl = item ptr")
        self._emit("    ld a, (_cf_elem_size)")
        self._emit("    ld b, a           ; B = bytes to compare")
        self._emit("_cf_cmp_loop:")
        self._emit("    ld a, (de)")
        self._emit("    cp (hl)")
        self._emit("    jr nz, _cf_cmp_ne")
        self._emit("    inc hl")
        self._emit("    inc de")
        self._emit("    djnz _cf_cmp_loop")
        self._emit("    ; All bytes match - found!")
        self._emit("    pop hl            ; discard element ptr")
        self._emit("    pop de            ; restore count")
        self._emit("    pop bc            ; position = result")
        self._emit("    ld h, b")
        self._emit("    ld l, c")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_cf_cmp_ne:")
        self._emit("    pop hl            ; discard element ptr")
        self._emit("_cf_next:")
        self._emit("    pop de            ; restore count")
        self._emit("    pop bc            ; restore position")
        self._emit("    inc bc            ; next position")
        self._emit("    jr _cf_loop")
        self._emit("_cf_notfound:")
        self._emit("    ld hl, 0xFFFF     ; No_Element")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("_cf_elem_size:")
        self._emit("    .db 0             ; temp storage for element size")
        self._emit("")
        self._emit("; _container_contains: Check if container has item")
        self._emit("; Input: Stack: container_ptr, item_ptr")
        self._emit("; Output: hl = 1 (found) or 0 (not found)")
        self._emit("CONT_CONTAINS_CONT   .equ 4")
        self._emit("CONT_CONTAINS_ITEM   .equ 6")
        self._emit("")
        self._emit("_container_contains:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Call find")
        self._emit("    ld l, (ix+CONT_CONTAINS_ITEM)")
        self._emit("    ld h, (ix+CONT_CONTAINS_ITEM+1)")
        self._emit("    push hl           ; item")
        self._emit("    ld l, (ix+CONT_CONTAINS_CONT)")
        self._emit("    ld h, (ix+CONT_CONTAINS_CONT+1)")
        self._emit("    push hl           ; container")
        self._emit("    call _container_find")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ; Check if found (not 0xFFFF)")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _cc_no")
        self._emit("    ld hl, 1          ; found")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_cc_no:")
        self._emit("    ld hl, 0          ; not found")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # =========================================
        # Doubly Linked List Operations
        # =========================================
        self._emit("; ==== Doubly Linked List Operations ====")
        self._emit("")
        self._emit("; List structure (shared with container header):")
        self._emit("; +0: type (CONT_TYPE_LIST)")
        self._emit("; +1: flags")
        self._emit("; +2-3: count")
        self._emit("; +4-5: head pointer (first node)")
        self._emit("; +6-7: tail pointer (last node)")
        self._emit("; +8-9: element size")
        self._emit("LIST_HEAD            .equ 4    ; head node pointer")
        self._emit("LIST_TAIL            .equ 6    ; tail node pointer")
        self._emit("LIST_ELEM_SIZE       .equ 8    ; element size")
        self._emit("LIST_HDR_SIZE        .equ 10   ; list header size")
        self._emit("")

        # Create list
        self._emit("; _list_create: Create a new doubly linked list")
        self._emit("; Input: Stack: element_size")
        self._emit("; Output: hl = list pointer (0 if failed)")
        self._emit("LIST_CREATE_ELEM_SIZE .equ 4")
        self._emit("")
        self._emit("_list_create:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Allocate list header")
        self._emit("    ld hl, LIST_HDR_SIZE")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _lc_fail")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = list header")
        self._emit("    ; Initialize header")
        self._emit("    ld (iy+CONT_HDR_TYPE), CONT_TYPE_LIST")
        self._emit("    ld (iy+CONT_HDR_FLAGS), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), 0")
        self._emit("    ld (iy+LIST_HEAD), 0")
        self._emit("    ld (iy+LIST_HEAD+1), 0")
        self._emit("    ld (iy+LIST_TAIL), 0")
        self._emit("    ld (iy+LIST_TAIL+1), 0")
        self._emit("    ; Set element size")
        self._emit("    ld l, (ix+LIST_CREATE_ELEM_SIZE)")
        self._emit("    ld h, (ix+LIST_CREATE_ELEM_SIZE+1)")
        self._emit("    ld (iy+LIST_ELEM_SIZE), l")
        self._emit("    ld (iy+LIST_ELEM_SIZE+1), h")
        self._emit("    ; Return list pointer")
        self._emit("    push iy")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_lc_fail:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Allocate node helper
        self._emit("; _list_alloc_node: Allocate a new list node")
        self._emit("; Input: hl = element size")
        self._emit("; Output: hl = node pointer (0 if failed)")
        self._emit("_list_alloc_node:")
        self._emit("    ; Node size = LIST_NODE_DATA + element_size")
        self._emit("    ld de, LIST_NODE_DATA")
        self._emit("    add hl, de")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    ret z             ; failed")
        self._emit("    ; Initialize links to null")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld (iy+LIST_NODE_PREV), 0")
        self._emit("    ld (iy+LIST_NODE_PREV+1), 0")
        self._emit("    ld (iy+LIST_NODE_NEXT), 0")
        self._emit("    ld (iy+LIST_NODE_NEXT+1), 0")
        self._emit("    push iy")
        self._emit("    pop hl")
        self._emit("    ret")
        self._emit("")

        # Append (add to end)
        self._emit("; _list_append: Append element to end of list")
        self._emit("; Input: Stack: list_ptr, element_ptr")
        self._emit("; Output: hl = 1 (success) or 0 (failed)")
        self._emit("LIST_APPEND_LIST     .equ 4")
        self._emit("LIST_APPEND_ELEM     .equ 6")
        self._emit("")
        self._emit("_list_append:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get list")
        self._emit("    ld l, (ix+LIST_APPEND_LIST)")
        self._emit("    ld h, (ix+LIST_APPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = list")
        self._emit("    ; Allocate new node")
        self._emit("    ld l, (iy+LIST_ELEM_SIZE)")
        self._emit("    ld h, (iy+LIST_ELEM_SIZE+1)")
        self._emit("    call _list_alloc_node")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _la_fail")
        self._emit("    push hl           ; save node ptr")
        self._emit("    ; Copy element data to node")
        self._emit("    ld de, LIST_NODE_DATA")
        self._emit("    add hl, de        ; hl = node data area")
        self._emit("    ex de, hl         ; de = destination")
        self._emit("    ld l, (ix+LIST_APPEND_ELEM)")
        self._emit("    ld h, (ix+LIST_APPEND_ELEM+1) ; hl = source")
        self._emit("    push iy           ; save list")
        self._emit("    ld c, (iy+LIST_ELEM_SIZE)")
        self._emit("    ld b, (iy+LIST_ELEM_SIZE+1)")
        self._emit("    ldir              ; copy element")
        self._emit("    pop iy            ; restore list")
        self._emit("    pop hl            ; node ptr")
        self._emit("    push hl")
        self._emit("    pop bc            ; bc = new node")
        self._emit("    ; Check if list is empty")
        self._emit("    ld l, (iy+LIST_HEAD)")
        self._emit("    ld h, (iy+LIST_HEAD+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _la_notempty")
        self._emit("    ; Empty list: new node is both head and tail")
        self._emit("    ld (iy+LIST_HEAD), c")
        self._emit("    ld (iy+LIST_HEAD+1), b")
        self._emit("    ld (iy+LIST_TAIL), c")
        self._emit("    ld (iy+LIST_TAIL+1), b")
        self._emit("    jr _la_inccount")
        self._emit("_la_notempty:")
        self._emit("    ; Get current tail")
        self._emit("    ld l, (iy+LIST_TAIL)")
        self._emit("    ld h, (iy+LIST_TAIL+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = old tail")
        self._emit("    ; Link old tail to new node")
        self._emit("    ld (iy+LIST_NODE_NEXT), c")
        self._emit("    ld (iy+LIST_NODE_NEXT+1), b")
        self._emit("    ; Link new node back to old tail")
        self._emit("    push bc")
        self._emit("    pop iy            ; iy = new node")
        self._emit("    pop hl            ; old tail (pushed earlier as hl)")
        self._emit("    push hl")
        self._emit("    ld l, (ix+LIST_APPEND_LIST)")
        self._emit("    ld h, (ix+LIST_APPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = list (restore)")
        self._emit("    ld l, (iy+LIST_TAIL)")
        self._emit("    ld h, (iy+LIST_TAIL+1) ; hl = old tail")
        self._emit("    push bc")
        self._emit("    pop iy            ; iy = new node")
        self._emit("    ld (iy+LIST_NODE_PREV), l")
        self._emit("    ld (iy+LIST_NODE_PREV+1), h")
        self._emit("    ; Update tail to new node")
        self._emit("    ld l, (ix+LIST_APPEND_LIST)")
        self._emit("    ld h, (ix+LIST_APPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = list")
        self._emit("    ld (iy+LIST_TAIL), c")
        self._emit("    ld (iy+LIST_TAIL+1), b")
        self._emit("_la_inccount:")
        self._emit("    ; Restore list pointer and increment count")
        self._emit("    ld l, (ix+LIST_APPEND_LIST)")
        self._emit("    ld h, (ix+LIST_APPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    inc hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("    ld hl, 1          ; success")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_la_fail:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Prepend (add to front)
        self._emit("; _list_prepend: Prepend element to front of list")
        self._emit("; Input: Stack: list_ptr, element_ptr")
        self._emit("; Output: hl = 1 (success) or 0 (failed)")
        self._emit("LIST_PREPEND_LIST    .equ 4")
        self._emit("LIST_PREPEND_ELEM    .equ 6")
        self._emit("")
        self._emit("_list_prepend:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get list")
        self._emit("    ld l, (ix+LIST_PREPEND_LIST)")
        self._emit("    ld h, (ix+LIST_PREPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Allocate new node")
        self._emit("    ld l, (iy+LIST_ELEM_SIZE)")
        self._emit("    ld h, (iy+LIST_ELEM_SIZE+1)")
        self._emit("    call _list_alloc_node")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _lp_fail")
        self._emit("    push hl           ; save node")
        self._emit("    ; Copy element data")
        self._emit("    ld de, LIST_NODE_DATA")
        self._emit("    add hl, de")
        self._emit("    ex de, hl         ; de = dest")
        self._emit("    ld l, (ix+LIST_PREPEND_ELEM)")
        self._emit("    ld h, (ix+LIST_PREPEND_ELEM+1)")
        self._emit("    ld l, (ix+LIST_PREPEND_LIST)")
        self._emit("    ld h, (ix+LIST_PREPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    push de")
        self._emit("    ld l, (ix+LIST_PREPEND_ELEM)")
        self._emit("    ld h, (ix+LIST_PREPEND_ELEM+1)")
        self._emit("    pop de")
        self._emit("    ld c, (iy+LIST_ELEM_SIZE)")
        self._emit("    ld b, (iy+LIST_ELEM_SIZE+1)")
        self._emit("    ldir")
        self._emit("    pop hl            ; node")
        self._emit("    push hl")
        self._emit("    pop bc            ; bc = new node")
        self._emit("    ; Check if list is empty")
        self._emit("    ld l, (iy+LIST_HEAD)")
        self._emit("    ld h, (iy+LIST_HEAD+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _lp_notempty")
        self._emit("    ; Empty: new node is head and tail")
        self._emit("    ld (iy+LIST_HEAD), c")
        self._emit("    ld (iy+LIST_HEAD+1), b")
        self._emit("    ld (iy+LIST_TAIL), c")
        self._emit("    ld (iy+LIST_TAIL+1), b")
        self._emit("    jr _lp_inccount")
        self._emit("_lp_notempty:")
        self._emit("    ; hl = old head")
        self._emit("    push hl           ; save old head")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = old head")
        self._emit("    ; Link old head back to new node")
        self._emit("    ld (iy+LIST_NODE_PREV), c")
        self._emit("    ld (iy+LIST_NODE_PREV+1), b")
        self._emit("    ; Link new node forward to old head")
        self._emit("    push bc")
        self._emit("    pop iy            ; iy = new node")
        self._emit("    pop hl            ; old head")
        self._emit("    ld (iy+LIST_NODE_NEXT), l")
        self._emit("    ld (iy+LIST_NODE_NEXT+1), h")
        self._emit("    ; Update head to new node")
        self._emit("    ld l, (ix+LIST_PREPEND_LIST)")
        self._emit("    ld h, (ix+LIST_PREPEND_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld (iy+LIST_HEAD), c")
        self._emit("    ld (iy+LIST_HEAD+1), b")
        self._emit("_lp_inccount:")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    inc hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("    ld hl, 1")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_lp_fail:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # First element (get head)
        self._emit("; _list_first: Get cursor to first element")
        self._emit("; Input: Stack: list_ptr")
        self._emit("; Output: hl = cursor (node ptr, 0xFFFF if empty)")
        self._emit("LIST_FIRST_LIST      .equ 4")
        self._emit("")
        self._emit("_list_first:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_FIRST_LIST)")
        self._emit("    ld h, (ix+LIST_FIRST_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+LIST_HEAD)")
        self._emit("    ld h, (iy+LIST_HEAD+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _lf_done")
        self._emit("    ld hl, 0xFFFF     ; No_Element")
        self._emit("_lf_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Last element (get tail)
        self._emit("; _list_last: Get cursor to last element")
        self._emit("; Input: Stack: list_ptr")
        self._emit("; Output: hl = cursor (node ptr, 0xFFFF if empty)")
        self._emit("LIST_LAST_LIST       .equ 4")
        self._emit("")
        self._emit("_list_last:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_LAST_LIST)")
        self._emit("    ld h, (ix+LIST_LAST_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+LIST_TAIL)")
        self._emit("    ld h, (iy+LIST_TAIL+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _ll_done")
        self._emit("    ld hl, 0xFFFF")
        self._emit("_ll_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Next element
        self._emit("; _list_next: Get next cursor")
        self._emit("; Input: Stack: cursor (node ptr)")
        self._emit("; Output: hl = next cursor (0xFFFF if at end)")
        self._emit("LIST_NEXT_CURSOR     .equ 4")
        self._emit("")
        self._emit("_list_next:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_NEXT_CURSOR)")
        self._emit("    ld h, (ix+LIST_NEXT_CURSOR+1)")
        self._emit("    ; Check for No_Element")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _ln_noelem")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+LIST_NODE_NEXT)")
        self._emit("    ld h, (iy+LIST_NODE_NEXT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _ln_done")
        self._emit("_ln_noelem:")
        self._emit("    ld hl, 0xFFFF")
        self._emit("_ln_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Previous element
        self._emit("; _list_prev: Get previous cursor")
        self._emit("; Input: Stack: cursor (node ptr)")
        self._emit("; Output: hl = previous cursor (0xFFFF if at start)")
        self._emit("LIST_PREV_CURSOR     .equ 4")
        self._emit("")
        self._emit("_list_prev:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_PREV_CURSOR)")
        self._emit("    ld h, (ix+LIST_PREV_CURSOR+1)")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _lprev_noelem")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+LIST_NODE_PREV)")
        self._emit("    ld h, (iy+LIST_NODE_PREV+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _lprev_done")
        self._emit("_lprev_noelem:")
        self._emit("    ld hl, 0xFFFF")
        self._emit("_lprev_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Get element at cursor
        self._emit("; _list_element: Get element pointer at cursor")
        self._emit("; Input: Stack: cursor (node ptr)")
        self._emit("; Output: hl = element pointer (0 if No_Element)")
        self._emit("LIST_ELEMENT_CURSOR  .equ 4")
        self._emit("")
        self._emit("_list_element:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_ELEMENT_CURSOR)")
        self._emit("    ld h, (ix+LIST_ELEMENT_CURSOR+1)")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _le_noelem")
        self._emit("    ; Return pointer to data area")
        self._emit("    ld de, LIST_NODE_DATA")
        self._emit("    add hl, de")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_le_noelem:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Delete at cursor
        self._emit("; _list_delete: Delete element at cursor")
        self._emit("; Input: Stack: list_ptr, cursor (node ptr)")
        self._emit("; Output: hl = next cursor (or 0xFFFF)")
        self._emit("LIST_DELETE_LIST     .equ 4")
        self._emit("LIST_DELETE_CURSOR   .equ 6")
        self._emit("")
        self._emit("_list_delete:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get cursor (node to delete)")
        self._emit("    ld l, (ix+LIST_DELETE_CURSOR)")
        self._emit("    ld h, (ix+LIST_DELETE_CURSOR+1)")
        self._emit("    ; Check for No_Element")
        self._emit("    ld a, h")
        self._emit("    and l")
        self._emit("    inc a")
        self._emit("    jr z, _ld_noelem")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = node to delete")
        self._emit("    ; Save next for return")
        self._emit("    ld e, (iy+LIST_NODE_NEXT)")
        self._emit("    ld d, (iy+LIST_NODE_NEXT+1)")
        self._emit("    push de           ; save next")
        self._emit("    ; Get prev and next")
        self._emit("    ld l, (iy+LIST_NODE_PREV)")
        self._emit("    ld h, (iy+LIST_NODE_PREV+1) ; hl = prev")
        self._emit("    ; de = next (from above)")
        self._emit("    ; Update prev's next to our next")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _ld_no_prev")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = prev")
        self._emit("    ld (iy+LIST_NODE_NEXT), e")
        self._emit("    ld (iy+LIST_NODE_NEXT+1), d")
        self._emit("    jr _ld_check_next")
        self._emit("_ld_no_prev:")
        self._emit("    ; No prev means this was head, update list head")
        self._emit("    ld l, (ix+LIST_DELETE_LIST)")
        self._emit("    ld h, (ix+LIST_DELETE_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = list")
        self._emit("    ld (iy+LIST_HEAD), e")
        self._emit("    ld (iy+LIST_HEAD+1), d")
        self._emit("_ld_check_next:")
        self._emit("    ; Get node to delete again")
        self._emit("    ld l, (ix+LIST_DELETE_CURSOR)")
        self._emit("    ld h, (ix+LIST_DELETE_CURSOR+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = node to delete")
        self._emit("    ; Get next again")
        self._emit("    ld e, (iy+LIST_NODE_NEXT)")
        self._emit("    ld d, (iy+LIST_NODE_NEXT+1)")
        self._emit("    ; Get prev")
        self._emit("    ld l, (iy+LIST_NODE_PREV)")
        self._emit("    ld h, (iy+LIST_NODE_PREV+1)")
        self._emit("    ; Update next's prev to our prev")
        self._emit("    ld a, d")
        self._emit("    or e")
        self._emit("    jr z, _ld_no_next")
        self._emit("    push de")
        self._emit("    pop iy            ; iy = next")
        self._emit("    ld (iy+LIST_NODE_PREV), l")
        self._emit("    ld (iy+LIST_NODE_PREV+1), h")
        self._emit("    jr _ld_free")
        self._emit("_ld_no_next:")
        self._emit("    ; No next means this was tail, update list tail")
        self._emit("    ld e, (ix+LIST_DELETE_LIST)")
        self._emit("    ld d, (ix+LIST_DELETE_LIST+1)")
        self._emit("    push de")
        self._emit("    pop iy            ; iy = list")
        self._emit("    ; hl still has prev")
        self._emit("    ld (iy+LIST_TAIL), l")
        self._emit("    ld (iy+LIST_TAIL+1), h")
        self._emit("_ld_free:")
        self._emit("    ; Decrement count")
        self._emit("    ld l, (ix+LIST_DELETE_LIST)")
        self._emit("    ld h, (ix+LIST_DELETE_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    dec hl")
        self._emit("    ld (iy+CONT_HDR_COUNT), l")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), h")
        self._emit("    ; Free the node")
        self._emit("    ld l, (ix+LIST_DELETE_CURSOR)")
        self._emit("    ld h, (ix+LIST_DELETE_CURSOR+1)")
        self._emit("    call _heap_free")
        self._emit("    ; Return saved next")
        self._emit("    pop hl            ; next cursor")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _ld_done")
        self._emit("    ld hl, 0xFFFF     ; No_Element if null")
        self._emit("_ld_done:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_ld_noelem:")
        self._emit("    ld hl, 0xFFFF")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Clear list
        self._emit("; _list_clear: Remove all elements from list")
        self._emit("; Input: Stack: list_ptr")
        self._emit("LIST_CLEAR_LIST      .equ 4")
        self._emit("")
        self._emit("_list_clear:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get list")
        self._emit("    ld l, (ix+LIST_CLEAR_LIST)")
        self._emit("    ld h, (ix+LIST_CLEAR_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Get head")
        self._emit("    ld l, (iy+LIST_HEAD)")
        self._emit("    ld h, (iy+LIST_HEAD+1)")
        self._emit("_lcl_loop:")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _lcl_done")
        self._emit("    ; Save next before freeing")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld e, (iy+LIST_NODE_NEXT)")
        self._emit("    ld d, (iy+LIST_NODE_NEXT+1)")
        self._emit("    push de           ; save next")
        self._emit("    ; Free current node")
        self._emit("    push iy")
        self._emit("    pop hl")
        self._emit("    call _heap_free")
        self._emit("    ; Move to next")
        self._emit("    pop hl            ; next")
        self._emit("    jr _lcl_loop")
        self._emit("_lcl_done:")
        self._emit("    ; Reset list")
        self._emit("    ld l, (ix+LIST_CLEAR_LIST)")
        self._emit("    ld h, (ix+LIST_CLEAR_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld (iy+LIST_HEAD), 0")
        self._emit("    ld (iy+LIST_HEAD+1), 0")
        self._emit("    ld (iy+LIST_TAIL), 0")
        self._emit("    ld (iy+LIST_TAIL+1), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT), 0")
        self._emit("    ld (iy+CONT_HDR_COUNT+1), 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # List length
        self._emit("; _list_length: Get list length")
        self._emit("; Input: Stack: list_ptr")
        self._emit("; Output: hl = element count")
        self._emit("LIST_LENGTH_LIST     .equ 4")
        self._emit("")
        self._emit("_list_length:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_LENGTH_LIST)")
        self._emit("    ld h, (ix+LIST_LENGTH_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Is empty
        self._emit("; _list_is_empty: Check if list is empty")
        self._emit("; Input: Stack: list_ptr")
        self._emit("; Output: hl = 1 (empty) or 0 (not empty)")
        self._emit("LIST_ISEMPTY_LIST    .equ 4")
        self._emit("")
        self._emit("_list_is_empty:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld l, (ix+LIST_ISEMPTY_LIST)")
        self._emit("    ld h, (ix+LIST_ISEMPTY_LIST+1)")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+CONT_HDR_COUNT)")
        self._emit("    ld h, (iy+CONT_HDR_COUNT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _lie_empty")
        self._emit("    ld hl, 0          ; not empty")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_lie_empty:")
        self._emit("    ld hl, 1          ; empty")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")


        # List element access (by index)
        self._emit("; _list_element: Get element at index (0-based)")
        self._emit("; Input: Stack: list_ptr, index")
        self._emit("; Output: hl = pointer to element data (0 if out of bounds)")
        self._emit("LIST_ELEM_LIST       .equ 4")
        self._emit("LIST_ELEM_IDX        .equ 6")
        self._emit("")
        self._emit("_list_element:")
        self._emit("    push ix")
        self._emit("    push bc")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get list")
        self._emit("    ld l, (ix+LIST_ELEM_LIST+2)  ; +2 for PUSH bc")
        self._emit("    ld h, (ix+LIST_ELEM_LIST+3)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = list")
        self._emit("    ; Check bounds")
        self._emit("    ld l, (ix+LIST_ELEM_IDX+2)")
        self._emit("    ld h, (ix+LIST_ELEM_IDX+3)  ; hl = index")
        self._emit("    ld e, (iy+CONT_HDR_COUNT)")
        self._emit("    ld d, (iy+CONT_HDR_COUNT+1) ; de = count")
        self._emit("    and a")
        self._emit("    sbc hl, de")
        self._emit("    jr nc, _le_oob    ; index >= count")
        self._emit("    ; Restore index")
        self._emit("    ld l, (ix+LIST_ELEM_IDX+2)")
        self._emit("    ld h, (ix+LIST_ELEM_IDX+3)")
        self._emit("    ld b, h")
        self._emit("    ld c, l           ; bc = remaining iterations")
        self._emit("    ; Start from head")
        self._emit("    ld l, (iy+LIST_HEAD)")
        self._emit("    ld h, (iy+LIST_HEAD+1)")
        self._emit("_le_loop:")
        self._emit("    ld a, b")
        self._emit("    or c")
        self._emit("    jr z, _le_found   ; index reached")
        self._emit("    ; Move to next node")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ld l, (iy+LIST_NODE_NEXT)")
        self._emit("    ld h, (iy+LIST_NODE_NEXT+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _le_oob     ; unexpected end")
        self._emit("    dec bc")
        self._emit("    jr _le_loop")
        self._emit("_le_found:")
        self._emit("    ; hl = node, return pointer to data")
        self._emit("    ld de, LIST_NODE_DATA")
        self._emit("    add hl, de")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_le_oob:")
        self._emit("    ld hl, 0          ; out of bounds")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # _cont_element is now in runtime library (containers.mac)
        # It dispatches to _vec_element or _list_element based on container type

        self._emit("; Current cursor container (for cursor operations)")
        self._emit("_cursor_container:")
        self._emit("    .dw 0")
        self._emit("")

    def _generate_stream_runtime(self) -> None:
        """Generate runtime for Ada.Streams and Stream I/O.

        Stream format is simple binary:
        - Integers: 2 bytes little-endian
        - Characters: 1 byte
        - Booleans: 1 byte (0 or 1)
        - Strings: 2-byte length prefix + characters
        - Records: each component in declaration order
        - Arrays: each element in index order
        """
        self._emit("")
        self._emit("; =========================================")
        self._emit("; Stream I/O Runtime")
        self._emit("; =========================================")
        self._emit("")
        self._emit("; Stream structure")
        self._emit("; A stream is a record with:")
        self._emit(";   +0: Read procedure pointer (2 bytes)")
        self._emit(";   +2: Write procedure pointer (2 bytes)")
        self._emit(";   +4: Stream-specific data (varies)")
        self._emit("")
        self._emit("; Stream offsets")
        self._emit("STREAM_READ_PROC     .equ 0")
        self._emit("STREAM_WRITE_PROC    .equ 2")
        self._emit("STREAM_DATA          .equ 4")
        self._emit("")
        self._emit("; File stream specifics (file handle at STREAM_DATA)")
        self._emit("FILE_STREAM_HANDLE   .equ 4    ; CP/M file handle (FCB)")
        self._emit("FILE_STREAM_BUFFER   .equ 6    ; Buffer pointer")
        self._emit("FILE_STREAM_POS      .equ 8    ; Position in buffer")
        self._emit("FILE_STREAM_SIZE     .equ 10   ; Size of data in buffer")
        self._emit("FILE_STREAM_HDR_SIZE .equ 12")
        self._emit("")

        # Stream_Element operations
        self._emit("; Stream_Element is 1 byte (Unsigned_8)")
        self._emit("; Stream_Element_Array is array of bytes")
        self._emit("")
        self._emit("; _stream_read: Read from stream")
        self._emit("; Input: Stack: stream_ptr, buffer_ptr, count")
        self._emit("; Output: hl = bytes actually read")
        self._emit("STM_READ_STREAM      .equ 4")
        self._emit("STM_READ_BUFFER      .equ 6")
        self._emit("STM_READ_COUNT       .equ 8")
        self._emit("")
        self._emit("_stream_read:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get stream pointer")
        self._emit("    ld l, (ix+STM_READ_STREAM)")
        self._emit("    ld h, (ix+STM_READ_STREAM+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = stream")
        self._emit("    ; Get read procedure")
        self._emit("    ld l, (iy+STREAM_READ_PROC)")
        self._emit("    ld h, (iy+STREAM_READ_PROC+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _sr_no_proc")
        self._emit("    ; Push args for read proc: stream, buffer, count")
        self._emit("    ld e, (ix+STM_READ_COUNT)")
        self._emit("    ld d, (ix+STM_READ_COUNT+1)")
        self._emit("    push de           ; count")
        self._emit("    ld e, (ix+STM_READ_BUFFER)")
        self._emit("    ld d, (ix+STM_READ_BUFFER+1)")
        self._emit("    push de           ; buffer")
        self._emit("    push iy           ; stream (as push hl)")
        self._emit("    ; Call read procedure")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    call _stream_call_iy")
        self._emit("    ; Clean up stack")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_sr_no_proc:")
        self._emit("    ld hl, 0          ; no read proc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("_stream_call_iy:")
        self._emit("    jp (iy)")
        self._emit("")
        self._emit("; _stream_write: Write to stream")
        self._emit("; Input: Stack: stream_ptr, buffer_ptr, count")
        self._emit("; Output: hl = bytes actually written")
        self._emit("STM_WRITE_STREAM     .equ 4")
        self._emit("STM_WRITE_BUFFER     .equ 6")
        self._emit("STM_WRITE_COUNT      .equ 8")
        self._emit("")
        self._emit("_stream_write:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get stream pointer")
        self._emit("    ld l, (ix+STM_WRITE_STREAM)")
        self._emit("    ld h, (ix+STM_WRITE_STREAM+1)")
        self._emit("    push hl")
        self._emit("    pop iy            ; iy = stream")
        self._emit("    ; Get write procedure")
        self._emit("    ld l, (iy+STREAM_WRITE_PROC)")
        self._emit("    ld h, (iy+STREAM_WRITE_PROC+1)")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _sw_no_proc")
        self._emit("    ; Push args: stream, buffer, count")
        self._emit("    ld e, (ix+STM_WRITE_COUNT)")
        self._emit("    ld d, (ix+STM_WRITE_COUNT+1)")
        self._emit("    push de")
        self._emit("    ld e, (ix+STM_WRITE_BUFFER)")
        self._emit("    ld d, (ix+STM_WRITE_BUFFER+1)")
        self._emit("    push de")
        self._emit("    push iy")
        self._emit("    ; Call write procedure")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    call _stream_call_iy")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_sw_no_proc:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

        # Type-specific read/write operations
        self._emit("; ==== Type-Specific Stream Operations ====")
        self._emit("")
        self._emit("; _stream_write_int: Write integer to stream")
        self._emit("; Input: Stack: stream_ptr, value")
        self._emit("STM_WINT_STREAM      .equ 4")
        self._emit("STM_WINT_VALUE       .equ 6")
        self._emit("")
        self._emit("_stream_write_int:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Store value in temp buffer")
        self._emit("    ld l, (ix+STM_WINT_VALUE)")
        self._emit("    ld h, (ix+STM_WINT_VALUE+1)")
        self._emit("    ld (_stream_temp), hl")
        self._emit("    ; Write 2 bytes")
        self._emit("    ld hl, 2          ; count")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl           ; buffer")
        self._emit("    ld l, (ix+STM_WINT_STREAM)")
        self._emit("    ld h, (ix+STM_WINT_STREAM+1)")
        self._emit("    push hl           ; stream")
        self._emit("    call _stream_write")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_read_int: Read integer from stream")
        self._emit("; Input: Stack: stream_ptr")
        self._emit("; Output: hl = value read")
        self._emit("STM_RINT_STREAM      .equ 4")
        self._emit("")
        self._emit("_stream_read_int:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Read 2 bytes")
        self._emit("    ld hl, 2          ; count")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl           ; buffer")
        self._emit("    ld l, (ix+STM_RINT_STREAM)")
        self._emit("    ld h, (ix+STM_RINT_STREAM+1)")
        self._emit("    push hl           ; stream")
        self._emit("    call _stream_read")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ; Return value")
        self._emit("    ld hl, (_stream_temp)")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_write_char: Write character to stream")
        self._emit("; Input: Stack: stream_ptr, char_value")
        self._emit("STM_WCHAR_STREAM     .equ 4")
        self._emit("STM_WCHAR_VALUE      .equ 6")
        self._emit("")
        self._emit("_stream_write_char:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld a, (ix+STM_WCHAR_VALUE)")
        self._emit("    ld (_stream_temp), a")
        self._emit("    ld hl, 1          ; count")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl")
        self._emit("    ld l, (ix+STM_WCHAR_STREAM)")
        self._emit("    ld h, (ix+STM_WCHAR_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_write")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_read_char: Read character from stream")
        self._emit("; Input: Stack: stream_ptr")
        self._emit("; Output: hl = char value (in L)")
        self._emit("STM_RCHAR_STREAM     .equ 4")
        self._emit("")
        self._emit("_stream_read_char:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld hl, 1")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl")
        self._emit("    ld l, (ix+STM_RCHAR_STREAM)")
        self._emit("    ld h, (ix+STM_RCHAR_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_read")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ld a, (_stream_temp)")
        self._emit("    ld l, a")
        self._emit("    ld h, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_write_string: Write Ada string to stream")
        self._emit("; Input: Stack: stream_ptr, string_ptr")
        self._emit("; Format: 2-byte length + characters")
        self._emit("STM_WSTR_STREAM      .equ 4")
        self._emit("STM_WSTR_STRING      .equ 6")
        self._emit("")
        self._emit("_stream_write_string:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Get string length")
        self._emit("    ld l, (ix+STM_WSTR_STRING)")
        self._emit("    ld h, (ix+STM_WSTR_STRING+1)")
        self._emit("    ld a, (hl)        ; length byte")
        self._emit("    ld e, a")
        self._emit("    ld d, 0           ; de = length")
        self._emit("    push de           ; save length")
        self._emit("    ; Write length as 2 bytes")
        self._emit("    ld (_stream_temp), de")
        self._emit("    ld hl, 2")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl")
        self._emit("    ld l, (ix+STM_WSTR_STREAM)")
        self._emit("    ld h, (ix+STM_WSTR_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_write")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ; Write characters")
        self._emit("    pop de            ; length")
        self._emit("    push de")
        self._emit("    ld l, (ix+STM_WSTR_STRING)")
        self._emit("    ld h, (ix+STM_WSTR_STRING+1)")
        self._emit("    inc hl            ; skip length byte")
        self._emit("    push hl           ; buffer (string content)")
        self._emit("    ld l, (ix+STM_WSTR_STREAM)")
        self._emit("    ld h, (ix+STM_WSTR_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_write")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_read_string: Read string from stream")
        self._emit("; Input: Stack: stream_ptr, buffer_ptr, max_len")
        self._emit("; Output: hl = pointer to string (in buffer)")
        self._emit("STM_RSTR_STREAM      .equ 4")
        self._emit("STM_RSTR_BUFFER      .equ 6")
        self._emit("STM_RSTR_MAXLEN      .equ 8")
        self._emit("")
        self._emit("_stream_read_string:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Read length (2 bytes)")
        self._emit("    ld hl, 2")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl")
        self._emit("    ld l, (ix+STM_RSTR_STREAM)")
        self._emit("    ld h, (ix+STM_RSTR_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_read")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ; Get length, clamp to max")
        self._emit("    ld hl, (_stream_temp)")
        self._emit("    ld e, (ix+STM_RSTR_MAXLEN)")
        self._emit("    ld d, (ix+STM_RSTR_MAXLEN+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr nc, _srstr_clamp")
        self._emit("    add hl, de        ; restore hl")
        self._emit("    jr _srstr_read")
        self._emit("_srstr_clamp:")
        self._emit("    ex de, hl         ; hl = max_len")
        self._emit("_srstr_read:")
        self._emit("    push hl           ; save actual length")
        self._emit("    ; Store length in buffer")
        self._emit("    ld e, (ix+STM_RSTR_BUFFER)")
        self._emit("    ld d, (ix+STM_RSTR_BUFFER+1)")
        self._emit("    ld a, l")
        self._emit("    ld (de), a        ; length byte")
        self._emit("    inc de")
        self._emit("    ; Read characters")
        self._emit("    pop hl            ; length")
        self._emit("    push hl")
        self._emit("    push de           ; buffer + 1")
        self._emit("    ld l, (ix+STM_RSTR_STREAM)")
        self._emit("    ld h, (ix+STM_RSTR_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_read")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ; Return buffer pointer")
        self._emit("    ld l, (ix+STM_RSTR_BUFFER)")
        self._emit("    ld h, (ix+STM_RSTR_BUFFER+1)")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_write_bool: Write Boolean to stream")
        self._emit("; Input: Stack: stream_ptr, bool_value")
        self._emit("STM_WBOOL_STREAM     .equ 4")
        self._emit("STM_WBOOL_VALUE      .equ 6")
        self._emit("")
        self._emit("_stream_write_bool:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld a, (ix+STM_WBOOL_VALUE)")
        self._emit("    ld (_stream_temp), a")
        self._emit("    ld hl, 1")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl")
        self._emit("    ld l, (ix+STM_WBOOL_STREAM)")
        self._emit("    ld h, (ix+STM_WBOOL_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_write")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _stream_read_bool: Read Boolean from stream")
        self._emit("; Input: Stack: stream_ptr")
        self._emit("; Output: hl = Boolean value (0 or 1)")
        self._emit("STM_RBOOL_STREAM     .equ 4")
        self._emit("")
        self._emit("_stream_read_bool:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ld hl, 1")
        self._emit("    push hl")
        self._emit("    ld hl, _stream_temp")
        self._emit("    push hl")
        self._emit("    ld l, (ix+STM_RBOOL_STREAM)")
        self._emit("    ld h, (ix+STM_RBOOL_STREAM+1)")
        self._emit("    push hl")
        self._emit("    call _stream_read")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    pop bc")
        self._emit("    ld a, (_stream_temp)")
        self._emit("    ld l, a")
        self._emit("    ld h, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; Temporary buffer for stream operations")
        self._emit("_stream_temp:")
        self._emit("    .ds 8")
        self._emit("")

        # Memory stream (for testing/internal use)
        self._emit("; ==== Memory Stream (in-memory buffer) ====")
        self._emit("; Memory stream has data pointer and current position")
        self._emit("MEM_STREAM_DATA      .equ 4    ; data buffer pointer")
        self._emit("MEM_STREAM_POS       .equ 6    ; current position")
        self._emit("MEM_STREAM_SIZE      .equ 8    ; buffer size")
        self._emit("MEM_STREAM_HDR_SIZE  .equ 10")
        self._emit("")
        self._emit("; _mem_stream_create: Create memory stream")
        self._emit("; Input: Stack: buffer_ptr, buffer_size")
        self._emit("; Output: hl = stream pointer")
        self._emit("MSC_BUFFER           .equ 4")
        self._emit("MSC_SIZE             .equ 6")
        self._emit("")
        self._emit("_mem_stream_create:")
        self._emit("    push ix")
        self._emit("    ld ix, 0")
        self._emit("    add ix, sp")
        self._emit("    ; Allocate stream header")
        self._emit("    ld hl, MEM_STREAM_HDR_SIZE")
        self._emit("    call _heap_alloc")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr z, _msc_fail")
        self._emit("    push hl")
        self._emit("    pop iy")
        self._emit("    ; Set read/write procedures")
        self._emit("    ld de, _mem_stream_read")
        self._emit("    ld (iy+STREAM_READ_PROC), e")
        self._emit("    ld (iy+STREAM_READ_PROC+1), d")
        self._emit("    ld de, _mem_stream_write")
        self._emit("    ld (iy+STREAM_WRITE_PROC), e")
        self._emit("    ld (iy+STREAM_WRITE_PROC+1), d")
        self._emit("    ; Set buffer info")
        self._emit("    ld l, (ix+MSC_BUFFER)")
        self._emit("    ld h, (ix+MSC_BUFFER+1)")
        self._emit("    ld (iy+MEM_STREAM_DATA), l")
        self._emit("    ld (iy+MEM_STREAM_DATA+1), h")
        self._emit("    ld (iy+MEM_STREAM_POS), 0")
        self._emit("    ld (iy+MEM_STREAM_POS+1), 0")
        self._emit("    ld l, (ix+MSC_SIZE)")
        self._emit("    ld h, (ix+MSC_SIZE+1)")
        self._emit("    ld (iy+MEM_STREAM_SIZE), l")
        self._emit("    ld (iy+MEM_STREAM_SIZE+1), h")
        self._emit("    ; Return stream pointer")
        self._emit("    push iy")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_msc_fail:")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mem_stream_read: Read bytes from memory stream")
        self._emit("; Input: de = stream pointer, hl = dest buffer, bc = count")
        self._emit("; Output: hl = bytes actually read")
        self._emit("_mem_stream_read:")
        self._emit("    push ix")
        self._emit("    push de")
        self._emit("    pop ix")
        self._emit("    ; ix = stream pointer")
        self._emit("    ; hl = dest buffer, bc = count")
        self._emit("    ; Get current position")
        self._emit("    ld e, (ix+MEM_STREAM_POS)")
        self._emit("    ld d, (ix+MEM_STREAM_POS+1)")
        self._emit("    ; Get buffer size")
        self._emit("    push hl")
        self._emit("    ld l, (ix+MEM_STREAM_SIZE)")
        self._emit("    ld h, (ix+MEM_STREAM_SIZE+1)")
        self._emit("    ; Calculate remaining = size - pos")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    ; hl = remaining bytes")
        self._emit("    ; If remaining <= 0, return 0")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _msr_has_data")
        self._emit("    pop hl")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_msr_has_data:")
        self._emit("    ; Clamp count to remaining: min(bc, hl)")
        self._emit("    push hl")
        self._emit("    or a")
        self._emit("    sbc hl, bc")
        self._emit("    pop hl")
        self._emit("    jr nc, _msr_use_count")
        self._emit("    ; remaining < count, use remaining")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("_msr_use_count:")
        self._emit("    ; bc = actual bytes to read")
        self._emit("    ; Save count for return")
        self._emit("    push bc")
        self._emit("    ; Get source address = data + pos")
        self._emit("    ld l, (ix+MEM_STREAM_DATA)")
        self._emit("    ld h, (ix+MEM_STREAM_DATA+1)")
        self._emit("    add hl, de")
        self._emit("    ; hl = source, pop dest into de")
        self._emit("    ex de, hl")
        self._emit("    pop bc")
        self._emit("    push bc")
        self._emit("    ex (sp), hl")
        self._emit("    ex de, hl")
        self._emit("    pop hl")
        self._emit("    push bc")
        self._emit("    ; de = dest, hl = source, bc = count")
        self._emit("    ex de, hl")
        self._emit("    ; hl = dest, de = source, bc = count")
        self._emit("    ; Use LDIR to copy")
        self._emit("    ldir")
        self._emit("    ; Update position")
        self._emit("    pop bc")
        self._emit("    ld l, (ix+MEM_STREAM_POS)")
        self._emit("    ld h, (ix+MEM_STREAM_POS+1)")
        self._emit("    add hl, bc")
        self._emit("    ld (ix+MEM_STREAM_POS), l")
        self._emit("    ld (ix+MEM_STREAM_POS+1), h")
        self._emit("    ; Return bytes read")
        self._emit("    push bc")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mem_stream_write: Write bytes to memory stream")
        self._emit("; Input: de = stream pointer, hl = source buffer, bc = count")
        self._emit("; Output: hl = bytes actually written")
        self._emit("_mem_stream_write:")
        self._emit("    push ix")
        self._emit("    push de")
        self._emit("    pop ix")
        self._emit("    ; ix = stream pointer")
        self._emit("    ; hl = source buffer, bc = count")
        self._emit("    ; Get current position")
        self._emit("    ld e, (ix+MEM_STREAM_POS)")
        self._emit("    ld d, (ix+MEM_STREAM_POS+1)")
        self._emit("    ; Get buffer size")
        self._emit("    push hl")
        self._emit("    ld l, (ix+MEM_STREAM_SIZE)")
        self._emit("    ld h, (ix+MEM_STREAM_SIZE+1)")
        self._emit("    ; Calculate remaining = size - pos")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    ; hl = remaining space")
        self._emit("    ; If remaining <= 0, return 0")
        self._emit("    ld a, h")
        self._emit("    or l")
        self._emit("    jr nz, _msw_has_space")
        self._emit("    pop hl")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_msw_has_space:")
        self._emit("    ; Clamp count to remaining: min(bc, hl)")
        self._emit("    push hl")
        self._emit("    or a")
        self._emit("    sbc hl, bc")
        self._emit("    pop hl")
        self._emit("    jr nc, _msw_use_count")
        self._emit("    ; remaining < count, use remaining")
        self._emit("    ld b, h")
        self._emit("    ld c, l")
        self._emit("_msw_use_count:")
        self._emit("    ; bc = actual bytes to write")
        self._emit("    ; Save count for return")
        self._emit("    push bc")
        self._emit("    ; Get dest address = data + pos")
        self._emit("    ld l, (ix+MEM_STREAM_DATA)")
        self._emit("    ld h, (ix+MEM_STREAM_DATA+1)")
        self._emit("    add hl, de")
        self._emit("    ; hl = dest")
        self._emit("    ex de, hl")
        self._emit("    ; de = dest")
        self._emit("    ; Get source from stack")
        self._emit("    pop bc")
        self._emit("    push bc")
        self._emit("    ex (sp), hl")
        self._emit("    pop hl")
        self._emit("    push bc")
        self._emit("    ; hl = source, de = dest, bc = count")
        self._emit("    ldir")
        self._emit("    ; Update position")
        self._emit("    pop bc")
        self._emit("    ld l, (ix+MEM_STREAM_POS)")
        self._emit("    ld h, (ix+MEM_STREAM_POS+1)")
        self._emit("    add hl, bc")
        self._emit("    ld (ix+MEM_STREAM_POS), l")
        self._emit("    ld (ix+MEM_STREAM_POS+1), h")
        self._emit("    ; Return bytes written")
        self._emit("    push bc")
        self._emit("    pop hl")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mem_stream_seek: Seek to position in memory stream")
        self._emit("; Input: de = stream pointer, hl = position")
        self._emit("; Output: hl = new position (clamped to size)")
        self._emit("_mem_stream_seek:")
        self._emit("    push ix")
        self._emit("    push de")
        self._emit("    pop ix")
        self._emit("    ; Clamp position to size")
        self._emit("    ld e, (ix+MEM_STREAM_SIZE)")
        self._emit("    ld d, (ix+MEM_STREAM_SIZE+1)")
        self._emit("    ; If hl > de, use de")
        self._emit("    push hl")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    pop hl")
        self._emit("    jr c, _mss_in_range")
        self._emit("    jr z, _mss_in_range")
        self._emit("    ; hl > size, clamp to size")
        self._emit("    ex de, hl")
        self._emit("_mss_in_range:")
        self._emit("    ; Set position")
        self._emit("    ld (ix+MEM_STREAM_POS), l")
        self._emit("    ld (ix+MEM_STREAM_POS+1), h")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mem_stream_tell: Get current position in memory stream")
        self._emit("; Input: de = stream pointer")
        self._emit("; Output: hl = current position")
        self._emit("_mem_stream_tell:")
        self._emit("    push ix")
        self._emit("    push de")
        self._emit("    pop ix")
        self._emit("    ld l, (ix+MEM_STREAM_POS)")
        self._emit("    ld h, (ix+MEM_STREAM_POS+1)")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mem_stream_eof: Check if at end of memory stream")
        self._emit("; Input: de = stream pointer")
        self._emit("; Output: hl = 1 if at end, 0 otherwise")
        self._emit("_mem_stream_eof:")
        self._emit("    push ix")
        self._emit("    push de")
        self._emit("    pop ix")
        self._emit("    ld l, (ix+MEM_STREAM_POS)")
        self._emit("    ld h, (ix+MEM_STREAM_POS+1)")
        self._emit("    ld e, (ix+MEM_STREAM_SIZE)")
        self._emit("    ld d, (ix+MEM_STREAM_SIZE+1)")
        self._emit("    or a")
        self._emit("    sbc hl, de")
        self._emit("    jr c, _mse_not_eof")
        self._emit("    ld hl, 1")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("_mse_not_eof:")
        self._emit("    ld hl, 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")
        self._emit("; _mem_stream_reset: Reset stream to beginning")
        self._emit("; Input: de = stream pointer")
        self._emit("_mem_stream_reset:")
        self._emit("    push ix")
        self._emit("    push de")
        self._emit("    pop ix")
        self._emit("    ld (ix+MEM_STREAM_POS), 0")
        self._emit("    ld (ix+MEM_STREAM_POS+1), 0")
        self._emit("    pop ix")
        self._emit("    ret")
        self._emit("")

    def _emit_instr(self, mnemonic: str, *operands: str) -> None:
        """Emit an instruction."""
        # Track runtime calls (but not calls to locally defined functions)
        if mnemonic.lower() == "call" and operands:
            target = operands[0]
            if target.startswith("_") and not target.startswith("__"):
                # Check if this is a locally defined function
                is_local = False
                if self.module:
                    for func in self.module.functions:
                        if self._mangle_symbol(func.name) == target:
                            is_local = True
                            break
                if not is_local:
                    # This is a call to a runtime routine
                    self.runtime_deps.add(target)

        if operands:
            self._emit(f"    {mnemonic} {', '.join(operands)}")
        else:
            self._emit(f"    {mnemonic}")

    def _generate_function(self, func: IRFunction) -> None:
        """Generate code for a function."""
        self.current_function = func

        # Allocate registers
        self.reg_alloc = self._allocate_registers(func)

        # Function prologue
        mangled_name = self._mangle_symbol(func.name)
        self._emit(f"; Function: {func.name}")
        self._emit(f"{mangled_name}:")

        # Set up stack frame
        # Always set up frame if we have locals, temps, OR parameters (params accessed via ix)
        has_frame = (func.locals_size > 0 or
                     self.reg_alloc.stack_size > 0 or
                     len(func.params) > 0)
        if has_frame:
            total_stack = func.locals_size + self.reg_alloc.stack_size
            self._emit_instr("push", "ix")
            self._emit_instr("ld", "ix", "0")
            self._emit_instr("add", "ix", "sp")
            if total_stack > 0:
                # Reserve stack space
                self._emit_instr("ld", "hl", str(-total_stack))
                self._emit_instr("add", "hl", "sp")
                self._emit_instr("ld", "sp", "hl")

        # Generate code for each block
        for block in func.blocks:
            self._generate_block(block)

        self._emit("")

    def _allocate_registers(self, func: IRFunction) -> RegisterAllocation:
        """Simple register allocation - spill everything to stack.

        Parameters are at positive offsets from ix (they were pushed before CALL):
            (ix+4) = first param, (ix+6) = second param, etc.
        Local variables/temporaries are at negative offsets from ix:
            (ix-2) = first local, (ix-4) = second local, etc.
        """
        alloc = RegisterAllocation()

        # Track which VReg IDs are parameters
        param_ids: set[int] = set()
        for param in func.params:
            if isinstance(param, VReg):
                param_ids.add(param.id)

        # Assign parameter offsets (positive from ix)
        # Stack layout after CALL and PUSH ix:
        #   (ix+0) = saved ix (2 bytes)
        #   (ix+2) = return address (2 bytes)
        #   (ix+4) = first parameter, (ix+6) = second, etc.
        param_offset = 4  # Start after saved ix and return address
        for param in func.params:
            if isinstance(param, VReg):
                # Store as a special marker: negative means local, positive means param
                # We use a large negative number to indicate it's a parameter
                # The actual offset is stored separately
                alloc.vreg_to_stack[param.id] = -10000 - param_offset
                param_offset += 2

        # Collect all other virtual registers (locals/temporaries)
        vregs: set[int] = set()
        for block in func.blocks:
            for instr in block.instructions:
                for operand in [instr.dst, instr.src1, instr.src2]:
                    if isinstance(operand, VReg) and operand.id not in param_ids:
                        vregs.add(operand.id)

        # Allocate stack slots for local vregs (negative offsets)
        # Start AFTER the space reserved for local variables (func.locals_size)
        # to avoid overlapping with record/array locals
        offset = func.locals_size
        for vreg_id in sorted(vregs):
            alloc.vreg_to_stack[vreg_id] = offset
            offset += 2  # All values are 2 bytes for simplicity

        alloc.stack_size = offset - func.locals_size  # Only count temp space
        return alloc

    def _generate_block(self, block: BasicBlock) -> None:
        """Generate code for a basic block."""
        self._emit(f"{block.label}:")

        for instr in block.instructions:
            self._generate_instr(instr)

    def _generate_instr(self, instr: IRInstr) -> None:
        """Generate code for an IR instruction."""
        op = instr.opcode

        # Add comment if present (but not for INLINE_ASM where comment IS the code)
        if instr.comment and op != OpCode.INLINE_ASM:
            self._emit(f"    ; {instr.comment}")

        if op == OpCode.MOV:
            self._gen_mov(instr)
        elif op == OpCode.LOAD:
            self._gen_load(instr)
        elif op == OpCode.STORE:
            self._gen_store(instr)
        elif op == OpCode.ADD:
            self._gen_add(instr)
        elif op == OpCode.SUB:
            self._gen_sub(instr)
        elif op == OpCode.MUL:
            self._gen_mul(instr)
        elif op == OpCode.DIV:
            self._gen_div(instr)
        elif op == OpCode.MOD:
            self._gen_mod(instr)
        elif op == OpCode.REM:
            self._gen_rem(instr)
        elif op == OpCode.NEG:
            self._gen_neg(instr)
        elif op == OpCode.AND:
            self._gen_and(instr)
        elif op == OpCode.OR:
            self._gen_or(instr)
        elif op == OpCode.XOR:
            self._gen_xor(instr)
        elif op == OpCode.NOT:
            self._gen_not(instr)
        elif op == OpCode.SHL:
            self._gen_shl(instr)
        elif op == OpCode.SHR:
            self._gen_shr(instr)
        elif op == OpCode.CMP:
            self._gen_cmp_flags_only(instr)
        elif op in (OpCode.CMP_EQ, OpCode.CMP_NE, OpCode.CMP_LT,
                    OpCode.CMP_LE, OpCode.CMP_GT, OpCode.CMP_GE):
            self._gen_cmp(instr)
        elif op == OpCode.JMP:
            self._gen_jmp(instr)
        elif op == OpCode.JZ:
            self._gen_jz(instr)
        elif op == OpCode.JNZ:
            self._gen_jnz(instr)
        elif op == OpCode.JL:
            self._gen_jl(instr)
        elif op == OpCode.JLE:
            self._gen_jle(instr)
        elif op == OpCode.JG:
            self._gen_jg(instr)
        elif op == OpCode.JGE:
            self._gen_jge(instr)
        elif op == OpCode.JC:
            self._gen_jc(instr)
        elif op == OpCode.JNC:
            self._gen_jnc(instr)
        elif op == OpCode.CALL:
            self._gen_call(instr)
        elif op == OpCode.CALL_INDIRECT:
            self._gen_call_indirect(instr)
        elif op == OpCode.DISPATCH:
            self._gen_dispatch(instr)
        elif op == OpCode.RET:
            self._gen_ret(instr)
        elif op == OpCode.PUSH:
            self._gen_push(instr)
        elif op == OpCode.POP:
            self._gen_pop(instr)
        elif op == OpCode.LABEL:
            # Emit inline label (for conditional expressions, case expressions, etc.)
            if isinstance(instr.dst, Label):
                self._emit(f"{instr.dst.name}:")
            elif isinstance(instr.dst, str):
                self._emit(f"{instr.dst}:")
        elif op == OpCode.LEA:
            self._gen_lea(instr)
        elif op == OpCode.NOP:
            self._emit_instr("nop")
        # Exception handling
        elif op == OpCode.EXC_PUSH:
            self._gen_exc_push(instr)
        elif op == OpCode.EXC_POP:
            self._gen_exc_pop(instr)
        elif op == OpCode.EXC_RAISE:
            self._gen_exc_raise(instr)
        elif op == OpCode.EXC_RERAISE:
            self._gen_exc_reraise(instr)
        # Floating point operations
        elif op == OpCode.FADD:
            self._gen_fadd(instr)
        elif op == OpCode.FSUB:
            self._gen_fsub(instr)
        elif op == OpCode.FMUL:
            self._gen_fmul(instr)
        elif op == OpCode.FDIV:
            self._gen_fdiv(instr)
        elif op == OpCode.FNEG:
            self._gen_fneg(instr)
        elif op == OpCode.FCMP:
            self._gen_fcmp(instr)
        elif op == OpCode.FABS:
            self._gen_fabs(instr)
        elif op == OpCode.ITOF:
            self._gen_itof(instr)
        elif op == OpCode.FTOI:
            self._gen_ftoi(instr)
        # Tasking operations
        elif op == OpCode.TASK_CREATE:
            self._gen_task_create(instr)
        elif op == OpCode.TASK_YIELD:
            self._gen_task_yield(instr)
        elif op == OpCode.TASK_TERMINATE:
            self._gen_task_terminate(instr)
        elif op == OpCode.TASK_DELAY:
            self._gen_task_delay(instr)
        elif op == OpCode.TASK_DELAY_UNTIL:
            self._gen_task_delay_until(instr)
        elif op == OpCode.ENTRY_CALL:
            self._gen_entry_call(instr)
        elif op == OpCode.ENTRY_ACCEPT:
            self._gen_entry_accept(instr)
        elif op == OpCode.INLINE_ASM:
            self._gen_inline_asm(instr)

    def _gen_inline_asm(self, instr: IRInstr) -> None:
        """Emit inline assembly code from pragma Machine_Code."""
        # The assembly code is stored in the comment field
        if instr.comment:
            # Split multiline assembly and emit each line
            for line in instr.comment.split('\n'):
                line = line.strip()
                if line:
                    # Emit as raw assembly (already formatted)
                    self.output.append(f"        {line}")

    def _load_to_hl(self, value: IRValue) -> None:
        """Load a value into hl."""
        if isinstance(value, Immediate):
            self._emit_instr("ld", "hl", str(value.value))
        elif isinstance(value, VReg):
            # Check if atomic - wrap in DI/EI
            if value.is_atomic:
                self._emit_instr("di")  # Disable interrupts
            offset = self._vreg_offset(value)
            self._emit_instr("ld", "l", f"(ix{offset:+d})")
            self._emit_instr("ld", "h", f"(ix{offset+1:+d})")
            if value.is_atomic:
                self._emit_instr("ei")  # Enable interrupts
        elif isinstance(value, Label):
            self._emit_instr("ld", "hl", value.name)

    def _load_to_de(self, value: IRValue) -> None:
        """Load a value into de."""
        if isinstance(value, Immediate):
            self._emit_instr("ld", "de", str(value.value))
        elif isinstance(value, VReg):
            # Check if atomic - wrap in DI/EI
            if value.is_atomic:
                self._emit_instr("di")  # Disable interrupts
            offset = self._vreg_offset(value)
            self._emit_instr("ld", "e", f"(ix{offset:+d})")
            self._emit_instr("ld", "d", f"(ix{offset+1:+d})")
            if value.is_atomic:
                self._emit_instr("ei")  # Enable interrupts
        elif isinstance(value, Label):
            self._emit_instr("ld", "de", value.name)

    def _load_to_a(self, value: IRValue) -> None:
        """Load a byte value into A."""
        if isinstance(value, Immediate):
            self._emit_instr("ld", "a", str(value.value & 0xFF))
        elif isinstance(value, VReg):
            # Check if atomic - wrap in DI/EI
            if value.is_atomic:
                self._emit_instr("di")  # Disable interrupts
            offset = self._vreg_offset(value)
            self._emit_instr("ld", "a", f"(ix{offset:+d})")
            if value.is_atomic:
                self._emit_instr("ei")  # Enable interrupts

    def _store_from_hl(self, dst: VReg) -> None:
        """Store hl to a vreg."""
        # Check if atomic - wrap in DI/EI
        if dst.is_atomic:
            self._emit_instr("di")  # Disable interrupts
        offset = self._vreg_offset(dst)
        self._emit_instr("ld", f"(ix{offset:+d})", "l")
        self._emit_instr("ld", f"(ix{offset+1:+d})", "h")
        if dst.is_atomic:
            self._emit_instr("ei")  # Enable interrupts

    def _store_from_a(self, dst: VReg) -> None:
        """Store A to a vreg (byte)."""
        # Check if atomic - wrap in DI/EI
        if dst.is_atomic:
            self._emit_instr("di")  # Disable interrupts
        offset = self._vreg_offset(dst)
        self._emit_instr("ld", f"(ix{offset:+d})", "a")
        if dst.is_atomic:
            self._emit_instr("ei")  # Enable interrupts

    def _vreg_offset(self, vreg: VReg) -> int:
        """Get the stack offset for a virtual register.

        Returns positive offset for parameters, negative for locals.
        """
        if self.reg_alloc and vreg.id in self.reg_alloc.vreg_to_stack:
            slot = self.reg_alloc.vreg_to_stack[vreg.id]
            if slot <= -10000:
                # This is a parameter - extract the positive offset
                return -(slot + 10000)  # e.g., -10004 -> +4
            else:
                # This is a local variable - negative offset from ix
                return -(slot + 2)
        return 0

    # Instruction generators

    def _gen_mov(self, instr: IRInstr) -> None:
        """Generate MOV instruction."""
        if not isinstance(instr.dst, VReg) or instr.src1 is None:
            return
        self._load_to_hl(instr.src1)
        self._store_from_hl(instr.dst)

    def _gen_load(self, instr: IRInstr) -> None:
        """Generate LOAD instruction."""
        if not isinstance(instr.dst, VReg) or not isinstance(instr.src1, MemoryLocation):
            return

        mem = instr.src1

        # Atomic access: disable interrupts before load
        if mem.is_atomic:
            self._emit_instr("di")  # Disable interrupts

        if mem.is_global:
            self._emit_instr("ld", "hl", f"({self._mangle_symbol(mem.symbol_name)})")
        elif mem.base is not None:
            # Load from computed address: base + offset
            # First load the base address into hl
            self._load_to_hl(mem.base)
            if mem.offset != 0:
                self._emit_instr("ld", "de", str(mem.offset))
                self._emit_instr("add", "hl", "de")
            # Now load the value at (hl)
            self._emit_instr("ld", "e", "(hl)")
            self._emit_instr("inc", "hl")
            self._emit_instr("ld", "d", "(hl)")
            self._emit_instr("ex", "de", "hl")
        else:
            # Load from stack-relative address
            self._emit_instr("ld", "l", f"(ix{mem.offset:+d})")
            self._emit_instr("ld", "h", f"(ix{mem.offset+1:+d})")

        self._store_from_hl(instr.dst)

        # Atomic access: re-enable interrupts after load
        if mem.is_atomic:
            self._emit_instr("ei")  # Enable interrupts

    def _gen_store(self, instr: IRInstr) -> None:
        """Generate STORE instruction."""
        if not isinstance(instr.dst, MemoryLocation) or instr.src1 is None:
            return

        mem = instr.dst

        # Atomic access: disable interrupts before store
        if mem.is_atomic:
            self._emit_instr("di")  # Disable interrupts

        if mem.is_global:
            self._load_to_hl(instr.src1)
            self._emit_instr("ld", f"({self._mangle_symbol(mem.symbol_name)})", "hl")
        elif mem.base is not None:
            # Store to computed address: base + offset
            # Load value into de first
            self._load_to_de(instr.src1)
            # Load address into hl
            self._load_to_hl(mem.base)
            if mem.offset != 0:
                self._emit_instr("push", "de")  # Save value
                self._emit_instr("ld", "de", str(mem.offset))
                self._emit_instr("add", "hl", "de")
                self._emit_instr("pop", "de")  # Restore value
            # Store de to (hl)
            self._emit_instr("ld", "(hl)", "e")
            self._emit_instr("inc", "hl")
            self._emit_instr("ld", "(hl)", "d")
        else:
            self._load_to_hl(instr.src1)
            self._emit_instr("ld", f"(ix{mem.offset:+d})", "l")
            self._emit_instr("ld", f"(ix{mem.offset+1:+d})", "h")

        # Atomic access: re-enable interrupts after store
        if mem.is_atomic:
            self._emit_instr("ei")  # Enable interrupts

    def _gen_lea(self, instr: IRInstr) -> None:
        """Generate LEA (load effective address) instruction."""
        if not isinstance(instr.dst, VReg):
            return

        # Handle Label source (for Float64 constants, string literals, etc.)
        if isinstance(instr.src1, Label):
            self._emit_instr("ld", "hl", instr.src1.name)
            self._store_from_hl(instr.dst)
            return

        if not isinstance(instr.src1, MemoryLocation):
            return

        mem = instr.src1

        # Handle address of vreg (for out/in out parameter passing)
        if mem.addr_vreg is not None:
            # Get the stack offset of the vreg using the same formula as _vreg_offset
            frame_offset = self._vreg_offset(mem.addr_vreg)
            self._emit_instr("push", "ix")
            self._emit_instr("pop", "hl")
            if frame_offset != 0:
                self._emit_instr("ld", "de", str(frame_offset))
                self._emit_instr("add", "hl", "de")
            self._store_from_hl(instr.dst)
            return

        if mem.is_global:
            # Load address of global variable
            self._emit_instr("ld", "hl", self._mangle_symbol(mem.symbol_name))
        elif mem.base is not None:
            # Computed address: base + offset
            self._load_to_hl(mem.base)
            if mem.offset != 0:
                self._emit_instr("ld", "de", str(mem.offset))
                self._emit_instr("add", "hl", "de")
        else:
            # Stack-relative address: ix + frame_offset
            if mem.is_frame_offset:
                # Offset is already a proper frame offset (negative)
                frame_offset = mem.offset
            else:
                # The mem.offset is a positive stack slot (0, 2, 4, ...)
                # but locals are at negative offsets from ix
                # Transform to match _vreg_offset convention: -(offset + 2)
                frame_offset = -(mem.offset + 2)
            self._emit_instr("push", "ix")
            self._emit_instr("pop", "hl")
            if frame_offset != 0:
                self._emit_instr("ld", "de", str(frame_offset))
                self._emit_instr("add", "hl", "de")

        self._store_from_hl(instr.dst)

    def _gen_add(self, instr: IRInstr) -> None:
        """Generate ADD instruction."""
        # Special case: adding to stack pointer (for stack cleanup)
        if isinstance(instr.dst, MemoryLocation) and instr.dst.symbol_name == "_SP":
            # ADD _SP, const -> LD hl, const; ADD hl, sp; LD sp, hl
            # The immediate value is in src1 (dst=_SP, src1=const)
            if isinstance(instr.src1, Immediate):
                self._emit_instr("ld", "hl", str(instr.src1.value))
                self._emit_instr("add", "hl", "sp")
                self._emit_instr("ld", "sp", "hl")
            return
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("add", "hl", "de")
        self._store_from_hl(instr.dst)

    def _gen_sub(self, instr: IRInstr) -> None:
        """Generate SUB instruction."""
        # Special case: SUB _SP, _SP, immediate (stack allocation for aggregates)
        if isinstance(instr.dst, MemoryLocation) and instr.dst.symbol_name == "_SP":
            if isinstance(instr.src2, Immediate):
                size = instr.src2.value
                # Allocate stack space: sp := sp - size
                self._emit_instr("ld", "hl", f"-{size}")
                self._emit_instr("add", "hl", "sp")
                self._emit_instr("ld", "sp", "hl")
                return
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("or", "a")  # Clear carry
        self._emit_instr("sbc", "hl", "de")
        self._store_from_hl(instr.dst)

    def _gen_mul(self, instr: IRInstr) -> None:
        """Generate MUL instruction (16-bit multiply via subroutine)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("call", "_mul16")
        self._store_from_hl(instr.dst)

    def _gen_div(self, instr: IRInstr) -> None:
        """Generate DIV instruction (16-bit divide via subroutine)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("call", "_div16")
        self._store_from_hl(instr.dst)

    def _gen_mod(self, instr: IRInstr) -> None:
        """Generate MOD instruction (16-bit modulo via subroutine)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("call", "_mod16")
        self._store_from_hl(instr.dst)

    def _gen_rem(self, instr: IRInstr) -> None:
        """Generate REM instruction (16-bit remainder via subroutine)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("call", "_rem16")
        self._store_from_hl(instr.dst)

    def _gen_neg(self, instr: IRInstr) -> None:
        """Generate NEG instruction."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        # Negate hl: hl = 0 - hl
        self._emit_instr("xor", "a")
        self._emit_instr("sub", "l")
        self._emit_instr("ld", "l", "a")
        self._emit_instr("sbc", "a", "a")
        self._emit_instr("sub", "h")
        self._emit_instr("ld", "h", "a")
        self._store_from_hl(instr.dst)

    def _gen_and(self, instr: IRInstr) -> None:
        """Generate AND instruction (16-bit)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("ld", "a", "l")
        self._emit_instr("and", "e")
        self._emit_instr("ld", "l", "a")
        self._emit_instr("ld", "a", "h")
        self._emit_instr("and", "d")
        self._emit_instr("ld", "h", "a")
        self._store_from_hl(instr.dst)

    def _gen_or(self, instr: IRInstr) -> None:
        """Generate OR instruction (16-bit)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("ld", "a", "l")
        self._emit_instr("or", "e")
        self._emit_instr("ld", "l", "a")
        self._emit_instr("ld", "a", "h")
        self._emit_instr("or", "d")
        self._emit_instr("ld", "h", "a")
        self._store_from_hl(instr.dst)

    def _gen_xor(self, instr: IRInstr) -> None:
        """Generate XOR instruction (16-bit)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        self._emit_instr("ld", "a", "l")
        self._emit_instr("xor", "e")
        self._emit_instr("ld", "l", "a")
        self._emit_instr("ld", "a", "h")
        self._emit_instr("xor", "d")
        self._emit_instr("ld", "h", "a")
        self._store_from_hl(instr.dst)

    def _gen_not(self, instr: IRInstr) -> None:
        """Generate NOT instruction (16-bit complement)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._emit_instr("ld", "a", "l")
        self._emit_instr("cpl")
        self._emit_instr("ld", "l", "a")
        self._emit_instr("ld", "a", "h")
        self._emit_instr("cpl")
        self._emit_instr("ld", "h", "a")
        self._store_from_hl(instr.dst)

    def _gen_shl(self, instr: IRInstr) -> None:
        """Generate SHL instruction (16-bit left shift)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        # Call shift left runtime
        self._emit_instr("call", "_shl16")
        self._store_from_hl(instr.dst)

    def _gen_shr(self, instr: IRInstr) -> None:
        """Generate SHR instruction (16-bit right shift, unsigned)."""
        if not isinstance(instr.dst, VReg):
            return
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)
        # Call shift right runtime
        self._emit_instr("call", "_shr16")
        self._store_from_hl(instr.dst)

    def _gen_cmp_flags_only(self, instr: IRInstr) -> None:
        """Generate comparison instruction that only sets flags (no result storage).

        Used for membership tests and other conditional jumps where we just
        need to set the Z, S, and c flags for subsequent JL/JG/JZ/etc.
        """
        # Load both operands
        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)

        # Compare hl with de (hl - de), setting flags
        self._emit_instr("or", "a")  # Clear carry
        self._emit_instr("sbc", "hl", "de")
        # Flags are now set:
        # - Z: set if hl == de
        # - S: set if hl < de (signed)
        # - C: set if hl < de (unsigned, borrow)

    def _gen_cmp(self, instr: IRInstr) -> None:
        """Generate comparison instruction."""
        if not isinstance(instr.dst, VReg):
            return

        self._load_to_hl(instr.src1)
        self._load_to_de(instr.src2)

        # Compare hl with de
        self._emit_instr("or", "a")  # Clear carry
        self._emit_instr("sbc", "hl", "de")

        # Set result based on comparison type
        op = instr.opcode
        if op == OpCode.CMP_EQ:
            # Result = 1 if Z flag set
            self._emit_instr("ld", "hl", "0")
            self._emit_instr("jr", "nz", "$+3")
            self._emit_instr("inc", "l")
        elif op == OpCode.CMP_NE:
            # Result = 1 if Z flag not set
            self._emit_instr("ld", "hl", "0")
            self._emit_instr("jr", "z", "$+3")
            self._emit_instr("inc", "l")
        elif op == OpCode.CMP_LT:
            # Result = 1 if S flag set (negative result, signed)
            self._emit_instr("ld", "hl", "0")
            self._emit_instr("jp", "p", "$+4")
            self._emit_instr("inc", "l")
        elif op == OpCode.CMP_GE:
            # Result = 1 if S flag not set (non-negative, signed)
            self._emit_instr("ld", "hl", "0")
            self._emit_instr("jp", "m", "$+4")
            self._emit_instr("inc", "l")
        elif op == OpCode.CMP_GT:
            # GT: not (LT or EQ) - more complex
            # Layout: LD hl,1 (3) | JP m,$+7 (3) | JR z,$+4 (2) | JR $+3 (2) | DEC L (1)
            # JP m at X+3 should jump to DEC L at X+10, so $+7
            self._emit_instr("ld", "hl", "1")
            self._emit_instr("jp", "m", "$+7")
            self._emit_instr("jr", "z", "$+4")
            self._emit_instr("jr", "$+3")
            self._emit_instr("dec", "l")
        elif op == OpCode.CMP_LE:
            # LE: LT or EQ
            # Layout: LD hl,0 (3) | JP m,$+5 (3) | JR nz,$+3 (2) | INC L (1)
            # JP m at X+3 should jump to INC L at X+8, so $+5
            self._emit_instr("ld", "hl", "0")
            self._emit_instr("jp", "m", "$+5")
            self._emit_instr("jr", "nz", "$+3")
            self._emit_instr("inc", "l")

        self._store_from_hl(instr.dst)

    def _gen_jmp(self, instr: IRInstr) -> None:
        """Generate JMP instruction."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", instr.dst.name)

    def _gen_jz(self, instr: IRInstr) -> None:
        """Generate JZ (jump if zero) instruction."""
        if isinstance(instr.dst, Label) and instr.src1:
            self._load_to_hl(instr.src1)
            self._emit_instr("ld", "a", "h")
            self._emit_instr("or", "l")
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "z", instr.dst.name)

    def _gen_jnz(self, instr: IRInstr) -> None:
        """Generate JNZ (jump if not zero) instruction."""
        if isinstance(instr.dst, Label) and instr.src1:
            self._load_to_hl(instr.src1)
            self._emit_instr("ld", "a", "h")
            self._emit_instr("or", "l")
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "nz", instr.dst.name)

    def _gen_jl(self, instr: IRInstr) -> None:
        """Generate JL (jump if less, signed) - after CMP which did SBC hl,de."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "m", instr.dst.name)

    def _gen_jle(self, instr: IRInstr) -> None:
        """Generate JLE (jump if less or equal, signed) - after CMP."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "z", instr.dst.name)
            self._emit_instr("jp", "m", instr.dst.name)

    def _gen_jg(self, instr: IRInstr) -> None:
        """Generate JG (jump if greater, signed) - after CMP.
        Greater means not zero AND not negative (positive non-zero)."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            # Skip if equal (Z=1). JP z is 3 bytes, next JP p is 3 bytes.
            self._emit_instr("jp", "z", "$+6")  # Skip over next JP p
            self._emit_instr("jp", "p", instr.dst.name)  # Jump if positive

    def _gen_jge(self, instr: IRInstr) -> None:
        """Generate JGE (jump if greater or equal, signed) - after CMP."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "p", instr.dst.name)

    def _gen_jc(self, instr: IRInstr) -> None:
        """Generate JC (jump if carry, unsigned less) - after CMP."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "c", instr.dst.name)

    def _gen_jnc(self, instr: IRInstr) -> None:
        """Generate JNC (jump if no carry, unsigned >=) - after CMP."""
        if isinstance(instr.dst, Label):
            self._track_runtime_dep(instr.dst.name)
            self._emit_instr("jp", "nc", instr.dst.name)

    def _track_runtime_dep(self, name: str) -> None:
        """Track a runtime library dependency if the name is a runtime symbol."""
        if name.startswith("_") and not name.startswith("__"):
            self.runtime_deps.add(name)

    def _gen_call(self, instr: IRInstr) -> None:
        """Generate CALL instruction."""
        if isinstance(instr.dst, Label):
            self._emit_instr("call", self._mangle_symbol(instr.dst.name))

    def _gen_call_indirect(self, instr: IRInstr) -> None:
        """Generate indirect CALL through function pointer.

        src1 = function pointer (address to call)
        Uses _dispatch_call helper which expects address in hl.
        """
        # Load function pointer into hl
        self._load_to_hl(instr.src1)
        # Call through hl using the dispatch helper
        self._emit_instr("call", "_dispatch_call")

    def _gen_dispatch(self, instr: IRInstr) -> None:
        """Generate dispatching call through vtable.

        src1 = object (pointer to tagged record, tag is at offset 0)
        src2 = slot index in vtable
        """
        # Load object pointer to get tag (vtable pointer)
        self._load_to_hl(instr.src1)
        # hl now points to object, dereference to get vtable ptr
        self._emit_instr("ld", "e", "(hl)")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "d", "(hl)")
        # de now has vtable pointer

        # Calculate offset in vtable: (slot + 1) * 2 (each entry is 2 bytes)
        # +1 because slot 0 is the parent vtable pointer, actual primitives start at slot 1
        if isinstance(instr.src2, Immediate):
            # VTABLE_SLOT_OFFSET = 2 (skip parent ptr at offset 0)
            slot_offset = (instr.src2.value + 1) * 2
            self._emit_instr("ld", "hl", str(slot_offset))
            self._emit_instr("add", "hl", "de")
            self._emit_instr("ex", "de", "hl")
        # de now points to vtable entry

        # Load the target address from vtable
        self._emit_instr("ex", "de", "hl")
        self._emit_instr("ld", "e", "(hl)")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "d", "(hl)")
        # de now has target procedure address

        # Call through de using jp (de) via helper
        self._emit_instr("ex", "de", "hl")
        self._emit_instr("call", "_dispatch_call")

    def _gen_ret(self, instr: IRInstr) -> None:
        """Generate RET instruction."""
        # Put return value in hl if present
        if instr.src1:
            self._load_to_hl(instr.src1)

        # Epilogue
        if self.current_function:
            total_stack = (self.current_function.locals_size +
                          (self.reg_alloc.stack_size if self.reg_alloc else 0))
            has_frame = (self.current_function.locals_size > 0 or
                        (self.reg_alloc.stack_size if self.reg_alloc else 0) > 0 or
                        len(self.current_function.params) > 0)
            if has_frame:
                if total_stack > 0:
                    self._emit_instr("ld", "sp", "ix")
                self._emit_instr("pop", "ix")

        self._emit_instr("ret")

    def _gen_push(self, instr: IRInstr) -> None:
        """Generate PUSH instruction."""
        if instr.src1:
            self._load_to_hl(instr.src1)
            self._emit_instr("push", "hl")

    def _gen_pop(self, instr: IRInstr) -> None:
        """Generate POP instruction."""
        if isinstance(instr.dst, VReg):
            self._emit_instr("pop", "hl")
            self._store_from_hl(instr.dst)

    # =========================================================================
    # Exception Handling
    # =========================================================================
    #
    # Runtime exception handling uses a linked list of handler frames.
    # Global variables:
    #   _exc_handler:  current handler chain head (pointer)
    #   _exc_current:  currently raised exception ID
    #   _exc_message:  exception message pointer (or 0)
    #
    # Handler frame structure (8 bytes):
    #   +0: previous handler pointer (2 bytes)
    #   +2: saved sp (2 bytes)
    #   +4: handler address (2 bytes)
    #   +6: exception ID to catch (2 bytes, 0 = catch all)

    def _gen_exc_push(self, instr: IRInstr) -> None:
        """Generate exception handler push.

        dst = handler label, src1 = exception ID to catch
        """
        # Track exception global variables as runtime dependencies
        self._track_runtime_dep("_exc_handler")
        self._track_runtime_dep("_exc_current")
        self._track_runtime_dep("_exc_message")

        self._emit(f"    ; push exception handler")

        # Get handler address
        if isinstance(instr.dst, Label):
            handler_addr = instr.dst.name
        else:
            return

        # Get exception ID
        exc_id = 0
        if isinstance(instr.src1, Immediate):
            exc_id = instr.src1.value

        # Allocate 8 bytes for handler frame on stack
        self._emit_instr("ld", "hl", "-8")
        self._emit_instr("add", "hl", "sp")
        self._emit_instr("ld", "sp", "hl")

        # Store previous handler pointer at +0
        self._emit_instr("ld", "de", "(_exc_handler)")
        self._emit_instr("ld", "(hl)", "e")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "(hl)", "d")
        self._emit_instr("inc", "hl")

        # Store saved sp at +2 (sp before the frame allocation + 8)
        self._emit_instr("push", "hl")  # Save hl
        self._emit_instr("ld", "hl", "10")  # 8 bytes + 2 for push hl
        self._emit_instr("add", "hl", "sp")
        self._emit_instr("ld", "d", "h")
        self._emit_instr("ld", "e", "l")
        self._emit_instr("pop", "hl")  # Restore hl
        self._emit_instr("ld", "(hl)", "e")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "(hl)", "d")
        self._emit_instr("inc", "hl")

        # Store handler address at +4
        self._emit_instr("ld", "de", handler_addr)
        self._emit_instr("ld", "(hl)", "e")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "(hl)", "d")
        self._emit_instr("inc", "hl")

        # Store exception ID at +6
        self._emit_instr("ld", "de", str(exc_id))
        self._emit_instr("ld", "(hl)", "e")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "(hl)", "d")

        # Update _exc_handler to point to new frame
        self._emit_instr("ld", "hl", "0")
        self._emit_instr("add", "hl", "sp")
        self._emit_instr("ld", "(_exc_handler)", "hl")

    def _gen_exc_pop(self, instr: IRInstr) -> None:
        """Generate exception handler pop (normal exit)."""
        self._emit(f"    ; pop exception handler")

        # Get previous handler from frame
        self._emit_instr("ld", "hl", "(_exc_handler)")
        self._emit_instr("ld", "e", "(hl)")
        self._emit_instr("inc", "hl")
        self._emit_instr("ld", "d", "(hl)")

        # Restore _exc_handler
        self._emit_instr("ld", "(_exc_handler)", "de")

        # Deallocate frame (8 bytes)
        self._emit_instr("ld", "hl", "8")
        self._emit_instr("add", "hl", "sp")
        self._emit_instr("ld", "sp", "hl")

    def _gen_exc_raise(self, instr: IRInstr) -> None:
        """Generate raise exception.

        src1 = exception ID, src2 = message pointer (optional)
        """
        # Track exception runtime dependencies
        self._track_runtime_dep("_exc_current")
        self._track_runtime_dep("_exc_message")
        self._track_runtime_dep("_exc_do_raise")

        self._emit(f"    ; raise exception")

        # Store exception ID
        exc_id = 0
        if isinstance(instr.src1, Immediate):
            exc_id = instr.src1.value
        self._emit_instr("ld", "hl", str(exc_id))
        self._emit_instr("ld", "(_exc_current)", "hl")

        # Store message pointer (0 if none)
        if instr.src2:
            self._load_to_hl(instr.src2)
        else:
            self._emit_instr("ld", "hl", "0")
        self._emit_instr("ld", "(_exc_message)", "hl")

        # Call runtime raise routine
        self._emit_instr("call", "_exc_do_raise")

    def _gen_exc_reraise(self, instr: IRInstr) -> None:
        """Generate re-raise current exception."""
        # Track exception runtime dependencies
        self._track_runtime_dep("_exc_do_raise")

        self._emit(f"    ; reraise exception")
        # _exc_current and _exc_message already set
        self._emit_instr("call", "_exc_do_raise")

    # =========================================================================
    # Floating Point Operations (48-bit z88dk math48 format)
    # =========================================================================
    #
    # z88dk math48 format (6 bytes):
    #   Bytes 0-4: 40-bit mantissa (LSB first, sign in bit 7 of byte 4)
    #   Byte 5: 8-bit exponent (bias 128, 0 = zero value)
    #
    # Primary accumulator: bc'de'hl' (alternate register set)
    # Secondary operand: BCDEHL (main register set)
    # Result in: bc'de'hl' (alternate register set)
    #
    # Floating point values are stored in memory as 6 consecutive bytes.
    # For register operations, we use the 48-bit accumulator convention.

    def _load_float_to_primary(self, value: IRValue) -> None:
        """Load a 48-bit float value into the primary accumulator (bc'de'hl')."""
        if isinstance(value, VReg):
            offset = self._vreg_offset(value)
            # Load 6 bytes from stack into alternate registers
            self._emit_instr("ld", "l", f"(ix{offset:+d})")
            self._emit_instr("ld", "h", f"(ix{offset+1:+d})")
            self._emit_instr("ld", "e", f"(ix{offset+2:+d})")
            self._emit_instr("ld", "d", f"(ix{offset+3:+d})")
            self._emit_instr("ld", "c", f"(ix{offset+4:+d})")
            self._emit_instr("ld", "b", f"(ix{offset+5:+d})")
            # Exchange to alternate set
            self._emit_instr("exx")

    def _load_float_to_secondary(self, value: IRValue) -> None:
        """Load a 48-bit float value into the secondary operand (BCDEHL)."""
        if isinstance(value, VReg):
            offset = self._vreg_offset(value)
            # Load 6 bytes from stack into main registers
            self._emit_instr("ld", "l", f"(ix{offset:+d})")
            self._emit_instr("ld", "h", f"(ix{offset+1:+d})")
            self._emit_instr("ld", "e", f"(ix{offset+2:+d})")
            self._emit_instr("ld", "d", f"(ix{offset+3:+d})")
            self._emit_instr("ld", "c", f"(ix{offset+4:+d})")
            self._emit_instr("ld", "b", f"(ix{offset+5:+d})")

    def _store_float_from_primary(self, dst: VReg) -> None:
        """Store the primary accumulator (bc'de'hl') to a vreg."""
        offset = self._vreg_offset(dst)
        # Switch to alternate set to access result
        self._emit_instr("exx")
        # Store 6 bytes to stack
        self._emit_instr("ld", f"(ix{offset:+d})", "l")
        self._emit_instr("ld", f"(ix{offset+1:+d})", "h")
        self._emit_instr("ld", f"(ix{offset+2:+d})", "e")
        self._emit_instr("ld", f"(ix{offset+3:+d})", "d")
        self._emit_instr("ld", f"(ix{offset+4:+d})", "c")
        self._emit_instr("ld", f"(ix{offset+5:+d})", "b")
        # Switch back to main set
        self._emit_instr("exx")

    def _gen_fadd(self, instr: IRInstr) -> None:
        """Generate FADD instruction (float add)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float add")
        # Load first operand to primary (bc'de'hl')
        self._load_float_to_primary(instr.src1)
        # Load second operand to secondary (BCDEHL)
        self._load_float_to_secondary(instr.src2)
        # Call runtime add
        self._emit_instr("call", "_FADD48")
        # Store result
        self._store_float_from_primary(instr.dst)

    def _gen_fsub(self, instr: IRInstr) -> None:
        """Generate FSUB instruction (float subtract)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float sub")
        self._load_float_to_primary(instr.src1)
        self._load_float_to_secondary(instr.src2)
        self._emit_instr("call", "_FSUB48")
        self._store_float_from_primary(instr.dst)

    def _gen_fmul(self, instr: IRInstr) -> None:
        """Generate FMUL instruction (float multiply)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float mul")
        self._load_float_to_primary(instr.src1)
        self._load_float_to_secondary(instr.src2)
        self._emit_instr("call", "_FMUL48")
        self._store_float_from_primary(instr.dst)

    def _gen_fdiv(self, instr: IRInstr) -> None:
        """Generate FDIV instruction (float divide)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float div")
        self._load_float_to_primary(instr.src1)
        self._load_float_to_secondary(instr.src2)
        self._emit_instr("call", "_FDIV48")
        self._store_float_from_primary(instr.dst)

    def _gen_fneg(self, instr: IRInstr) -> None:
        """Generate FNEG instruction (float negate)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float negate")
        self._load_float_to_primary(instr.src1)
        self._emit_instr("call", "_FNEG48")
        self._store_float_from_primary(instr.dst)

    def _gen_fcmp(self, instr: IRInstr) -> None:
        """Generate FCMP instruction (float compare)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float compare")
        self._load_float_to_primary(instr.src1)
        self._load_float_to_secondary(instr.src2)
        self._emit_instr("call", "_FCMP48")
        # Result in A: -1 (less), 0 (equal), 1 (greater)
        # Store as word result
        self._emit_instr("ld", "l", "a")
        self._emit_instr("ld", "h", "0")
        # Sign extend if negative
        self._emit_instr("bit", "7", "l")
        self._emit_instr("jr", "z", "_fcmp_pos")
        self._emit_instr("ld", "h", "0xFF")
        self._emit("_fcmp_pos:")
        self._store_from_hl(instr.dst)

    def _gen_fabs(self, instr: IRInstr) -> None:
        """Generate FABS instruction (float absolute value)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float abs")
        self._load_float_to_primary(instr.src1)
        self._emit_instr("call", "_FABS48")
        self._store_float_from_primary(instr.dst)

    def _gen_itof(self, instr: IRInstr) -> None:
        """Generate ITOF instruction (integer to float conversion)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; int to float")
        # Load integer to hl
        self._load_to_hl(instr.src1)
        # Call conversion routine
        self._emit_instr("call", "_ITOF48")
        # Store result from primary accumulator
        self._store_float_from_primary(instr.dst)

    def _gen_ftoi(self, instr: IRInstr) -> None:
        """Generate FTOI instruction (float to integer conversion)."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; float to int")
        self._load_float_to_primary(instr.src1)
        self._emit_instr("call", "_FTOI48")
        # Result in hl
        self._store_from_hl(instr.dst)

    # =========================================================================
    # Tasking Operations
    # =========================================================================
    #
    # Tasking model:
    # - Tasks have Task Control Blocks (TCBs) with state, priority, sp, etc.
    # - Scheduler runs on timer interrupt (IM 1 or IM 2)
    # - Entry calls block until accepted
    #
    # TCB structure (16 bytes):
    #   +0: saved sp (2 bytes)
    #   +2: stack base (2 bytes)
    #   +4: stack size (2 bytes)
    #   +6: state (1 byte): 0=inactive, 1=ready, 2=waiting, 3=terminated
    #   +7: priority (1 byte)
    #   +8: next TCB in queue (2 bytes)
    #   +10: prev TCB in queue (2 bytes)
    #   +12: task ID (2 bytes)
    #   +14: reserved (2 bytes)

    def _gen_task_create(self, instr: IRInstr) -> None:
        """Generate TASK_CREATE instruction."""
        if not isinstance(instr.dst, VReg):
            return
        self._emit("    ; create task")
        # Load entry point address
        if isinstance(instr.src1, Label):
            self._emit_instr("ld", "hl", instr.src1.name)
        else:
            self._load_to_hl(instr.src1)
        # Call task creation runtime
        self._emit_instr("call", "_TASK_CREATE")
        # Result (task ID) in hl
        self._store_from_hl(instr.dst)

    def _gen_task_yield(self, instr: IRInstr) -> None:
        """Generate TASK_YIELD instruction."""
        self._emit("    ; yield to scheduler")
        self._emit_instr("call", "_TASK_YIELD")

    def _gen_task_terminate(self, instr: IRInstr) -> None:
        """Generate TASK_TERMINATE instruction."""
        self._emit("    ; terminate current task")
        self._emit_instr("call", "_TASK_TERMINATE")

    def _gen_task_delay(self, instr: IRInstr) -> None:
        """Generate TASK_DELAY instruction."""
        self._emit("    ; delay for ticks")
        # Load delay count to hl
        self._load_to_hl(instr.src1)
        self._emit_instr("call", "_TASK_DELAY")

    def _gen_task_delay_until(self, instr: IRInstr) -> None:
        """Generate TASK_DELAY_UNTIL instruction."""
        self._emit("    ; delay until time")
        # Load target time to hl
        self._load_to_hl(instr.src1)
        self._emit_instr("call", "_TASK_DELAY_UNTIL")

    def _gen_entry_call(self, instr: IRInstr) -> None:
        """Generate ENTRY_CALL instruction."""
        self._emit("    ; call task entry")
        # Push entry ID
        self._load_to_hl(instr.src2)
        self._emit_instr("push", "hl")
        # Push task ID
        self._load_to_hl(instr.src1)
        self._emit_instr("push", "hl")
        # Call entry
        self._emit_instr("call", "_ENTRY_CALL")
        # Clean up stack
        self._emit_instr("pop", "hl")
        self._emit_instr("pop", "hl")

    def _gen_entry_accept(self, instr: IRInstr) -> None:
        """Generate ENTRY_ACCEPT instruction."""
        self._emit("    ; accept entry call")
        # Load entry ID
        self._load_to_hl(instr.src1)
        self._emit_instr("call", "_ENTRY_ACCEPT")


def generate_z80(module: IRModule, emit_inline_runtime: bool = False) -> str:
    """Generate Z80 assembly from an IR module.

    Args:
        module: The IR module to compile
        emit_inline_runtime: If True, emit all runtime code inline (standalone mode).
                           If False (default), emit EXTRN declarations and link with libada.lib.
    """
    codegen = Z80CodeGen(emit_inline_runtime=emit_inline_runtime)
    return codegen.generate(module)
