// retroDE_ps2 — tb_top_psmct32_texalpha_demo (Ch344)
//
// TOP-LEVEL TEXTURED + source-over ALPHA SPRITE demo, end-to-end through the BRAM BOARD VARIANT
// (top_psmct32_raster_demo_bram) with SPRITE_TEX_ALPHA=1 — the integration the de25 board top fits,
// driving gs_stub through the REGISTERED read2 wrapper (the model the unit TB now matches).
//
//   EE bootlet (bios_texalpha.mem) + GIF payload (payload_texalpha.mem):
//     U1 -> upload an 8x8 checkerboard-ALPHA texture (opaque-white / fully-transparent).
//     U2 -> opaque BG SPRITE (ABE=0), solid blue, full 64x64.
//     U3 -> textured-alpha SPRITE (PRIM SPRITE+TME+ABE, source-over) over screen (16,16)-(48,48).
//
//   Per pixel of the overlay: As = TEXEL alpha. Opaque texel (A=0x80) -> Cv = Cs (the gray texel,
//   identity MODULATE by a white tint); transparent texel (A=0) -> Cv = Cd (the blue BG shows through).
//
// Verification (PCRTC scanout RGB — what a board wires to the video PHY):
//   - the BORDER (outside the overlay rect) is the pure blue BG.
//   - EVERY overlay pixel is EITHER gray (0xC0,0xC0,0xC0) OR blue BG — nothing else (binary alpha,
//     no garbage), and BOTH appear in quantity (the checkerboard + per-texel alpha both rendered).

`timescale 1ns/1ps

module tb_top_psmct32_texalpha_demo;

    localparam int H_ACTIVE = 64, V_ACTIVE = 64;
    localparam int SX0 = 16, SY0 = 16, SX1 = 48, SY1 = 48;   // overlay sprite rect

    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;

    top_psmct32_raster_demo_bram #(
        .H_ACTIVE(H_ACTIVE), .V_ACTIVE(V_ACTIVE),
        .VRAM_BYTES(32*1024), .PSMCT32_SWIZZLE(1'b0),
        .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)
    );

    // expected colors
    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;
    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_sprite(input int x, input int y);
        in_sprite = (x>=SX0) && (x<SX1) && (y>=SY0) && (y<SY1);
    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;
        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);
        if (dut.u_gs.raster_active==1'b1) wait (dut.u_gs.raster_active==1'b0);
        repeat(10) @(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++) begin
            for (int x=0;x<H_ACTIVE;x++) begin
                if (!cap_de[y][x]) begin
                    if (errors<8) $error("[texalpha] (%0d,%0d) DE never asserted", x, y); errors++;
                end else if (in_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("[texalpha] overlay (%0d,%0d)=(%02x,%02x,%02x) is neither gray nor BG",
                                              x, y, cap_b[y][x], cap_g[y][x], cap_r[y][x]);
                        errors++;
                    end
                end else begin
                    // border: pure BG
                    if (is_bg(x,y)) border_ok++;
                    else begin
                        if (errors<12) $error("[texalpha] border (%0d,%0d)=(%02x,%02x,%02x) not BG blue",
                                              x, y, cap_b[y][x], cap_g[y][x], cap_r[y][x]);
                        errors++;
                    end
                end
            end
        end

        // The checkerboard + per-texel alpha must BOTH render (gray opaque checks AND BG-through checks).
        if (spr_gray < 64) begin $error("[texalpha] too few opaque (gray) overlay px (%0d) — texel/blend dead", spr_gray); errors++; end
        if (spr_bg   < 64) begin $error("[texalpha] too few transparent overlay px (%0d) — texel ALPHA not honored", spr_bg); errors++; end
        if (spr_bad != 0)  begin $error("[texalpha] %0d overlay px were neither gray nor BG", spr_bad); errors++; end
        if (raster_overflow) begin $error("[texalpha] raster_overflow set"); errors++; end

        $display("[tb_top_psmct32_texalpha_demo] border_ok=%0d overlay 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_texalpha_demo] PASS"); $finish; end
        else $fatal(1, "[tb_top_psmct32_texalpha_demo] FAIL (%0d errors)", errors);
    end
    initial begin #30000000; $error("[tb_top_psmct32_texalpha_demo] TIMEOUT"); $finish; end
endmodule : tb_top_psmct32_texalpha_demo
