// retroDE_ps2 — gs_texel_addr
//
// Texture-sampling address generator (brick 1, step 1 of GS texturing).
//
// Given a texture coordinate (u,v) and the TEX0 texture descriptor, computes
// the LINEAR VRAM byte address of that texel — the read-side mirror of the
// rasterizer's existing framebuffer-address math (gs_stub.sv ~line 530:
//   fb_addr = base + (Y*FBW*64 + X) * bytes_per_pixel ).
//
// Linear (non-swizzled) only, on purpose: the swizzle paths in gs_stub are
// param-gated OFF by default, so linear is the baseline. Swizzled texel
// addressing will reuse the existing gs_swizzle_* modules later.
//
// `base_byte_addr` is the texture base in VRAM, ALREADY scaled to bytes by
// the caller from TEX0.TBP0. Keeping the base as a byte input (rather than
// scaling TBP0 here) isolates the one thing that must be reconciled with the
// texture-UPLOAD path (gif_image_xfer_stub / BITBLTBUF) — so we read texels
// from exactly where BITBLT wrote them. That reconciliation is tracked as the
// next integration step; this module's (u,v)->offset math is unambiguous and
// unit-tested below.

`timescale 1ns/1ps

module gs_texel_addr #(
    parameter int ADDR_W = 32
) (
    input  logic [31:0]        base_byte_addr, // texture base in VRAM (bytes)
    input  logic [10:0]        u,              // texel column (0..2047)
    input  logic [10:0]        v,              // texel row    (0..2047)
    input  logic [13:0]        tbw,            // TEX0.TBW — texels-per-row / 64
    input  logic [5:0]         psm,            // pixel storage mode
    output logic [ADDR_W-1:0]  texel_byte_addr,
    output logic               nibble_hi       // PSMT4: high nibble of the byte?
);

    localparam logic [5:0] PSMCT32 = 6'h00;
    localparam logic [5:0] PSMCT16 = 6'h02;
    localparam logic [5:0] PSMT8   = 6'h13;
    localparam logic [5:0] PSMT4   = 6'h14;

    // texels per row = TBW * 64
    logic [19:0] row_texels;
    // linear texel index = v * row_texels + u
    logic [31:0] texel_offset;

    always_comb begin
        row_texels   = {tbw, 6'b000000};                 // tbw * 64
        texel_offset = (v * row_texels) + {21'd0, u};

        unique case (psm)
            PSMCT32: begin
                texel_byte_addr = base_byte_addr + (texel_offset << 2);  // 4 B/texel
                nibble_hi       = 1'b0;
            end
            PSMCT16: begin
                texel_byte_addr = base_byte_addr + (texel_offset << 1);  // 2 B/texel
                nibble_hi       = 1'b0;
            end
            PSMT8: begin
                texel_byte_addr = base_byte_addr + texel_offset;         // 1 B/texel
                nibble_hi       = 1'b0;
            end
            PSMT4: begin
                texel_byte_addr = base_byte_addr + (texel_offset >> 1);  // 4 b/texel
                nibble_hi       = texel_offset[0];
            end
            default: begin
                texel_byte_addr = base_byte_addr + (texel_offset << 2);
                nibble_hi       = 1'b0;
            end
        endcase
    end

endmodule : gs_texel_addr
