// retroDE_ps2 — tb_top_psmct32_feeder_sprite_demo (Ch345a)
//
// RUNTIME FEEDER SPRITE path: textured + source-over alpha SPRITEs streamed through the gs_prim_list_feeder
// (sprite_mode = staging word0[33]) into the SPRITE_TEX_ALPHA hardware, on the bram board variant. Proves
// the capability: host sprite command -> staging -> feeder -> textured-alpha sprite hardware -> pixels.
// This is RUNTIME SPRITE INGESTION (the Ch344-proven subset), NOT authentic glyph ingestion (Ch345b).
//
//   Setup bootlet (bios/payload_sprite_setup): upload an 8x8 alpha-checker texture + draw an opaque blue BG.
//   Feeder staging (feeder_sprite.mem): 3 textured-alpha SPRITE records over the BG (PRIM SPRITE+TME+ABE,
//   source-over, white tint = identity MODULATE, As = texel alpha).
//
// Verify (PCRTC scanout): each of the 3 sprite rects shows the alpha checker — gray (opaque texels) and
// blue (transparent texels reveal the BG); the border is the pure blue BG; both gray+blue appear.

`timescale 1ns/1ps

module tb_top_psmct32_feeder_sprite_demo;

    localparam int H_ACTIVE = 64, V_ACTIVE = 64;
    // the 3 SPRITE_FEEDER_SET rects from bake.py: (8,24)-(24,40), (26,24)-(42,40), (44,24)-(60,40)

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

    logic        core_go;
    logic [7:0]  r, g, b;
    logic        hsync, vsync, de;
    logic        core_halt, dma_done_seen, frame_seen, raster_overflow;
    logic        frame_toggle, dma_done_toggle;
    logic        feeder_go_tb, feeder_ready_tb;

    top_psmct32_raster_demo_bram #(
        .H_ACTIVE(H_ACTIVE), .V_ACTIVE(V_ACTIVE),
        .VRAM_BYTES(32*1024), .PSMCT32_SWIZZLE(1'b0),
        .COMBINED_TAZ(1'b0), .TILE_LOCAL(1'b0), .TILE_COLS(1), .TILE_ROWS(1),
        .TILE_MULTIPRIM(1'b0), .TILE_PRIM_COUNT(1), .TILE_FIFO_DEPTH(8),
        .BIN_BUFFER_ENABLE(1'b0), .HEARTBEAT_SPLICE_ENABLE(1'b0),
        .FEEDER_ENABLE(1'b1), .FEEDER_STG_WORDS(256),
        .SPRITE_TEX_ALPHA(1'b1)
    ) dut (
        .clk(clk), .rst_n(rst_n), .core_go(core_go),
        .r(r), .g(g), .b(b), .hsync(hsync), .vsync(vsync), .de(de),
        .core_halt(core_halt), .dma_done_seen(dma_done_seen),
        .frame_seen(frame_seen), .raster_overflow(raster_overflow),
        .frame_toggle(frame_toggle), .dma_done_toggle(dma_done_toggle),
        .joy_a_pressed_i(1'b0), .joy_b_pressed_i(1'b0),
        .feeder_stg_we_i(1'b0), .feeder_stg_waddr_i(12'd0), .feeder_stg_wdata_i(64'd0),
        .feeder_go_i(feeder_go_tb),
        .feeder_ready_o(feeder_ready_tb), .feeder_records_o(), .feeder_waits_o()
    );

    localparam logic [7:0] BG_R=8'h00, BG_G=8'h00, BG_B=8'hC0;   // blue BG
    localparam logic [7:0] GR_R=8'hC0, GR_G=8'hC0, GR_B=8'hC0;   // opaque-texel gray

    logic [7:0] cap_r[0:V_ACTIVE-1][0:H_ACTIVE-1], cap_g[0:V_ACTIVE-1][0:H_ACTIVE-1], cap_b[0:V_ACTIVE-1][0:H_ACTIVE-1];
    logic       cap_de[0:V_ACTIVE-1][0:H_ACTIVE-1];
    bit capture_armed;
    initial begin
        for (int y=0;y<V_ACTIVE;y++) for (int x=0;x<H_ACTIVE;x++) begin cap_r[y][x]=0;cap_g[y][x]=0;cap_b[y][x]=0;cap_de[y][x]=0; end
        capture_armed=1'b0; feeder_go_tb=1'b0;
    end
    logic [31:0] hcnt_d, vcnt_d;
    always_ff @(posedge clk) begin
        if (!rst_n) begin hcnt_d<=0; vcnt_d<=0; end
        else begin hcnt_d<=32'(dut.u_pcrtc.hcnt); vcnt_d<=32'(dut.u_pcrtc.vcnt); end
    end
    always_ff @(posedge clk)
        if (rst_n && capture_armed && de && (vcnt_d<V_ACTIVE) && (hcnt_d<H_ACTIVE)) begin
            cap_r[vcnt_d][hcnt_d]<=r; cap_g[vcnt_d][hcnt_d]<=g; cap_b[vcnt_d][hcnt_d]<=b; cap_de[vcnt_d][hcnt_d]<=1'b1; end

    function automatic bit in_any_sprite(input int x, input int y);
        in_any_sprite = (x>=8  && x<24 && y>=24 && y<40)
                     || (x>=26 && x<42 && y>=24 && y<40)
                     || (x>=44 && x<60 && y>=24 && y<40);
    endfunction
    function automatic bit is_bg  (input int x, input int y); is_bg  =(cap_b[y][x]==BG_B)&&(cap_g[y][x]==BG_G)&&(cap_r[y][x]==BG_R); endfunction
    function automatic bit is_gray(input int x, input int y); is_gray=(cap_b[y][x]==GR_B)&&(cap_g[y][x]==GR_G)&&(cap_r[y][x]==GR_R); endfunction

    int errors, border_ok, spr_gray, spr_bg, spr_bad;
    initial begin errors=0; border_ok=0; spr_gray=0; spr_bg=0; spr_bad=0; end

    initial begin
        rst_n=1'b0; core_go=1'b0;
        $readmemh(`FEEDER_SPRITE_FILE, dut.g_feeder.feeder_stg);
        repeat(4) @(posedge clk); rst_n=1'b1; repeat(8) @(posedge clk);
        @(negedge clk); core_go=1'b1; @(negedge clk); core_go=1'b0;
        wait (core_halt==1'b1); repeat(4) @(posedge clk);
        wait (dma_done_seen==1'b1); repeat(10) @(posedge clk);
        if (dut.xfer_busy==1'b1) wait (dut.xfer_busy==1'b0);
        wait (feeder_ready_tb==1'b1);
        repeat(200000) @(posedge clk);
        @(posedge dut.u_pcrtc.end_of_frame); @(posedge clk); capture_armed=1'b1;
        @(posedge dut.u_pcrtc.end_of_frame); @(posedge clk); capture_armed=1'b0;

        for (int y=0;y<V_ACTIVE;y++) for (int x=0;x<H_ACTIVE;x++) begin
            if (!cap_de[y][x]) begin if (errors<8) $error("[fsprite] (%0d,%0d) DE never asserted", x, y); errors++; end
            else if (in_any_sprite(x,y)) begin
                if      (is_gray(x,y)) spr_gray++;
                else if (is_bg(x,y))   spr_bg++;
                else begin spr_bad++; if (errors<12) $error("[fsprite] sprite (%0d,%0d)=(%02x,%02x,%02x) neither gray nor BG", x,y,cap_b[y][x],cap_g[y][x],cap_r[y][x]); errors++; end
            end else begin
                if (is_bg(x,y)) border_ok++;
                else begin if (errors<12) $error("[fsprite] border (%0d,%0d)=(%02x,%02x,%02x) not BG", x,y,cap_b[y][x],cap_g[y][x],cap_r[y][x]); errors++; end
            end
        end
        if (spr_gray < 96) begin $error("[fsprite] too few gray (opaque) sprite px (%0d) across 3 sprites", spr_gray); errors++; end
        if (spr_bg   < 96) begin $error("[fsprite] too few transparent sprite px (%0d) — texel alpha not honored", spr_bg); errors++; end
        if (spr_bad != 0)  begin $error("[fsprite] %0d sprite px neither gray nor BG", spr_bad); errors++; end
        if (raster_overflow) begin $error("[fsprite] raster_overflow set"); errors++; end

        $display("[tb_top_psmct32_feeder_sprite_demo] border_ok=%0d sprite gray=%0d bg=%0d bad=%0d emits=%0d errors=%0d",
                 border_ok, spr_gray, spr_bg, spr_bad, dut.u_gs.raster_pixel_emit_count, errors);
        if (errors==0) begin $display("[tb_top_psmct32_feeder_sprite_demo] PASS"); $finish; end
        else $fatal(1, "[tb_top_psmct32_feeder_sprite_demo] FAIL (%0d errors)", errors);
    end
    initial begin #1500000000; $error("[tb_top_psmct32_feeder_sprite_demo] TIMEOUT"); $finish; end
endmodule : tb_top_psmct32_feeder_sprite_demo
