`include "tune.v"

// This module renders pixels into TS-line for tiles/sprites
// Task execution is initiated by 'tsr_go' (one 'clk' period strobe).
// Inputs (only valid by 'tsr_go'):
// - DRAM address of graphics data (including page, line, word)
// - number of cycles to render (one cycle is 2 words = 8 pixels; 8 cycles = 64 pixels max)
// - X coordinate
// - X flip bit
// - palette selector
// Address in TS-line is calculated from X coordinate respecting the X flip.
// Inc/dec direction is set automatically.
// At the 'c2' of last DRAM cycle 'mem_rdy' is asserted.
// It should be used in comb to generate next cycle 'tsr_go' strobe for continuous renderer operation.
// First 'tsr_go' may be issued at any DRAM cycle, but the operation starts only
// after TS request recognized and processed by DRAM controller.
// It is recommended to assert 'tsr_go' at 'c2'.

module video_ts_render
(
  // clocks
  input wire clk,

  // controls
  input  wire        reset,      // line start strobe, inits the machine

  input  wire [ 8:0] x_coord,    // address in TS-line where render to, auto-flipped to match 'flip' times 'x_size'
  input  wire [ 2:0] x_size,     // number of rendering cycles (each cycle is 8 pixels, 0 = 1 cycle)
  input  wire        flip,       // indicates that sprite is X-flipped

  input  wire        tsr_go,     // 1 clk 28MHz strobe for render start. Should be issued along with 'mem_rdy' for continuous process
  input  wire [ 5:0] addr,       // address of dword within the line (dword = 8 pix)
  input  wire [ 8:0] line,       // line of bitmap
  input  wire [ 7:0] page,       // 1st page of bitmap (TxGpage or SGpage)
  input  wire [ 3:0] pal,        // palette selector, bits[7:4] of CRAM address
  output wire        mem_rdy,    // ready to receive new task

  // TS-Line interface
  output reg  [ 8:0] ts_waddr,
  output wire [ 7:0] ts_wdata,
  output wire        ts_we,

  // DRAM interface
  output wire [20:0] dram_addr,
  output wire        dram_req,
  input  wire [15:0] dram_rdata,
  input  wire        dram_pre_next,
  input  wire        dram_next
);

  // renderer mux
  reg [15:0] data;
  reg [3:0] pal_r;
  reg [2:0] pix_cnt;

  wire [3:0] pix_m[0:3];
  wire [3:0] pix = pix_m[pix_cnt[1:0]];
  assign ts_wdata = {pal_r, pix};

  assign pix_m[0] = data[7:4];
  assign pix_m[1] = data[3:0];
  assign pix_m[2] = data[15:12];
  assign pix_m[3] = data[11:8];

  // DRAM request
  assign dram_req = tsr_go || !mem_rdy;

  // DRAM addressing
  reg [20:0] addr_reg;

  wire [13:0] addr_offset = {page[7:3], line};
  wire [20:0] addr_in = {addr_offset, addr, 1'b0};
  wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
  assign dram_addr = tsr_go ? addr_in : addr_next;

  always @(posedge clk)
    addr_reg <= dram_addr;

  // DRAM cycles counter
  reg [4:0] cyc;

  assign mem_rdy = cyc[4];

  always @(posedge clk or posedge reset)
    if (reset)
      cyc <= 5'b10000;
    else if (tsr_go)
      cyc <= {1'b0, x_size, 1'b1};
    else if (dram_pre_next)
      cyc <= cyc - 5'd1;

  // DRAM data fetching
  always @(posedge clk)
    if (dram_next)
      data <= dram_rdata;

  // pixel render counter
  wire render_on = !pix_cnt[2];
  assign ts_we = render_on && |pix;   // write signal for TS-line

  always @(posedge clk or posedge reset)
    if (reset)
      pix_cnt <= 3'b100;
    else if (dram_next)
      pix_cnt <= 3'b000;
    else if (render_on)
      pix_cnt <= pix_cnt + 3'd1;

  // renderer reload
  reg tsr_rld;
  always @(posedge clk or posedge reset)
  if (reset)
    tsr_rld <= 1'b0;
    else if (tsr_go)
      tsr_rld <= 1'b1;
    else if (dram_next)
      tsr_rld <= 1'b0;

  // delayed values
  reg [8:0] x_coord_d;
  reg [3:0] pal_d;
  reg flip_d;

  always @(posedge clk)
  if (tsr_go)
  begin
    x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0);
    pal_d <= pal;
    flip_d <= flip;
  end


  // TS-line address
  reg flip_r;

  wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
  wire tsr_rld_stb = tsr_rld && dram_next;
  wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr);

  always @(posedge clk)
    ts_waddr <= ts_waddr_mx;

  always @(posedge clk)
  if (tsr_rld_stb)
  begin
    pal_r <= pal_d;
    flip_r <= flip_d;
  end

endmodule
