`ifndef SPRITE_ROTATION_H `define SPRITE_ROTATION_H `include "hvsync_generator.v" /* tank_bitmap - ROM for tank bitmaps (5 different rotations) sprite_renderer2 - Displays a 16x16 sprite. tank_controller - Handles display and movement for one tank. */ module tank_bitmap(addr, bits); input [7:0] addr; output [7:0] bits; reg [15:0] bitarray[0:255]; assign bits = (addr[0]) ? bitarray[addr>>1][15:8] : bitarray[addr>>1][7:0]; initial begin/*{w:16,h:16,bpw:16,count:5}*/ bitarray['h00] = 16'b11110000000; bitarray['h01] = 16'b11110000000; bitarray['h02] = 16'b1100000000; bitarray['h03] = 16'b1100000000; bitarray['h04] = 16'b111101101111000; bitarray['h05] = 16'b111101101111000; bitarray['h06] = 16'b111111111111000; bitarray['h07] = 16'b111111111111000; bitarray['h08] = 16'b111111111111000; bitarray['h09] = 16'b111111111111000; bitarray['h0a] = 16'b111111111111000; bitarray['h0b] = 16'b111100001111000; bitarray['h0c] = 16'b111100001111000; bitarray['h0d] = 16'b0; bitarray['h0e] = 16'b0; bitarray['h0f] = 16'b0; bitarray['h10] = 16'b111000000000; bitarray['h11] = 16'b1111000000000; bitarray['h12] = 16'b1111000000000; bitarray['h13] = 16'b11000000000; bitarray['h14] = 16'b11101110000; bitarray['h15] = 16'b1101110000; bitarray['h16] = 16'b111101111110000; bitarray['h17] = 16'b111101111111000; bitarray['h18] = 16'b111111111111000; bitarray['h19] = 16'b11111111111000; bitarray['h1a] = 16'b11111111111100; bitarray['h1b] = 16'b11111111111100; bitarray['h1c] = 16'b11111001111100; bitarray['h1d] = 16'b1111001110000; bitarray['h1e] = 16'b1111000000000; bitarray['h1f] = 16'b1100000000000; bitarray['h20] = 16'b0; bitarray['h21] = 16'b0; bitarray['h22] = 16'b11000011000000; bitarray['h23] = 16'b111000111100000; bitarray['h24] = 16'b111101111110000; bitarray['h25] = 16'b1110111111000; bitarray['h26] = 16'b111111111100; bitarray['h27] = 16'b11111111110; bitarray['h28] = 16'b11011111111110; bitarray['h29] = 16'b111111111111100; bitarray['h2a] = 16'b111111111001000; bitarray['h2b] = 16'b11111110000000; bitarray['h2c] = 16'b1111100000000; bitarray['h2d] = 16'b111110000000; bitarray['h2e] = 16'b11110000000; bitarray['h2f] = 16'b1100000000; bitarray['h30] = 16'b0; bitarray['h31] = 16'b0; bitarray['h32] = 16'b110000000; bitarray['h33] = 16'b100001111000000; bitarray['h34] = 16'b1110001111110000; bitarray['h35] = 16'b1111010111111100; bitarray['h36] = 16'b1111111111111111; bitarray['h37] = 16'b1111111111111; bitarray['h38] = 16'b11111111110; bitarray['h39] = 16'b101111111110; bitarray['h3a] = 16'b1111111101100; bitarray['h3b] = 16'b11111111000000; bitarray['h3c] = 16'b1111111100000; bitarray['h3d] = 16'b11111110000; bitarray['h3e] = 16'b111100000; bitarray['h3f] = 16'b1100000; bitarray['h40] = 16'b0; bitarray['h41] = 16'b0; bitarray['h42] = 16'b0; bitarray['h43] = 16'b111111111000; bitarray['h44] = 16'b111111111000; bitarray['h45] = 16'b111111111000; bitarray['h46] = 16'b111111111000; bitarray['h47] = 16'b1100001111100000; bitarray['h48] = 16'b1111111111100000; bitarray['h49] = 16'b1111111111100000; bitarray['h4a] = 16'b1100001111100000; bitarray['h4b] = 16'b111111111000; bitarray['h4c] = 16'b111111111000; bitarray['h4d] = 16'b111111111000; bitarray['h4e] = 16'b111111111000; bitarray['h4f] = 16'b0; end endmodule // 16x16 sprite renderer that supports rotation module sprite_renderer2(clk, vstart, load, hstart, rom_addr, rom_bits, hmirror, vmirror, gfx, busy); input clk, vstart, load, hstart; input hmirror, vmirror; output reg [4:0] rom_addr; input [7:0] rom_bits; output reg gfx; output busy; assign busy = state != WAIT_FOR_VSTART; 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 // converts 0..15 rotation value to bitmap index / mirror bits module rotation_selector(rotation, bitmap_num, hmirror, vmirror); input [3:0] rotation; // angle (0..15) output reg [2:0] bitmap_num; // bitmap index (0..4) output reg hmirror, vmirror; // horiz & vert mirror bits always @(*) case (rotation[3:2]) // 4 quadrants 0: begin // 0..3 -> 0..3 bitmap_num = {1'b0, rotation[1:0]}; hmirror = 0; vmirror = 0; end 1: begin // 4..7 -> 4..1 bitmap_num = -rotation[2:0]; hmirror = 0; vmirror = 1; end 2: begin // 8-11 -> 0..3 bitmap_num = {1'b0, rotation[1:0]}; hmirror = 1; vmirror = 1; end 3: begin // 12-15 -> 4..1 bitmap_num = -rotation[2:0]; hmirror = 1; vmirror = 0; end endcase endmodule // tank controller module -- handles rendering and movement module tank_controller(clk, reset, hpos, vpos, hsync, vsync, sprite_addr, sprite_bits, gfx, playfield, switch_left, switch_right, switch_up); 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; input playfield; input switch_left, switch_right, switch_up; parameter initial_x = 128; parameter initial_y = 120; parameter initial_rot = 0; wire hmirror, vmirror; wire busy; wire collision_gfx = gfx && playfield; 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; reg [3:0] player_speed; reg [3:0] frame = 0; wire vstart = {1'b0,player_y} == vpos; wire hstart = {1'b0,player_x} == hpos; sprite_renderer2 renderer( .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)); always @(posedge vsync or posedge reset) begin if (reset) begin player_rot <= initial_rot; player_speed <= 0; end else begin frame <= frame + 1; // increment frame counter if (frame[0]) begin // only update every other frame if (switch_left) player_rot <= player_rot - 1; // turn left else if (switch_right) player_rot <= player_rot + 1; // turn right if (switch_up) begin if (player_speed != 15) // max accel player_speed <= player_speed + 1; end else player_speed <= 0; // stop end end end // set if collision; cleared at vsync reg collision_detected; always @(posedge clk) if (vstart) collision_detected <= 0; else if (collision_gfx) collision_detected <= 1; // sine lookup (4 bits input, 4 signed bits output) function signed [3:0] sin_16x4; input [3:0] in; // input angle 0..15 integer y; case (in[1:0]) // 4 values per quadrant 0: y = 0; 1: y = 3; 2: y = 5; 3: y = 6; endcase case (in[3:2]) // 4 quadrants 0: sin_16x4 = 4'(y); 1: sin_16x4 = 4'(7-y); 2: sin_16x4 = 4'(-y); 3: sin_16x4 = 4'(y-7); endcase endfunction always @(posedge hsync or posedge reset) if (reset) begin // set initial position player_x_fixed <= initial_x << 4; player_y_fixed <= initial_y << 4; end else begin // collision detected? move backwards if (collision_detected && vpos[3:1] == 0) begin 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 // forward movement 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 endmodule //TODO: debouncing module control_test_top(clk, reset, hsync, vsync, rgb, switches_p1); input clk; input reset; output hsync; output vsync; output [2:0] rgb; input [7:0] switches_p1; 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), .gfx(tank1_gfx), .playfield(playfield_gfx), .switch_left(switches_p1[0]), .switch_right(switches_p1[1]), .switch_up(switches_p1[2]) ); wire tank1_gfx; wire playfield_gfx = hpos[5] && vpos[5]; wire r = display_on && tank1_gfx; wire g = display_on && tank1_gfx; wire b = display_on && (tank1_gfx || playfield_gfx); assign rgb = {b,g,r}; endmodule `endif