module game_controller #( parameter SCREEN_WIDTH = 1024, parameter SCREEN_HEIGHT = 768, parameter BALL_START_X = SCREEN_WIDTH / 2, parameter BALL_START_Y = SCREEN_HEIGHT / 6, //parameter BALL_START_SPEED_VEC = 2'b11, parameter POD_TOP = SCREEN_HEIGHT - 50, parameter POD_WIDTH = SCREEN_WIDTH / 6, parameter POD1_START_X = (SCREEN_WIDTH / 2 - POD_WIDTH) / 2, parameter POD2_START_X = POD1_START_X + SCREEN_WIDTH / 2, parameter WALL_HALF_WIDTH = 4, parameter WALL_HEIGHT = SCREEN_HEIGHT / 3, parameter SIMDIV = 2500, parameter N_SCR_WIDTH = $clog2(SCREEN_WIDTH), parameter N_SCR_HEIGHT = $clog2(SCREEN_HEIGHT), parameter N_SIMDIV = $clog2(SIMDIV) )( input wire clk, input wire rst, input wire rst2, input wire start, input wire acknowledge, output reg [N_SCR_WIDTH - 1:0] pod1x, output reg [N_SCR_WIDTH - 1:0] pod2x, output reg [N_SCR_WIDTH - 1:0] ballx, output reg [N_SCR_HEIGHT - 1:0] bally, input wire move_pod1_en, input wire move_pod1_dir, input wire move_pod2_en, input wire move_pod2_dir, output reg [7:0] score1, output reg [7:0] score2, input wire clear_score, output reg hit, output reg miss ); reg [15:0] rnd; always @(posedge clk) begin if (rst) begin rnd <= 16'hACE1; end else begin rnd <= { rnd[14:0], rnd[15] ^ rnd[13] ^ rnd[12] ^ rnd[10] }; end end // ----------- localparam GS_FROZEN = 0; localparam GS_RUNNING = 1; localparam GS_SCORING = 2; localparam GS_GAME_OVER = 3; wire restart = (next_game_state == GS_FROZEN) && (game_state == GS_GAME_OVER); reg [1:0] next_game_state; always @(*) begin case (game_state) default : next_game_state <= start ? GS_RUNNING : GS_FROZEN; GS_RUNNING : next_game_state <= (bally > SCREEN_HEIGHT - 5) ? GS_SCORING : GS_RUNNING; GS_SCORING : next_game_state <= GS_GAME_OVER; GS_GAME_OVER : next_game_state <= acknowledge ? GS_FROZEN : GS_GAME_OVER; endcase end reg [1:0] game_state; always @(posedge clk) begin if (rst) begin game_state <= GS_FROZEN; end else begin game_state <= next_game_state; end end wire game_running = (game_state == GS_RUNNING); // ----------- reg speed_boost; reg [2:0] speed_vec; always @(posedge clk) begin if (rst || restart) begin speed_boost <= 1'b0; speed_vec <= rnd[15:14]; hit <= 1'b0; end else begin if (((ballx == (SCREEN_WIDTH - 1)) && (speed_vec[0] == 1'b1)) || ((ballx == 0) && (speed_vec[0] == 1'b0)) || ((bally > (SCREEN_HEIGHT - WALL_HEIGHT - 1)) && (((ballx == (SCREEN_WIDTH / 2 - 1)) && (speed_vec[0] == 1'b1)) || ((ballx == (SCREEN_WIDTH / 2)) && (speed_vec[0] == 1'b0))))) begin speed_vec[0] <= ~speed_vec[0]; end if ((bally == 0) && (speed_vec[1] == 1'b0)) begin speed_vec[1] <= ~speed_vec[1]; end hit <= 1'b0; if ((bally == POD_TOP - 1) && (speed_vec[1] == 1'b1) && (((ballx > pod1x) && (ballx < (pod1x + POD_WIDTH))))) begin speed_vec[1] <= ~speed_vec[1]; speed_boost <= move_pod1_en; hit <= 1'b1; end if ((bally == POD_TOP - 1) && (speed_vec[1] == 1'b1) && ((ballx > pod2x) && (ballx < (pod2x + POD_WIDTH)))) begin speed_vec[1] <= ~speed_vec[1]; speed_boost <= move_pod2_en; hit <= 1'b1; end end end // ----------------- reg [N_SIMDIV - 1:0] sim_cntr; reg [N_SIMDIV - 1:0] sim_cntr_max; always @(posedge clk) begin if (rst) begin sim_cntr <= 0; sim_cntr_max <= SIMDIV - 1'b1; end else begin if (sim_cntr == sim_cntr_max) begin sim_cntr_max <= speed_boost ? ((SIMDIV / 2) - 1'b1) : (SIMDIV - 1'b1); sim_cntr <= 0; end else begin sim_cntr <= sim_cntr + 1'b1; end end end wire sim_step = game_running && (sim_cntr == sim_cntr_max); always @(posedge clk) begin if (rst || restart) begin ballx <= BALL_START_X + rnd[8:0] - 8'd255; bally <= BALL_START_Y + rnd[13:9] - 5'd16; end else begin if (sim_step) begin ballx <= speed_vec[0] ? (ballx + 1'b1) : (ballx - 1'b1); bally <= speed_vec[1] ? (bally + 1'b1) : (bally - 1'b1); end end end always @(posedge clk) begin if (rst || restart) begin pod1x <= POD1_START_X; pod2x <= POD2_START_X; end else if (sim_step) begin if (move_pod1_en) begin if (move_pod1_dir && (pod1x < ((SCREEN_WIDTH / 2) - POD_WIDTH - WALL_HALF_WIDTH))) // jobbra mozgatás begin pod1x <= pod1x + 1'b1; end else if ((!move_pod1_dir) && (pod1x > 0)) // balra mozgatás begin pod1x <= pod1x - 1'b1; end end if (move_pod2_en) begin if (move_pod2_dir && (pod2x < (SCREEN_WIDTH - POD_WIDTH))) // jobbra mozgatás begin pod2x <= pod2x + 1'b1; end else if ((!move_pod2_dir) && (pod2x > ((SCREEN_WIDTH / 2) + WALL_HALF_WIDTH))) // balra mozgatás begin pod2x <= pod2x - 1'b1; end end end end function [7:0] bcd_inc; input [7:0] a; begin bcd_inc = (a[3:0] == 4'd9) ? { a[7:4] + 4'd1, 4'd0 } : a + 8'd1; end endfunction always @(posedge clk) begin if (rst || clear_score) begin score1 <= 8'h00; score2 <= 8'h00; end else if (game_state == GS_SCORING) begin if (ballx >= (SCREEN_WIDTH / 2)) begin score1 <= bcd_inc(score1); end else begin score2 <= bcd_inc(score2); end end end always @(posedge clk) begin if (rst) begin miss <= 1'b0; end else if (game_state == GS_SCORING) begin miss <= 1'b1; end else begin miss <= 1'b0; end end endmodule