/*
 * game.v
 * 
 * Snake game engine - FPGA class project
 * 
 * 
 * Authors: Dario De Micco - Marta Martelossi
 * Contact: <email???>
 * Release date: oct 2013
 * 
 * References: "Tetris" class project
 *
 * 
 * This program is free software; you can redistribute it and/or modify
 * 
 * it under the terms of the GNU General Public License as published by
 * 
 * the Free Software Foundation; either version 2 of the License, or
 * 
 * (at your option) any later version.
 * 
 * 
 * 
 * This program is distributed in the hope that it will be useful,
 * 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * 
 * GNU General Public License for more details.
 * 
 * 
 * 
 * You should have received a copy of the GNU General Public License
 * 
 * along with this program; if not, write to the Free Software
 * 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * 
 * MA 02110-1301, USA.
 * 
 */

// module: game engine

module gameblock(

	sram_address, sram_data, sram_writereq, sram_writeack,
	clock, reset,
	kb_scancode,ps2strobe,
	bufselect,
	ost_play, sfx_play, sfx_idx,
	clock_1hz, clock_5hz, fast_clock,
	pushbuttons, externalpause,
	KEY,
	gameover,
	audioready,
	ocurrent_level,
	ofruit_count,
	// icurrent_level
	);



// game parameters
parameter	BKGND_COL			=	4'h0;			// background color
parameter	SNAKE_COL			=	4'h2;			// snake color
parameter	WALL_COL				=	4'hF;			// wall color
parameter	XPIECES 				=	64;			// gameboard x size in blks
parameter	YPIECES 				=	64;			// gameboard y size in blks
parameter	DEFX 					=	4;				// start x in blks
parameter	DEFY 					=	4;				// start y in blks
parameter	Xresolution			=	640;			// x res
parameter	Yresolution			=	480;			// y res
parameter	Xoffset 				=	20;			// gameboard x start
parameter	Yoffset 				=	20;			// gameboard y start
parameter 	blocksize			= 	5;				// block size in px
parameter	Xnpoffset 			=	520;			// nextpiece box x start
parameter	Ynpoffset 			=	380;			// nextpiece box y start
parameter	matrixsize 			=	1;				// piecematrix size in blocks
parameter	blockgap				=	1;				// distance between each block
parameter	bufferoffset		= 76800;			// start of next framebuffer (fb lenght)
parameter	PIECESIZE			= 2;				// bits to define a piece type (3 types: empty, hard, fruit)
parameter	KEYBUFFERSIZE		= 2;				// max commands being buffered

	
input clock, reset;

output reg [15:0] sram_data;
output reg [17:0] sram_address;

reg [3:0] punti[5];

input [3:0] KEY;

input 	clock_1hz, clock_5hz, fast_clock;
input pushbuttons, externalpause;

output reg sram_writereq;
input sram_writeack;

output reg bufselect;
output [2:0] ofruit_count;

wire rndclock;
assign rndclock = fast_clock;

/* sfxs - index: desc #samples
0: mangia 33024
1: levelup 35432
2: morte 262265
*/
output reg [1:0]	sfx_idx;
output reg sfx_play;
output ost_play;

input audioready;

output reg gameover;

output [2:0] ocurrent_level;

wire	gametick;

assign ofruit_count = fruit_count;

reg [15:0] status;
reg [9:0] Xcount;
reg [8:0] Ycount;

reg [9:0] Xblock, Yblock;

reg [7:0] Xgrid;
reg [7:0] Ygrid;

reg was_fruit;

reg [1:0] Xramaddress;

input [7:0] kb_scancode;
input ps2strobe;
wire kb_strobe_ps2;

wire kb_strobe;


reg [2:0]	d_minuti;
reg [3:0]	u_minuti;
reg [2:0]	d_secondi;
reg [3:0]	u_secondi;


reg [2:0]	currpiecetype;
wire [2:0]	currfruittype;
reg [2:0]	nextpiecetype;
reg [2:0]	nextfruittype;
wire [2:0]	randomoutput;
wire [5:0]	randomtrigger;
// reg	[2:0]	nextfruittype;

reg [2:0] fruit_count;

wire [3:0]	nextpiecedata;
wire [3:0]	currpiecedata;
reg [PIECESIZE-1:0]	currpiece[matrixsize][matrixsize];


// game tick genereator
gtg			tickgen	(.in(clock), .out(gametick), .score(currentscore),.reset(reset));

// plays music when game is running
assign ost_play = timerenabled;


// these flags controls drawing inside the boxes
wire insideblock, insideX, insideY;
wire insidenpblock, insidenpX, insidenpY;

assign insideX = 	( (Xcount >= (4+blockgap+Xoffset+Xgrid*(blocksize+blockgap))) && (Xcount < (4+blockgap+Xoffset+Xgrid*(blocksize+blockgap)+blocksize)) &&
						  (Xcount <  (4+blockgap+Xoffset+XPIECES*(blocksize+blockgap))) ) ? 1'b1:1'b0;
assign insideY =  ( (Ycount >= (4+blockgap+Yoffset+Ygrid*(blocksize+blockgap))) && (Ycount < (4+blockgap+Yoffset+Ygrid*(blocksize+blockgap)+blocksize)) && 
						  (Ycount <  (4+blockgap+Yoffset+YPIECES*(blocksize+blockgap))) ) ? 1'b1:1'b0;
assign insideblock =  insideX & insideY;

assign insidenpX = 	( (Xcount >= (4+blockgap+Xnpoffset+Xgrid*(blocksize+blockgap))) && (Xcount < (4+blockgap+Xnpoffset+Xgrid*(blocksize+blockgap)+blocksize)) &&
						  (Xcount <  (4+blockgap+Xnpoffset+matrixsize*(blocksize+blockgap))) ) ? 1'b1:1'b0;
assign insidenpY =  ( (Ycount >= (4+blockgap+Ynpoffset+Ygrid*(blocksize+blockgap))) && (Ycount < (4+blockgap+Ynpoffset+Ygrid*(blocksize+blockgap)+blocksize)) && 
						  (Ycount <  (4+blockgap+Ynpoffset+matrixsize*(blocksize+blockgap))) ) ? 1'b1:1'b0;
assign insidenpblock =  insidenpX & insidenpY;


// randomize "timer": random generators... 
wire [5:0] fruitX, fruitY;
reg [5:0] new_fruitX, new_fruitY;
reg generate_coords;

// next fruit type
rndgen		nextft	(.clock(rndclock), .reset(reset|((gametick==0)?1'b1:1'b0)), .seed(myrand[2:0]),.out(randomoutput));

// when to add fruit
rndgen6		fttrigger(.clock(rndclock), .reset(reset|generate_coords), .seed(myrand[8:3]),.out(randomtrigger));
// new fruit coords
rndgen6		nextftpX (.clock(rndclock), .reset(reset|generate_coords), .seed(myrand[13:8]),.out(fruitX));
rndgen6		nextftpY (.clock(rndclock), .reset(reset|generate_coords), .seed(myrand[10:5]),.out(fruitY));

// roms for fruit texture loading
piecesrom	nextprom	(.address(Xblock+(Yblock*blocksize)+(blocksize*blocksize)*nextfruittype),.clock(clock),.q(nextpiecedata));
piecesrom	currprom	(.address(Xblock+(Yblock*blocksize)+(blocksize*blocksize)*currfruittype),.clock(clock),.q(currpiecedata));


// rom for levels maps
reg [14:0] levelmap_address;
wire levelmap_data;
levelmaps_rom	levelmap	(.address(levelmap_address),.clock(clock),.q(levelmap_data));


