// játéklogika module game_controller #( parameter SCREEN_WIDTH = 1024, // képernyő szélessége parameter SCREEN_HEIGHT = 768, // képernyő magassága // labda kezdőkoordinátái parameter BALL_START_X = SCREEN_WIDTH / 2, parameter BALL_START_Y = SCREEN_HEIGHT / 6, //parameter BALL_START_SPEED_VEC = 2'b11, // platformok tulajdonságai 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, // középső fal tulajdonságai parameter WALL_HALF_WIDTH = 4, parameter WALL_HEIGHT = SCREEN_HEIGHT / 3, // szimuláció sebessége parameter SIMDIV = 2500, // bithosszok parameter N_SCR_WIDTH = $clog2(SCREEN_WIDTH), parameter N_SCR_HEIGHT = $clog2(SCREEN_HEIGHT), parameter N_SIMDIV = $clog2(SIMDIV) )( input wire clk, // órajel (1MHz) input wire rst, // szinkron reset input wire start, // játék indítása input wire acknowledge, // továbblépés játék vége után // bal oldali platform koordinátái output reg [N_SCR_WIDTH - 1:0] pod1x, output reg [N_SCR_WIDTH - 1:0] pod2x, // jobb oldali platform koordinátái output reg [N_SCR_WIDTH - 1:0] ballx, output reg [N_SCR_HEIGHT - 1:0] bally, // platformok mozgatás-engedélyezése és irányvezérlése input wire move_pod1_en, input wire move_pod1_dir, input wire move_pod2_en, input wire move_pod2_dir, // pontszám-kimenetek és pontszámok törlése output reg [7:0] score1, output reg [7:0] score2, input wire clear_score, // hangvezérlése output reg hit, output reg miss ); // randobgenerátor (LFSR) 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 // ----------- // játékállapotok localparam GS_FROZEN = 0; // fagyasztva localparam GS_RUNNING = 1; // játék fut localparam GS_SCORING = 2; // pontok kiosztása localparam GS_GAME_OVER = 3; // játék vége wire restart = (next_game_state == GS_FROZEN) && (game_state == GS_GAME_OVER); // követvező játékállapot 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 // játékállapot-regiszter 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); // játék fut jelzés // ----------- // ladba sebsségszimulációja reg speed_boost; // gyorsítás reg [2:0] speed_vec; // sebességvektor always @(posedge clk) begin if (rst || restart) // szinkron reset vagy új játék begin speed_boost <= 1'b0; speed_vec <= rnd[15:14]; hit <= 1'b0; end else begin // visszapattanás a függőleges falakról if (((ballx == (SCREEN_WIDTH - 1)) && (speed_vec[0] == 1'b1)) || // jobb képernyőszél ((ballx == 0) && (speed_vec[0] == 1'b0)) || // bal képernyőszél ((bally > (SCREEN_HEIGHT - WALL_HEIGHT - 1)) && // középső fal (((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 // visszapattanás a képernyő tetejéről if ((bally == 0) && (speed_vec[1] == 1'b0)) begin speed_vec[1] <= ~speed_vec[1]; end hit <= 1'b0; // visszapattanás a bal platformról 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 // visszapattanás a jobb platformról 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 // ----------------- // szimuláció frekvenciaosztó számlálója 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); // szimulációs lépés jelzés // labda-pozíciószimuláció 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 // platform mozgatása 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) // bal platform mozgatás-engedélyezése 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) // jobb platform mozgatás-engedélyezése 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 // BCD-számláló funkcionalitás 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 // pontozás 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 // leeső labda hangvezérlése 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