// retroDE_ps2 — tb_ee_core_syscall_hle
//
// Ch273 — Focused TB for the minimal EE syscall HLE dispatcher in
// ee_core_stub. Known syscalls each resume at PC+4 with a stubbed
// $v0; one unhandled syscall halts.
// Ch285 — extended with syscall 0x40 case (Codex framing:
// "registered OK" stub, $v0=0). qbert.elf hits this at PC 0x00111D24
// past Ch284's LD landing.
// Ch289 — extended with syscall 0x78 case (Codex framing:
// "kernel setup OK" stub, $v0=0). qbert.elf hits this at PC
// 0x00112AA4 past Ch288's MMIO sweep.
// Ch290 — extended with syscall 0x12 case (Codex framing:
// "handler registered OK" stub, $v0=0). qbert.elf hits this at
// PC 0x00112A54 with the classic handler-install arg shape
// ($a0=5 channel/slot, $a1=fn_ptr, $a3=ctx_ptr); plausibly
// AddDmacHandler. Do NOT invoke the handler or mutate DMAC state.
// Ch291 — extended with syscall 0x16 case (Codex framing:
// "handler enabled OK" stub, $v0=0). qbert.elf hits this at PC
// 0x00112A74 with IDENTICAL args to Ch290's 0x12 call — the
// activation companion call. Same accept-and-continue treatment.
// Ch293 — extended with syscall 0x7A case (Codex framing: likely
// SyncDCache; cacheless model → no-op success, $v0=0). qbert.elf
// hits this at PC 0x00110994 right after Ch292's SYNC.
// Ch295 — extended with $a0=4 subcase of syscall 0x7A. The Ch294
// autopsy showed qbert polls syscall 0x7A($a0=4) for bit 17 in the
// return. Ch295's $a0-aware HLE returns $v0=0x00020000 for $a0=4,
// $v0=0 otherwise. This TB covers both subcases.
// Ch296 — extended with syscall 0x79 case (Codex framing: adjacent
// finalize/reset call, $v0=0). qbert.elf hits this at PC
// 0x00111D94 right after Ch295's $a0-aware unblock.
// Ch297 — extended with syscall 0x77 case (new syscall-family
// boundary; $a3 changed from global ctx ptr to small int 20).
// Plausibly SetVTLBRefillHandler or RegisterLibraryEntries.
// First-pass $v0=0.
//
// Test sequence:
//   ori   $v1, $0, 0x3C          ; $v1 = 60 = EndOfHeap
//   syscall                       ; -> $v0 = 0x001E0000, PC += 4
//   lui   $t1, 0x001E
//   bne   $v0, $t1, FAIL          ; verify return
//   nop
//
//   ori   $v1, $0, 0x3D          ; $v1 = 61 = InitMainThread
//   syscall                       ; -> $v0 = 0, PC += 4
//   bne   $v0, $0, FAIL
//   nop
//
//   ori   $v1, $0, 0x64          ; $v1 = 100 = FlushCache
//   syscall                       ; -> $v0 = 0, PC += 4
//   bne   $v0, $0, FAIL
//   nop
//
//   ori   $v1, $0, 0x7777        ; unknown syscall — should HALT
//   syscall                       ; <-- final halt = PASS marker
//
// Strict mode ON + EE_SYSCALL_HLE_ENABLE=1.

