2018-02-15 18:31:32 +00:00
|
|
|
`ifndef SPRITE_ROTATION_H
|
|
|
|
`define SPRITE_ROTATION_H
|
|
|
|
|
2018-02-03 20:20:56 +00:00
|
|
|
`include "hvsync_generator.v"
|
|
|
|
|
|
|
|
module tank_bitmap(addr, bits);
|
|
|
|
|
|
|
|
input [7:0] addr;
|
|
|
|
output [7:0] bits;
|
|
|
|
|
2018-02-19 00:19:20 +00:00
|
|
|
reg [15:0] bitarray[0:255];
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
assign bits = (addr[0]) ? bitarray[addr>>1][15:8] : bitarray[addr>>1][7:0];
|
|
|
|
|
|
|
|
initial begin/*{w:16,h:16,bpw:16,count:5}*/
|
2018-02-07 00:07:40 +00:00
|
|
|
bitarray[8'h00] = 16'b11110000000;
|
|
|
|
bitarray[8'h01] = 16'b11110000000;
|
|
|
|
bitarray[8'h02] = 16'b1100000000;
|
|
|
|
bitarray[8'h03] = 16'b1100000000;
|
|
|
|
bitarray[8'h04] = 16'b111101101111000;
|
|
|
|
bitarray[8'h05] = 16'b111101101111000;
|
|
|
|
bitarray[8'h06] = 16'b111111111111000;
|
|
|
|
bitarray[8'h07] = 16'b111111111111000;
|
|
|
|
bitarray[8'h08] = 16'b111111111111000;
|
|
|
|
bitarray[8'h09] = 16'b111111111111000;
|
|
|
|
bitarray[8'h0a] = 16'b111111111111000;
|
|
|
|
bitarray[8'h0b] = 16'b111100001111000;
|
|
|
|
bitarray[8'h0c] = 16'b111100001111000;
|
|
|
|
bitarray[8'h0d] = 16'b0;
|
|
|
|
bitarray[8'h0e] = 16'b0;
|
|
|
|
bitarray[8'h0f] = 16'b0;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
2018-02-07 00:07:40 +00:00
|
|
|
bitarray[8'h10] = 16'b111000000000;
|
|
|
|
bitarray[8'h11] = 16'b1111000000000;
|
|
|
|
bitarray[8'h12] = 16'b1111000000000;
|
|
|
|
bitarray[8'h13] = 16'b11000000000;
|
|
|
|
bitarray[8'h14] = 16'b11101110000;
|
|
|
|
bitarray[8'h15] = 16'b1101110000;
|
|
|
|
bitarray[8'h16] = 16'b111101111110000;
|
|
|
|
bitarray[8'h17] = 16'b111101111111000;
|
|
|
|
bitarray[8'h18] = 16'b111111111111000;
|
|
|
|
bitarray[8'h19] = 16'b11111111111000;
|
|
|
|
bitarray[8'h1a] = 16'b11111111111100;
|
|
|
|
bitarray[8'h1b] = 16'b11111111111100;
|
|
|
|
bitarray[8'h1c] = 16'b11111001111100;
|
|
|
|
bitarray[8'h1d] = 16'b1111001110000;
|
|
|
|
bitarray[8'h1e] = 16'b1111000000000;
|
|
|
|
bitarray[8'h1f] = 16'b1100000000000;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
2018-02-07 00:07:40 +00:00
|
|
|
bitarray[8'h20] = 16'b0;
|
|
|
|
bitarray[8'h21] = 16'b0;
|
|
|
|
bitarray[8'h22] = 16'b11000011000000;
|
|
|
|
bitarray[8'h23] = 16'b111000111100000;
|
|
|
|
bitarray[8'h24] = 16'b111101111110000;
|
|
|
|
bitarray[8'h25] = 16'b1110111111000;
|
|
|
|
bitarray[8'h26] = 16'b111111111100;
|
|
|
|
bitarray[8'h27] = 16'b11111111110;
|
|
|
|
bitarray[8'h28] = 16'b11011111111110;
|
|
|
|
bitarray[8'h29] = 16'b111111111111100;
|
|
|
|
bitarray[8'h2a] = 16'b111111111001000;
|
|
|
|
bitarray[8'h2b] = 16'b11111110000000;
|
|
|
|
bitarray[8'h2c] = 16'b1111100000000;
|
|
|
|
bitarray[8'h2d] = 16'b111110000000;
|
|
|
|
bitarray[8'h2e] = 16'b11110000000;
|
|
|
|
bitarray[8'h2f] = 16'b1100000000;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
2018-02-07 00:07:40 +00:00
|
|
|
bitarray[8'h30] = 16'b0;
|
|
|
|
bitarray[8'h31] = 16'b0;
|
|
|
|
bitarray[8'h32] = 16'b110000000;
|
|
|
|
bitarray[8'h33] = 16'b100001111000000;
|
|
|
|
bitarray[8'h34] = 16'b1110001111110000;
|
|
|
|
bitarray[8'h35] = 16'b1111010111111100;
|
|
|
|
bitarray[8'h36] = 16'b1111111111111111;
|
|
|
|
bitarray[8'h37] = 16'b1111111111111;
|
|
|
|
bitarray[8'h38] = 16'b11111111110;
|
|
|
|
bitarray[8'h39] = 16'b101111111110;
|
|
|
|
bitarray[8'h3a] = 16'b1111111101100;
|
|
|
|
bitarray[8'h3b] = 16'b11111111000000;
|
|
|
|
bitarray[8'h3c] = 16'b1111111100000;
|
|
|
|
bitarray[8'h3d] = 16'b11111110000;
|
|
|
|
bitarray[8'h3e] = 16'b111100000;
|
|
|
|
bitarray[8'h3f] = 16'b1100000;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
2018-02-07 00:07:40 +00:00
|
|
|
bitarray[8'h40] = 16'b0;
|
|
|
|
bitarray[8'h41] = 16'b0;
|
|
|
|
bitarray[8'h42] = 16'b0;
|
|
|
|
bitarray[8'h43] = 16'b111111111000;
|
|
|
|
bitarray[8'h44] = 16'b111111111000;
|
|
|
|
bitarray[8'h45] = 16'b111111111000;
|
|
|
|
bitarray[8'h46] = 16'b111111111000;
|
|
|
|
bitarray[8'h47] = 16'b1100001111100000;
|
|
|
|
bitarray[8'h48] = 16'b1111111111100000;
|
|
|
|
bitarray[8'h49] = 16'b1111111111100000;
|
|
|
|
bitarray[8'h4a] = 16'b1100001111100000;
|
|
|
|
bitarray[8'h4b] = 16'b111111111000;
|
|
|
|
bitarray[8'h4c] = 16'b111111111000;
|
|
|
|
bitarray[8'h4d] = 16'b111111111000;
|
|
|
|
bitarray[8'h4e] = 16'b111111111000;
|
|
|
|
bitarray[8'h4f] = 16'b0;
|
2018-02-03 20:20:56 +00:00
|
|
|
end
|
|
|
|
endmodule
|
|
|
|
|
2018-02-15 18:31:32 +00:00
|
|
|
module sprite_renderer2(clk, vstart, load, hstart, rom_addr, rom_bits,
|
2018-02-03 20:20:56 +00:00
|
|
|
hmirror, vmirror,
|
|
|
|
gfx, busy);
|
|
|
|
|
|
|
|
input clk, vstart, load, hstart;
|
|
|
|
input hmirror, vmirror;
|
|
|
|
output [4:0] rom_addr;
|
|
|
|
input [7:0] rom_bits;
|
|
|
|
output gfx;
|
2018-02-19 00:19:20 +00:00
|
|
|
output busy;
|
|
|
|
|
|
|
|
assign busy = state != WAIT_FOR_VSTART;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
reg [2:0] state;
|
|
|
|
reg [3:0] ycount;
|
|
|
|
reg [3:0] xcount;
|
|
|
|
|
|
|
|
reg [15:0] outbits;
|
|
|
|
|
|
|
|
localparam WAIT_FOR_VSTART = 0;
|
|
|
|
localparam WAIT_FOR_LOAD = 1;
|
|
|
|
localparam LOAD1_SETUP = 2;
|
|
|
|
localparam LOAD1_FETCH = 3;
|
|
|
|
localparam LOAD2_SETUP = 4;
|
|
|
|
localparam LOAD2_FETCH = 5;
|
|
|
|
localparam WAIT_FOR_HSTART = 6;
|
|
|
|
localparam DRAW = 7;
|
|
|
|
|
|
|
|
always @(posedge clk)
|
|
|
|
begin
|
|
|
|
case (state)
|
|
|
|
WAIT_FOR_VSTART: begin
|
|
|
|
ycount <= 0;
|
|
|
|
// set a default value (blank) for pixel output
|
|
|
|
// note: multiple non-blocking assignments are vendor-specific
|
|
|
|
gfx <= 0;
|
|
|
|
if (vstart) state <= WAIT_FOR_LOAD;
|
|
|
|
end
|
|
|
|
WAIT_FOR_LOAD: begin
|
|
|
|
xcount <= 0;
|
|
|
|
gfx <= 0;
|
|
|
|
if (load) state <= LOAD1_SETUP;
|
|
|
|
end
|
|
|
|
LOAD1_SETUP: begin
|
|
|
|
rom_addr <= {vmirror?~ycount:ycount, 1'b0};
|
|
|
|
state <= LOAD1_FETCH;
|
|
|
|
end
|
|
|
|
LOAD1_FETCH: begin
|
|
|
|
outbits[7:0] <= rom_bits;
|
|
|
|
state <= LOAD2_SETUP;
|
|
|
|
end
|
|
|
|
LOAD2_SETUP: begin
|
|
|
|
rom_addr <= {vmirror?~ycount:ycount, 1'b1};
|
|
|
|
state <= LOAD2_FETCH;
|
|
|
|
end
|
|
|
|
LOAD2_FETCH: begin
|
|
|
|
outbits[15:8] <= rom_bits;
|
|
|
|
state <= WAIT_FOR_HSTART;
|
|
|
|
end
|
|
|
|
WAIT_FOR_HSTART: begin
|
|
|
|
if (hstart) state <= DRAW;
|
|
|
|
end
|
|
|
|
DRAW: begin
|
|
|
|
// mirror graphics left/right
|
|
|
|
gfx <= outbits[hmirror ? ~xcount[3:0] : xcount[3:0]];
|
|
|
|
xcount <= xcount + 1;
|
|
|
|
if (xcount == 15) begin // pre-increment value
|
|
|
|
ycount <= ycount + 1;
|
|
|
|
if (ycount == 15) // pre-increment value
|
|
|
|
state <= WAIT_FOR_VSTART; // done drawing sprite
|
|
|
|
else
|
|
|
|
state <= WAIT_FOR_LOAD; // done drawing this scanline
|
|
|
|
end
|
|
|
|
end
|
|
|
|
endcase
|
|
|
|
end
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
module rotation_selector(rotation, bitmap_num, hmirror, vmirror);
|
2018-02-19 00:19:20 +00:00
|
|
|
|
2018-02-05 20:51:20 +00:00
|
|
|
input [3:0] rotation;
|
2018-02-03 20:20:56 +00:00
|
|
|
output [2:0] bitmap_num;
|
|
|
|
output hmirror, vmirror;
|
|
|
|
|
|
|
|
always @(*)
|
|
|
|
case (rotation[3:2])
|
|
|
|
0: begin
|
|
|
|
bitmap_num = {1'b0, rotation[1:0]};
|
|
|
|
hmirror = 0;
|
|
|
|
vmirror = 0;
|
|
|
|
end
|
|
|
|
1: begin
|
|
|
|
bitmap_num = -rotation[2:0];
|
|
|
|
hmirror = 0;
|
|
|
|
vmirror = 1;
|
|
|
|
end
|
|
|
|
2: begin
|
|
|
|
bitmap_num = {1'b0, rotation[1:0]};
|
|
|
|
hmirror = 1;
|
|
|
|
vmirror = 1;
|
|
|
|
end
|
|
|
|
3: begin
|
|
|
|
bitmap_num = -rotation[2:0];
|
|
|
|
hmirror = 1;
|
|
|
|
vmirror = 0;
|
|
|
|
end
|
|
|
|
endcase
|
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
module tank_controller(clk, reset, hpos, vpos, hsync, vsync,
|
2018-02-05 20:51:20 +00:00
|
|
|
sprite_addr, sprite_bits, gfx,
|
2018-02-07 00:07:40 +00:00
|
|
|
playfield,
|
2018-02-05 20:51:20 +00:00
|
|
|
switch_left, switch_right, switch_up);
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
input clk;
|
|
|
|
input reset;
|
|
|
|
input hsync;
|
|
|
|
input vsync;
|
|
|
|
input [8:0] hpos;
|
|
|
|
input [8:0] vpos;
|
|
|
|
output [7:0] sprite_addr;
|
|
|
|
input [7:0] sprite_bits;
|
|
|
|
output gfx;
|
2018-02-07 00:07:40 +00:00
|
|
|
input playfield;
|
2018-02-05 20:51:20 +00:00
|
|
|
input switch_left, switch_right, switch_up;
|
|
|
|
|
2018-02-07 00:07:40 +00:00
|
|
|
parameter initial_x = 128;
|
|
|
|
parameter initial_y = 120;
|
|
|
|
parameter initial_rot = 0;
|
|
|
|
|
2018-02-05 20:51:20 +00:00
|
|
|
wire hmirror, vmirror;
|
2018-02-03 20:20:56 +00:00
|
|
|
wire busy;
|
2018-02-07 00:07:40 +00:00
|
|
|
wire collision_gfx = gfx && playfield;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
2018-02-05 20:51:20 +00:00
|
|
|
reg [11:0] player_x_fixed;
|
|
|
|
wire [7:0] player_x = player_x_fixed[11:4];
|
|
|
|
wire [3:0] player_x_frac = player_x_fixed[3:0];
|
|
|
|
|
|
|
|
reg [11:0] player_y_fixed;
|
|
|
|
wire [7:0] player_y = player_y_fixed[11:4];
|
|
|
|
wire [3:0] player_y_frac = player_y_fixed[3:0];
|
|
|
|
|
|
|
|
reg [3:0] player_rot;
|
2018-02-07 00:07:40 +00:00
|
|
|
reg [3:0] player_speed;
|
2018-02-05 20:51:20 +00:00
|
|
|
reg [3:0] frame = 0;
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
wire vstart = {1'b0,player_y} == vpos;
|
|
|
|
wire hstart = {1'b0,player_x} == hpos;
|
|
|
|
|
2018-02-15 18:31:32 +00:00
|
|
|
sprite_renderer2 renderer(
|
2018-02-03 20:20:56 +00:00
|
|
|
.clk(clk),
|
|
|
|
.vstart(vstart),
|
|
|
|
.load(hsync),
|
|
|
|
.hstart(hstart),
|
|
|
|
.hmirror(hmirror),
|
|
|
|
.vmirror(vmirror),
|
|
|
|
.rom_addr(sprite_addr[4:0]),
|
|
|
|
.rom_bits(sprite_bits),
|
|
|
|
.gfx(gfx),
|
|
|
|
.busy(busy));
|
|
|
|
|
|
|
|
rotation_selector rotsel(
|
|
|
|
.rotation(player_rot),
|
|
|
|
.bitmap_num(sprite_addr[7:5]),
|
|
|
|
.hmirror(hmirror),
|
|
|
|
.vmirror(vmirror));
|
2018-02-05 20:51:20 +00:00
|
|
|
|
|
|
|
always @(posedge vsync or posedge reset)
|
2018-02-07 00:07:40 +00:00
|
|
|
begin
|
2018-02-05 20:51:20 +00:00
|
|
|
if (reset) begin
|
2018-02-07 00:07:40 +00:00
|
|
|
player_rot <= initial_rot;
|
|
|
|
player_speed <= 0;
|
2018-02-05 20:51:20 +00:00
|
|
|
end else begin
|
|
|
|
frame <= frame + 1;
|
|
|
|
// rotation
|
|
|
|
if (frame[0]) begin
|
|
|
|
if (switch_left)
|
|
|
|
player_rot <= player_rot - 1;
|
|
|
|
else if (switch_right)
|
|
|
|
player_rot <= player_rot + 1;
|
|
|
|
if (switch_up) begin
|
|
|
|
if (player_speed != 15)
|
|
|
|
player_speed <= player_speed + 1;
|
|
|
|
end else
|
|
|
|
player_speed <= 0;
|
|
|
|
end
|
|
|
|
end
|
2018-02-07 00:07:40 +00:00
|
|
|
end
|
|
|
|
|
2018-02-19 00:19:20 +00:00
|
|
|
// set if collision; cleared at vsync
|
|
|
|
reg collision_detected;
|
|
|
|
|
|
|
|
always @(posedge clk)
|
2018-02-21 19:32:11 +00:00
|
|
|
if (vstart)
|
2018-02-19 00:19:20 +00:00
|
|
|
collision_detected <= 0;
|
2018-02-21 19:32:11 +00:00
|
|
|
else if (collision_gfx)
|
|
|
|
collision_detected <= 1;
|
2018-02-19 00:19:20 +00:00
|
|
|
|
|
|
|
// sine lookup (4 bits input, 4 signed bits output)
|
|
|
|
|
|
|
|
function signed [3:0] sin_16x4;
|
|
|
|
input [3:0] in;
|
|
|
|
integer y;
|
|
|
|
case (in[1:0])
|
|
|
|
0: y = 0;
|
|
|
|
1: y = 3;
|
|
|
|
2: y = 5;
|
|
|
|
3: y = 6;
|
|
|
|
endcase
|
|
|
|
case (in[3:2])
|
|
|
|
0: sin_16x4 = 4'(y);
|
|
|
|
1: sin_16x4 = 4'(7-y);
|
|
|
|
2: sin_16x4 = 4'(-y);
|
|
|
|
3: sin_16x4 = 4'(y-7);
|
|
|
|
endcase
|
|
|
|
endfunction
|
2018-02-03 20:20:56 +00:00
|
|
|
|
2018-02-05 20:51:20 +00:00
|
|
|
always @(posedge hsync or posedge reset)
|
|
|
|
if (reset) begin
|
2018-02-07 00:07:40 +00:00
|
|
|
player_x_fixed <= initial_x << 4;
|
|
|
|
player_y_fixed <= initial_y << 4;
|
2018-02-05 20:51:20 +00:00
|
|
|
end else begin
|
|
|
|
// movement
|
2018-02-21 19:32:11 +00:00
|
|
|
if (collision_detected && vpos[3:1] == 0) begin
|
2018-02-07 00:07:40 +00:00
|
|
|
if (vpos[0])
|
|
|
|
player_x_fixed <= player_x_fixed + 12'(sin_16x4(player_rot+8));
|
|
|
|
else
|
|
|
|
player_y_fixed <= player_y_fixed - 12'(sin_16x4(player_rot+12));
|
|
|
|
end else
|
2018-02-05 20:51:20 +00:00
|
|
|
if (vpos < 9'(player_speed)) begin
|
|
|
|
if (vpos[0])
|
|
|
|
player_x_fixed <= player_x_fixed + 12'(sin_16x4(player_rot));
|
|
|
|
else
|
|
|
|
player_y_fixed <= player_y_fixed - 12'(sin_16x4(player_rot+4));
|
|
|
|
end
|
|
|
|
end
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
endmodule
|
|
|
|
|
2018-02-05 20:51:20 +00:00
|
|
|
//TODO: debouncing
|
|
|
|
|
2018-02-07 00:07:40 +00:00
|
|
|
module control_test_top(clk, reset, hsync, vsync, rgb, switches_p1);
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
input clk;
|
|
|
|
input reset;
|
|
|
|
output hsync;
|
|
|
|
output vsync;
|
|
|
|
output [2:0] rgb;
|
2018-02-05 20:51:20 +00:00
|
|
|
input [7:0] switches_p1;
|
|
|
|
|
2018-02-03 20:20:56 +00:00
|
|
|
wire display_on;
|
|
|
|
wire [8:0] hpos;
|
|
|
|
wire [8:0] vpos;
|
|
|
|
|
|
|
|
reg [7:0] paddle_x;
|
|
|
|
reg [7:0] paddle_y;
|
|
|
|
|
|
|
|
hvsync_generator hvsync_gen(
|
|
|
|
.clk(clk),
|
|
|
|
.reset(reset),
|
|
|
|
.hsync(hsync),
|
|
|
|
.vsync(vsync),
|
|
|
|
.display_on(display_on),
|
|
|
|
.hpos(hpos),
|
|
|
|
.vpos(vpos)
|
|
|
|
);
|
|
|
|
|
|
|
|
wire [7:0] tank_sprite_addr;
|
|
|
|
wire [7:0] tank_sprite_bits;
|
|
|
|
|
|
|
|
tank_bitmap tank_bmp(
|
|
|
|
.addr(tank_sprite_addr),
|
|
|
|
.bits(tank_sprite_bits));
|
|
|
|
|
|
|
|
tank_controller tank1(
|
|
|
|
.clk(clk),
|
|
|
|
.reset(reset),
|
|
|
|
.hpos(hpos),
|
|
|
|
.vpos(vpos),
|
|
|
|
.hsync(hsync),
|
|
|
|
.vsync(vsync),
|
|
|
|
.sprite_addr(tank_sprite_addr),
|
|
|
|
.sprite_bits(tank_sprite_bits),
|
2018-02-05 20:51:20 +00:00
|
|
|
.gfx(tank1_gfx),
|
2018-02-07 00:07:40 +00:00
|
|
|
.playfield(playfield_gfx),
|
2018-02-05 20:51:20 +00:00
|
|
|
.switch_left(switches_p1[0]),
|
|
|
|
.switch_right(switches_p1[1]),
|
|
|
|
.switch_up(switches_p1[2])
|
2018-02-03 20:20:56 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
wire tank1_gfx;
|
2018-02-07 00:07:40 +00:00
|
|
|
wire playfield_gfx = hpos[5] && vpos[5];
|
2018-02-03 20:20:56 +00:00
|
|
|
|
|
|
|
wire r = display_on && tank1_gfx;
|
|
|
|
wire g = display_on && tank1_gfx;
|
2018-02-07 00:07:40 +00:00
|
|
|
wire b = display_on && (tank1_gfx || playfield_gfx);
|
2018-02-03 20:20:56 +00:00
|
|
|
assign rgb = {b,g,r};
|
|
|
|
|
|
|
|
endmodule
|
2018-02-15 18:31:32 +00:00
|
|
|
|
|
|
|
`endif
|