mirror of
https://github.com/MiSTer-devel/Gameboy_MiSTer.git
synced 2026-05-24 03:03:25 +00:00
PPU timing rework
This commit is contained in:
@@ -6,9 +6,7 @@ set_global_assignment -name VHDL_FILE rtl/boot_rom.vhd
|
||||
set_global_assignment -name SYSTEMVERILOG_FILE rtl/cheatcodes.sv
|
||||
set_global_assignment -name VERILOG_FILE rtl/video.v
|
||||
set_global_assignment -name VERILOG_FILE rtl/timer.v
|
||||
set_global_assignment -name VERILOG_FILE rtl/sprite_sort.v
|
||||
set_global_assignment -name VERILOG_FILE rtl/sprites.v
|
||||
set_global_assignment -name VERILOG_FILE rtl/sprite.v
|
||||
set_global_assignment -name VERILOG_FILE rtl/lcd.v
|
||||
set_global_assignment -name VHDL_FILE rtl/gbc_snd.vhd
|
||||
set_global_assignment -name VERILOG_FILE rtl/gb.v
|
||||
|
||||
26
rtl/gb.v
26
rtl/gb.v
@@ -325,6 +325,8 @@ wire irq_n = !(ie_r & if_r);
|
||||
|
||||
reg [4:0] if_r;
|
||||
reg [4:0] ie_r; // writing $ffff sets the irq enable mask
|
||||
|
||||
reg old_vblank_irq, old_video_irq, old_timer_irq, old_serial_irq;
|
||||
always @(negedge clk_cpu) begin //negedge to trigger interrupt earlier
|
||||
reg old_ack = 0;
|
||||
|
||||
@@ -333,19 +335,22 @@ always @(negedge clk_cpu) begin //negedge to trigger interrupt earlier
|
||||
if_r <= 5'h00;
|
||||
end
|
||||
|
||||
// rising edge on vs
|
||||
// vsD <= vs;
|
||||
// vsD2 <= vsD;
|
||||
if(vblank_irq) if_r[0] <= 1'b1;
|
||||
// "When an interrupt signal changes from low to high,
|
||||
// then the corresponding bit in the IF register becomes set."
|
||||
old_vblank_irq <= vblank_irq;
|
||||
if(~old_vblank_irq & vblank_irq) if_r[0] <= 1'b1;
|
||||
|
||||
// video irq already is a 1 clock event
|
||||
if(video_irq) if_r[1] <= 1'b1;
|
||||
old_video_irq <= video_irq;
|
||||
if(~old_video_irq & video_irq) if_r[1] <= 1'b1;
|
||||
|
||||
// timer_irq already is a 1 clock event
|
||||
if(timer_irq) if_r[2] <= 1'b1;
|
||||
old_timer_irq <= timer_irq;
|
||||
if(~old_timer_irq & timer_irq) if_r[2] <= 1'b1;
|
||||
|
||||
// serial irq already is a 1 clock event
|
||||
if(serial_irq) if_r[3] <= 1'b1;
|
||||
old_serial_irq <= serial_irq;
|
||||
if(~old_serial_irq & serial_irq) if_r[3] <= 1'b1;
|
||||
|
||||
// falling edge on any input line P10..P13
|
||||
inputD <= {joy_p4, joy_p5};
|
||||
@@ -406,9 +411,10 @@ wire [7:0] dma_data = dma_sel_iram?iram_do:dma_sel_vram?(isGBC&&vram_bank)?vram1
|
||||
|
||||
|
||||
video video (
|
||||
.reset ( reset ),
|
||||
.clk ( clk ),
|
||||
.clk_reg ( clk_cpu ), //can be 2x in cgb double speed mode
|
||||
.reset ( reset ),
|
||||
.clk ( clk_sys ),
|
||||
.ce ( ce ), // 4Mhz
|
||||
.ce_cpu ( ce_cpu ), //can be 2x in cgb double speed mode
|
||||
.isGBC ( isGBC ),
|
||||
.isGBC_game ( isGBC_game|boot_rom_enabled ), //enable GBC mode during bootstrap rom
|
||||
|
||||
|
||||
115
rtl/sprite.v
115
rtl/sprite.v
@@ -1,115 +0,0 @@
|
||||
//
|
||||
// sprite.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 sprite (
|
||||
input clk,
|
||||
input size16,
|
||||
input isGBC_game,
|
||||
input [7:0] sprite_index,
|
||||
|
||||
input [7:0] v_cnt,
|
||||
input [7:0] h_cnt,
|
||||
|
||||
output [7:0] x,
|
||||
|
||||
// interface to read pixel data from memory
|
||||
output [10:0] addr,
|
||||
input [1:0] ds,
|
||||
input [7:0] data,
|
||||
input [7:0] data_1,
|
||||
|
||||
output pixel_active,
|
||||
output pixel_cmap,
|
||||
output pixel_prio,
|
||||
output [1:0] pixel_data,
|
||||
|
||||
//gbc
|
||||
output [2:0] pixel_cmap_gbc,
|
||||
|
||||
input oam_wr,
|
||||
input [1:0] oam_addr,
|
||||
input [7:0] oam_di,
|
||||
output [7:0] oam_do
|
||||
);
|
||||
|
||||
// x position for priority detection. Invisible sprites are far to the right and
|
||||
// have minimum priority
|
||||
assign x = v_visible?isGBC_game?sprite_index:x_pos:8'hff;
|
||||
|
||||
// register used to store pixel data for current line
|
||||
reg [7:0] data0;
|
||||
reg [7:0] data1;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if(ds[0]) data0 <= flags[3]&&isGBC_game?data_1:data;
|
||||
if(ds[1]) data1 <= flags[3]&&isGBC_game?data_1:data;
|
||||
end
|
||||
|
||||
wire [7:0] height = size16?8'd16:8'd8;
|
||||
|
||||
wire v_visible = (v_cnt + 8'd16 >= y_pos) && (v_cnt + 8'd16 < y_pos + height);
|
||||
wire visible = v_visible && (h_cnt + 8'd8 >= x_pos) && (h_cnt < x_pos);
|
||||
|
||||
// x position within sprite, mirror horizontally if required
|
||||
wire [7:0] col_n = h_cnt - x_pos;
|
||||
wire [2:0] col = flags[5]?col_n[2:0]:~col_n[2:0];
|
||||
|
||||
assign pixel_data = { data1[col], data0[col] };
|
||||
assign pixel_active = (pixel_data != 0) && visible;
|
||||
|
||||
// y position within sprite, mirror vertically if required
|
||||
wire [7:0] row_n = v_cnt - y_pos;
|
||||
wire [3:0] row = flags[6]?~row_n[3:0]:row_n[3:0];
|
||||
|
||||
// 16 pixel tall sprites use one more rwo counter bit and the lsb
|
||||
// of the tile index is ignored
|
||||
wire [10:0] addr8 = { tile , row[2:0]};
|
||||
wire [10:0] addr16 = { tile[7:1] , row};
|
||||
assign addr = size16?addr16:addr8;
|
||||
|
||||
assign pixel_cmap = flags[4];
|
||||
assign pixel_prio = flags[7];
|
||||
|
||||
assign pixel_cmap_gbc = flags[2:0];
|
||||
|
||||
reg [7:0] y_pos;
|
||||
reg [7:0] x_pos;
|
||||
reg [7:0] tile;
|
||||
reg [7:0] flags;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if(oam_wr) begin
|
||||
case(oam_addr)
|
||||
0: y_pos <= oam_di;
|
||||
1: x_pos <= oam_di;
|
||||
2: tile <= oam_di;
|
||||
3: flags <= oam_di;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
assign oam_do =
|
||||
(oam_addr == 0)?y_pos:
|
||||
(oam_addr == 1)?x_pos:
|
||||
(oam_addr == 2)?tile:
|
||||
flags;
|
||||
|
||||
endmodule
|
||||
@@ -1,100 +0,0 @@
|
||||
//
|
||||
// sprite_sort.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 sprite_sort #(
|
||||
parameter WIDTH = 40
|
||||
)(
|
||||
// system signals
|
||||
input clk,
|
||||
input load,
|
||||
|
||||
// sort
|
||||
input [8*WIDTH-1:0] x,
|
||||
output [6*WIDTH-1:0] idx
|
||||
);
|
||||
|
||||
wire [7:0] in [WIDTH-1:0];
|
||||
|
||||
generate
|
||||
genvar i;
|
||||
|
||||
// map 1d input array onto 2d work array
|
||||
// and 2d result array into 1d output array
|
||||
for(i=0;i<WIDTH;i=i+1) begin : input_map
|
||||
assign in[i] = x[(8*i)+7:8*i];
|
||||
assign idx[(6*i)+5:6*i] = index[i];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
reg [5:0] index [WIDTH-1:0];
|
||||
reg [7:0] values [WIDTH-1:0];
|
||||
wire [WIDTH/2-1:0] swap0;
|
||||
wire [7:0] int_val [WIDTH-1:0];
|
||||
wire [5:0] int_idx [WIDTH-1:0];
|
||||
wire [WIDTH/2-2:0] swap1;
|
||||
wire [7:0] sort_val [WIDTH-1:0];
|
||||
wire [5:0] sort_idx [WIDTH-1:0];
|
||||
|
||||
// sorting takes 10 clock cycles
|
||||
reg [3:0] cnt;
|
||||
always @(posedge clk) begin
|
||||
if(load) cnt <= 4'd0;
|
||||
if(cnt != 10) cnt <= cnt + 4'd1;
|
||||
end
|
||||
|
||||
generate
|
||||
// 1st stage
|
||||
for(i=0;i<WIDTH/2;i=i+1) begin : stage1
|
||||
assign swap0[i] = values[2*i+0] > values[2*i+1];
|
||||
assign int_val[2*i+0] = swap0[i]?values[2*i+1]:values[2*i+0];
|
||||
assign int_val[2*i+1] = swap0[i]?values[2*i+0]:values[2*i+1];
|
||||
assign int_idx[2*i+0] = swap0[i]?index[2*i+1]:index[2*i+0];
|
||||
assign int_idx[2*i+1] = swap0[i]?index[2*i+0]:index[2*i+1];
|
||||
end
|
||||
|
||||
// 2nd stage
|
||||
assign sort_val[0] = int_val[0];
|
||||
assign sort_idx[0] = int_idx[0];
|
||||
assign sort_val[WIDTH-1] = int_val[WIDTH-1];
|
||||
assign sort_idx[WIDTH-1] = int_idx[WIDTH-1];
|
||||
for(i=0;i<WIDTH/2-1;i=i+1) begin : stage4
|
||||
assign swap1[i] = int_val[2*i+1] > int_val[2*i+2];
|
||||
assign sort_val[2*i+1] = swap1[i]?int_val[2*i+2]:int_val[2*i+1];
|
||||
assign sort_val[2*i+2] = swap1[i]?int_val[2*i+1]:int_val[2*i+2];
|
||||
assign sort_idx[2*i+1] = swap1[i]?int_idx[2*i+2]:int_idx[2*i+1];
|
||||
assign sort_idx[2*i+2] = swap1[i]?int_idx[2*i+1]:int_idx[2*i+2];
|
||||
end
|
||||
|
||||
for(i=0;i<WIDTH;i=i+1) begin : advance
|
||||
always @(posedge clk) begin
|
||||
if(load) begin
|
||||
values[i] <= in[i];
|
||||
index[i] <= i[5:0];
|
||||
end else begin
|
||||
values[i] <= sort_val[i];
|
||||
index[i] <= sort_idx[i];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
323
rtl/sprites.v
323
rtl/sprites.v
@@ -21,200 +21,187 @@
|
||||
|
||||
module sprites (
|
||||
input clk,
|
||||
input clk_reg,
|
||||
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,
|
||||
|
||||
// pixel output
|
||||
output pixel_active, // current pixel
|
||||
output [1:0] pixel_data,
|
||||
output pixel_cmap,
|
||||
output pixel_prio,
|
||||
|
||||
//gbc
|
||||
output [2:0] pixel_cmap_gbc,
|
||||
input sprite_fetch_done,
|
||||
output sprite_fetch,
|
||||
|
||||
input sort,
|
||||
input [3:0] index, // index of sprite which video wants to read data for
|
||||
output [10:0] addr,
|
||||
input [1:0] dvalid,
|
||||
input [7:0] data,
|
||||
input [7:0] data1,
|
||||
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,
|
||||
input [7:0] oam_addr_in,
|
||||
input [7:0] oam_di,
|
||||
output [7:0] oam_do
|
||||
);
|
||||
|
||||
localparam SPRITES = 40;
|
||||
localparam SPRITES_PER_LINE = 10;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// ---------------------------- priority sorting --------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// sprites have priority from left to right and the leftmost 10 are
|
||||
// being displayed. We thus need to sort them
|
||||
wire [SPRITES*8-1:0] sprite_x;
|
||||
wire [SPRITES*6-1:0] sprite_idx;
|
||||
wire [7:0] oam_addr = dma_active ? oam_addr_in :
|
||||
oam_eval ? oam_spr_addr :
|
||||
oam_fetch ? oam_fetch_addr :
|
||||
oam_addr_in;
|
||||
|
||||
sprite_sort #(.WIDTH(SPRITES)) sprite_sort (
|
||||
.clk ( clk ),
|
||||
.load ( sort ), // begin of oam phase
|
||||
.x ( sprite_x ),
|
||||
.idx ( sprite_idx )
|
||||
);
|
||||
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;
|
||||
|
||||
wire [SPRITES-1:0] sprite_pixel_active;
|
||||
wire [SPRITES-1:0] sprite_pixel_cmap;
|
||||
wire [SPRITES-1:0] sprite_pixel_prio;
|
||||
wire [1:0] sprite_pixel_data [SPRITES-1:0];
|
||||
|
||||
wire [10:0] sprite_addr [SPRITES-1:0];
|
||||
wire [7:0] sprite_oam_do [SPRITES-1:0];
|
||||
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
|
||||
|
||||
assign oam_do = sprite_oam_do[oam_addr[7:2]];
|
||||
|
||||
// address where the sprite wants to read data from
|
||||
wire [5:0] sprite_idx_array [SPRITES-1:0];
|
||||
wire [5:0] padded_index = {2'd0,index};
|
||||
wire [5:0] prio_index = sprite_idx_array[padded_index];
|
||||
assign addr = sprite_addr[prio_index];
|
||||
|
||||
//gbc
|
||||
wire [2:0] sprite_pixel_cmap_gbc [SPRITES-1:0];
|
||||
wire sprite_tile_vbank [SPRITES-1:0];
|
||||
|
||||
generate
|
||||
genvar i;
|
||||
for(i=0;i<SPRITES;i=i+1) begin : spr
|
||||
// map 1d array to 2d array
|
||||
assign sprite_idx_array[i] = sprite_idx[6*i+5:6*i];
|
||||
|
||||
sprite sprite (
|
||||
.clk ( clk_reg ),
|
||||
.size16 ( size16 ),
|
||||
.isGBC_game ( isGBC_game ),
|
||||
|
||||
.sprite_index ( i ),
|
||||
|
||||
.v_cnt ( v_cnt ),
|
||||
.h_cnt ( h_cnt ),
|
||||
.x ( sprite_x[(8*i)+7:(8*i)] ),
|
||||
|
||||
.addr ( sprite_addr[i] ),
|
||||
.ds ( (prio_index == i)?dvalid:2'b00),
|
||||
.data ( data ),
|
||||
.data_1 ( data1 ),
|
||||
|
||||
.pixel_cmap ( sprite_pixel_cmap[i] ),
|
||||
.pixel_prio ( sprite_pixel_prio[i] ),
|
||||
.pixel_active ( sprite_pixel_active[i] ),
|
||||
.pixel_data ( sprite_pixel_data[i] ),
|
||||
|
||||
|
||||
//gbc
|
||||
.pixel_cmap_gbc ( sprite_pixel_cmap_gbc[i] ),
|
||||
|
||||
.oam_wr ( oam_wr && (oam_addr[7:2] == i) ),
|
||||
.oam_addr ( oam_addr[1:0] ),
|
||||
.oam_di ( oam_di ),
|
||||
.oam_do ( sprite_oam_do[i] )
|
||||
);
|
||||
oam_q <= oam_data[oam_addr];
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// ---------------------------- priority display --------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
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];
|
||||
|
||||
// only the 10 leftmost sprites are potentially being displayed
|
||||
// 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;
|
||||
|
||||
// get the indices of the 10 leftmost sprites
|
||||
wire [5:0] spr0 = sprite_idx_array[0];
|
||||
wire [5:0] spr1 = sprite_idx_array[1];
|
||||
wire [5:0] spr2 = sprite_idx_array[2];
|
||||
wire [5:0] spr3 = sprite_idx_array[3];
|
||||
wire [5:0] spr4 = sprite_idx_array[4];
|
||||
wire [5:0] spr5 = sprite_idx_array[5];
|
||||
wire [5:0] spr6 = sprite_idx_array[6];
|
||||
wire [5:0] spr7 = sprite_idx_array[7];
|
||||
wire [5:0] spr8 = sprite_idx_array[8];
|
||||
wire [5:0] spr9 = sprite_idx_array[9];
|
||||
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);
|
||||
|
||||
// if any of these is active then the current pixel is being driven by
|
||||
// the sprite engine
|
||||
assign pixel_active =
|
||||
sprite_pixel_active[spr0] ||
|
||||
sprite_pixel_active[spr1] ||
|
||||
sprite_pixel_active[spr2] ||
|
||||
sprite_pixel_active[spr3] ||
|
||||
sprite_pixel_active[spr4] ||
|
||||
sprite_pixel_active[spr5] ||
|
||||
sprite_pixel_active[spr6] ||
|
||||
sprite_pixel_active[spr7] ||
|
||||
sprite_pixel_active[spr8] ||
|
||||
sprite_pixel_active[spr9];
|
||||
assign oam_eval_end = (spr_index == 6'd40);
|
||||
|
||||
// get the pixel information of the leftmost sprite
|
||||
assign pixel_data =
|
||||
sprite_pixel_active[spr0]?sprite_pixel_data[spr0]:
|
||||
sprite_pixel_active[spr1]?sprite_pixel_data[spr1]:
|
||||
sprite_pixel_active[spr2]?sprite_pixel_data[spr2]:
|
||||
sprite_pixel_active[spr3]?sprite_pixel_data[spr3]:
|
||||
sprite_pixel_active[spr4]?sprite_pixel_data[spr4]:
|
||||
sprite_pixel_active[spr5]?sprite_pixel_data[spr5]:
|
||||
sprite_pixel_active[spr6]?sprite_pixel_data[spr6]:
|
||||
sprite_pixel_active[spr7]?sprite_pixel_data[spr7]:
|
||||
sprite_pixel_active[spr8]?sprite_pixel_data[spr8]:
|
||||
sprite_pixel_active[spr9]?sprite_pixel_data[spr9]:
|
||||
2'b00;
|
||||
|
||||
// get the colormap of the leftmost sprite
|
||||
assign pixel_cmap =
|
||||
sprite_pixel_active[spr0]?sprite_pixel_cmap[spr0]:
|
||||
sprite_pixel_active[spr1]?sprite_pixel_cmap[spr1]:
|
||||
sprite_pixel_active[spr2]?sprite_pixel_cmap[spr2]:
|
||||
sprite_pixel_active[spr3]?sprite_pixel_cmap[spr3]:
|
||||
sprite_pixel_active[spr4]?sprite_pixel_cmap[spr4]:
|
||||
sprite_pixel_active[spr5]?sprite_pixel_cmap[spr5]:
|
||||
sprite_pixel_active[spr6]?sprite_pixel_cmap[spr6]:
|
||||
sprite_pixel_active[spr7]?sprite_pixel_cmap[spr7]:
|
||||
sprite_pixel_active[spr8]?sprite_pixel_cmap[spr8]:
|
||||
sprite_pixel_active[spr9]?sprite_pixel_cmap[spr9]:
|
||||
1'b0;
|
||||
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
|
||||
|
||||
// get the colormap of the leftmost sprite gbc
|
||||
assign pixel_cmap_gbc =
|
||||
sprite_pixel_active[spr0]?sprite_pixel_cmap_gbc[spr0]:
|
||||
sprite_pixel_active[spr1]?sprite_pixel_cmap_gbc[spr1]:
|
||||
sprite_pixel_active[spr2]?sprite_pixel_cmap_gbc[spr2]:
|
||||
sprite_pixel_active[spr3]?sprite_pixel_cmap_gbc[spr3]:
|
||||
sprite_pixel_active[spr4]?sprite_pixel_cmap_gbc[spr4]:
|
||||
sprite_pixel_active[spr5]?sprite_pixel_cmap_gbc[spr5]:
|
||||
sprite_pixel_active[spr6]?sprite_pixel_cmap_gbc[spr6]:
|
||||
sprite_pixel_active[spr7]?sprite_pixel_cmap_gbc[spr7]:
|
||||
sprite_pixel_active[spr8]?sprite_pixel_cmap_gbc[spr8]:
|
||||
sprite_pixel_active[spr9]?sprite_pixel_cmap_gbc[spr9]:
|
||||
1'b0;
|
||||
if (spr_index < 6'd40) begin
|
||||
if (sprite_cycle) spr_index <= spr_index + 1'b1;
|
||||
|
||||
// get the priority of the leftmost sprite
|
||||
assign pixel_prio =
|
||||
sprite_pixel_active[spr0]?sprite_pixel_prio[spr0]:
|
||||
sprite_pixel_active[spr1]?sprite_pixel_prio[spr1]:
|
||||
sprite_pixel_active[spr2]?sprite_pixel_prio[spr2]:
|
||||
sprite_pixel_active[spr3]?sprite_pixel_prio[spr3]:
|
||||
sprite_pixel_active[spr4]?sprite_pixel_prio[spr4]:
|
||||
sprite_pixel_active[spr5]?sprite_pixel_prio[spr5]:
|
||||
sprite_pixel_active[spr6]?sprite_pixel_prio[spr6]:
|
||||
sprite_pixel_active[spr7]?sprite_pixel_prio[spr7]:
|
||||
sprite_pixel_active[spr8]?sprite_pixel_prio[spr8]:
|
||||
sprite_pixel_active[spr9]?sprite_pixel_prio[spr9]:
|
||||
1'b0;
|
||||
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
|
||||
|
||||
endmodule
|
||||
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
|
||||
864
rtl/video.v
864
rtl/video.v
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user