Files
Gameboy_MiSTer/rtl/sprites.v
2020-06-22 11:05:08 +02:00

207 lines
5.2 KiB
Verilog

//
// sprites.v
//
// Gameboy for the MIST board https://github.com/mist-devel
//
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
//
// This source file is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
module sprites (
input clk,
input ce,
input ce_cpu,
input size16,
input isGBC_game,
input lcd_on,
// pixel position input which the current pixel is generated for
input [7:0] v_cnt,
input [7:0] h_cnt,
input sprite_fetch_done,
output sprite_fetch,
input oam_eval,
input oam_fetch,
input oam_eval_reset,
output [10:0] sprite_addr,
output reg [7:0] sprite_attr,
output [3:0] sprite_index,
output oam_eval_end,
// oam memory interface
input dma_active,
input oam_wr,
input [7:0] oam_addr_in,
input [7:0] oam_di,
output [7:0] oam_do
);
localparam SPRITES_PER_LINE = 10;
wire [7:0] oam_addr = dma_active ? oam_addr_in :
oam_eval ? oam_spr_addr :
oam_fetch ? oam_fetch_addr :
oam_addr_in;
reg [7:0] oam_spr_addr;
wire valid_oam_addr = (oam_addr[7:4] < 4'hA); // $FEA0 - $FEFF unused range
assign oam_do = dma_active ? 8'hFF : valid_oam_addr ? oam_q : 8'd0;
reg [7:0] oam_data[0:159];
reg [7:0] oam_q;
always @(posedge clk) begin
if (ce_cpu) begin
if(oam_wr && valid_oam_addr) begin
oam_data[oam_addr] <= oam_di;
end
end
oam_q <= oam_data[oam_addr];
end
reg [7:0] sprite_x[0:SPRITES_PER_LINE-1];
reg [3:0] sprite_y[0:SPRITES_PER_LINE-1];
reg [5:0] sprite_no[0:SPRITES_PER_LINE-1];
// OAM evaluation. Get the first 10 sprites on the current line.
reg [5:0] spr_index; // 40 sprites
reg [3:0] sprite_cnt;
reg sprite_cycle;
reg [7:0] spr_y;
wire [7:0] spr_height = size16 ? 8'd16 : 8'd8;
wire sprite_on_line = (v_cnt + 8'd16 >= spr_y) && (v_cnt + 8'd16 < spr_y + spr_height);
assign oam_eval_end = (spr_index == 6'd40);
reg old_fetch_done;
always @(posedge clk) begin
if (!lcd_on) begin
sprite_cnt <= 0;
spr_index <= 0;
sprite_cycle <= 0;
oam_spr_addr <= 0;
end else if (ce) begin
if (oam_eval) begin
if (spr_index < 6'd40) begin
if (sprite_cycle) spr_index <= spr_index + 1'b1;
if (sprite_cnt < SPRITES_PER_LINE) begin
if (~sprite_cycle) begin
spr_y <= oam_do;
oam_spr_addr <= {spr_index,2'b01};
end else begin
if (sprite_on_line) begin
sprite_no[sprite_cnt] <= spr_index;
sprite_x[sprite_cnt] <= oam_do;
sprite_y[sprite_cnt] <= v_cnt[3:0] - spr_y[3:0];
sprite_cnt <= sprite_cnt + 1'b1;
end
oam_spr_addr <= {spr_index+1'b1, 2'b00};
end
end
end
sprite_cycle <= ~sprite_cycle;
end
if (oam_eval_reset) begin
sprite_cnt <= 0;
spr_index <= 0;
sprite_cycle <= 0;
oam_spr_addr <= 0;
end
// Set X-position to FF after fetching the sprite to prevent fetching it again.
old_fetch_done <= sprite_fetch_done;
if (~old_fetch_done & sprite_fetch_done) begin
if (sprite_x_matches[0]) sprite_x[0] <= 8'hFF;
else if (sprite_x_matches[1]) sprite_x[1] <= 8'hFF;
else if (sprite_x_matches[2]) sprite_x[2] <= 8'hFF;
else if (sprite_x_matches[3]) sprite_x[3] <= 8'hFF;
else if (sprite_x_matches[4]) sprite_x[4] <= 8'hFF;
else if (sprite_x_matches[5]) sprite_x[5] <= 8'hFF;
else if (sprite_x_matches[6]) sprite_x[6] <= 8'hFF;
else if (sprite_x_matches[7]) sprite_x[7] <= 8'hFF;
else if (sprite_x_matches[8]) sprite_x[8] <= 8'hFF;
else if (sprite_x_matches[9]) sprite_x[9] <= 8'hFF;
end
end
end
// Sprite fetching
wire [0:9] sprite_x_matches = {
sprite_x[0] == h_cnt,
sprite_x[1] == h_cnt,
sprite_x[2] == h_cnt,
sprite_x[3] == h_cnt,
sprite_x[4] == h_cnt,
sprite_x[5] == h_cnt,
sprite_x[6] == h_cnt,
sprite_x[7] == h_cnt,
sprite_x[8] == h_cnt,
sprite_x[9] == h_cnt
};
assign sprite_fetch = |sprite_x_matches & oam_fetch;
wire [3:0] active_sprite =
sprite_x_matches[0] ? 4'd0 :
sprite_x_matches[1] ? 4'd1 :
sprite_x_matches[2] ? 4'd2 :
sprite_x_matches[3] ? 4'd3 :
sprite_x_matches[4] ? 4'd4 :
sprite_x_matches[5] ? 4'd5 :
sprite_x_matches[6] ? 4'd6 :
sprite_x_matches[7] ? 4'd7 :
sprite_x_matches[8] ? 4'd8 :
4'd9;
assign sprite_index = active_sprite;
wire [5:0] oam_fetch_index = sprite_no[active_sprite];
reg [3:0] row;
reg [7:0] tile_no;
reg oam_fetch_cycle;
wire [7:0] oam_fetch_addr = {oam_fetch_index, 1'b1, oam_fetch_cycle};
assign sprite_addr = size16 ? {tile_no[7:1],row} : {tile_no,row[2:0]};
always @(posedge clk) begin
if (ce) begin
if (sprite_fetch) begin
if (~oam_fetch_cycle) begin
tile_no <= oam_do;
end else begin
sprite_attr <= oam_do;
row <= oam_do[6] ? ~sprite_y[active_sprite] : sprite_y[active_sprite];
end
oam_fetch_cycle <= ~oam_fetch_cycle;
end else begin
oam_fetch_cycle <= 0;
end
end
end
endmodule