/*
 * Avalon memory-mapped peripheral that generates VGA
* gic [29:0
 *
 * Stephen A. Edwards
 * Columbia University
 */

module vga_ball(input logic        clk,
	        input logic 	   reset,
		input logic [31:0]  writedata,
		input logic 	   write,
		input 		   chipselect,
		input logic [5:0]  address,

		output logic [7:0] VGA_R, VGA_G, VGA_B,
		output logic 	   VGA_CLK, VGA_HS, VGA_VS,
		                   VGA_BLANK_n,
		output logic 	   VGA_SYNC_n);

   logic [10:0]	   hcount;
   logic [9:0]     vcount;
   logic [7:0]     background_r, background_g, background_b;
	logic[31:0] background_colors, falling_reg, backZero;
   	logic[31:0]    back0, back1, back2, back3, back4, back5, back6, back7, back8, back9;
   logic [31:0]    back10, back11, back12, back13, back14, back15, back16, back17, back18, back19;
   logic [399:0] rows;

   logic [7:0]     backnext;
   logic [7:0]    falling_sprite;
   logic [9:0]     falling_v;
   logic [9:0]     falling_h;
   logic [1:0]     falling_ori;
   logic [31:0]    curr_score;
   logic [31:0]    high_score;
   logic [3:0]     music_sound;
   logic           music_en;
   logic [0:39] v_start;
   logic [0:29] h_start;

	//system nets
	logic [23:0] assigned_color, block_color;
	logic [15:0] num; 
	logic stitch;
   	logic v_line, v_on;
  	logic h_line, h_on;
	
	//yellow tetromino bitmap, //2D array for 20 pixel per line for 20 lines
	logic [0:19] yell [20];

	//block bitmap ROM
	logic [8:0] block_rom_addr;
	logic [19:0] row;
	logic [1:0] block;

	block_decode bdc(hcount, vcount, block_rom_addr);
   	color_assign bas(block_rom_data,block_color);
	

   //create sprite bitmap ROM
   localparam SPR_ROM_DEPTH = (80*80); //sprite width = 80px, sprite height = 80px
   logic [12:0] spr_rom_addr; //pixel position
   logic [7:0] spr_rom_data, block_rom_data; //pixel color
	//logic [7:0] yellow_data, test_data;
	logic [7:0] i1_data, i2_data, i3_data, i4_data;
	logic [7:0] o_data; 
        logic [7:0] t1_data, t2_data, t3_data, t4_data;
        logic [7:0] s1_data, s2_data, s3_data, s4_data;
	logic [7:0] z1_data, z2_data, z3_data, z4_data;
        logic [7:0] j1_data, j2_data, j3_data, j4_data;
        logic [7:0] l1_data, l2_data, l3_data, l4_data;
	logic [7:0] white_data, yellow_data, blue_data;

	//develop synchronous memory for all of the sprites
	rom_sync #(
		.INIT_F("i1.mem")
	) i1_rom (clk, spr_rom_addr, i1_data);

	rom_sync #(
		.INIT_F("i2.mem")
	) i2_rom (clk, spr_rom_addr, i2_data);

	rom_sync #(
		.INIT_F("i3.mem")
	) i3_rom (clk, spr_rom_addr, i3_data);

	rom_sync #(
		.INIT_F("i4.mem")
	) i4_rom (clk, spr_rom_addr, i4_data);

	rom_sync #(
		.INIT_F("t1.mem")
	) t1_rom (clk, spr_rom_addr, t1_data);

	rom_sync #(
		.INIT_F("t2.mem")
	) t2_rom (clk, spr_rom_addr, t2_data);

	rom_sync #(
		.INIT_F("t3.mem")
	) t3_rom (clk, spr_rom_addr, t3_data);

	rom_sync #(
		.INIT_F("t4.mem")
	) t4_rom (clk, spr_rom_addr, t4_data);

	rom_sync #(
		.INIT_F("o1.mem")
	) o_rom (clk, spr_rom_addr, o_data);

	rom_sync #(
		.INIT_F("s1.mem")
	) s1_rom (clk, spr_rom_addr, s1_data);

	rom_sync #(
		.INIT_F("s2.mem")
	) s2_rom (clk, spr_rom_addr, s2_data);

	rom_sync #(
		.INIT_F("s3.mem")
	) s3_rom (clk, spr_rom_addr, s3_data);

	rom_sync #(
		.INIT_F("s4.mem")
	) s4_rom (clk, spr_rom_addr, s4_data);

	rom_sync #(
		.INIT_F("z1.mem")
	) z1_rom (clk, spr_rom_addr, z1_data);

	rom_sync #(
		.INIT_F("z2.mem")
	) z2_rom (clk, spr_rom_addr, z2_data);

	rom_sync #(
		.INIT_F("z3.mem")
	) z3_rom (clk, spr_rom_addr, z3_data);

	rom_sync #(
		.INIT_F("z4.mem")
	) z4_rom (clk, spr_rom_addr, z4_data);

	rom_sync #(
		.INIT_F("j1.mem")
	) j1_rom (clk, spr_rom_addr, j1_data);

	rom_sync #(
		.INIT_F("j2.mem")
	) j2_rom (clk, spr_rom_addr, j2_data);

	rom_sync #(
		.INIT_F("j3.mem")
	) j3_rom (clk, spr_rom_addr, j3_data);

	rom_sync #(
		.INIT_F("j4.mem")
	) j4_rom (clk, spr_rom_addr, j4_data);

	rom_sync #(
		.INIT_F("l1.mem")
	) l1_rom (clk, spr_rom_addr, l1_data);

	rom_sync #(
		.INIT_F("l2.mem")
	) l2_rom (clk, spr_rom_addr, l2_data);

	rom_sync #(
		.INIT_F("l3.mem")
	) l3_rom (clk, spr_rom_addr, l3_data);

	rom_sync #(
		.INIT_F("l4.mem")
	) l4_rom (clk, spr_rom_addr, l4_data);

	//block ROMs
	rom_sync #(
		.INIT_F("white.mem")
	) white_rom (clk, block_rom_addr, white_data);

	rom_sync #(
		.INIT_F("red.mem")
	) red_rom (clk, block_rom_addr, red_data);

	rom_sync #(
		.INIT_F("blue.mem")
	) blue_rom (clk, block_rom_addr, blue_data);


   //make color assignment decoder
   color_decode cde(hcount[10:1],vcount,falling_h,falling_v, spr_rom_addr);
   color_assign cas(spr_rom_data,assigned_color);


   //stich background work
   initial begin  // random start values
	v_start = 40'b01100_00101_00110_10011_10101_10101_01111_01101;
	h_start = 30'b10111_01001_00001_10100_00111_01010;
   end
   // paint stitch pattern with 16x16 pixel grid
   always_comb begin
	v_line = (hcount[4:1] == 4'b0000);
	h_line = (vcount[3:0] == 4'b0000);
	v_on = vcount[4] ^ v_start[hcount[10:5]];
	h_on = hcount[5] ^ h_start[vcount[8:4]];
	stitch = (v_line && v_on) || (h_line && h_on);
   end

   vga_counters counters(.clk50(clk), .*);

   always_ff @(posedge clk)
     if (reset) begin
	     background_colors <= 32'h001a1c80;
	     falling_reg <= 32'h0;
	     backZero <= 32'h003a5e3f;
	     background_r <= 8'h1a;
	     background_g <= 8'h1c;
	     background_b <= 8'h80;
	     //write black to the entire gamespace
	     back0 <= 32'h004f061f;
	     back1 <= 32'h0;
	     back2 <= 32'h0;
	     back3 <= 32'h0;
	     back4 <= 32'h0;
	     back5 <= 32'h0;
	     back6 <= 32'h0;
	     back7 <= 32'h0;
	     back8 <= 32'h0;
	     back9 <= 32'h0;
	     back10 <= 32'h0;
	     back11 <= 32'h0;
	     back12 <= 32'h0;
	     back13 <= 32'h0;
	     back14 <= 32'h0;
	     back15 <= 32'h0;
	     back16 <= 32'h0;
	     back17 <= 32'h0;
	     back18 <= 32'h0;
	     back19 <= 32'h0;
	     //put certain of tetromino in random place
	     falling_v <= 10'b0000010000;
	     falling_h <= 10'b1111111111;
	     falling_ori <= 2'b00;
	     //falling_sprite <= 8'd0;
	     backnext <= 3'b000;
	     //set initial scores to 0
	     curr_score <= 32'h0;
	     high_score <= 32'h0;
	     //start with no music on
	     music_sound <= 4'h0;
	     music_en <= 1'b0;
	    
     end else if (chipselect && write) begin 
       case (address)
	       6'd0 : begin
		       background_colors <= writedata;
	       end
	       6'd1 : begin 
		       falling_reg <= writedata;
	       end
	       6'd2 : back0 <= writedata;
	       6'd3 : back1 <= writedata;
	       6'd4 : back4 <= writedata;
	       6'd5 : back5 <= writedata;
	       6'd6 : back6 <= writedata;
	       6'd7 : back7 <= writedata;
	       6'd8 : back8 <= writedata;
	       6'd9 : back9 <= writedata;
	       6'd10 : back10 <= writedata;
	       6'd11 : back11 <= writedata;
	       6'd12 : back12 <= writedata;
	       6'd13 : back13 <= writedata;
	       6'd14 : back14 <= writedata;
	       6'd15 : back15 <= writedata;
	       6'd16 : back16 <= writedata;
	       6'd17 : back17 <= writedata;
	       6'd18 : back18 <= writedata;
	       6'd19 : back19 <= writedata;
	       6'd20 : falling_v_reg <= writedata;
	       //falling_v <= writedata[9:0]
	       6'd21 : falling_h_reg <= writedata;
	       //falling_h <= writedata[9:0]
	      // 6'd22 : falling_sprite <= writedata[7:0];
	       6'd23 : backnext <= writedata[2:0];
	       6'd24 : curr_score <= writedata;
	       6'd25 : high_score <= writedata;
	       6'd26 : music_sound <= writedata[3:0];
	       6'd27 : music_en <= writedata[0]; 
	       6'd28 : falling_ori <= writedata[1:0];
       endcase
     end
	//UPLOAD ALL CODE TO A GITHUBB BRANCH

	//hcount is 11 bits, but you only use the top 10
	logic [3:0] sel;
	logic [31:0] back0cpy;
   always_comb begin
      {VGA_R, VGA_G, VGA_B} = {8'h0, 8'h0, 8'h0};
	   falling_v = falling_v_reg[9:0]; 
	   falling_h = falling_h_reg[9:0]; 
	  // back0cpy = backZero[31:0];
	   sel = 4'd0; 
	  // block <= 2'd0;
	   //row <= 20'd0;
	   //assign falling sprite from its 32 bit register
	   falling_sprite = falling_reg[7:0];
	   case (falling_sprite)
		   8'd0: begin
			   spr_rom_data = i1_data;
			   sel = 4'd0;
		   end
		   8'd1: begin
			   spr_rom_data = i2_data;
			   sel = 4'd1;
		   end
		   8'd2: begin
			   spr_rom_data = i3_data;
			   sel = 4'd2;
		   end
		   8'd3: spr_rom_data = i4_data;
		   8'd4: spr_rom_data = o_data;
		   8'd5: spr_rom_data = o_data;
		   8'd6: spr_rom_data = o_data;
		   8'd7: spr_rom_data = o_data;
		   8'd8: spr_rom_data = t1_data;
		   8'd9: spr_rom_data = t2_data;
		   8'd10: spr_rom_data = t3_data;
		   8'd11: spr_rom_data = t4_data;
		   8'd12: spr_rom_data = s1_data;
		   8'd13: spr_rom_data = s2_data;
		   8'd14: spr_rom_data = s3_data;
		   8'd15: spr_rom_data = s4_data;
		   8'd16: spr_rom_data = z1_data;
		   8'd17: spr_rom_data = z2_data;
		   8'd18: spr_rom_data = z3_data;
		   8'd19: spr_rom_data = z4_data;
		   8'd20: spr_rom_data = j1_data;
		   8'd21: spr_rom_data = j2_data;
		   8'd22: spr_rom_data = j3_data;
		   8'd23: spr_rom_data = j4_data;
		   8'd24: spr_rom_data = l1_data;
		   8'd25: spr_rom_data = l2_data;
		   8'd26: spr_rom_data = l3_data;
		   8'd27: spr_rom_data = l4_data;
		   default: spr_rom_data = l4_data;
	   endcase

	   if (VGA_BLANK_n) begin
		   //if in game space
		   if (hcount[10:1] > 220 && hcount[10:1] < 420 && (vcount > 70 && vcount < 470)) begin
			   //first draw  falling tetromino
			   if ((hcount[10:1] <= falling_h + 79) && (hcount[10:1] >= falling_h) && (vcount >= falling_v) && (vcount < falling_v + 79)) begin
			 	//assign vga values based upon assigned color
					{VGA_R, VGA_G, VGA_B} = assigned_color; 
			   end
			   //now draw the game space background
			   else if (vcount >= 450 && vcount < 470) 
				    {VGA_R, VGA_G, VGA_B} = {back0[7:0], back0[15:8], back0[23:16]};
			   else if (vcount >= 430 && vcount < 450) 
				    {VGA_R, VGA_G, VGA_B} = {back1[7:0], back1[15:8], back1[23:16]};
			   else if (vcount >= 410 && vcount < 430)
				    {VGA_R, VGA_G, VGA_B} = {back2[7:0], back2[15:8], back2[23:16]};
			   else if (vcount >= 390 && vcount < 410) 
				    {VGA_R, VGA_G, VGA_B} = {back3[7:0], back3[15:8], back3[23:16]};
			   else if (vcount >= 370  && vcount < 390) 
				    {VGA_R, VGA_G, VGA_B} = {back4[7:0], back4[15:8], back4[23:16]};
			   else if (vcount >= 350 && vcount < 370) 
				    {VGA_R, VGA_G, VGA_B} = {back5[7:0], back5[15:8], back5[23:16]};
			   else if (vcount >= 330 && vcount < 350)
				    {VGA_R, VGA_G, VGA_B} = {back6[7:0], back6[15:8], back6[23:16]};
			   else if (vcount >= 310 && vcount < 330)
				    {VGA_R, VGA_G, VGA_B} = {back7[7:0], back7[15:8], back7[23:16]};
			   else if (vcount >= 290 && vcount < 310) 
				    {VGA_R, VGA_G, VGA_B} = {back8[7:0], back8[15:8], back8[23:16]};
			   else if (vcount >= 270 && vcount < 290)
				    {VGA_R, VGA_G, VGA_B} = {back9[7:0], back9[15:8], back9[23:16]};
			   else if (vcount >= 250 && vcount < 270) 
				    {VGA_R, VGA_G, VGA_B} = {back10[7:0], back10[15:8], back10[23:16]};
			   else if (vcount >= 230 && vcount < 250) 
				    {VGA_R, VGA_G, VGA_B} = {back11[7:0], back11[15:8], back11[23:16]};
			   else if (vcount >= 210  && vcount < 230) 
				    {VGA_R, VGA_G, VGA_B} = {back12[7:0], back12[15:8], back12[23:16]};
			   else if (vcount >= 190 && vcount < 210)
				    {VGA_R, VGA_G, VGA_B} = {back13[7:0], back13[15:8], back13[23:16]};
			   else if (vcount >= 170 && vcount < 190) 
				    {VGA_R, VGA_G, VGA_B} = {back14[7:0], back14[15:8], back14[23:16]};
			   else if (vcount >= 150 && vcount < 170) 
				    {VGA_R, VGA_G, VGA_B} = {back15[7:0], back15[15:8], back15[23:16]};
			   else if (vcount >= 130 && vcount < 150) 
				    {VGA_R, VGA_G, VGA_B} = {back16[7:0], back16[15:8], back16[23:16]};
			   else if (vcount >= 110  && vcount < 130)
				    {VGA_R, VGA_G, VGA_B} = {back17[7:0], back17[15:8], back17[23:16]};
			   else if (vcount >= 90 && vcount < 110)
				    {VGA_R, VGA_G, VGA_B} = {back18[7:0], back18[15:8], back18[23:16]};
			   else if (vcount >= 70 && vcount < 90)
				    {VGA_R, VGA_G, VGA_B} = {back19[7:0], back19[15:8], back19[23:16]};
			   else begin
			   //lastly draw everything else as black
			  	{VGA_R, VGA_G, VGA_B} = {8'h00, 8'h00, 8'h00};
			   end 
		   end
		   //if not in gamespace
		   else if ((hcount[10:1] > 440 && hcount[10:1] < 600) && (vcount > 20 && vcount < 150)) begin
			//draw score box
			{VGA_R, VGA_G, VGA_B} = {8'h00, 8'h00, 8'h00};
		   end
		   else if ((hcount[10:1] > 440 && hcount[10:1] < 550) && (vcount > 170 && vcount < 250)) begin
			//draw next tetro box
			{VGA_R, VGA_G, VGA_B} = {8'h00, 8'h00, 8'h00};
		   end
		   else if ((hcount[10:1] > 220 && hcount[10:1] < 420) && (vcount > 20 && vcount < 60)) begin
			//draw line count box
			{VGA_R, VGA_G, VGA_B} = {8'h00, 8'h00, 8'h00};
		   end
		   //indicator lower
		   else if ((hcount[10:1] > 20 && hcount[10:1] < 70) && (vcount > 400 && vcount < 440)) begin
			   {VGA_R, VGA_G, VGA_B} = {back0[23:16], back0[15:8], back0[7:0]};
		   end
		   else if ((hcount[10:1] > 20 && hcount[10:1] < 70) && (vcount > 300 && vcount < 350)) begin
			   {VGA_R, VGA_G, VGA_B} = {background_colors[7:0], background_colors[15:8], background_colors[23:16]};
			    /* case(sel)
				     4'd0: {VGA_R, VGA_G, VGA_B} = {8'hff, 8'h00, 8'h00};
				     4'd1: {VGA_R, VGA_G, VGA_B} = {8'h00, 8'h00, 8'hff};
				     4'd2: {VGA_R, VGA_G, VGA_B} = {8'h00, 8'hff, 8'h00};
				     default: {VGA_R, VGA_G, VGA_B} = {8'hff, 8'hff, 8'h00};
			     endcase 
				    */
		   end
		   else begin
			  // {VGA_R, VGA_G, VGA_B} = {background_r, background_g, background_b};
			   VGA_R = (stitch) ? 8'h86 : 8'hc4;
			   VGA_G = (stitch) ? 8'hf5 : 8'hbf;
			   VGA_B = (stitch) ? 8'hfc : 8'hbe;
		   end
	   end
   end
	       
endmodule

module vga_counters(
 input logic 	     clk50, reset,
 output logic [10:0] hcount,  // hcount[10:1] is pixel column
 output logic [9:0]  vcount,  // vcount[9:0] is pixel row
 output logic 	     VGA_CLK, VGA_HS, VGA_VS, VGA_BLANK_n, VGA_SYNC_n);

/*
 * 640 X 480 VGA timing for a 50 MHz clock: one pixel every other cycle
 * 
 * HCOUNT 1599 0             1279       1599 0
 *             _______________              ________
 * ___________|    Video      |____________|  Video
 * 
 * 
 * |SYNC| BP |<-- HACTIVE -->|FP|SYNC| BP |<-- HACTIVE
 *       _______________________      _____________
 * |____|       VGA_HS          |____|
 */
   // Parameters for hcount
   parameter HACTIVE      = 11'd 1280,
             HFRONT_PORCH = 11'd 32,
             HSYNC        = 11'd 192,
             HBACK_PORCH  = 11'd 96,   
             HTOTAL       = HACTIVE + HFRONT_PORCH + HSYNC +
                            HBACK_PORCH; // 1600
   
   // Parameters for vcount
   parameter VACTIVE      = 10'd 480,
             VFRONT_PORCH = 10'd 10,
             VSYNC        = 10'd 2,
             VBACK_PORCH  = 10'd 33,
             VTOTAL       = VACTIVE + VFRONT_PORCH + VSYNC +
                            VBACK_PORCH; // 525

   logic endOfLine;
   
   always_ff @(posedge clk50 or posedge reset)
     if (reset)          hcount <= 0;
     else if (endOfLine) hcount <= 0;
     else  	         hcount <= hcount + 11'd 1;

   assign endOfLine = hcount == HTOTAL - 1;
       
   logic endOfField;
   
   always_ff @(posedge clk50 or posedge reset)
     if (reset)          vcount <= 0;
     else if (endOfLine)
       if (endOfField)   vcount <= 0;
       else              vcount <= vcount + 10'd 1;

   assign endOfField = vcount == VTOTAL - 1;

   // Horizontal sync: from 0x520 to 0x5DF (0x57F)
   // 101 0010 0000 to 101 1101 1111
   assign VGA_HS = !( (hcount[10:8] == 3'b101) &
		      !(hcount[7:5] == 3'b111));
   assign VGA_VS = !( vcount[9:1] == (VACTIVE + VFRONT_PORCH) / 2);

   assign VGA_SYNC_n = 1'b0; // For putting sync on the green signal; unused
   
   // Horizontal active: 0 to 1279     Vertical active: 0 to 479
   // 101 0000 0000  1280	       01 1110 0000  480
   // 110 0011 1111  1599	       10 0000 1100  524
   assign VGA_BLANK_n = !( hcount[10] & (hcount[9] | hcount[8]) ) &
			!( vcount[9] | (vcount[8:5] == 4'b1111) );

   /* VGA_CLK is 25 MHz
    *             __    __    __
    * clk50    __|  |__|  |__|
    *        
    *             _____       __
    * hcount[0]__|     |_____|
    */
   assign VGA_CLK = hcount[0]; // 25 MHz clock: rising edge sensitive
   
endmodule
