mirror of
https://github.com/MiSTer-devel/Gameboy_MiSTer.git
synced 2026-04-19 03:04:09 +00:00
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:
17
Gameboy.sv
17
Gameboy.sv
@@ -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)
|
||||
);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
6
rtl/gb.v
6
rtl/gb.v
@@ -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]),
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
205
rtl/sprites_extra.v
Normal 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
68
rtl/sprites_extra_store.v
Normal 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
|
||||
163
rtl/video.v
163
rtl/video.v
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user