diff --git a/Gameboy.sv b/Gameboy.sv index 1e712f1..a9d06e3 100644 --- a/Gameboy.sv +++ b/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) ); diff --git a/files.qip b/files.qip index daffe35..1bcfbbf 100644 --- a/files.qip +++ b/files.qip @@ -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 diff --git a/rtl/gb.v b/rtl/gb.v index f7fbb49..75621dd 100644 --- a/rtl/gb.v +++ b/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]), diff --git a/rtl/speedcontrol.vhd b/rtl/speedcontrol.vhd index 1e1f4d6..35b3c7f 100644 --- a/rtl/speedcontrol.vhd +++ b/rtl/speedcontrol.vhd @@ -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"; diff --git a/rtl/sprites.v b/rtl/sprites.v index 1dc9490..ec1ceff 100644 --- a/rtl/sprites.v +++ b/rtl/sprites.v @@ -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 \ No newline at end of file diff --git a/rtl/sprites_extra.v b/rtl/sprites_extra.v new file mode 100644 index 0000000..31a6a22 --- /dev/null +++ b/rtl/sprites_extra.v @@ -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 \ No newline at end of file diff --git a/rtl/sprites_extra_store.v b/rtl/sprites_extra_store.v new file mode 100644 index 0000000..5f562bb --- /dev/null +++ b/rtl/sprites_extra_store.v @@ -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 \ No newline at end of file diff --git a/rtl/video.v b/rtl/video.v index e65b58d..c36dd0f 100644 --- a/rtl/video.v +++ b/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),