/*
* Graphical processor (Frame Synthesizer)
* Generates video from VRAM
*/
module FSX(
    //Clocks
    input clkPixel,
    input clkTMDShalf,
    //input clk14,
    //input clk114,
    input clkMuxOut,

    //HDMI
    output [3:0] TMDS_p,
    output [3:0] TMDS_n,

    //NTSC composite
    //output [7:0] composite,

    //Select output method
    //input selectOutput,

    input halfRes,

    //VRAM32
    output [13:0]       vram32_addr,
    input  [31:0]       vram32_q, 

    //VRAM322
    output [13:0]       vram322_addr,
    input  [31:0]       vram322_q, 

    //VRAM8
    output [13:0]       vram8_addr,
    input  [7:0]        vram8_q,

    //VRAMSPR
    output [13:0]       vramSPR_addr,
    input  [8:0]        vramSPR_q,

    //VRAMpixel
    output [16:0]       vramPX_addr,
    input  [23:0]       vramPX_q,

    //Interrupt signal
    output              frameDrawn
);

wire selectOutput = 1'b1; // always HDMI, as I no longer wish to include NTSC as a tiny HDMI monitor is now used as primary display

// LVDS Converter
wire [3:0] TMDS;

lvds lvdsConverter(
    .datain     (TMDS),
    .dataout    (TMDS_n),
    .dataout_b  (TMDS_p) // Reversed because of a LVDS polarity swap on the V3 PCB
);


wire [11:0] h_count_hdmi;
wire [11:0] v_count_hdmi;

wire hsync_hdmi;
wire vsync_hdmi;
wire csync;
wire blank_hdmi;
wire frameDrawn_hdmi;

TimingGenerator timingGenerator(
    // Clock
    .clkPixel(clkPixel),

    // Position counters
    .h_count(h_count_hdmi),
    .v_count(v_count_hdmi),

    // Video signals
    .hsync(hsync_hdmi),
    .vsync(vsync_hdmi),
    .csync(csync),
    .blank(blank_hdmi),
    
    // Interrupt signal
    .frameDrawn(frameDrawn_hdmi)
);

/*
wire [2:0] r_ntsc;
wire [2:0] g_ntsc;
wire [1:0] b_ntsc;


wire frameDrawn_ntsc;
wire [11:0] h_count_ntsc;
wire [11:0] v_count_ntsc;

wire hsync_ntsc;
wire vsync_ntsc;
wire blank_ntsc;


RGB332toNTSC rgb2ntsc(
    .clk(clk14), //14.318MHz
    .clkColor(clk114), //114.5454MHz
    .r(r_ntsc),
    .g(g_ntsc),
    .b(b_ntsc),
    .hcount(h_count_ntsc),
    .vcount(v_count_ntsc),
    .hs(hsync_ntsc),
    .vs(vsync_ntsc),
    .blank(blank_ntsc),
    .composite(composite), // video output signal
    .frameDrawn(frameDrawn_ntsc) // interrupt signal
);
*/

wire hsync;
wire vsync;
wire blank;
wire [11:0] h_count;
wire [11:0] v_count;

/*
assign frameDrawn   = (selectOutput == 1'b1) ? frameDrawn_hdmi : frameDrawn_ntsc;
assign hsync        = (selectOutput == 1'b1) ? hsync_hdmi : hsync_ntsc;
assign vsync        = (selectOutput == 1'b1) ? vsync_hdmi : ~vsync_ntsc; // ntsc vsync is inverted
assign blank        = (selectOutput == 1'b1) ? blank_hdmi : blank_ntsc;
assign h_count      = (selectOutput == 1'b1) ? h_count_hdmi : h_count_ntsc;
assign v_count      = (selectOutput == 1'b1) ? v_count_hdmi : v_count_ntsc;
*/

assign frameDrawn   = frameDrawn_hdmi;
assign hsync        = hsync_hdmi;
assign vsync        = vsync_hdmi;
assign blank        = blank_hdmi;
assign h_count      = h_count_hdmi;
assign v_count      = v_count_hdmi;



wire [2:0] BGW_r;
wire [2:0] BGW_g;
wire [1:0] BGW_b;


BGWrenderer bgwrenderer(
    // Video I/O
    .clk(clkMuxOut),
    .hs(hsync),
    .vs(vsync),
    .blank(blank),

    .scale2x(selectOutput),
    
    // Output colors
    .r(BGW_r),
    .g(BGW_g),
    .b(BGW_b),

    .h_count(h_count),  // line position in pixels including blanking 
    .v_count(v_count),  // frame position in lines including blanking 

    // VRAM32
    .vram32_addr(vram32_addr),
    .vram32_q(vram32_q), 

    // VRAM8
    .vram8_addr(vram8_addr),
    .vram8_q(vram8_q)
);


