Add option for extra sprites

Maximum of 6 extra sprites per line
Extra sprites are fetched during mode2 to not break mode3 timing
This commit is contained in:
paulb-nl
2025-06-21 17:41:30 +02:00
parent d11c2bdd3b
commit 4f937979ec
8 changed files with 477 additions and 56 deletions

View File

@@ -210,7 +210,7 @@ assign DDRAM_WE = 0;
// 0 1 2 3 4 5 6
// 01234567890123456789012345678901 23456789012345678901234567890123
// 0123456789ABCDEFGHIJKLMNOPQRSTUV 0123456789ABCDEFGHIJKLMNOPQRSTUV
// XXXXXXXXXXXXXXXXXXXXXXXX X XXXXXXX
// XXXXXXXXXXXXXXXXXXXXXXXX X XXXXXXXX
`include "build_id.v"
localparam CONF_STR = {
@@ -228,16 +228,17 @@ localparam CONF_STR = {
"P1,Audio & Video;",
"P1-;",
"P1O[44],Extra sprites,No,Yes;",
"P1ON,Seperator Line,Off,On;",
"P1OC,Inverted color,No,Yes;",
"P1O12,Custom Palette,Off,Auto,On;",
"h1P1FC3,GBP,Load Palette;",
"d4P1OU,GBC Colors,Corrected,Raw;",
"P1O5,Sync Video,Off,On;",
"P1-;",
"P1O34,Aspect ratio,Original,Full Screen,[ARC1],[ARC2];",
"P1OLM,Scale,Normal,V-Integer,Narrower HV-Integer,Wider HV-Integer;",
"P1OIK,Scandoubler Fx,None,HQ2x,CRT 25%,CRT 50%,CRT 75%;",
"d4P1OU,GBC Colors,Corrected,Raw;",
"P1O5,Sync Video,Off,On;",
"P1-;",
"P1O78,Stereo mix,none,25%,50%,100%;",
"P1O[43],Audio mode,Accurate,No Pops;",
@@ -637,12 +638,14 @@ gb gb1 (
.clk_sys ( clk_sys ),
.ce ( ce1_cpu ), // the whole gameboy runs on 4mhnz
.ce_n ( ce1_cpu_n ), // 4MHz falling edge clock enable
.ce_2x ( ce1_cpu2x ), // ~8MHz in dualspeed mode (GBC)
.isGBC ( isGBC ),
.real_cgb_boot ( using_real_cgb_bios ),
.isSGB ( 1'b0 ),
.megaduck ( megaduck ),
.extra_spr_en( status[44] ),
.joy_p54 ( joy1_p54 ),
.joy_din ( joy1_do ),
@@ -826,6 +829,7 @@ gb gb2 (
.clk_sys ( clk_sys ),
.ce ( ce2_cpu ), // the whole gameboy runs on 4mhnz
.ce_n ( ce2_cpu_n ), // 4MHz falling edge clock enable
.ce_2x ( ce2_cpu2x ), // ~8MHz in dualspeed mode (GBC)
@@ -833,6 +837,7 @@ gb gb2 (
.real_cgb_boot ( using_real_cgb_bios ),
.isSGB ( 1'b0 ),
.megaduck ( megaduck ),
.extra_spr_en( status[44] ),
.joy_p54 ( joy2_p54 ),
.joy_din ( joy2_do ),
@@ -1050,8 +1055,8 @@ video_freak video_freak
//////////////////////////////// CE ////////////////////////////////////
wire ce1_cpu, ce1_cpu2x;
wire ce2_cpu, ce2_cpu2x;
wire ce1_cpu, ce1_cpu_n, ce1_cpu2x;
wire ce2_cpu, ce2_cpu_n, ce2_cpu2x;
wire ce_ram2x;
speedcontrol speedcontrol1
@@ -1062,6 +1067,7 @@ speedcontrol speedcontrol1
.romack (sdram_ack),
.pausevideo (pauseVideoCore1 & status[5]),
.ce (ce1_cpu),
.ce_n (ce1_cpu_n),
.ce_2x (ce1_cpu2x)
);
@@ -1073,6 +1079,7 @@ speedcontrol speedcontrol2
.romack (sdram_ack2),
.pausevideo (pauseVideoCore2 & status[5]),
.ce (ce2_cpu),
.ce_n (ce2_cpu_n),
.ce_2x (ce2_cpu2x)
);

View File

@@ -8,6 +8,8 @@ set_global_assignment -name SYSTEMVERILOG_FILE rtl/megaswizzle.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/sprites.v
set_global_assignment -name VERILOG_FILE rtl/sprites_extra.v
set_global_assignment -name VERILOG_FILE rtl/sprites_extra_store.v
set_global_assignment -name VERILOG_FILE rtl/lcd.v
set_global_assignment -name VHDL_FILE rtl/gbc_snd.vhd
set_global_assignment -name VHDL_FILE rtl/speedcontrol.vhd

View File

@@ -24,12 +24,14 @@ module gb (
input clk_sys,
input ce,
input ce_n,
input ce_2x,
input [7:0] joystick,
input isGBC,
input real_cgb_boot,
input isSGB,
input extra_spr_en,
// cartridge interface
// can adress up to 1MB ROM
@@ -683,6 +685,7 @@ video video (
.reset ( reset_ss ),
.clk ( clk_sys ),
.ce ( ce ), // 4Mhz
.ce_n ( ce_n ),
.ce_cpu ( ce_cpu ), //can be 2x in cgb double speed mode
.isGBC ( isGBC ),
.isGBC_mode ( isGBC_mode ), //enable GBC mode during bootstrap rom
@@ -720,6 +723,9 @@ video video (
.dma_rd ( dma_rd ),
.dma_addr ( dma_addr ),
.dma_data ( dma_data ),
.extra_spr_en( extra_spr_en ),
.extra_wait ( (isGBC & hdma_rd) | dma_rd | sel_vram ),
.Savestate_OAMRAMAddr (Savestate_RAMAddr[7:0]),
.Savestate_OAMRAMRWrEn (Savestate_RAMRWrEn[2]),

View File

@@ -11,6 +11,7 @@ entity speedcontrol is
romack : in std_logic;
pausevideo : in std_logic;
ce : out std_logic := '0';
ce_n : out std_logic := '0';
ce_2x : out std_logic := '0'
);
end entity;
@@ -34,6 +35,7 @@ begin
if falling_edge(clk_sys) then
ce <= '0';
ce_n <= '0';
ce_2x <= '0';
skipclock := '0';
@@ -42,6 +44,9 @@ begin
if (clkdiv = "000") then
ce <= '1';
end if;
if (clkdiv = "100") then
ce_n <= '1';
end if;
if (clkdiv(1 downto 0) = "00") then
ce_2x <= '1';
end if;
@@ -76,6 +81,7 @@ begin
if (skipclock = '1') then
ce <= '0';
ce_n <= '0';
ce_2x <= '0';
if (clkdiv = "100") then
clkdiv <= "001";

View File

@@ -53,6 +53,21 @@ module sprites (
input [7:0] oam_addr_in,
input [7:0] oam_di,
output [7:0] oam_do,
input extra_spr_en,
input extra_wait,
output extra_tile_fetch,
output [11:0] extra_tile_addr,
input [7:0] tile_data_in,
output spr_extra_found,
output [7:0] spr_extra_tile0,
output [7:0] spr_extra_tile1,
output [2:0] spr_extra_cgb_pal,
output [3:0] spr_extra_index,
output spr_extra_pal,
output spr_extra_prio,
// savestates
input [7:0] Savestate_OAMRAMAddr,
@@ -71,7 +86,12 @@ assign oam_eval = lcd_on & ~oam_eval_end & oam_eval_en & ~oam_eval_reset;
wire [3:0] fetch_row;
wire oam_eval_extra;
wire [7:1] oam_extra_addr;
wire [7:0] spr_extra_fetch_attr;
wire [7:1] oam_addr = dma_active ? oam_addr_in[7:1] :
oam_eval_extra ? { oam_extra_addr } :
oam_eval ? { oam_eval_addr, 1'b0 } :
oam_fetch ? { oam_fetch_addr, 1'b1 } :
oam_addr_in[7:1];
@@ -222,7 +242,7 @@ wire [3:0] active_sprite =
sprite_x_matches[8] ? 4'd8 :
4'd9;
assign sprite_index = active_sprite;
assign sprite_attr = sprite_x_attr;
assign sprite_attr = oam_eval_extra ? spr_extra_fetch_attr : sprite_x_attr;
assign oam_fetch_addr = sprite_no[active_sprite];
@@ -230,4 +250,48 @@ assign fetch_row = sprite_attr[6] ? ~sprite_y[active_sprite] : sprite_y[active_s
assign sprite_addr = size16 ? {tile_index_y[7:1],fetch_row} : {tile_index_y,fetch_row[2:0]};
// Extra sprites:
// Sprite tile fetching during mode3 reduces the length of HBlank.
// Simply adding more sprites will shorten HBlank even more which breaks timing.
// Instead, this module will try to fetch tile data for extra sprites during mode2 if VRAM is idle.
sprites_extra sprites_extra (
.clk ( clk ),
.ce ( ce ),
.extra_spr_en ( extra_spr_en ),
.v_cnt ( v_cnt ),
.h_cnt ( h_cnt ),
.oam_eval_clk ( oam_eval_clk ),
.oam_eval_reset ( oam_eval_reset | ~lcd_on),
.oam_eval_end ( oam_eval_end ),
.size16 ( size16 ),
.oam_index ( spr_index ),
.sprite_cnt ( sprite_cnt ),
.oam_l_q ( oam_l_q ),
.oam_h_q ( oam_h_q ),
.extra_wait ( extra_wait ),
.oam_eval_extra ( oam_eval_extra ),
.oam_extra_addr ( oam_extra_addr ) ,
.spr_fetch_attr ( spr_extra_fetch_attr ),
.tile_fetch ( extra_tile_fetch ),
.tile_data_in ( tile_data_in ),
.tile_addr ( extra_tile_addr ),
.spr_found ( spr_extra_found ),
.spr_tile0 ( spr_extra_tile0 ),
.spr_tile1 ( spr_extra_tile1 ),
.spr_pal ( spr_extra_pal ),
.spr_prio ( spr_extra_prio ),
.spr_cgb_pal ( spr_extra_cgb_pal ),
.spr_index ( spr_extra_index )
);
endmodule

205
rtl/sprites_extra.v Normal file
View File

@@ -0,0 +1,205 @@
module sprites_extra (
input clk,
input ce,
input extra_spr_en,
input oam_eval_end,
input oam_eval_clk,
input oam_eval_reset,
input size16,
input [5:0] oam_index,
input [3:0] sprite_cnt,
input [7:0] oam_l_q,
input [7:0] oam_h_q,
input [7:0] v_cnt,
input [7:0] h_cnt,
input extra_wait,
output oam_eval_extra,
output [7:1] oam_extra_addr,
output [7:0] spr_fetch_attr,
output tile_fetch,
output [11:0] tile_addr,
input [7:0] tile_data_in,
output spr_found,
output [7:0] spr_tile0,
output [7:0] spr_tile1,
output [2:0] spr_cgb_pal,
output [3:0] spr_index,
output spr_pal,
output spr_prio
);
// Maximum extra sprites is 6 currently because sprite index in the pixel shifters is 4 bits.
localparam SPRITES_PER_LINE = 10;
localparam SPRITES_EXTRA = 6;
wire [SPRITES_EXTRA-1:0] sprite_x_matches;
reg [7:0] sprite_x;
reg [3:0] sprite_y;
reg [5:0] extra_oam_index;
reg [3:0] new_sprite_x_index;
reg [4:0] extra_sprite_index;
reg oam_attr_fetch;
reg [7:0] tile_index;
reg [7:0] tile_y;
reg [3:0] tile_row;
reg [7:0] tile_attr;
reg extra_waiting;
wire extra_pause = extra_wait | extra_waiting;
wire oam_extra_start = extra_spr_en & (sprite_cnt == SPRITES_PER_LINE);
assign oam_eval_extra = oam_extra_start & ~oam_eval_end & ~extra_pause;
assign oam_extra_addr = { extra_oam_index, oam_attr_fetch };
assign spr_fetch_attr = tile_attr;
wire [7:0] spr_height = size16 ? 8'd16 : 8'd8;
wire sprite_on_line = (v_cnt + 8'd16 >= oam_l_q) && (v_cnt + 8'd16 < oam_l_q + spr_height);
reg tile_fetching;
reg [3:0] tile_fetch_x_index;
reg [3:0] tile_fetch_sprite_index;
reg [1:0] tile_fetch_cnt;
reg tile1_fetch;
wire tile_save;
reg save_x;
always @(posedge clk) begin
if (ce) begin
if (~oam_extra_start) begin
extra_waiting <= 0;
end else if (oam_eval_clk) begin
extra_waiting <= extra_wait;
end
end
end
always @(posedge clk) begin
if (ce) begin
save_x <= 0;
if (~oam_extra_start) begin
extra_oam_index <= oam_index;
oam_attr_fetch <= 0;
new_sprite_x_index <= 0;
extra_sprite_index <= SPRITES_PER_LINE[4:0];
end else begin
if (oam_eval_clk & ~extra_pause) begin
if (~oam_attr_fetch) begin
if (sprite_on_line) begin
sprite_x <= oam_h_q;
sprite_y <= v_cnt[3:0] - oam_l_q[3:0];
oam_attr_fetch <= 1;
end else begin
extra_oam_index <= extra_oam_index + 1'b1;
end
end else begin // Fetched attributes
extra_oam_index <= extra_oam_index + 1'b1;
oam_attr_fetch <= 0;
tile_index <= oam_l_q;
tile_attr <= oam_h_q;
tile_row <= oam_h_q[6] ? ~sprite_y : sprite_y;
tile_fetch_sprite_index <= extra_sprite_index[3:0];
if (extra_sprite_index != SPRITES_PER_LINE+SPRITES_EXTRA) begin
if (~spr_found) begin // Skip sprite if this X position was already found
tile_fetch_x_index <= new_sprite_x_index;
save_x <= 1; // Store X position and start tile fetch
new_sprite_x_index <= new_sprite_x_index + 1'b1;
end
extra_sprite_index <= extra_sprite_index + 1'b1;
end
end
end
end
end
end
assign tile_addr[11:5] = tile_index[7:1];
assign tile_addr[4:1] = size16 ? tile_row : { tile_index[0],tile_row[2:0] };
assign tile_addr[0] = tile1_fetch;
assign tile_fetch = (save_x | tile_fetching) & ~extra_pause;
assign tile_save = tile_fetch & oam_eval_clk & tile1_fetch;
reg [7:0] tile_data_0;
always @(posedge clk) begin
if (ce) begin
if (oam_eval_reset | oam_eval_end) begin
tile_fetching <= 0;
tile1_fetch <= 0;
end else begin
if (save_x) begin
tile_fetching <= 1;
end
if (tile_fetch & oam_eval_clk ) begin
if (~tile1_fetch) begin
tile_data_0 <= tile_data_in;
end else begin
tile_fetching <= 0;
end
tile1_fetch <= ~tile1_fetch;
end
end
end
end
wire [7:0] sprite_x_sel = oam_eval_extra ? sprite_x : h_cnt;
genvar j;
generate
for (j = 0; j < SPRITES_EXTRA; j = j + 1) begin : gen_sprite_extra_store
sprites_extra_store st (
.clk ( clk ),
.ce ( ce ),
.reset ( oam_eval_reset ),
.save_x ( save_x & (tile_fetch_x_index == (j)) ),
.xpos ( sprite_x_sel ),
.tile_save ( tile_save & (tile_fetch_x_index == (j)) ),
.tile0_in ( tile_data_0 ),
.tile1_in ( tile_data_in ),
.index_in ( tile_fetch_sprite_index ),
.cgb_pal_in ( tile_attr[2:0] ),
.pal_in ( tile_attr[4] ),
.prio_in ( tile_attr[7] ),
.x_match ( sprite_x_matches[j] ),
.tile0_o ( spr_tile0 ),
.tile1_o ( spr_tile1 ),
.pal_o ( spr_pal ),
.prio_o ( spr_prio ),
.cgb_pal_o ( spr_cgb_pal ),
.index_o ( spr_index )
);
end
endgenerate
assign spr_found = |{sprite_x_matches} & extra_spr_en;
endmodule

68
rtl/sprites_extra_store.v Normal file
View File

@@ -0,0 +1,68 @@
module sprites_extra_store (
input clk,
input ce,
input reset,
input save_x,
input [7:0] xpos,
input tile_save,
input [7:0] tile0_in,
input [7:0] tile1_in,
input [3:0] index_in,
input [2:0] cgb_pal_in,
input pal_in,
input prio_in,
output x_match,
output [7:0] tile0_o,
output [7:0] tile1_o,
output [2:0] cgb_pal_o,
output [3:0] index_o,
output pal_o,
output prio_o
);
reg [7:0] x;
reg [7:0] tile0;
reg [7:0] tile1;
reg [2:0] cgb_pal;
reg [3:0] index;
reg pal;
reg prio;
always @(posedge clk) begin
if (ce) begin
if (reset) begin
x <= 8'hFF;
tile0 <= 8'd0;
tile1 <= 8'd0;
end else begin
if (save_x) begin
x <= xpos;
end
if (tile_save) begin
tile0 <= tile0_in;
tile1 <= tile1_in;
pal <= pal_in;
prio <= prio_in;
cgb_pal <= cgb_pal_in;
index <= index_in;
end
end
end
end
assign x_match = (xpos == x);
assign tile0_o = x_match ? tile0 : 8'hZZ;
assign tile1_o = x_match ? tile1 : 8'hZZ;
assign pal_o = x_match ? pal : 1'bZ;
assign prio_o = x_match ? prio : 1'bZ;
assign cgb_pal_o = x_match ? cgb_pal : 3'hZ;
assign index_o = x_match ? index : 4'hZ;
endmodule

View File

@@ -23,6 +23,7 @@ module video (
input reset,
input clk,
input ce, // 4 Mhz cpu clock
input ce_n, // falling edge
input ce_cpu, // 4 or 8Mhz
input isGBC,
input isGBC_mode,
@@ -64,6 +65,9 @@ module video (
output [15:0] dma_addr,
input [7:0] dma_data,
input extra_spr_en,
input extra_wait,
// savestates
input [7:0] Savestate_OAMRAMAddr,
input Savestate_OAMRAMRWrEn,
@@ -206,6 +210,8 @@ reg[7:0] obpd [63:0]; //64 bytes
reg ff6c_opri;
reg obj_prio_dmg_mode;
integer i;
// --------------------------------------------------------------------
// ----------------------------- DMA engine ---------------------------
// --------------------------------------------------------------------
@@ -829,21 +835,24 @@ wire [2:0] spr_cgb_pal = sprite_attr[2:0];
wire [7:0] spr_vram_data = (isGBC & isGBC_mode & spr_attr_cgb_bank) ? vram1_data : vram_data;
wire [7:0] spr_tile_data_in = spr_attr_h_flip ? bit_reverse(spr_vram_data) : spr_vram_data;
// CGB sprite priority. Non-transparent pixels with lower sprite_index have priority.
function [7:0] spr_cgb_prio;
input [7:0] a3,a2,a1,a0;
integer i;
begin
for (i=0;i<8;i=i+1)
spr_cgb_prio[i] = (sprite_index < {a3[i], a2[i], a1[i], a0[i]}) & (spr_tile_data_in[i] | spr_tile_data0[i]);
end
endfunction
wire [7:0] spr_cgb_index_prio = spr_cgb_prio(spr_cgb_index_shift[3], spr_cgb_index_shift[2], spr_cgb_index_shift[1], spr_cgb_index_shift[0]);
// DMG sprite pixels are only loaded into the shift register if the old pixel is transparent.
// CGB will mask the old pixel to 0 if the new pixel has higher priority.
wire [7:0] spr_tile_mask = (spr_tile_shift_0 | spr_tile_shift_1) & ((isGBC & ~obj_prio_dmg_mode) ? ~spr_cgb_index_prio : 8'hFF);
wire [7:0] spr_pixel_empty = ~(spr_tile_shift_0 | spr_tile_shift_1);
// CGB sprite priority. Non-transparent pixels with lower sprite_index have priority.
reg [7:0] spr_cgb_higher_prio, spr_extra_cgb_higher_prio;
always @(*) begin
for (i = 0; i < 8; i = i + 1) begin
spr_cgb_higher_prio[i] = (sprite_index < {spr_cgb_index_shift[3][i], spr_cgb_index_shift[2][i], spr_cgb_index_shift[1][i], spr_cgb_index_shift[0][i]}) & (spr_tile_data_in[i] | spr_tile_data0[i]);
spr_extra_cgb_higher_prio[i] = (spr_extra_index < {spr_cgb_index_shift[3][i], spr_cgb_index_shift[2][i], spr_cgb_index_shift[1][i], spr_cgb_index_shift[0][i]}) & (spr_extra_tile[0][i] | spr_extra_tile[1][i]);
end
end
wire [7:0] spr_extra_tile[0:1];
wire [2:0] spr_extra_cgb_pal;
wire [3:0] spr_extra_index;
wire spr_extra_pal;
wire spr_extra_prio;
wire spr_extra_found;
// cycle through the B01s states
wire bg_tile_map_rd = (mode3 && bg_fetch_cycle[2:1] == 2'b00);
@@ -879,40 +888,6 @@ always @(posedge clk) begin
if(bg_tile_data1_rd) bg_tile_data1 <= bg_vram_data_in;
end
// Shift sprite data out
if (~pcnt_paused) begin
spr_tile_shift_0 <= spr_tile_shift_0 << 1;
spr_tile_shift_1 <= spr_tile_shift_1 << 1;
spr_pal_shift <= spr_pal_shift << 1;
spr_prio_shift <= spr_prio_shift << 1;
spr_cgb_pal_shift[0] <= spr_cgb_pal_shift[0] << 1;
spr_cgb_pal_shift[1] <= spr_cgb_pal_shift[1] << 1;
spr_cgb_pal_shift[2] <= spr_cgb_pal_shift[2] << 1;
spr_cgb_index_shift[0] <= spr_cgb_index_shift[0] << 1;
spr_cgb_index_shift[1] <= spr_cgb_index_shift[1] << 1;
spr_cgb_index_shift[2] <= spr_cgb_index_shift[2] << 1;
spr_cgb_index_shift[3] <= spr_cgb_index_shift[3] << 1;
end
// Fetch sprite new data
if (sprite_fetch_cycle[0]) begin
if(bg_tile_obj0_rd) spr_tile_data0 <= spr_tile_data_in;
if(bg_tile_obj1_rd) begin
spr_tile_shift_0 <= (spr_tile_shift_0 & spr_tile_mask) | ( spr_tile_data0 & ~spr_tile_mask);
spr_tile_shift_1 <= (spr_tile_shift_1 & spr_tile_mask) | ( spr_tile_data_in & ~spr_tile_mask);
spr_pal_shift <= (spr_pal_shift & spr_tile_mask) | ({8{spr_pal}} & ~spr_tile_mask);
spr_prio_shift <= (spr_prio_shift & spr_tile_mask) | ({8{spr_prio}} & ~spr_tile_mask);
spr_cgb_pal_shift[0] <= (spr_cgb_pal_shift[0] & spr_tile_mask) | ({8{spr_cgb_pal[0]}} & ~spr_tile_mask);
spr_cgb_pal_shift[1] <= (spr_cgb_pal_shift[1] & spr_tile_mask) | ({8{spr_cgb_pal[1]}} & ~spr_tile_mask);
spr_cgb_pal_shift[2] <= (spr_cgb_pal_shift[2] & spr_tile_mask) | ({8{spr_cgb_pal[2]}} & ~spr_tile_mask);
spr_cgb_index_shift[0] <= (spr_cgb_index_shift[0] & spr_tile_mask) | ({8{sprite_index[0]}} & ~spr_tile_mask);
spr_cgb_index_shift[1] <= (spr_cgb_index_shift[1] & spr_tile_mask) | ({8{sprite_index[1]}} & ~spr_tile_mask);
spr_cgb_index_shift[2] <= (spr_cgb_index_shift[2] & spr_tile_mask) | ({8{sprite_index[2]}} & ~spr_tile_mask);
spr_cgb_index_shift[3] <= (spr_cgb_index_shift[3] & spr_tile_mask) | ({8{sprite_index[3]}} & ~spr_tile_mask);
end
end
if (~&bg_fetch_cycle) begin
bg_fetch_cycle <= bg_fetch_cycle + 1'b1;
end
@@ -966,8 +941,77 @@ always @(posedge clk) begin
end
always @(posedge clk) begin
if (reset) begin
end else begin
if (ce) begin
// Shift sprite data out
if (~pcnt_paused) begin
spr_tile_shift_0 <= spr_tile_shift_0 << 1;
spr_tile_shift_1 <= spr_tile_shift_1 << 1;
spr_pal_shift <= spr_pal_shift << 1;
spr_prio_shift <= spr_prio_shift << 1;
spr_cgb_pal_shift[0] <= spr_cgb_pal_shift[0] << 1;
spr_cgb_pal_shift[1] <= spr_cgb_pal_shift[1] << 1;
spr_cgb_pal_shift[2] <= spr_cgb_pal_shift[2] << 1;
spr_cgb_index_shift[0] <= spr_cgb_index_shift[0] << 1;
spr_cgb_index_shift[1] <= spr_cgb_index_shift[1] << 1;
spr_cgb_index_shift[2] <= spr_cgb_index_shift[2] << 1;
spr_cgb_index_shift[3] <= spr_cgb_index_shift[3] << 1;
end
// Fetch sprite new data
if (sprite_fetch_cycle[0]) begin
if(bg_tile_obj0_rd) spr_tile_data0 <= spr_tile_data_in;
if(bg_tile_obj1_rd) begin
for (i = 0; i < 8; i = i + 1) begin
if (spr_pixel_empty[i] | (isGBC & ~obj_prio_dmg_mode & spr_cgb_higher_prio[i])) begin
spr_tile_shift_0[i] <= spr_tile_data0[i];
spr_tile_shift_1[i] <= spr_tile_data_in[i];
spr_pal_shift[i] <= spr_pal;
spr_prio_shift[i] <= spr_prio;
spr_cgb_pal_shift[0][i] <= spr_cgb_pal[0];
spr_cgb_pal_shift[1][i] <= spr_cgb_pal[1];
spr_cgb_pal_shift[2][i] <= spr_cgb_pal[2];
spr_cgb_index_shift[0][i] <= sprite_index[0];
spr_cgb_index_shift[1][i] <= sprite_index[1];
spr_cgb_index_shift[2][i] <= sprite_index[2];
spr_cgb_index_shift[3][i] <= sprite_index[3];
end
end
end
end
end
// Load extra sprite
if (ce_n) begin
if (~pcnt_paused & spr_extra_found) begin
for (i = 0; i < 8; i = i + 1) begin
if (spr_pixel_empty[i] | (isGBC & ~obj_prio_dmg_mode & spr_extra_cgb_higher_prio[i])) begin
spr_tile_shift_0[i] <= spr_extra_tile[0][i];
spr_tile_shift_1[i] <= spr_extra_tile[1][i];
spr_pal_shift[i] <= spr_extra_pal;
spr_prio_shift[i] <= spr_extra_prio;
spr_cgb_pal_shift[0][i] <= spr_extra_cgb_pal[0];
spr_cgb_pal_shift[1][i] <= spr_extra_cgb_pal[1];
spr_cgb_pal_shift[2][i] <= spr_extra_cgb_pal[2];
spr_cgb_index_shift[0][i] <= spr_extra_index[0];
spr_cgb_index_shift[1][i] <= spr_extra_index[1];
spr_cgb_index_shift[2][i] <= spr_extra_index[2];
spr_cgb_index_shift[3][i] <= spr_extra_index[3];
end
end
end
end
end
end
assign vram_rd = lcdc_on && (bg_tile_map_rd || bg_tile_data0_rd ||
bg_tile_data1_rd || bg_tile_obj0_rd || bg_tile_obj1_rd);
bg_tile_data1_rd || bg_tile_obj0_rd ||
bg_tile_obj1_rd || tile_obj_extra_rd);
wire bg_tile_a12 = !lcdc_tile_data_sel?(~bg_tile[7]):1'b0;
@@ -976,12 +1020,16 @@ wire tile_map_sel = window_ena?lcdc_win_tile_map_sel:lcdc_bg_tile_map_sel;
//GBC: check if flipped y
wire [2:0] tile_line_flip = (isGBC && isGBC_mode && bg_tile_attr_new[6]) ? ~tile_line : tile_line;
wire tile_obj_extra_rd;
wire [11:0] sprite_extra_addr;
assign vram_addr =
bg_tile_map_rd?{2'b11, tile_map_sel, bg_tile_map_addr}:
bg_tile_data0_rd?{bg_tile_a12, bg_tile, tile_line_flip, 1'b0}:
bg_tile_data1_rd?{bg_tile_a12, bg_tile, tile_line_flip, 1'b1}:
bg_tile_obj0_rd ? {1'b0, sprite_addr, 1'b0} :
{1'b0, sprite_addr, 1'b1};
bg_tile_obj1_rd ? {1'b0, sprite_addr, 1'b1} :
{1'b0, sprite_extra_addr };
sprites sprites (
.clk ( clk ),
@@ -1013,6 +1061,21 @@ sprites sprites (
.oam_di ( oam_di ),
.oam_do ( oam_do ),
// For extra sprites
.extra_spr_en ( extra_spr_en ),
.extra_wait ( extra_wait ),
.tile_data_in ( spr_tile_data_in ),
.extra_tile_fetch ( tile_obj_extra_rd ),
.extra_tile_addr ( sprite_extra_addr ),
.spr_extra_found ( spr_extra_found ),
.spr_extra_tile0 ( spr_extra_tile[0] ),
.spr_extra_tile1 ( spr_extra_tile[1] ),
.spr_extra_pal ( spr_extra_pal ),
.spr_extra_prio ( spr_extra_prio ),
.spr_extra_cgb_pal( spr_extra_cgb_pal ),
.spr_extra_index ( spr_extra_index ),
.Savestate_OAMRAMAddr (Savestate_OAMRAMAddr),
.Savestate_OAMRAMRWrEn (Savestate_OAMRAMRWrEn),
.Savestate_OAMRAMWriteData (Savestate_OAMRAMWriteData),