pong-verilog/game_controller.v
2025-11-05 07:26:15 +01:00

296 lines
6.5 KiB
Verilog

// 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