2018-10-03 19:06:48 +00:00
|
|
|
|
2017-11-21 21:36:38 +00:00
|
|
|
`include "hvsync_generator.v"
|
2018-02-15 18:31:32 +00:00
|
|
|
`include "sprite_bitmap.v"
|
2017-11-21 21:36:38 +00:00
|
|
|
`include "sprite_renderer.v"
|
|
|
|
|
2018-10-01 16:30:47 +00:00
|
|
|
/*
|
|
|
|
A simple racing game with two sprites and a scrolling playfield.
|
|
|
|
This version does not use a CPU; all logic is straight Verilog.
|
|
|
|
*/
|
|
|
|
|
2018-07-22 12:31:42 +00:00
|
|
|
module racing_game_top(clk, hsync, vsync, rgb, hpaddle, vpaddle);
|
2017-11-21 21:36:38 +00:00
|
|
|
|
|
|
|
input clk;
|
|
|
|
input hpaddle, vpaddle;
|
|
|
|
output hsync, vsync;
|
|
|
|
output [2:0] rgb;
|
|
|
|
wire display_on;
|
|
|
|
wire [8:0] hpos;
|
|
|
|
wire [8:0] vpos;
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// player car position (set at VSYNC)
|
2017-11-21 21:36:38 +00:00
|
|
|
reg [7:0] player_x;
|
|
|
|
reg [7:0] player_y;
|
2018-07-31 18:41:27 +00:00
|
|
|
// paddle position (set continuously during frame)
|
2017-11-21 21:36:38 +00:00
|
|
|
reg [7:0] paddle_x;
|
|
|
|
reg [7:0] paddle_y;
|
2018-07-31 18:41:27 +00:00
|
|
|
// enemy car position
|
2017-11-21 21:36:38 +00:00
|
|
|
reg [7:0] enemy_x = 128;
|
|
|
|
reg [7:0] enemy_y = 128;
|
2018-07-31 18:41:27 +00:00
|
|
|
// enemy car direction, 1=right, 0=left
|
|
|
|
reg enemy_dir = 0;
|
2017-11-21 21:36:38 +00:00
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
reg [15:0] track_pos = 0; // player position along track (16 bits)
|
|
|
|
reg [7:0] speed = 31; // player velocity along track (8 bits)
|
2017-11-21 21:36:38 +00:00
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// video sync generator
|
2017-11-21 21:36:38 +00:00
|
|
|
hvsync_generator hvsync_gen(
|
|
|
|
.clk(clk),
|
2017-11-29 01:48:27 +00:00
|
|
|
.reset(0),
|
2017-11-21 21:36:38 +00:00
|
|
|
.hsync(hsync),
|
|
|
|
.vsync(vsync),
|
|
|
|
.display_on(display_on),
|
|
|
|
.hpos(hpos),
|
|
|
|
.vpos(vpos)
|
|
|
|
);
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// set paddle registers
|
|
|
|
always @(posedge hsync)
|
|
|
|
begin
|
|
|
|
if (!hpaddle) paddle_x <= vpos[7:0];
|
|
|
|
if (!vpaddle) paddle_y <= vpos[7:0];
|
|
|
|
end
|
2018-10-01 23:43:06 +00:00
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
|
2018-10-01 23:43:06 +00:00
|
|
|
// select player or enemy access to ROM
|
|
|
|
wire player_load = (hpos >= 256) && (hpos < 260);
|
|
|
|
wire enemy_load = (hpos >= 260);
|
2018-07-31 18:41:27 +00:00
|
|
|
// wire up car sprite ROM
|
2018-10-01 23:43:06 +00:00
|
|
|
// multiplex between player and enemy ROM address
|
|
|
|
wire [3:0] player_sprite_yofs;
|
|
|
|
wire [3:0] enemy_sprite_yofs;
|
|
|
|
wire [3:0] car_sprite_yofs = player_load ? player_sprite_yofs : enemy_sprite_yofs;
|
|
|
|
wire [7:0] car_sprite_bits;
|
2017-11-21 21:36:38 +00:00
|
|
|
car_bitmap car(
|
|
|
|
.yofs(car_sprite_yofs),
|
|
|
|
.bits(car_sprite_bits));
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// signals for player sprite generator
|
2018-10-01 23:43:06 +00:00
|
|
|
wire player_vstart = {1'b0,player_y} == vpos;
|
|
|
|
wire player_hstart = {1'b0,player_x} == hpos;
|
2017-11-21 21:36:38 +00:00
|
|
|
wire player_gfx;
|
|
|
|
wire player_is_drawing;
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// signals for enemy sprite generator
|
2018-10-01 23:43:06 +00:00
|
|
|
wire enemy_vstart = {1'b0,enemy_y} == vpos;
|
|
|
|
wire enemy_hstart = {1'b0,enemy_x} == hpos;
|
2017-11-21 21:36:38 +00:00
|
|
|
wire enemy_gfx;
|
|
|
|
wire enemy_is_drawing;
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// player sprite generator
|
2017-11-21 21:36:38 +00:00
|
|
|
sprite_renderer player_renderer(
|
|
|
|
.clk(clk),
|
|
|
|
.vstart(player_vstart),
|
2018-10-01 23:43:06 +00:00
|
|
|
.load(player_load),
|
2017-11-21 21:36:38 +00:00
|
|
|
.hstart(player_hstart),
|
2018-10-01 23:43:06 +00:00
|
|
|
.rom_addr(player_sprite_yofs),
|
2017-11-21 21:36:38 +00:00
|
|
|
.rom_bits(car_sprite_bits),
|
|
|
|
.gfx(player_gfx),
|
|
|
|
.in_progress(player_is_drawing));
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// enemy sprite generator
|
2017-11-21 21:36:38 +00:00
|
|
|
sprite_renderer enemy_renderer(
|
|
|
|
.clk(clk),
|
|
|
|
.vstart(enemy_vstart),
|
2018-10-01 23:43:06 +00:00
|
|
|
.load(enemy_load),
|
2017-11-21 21:36:38 +00:00
|
|
|
.hstart(enemy_hstart),
|
2018-10-01 23:43:06 +00:00
|
|
|
.rom_addr(enemy_sprite_yofs),
|
2017-11-21 21:36:38 +00:00
|
|
|
.rom_bits(car_sprite_bits),
|
|
|
|
.gfx(enemy_gfx),
|
2018-10-05 18:02:46 +00:00
|
|
|
.in_progress(enemy_is_drawing));
|
2018-07-31 18:41:27 +00:00
|
|
|
|
|
|
|
// signals for enemy bouncing off left/right borders
|
2017-11-28 02:08:19 +00:00
|
|
|
wire enemy_hit_left = (enemy_x == 64);
|
|
|
|
wire enemy_hit_right = (enemy_x == 192);
|
|
|
|
wire enemy_hit_edge = enemy_hit_left || enemy_hit_right;
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// update player, enemy, track counters
|
|
|
|
// runs once per frame
|
2017-11-21 21:36:38 +00:00
|
|
|
always @(posedge vsync)
|
|
|
|
begin
|
|
|
|
player_x <= paddle_x;
|
|
|
|
player_y <= 180;
|
|
|
|
track_pos <= track_pos + {11'b0,speed[7:4]};
|
|
|
|
enemy_y <= enemy_y + {3'b0, speed[7:4]};
|
2017-11-28 02:08:19 +00:00
|
|
|
if (enemy_hit_edge)
|
|
|
|
enemy_dir <= !enemy_dir;
|
|
|
|
if (enemy_dir ^ enemy_hit_edge)
|
|
|
|
enemy_x <= enemy_x + 1;
|
|
|
|
else
|
|
|
|
enemy_x <= enemy_x - 1;
|
2017-11-21 21:36:38 +00:00
|
|
|
// collision check?
|
|
|
|
if (frame_collision)
|
|
|
|
speed <= 16;
|
2017-11-28 02:08:19 +00:00
|
|
|
else if (speed < ~paddle_y)
|
2017-11-21 21:36:38 +00:00
|
|
|
speed <= speed + 1;
|
2017-11-28 02:08:19 +00:00
|
|
|
else
|
|
|
|
speed <= speed - 1;
|
2017-11-21 21:36:38 +00:00
|
|
|
end
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// set to 1 when player collides with enemy or track
|
2017-11-21 21:36:38 +00:00
|
|
|
reg frame_collision;
|
|
|
|
|
|
|
|
always @(posedge clk)
|
|
|
|
if (player_gfx && (enemy_gfx || track_gfx))
|
|
|
|
frame_collision <= 1;
|
|
|
|
else if (vsync)
|
|
|
|
frame_collision <= 0;
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// track graphics signals
|
2017-11-21 21:36:38 +00:00
|
|
|
wire track_offside = (hpos[7:5]==0) || (hpos[7:5]==7);
|
|
|
|
wire track_shoulder = (hpos[7:3]==3) || (hpos[7:3]==28);
|
|
|
|
wire track_gfx = (vpos[5:1]!=track_pos[5:1]) && track_offside;
|
|
|
|
|
2018-07-31 18:41:27 +00:00
|
|
|
// combine signals for RGB output
|
2017-11-21 21:36:38 +00:00
|
|
|
wire r = display_on && (player_gfx || enemy_gfx || track_shoulder);
|
|
|
|
wire g = display_on && (player_gfx || track_gfx);
|
|
|
|
wire b = display_on && (enemy_gfx || track_shoulder);
|
|
|
|
assign rgb = {b,g,r};
|
|
|
|
|
|
|
|
endmodule
|