OAM: Change data bus to 16bit

-Add shared buffers for X/Y and Tile index/attributes
-X/Y buffers during mode2 are not updated when OAM dma is active
(Needed for strikethrough.gb test glitch)
This commit is contained in:
paulb-nl
2025-06-21 16:08:34 +02:00
parent dbe7e7ef4a
commit d11c2bdd3b
2 changed files with 72 additions and 63 deletions

View File

@@ -32,7 +32,8 @@ module sprites (
// pixel position input which the current pixel is generated for
input [7:0] v_cnt,
input [7:0] h_cnt,
input sprite_fetch_c1,
input sprite_fetch_done,
output sprite_fetch,
@@ -41,7 +42,7 @@ module sprites (
output oam_eval,
output [10:0] sprite_addr,
output reg [7:0] sprite_attr,
output [7:0] sprite_attr,
output [3:0] sprite_index,
output oam_eval_end,
@@ -62,48 +63,67 @@ module sprites (
localparam SPRITES_PER_LINE = 10;
reg [7:0] oam_spr_addr;
wire [7:0] oam_fetch_addr;
reg [7:0] oam_q;
wire [7:2] oam_eval_addr, oam_fetch_addr;
wire [7:0] oam_l_q, oam_h_q;
reg oam_eval_en;
assign oam_eval = lcd_on & ~oam_eval_end & oam_eval_en & ~oam_eval_reset;
wire [7:0] oam_addr = dma_active ? oam_addr_in :
oam_eval ? oam_spr_addr :
oam_fetch ? oam_fetch_addr :
oam_addr_in;
wire [3:0] fetch_row;
wire [7:1] oam_addr = dma_active ? oam_addr_in[7:1] :
oam_eval ? { oam_eval_addr, 1'b0 } :
oam_fetch ? { oam_fetch_addr, 1'b1 } :
oam_addr_in[7:1];
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;
assign oam_do = ~valid_oam_addr ? 8'd0 : (oam_addr_in[0] ? oam_h_q : oam_l_q);
wire [7:0] Savestate_OAMRAMReadDataL, Savestate_OAMRAMReadDataH;
dpram #(8) oam_data (
dpram #(7,8) oam_data_l (
.clock_a (clk ),
.address_a (oam_addr ),
.wren_a (ce_cpu && oam_wr && valid_oam_addr),
.address_a (oam_addr[7:1]),
.wren_a (ce_cpu && oam_wr && valid_oam_addr && ~oam_addr_in[0]),
.data_a (oam_di ),
.q_a (oam_q ),
.q_a (oam_l_q ),
.clock_b (clk),
.address_b (Savestate_OAMRAMAddr ),
.wren_b (Savestate_OAMRAMRWrEn ),
.address_b (Savestate_OAMRAMAddr[7:1]),
.wren_b (Savestate_OAMRAMRWrEn & ~Savestate_OAMRAMAddr[0]),
.data_b (Savestate_OAMRAMWriteData),
.q_b (Savestate_OAMRAMReadData )
.q_b (Savestate_OAMRAMReadDataL)
);
dpram #(7,8) oam_data_h (
.clock_a (clk ),
.address_a (oam_addr[7:1] ),
.wren_a (ce_cpu && oam_wr && valid_oam_addr && oam_addr_in[0]),
.data_a (oam_di ),
.q_a (oam_h_q ),
.clock_b (clk),
.address_b (Savestate_OAMRAMAddr[7:1]),
.wren_b (Savestate_OAMRAMRWrEn & Savestate_OAMRAMAddr[0]),
.data_b (Savestate_OAMRAMWriteData),
.q_b (Savestate_OAMRAMReadDataH)
);
assign Savestate_OAMRAMReadData = Savestate_OAMRAMAddr[0] ? Savestate_OAMRAMReadDataH : Savestate_OAMRAMReadDataL;
reg [7:0] sprite_x[0:SPRITES_PER_LINE-1];
reg [3:0] sprite_y[0:SPRITES_PER_LINE-1];
reg [5:0] sprite_no[0:SPRITES_PER_LINE-1];
// OAM evaluation. Get the first 10 sprites on the current line.
reg [5:0] spr_index; // 40 sprites
reg [5:0] spr_index, spr_index_d; // 40 sprites
reg [3:0] sprite_cnt;
reg sprite_cycle;
reg oam_eval_clk, oam_eval_clk_d, oam_eval_save;
reg [7:0] spr_y;
reg [7:0] sprite_x_attr, tile_index_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);
wire sprite_on_line = (v_cnt + 8'd16 >= tile_index_y) && (v_cnt + 8'd16 < tile_index_y + spr_height);
wire sprite_save = oam_eval_clk_d & oam_eval_en & sprite_on_line;
assign oam_eval_end = (spr_index == 6'd40);
@@ -117,8 +137,8 @@ always @(posedge clk) begin
if (oam_eval_reset | ~lcd_on) begin
sprite_cnt <= 0;
spr_index <= ~lcd_on ? 6'd1 : 6'd0;
sprite_cycle <= 0;
oam_spr_addr <= 0;
oam_eval_clk <= 0;
oam_eval_clk_d <= 0;
oam_eval_en <= oam_eval_reset ? 1'b1 : 1'b0; // OAM evaluation does not run on the first line after enabling the lcd
for (spr_i=0; spr_i < SPRITES_PER_LINE; spr_i=spr_i+1) begin
sprite_x[spr_i] <= 8'hFF;
@@ -127,24 +147,19 @@ always @(posedge clk) begin
end else begin
if (~oam_eval_end) begin
if (sprite_cycle) spr_index <= spr_index + 1'b1;
if (oam_eval_en && 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
if (oam_eval_clk) begin
spr_index <= spr_index + 1'b1;
spr_index_d <= spr_index;
end
oam_eval_clk <= ~oam_eval_clk;
end
sprite_cycle <= ~sprite_cycle;
oam_eval_clk_d <= oam_eval_clk;
if (sprite_save & (sprite_cnt < SPRITES_PER_LINE)) begin
sprite_no[sprite_cnt] <= spr_index_d;
sprite_x[sprite_cnt] <= sprite_x_attr;
sprite_y[sprite_cnt] <= v_cnt[3:0] - tile_index_y[3:0];
sprite_cnt <= sprite_cnt + 1'b1;
end
// Set X-position to FF after fetching the sprite to prevent fetching it again.
@@ -166,6 +181,18 @@ always @(posedge clk) begin
end
end
assign oam_eval_addr = spr_index;
wire eval_save_xy = (~oam_eval_end & oam_eval_en & oam_eval_clk & ~dma_active);
wire fetch_save_index_attr = (sprite_fetch & sprite_fetch_c1);
always @(posedge clk) begin
if (ce) begin
if (eval_save_xy | fetch_save_index_attr) begin
tile_index_y <= oam_l_q;
sprite_x_attr <= oam_h_q;
end
end
end
// Sprite fetching
assign sprite_x_matches = {
@@ -195,31 +222,12 @@ wire [3:0] active_sprite =
sprite_x_matches[8] ? 4'd8 :
4'd9;
assign sprite_index = active_sprite;
assign sprite_attr = sprite_x_attr;
wire [5:0] oam_fetch_index = sprite_no[active_sprite];
assign oam_fetch_addr = sprite_no[active_sprite];
reg [3:0] row;
reg [7:0] tile_no;
reg oam_fetch_cycle;
assign 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]};
assign fetch_row = sprite_attr[6] ? ~sprite_y[active_sprite] : sprite_y[active_sprite];
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
assign sprite_addr = size16 ? {tile_index_y[7:1],fetch_row} : {tile_index_y,fetch_row[2:0]};
endmodule

View File

@@ -370,7 +370,7 @@ assign mode =
mode3_l & ~mode3_end ? 2'b11 :
2'b00;
assign oam_cpu_allow = ~(oam_eval | mode3);
assign oam_cpu_allow = ~(oam_eval | mode3 | dma_active);
assign vram_cpu_allow = ~mode3;
// --------------------------------------------------------------------
@@ -1004,6 +1004,7 @@ sprites sprites (
.sprite_addr ( sprite_addr ),
.sprite_attr ( sprite_attr ),
.sprite_index ( sprite_index ),
.sprite_fetch_c1 ( sprite_fetch_cycle == 3'd1 ),
.sprite_fetch_done ( sprite_fetch_done) ,
.dma_active ( dma_active),