// gameboard, port A is VGA reading, port B is game I/O
gameboard	board0	(
							.clock(clock),
							.address_a(gb_videoaddress),
							.data_a(15'd0),
							.wren_a(1'b0),
							.q_a(gb_videodata),
							.address_b(gb_gameaddress),
							.data_b(gb_inputdata),
							.wren_b(gb_wren),
							.q_b(gb_outputdata));

						
reg	gb_wren;
wire	[14:0]	gb_videodata;
wire	[14:0]	gb_outputdata;
reg	[14:0]	gb_inputdata;

			
reg [12:0]	gb_gameaddress; 
wire [12:0] gb_videoaddress;			
reg [15:0]	gameboardtemp;
reg [15:0]	nextpiecetemp;
wire [3:0]	gameboard_predata;

reg has_pixelset;



// gameboard data managing
//  
wire [3:0] correct_cpdata, correct_npdata;
assign correct_cpdata = (currpiecedata==4'hF)?(BKGND_COL):currpiecedata; // fruit has white background !!!!
assign correct_npdata = (nextpiecedata==4'hF)?(BKGND_COL):nextpiecedata; // fruit has white background !!!!
assign gameboard_predata = (gb_videodata[1:0] == 2'b10)?correct_cpdata:((gb_videodata[1:0] == 2'b01)?SNAKE_COL:((gb_videodata[1:0] == 2'b11)?WALL_COL:(BKGND_COL)));
// debug
//assign gameboard_predata = SNAKE_COL; 

// extract from videodata fruit type
assign currfruittype = (gb_videodata[1:0] == 2'b10)?gb_videodata[7:5]:3'd0;
// reading of gameboard to write framebuffer
assign gb_videoaddress = (Xgrid+matrixsize)+(Ygrid+matrixsize)*(XPIECES+13'd2*matrixsize);


// snake position
reg [12:0]	snake_lenght;
reg [12:0]	snake_start, snake_end;
reg [9:0] snake_startX, snake_startY;
reg [9:0] snake_endX, snake_endY;

reg [2:0] scan_count;
reg [2:0] remove_idx, add_idx, autoremove_idx;


// fruit control
reg [15:0]	fruit_array[8];

reg add_fruit_req, add_fruit_ack;
reg remove_fruit_req, remove_fruit_ack; 


// game management and others


// 0 right,1 down, 2 left, 3 up
reg [1:0]	direction;	
reg [15:0] strech_counter;
reg is_fruit;
reg change_level_ack,change_level_req;

reg [15:0] currentscore;

reg preload;

// OSD system
reg [2:0] rtemp, btemp;
wire [3:0]	scoredigits[6];

wire [3:0] currenttimedigitvalue, currentscoredigitvalue;

wire [15:0]	numberdata, scromdata, etromdata, npromdata, timedata, scoredata;
wire [3:0]	numberromdata, scromromdata, etromromdata, npromromdata, timeromdata, scoreromdata;

wire [9:0]  scoreaddress, timeaddress,  scaddress,  etaddress, npaddress, numberXtime, numberYtime, numberXscore, numberYscore;

wire [1:0]	timedigitindex;
wire [2:0]	scoredigitindex;

// digits are 10x20
assign currenttimedigitvalue = (timedigitindex==3)?u_secondi:((timedigitindex==2)?{1'b0,d_secondi}:((timedigitindex==1)?u_minuti:({1'b0,d_minuti})));
assign currentscoredigitvalue = scoredigits[5-scoredigitindex];

assign timedigitindex = (Xcount >= 568)?2'd3:((Xcount >= 548)?2'd2:((Xcount >= 528)?2'd1:(2'd0)));
assign scoredigitindex = (Xcount >= 568)?3'd5:((Xcount >= 548)?3'd4:((Xcount >= 528)?3'd3:((Xcount >= 508)?3'd2:((Xcount >= 488)?3'd1:(3'd0)))));

assign numberXtime = Xcount - 508 - (20*timedigitindex);
assign numberXscore = Xcount - 468 - (20*scoredigitindex);
assign numberYtime = Ycount - 90; 
assign numberYscore = Ycount - 190;

// create score digit array filled with external module. cready score digit index. map them.

assign timeaddress = (currenttimedigitvalue * (20*12) + numberXtime + (12*numberYtime))/4;

assign scoreaddress = (currentscoredigitvalue * (20*12) + numberXscore + (12*numberYscore))/4;

assign scaddress=((Xcount-428)+(192*(Ycount-150)))/4;
assign etaddress=((Xcount-428)+(192*(Ycount-50)))/4;
assign npaddress=((Xcount-428)+(192*(Ycount-350)))/4;

bcdexpander	scexp (.in(currentscore), .out({scoredigits[5],scoredigits[4],scoredigits[3],scoredigits[2],scoredigits[1],scoredigits[0]}));

numbersrom 	trom	(.address(timeaddress),.clock(clock),.q(timeromdata));
numbersrom 	srom	(.address(scoreaddress),.clock(clock),.q(scoreromdata));
textrom0		scrom	(.address(scaddress),.clock(clock),.q(scromromdata));
textrom1		etrom	(.address(etaddress),.clock(clock),.q(etromromdata));
textrom2		nprom	(.address(npaddress),.clock(clock),.q(npromromdata));

romtopalette	tpal	(.in(timeromdata),.out(timedata), .forecolor(4'hF),.backcolor(BKGND_COL));
romtopalette	spal	(.in(scoreromdata),.out(scoredata), .forecolor((gameover==1'b1)?4'h9:((preload)?4'h2:4'hF)),.backcolor(BKGND_COL));
romtopalette	scpal	(.in(~scromromdata),.out(scromdata), .forecolor(4'hF),.backcolor(BKGND_COL));
romtopalette	etpal	(.in(~etromromdata),.out(etromdata), .forecolor(4'hF),.backcolor(BKGND_COL));
romtopalette	nppal	(.in(~npromromdata),.out(npromdata), .forecolor(4'hF),.backcolor(BKGND_COL));

// FSM0: for graphic updates (fills framebuffer in SRAM)
//
//	by default uses framebuffer0, framebuffer1 is currently unused
// 	
always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		// clear all
		sram_address<=18'd0;
		status<=16'd0;
		Xcount<=10'd0;
		Ycount<=9'd0;
		bufselect<=1'b0;
		refresh_ack <= 1'b0;
		Xramaddress<=2'd0;
	end else begin
		case (status)
			16'd0:
				begin
					// clears entire framebuffer 0
					bufselect<=1'b0;
					if (sram_address != (bufferoffset -1)) begin
						if (sram_writeack==1'b0)  begin
							status<=16'd10;
							sram_address<=sram_address+18'd1;
							sram_writereq<=1'b1;
							sram_data <= {BKGND_COL,BKGND_COL,BKGND_COL,BKGND_COL};

						end else begin
							sram_writereq<=1'b0;
						end
					end else begin
						Xcount <=10'd0;
						Ycount <=9'd0;
						sram_address<=((bufselect)?bufferoffset:18'd0); // start from upper left corner
						status <= 16'd20; // done
					end
				end
			16'd10:
				begin
					if (sram_writeack) begin
						sram_writereq<=1'b0;
						status<=16'd0;
					end
					
				end		
			16'd20:
				begin
					// scans entire framebuffer area, if inside "areas of interest", fills them
					if ((sram_address-((bufselect)?bufferoffset:0)) != (bufferoffset -1)) begin
						if (sram_writeack==1'b0)  begin
							status<=16'd30;
							sram_address<=sram_address+18'd1;
							sram_writereq<=1'b1;
							if (((((Ycount >= Yoffset )&&(Ycount < Yoffset+4)) || ((Ycount >= 4+blockgap+Yoffset+YPIECES*(blocksize+blockgap))&&(Ycount < 4+4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)))) && (Xcount >= Xoffset) && (Xcount <= (blockgap+Xoffset+XPIECES*(blocksize+blockgap)))) || ((Ycount > Yoffset) && (Ycount < 4+4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && ((Xcount == Xoffset) || (Xcount == (4+blockgap+Xoffset+XPIECES*(blocksize+blockgap))))))
								sram_data <= {WALL_COL,WALL_COL,WALL_COL,WALL_COL}; // draw gameboard border
							else if ( (Ycount >= Yoffset+4) && (Ycount < 4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && (Xcount == (4+Xoffset+XPIECES*(blocksize+blockgap))) )
								sram_data <= {WALL_COL,WALL_COL,WALL_COL,4'd0}; // draw gameboard border (right)
							else if ( (Ycount >= Yoffset+4) && (Ycount < 4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && (Xcount == (4+4+Xoffset+XPIECES*(blocksize+blockgap))) )
								sram_data <= {12'd0,WALL_COL}; // draw gameboard border (right)
							else if ( (Ycount >= Yoffset) && (Ycount < 4+Yoffset) && (Xcount == (4+Xoffset+XPIECES*(blocksize+blockgap))) )
								sram_data <= {WALL_COL,WALL_COL,WALL_COL,WALL_COL}; // draw gameboard border (right)
							else if ( (Ycount >= Yoffset) && (Ycount < 4+Yoffset) && (Xcount == (4+4+Xoffset+XPIECES*(blocksize+blockgap))) )
								sram_data <= {12'd0,WALL_COL}; // draw gameboard border (right)
							else if ( (Ycount >= 4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && (Ycount < 4+4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && (Xcount == (4+Xoffset+XPIECES*(blocksize+blockgap))) )
								sram_data <= {WALL_COL,WALL_COL,WALL_COL,WALL_COL}; // draw gameboard border (right)
							else if ( (Ycount >= 4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && (Ycount < 4+4+blockgap+Yoffset+YPIECES*(blocksize+blockgap)) && (Xcount == (4+4+Xoffset+XPIECES*(blocksize+blockgap))) )
								sram_data <= {12'd0,WALL_COL}; // draw gameboard border (right)
								
							else if (((((Ycount >= Ynpoffset)&&(Ycount < Ynpoffset+4)) || ((Ycount >= Ynpoffset+4+blockgap+matrixsize*(blocksize+blockgap))&&(Ycount < Ynpoffset+4+4+blockgap+matrixsize*(blocksize+blockgap)))) && (Xcount >= Xnpoffset) && (Xcount <= (4+blockgap+Xnpoffset+matrixsize*(blocksize+blockgap)))) || ((Ycount >= Ynpoffset) && (Ycount < 4+4+blockgap+Ynpoffset+matrixsize*(blocksize+blockgap)) && ((Xcount == Xnpoffset) || (Xcount == (4+2+Xnpoffset+matrixsize*(blocksize+blockgap))))))
								sram_data <= {16'hFFFF}; // draw next piece border
														
							else if ((Ycount >= 9'd50)&&(Ycount < 70)&& (Xcount >= 10'd428) && (Xcount < 10'd620))
								sram_data <= etromdata; // draw text for time00
							else if ((Ycount >= 9'd109)&&(Ycount < 111)&& (Xcount >= 10'd540) && (Xcount < 10'd542))
								sram_data <= 16'h00FF; // draw dots time	
							else if ((Ycount >= 9'd150)&&(Ycount < 170)&& (Xcount >= 10'd428) && (Xcount < 10'd620))
								sram_data <= scromdata; // draw text for points
							else if ((Ycount >= 9'd350)&&(Ycount < 370)&& (Xcount >= 10'd428) && (Xcount < 10'd620))
								sram_data <= npromdata; // draw text for next piece
							
							else
								sram_data <= {BKGND_COL,BKGND_COL,BKGND_COL,BKGND_COL};	// not inside anything special...
							
							if (Xcount != (Xresolution-4))
								Xcount<=Xcount+10'd4;
							else begin
								Xcount <= 0;
								if (Ycount != (Yresolution -1 )) Ycount <= Ycount+9'd1;
								
							end

						
						end else begin
							sram_writereq<=1'b0;
						end
					end else begin
						Xcount <= 10'd0;
						Ycount <= 9'd0;
						sram_address<=((bufselect)?bufferoffset:18'd0); // start again from 0
						status <= 16'd40;	// done
					end
					
				end
			16'd30:
				begin
					if (sram_writeack) begin
						sram_writereq<=1'b0;
						status<=16'd20;
					end
					
				end
			16'd40:
				begin
					// fist step of "standard" refresh
					//   starts from first thing to update (time)
					Xcount <= 10'd508;
					Ycount <= 9'd90;
					sram_address <= ((bufselect)?bufferoffset:18'd0)+((18'd508+18'd90*Xresolution)/18'd4); 
					status<=16'd50;
					refresh_ack <= 1'b0;
					
				end
			16'd50:
				begin
					// draw time(rectangle 488-600 90-110)
					if (Ycount != 110) begin
							if ((Xcount >= 10'd508) && (Xcount < 10'd520)) begin// 1st digit 
								status<=16'd60;
							end else if ((Xcount >= 10'd528) && (Xcount < 10'd540)) begin // 2nd digit
								status<=16'd60;
							end else if ((Xcount >= 10'd548) && (Xcount < 10'd560)) begin // 3rd digit
								status<=16'd60;
							end else if ((Xcount >= 10'd568) && (Xcount < 10'd580)) begin // 4th digit
								status<=16'd60;
							end else begin
								
								sram_writereq<=1'b0;
								sram_address<=sram_address+18'd1;
								if (Xcount != (Xresolution - 4))
									Xcount<=Xcount+10'd4;
								else begin
									Xcount <= 0;
									if (Ycount != (Yresolution -1 )) Ycount <= Ycount+9'd1;
									
								end
							end
						
					end else begin
						Xcount <= 10'd428;
						Ycount <= 9'd190;
						sram_address <= ((bufselect)?bufferoffset:18'd0)+(18'd428+18'd190*Xresolution)/18'd4;
						status <= 16'd90; // done...
					end
					
				end
			16'd60:
				begin
					status <= 16'd70;
				end
			16'd70:
				begin
					if (sram_writeack==1'b0)  begin
						
						sram_data <= timedata;
						sram_writereq<=1'b1;
						status <= 16'd80;
					end else begin
						sram_writereq<=1'b0;
					end
					
				end
			16'd80:
				begin
					if (sram_writeack) begin
						sram_writereq<=1'b0;
						
						sram_address<=sram_address+18'd1;
						if (Xcount != (Xresolution -4 ))
							Xcount<=Xcount+10'd4;
						else begin
							Xcount <= 10'd0;
							if (Ycount != (Yresolution -1)) Ycount <= Ycount+9'd1;
							
						end
						status<=16'd50;
					end
				end
			16'd90:
				begin
					// draw points (rectangle 408-600 190-210)
					if (Ycount != 210) begin
							if ((Xcount >= 10'd468) && (Xcount < 10'd480)) begin// 1st digit 
								status<=16'd100;
							end else if ((Xcount >= 10'd488) && (Xcount < 10'd500)) begin// 2nd digit 
								status<=16'd100;
							end else if ((Xcount >= 10'd508) && (Xcount < 10'd520)) begin// 3rd digit 
								status<=16'd100;
							end else if ((Xcount >= 10'd528) && (Xcount < 10'd540)) begin // 4th digit
								status<=16'd100;
							end else if ((Xcount >= 10'd548) && (Xcount < 10'd560)) begin // 5th digit
								status<=16'd100;
							end else if ((Xcount >= 10'd568) && (Xcount < 10'd580)) begin // 6th digit
								status<=16'd100;
							end else begin
								sram_writereq<=1'b0;
								sram_address<=sram_address+18'd1;
								if (Xcount != (Xresolution -4))
									Xcount<=Xcount+10'd4;
								else begin
									Xcount <= 10'd0;
									if (Ycount != (Yresolution -1 )) Ycount <= Ycount+9'd1;
									
								end
							end
		
					end else begin
						status <= 16'd130;
						
					end
					
				end
			16'd100:
				begin
					status <= 16'd110;
				end
			16'd110:
				begin
					if (sram_writeack==1'b0)  begin	
						sram_data <= scoredata;
						sram_writereq<=1'b1;
						status <= 16'd120;
					end else begin
						sram_writereq<=1'b0;
					end
				end								
			16'd120:
				begin
					if (sram_writeack) begin
						sram_writereq<=1'b0;
						status<=16'd90;
						sram_address<=sram_address+18'd1;
						if (Xcount != (Xresolution -4 ))
							Xcount<=Xcount+10'd4;
						else begin
							Xcount <= 10'd0;
							if (Ycount != (Yresolution -1 )) Ycount <= Ycount+9'd1;
							
						end
					end
					
				end
				
			16'd130:
				begin
					// prepare for gameboard area drawing (starts @ Yoffset)
					Xcount <= 10'd0;
					Ycount <= Yoffset;
					sram_address <= ((bufselect)?bufferoffset:18'd0)+(Yoffset*Xresolution)/18'd4;
					status<=16'd131;
					Xgrid <= 8'd0;
					Ygrid <= 8'd0;
					Xblock <= 10'd0;
					Yblock <= 10'd0;
					has_pixelset<=1'b0;
					Xramaddress<=2'd0;
				end
			16'd131:
				// wait state for read data to be ready
				begin
					status<=16'd132;
				end
			16'd132:
				// wait state for read data to be ready
				begin
					status<=16'd140;
				end
			16'd140:
				begin
					// redraw entire gameboard: modified (from Tetris) to preload 4 pixels
					if ((sram_address-((bufselect)?bufferoffset:18'd0)) < (((Xresolution - Xoffset) +(Yresolution - 2*Yoffset)*Xresolution)/4)) begin
						if (sram_writeack==1'b0)  begin
							// use counter (2bits) to address correct nibble of 16bit word
							case (Xramaddress)
								2'b00:
									begin
										// insideblock indicates if current position (Xcount,Ycount) is inside a block
										if (insideblock) begin
											has_pixelset<=1'b1;	// if in gameboardtemp (16bit) we have something different from BKGND_COL
											// gameboardpredata contains info about current block
											gameboardtemp<={BKGND_COL,BKGND_COL,BKGND_COL,gameboard_predata};
										end else begin
											has_pixelset<=1'b0;
											gameboardtemp<={BKGND_COL,BKGND_COL,BKGND_COL,BKGND_COL};
										end	
										
									end
								2'b01:
									begin
										if (insideblock) begin
											has_pixelset<=1'b1;
											gameboardtemp[7:4]<=gameboard_predata;
										end
										
									end
								2'b10:
									begin
										if (insideblock) begin
											has_pixelset<=1'b1;
											gameboardtemp[11:8]<=gameboard_predata;
										end
										
									end
								2'b11:
									begin
										if (insideblock) begin
											has_pixelset<=1'b1;
											gameboardtemp[15:12]<=gameboard_predata;
										end
										
										sram_address<=sram_address+18'd1;
										if (has_pixelset | insideblock) // 16bit of gameboardtemp is different from BKGND_COL,BKGND_COL,BKGND_COL,BKGND_COL
											status <= 16'd180; // got all 4 pixels
										
									end
							endcase
							
							if (Xcount != (Xresolution -1) ) begin
								Xcount<=Xcount+10'd1;
								Xramaddress<=Xramaddress+2'd1;
								if (~((has_pixelset | insideblock)&((Xramaddress==2'b11)?1'b1:1'b0)))
									status<=16'd131;
											
								if (insideblock) begin
									if (Xblock != (blocksize-1))
										Xblock <= Xblock + 10'd1;
									else begin
										Xblock <= 10'd0;
										Xgrid <= Xgrid + 8'd1;
										
										if (~((has_pixelset | insideblock)&((Xramaddress==2'b11)?1'b1:1'b0)))
											status<=16'd131;
									end
										
								end else begin
									sram_writereq<=1'b0;
								end
								
							end else begin
								Xcount <= 10'd0;
								Xramaddress<=2'd0;
								if (Ycount != (Yresolution-1)) Ycount <= Ycount+9'd1;
								Xgrid <= 8'd0;
								
								if (~((has_pixelset | insideblock)&((Xramaddress==2'b11)?1'b1:1'b0)))
									status<=16'd131;
									
								if (insideY) begin
									if (Yblock != blocksize-1) begin
										Yblock <= Yblock + 10'd1;
									end else begin
										Yblock <= 10'd0;
										Ygrid <= Ygrid+8'd1;
		
									end
								end
							end

						
						end else begin
							sram_writereq<=1'b0;
						end
					end else begin
						// next task
						status <= 16'd200;
					end
				end
			16'd180:
				begin
					// got all 4 pixels: write them to framebuffer in sram
					sram_data <= gameboardtemp;
					sram_writereq<=1'b1;
					status<=16'd190;
				end
			16'd190:
				begin
					if (sram_writeack) begin
						sram_writereq<=1'b0;
						status<=16'd131;
					end
					
				end
			16'd200:
				begin
					// next task: draw next piece
					status<=16'd230;
				end
			16'd230:
				begin
					// prepare to preload next piece mask
					Xcount <= 10'd0;
					Ycount <= Ynpoffset;
					sram_address <= ((bufselect)?bufferoffset:18'd0)+(Ynpoffset*Xresolution)/18'd4;
					status<=16'd231;
					Xgrid <= 8'd0;
					Ygrid <= 8'd0;
					Xblock <= 10'd0;
					Yblock <= 10'd0;
					has_pixelset<=1'b0;
					Xramaddress<=1'b0;
				end
			16'd231:
				// wait state for read data to be ready
				begin
					status<=16'd232;
				end
			16'd232:
				// wait state for read data to be ready
				begin
					status<=16'd240;
				end
			16'd240:
				begin
					// redraw nextpiece mask
					if ((sram_address-((bufselect)?bufferoffset:18'd0)) < (((Xresolution - Xoffset) +(Yresolution - Yoffset)*Xresolution)/4)) begin
						if (sram_writeack==1'b0)  begin
							// use counter (2bits) to address correct nibble of 16bit word
							case (Xramaddress)
								2'b00:
									begin
										if (insidenpblock) begin
											has_pixelset<=1'b1;
											nextpiecetemp<={BKGND_COL,BKGND_COL,BKGND_COL,correct_npdata};
										end else begin
											has_pixelset<=1'b0;
											nextpiecetemp<={BKGND_COL,BKGND_COL,BKGND_COL,BKGND_COL};
										end	
										
									end
								2'b01:
									begin
										if (insidenpblock) begin
											has_pixelset<=1'b1;
											nextpiecetemp[7:4]<=correct_npdata;
										end
										
									end
								2'b10:
									begin
										if (insidenpblock) begin
											has_pixelset<=1;
											nextpiecetemp[11:8]<=correct_npdata;
										end
										
									end
								2'b11:
									begin
										if (insidenpblock) begin
											has_pixelset<=1;
											nextpiecetemp[15:12]<=correct_npdata;
										end
										
										sram_address<=sram_address+18'd1;
										if (has_pixelset | insidenpblock)
											status <= 16'd280; // got all 4 pixels
										
									end
							endcase
							
							if (Xcount != (Xresolution -1) ) begin
								Xcount<=Xcount+10'd1;
								Xramaddress<=Xramaddress+2'd1;
								if (~((has_pixelset | insidenpblock)&((Xramaddress==2'b11)?1'b1:1'b0)))
									status<=16'd231;
											
								if (insidenpblock) begin
									if (Xblock != (blocksize-1))
										Xblock <= Xblock + 10'd1;
									else begin
										Xblock <= 0;
										Xgrid <= Xgrid + 8'd1;
										
										if (~((has_pixelset | insidenpblock)&((Xramaddress==2'b11)?1'b1:1'b0)))
											status<=16'd231;
									end
										
								end else begin
									sram_writereq<=1'b0;
								end
								
							end else begin
								Xcount <= 10'd0;
								Xramaddress<=2'd0;
								if (Ycount != (Yresolution-1)) Ycount <= Ycount+9'd1;
								Xgrid <= 8'd0;
								
								if (~((has_pixelset | insidenpblock)&((Xramaddress==2'b11)?1'b1:1'b0)))
									status<=16'd231;
									
								if (insidenpY) begin
									if (Yblock != blocksize-1) begin
										Yblock <= Yblock +10'd1;
									end else begin
										Yblock <= 10'd0;
										Ygrid <= Ygrid+8'd1;
		
									end
								end
							end

						
						end else begin
							sram_writereq<=1'b0;
						end
					end else begin
						// next task
						status <= 16'd300;
					end
				end			
			16'd280:
				begin
					// got all 4 pixels: time to write them to sram
					sram_data <= nextpiecetemp;
					sram_writereq<=1'b1;
					status<=16'd290;
				end
			16'd290:
				begin
					if (sram_writeack) begin
						sram_writereq<=1'b0;
						status<=16'd231;
					end
					
				end			
			16'd300:
				begin
					// idle waiting to refresh it again...
					if (refresh_req) begin
						status <= 16'd40;
						refresh_ack <= 1'b1;
					end
				end				
			
		endcase
		
	end
	
end


// update graphics  on request (graphicupdate) or on timer_1hz (for time and similar)

reg graphicupdate, refresh_reqA, refresh_reqB, refresh_ack;
wire mainrefreshreset;
wire refresh_req2, refresh_req;
assign refresh_req = refresh_reqA|refresh_reqB;
assign mainrefreshreset=reset|refresh_ack;

always @ (posedge clock_1hz or posedge mainrefreshreset) 
begin
	if (mainrefreshreset==1'b1) begin
		refresh_reqA <= 1'b0;
		
	end else begin
		
		refresh_reqA <= 1'b1;
			
		
	end

end

always @ (posedge graphicupdate or posedge mainrefreshreset) 
begin
	if (mainrefreshreset==1'b1) begin
		refresh_reqB <= 1'b0;
		
	end else begin
		
		refresh_reqB <= 1'b1;
			
		
	end

end


// trigger "new input event" for game: only gametick, no keypress

wire validkey;
reg keypressed, refresh_req2A, latchdir_req, refresh_ack2, latchdir_ack;
wire refreshreset, refreshresetA, refreshresetB;
wire refreshtrigger, latchdir_reset;
assign refreshresetA = reset | refresh_ack2;
assign latchdir_reset = reset | latchdir_ack;
assign refresh_req2 = refresh_req2A; // input event won't trigger anymore the graphic update

always @ (posedge gametick  or posedge refreshresetA)
begin
	if (refreshresetA==1'b1) begin
		refresh_req2A <= 1'b0;
		
	end else begin
		
		if (timerenabled==1'b1) begin
			refresh_req2A <= 1'b1;
			
		end else begin
			refresh_req2A <= 1'b0;
			
		end
	
	end

end

// if valid key pressed: buffer it. if buffer not empty, update direction register
//KEYBUFFERSIZE keyprocessed<=1'b1;
reg [3:0] pbkey_buf[KEYBUFFERSIZE];
reg [7:0] latkey_buf[KEYBUFFERSIZE];

wire [3:0] pbkey_bout, pbkey_idx;
wire [7:0] latkey_bout, latkey_idx;

reg [15:0] kbufcount;
reg [15:0] keybufferFSMstatus;

reg [7:0] shift_count;

wire [15:0] kbufidx;

wire idxdown, idxleft, idxright, idxup;

assign kbufidx = kbufcount -1 ;

integer kbufindex;

assign idxdown = (pushbuttons==1'b1)?pbkey_idx[2]:latkey_idx[1];
assign idxleft = (pushbuttons==1'b1)?pbkey_idx[3]:latkey_idx[2];
assign idxright = (pushbuttons==1'b1)?pbkey_idx[0]:latkey_idx[3];
assign idxup = (pushbuttons==1'b1)?pbkey_idx[1]:latkey_idx[0]; 

assign down = (pushbuttons==1'b1)?pbkey_bout[2]:latkey_bout[1];
assign left = (pushbuttons==1'b1)?pbkey_bout[3]:latkey_bout[2];
assign right = (pushbuttons==1'b1)?pbkey_bout[0]:latkey_bout[3];
assign up = (pushbuttons==1'b1)?pbkey_bout[1]:latkey_bout[0];  // up
//assign up = key[6];
//assign up = key[7];

assign pbkey_idx = pbkey_buf[kbufidx];
assign latkey_idx = latkey_buf[kbufidx];

assign pbkey_bout = pbkey_buf[0];
assign latkey_bout = latkey_buf[0];

// buffer for the keyboard
always @ (posedge clock  or posedge reset)
begin
	if (reset) begin
		latchdir_req <= 1'b0;
		keyprocessed<=1'b0;
		kbufcount<=16'd0;
		shift_count<=8'd0;
		
		keybufferFSMstatus<=16'd0;
		for (kbufindex = 0; kbufindex < KEYBUFFERSIZE; kbufindex = kbufindex +1) begin
			pbkey_buf[kbufindex]<=4'd0;
			latkey_buf[kbufindex]<=8'd0;
		end
	end else begin
		case (keybufferFSMstatus) 
			16'd0:
				begin
					// wait for key
					keyprocessed <= 1'b0;
					if (latchdir_ack) begin 
						if (kbufcount != 0) begin
							// clear one position
							kbufcount<=kbufcount-1;
							if (kbufcount > 1) begin
								// shift remaining
								keybufferFSMstatus<=16'd20;
								shift_count<=8'd0;
							end
						end
						latchdir_req <= 1'b0;
					
					end else if (keypressed) begin
						if (timerenabled==1'b1) begin
							if (kbufcount == 0) begin
								// check not insisting.. 0 right,1 down, 2 left, 3 up
								if (validup && ((direction == 1) || (direction == 3))) begin
									keybufferFSMstatus<=16'd200;
								end else if (validdown && ((direction == 1) || (direction == 3))) begin
									keybufferFSMstatus<=16'd200;
								end else if (validleft && ((direction == 0) || (direction == 2))) begin
									keybufferFSMstatus<=16'd200;
								end else if (validright && ((direction == 0) || (direction == 2))) begin
									keybufferFSMstatus<=16'd200;
								end else begin 
									keybufferFSMstatus<=16'd10;
								end 
							end else begin 
								// not first command...
								// have to check the previous one...
								if (validup && (idxdown || idxup)) begin
									keybufferFSMstatus<=16'd200;
								end else if (validdown && (idxdown || idxup)) begin
									keybufferFSMstatus<=16'd200;
								end else if (validleft && (idxleft || idxright)) begin
									keybufferFSMstatus<=16'd200;
								end else if (validright && (idxleft || idxright)) begin
									keybufferFSMstatus<=16'd200;
								end else begin 
									keybufferFSMstatus<=16'd10;
								end 
							end
						end else begin
							keybufferFSMstatus<=16'd200;
						end
					end
				end
			16'd10:
				begin
					if (kbufcount < KEYBUFFERSIZE) begin
						// buffer has freespace: add
						kbufcount<=kbufcount+1;
						pbkey_buf[kbufcount] <= pbkey;
						latkey_buf[kbufcount] <= latkey;
						
						keybufferFSMstatus<=16'd210;
					end else begin
						// buffer full: discard
						keybufferFSMstatus<=16'd200;
					end
					//
				end
			16'd20:
				begin
					// shift
					if (shift_count < (KEYBUFFERSIZE -1)) begin
						pbkey_buf[shift_count]<=pbkey_buf[shift_count+1];
						latkey_buf[shift_count]<=latkey_buf[shift_count+1];
						shift_count<=shift_count+8'd1;
					end else begin
						// done: go processing other things buffered
						keybufferFSMstatus<=16'd210;
					end
				end
			
			16'd200:
				begin
					// send ack of key processed, but discard
					keyprocessed <= 1'b1;
					keybufferFSMstatus<=16'd0;
					//latchdir_req <= 1'b0;
				end
			16'd210:
				begin
					// send ack of key processed
					latchdir_req <= 1'b1;
					keyprocessed <= 1'b1;
					keybufferFSMstatus<=16'd0;
				end
			
		endcase
		
	end

end



// main block: map and snake controller
reg [15:0]	mainFSMstatus;

// current piece coords (upperleft, lowerright)
reg [15:0]	X1, X2, Xclean;
reg [15:0]	Y1, Y2, Yclean;

// nextpiecetype<=randomoutput;	// get new piece from random generator


wire left, right, up, down;
reg pausecontrol;

reg [3:0] oldseconds;

reg keyprocessed;

reg [3:0] droppedlines;

//input [2:0] icurrent_level;
integer fruitpreloadidx;

reg [7:0] current_level;

assign ocurrent_level=current_level[2:0];

integer x, y, xf, yf, xu, yu;

reg [9:0] try_count;

// FSM1: map manager (snake, fruit, etc)
//
//	
always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		// clears all
		mainFSMstatus<=16'd0;
		fruit_count<=3'd0;
		gb_gameaddress<=13'd0;
		gb_inputdata<=15'd0;
		direction <= 2'd0;
		remove_fruit_ack<=1'b0;
		add_fruit_ack<=1'b0;
		load_timer_req<=1'b0;
		currentscore<=16'd0;
		Xclean<=16'd0;
		Yclean<=16'd0;
		X1<=16'd0;
		X2<=16'd0;
		Y1<=16'd0;
		Y2<=16'd0;
		gameover<=1'b0;
		sfx_idx<=2'd0;
		sfx_play<=1'b0; 
		scan_count<=3'd0;
		preload<=1'b0;
		oldseconds<=4'd0;
		try_count<=10'd0;
		was_fruit<=1'b0;
	end else begin
		case (mainFSMstatus)
		16'd0:
			begin
				// full reset
				mainFSMstatus<=16'd10; 
				currentscore<=16'd0;
				sfx_idx<=2'd0;
				sfx_play<=1'b0; 
			end
		16'd10:
			begin
				// clear, loading level (score is preserverd)
				change_level_ack<=1'b1;	// pulse ack to signal a new levels is being loaded
				mainFSMstatus<=16'd20;
				
				// clears all vars
				Xclean<=16'd0;
				Yclean<=16'd0;
				X1<=16'd0;
				X2<=16'd0;
				Y1<=16'd0;
				Y2<=16'd0;
				preload<=1'b0;
				fruit_count<=3'd0;
				gameover<=1'b0;
				was_fruit<=1'b0;
				try_count<=10'd0;
				
				sfx_idx<=2'd1;
				sfx_play<=1'b0; // is level is being loaded, stop from replaying "levelup" sfx
				
			end
		16'd20:
			begin
				change_level_ack<=1'b0;
				
				// prepare to load new level from ROM
				gb_gameaddress<=13'd0;
				mainFSMstatus<=16'd21;
				
				// rom address @ current_level [0,0]
				levelmap_address<=current_level*(XPIECES*YPIECES);
				// DEBUG
				//levelmap_address<=15'd1*(XPIECES*YPIECES);
				
			end
		16'd21:
			begin
				// waitstate for memory address setup reqs
				mainFSMstatus<=16'd22;
			end
		16'd22:
			begin
				// start filling gameboard with 0 (there is a non visible area of "matrixsize" width outside the whole gameboard)
				gb_wren<=1'b1;
				//gb_inputdata<={15'd0,levelmap_data};
				gb_inputdata<=15'd0;
				mainFSMstatus<=16'd23;
			end
		16'd23:
			begin
				mainFSMstatus<=16'd30;
			end
		16'd30:
			begin
				// increment counters for gameboard indexing
				if (Xclean != XPIECES-1+2*matrixsize) begin
					Xclean<=Xclean+16'd1;
					mainFSMstatus<=16'd31;
				end else begin
					if (Yclean != YPIECES-1+2*matrixsize) begin
						Yclean<=Yclean+16'd1;
						Xclean<=16'd0;
						mainFSMstatus<=16'd31;
					end else begin
						// done!
						mainFSMstatus<=16'd100;
					end
				end
				// increments address too...
				gb_gameaddress<=gb_gameaddress+13'd1;
				
				// if in "visibile" area 
				if ((Yclean >= matrixsize) && (Yclean < YPIECES+matrixsize) && (Xclean >= matrixsize) && (Xclean < XPIECES+matrixsize))
				begin
					// increment level rom address
					levelmap_address<=levelmap_address+15'd1;
				end
				
			end
		16'd31:
			begin
				mainFSMstatus<=16'd32;
			end
		16'd32:
			begin
				// write data on gameboard
				gb_wren<=1'b1;
				// which data?
				if ((Yclean >= matrixsize) && (Yclean < YPIECES+matrixsize) && (Xclean >= matrixsize) && (Xclean < XPIECES+matrixsize))
				begin
					// in visible area -> level pattern
					if ( (Xclean == (DEFX+matrixsize)) && (Yclean == (DEFY+matrixsize)) ) begin
						// start area: preload snake head
						gb_inputdata<={13'd0,2'b01};
					end else begin
						// other area
						gb_inputdata<={13'd0,levelmap_data,levelmap_data};
					end
				end else begin
					// outside visible area -> blank
					gb_inputdata<=15'd0;
				end
				mainFSMstatus<=16'd33;
			end
		16'd33:
			begin
				gb_wren<=1'b0;
				mainFSMstatus<=16'd30;
				// keep going until end of gameboard has been reached
			end
		16'd100:
				begin
					// a new level has been loaded, preset game status
					gb_wren<=1'b0;
								
					X2<=16'd0;
					Y2<=16'd0;

					direction<=2'd0;
					
					// snake 1 pcs
					snake_lenght<=13'd1;
					strech_counter<=16'd0;
					
					// remove all fruit
					for (fruitpreloadidx = 0; fruitpreloadidx < 8; fruitpreloadidx = fruitpreloadidx +1) begin
						fruit_array[fruitpreloadidx]<=16'd0;
					end
					fruit_array[remove_idx]<=6'd0;	// remove the fruit
					fruit_count<=3'd0;
					was_fruit<=1'b0;
					try_count<=10'd0;
					// starting coords
					
					// these are aligned on visible area (XPIECES*YPIECES)
					snake_startX<=DEFX;
					snake_startY<=DEFY;
					snake_endX<=DEFX;
					snake_endY<=DEFY;
					
					// these are aligned on the gameboard array (with outside area)
					snake_start<=(matrixsize+DEFX)+(XPIECES+2*matrixsize)*(matrixsize+DEFY);
					snake_end<=(matrixsize+DEFX)+(XPIECES+2*matrixsize)*(matrixsize+DEFY);
					
					nextfruittype<=randomoutput;	// get new piece from random generator
					
					sfx_idx<=2'd0;
					
					mainFSMstatus<=16'd111;
				end
			16'd111:
				begin
					graphicupdate<=1'b1;		// trigger refresh of framebuffer with new data
					mainFSMstatus<=16'd112;
				end
			16'd112:
				begin
					graphicupdate<=1'b0;		// trigger refresh of framebuffer with new data
					preload<=1'b1;
					if (latchdir_req) begin
						latchdir_ack<=1'b1;
						
					end else if (add_fruit_req) begin
						add_fruit_ack<=1'b1;
					end else if (remove_fruit_req) begin
						remove_fruit_ack<=1'b1;
					end else begin
						remove_fruit_ack<=1'b0;
						add_fruit_ack<=1'b0;
						latchdir_ack<=1'b0;
						mainFSMstatus<=16'd113;
						oldseconds <= (u_secondi>6)?(u_secondi-6):(u_secondi+3);
					end
				end
			16'd113:
				begin
					// wait 2 secs
					
					if (u_secondi == oldseconds) begin
						preload <=1'b0;
						mainFSMstatus<=16'd119;
					end
					
				end 
			16'd119:
				begin
					// prepare to wait for an input event 
					graphicupdate<=1'b1;		// trigger refresh of framebuffer with new data
					mainFSMstatus<=16'd120;
				end
			
			16'd120:
				begin
					// wait for a tick and graphic refresh to be finished. 
					
					graphicupdate<=1'b0;	// screen has been refreshed. no need to do it again.
					
					if (refresh_req2 && status==300) begin		// graphics fsm idling (update completed) + gametick req
						// gametick
						refresh_ack2<=1'b1;
						
						// if a key was pressed, process it! 
						if (latchdir_req) begin
							// keystrobe
							latchdir_ack<=1'b1;
							
							// 0 right,1 down, 2 left, 3 up
							if (up && (direction != 1)) begin
								direction<=2'd3;
							end else if (down && (direction != 3)) begin
								direction<=2'd1;
							end else if (left && (direction != 0)) begin
								direction<=2'd2;
							end else if (right && (direction != 2)) begin
								direction<=2'd0;
							end 
						end
						
						// load coords
						X1<=snake_startX+matrixsize;
						Y1<=snake_startY+matrixsize;
						mainFSMstatus<=16'd125;
					end else begin
						if (change_level_req) begin
							sfx_idx <= 2'd1;	// 
							sfx_play <= 1'b0;	// force stop
							if (audioready) mainFSMstatus<=16'd123;	
						end
						
					end
					
					
				end
			16'd123:
				begin
					// change level request found...
					sfx_idx <= 2'd1;	// sfx: level up!
					sfx_play <= 1'b1;	// play it..
					mainFSMstatus<=16'd10;	// jump to "load new level"
				end
				
			16'd125:
				begin
					// calculate new head coordinates
					
					// direction change or inputevent have been processed.
					latchdir_ack<=1'b0;	
					refresh_ack2<=1'b0;
					
					// calculate X2,Y2 
					// 0 right,1 down, 2 left, 3 up
					case(direction)
						4'd0:
							begin
								X2<=X1+16'd1;
								Y2<=Y1;
							end
						4'd1:
							begin
								X2<=X1;
								Y2<=Y1+16'd1;
							end
						4'd2:
							begin
								X2<=X1-16'd1;
								Y2<=Y1;
							end
						4'd3:
							begin
								X2<=X1;
								Y2<=Y1-16'd1;
							end
					endcase
					mainFSMstatus<=16'd126;
				end
			16'd126:
				begin
					// check coordinates 
					if (	
						(X2 < matrixsize)|| (X2 >= XPIECES + matrixsize)||
						(Y2 < matrixsize)|| (Y2 >= YPIECES + matrixsize)	)
						begin
						// failure (outside game area)
							mainFSMstatus<=16'd1000;
					end else begin
						// OK, let's check what's inside current block
						gb_gameaddress<=X2+Y2*(XPIECES+13'd2*matrixsize);
						mainFSMstatus<=16'd127;
					end
				end
			16'd127:
				begin
					mainFSMstatus<=16'd128;
				end
			16'd128:
				begin
					mainFSMstatus<=16'd129;
				end
			16'd129:
				begin
					// scan for 2 LSb of gameboard output: type of block
					case (gb_outputdata[1:0])
						2'b00:
							begin 
								// empty
								is_fruit<=1'b0;
								// move head in this position
								mainFSMstatus<=16'd130;
							end
						2'b01:
							begin
								// snake: die
								mainFSMstatus<=16'd1000;
								
							end
						2'b10:
							begin
								// fruit
								sfx_idx <= 2'd0;	// play "eat" sfx
								sfx_play <= 1'b1;
								
								is_fruit<=1'b1;	// set fruit flag for current block
								
								// determine idx: bits 4,3,2 of gameboard output contains index (in fruit_array) of fruit in the current block
								fruit_index<= gb_outputdata[4:2];
								
								
								// move head in this position
								mainFSMstatus<=16'd130;
							end
						2'b11:
							begin
								// level hard block: die
								mainFSMstatus<=16'd1000;
							end
						
					endcase
				end
			
			16'd130:
				begin
					// stop playing any sound
					sfx_play <= 1'b0;
					
					if (is_fruit) begin
						// if fruit->get new features depending on fruit type (setting fruit_idx prepares this data)
						strech_counter<=strech_counter+fruit_extension; // sets stretch_counter to extend snake lenght
						currentscore<=currentscore+fruit_points*16'd5; // update points
					end
					
					
					// add new head!
					/*
					addhead (newindex): 
					list[startindex].nextvalue=newindex
					list[newindex].value=snake
					lenght++
					startindex=newindex
					*/
					
					// list[newindex].value=snake
					gb_gameaddress<=X2+Y2*(XPIECES+13'd2*matrixsize);
					
					
					// lenght++
					// startindex=newindex
					snake_lenght<=snake_lenght+13'd1;
					snake_startX<=X2;
					snake_startY<=Y2;
					mainFSMstatus<=16'd131;
					
					 
				end
			16'd131:
				begin
					gb_inputdata[1:0]<=2'b01;
					gb_wren<=1'b1;
					mainFSMstatus<=16'd132;
				end
			16'd132:
				begin
					// list[startindex].nextvalue=newindex
					gb_wren<=1'b0;
					gb_gameaddress<=X1+Y1*(XPIECES+13'd2*matrixsize);
					mainFSMstatus<=16'd133;
				end
			16'd133:
				begin
					gb_wren<=1'b1;
					gb_inputdata[14:2]<=snake_startX+(XPIECES+13'd2*matrixsize)*snake_startY; 
					mainFSMstatus<=16'd134;
				end
			16'd134:
				begin
					gb_wren<=1'b0;
					mainFSMstatus<=16'd200;
				end
			
				
			
			16'd200:
				begin
					// manage tail
					
					if (strech_counter == 16'd0) begin
						// do not stretch: lenght remains fixed... already added head, remove tail
						
						/*
						removetail():
						lenght--
						endindex=list[endindex].nextvalue
						list[endindex].value=empty
						*/
						snake_lenght<=snake_lenght-13'd1;
						
						gb_gameaddress<=snake_end;
											
						
						mainFSMstatus<=16'd210;
					end else begin
						// must extend snake: already added head...
						strech_counter = strech_counter - 16'd1;	// decrements steps left
						mainFSMstatus<=16'd250;
					end
					
					
				end
			16'd210:
				begin
					mainFSMstatus<=16'd211;
				end
			16'd211:
				begin
					mainFSMstatus<=16'd212;
				end
			16'd212:
				begin
					//endindex=list[endindex].nextvalue
					// list[endindex].value=empty
					
					snake_end<=gb_outputdata[14:2];
					gb_inputdata<=15'd0; // clear old end element
					gb_wren<=1'b1;
					mainFSMstatus<=16'd250;
					
				end
				
			16'd250:
				begin
					// ok perfect... update coords of head
					snake_startX<=X2-matrixsize;
					snake_startY<=Y2-matrixsize;
					
					gb_wren<=1'b0;
					// next task
					mainFSMstatus<=16'd500;
				end
			16'd500:
				begin
					// fruit processor: we can do it after snake movements, no hurry..
					// eaten? (if_fruit) -> remove it, priority.
					// time to add something?
					// time to remove something
					
					// DEBUG
					//mainFSMstatus<=16'd119;
					
					// manages requests.
					try_count<=10'd0;
					if (is_fruit) begin
						// eaten, remove it
						mainFSMstatus<=16'd520;
						remove_idx <= fruit_index;
						was_fruit<=1'b1;
						scan_count <= 3'd0;
					end else begin
						was_fruit<=1'b0;
						if (add_fruit_req) begin
							// requires setting of fruit timer!
							// nextfruittype
							mainFSMstatus<=16'd510;
							scan_count <= 3'd0;
							
						end else if (remove_fruit_req) begin	// remove_fruit_req
							
							remove_idx <= autoremove_idx;
							mainFSMstatus<=16'd520;
						end else begin
							mainFSMstatus<=16'd119;  // all done, refresh graphics and wait for event
						end
					end
					
					
				end
			16'd501: 
				begin
					// fruit processor: check for any other pending requests about fruit...
					try_count<=10'd0;
					remove_fruit_ack <= 1'd0;
					add_fruit_ack <= 1'd0;
					load_timer_req<=1'b0;
					gb_wren<=1'b0;
					mainFSMstatus<=16'd502;
					was_fruit<=1'b0;
				end
			16'd502:
				begin
					// other requests pending
					if (add_fruit_req) begin
						// requires setting of fruit timer!
						// nextfruittype
						mainFSMstatus<=16'd510;
						scan_count <= 3'd0;
						
					end else if (remove_fruit_req) begin
						
						remove_idx <= autoremove_idx;
						mainFSMstatus<=16'd520;
					end else begin
						mainFSMstatus<=16'd119;  // all done, refresh graphics and wait for event
					end
				end
			16'd510:
				begin
					// ADD:  find empty index  and populate
					if (fruit_count == 7) begin
						mainFSMstatus<=16'd501;			// go processing other fruit events
						add_fruit_ack <= 1'd1;
					end else begin 
						if ( fruit_array[scan_count] == 6'd0) begin	// empty
							
							generate_coords<=1'b1;	// generte random coords
							mainFSMstatus<=16'd511;
							
							add_idx<=scan_count;
						end else begin
							scan_count <= scan_count+3'd1;	// go next
							if (scan_count == 7) begin	// all procssed, no empty found, skip adding. (signal as added)
								mainFSMstatus<=16'd501;
								add_fruit_ack <= 1'd1;
							end
						end
					end
					
				end
			16'd511:
				begin
					// latch new coords
					try_count<=try_count+10'd1;
					if (try_count != 1024) begin
						generate_coords<=1'b0;
						mainFSMstatus<=16'd512;
						
					end else begin
						// tried too many times... skip
						mainFSMstatus<=16'd501;
						add_fruit_ack <= 1'd1;
					end
					
				end
			16'd512:
				begin	
					new_fruitX<=fruitX+matrixsize;
					new_fruitY<=fruitY+matrixsize;
					mainFSMstatus<=16'd513;
					
				end
			16'd513:
				begin
					// ADD:  check coords valid
		
					// check coordinates
					if (	
						(new_fruitX < matrixsize)|| (new_fruitX >= XPIECES + matrixsize)||
						(new_fruitY < matrixsize)|| (new_fruitY >= YPIECES + matrixsize)	)
						begin
						// failure
							mainFSMstatus<=16'd511;
							generate_coords<=1'b1;
					end else begin
						// check what's inside current block
						gb_gameaddress<=new_fruitX+new_fruitY*(XPIECES+13'd2*matrixsize);
						mainFSMstatus<=16'd514;
					end
				end
			16'd514:
				begin
					mainFSMstatus<=16'd515;
				end
			16'd515:
				begin
					mainFSMstatus<=16'd516;
				end
			
			16'd516:
				begin
					// gameboard content...
					if (gb_outputdata[1:0] == 2'b00) begin
						// empty: OK -> we can add fruit here
						gb_inputdata[1:0]<=2'b10;
						gb_inputdata[4:2]<=add_idx; // save index
						gb_inputdata[7:5]<=nextfruittype; // save type too (got free bits...)
						fruit_array[add_idx] <= {gb_gameaddress,nextfruittype}; // save position and type
						fruit_count<=fruit_count+3'd1;
						gb_wren<=1'b1;
						mainFSMstatus<=16'd517;
					
					end else begin
						// occupied, try again with other coords.
						mainFSMstatus<=16'd511;
						generate_coords<=1'b1;
					end
				end			
			16'd517:
				begin
					gb_wren<=1'b0;
					add_fruit_ack <= 1'd1;	// signals fruit has been added
					load_timer_req<=1'b1;	// update timer for fruit duration
					nextfruittype<=randomoutput;	// get new piece from random generator
					mainFSMstatus<=16'd518;
				end
			16'd518:
				begin
					// wait for the timer to be loaded
					remove_fruit_ack <= 1'd0;
					add_fruit_ack <= 1'd0;
					if (load_timer_ack) begin
						
						load_timer_req<=1'b0;
						mainFSMstatus<=16'd501;	// go processing other fruit events
						
					end
				end
			16'd520:
				begin
					// remove: from gameboard and fruitarray (Disappear) or fruitarray only (eaten)
					if (fruit_count == 0) begin
						mainFSMstatus<=16'd501;			// go processing other fruit events
						remove_fruit_ack <= 1'd1;
					end else begin 
						if (was_fruit) begin 
							// eaten -> skip clearing gameboard @ address of fruit
							mainFSMstatus<=16'd523;	
						end else begin
							// disappear (autoremove)
							
							mainFSMstatus<=16'd521;	
						end
						fruit_index <= remove_idx;	// load data from fruit to be removed
					end
				end
			16'd521:
				begin
					gb_gameaddress<=fruit_address;	// got address
					mainFSMstatus<=16'd522;	
				end
			16'd522:
				begin
					gb_inputdata<=15'd0;				// clear from map the fruit
					gb_wren<=1'b1;
					mainFSMstatus<=16'd523;	
				end
			16'd523:
				begin
					// remove from array
					gb_wren<=1'b0;
					fruit_count<=fruit_count-3'd1;
					fruit_array[remove_idx]<=16'd0;	// remove the fruit
					mainFSMstatus<=16'd501;			// go processing other fruit events
					remove_fruit_ack <= 1'd1;
				end
			16'd1000:	
				begin
					// go to game over 
					
					gameover<=1'b1; // stop timer, ost, etc
					graphicupdate<=1'b1; // trigger graphic update
					mainFSMstatus<=16'd1010;
				end
			16'd1010: 
				begin
					graphicupdate<=1'b0; // graphics have been updated with dead condition
					sfx_idx <= 2'd2; // plays die sfx
					sfx_play <= 1'b1;
					
					mainFSMstatus<=16'd1020;
				end
					
			16'd1020:
				begin
					sfx_play <= 1'b0;
					
					// end... game over :(
				end
				
		endcase
	
	end
end

// level manager
reg [7:0] oldlevel;

always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		change_level_req<=1'b0;
		oldlevel<=8'd0;
	end else begin
		if (current_level != oldlevel) begin
			change_level_req<=1'b1; // level has changed -> trigger reload
			oldlevel<=current_level; // update old level
		end else if (change_level_ack) begin
			change_level_req<=1'b0; // new level has been loaded, current_level == oldlevel -> ok, done.
		end
		
		
	end
end

/*
always @ (icurrent_level)
begin
	// load level from outside
	current_level<=icurrent_level;
end
*/

reg [15:0] level_duration_coefficient;

// changes level depending on current score.
always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		current_level<=3'd0;
		level_duration_coefficient<=16'd2;
	end else begin
		if (currentscore >=	 10000) begin 
			current_level<=3'd7;
			level_duration_coefficient<=16'd2;
		end else if (currentscore >= 8000 ) begin 
			current_level<=3'd6;
			level_duration_coefficient<=16'd4;
		end else if (currentscore >= 6000 ) begin 
			current_level<=3'd5;
			level_duration_coefficient<=16'd3;
		end else if (currentscore >= 4000 ) begin 
			current_level<=3'd4;
			level_duration_coefficient<=16'd3;
		end else if (currentscore >= 3000 ) begin 
			current_level<=3'd3;
			level_duration_coefficient<=16'd2;
		end else if (currentscore >= 2000 ) begin 
			current_level<=3'd2;
			level_duration_coefficient<=16'd2;
		end else if (currentscore >= 1000 ) begin 
			current_level<=3'd1;
			level_duration_coefficient<=16'd3;
		end else  
			current_level<=3'd0;
			level_duration_coefficient<=16'd4;
		end
end



// fruit timers!

reg [15:0] fruit_timer[8];

integer ft_scan;

// time left for each fruit index

reg load_timer_req, load_timer_ack;
reg decrement_timer_req, decrement_timer_ack;


wire ft_enable;
assign ft_enable = clock_1hz & ~remove_fruit_req;

wire ftl_reset;
assign ftl_reset = reset | decrement_timer_ack;

always @ (posedge ft_enable or posedge ftl_reset)
begin
	if (ftl_reset) begin
		decrement_timer_req<=1'b0;
	end else begin
		decrement_timer_req<=1'b1;
	end
	
end

// manages timer
always @  (posedge clock or posedge reset)
begin
	if (reset) begin
		for (ft_scan = 0; ft_scan < 8; ft_scan = ft_scan +1) begin
			fruit_timer[ft_scan]<=16'd0;
		end
	end else begin
		if (load_timer_req) begin
			fruit_timer[add_idx]<=fruit_duration*level_duration_coefficient;
			load_timer_ack<=1'b1;
			decrement_timer_ack<=1'b0;
		end else if (decrement_timer_req) begin
			load_timer_ack<=1'b0;
			decrement_timer_ack<=1'b1;
			for (ft_scan = 0; ft_scan < 8; ft_scan = ft_scan +1) begin
				if ( (fruit_timer[ft_scan] != 16'd0 ) && (timerenabled)) 
					fruit_timer[ft_scan]<=fruit_timer[ft_scan]-16'd1;
			end
		end else begin
			decrement_timer_ack<=1'b0;
			load_timer_ack<=1'b0;
		end
		
	end
	

end

// timer for adding fruit.
reg [15:0] ft_add_timer;



wire ft_reset;

// when fruit is succesfully added, resets timer and req.
assign ft_reset = reset | add_fruit_ack;

always @  (posedge clock_5hz or posedge ft_reset)
begin
	if (ft_reset) begin
		ft_add_timer <= 16'd0;
		add_fruit_req<=1'b0;
	end else begin
		
		if (~add_fruit_req) begin
			// fast fruit popup if no fruit onboard
			if (fruit_count == 3'd0) begin
				if (ft_add_timer[5:2] == randomtrigger[5:2] ) begin
					add_fruit_req<=1'b1;
					
				end else begin
					add_fruit_req<=1'b0;
					if (timerenabled) 
						ft_add_timer <= ft_add_timer+16'd1;
				end
			end else if (fruit_count < 3'd3) begin
				if (ft_add_timer[5:1] == randomtrigger[5:1] ) begin
					add_fruit_req<=1'b1;
					
				end else begin
					add_fruit_req<=1'b0;
					if (timerenabled) 
						ft_add_timer <= ft_add_timer+16'd1;
				end
			end else	begin
				if (ft_add_timer[7:2] == randomtrigger) begin
					add_fruit_req<=1'b1;
					
				end else begin
					add_fruit_req<=1'b0;
					if (timerenabled) 
						ft_add_timer <= ft_add_timer+16'd1;
				end
				
			end
		end
	end
end


// scans for fruit time left timers to see if there is some fruit expired.......
reg [2:0] ftrem_scan;

always @  (posedge clock or posedge reset)
begin
	if (reset) begin
		ftrem_scan <= 3'd0;
		remove_fruit_req<=1'b0;
		autoremove_idx <=1'b0;
	end else begin
		if (~remove_fruit_req || remove_fruit_ack) begin
			ftrem_scan <= ftrem_scan + 3'd1; // scan all 8 positions...
			// was == 1 (going to expire)
			if ((fruit_timer[ftrem_scan] == 16'd0 ) && (fruit_array[ftrem_scan] != 0)) begin // timer expired and there is fruit...
					
				autoremove_idx <= ftrem_scan;
				remove_fruit_req <= 1'b1;
			end else begin
				remove_fruit_req <= 1'b0;
			end
		end 
		
	end
	

end


// manages fruit array and fruit data outputs
reg [5:0] fruit_duration, fruit_points, fruit_extension;

reg [2:0] fruit_index;

wire [15:0] fruit_data;
wire [12:0] fruit_address;

assign fruit_data = fruit_array[fruit_index];
assign fruit_address = fruit_data[15:3];

always @ (*)
begin
	case ( fruit_data[2:0])
		3'd0:
			begin
				// orange
				fruit_duration=6'd10;
				fruit_extension=6'd5;
				fruit_points=6'd16;
			end
		3'd1:
			begin
				// banana
				fruit_duration=6'd30;
				fruit_extension=6'd1;
				fruit_points=6'd1;
			end
		3'd2:
			begin
				// cherry
				fruit_duration=6'd5;
				fruit_extension=6'd1;
				fruit_points=6'd20;
			end
		3'd3:
			begin
				// acorn
				fruit_duration=6'd20;
				fruit_extension=6'd2;
				fruit_points=6'd5;
			end
		3'd4:
			begin
				// lemon
				fruit_duration=6'd40;
				fruit_extension=6'd8;
				fruit_points=6'd2;
			end
		3'd5:
			begin
				// apple
				fruit_duration=6'd25;
				fruit_extension=6'd4;
				fruit_points=6'd9;
			end
		3'd6:
			begin
				// pear
				fruit_duration=6'd20;
				fruit_extension=6'd7;
				fruit_points=6'd7;
			end
		3'd7:
			begin
				// grapevine
				fruit_duration=6'd15;
				fruit_extension=6'd6;
				fruit_points=6'd10;
			end
		
			
	
	endcase
end




// seed genertor for randomizer (std timer)
reg [18:0]	myrand;

always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		myrand<=16'd0;
	end else begin
		myrand<=myrand+16'd1;
	end
end
	
// manage input keys

reg kb_strobe_pb;
assign kb_strobe = (pushbuttons==1'b1)?kb_strobe_pb:kb_strobe_ps2;

reg [3:0] debounceFSMstatus;
reg [31:0] debounce_timer;



parameter	DEBOUNCE_THRESHOLD	= 32'h000FFFFF;

always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		debounceFSMstatus<=4'd0;
		kb_strobe_pb<=1'b0;
		debounce_timer<=32'd0;
	end else begin
		case (debounceFSMstatus) 
			4'd0:
				begin
					if (KEY!=4'b1111) begin
						debounceFSMstatus<=4'd1;
						debounce_timer<=32'd0;
						latchedpb<=~KEY;
					end
					kb_strobe_pb<=1'b0;
				end
			4'd1:
				begin
					if (debounce_timer<DEBOUNCE_THRESHOLD) 
						debounce_timer<=debounce_timer+32'd1;
					else
						debounceFSMstatus<=4'd2;
				end
			4'd2:
				begin
					if (KEY==4'b1111) begin
						
						kb_strobe_pb<=1'b1;
					end else begin
						kb_strobe_pb<=1'b0;
						
					end
					debounceFSMstatus<=4'd0;
				end
			
		endcase
	end
end
		
		
	
// keyboard input analyzer
ps2conv  		dec0(.clock(clock),.strobe (ps2strobe), .reset(reset), 
					.scancode(kb_scancode), .key(scankey), .outputstrobe(kb_strobe_ps2),
					);



wire	[7:0] scankey;
reg [7:0] latkey;
reg [3:0] pbkey, latchedpb;
reg [15:0] keyFSMstatus;

wire keyreset;

wire validleft , validright , validup , validdown;

assign validkey = validleft | validright | validup | validdown;

assign validdown = (pushbuttons==1'b1)?pbkey[2]:latkey[1];
assign validleft = (pushbuttons==1'b1)?pbkey[3]:latkey[2];
assign validright = (pushbuttons==1'b1)?pbkey[0]:latkey[3];
assign validup = (pushbuttons==1'b1)?pbkey[1]:latkey[0];  // up

assign keyreset = reset | keyprocessed;



// on keypress, latch decoded value
always @ (posedge clock or posedge keyreset)
begin
	if (keyreset) begin
		latkey<=8'h00;
		pbkey<=4'd0;
		
		keyFSMstatus<=16'd0;
		keypressed<=1'b0;
	end else begin
		case (keyFSMstatus)
			16'd0:
				begin
					// idling state
					if ((inhibit==1'b0)&&(kb_strobe==1'b1)) begin
						keyFSMstatus<=16'd1;
					end
					
				end
			16'd1:
				begin
					keyFSMstatus<=16'd2;
					latkey<=8'd0;
					pbkey<=4'd0;
				end
			16'd2:
				begin
					keyFSMstatus<=16'd3;
				end
			16'd3:
				begin
					keyFSMstatus<=16'd4;
				end
			16'd4:
				begin
					keyFSMstatus<=16'd5;
				end
			16'd5:
				begin
					keyFSMstatus<=16'd6;
				end
			16'd6:
				begin
					latkey<=scankey;
					pbkey<=latchedpb;
					keyFSMstatus<=16'd7;
				end
			16'd7:
				begin
					keyFSMstatus<=16'd8;
					if (validkey==1'b1) begin 
						keypressed<=1'b1;
					end
				end
			16'd8:
				begin
					keypressed<=1'b0;
					keyFSMstatus<=16'd0;
				end
		endcase
			
	end
end


// inhibit getting new keys for a while (to debounce)
reg inhibit;
reg [31:0] inhibit_timer;

wire inhibit_reset;

parameter	INHIBIT_THRESHOLD	= 32'h0002FFFF;
reg [3:0]	inhibitFSMstatus;

assign inhibit_reset=reset | (gametick) ;

always @ (posedge clock or posedge inhibit_reset)
begin
	if (inhibit_reset) begin
		inhibit_timer<=32'd0;
		inhibit<=1'b0;
		inhibitFSMstatus<=4'd0;
	end else begin
		case (inhibitFSMstatus)
			4'd0:
				begin
					if (kb_strobe) begin
						inhibitFSMstatus<=4'd1;
						inhibit<=1'b1;
					end
				end
			4'd1:
				begin
					if (inhibit_timer < INHIBIT_THRESHOLD) begin
						inhibit_timer<=inhibit_timer+32'd1;
						inhibit<=1'b1;
					end else	begin
						inhibit<=1'b0;
						inhibitFSMstatus<=4'd0;
						inhibit_timer<=32'd0;
					end
				end
		endcase
	end
end




/*
assign up = 0;
assign drop = 0;
assign left = 0;
assign right = 0;
assign rotate = 0; // space
*/

always @ (posedge latkey[5] or posedge reset) // pause
begin
	if (reset) begin
		pausecontrol <= 1'b0;
	end else begin
		pausecontrol <= ~pausecontrol;
	end
end
	

	
wire timerenabled;

assign timerenabled = (~gameover)&((pushbuttons==1'b1)?externalpause:(~pausecontrol)); // add here "pause function"

// time block... mananges elapsed time
always @ (posedge clock_1hz or posedge reset)
begin
	if (reset) begin
		d_minuti<=3'd0;
		u_minuti<=4'd0;
		d_secondi<=3'd0;
		u_secondi<=4'd0;
	
	end else begin
		if (timerenabled) begin
			if (u_secondi != 4'd9)
				u_secondi<=u_secondi+4'd1;
			else begin
				u_secondi<=4'd0;
				if(d_secondi!= 3'd5) begin
					d_secondi<=d_secondi+3'd1;
				end else begin
					d_secondi<=3'd0;
					if (u_minuti != 4'd9)
						u_minuti<=u_minuti+4'd1;
					else begin
						u_minuti<=4'd0;
						if(d_minuti != 3'd5) begin
							d_minuti<=d_minuti+3'd1;
						end else begin
							d_minuti<=3'd0;
						
						end
					end
				end
			end
		end
	end
end

endmodule



// converts rom data format to compatible format for vga palette
module romtopalette (in, out, forecolor, backcolor);

input [3:0] in, forecolor, backcolor;
output [15:0] out;

wire [3:0] out0,out1,out2, out3;

assign out0= (in[3])?(forecolor):(backcolor);
assign out1= (in[2])?(forecolor):(backcolor);
assign out2= (in[1])?(forecolor):(backcolor);
assign out3= (in[0])?(forecolor):(backcolor);

assign out = {out3,out2,out1,out0};

endmodule



// convert 16 bit score to 6 digits BCD
module bcdexpander (in, out);

input [15:0] in;
output [23:0] out;

reg [3:0] out0, out1, out2, out3, out4, out5;

assign out = {out5, out4, out3,out2,out1,out0};

integer i;
always @ (in)
begin
	out0=4'd0;
	out1=4'd0;
	out2=4'd0;
	out3=4'd0;
	out4=4'd0;
	out5=4'd0;
	for (i=15; i>=0; i=i-1)
	begin
		if (out5 >= 5)
			out5 = out5 + 4'd3;
		if (out4 >= 5)
			out4 = out4 + 4'd3;
		if (out3 >= 5)
			out3 = out3 + 4'd3;
		if (out2 >= 5)
			out2 = out2 + 4'd3;
		if (out1 >= 5)
			out1 = out1 + 4'd3;
		if (out0 >= 5)
			out0 = out0 + 4'd3;

		out5 = out5 << 1;
		out5[0] = out4[3];
		out4 = out4 << 1;
		out4[0] = out3[3];
		out3 = out3 << 1;
		out3[0] = out2[3];
		out2 = out2 << 1;
		out2[0] = out1[3];
		out1 = out1 << 1;
		out1[0] = out0[3];
		out0 = out0 << 1;
		out0[0] = in[i];
	end
end



endmodule



// generate gametick
module gtg(in, out, score, reset);

input in, reset;
input [15:0] score;

reg [31:0] tickthreshold;

parameter 	INITIAL_TICKTHRESHOLD	=	32'h007FF000; // 8384512

// maxspeed 6500000
// medium speed 5000000
// min speed 3072000 2EE000

always @ (score)
begin
	if (score < 16'd50)
		tickthreshold=32'h00FFF+score*1024;
	else if (score < 16'd200)
		tickthreshold=32'h0FFFF+score*2048;
	else if (score < 16'd1000)
		tickthreshold=32'h22E000+(score-200)*2048;
	else if (score < 16'd6000)
		tickthreshold=32'h22E000+(score-1000)*512; // 3072000 + 
	else if (score < 16'd10000)
		tickthreshold=32'h22E000+(score-6000)*512;
	else
		tickthreshold=32'h4C4B40;
end

output reg out;

reg	[31:0] aux;

always @ (posedge in or posedge reset)
begin
	if (reset) begin
		aux<=32'd0;
		
	end else begin
		if (aux > (INITIAL_TICKTHRESHOLD-tickthreshold)) begin
			out<=~out;
			aux<=32'd0;
		end else
			aux<=aux+1; //+(score/2048);
		
	end

end



endmodule

// 3 bit pseudo-random number generator
module rndgen (clock, reset, seed, out);

input clock, reset;
input [2:0] seed;
output [2:0] out;


reg [2:0] temp;

wire ret;
assign ret = temp[0] ^ temp[2];

assign out = temp;

always@(posedge clock or posedge reset) begin
	if (reset) begin
		temp[2:0]<=seed;  //load seed at 'reset'
	end 
	else begin
		temp[0]<=ret;
		temp[1]<=temp[0];
		temp[2]<=temp[1];
	end
 end
 
 endmodule
 



// 6 bit pseudo-random number generator
module rndgen6(clock, reset, seed, out);

input clock, reset;
input [5:0] seed;
output [5:0] out;

assign out = temp;

reg [5:0] temp;

wire ret;
assign ret = temp[0] ^ temp[5];

always@(posedge clock or posedge reset) begin
	if (reset) begin
		temp[5:0]<=seed;  //load seed at 'reset'
	end 
	else begin
		temp[0]<=ret;
		temp[1]<=temp[0];
		temp[2]<=temp[1];
		temp[3]<=temp[2];
		temp[4]<=temp[3];
		temp[5]<=temp[4];
	end
 end
 
 endmodule
 
 // generate slow clocks 
module clk_slow(CLOCK_50, CLOCK_1, CLOCK_5, reset);

input CLOCK_50, reset;
output reg CLOCK_1, CLOCK_5;

reg	[25:0] aux, aux2;
always @ (posedge CLOCK_50 or posedge reset)
begin
	if (reset) begin
		aux<=26'd0;
		aux2<=26'd0;
		CLOCK_1<=1'd0;
		CLOCK_5<=1'd0;
	end else begin
		if (aux == 26'd24999999) begin
			CLOCK_1<=~CLOCK_1;
			aux<=26'd0;
		end else
			aux<=aux+26'd1;
		if (aux2 == 26'd1999999) begin
			CLOCK_5<=~CLOCK_5;
			aux2<=26'd0;
		end else
			aux2<=aux2+26'd1;
	end

end

endmodule