wire [7:0] PX_r;
wire [7:0] PX_g;
wire [7:0] PX_b;


PixelEngine pixelEngine(
    // Video I/O
    .clk(clkMuxOut),
    .hs(hsync),
    .vs(vsync),
    .blank(blank),

    .scale2x(selectOutput),
    .halfRes(halfRes),
    
    // Output colors
    .r(PX_r),
    .g(PX_g),
    .b(PX_b),

    .h_count(h_count),  // line position in pixels including blanking 
    .v_count(v_count),  // frame position in lines including blanking 

    // VRAM
    .vram_addr(vramPX_addr),
    .vram_q(vramPX_q)
);


// Give priority to pixel plane if bgw plane is black
wire pxPriority = (BGW_r == 3'd0 && BGW_g == 3'd0 && BGW_b == 2'd0);

wire [7:0] BGW_r_Byte;
wire [7:0] BGW_g_Byte;
wire [7:0] BGW_b_Byte;

assign BGW_r_Byte = (BGW_r == 3'd0) ?  {BGW_r, 5'b00000} : {BGW_r, 5'b11111};
assign BGW_g_Byte = (BGW_g == 3'd0) ?  {BGW_g, 5'b00000} : {BGW_g, 5'b11111};
assign BGW_b_Byte = (BGW_b == 2'd0) ?  {BGW_b, 6'b000000} : {BGW_b, 6'b111111};

wire [7:0] rByte;
wire [7:0] gByte;
wire [7:0] bByte;

assign rByte = (pxPriority) ? PX_r: BGW_r_Byte;
assign gByte = (pxPriority) ? PX_g: BGW_g_Byte;
assign bByte = (pxPriority) ? PX_b : BGW_b_Byte;


// Convert VGA signal to HDMI signals
RGB2HDMI rgb2hdmi(
    .clkTMDS(clkTMDShalf),
    .clkRGB (clkPixel),
    .rRGB   (rByte),
    .gRGB   (gByte),
    .bRGB   (bByte),
    .blk    (blank_hdmi),
    .hs     (hsync_hdmi),
    .vs     (vsync_hdmi), 
    .bTMDS  (TMDS[0]),
    .gTMDS  (TMDS[1]),
    .rTMDS  (TMDS[2]),
    .cTMDS  (TMDS[3])
);


// Image file generator for simulation
integer file;
integer framecounter = 0;

// HDMI
always @(negedge vsync_hdmi)
begin
    if (selectOutput == 1'b1)
    begin
        file = $fopen($sformatf("/home/bart/Documents/FPGA/FPGC6/Verilog/output/frame%0d.ppm", framecounter), "w");
        $fwrite(file, "P3\n");
        $fwrite(file, "640 480\n");
        $fwrite(file, "255\n");
        framecounter = framecounter + 1;
    end
end

always @(posedge clkPixel)
begin
    if (selectOutput == 1'b1)
    begin
        if (~blank_hdmi)
        begin
            $fwrite(file, "%d  %d  %d\n", rByte, gByte, bByte);
        end
    end
end

/*
wire [7:0] rByte_ntsc;
wire [7:0] gByte_ntsc;
wire [7:0] bByte_ntsc;
assign rByte_ntsc = (r_ntsc == 3'd0) ?  {r_ntsc, 5'b00000} : {r_ntsc, 5'b11111};
assign gByte_ntsc = (g_ntsc == 3'd0) ?  {g_ntsc, 5'b00000} : {g_ntsc, 5'b11111};
assign bByte_ntsc = (b_ntsc == 2'd0) ?  {b_ntsc, 6'b000000} : {b_ntsc, 6'b111111};

// NTSC
always @(negedge vsync_ntsc)
begin
    if (selectOutput == 1'b0)
    begin
        file = $fopen($sformatf("/home/bart/Documents/FPGA/FPGC6/Verilog/output/frame%0d.ppm", framecounter), "w");
        $fwrite(file, "P3\n");
        $fwrite(file, "320 240\n");
        $fwrite(file, "255\n");
        framecounter = framecounter + 1;
    end
end

always @(posedge clkPixel)
begin
    if (selectOutput == 1'b0)
    begin
        if (~blank_ntsc)
        begin
            $fwrite(file, "%d  %d  %d\n", rByte_ntsc, gByte_ntsc, bByte_ntsc);
        end
    end
end
*/

endmodule