`timescale 1ns/1ps

module tb_ee_core_syscall_hle;

    localparam int BIOS_BYTES    = 4 * 1024 * 1024;
    localparam int EE_RAM_BYTES  = 4 * 1024;
    localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES);

    localparam logic [31:0] RESET_VECTOR  = 32'hBFC0_0000;
    localparam logic [31:0] BOOTSTRAP_VA  = 32'hBFC0_0100;
    localparam int          RESET_SLOT     = 0;
    localparam int          BOOTSTRAP_SLOT = 32'h100 >> 2;

    // ----- Slot layout -----
    localparam int S_ORI_V1_3C = BOOTSTRAP_SLOT + 0;
    localparam int S_SYS_3C    = BOOTSTRAP_SLOT + 1;
    localparam int S_LUI_T1    = BOOTSTRAP_SLOT + 2;
    localparam int S_BNE_3C    = BOOTSTRAP_SLOT + 3;
    localparam int S_DS_3C     = BOOTSTRAP_SLOT + 4;

    localparam int S_ORI_V1_3D = BOOTSTRAP_SLOT + 5;
    localparam int S_SYS_3D    = BOOTSTRAP_SLOT + 6;
    localparam int S_BNE_3D    = BOOTSTRAP_SLOT + 7;
    localparam int S_DS_3D     = BOOTSTRAP_SLOT + 8;

    localparam int S_ORI_V1_64 = BOOTSTRAP_SLOT + 9;
    localparam int S_SYS_64    = BOOTSTRAP_SLOT + 10;
    localparam int S_BNE_64    = BOOTSTRAP_SLOT + 11;
    localparam int S_DS_64     = BOOTSTRAP_SLOT + 12;

    // Ch285 — syscall 0x40 ("registered OK" stub, $v0=0).
    localparam int S_ORI_V1_40 = BOOTSTRAP_SLOT + 13;
    localparam int S_SYS_40    = BOOTSTRAP_SLOT + 14;
    localparam int S_BNE_40    = BOOTSTRAP_SLOT + 15;
    localparam int S_DS_40     = BOOTSTRAP_SLOT + 16;

    // Ch289 — syscall 0x78 ("kernel setup OK" stub, $v0=0).
    localparam int S_ORI_V1_78 = BOOTSTRAP_SLOT + 17;
    localparam int S_SYS_78    = BOOTSTRAP_SLOT + 18;
    localparam int S_BNE_78    = BOOTSTRAP_SLOT + 19;
    localparam int S_DS_78     = BOOTSTRAP_SLOT + 20;

    // Ch290 — syscall 0x12 ("handler registered OK" stub, $v0=0).
    localparam int S_ORI_V1_12 = BOOTSTRAP_SLOT + 21;
    localparam int S_SYS_12    = BOOTSTRAP_SLOT + 22;
    localparam int S_BNE_12    = BOOTSTRAP_SLOT + 23;
    localparam int S_DS_12     = BOOTSTRAP_SLOT + 24;

    // Ch291 — syscall 0x16 ("handler enabled OK" stub, $v0=0).
    localparam int S_ORI_V1_16 = BOOTSTRAP_SLOT + 25;
    localparam int S_SYS_16    = BOOTSTRAP_SLOT + 26;
    localparam int S_BNE_16    = BOOTSTRAP_SLOT + 27;
    localparam int S_DS_16     = BOOTSTRAP_SLOT + 28;

    // Ch293 — syscall 0x7A with $a0=0 (init-style; $v0=0 expected).
    localparam int S_ORI_V1_7A = BOOTSTRAP_SLOT + 29;
    localparam int S_SYS_7A    = BOOTSTRAP_SLOT + 30;
    localparam int S_BNE_7A    = BOOTSTRAP_SLOT + 31;
    localparam int S_DS_7A     = BOOTSTRAP_SLOT + 32;

    // Ch295 — syscall 0x7A with $a0=4 (poll-style; $v0=0x00020000
    // expected per the $a0-aware HLE patch).
    localparam int S_ORI_A0_4    = BOOTSTRAP_SLOT + 33;
    localparam int S_ORI_V1_7A_4 = BOOTSTRAP_SLOT + 34;
    localparam int S_SYS_7A_4    = BOOTSTRAP_SLOT + 35;
    localparam int S_LUI_EXP_4   = BOOTSTRAP_SLOT + 36;
    localparam int S_BNE_7A_4    = BOOTSTRAP_SLOT + 37;
    localparam int S_DS_7A_4     = BOOTSTRAP_SLOT + 38;

    // Ch296 — syscall 0x79 (adjacent/experimental, $v0=0).
    localparam int S_ORI_V1_79 = BOOTSTRAP_SLOT + 39;
    localparam int S_SYS_79    = BOOTSTRAP_SLOT + 40;
    localparam int S_BNE_79    = BOOTSTRAP_SLOT + 41;
    localparam int S_DS_79     = BOOTSTRAP_SLOT + 42;

    // Ch297 — syscall 0x77 (new syscall-family boundary, $v0=0).
    localparam int S_ORI_V1_77 = BOOTSTRAP_SLOT + 43;
    localparam int S_SYS_77    = BOOTSTRAP_SLOT + 44;
    localparam int S_BNE_77    = BOOTSTRAP_SLOT + 45;
    localparam int S_DS_77     = BOOTSTRAP_SLOT + 46;

    // Ch301 — syscall 0x17 (per-channel call, $v0=0).
    localparam int S_ORI_V1_17 = BOOTSTRAP_SLOT + 47;
    localparam int S_SYS_17    = BOOTSTRAP_SLOT + 48;
    localparam int S_BNE_17    = BOOTSTRAP_SLOT + 49;
    localparam int S_DS_17     = BOOTSTRAP_SLOT + 50;

    // Ch302 — syscall 0x13 (paired with 0x17, $v0=0).
    localparam int S_ORI_V1_13 = BOOTSTRAP_SLOT + 51;
    localparam int S_SYS_13    = BOOTSTRAP_SLOT + 52;
    localparam int S_BNE_13    = BOOTSTRAP_SLOT + 53;
    localparam int S_DS_13     = BOOTSTRAP_SLOT + 54;

    // Ch304 — syscall 0x6B (Table1 wrapper, return ignored, $v0=0).
    localparam int S_ORI_V1_6B = BOOTSTRAP_SLOT + 55;
    localparam int S_SYS_6B    = BOOTSTRAP_SLOT + 56;
    localparam int S_BNE_6B    = BOOTSTRAP_SLOT + 57;
    localparam int S_DS_6B     = BOOTSTRAP_SLOT + 58;

    localparam int S_ORI_V1_UN = BOOTSTRAP_SLOT + 59;
    localparam int S_SYS_UN    = BOOTSTRAP_SLOT + 60;  // PASS marker (halt)
    localparam int S_FAIL      = BOOTSTRAP_SLOT + 61;

    localparam logic [5:0] OP_J     = 6'h02;
    localparam logic [5:0] OP_BNE   = 6'h05;
    localparam logic [5:0] OP_ORI   = 6'h0D;
    localparam logic [5:0] OP_LUI   = 6'h0F;

    localparam int RV0 = 2;   // $v0
    localparam int RV1 = 3;   // $v1
    localparam int RT1 = 9;   // $t1
    localparam int RA0 = 4;   // $a0

    logic clk, rst_n;
    initial clk = 1'b0;
    always #5 clk = ~clk;

    // ----- EE core (strict mode + SYSCALL HLE ON) -----
    logic        core_go, core_halt, core_trap;
    logic [31:0] core_pc, core_trap_pc, core_trap_instr;
    logic        ee_rd_en, ee_rd_valid, ee_wr_en;
    logic [31:0] ee_rd_addr, ee_rd_data, ee_wr_addr, ee_wr_data;
    logic [3:0]  ee_wr_be;

    logic                core_ev_valid;
    trace_pkg::subsys_e  core_ev_subsys;
    trace_pkg::event_e   core_ev_event;
    logic [63:0]         core_ev_arg0, core_ev_arg1, core_ev_arg2, core_ev_arg3;
    logic [31:0]         core_ev_flags;

    ee_core_stub #(
        .STRICT_UNSUPPORTED(1'b1),
        .EE_SYSCALL_HLE_ENABLE(1'b1)
    ) u_core (
        .clk(clk), .rst_n(rst_n), .go_i(core_go),
        .map_rd_en(ee_rd_en), .map_rd_addr(ee_rd_addr),
        .map_rd_data(ee_rd_data), .map_rd_valid(ee_rd_valid),
        .map_wr_en(ee_wr_en), .map_wr_addr(ee_wr_addr),
        .map_wr_data(ee_wr_data), .map_wr_be(ee_wr_be),
        .cpu_irq(1'b0),
        .halt_o(core_halt), .pc_o(core_pc),
        .trap_o(core_trap), .trap_pc_o(core_trap_pc),
        .trap_instr_o(core_trap_instr),
        .ev_valid(core_ev_valid), .ev_subsys(core_ev_subsys),
        .ev_event(core_ev_event),
        .ev_arg0(core_ev_arg0), .ev_arg1(core_ev_arg1),
        .ev_arg2(core_ev_arg2), .ev_arg3(core_ev_arg3),
        .ev_flags(core_ev_flags)
    );

    // ----- Memory map + RAM + BIOS -----
    logic         bios_rd_en, bios_rd_valid;
    logic [21:0]  bios_rd_addr;
    logic [31:0]  bios_rd_data;

    logic         ee_ram_rd_en, ee_ram_rd_valid, ee_ram_wr_en;
    logic [24:0]  ee_ram_rd_addr, ee_ram_wr_addr;
    logic [127:0] ee_ram_rd_data, ee_ram_wr_data;
    logic [15:0]  ee_ram_wr_be;
    logic [7:0]   ee_ram_master_id;

    ee_memory_map_stub u_ee_map (
        .clk(clk), .rst_n(rst_n),
        .ee_rd_en(ee_rd_en), .ee_rd_addr(ee_rd_addr),
        .ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
        .ee_wr_en(ee_wr_en), .ee_wr_addr(ee_wr_addr),
        .ee_wr_data(ee_wr_data), .ee_wr_be(ee_wr_be),
        .dmac_rd_en(1'b0), .dmac_rd_addr(32'd0),
        .dmac_rd_data(), .dmac_rd_valid(),
        .bridge_wr_en(1'b0), .bridge_wr_addr(32'd0),
        .bridge_wr_data(128'd0), .bridge_wr_be(16'd0),
        .bridge_master_id(8'd0),
        .bios_rd_en(bios_rd_en), .bios_rd_addr(bios_rd_addr),
        .bios_rd_data(bios_rd_data), .bios_rd_valid(bios_rd_valid),
        .ram_rd_en(ee_ram_rd_en), .ram_rd_addr(ee_ram_rd_addr),
        .ram_rd_data(ee_ram_rd_data), .ram_rd_valid(ee_ram_rd_valid),
        .ram_wr_en(ee_ram_wr_en), .ram_wr_addr(ee_ram_wr_addr),
        .ram_wr_data(ee_ram_wr_data), .ram_wr_be(ee_ram_wr_be),
        .ram_master_id(ee_ram_master_id),
        .ee_dmac_ch2_wr_en(), .ee_dmac_ch2_wr_addr(), .ee_dmac_ch2_wr_data(),
        .ee_dmac_ch2_rd_en(), .ee_dmac_ch2_rd_addr(),
        .ee_dmac_ch2_rd_data(32'd0), .ee_dmac_ch2_rd_valid(1'b0),
        .ee_intc_wr_en(), .ee_intc_wr_addr(), .ee_intc_wr_data(),
        .ee_intc_rd_en(), .ee_intc_rd_addr(),
        .ee_intc_rd_data(32'd0), .ee_intc_rd_valid(1'b0),
        .ee_misc_mmio_wr_en(), .ee_misc_mmio_wr_addr(), .ee_misc_mmio_wr_data(), .ee_misc_mmio_wr_be(),
        .ee_misc_mmio_rd_en(), .ee_misc_mmio_rd_addr(),
        .ee_misc_mmio_rd_data(32'd0), .ee_misc_mmio_rd_valid(1'b0),
        .ee_biu_wr_en(), .ee_biu_wr_addr(), .ee_biu_wr_data(), .ee_biu_wr_be(),
        .ee_biu_rd_en(), .ee_biu_rd_addr(),
        .ee_biu_rd_data(32'd0), .ee_biu_rd_valid(1'b0),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(ee_ram_rd_en), .rd_addr(ee_ram_rd_addr[EE_RAM_ADDR_W-1:0]),
        .rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid),
        .wr_en(ee_ram_wr_en), .wr_addr(ee_ram_wr_addr[EE_RAM_ADDR_W-1:0]),
        .wr_data(ee_ram_wr_data), .wr_be(ee_ram_wr_be),
        .master_id(ee_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    bios_rom_stub #(.SIZE_BYTES(BIOS_BYTES)) u_bios (
        .clk(clk), .rst_n(rst_n),
        .rd_en(bios_rd_en), .rd_addr(bios_rd_addr),
        .rd_data(bios_rd_data), .rd_valid(bios_rd_valid),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    trace_sink_stub #(.FILENAME("ee_core_syscall_hle_core.trace"), .SINK_LABEL("ee_core"))
        u_trace_core (.clk(clk), .rst_n(rst_n),
            .ev_valid(core_ev_valid), .ev_subsys(core_ev_subsys),
            .ev_event(core_ev_event), .ev_arg0(core_ev_arg0),
            .ev_arg1(core_ev_arg1), .ev_arg2(core_ev_arg2),
            .ev_arg3(core_ev_arg3), .ev_flags(core_ev_flags));

    // ----- Observers -----
    int core_retired_events, core_halt_events, core_trap_events, errors;
    // Capture $v0 at each known-syscall retire so we can verify the
    // dispatched return value INDEPENDENTLY of the BNE chain.
    logic [31:0] v0_after_3C, v0_after_3D, v0_after_64, v0_after_40, v0_after_78, v0_after_12, v0_after_16, v0_after_7A, v0_after_7A_a0_4, v0_after_79, v0_after_77, v0_after_17, v0_after_13, v0_after_6B;
    logic        seen_3C_return, seen_3D_return, seen_64_return, seen_40_return, seen_78_return, seen_12_return, seen_16_return, seen_7A_return, seen_7A_a0_4_return, seen_79_return, seen_77_return, seen_17_return, seen_13_return, seen_6B_return;

    initial begin
        core_retired_events = 0;
        core_halt_events    = 0;
        core_trap_events    = 0;
        errors              = 0;
        v0_after_3C         = 32'hDEAD_BEEF;
        v0_after_3D         = 32'hDEAD_BEEF;
        v0_after_64         = 32'hDEAD_BEEF;
        v0_after_40         = 32'hDEAD_BEEF;
        v0_after_78         = 32'hDEAD_BEEF;
        v0_after_12         = 32'hDEAD_BEEF;
        v0_after_16         = 32'hDEAD_BEEF;
        v0_after_7A         = 32'hDEAD_BEEF;
        v0_after_7A_a0_4    = 32'hDEAD_BEEF;
        v0_after_79         = 32'hDEAD_BEEF;
        v0_after_77         = 32'hDEAD_BEEF;
        v0_after_17         = 32'hDEAD_BEEF;
        v0_after_13         = 32'hDEAD_BEEF;
        v0_after_6B         = 32'hDEAD_BEEF;
        seen_3C_return      = 1'b0;
        seen_3D_return      = 1'b0;
        seen_64_return      = 1'b0;
        seen_40_return      = 1'b0;
        seen_78_return      = 1'b0;
        seen_12_return      = 1'b0;
        seen_16_return      = 1'b0;
        seen_7A_return      = 1'b0;
        seen_7A_a0_4_return = 1'b0;
        seen_79_return      = 1'b0;
        seen_77_return      = 1'b0;
        seen_17_return      = 1'b0;
        seen_13_return      = 1'b0;
        seen_6B_return      = 1'b0;
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            core_retired_events <= core_retired_events + 1;
            if ((core_ev_flags & 32'h08) != 32'd0)
                core_halt_events <= core_halt_events + 1;
            if ((core_ev_flags & 32'h80) != 32'd0)
                core_trap_events <= core_trap_events + 1;
            // Snapshot $v0 the cycle AFTER each known syscall retires.
            // u_core.retired_pc is the syscall slot's PC (= S_SYS_*).
            // $v0 has the dispatched return at that cycle.
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_3C - BOOTSTRAP_SLOT) << 2)
                    && !seen_3C_return) begin
                v0_after_3C    <= u_core.regfile[RV0];
                seen_3C_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_3D - BOOTSTRAP_SLOT) << 2)
                    && !seen_3D_return) begin
                v0_after_3D    <= u_core.regfile[RV0];
                seen_3D_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_64 - BOOTSTRAP_SLOT) << 2)
                    && !seen_64_return) begin
                v0_after_64    <= u_core.regfile[RV0];
                seen_64_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_40 - BOOTSTRAP_SLOT) << 2)
                    && !seen_40_return) begin
                v0_after_40    <= u_core.regfile[RV0];
                seen_40_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_78 - BOOTSTRAP_SLOT) << 2)
                    && !seen_78_return) begin
                v0_after_78    <= u_core.regfile[RV0];
                seen_78_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_12 - BOOTSTRAP_SLOT) << 2)
                    && !seen_12_return) begin
                v0_after_12    <= u_core.regfile[RV0];
                seen_12_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_16 - BOOTSTRAP_SLOT) << 2)
                    && !seen_16_return) begin
                v0_after_16    <= u_core.regfile[RV0];
                seen_16_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_7A - BOOTSTRAP_SLOT) << 2)
                    && !seen_7A_return) begin
                v0_after_7A    <= u_core.regfile[RV0];
                seen_7A_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_7A_4 - BOOTSTRAP_SLOT) << 2)
                    && !seen_7A_a0_4_return) begin
                v0_after_7A_a0_4    <= u_core.regfile[RV0];
                seen_7A_a0_4_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_79 - BOOTSTRAP_SLOT) << 2)
                    && !seen_79_return) begin
                v0_after_79    <= u_core.regfile[RV0];
                seen_79_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_77 - BOOTSTRAP_SLOT) << 2)
                    && !seen_77_return) begin
                v0_after_77    <= u_core.regfile[RV0];
                seen_77_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_17 - BOOTSTRAP_SLOT) << 2)
                    && !seen_17_return) begin
                v0_after_17    <= u_core.regfile[RV0];
                seen_17_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_13 - BOOTSTRAP_SLOT) << 2)
                    && !seen_13_return) begin
                v0_after_13    <= u_core.regfile[RV0];
                seen_13_return <= 1'b1;
            end
            if (u_core.retired_pc == BOOTSTRAP_VA + ((S_SYS_6B - BOOTSTRAP_SLOT) << 2)
                    && !seen_6B_return) begin
                v0_after_6B    <= u_core.regfile[RV0];
                seen_6B_return <= 1'b1;
            end
        end
    end

    // ----- Helpers -----
    task automatic bios_word(input int slot, input logic [31:0] word);
        u_bios.mem[slot] = word;
    endtask

    function automatic logic [31:0] enc_i(input logic [5:0] op,
                                          input int rs, input int rt,
                                          input logic [15:0] imm);
        return {op, rs[4:0], rt[4:0], imm};
    endfunction

    function automatic logic [31:0] enc_j(input logic [31:0] target_addr);
        return {OP_J, target_addr[27:2]};
    endfunction

    function automatic logic [15:0] branch_imm(input int branch_slot,
                                               input int target_slot);
        int diff;
        diff = target_slot - branch_slot - 1;
        return diff[15:0];
    endfunction

    // ----- Stimulus -----
    initial begin
        rst_n   = 1'b0;
        core_go = 1'b0;
        repeat (4) @(posedge clk);
        rst_n = 1'b1;
        repeat (2) @(posedge clk);

        bios_word(RESET_SLOT,     enc_j(BOOTSTRAP_VA));
        bios_word(RESET_SLOT + 1, 32'd0);

        // EndOfHeap (0x3C)
        bios_word(S_ORI_V1_3C, enc_i(OP_ORI, 0, RV1, 16'h003C));
        bios_word(S_SYS_3C,    32'h0000_000C);   // syscall
        bios_word(S_LUI_T1,    enc_i(OP_LUI, 0, RT1, 16'h001E));
        bios_word(S_BNE_3C,    enc_i(OP_BNE, RV0, RT1, branch_imm(S_BNE_3C, S_FAIL)));
        bios_word(S_DS_3C,     32'd0);

        // InitMainThread (0x3D) — expects $v0=0
        bios_word(S_ORI_V1_3D, enc_i(OP_ORI, 0, RV1, 16'h003D));
        bios_word(S_SYS_3D,    32'h0000_000C);
        bios_word(S_BNE_3D,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_3D, S_FAIL)));
        bios_word(S_DS_3D,     32'd0);

        // FlushCache (0x64) — expects $v0=0
        bios_word(S_ORI_V1_64, enc_i(OP_ORI, 0, RV1, 16'h0064));
        bios_word(S_SYS_64,    32'h0000_000C);
        bios_word(S_BNE_64,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_64, S_FAIL)));
        bios_word(S_DS_64,     32'd0);

        // Ch285 — syscall 0x40 (Codex framing: "registered OK", $v0=0)
        bios_word(S_ORI_V1_40, enc_i(OP_ORI, 0, RV1, 16'h0040));
        bios_word(S_SYS_40,    32'h0000_000C);
        bios_word(S_BNE_40,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_40, S_FAIL)));
        bios_word(S_DS_40,     32'd0);

        // Ch289 — syscall 0x78 (Codex framing: "kernel setup OK", $v0=0)
        bios_word(S_ORI_V1_78, enc_i(OP_ORI, 0, RV1, 16'h0078));
        bios_word(S_SYS_78,    32'h0000_000C);
        bios_word(S_BNE_78,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_78, S_FAIL)));
        bios_word(S_DS_78,     32'd0);

        // Ch290 — syscall 0x12 (Codex framing: "handler registered OK", $v0=0)
        bios_word(S_ORI_V1_12, enc_i(OP_ORI, 0, RV1, 16'h0012));
        bios_word(S_SYS_12,    32'h0000_000C);
        bios_word(S_BNE_12,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_12, S_FAIL)));
        bios_word(S_DS_12,     32'd0);

        // Ch291 — syscall 0x16 (Codex framing: "handler enabled OK", $v0=0)
        bios_word(S_ORI_V1_16, enc_i(OP_ORI, 0, RV1, 16'h0016));
        bios_word(S_SYS_16,    32'h0000_000C);
        bios_word(S_BNE_16,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_16, S_FAIL)));
        bios_word(S_DS_16,     32'd0);

        // Ch293 — syscall 0x7A with $a0=0 (init-style, $v0=0).
        bios_word(S_ORI_V1_7A, enc_i(OP_ORI, 0, RV1, 16'h007A));
        bios_word(S_SYS_7A,    32'h0000_000C);
        bios_word(S_BNE_7A,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_7A, S_FAIL)));
        bios_word(S_DS_7A,     32'd0);

        // Ch295 — syscall 0x7A with $a0=4 (poll-style, $v0=0x20000).
        bios_word(S_ORI_A0_4,    enc_i(OP_ORI, 0, RA0, 16'h0004));
        bios_word(S_ORI_V1_7A_4, enc_i(OP_ORI, 0, RV1, 16'h007A));
        bios_word(S_SYS_7A_4,    32'h0000_000C);
        bios_word(S_LUI_EXP_4,   enc_i(OP_LUI, 0, RT1, 16'h0002));
        bios_word(S_BNE_7A_4,    enc_i(OP_BNE, RV0, RT1, branch_imm(S_BNE_7A_4, S_FAIL)));
        bios_word(S_DS_7A_4,     32'd0);

        // Ch296 — syscall 0x79 (adjacent/experimental, $v0=0).
        bios_word(S_ORI_V1_79, enc_i(OP_ORI, 0, RV1, 16'h0079));
        bios_word(S_SYS_79,    32'h0000_000C);
        bios_word(S_BNE_79,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_79, S_FAIL)));
        bios_word(S_DS_79,     32'd0);

        // Ch297 — syscall 0x77 (new syscall-family boundary, $v0=0).
        bios_word(S_ORI_V1_77, enc_i(OP_ORI, 0, RV1, 16'h0077));
        bios_word(S_SYS_77,    32'h0000_000C);
        bios_word(S_BNE_77,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_77, S_FAIL)));
        bios_word(S_DS_77,     32'd0);

        // Ch301 — syscall 0x17 (per-channel call, $v0=0).
        bios_word(S_ORI_V1_17, enc_i(OP_ORI, 0, RV1, 16'h0017));
        bios_word(S_SYS_17,    32'h0000_000C);
        bios_word(S_BNE_17,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_17, S_FAIL)));
        bios_word(S_DS_17,     32'd0);

        // Ch302 — syscall 0x13 (paired with 0x17, $v0=0).
        bios_word(S_ORI_V1_13, enc_i(OP_ORI, 0, RV1, 16'h0013));
        bios_word(S_SYS_13,    32'h0000_000C);
        bios_word(S_BNE_13,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_13, S_FAIL)));
        bios_word(S_DS_13,     32'd0);

        // Ch304 — syscall 0x6B (Table1 wrapper, return ignored, $v0=0).
        bios_word(S_ORI_V1_6B, enc_i(OP_ORI, 0, RV1, 16'h006B));
        bios_word(S_SYS_6B,    32'h0000_000C);
        bios_word(S_BNE_6B,    enc_i(OP_BNE, RV0, 0, branch_imm(S_BNE_6B, S_FAIL)));
        bios_word(S_DS_6B,     32'd0);

        // Unknown $v1 — should halt (PASS marker).
        bios_word(S_ORI_V1_UN, enc_i(OP_ORI, 0, RV1, 16'h7777));
        bios_word(S_SYS_UN,    32'h0000_000C);

        // FAIL slot — also a syscall, but $v1 still 0x77 at that
        // point so this would halt too if reached. The PC check
        // separates PASS halt from FAIL halt.
        bios_word(S_FAIL,      32'h0000_000C);

        @(negedge clk);
        core_go = 1'b1;
        @(negedge clk);
        core_go = 1'b0;

        while (!core_halt) @(posedge clk);
        repeat (4) @(posedge clk);

        // ---- Checks ----
        if (core_trap_events != 0) begin
            $error("[tb_ee_core_syscall_hle] unexpected trap (HLE dispatcher broken?): %0d",
                   core_trap_events);
            errors = errors + 1;
        end
        if (core_halt_events != 1) begin
            $error("[tb_ee_core_syscall_hle] expected exactly 1 halt (the unknown-syscall PASS marker), got %0d",
                   core_halt_events);
            errors = errors + 1;
        end
        // Final PC should be the unknown-syscall slot. If it's S_FAIL,
        // one of the BNE chains tripped.
        if (core_pc !== BOOTSTRAP_VA + ((S_SYS_UN - BOOTSTRAP_SLOT) << 2)) begin
            $error("[tb_ee_core_syscall_hle] final PC mismatch: got 0x%08h expected PASS-halt 0x%08h (FAIL slot=0x%08h)",
                   core_pc,
                   BOOTSTRAP_VA + ((S_SYS_UN - BOOTSTRAP_SLOT) << 2),
                   BOOTSTRAP_VA + ((S_FAIL    - BOOTSTRAP_SLOT) << 2));
            errors = errors + 1;
        end

        // $v0 returns captured independently of BNE chain.
        if (!seen_3C_return || v0_after_3C !== 32'h001E_0000) begin
            $error("[tb_ee_core_syscall_hle] $v0 after EndOfHeap = 0x%08h (expected 0x001E0000) seen=%0b",
                   v0_after_3C, seen_3C_return);
            errors = errors + 1;
        end
        if (!seen_3D_return || v0_after_3D !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after InitMainThread = 0x%08h (expected 0) seen=%0b",
                   v0_after_3D, seen_3D_return);
            errors = errors + 1;
        end
        if (!seen_64_return || v0_after_64 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after FlushCache = 0x%08h (expected 0) seen=%0b",
                   v0_after_64, seen_64_return);
            errors = errors + 1;
        end
        if (!seen_40_return || v0_after_40 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x40 = 0x%08h (expected 0) seen=%0b",
                   v0_after_40, seen_40_return);
            errors = errors + 1;
        end
        if (!seen_78_return || v0_after_78 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x78 = 0x%08h (expected 0) seen=%0b",
                   v0_after_78, seen_78_return);
            errors = errors + 1;
        end
        if (!seen_12_return || v0_after_12 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x12 = 0x%08h (expected 0) seen=%0b",
                   v0_after_12, seen_12_return);
            errors = errors + 1;
        end
        if (!seen_16_return || v0_after_16 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x16 = 0x%08h (expected 0) seen=%0b",
                   v0_after_16, seen_16_return);
            errors = errors + 1;
        end
        if (!seen_7A_return || v0_after_7A !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x7A ($a0=0) = 0x%08h (expected 0) seen=%0b",
                   v0_after_7A, seen_7A_return);
            errors = errors + 1;
        end
        if (!seen_7A_a0_4_return || v0_after_7A_a0_4 !== 32'h0002_0000) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x7A ($a0=4) = 0x%08h (expected 0x00020000) seen=%0b",
                   v0_after_7A_a0_4, seen_7A_a0_4_return);
            errors = errors + 1;
        end
        if (!seen_79_return || v0_after_79 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x79 = 0x%08h (expected 0) seen=%0b",
                   v0_after_79, seen_79_return);
            errors = errors + 1;
        end
        if (!seen_77_return || v0_after_77 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x77 = 0x%08h (expected 0) seen=%0b",
                   v0_after_77, seen_77_return);
            errors = errors + 1;
        end
        if (!seen_17_return || v0_after_17 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x17 = 0x%08h (expected 0) seen=%0b",
                   v0_after_17, seen_17_return);
            errors = errors + 1;
        end
        if (!seen_13_return || v0_after_13 !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x13 = 0x%08h (expected 0) seen=%0b",
                   v0_after_13, seen_13_return);
            errors = errors + 1;
        end
        if (!seen_6B_return || v0_after_6B !== 32'd0) begin
            $error("[tb_ee_core_syscall_hle] $v0 after syscall 0x6B = 0x%08h (expected 0) seen=%0b",
                   v0_after_6B, seen_6B_return);
            errors = errors + 1;
        end

        // Final $v1 should be 0x7777 (the unknown number); confirms
        // we halted on the unknown-syscall path, not on an earlier
        // known one that somehow misbehaved.
        if (u_core.regfile[RV1] !== 32'h0000_7777) begin
            $error("[tb_ee_core_syscall_hle] $v1 at halt = 0x%08h (expected 0x00007777 unknown marker)",
                   u_core.regfile[RV1]);
            errors = errors + 1;
        end

        $display("[tb_ee_core_syscall_hle] retired=%0d halt=%0d trap=%0d pc=0x%08h errors=%0d",
                 core_retired_events, core_halt_events,
                 core_trap_events, core_pc, errors);
        $display("[tb_ee_core_syscall_hle]   $v0_after_3C=0x%08h $v0_after_3D=0x%08h $v0_after_64=0x%08h $v0_after_40=0x%08h $v0_after_78=0x%08h $v0_after_12=0x%08h $v0_after_16=0x%08h $v0_after_7A=0x%08h $v0_after_7A_a0_4=0x%08h $v0_after_79=0x%08h $v0_after_77=0x%08h $v0_after_17=0x%08h $v0_after_13=0x%08h $v0_after_6B=0x%08h $v1_at_halt=0x%08h",
                 v0_after_3C, v0_after_3D, v0_after_64, v0_after_40, v0_after_78, v0_after_12, v0_after_16, v0_after_7A, v0_after_7A_a0_4, v0_after_79, v0_after_77, v0_after_17, v0_after_13, v0_after_6B,
                 u_core.regfile[RV1]);

        if (errors == 0)
            $display("[tb_ee_core_syscall_hle] PASS");
        else
            $display("[tb_ee_core_syscall_hle] FAIL");
        $finish;
    end

    initial begin
        #2000000;
        $error("[tb_ee_core_syscall_hle] timeout");
        $finish;
    end

endmodule : tb_ee_core_syscall_hle
