1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-02-06 08:04:41 +00:00

[Gameboy] Updates from MiSTer (brNX)

This commit is contained in:
Gyorgy Szombathelyi
2019-03-08 12:08:31 +01:00
parent ec298a618a
commit 366853b166
4 changed files with 426 additions and 365 deletions

View File

@@ -115,11 +115,11 @@ wire [7:0] cpu_di =
sel_sc?sc_r: // serial transfer control register
sel_timer?timer_do: // timer registers
sel_video_reg?video_do: // video registers
sel_video_oam?video_do: // video object attribute memory
(sel_video_oam&&!(lcd_mode==3 || lcd_mode==2))?video_do: // video object attribute memory
sel_audio?audio_do: // audio registers
sel_rom?rom_do: // boot rom + cartridge rom
sel_cram?rom_do: // cartridge ram
sel_vram?(isGBC&&vram_bank)?vram1_do:vram_do: // vram (GBC bank 0+1)
(sel_vram&&lcd_mode!=3)?(isGBC&&vram_bank)?vram1_do:vram_do: // vram (GBC bank 0+1)
sel_zpram?zpram_do: // zero page ram
sel_iram?iram_do: // internal ram
sel_ie?{3'b000, ie_r}: // interrupt enable register
@@ -131,7 +131,7 @@ wire cpu_iorq_n;
wire cpu_m1_n;
wire cpu_mreq_n;
wire cpu_clken = isGBC ? !hdma_rd:1'b1; //when hdma is enabled stop CPU (GBC)
wire cpu_clken = isGBC ? !hdma_active:1'b1; //when hdma is enabled stop CPU (GBC)
wire cpu_stop;
GBse cpu (
@@ -278,8 +278,8 @@ wire [7:0] irq_vec =
if_r[4]&&ie_r[4]?8'h60: // input
8'h55;
wire vs = (lcd_mode == 2'b01);
reg vsD, vsD2;
//wire vs = (lcd_mode == 2'b01);
//reg vsD, vsD2;
reg [7:0] inputD, inputD2;
// irq is low when an enable irq is active
@@ -296,9 +296,9 @@ always @(negedge clk_cpu) begin //negedge to trigger interrupt earlier
end
// rising edge on vs
vsD <= vs;
vsD2 <= vsD;
if(vsD && !vsD2) if_r[0] <= 1'b1;
// vsD <= vs;
// vsD2 <= vsD;
if(vblank_irq) if_r[0] <= 1'b1;
// video irq already is a 1 clock event
if(video_irq) if_r[1] <= 1'b1;
@@ -359,7 +359,7 @@ timer timer (
// --------------------------------------------------------------------
// cpu tries to read or write the lcd controller registers
wire video_irq;
wire video_irq,vblank_irq;
wire [7:0] video_do;
wire [12:0] video_addr;
wire [15:0] dma_addr;
@@ -375,6 +375,8 @@ video video (
.irq ( video_irq ),
.vblank_irq ( vblank_irq ),
.cpu_sel_reg ( sel_video_reg ),
.cpu_sel_oam ( sel_video_oam ),
@@ -402,7 +404,7 @@ video video (
);
// total 8k/16k (CGB) vram from $8000 to $9fff
wire cpu_wr_vram = sel_vram && !cpu_wr_n;
wire cpu_wr_vram = sel_vram && !cpu_wr_n && lcd_mode!=3;
reg vram_bank; //0-1 FF4F - VBK
@@ -453,10 +455,12 @@ wire [15:0] hdma_source_addr;
wire [15:0] hdma_target_addr;
wire [7:0] hdma_do;
wire hdma_rd;
wire hdma_active;
hdma hdma(
.reset ( reset ),
.clk ( clk2x ),
.speed ( cpu_speed ),
// cpu register interface
.sel_reg ( sel_hdma ),
@@ -469,6 +473,7 @@ hdma hdma(
// dma connection
.hdma_rd ( hdma_rd ),
.hdma_active ( hdma_active ),
.hdma_source_addr ( hdma_source_addr ),
.hdma_target_addr ( hdma_target_addr )

View File

@@ -1,331 +1,360 @@
module hdma(
input reset,
input clk, // 8 Mhz cpu clock
// cpu register interface
input sel_reg,
input [3:0] addr,
input wr,
output [7:0] dout,
input [7:0] din,
input [1:0] lcd_mode,
// dma connection
output hdma_rd,
output [15:0] hdma_source_addr,
output [15:0] hdma_target_addr
module hdma(
input reset,
input clk, // 8 Mhz cpu clock
input speed, // cpu speed mode use for initial delay
// cpu register interface
input sel_reg,
input [3:0] addr,
input wr,
output [7:0] dout,
input [7:0] din,
input [1:0] lcd_mode,
// dma connection
output reg hdma_rd,
output reg hdma_active,
output [15:0] hdma_source_addr,
output [15:0] hdma_target_addr
);
//ff51-ff55 HDMA1-5 (GBC)
reg [7:0] hdma_source_h; // ff51
reg [3:0] hdma_source_l; // ff52 only top 4 bits used
reg [4:0] hdma_target_h; // ff53 only lowest 5 bits used
reg [3:0] hdma_target_l; // ff54 only top 4 bits used
reg hdma_mode; // ff55 bit 7 - 1=General Purpose DMA 0=H-Blank DMA
reg hdma_enabled; // ff55 !bit 7 when read
reg [7:0] hdma_length; // ff55 bit 6:0 - dma transfer length (hdma_length+1)*16 bytes
reg hdma_active;
// it takes about 8us to transfer a block of 16 bytes. -> 500ns per byte -> 2Mhz
// 32 cycles in Normal Speed Mode, and 64 'fast' cycles in Double Speed Mode
reg [13:0] hdma_cnt;
reg [5:0] hdma_16byte_cnt; //16bytes*4
assign hdma_rd = hdma_active;
assign hdma_source_addr = { hdma_source_h,hdma_source_l,4'd0} + hdma_cnt[13:2];
assign hdma_target_addr = { 3'b100,hdma_target_h,hdma_target_l,4'd0} + hdma_cnt[13:2];
reg [1:0] hdma_state;
parameter active=2'd0,blocksent=2'd1,wait_h=2'd2;
always @(posedge clk) begin
if(reset) begin
hdma_active <= 1'b0;
hdma_state <= wait_h;
hdma_enabled <= 1'b0;
hdma_source_h <= 8'hFF;
hdma_source_l <= 4'hF;
hdma_target_h <= 5'h1F;
hdma_target_l <= 4'hF;
end else begin
if(sel_reg && wr) begin
case (addr)
4'd1: hdma_source_h <= din;
4'd2: hdma_source_l <= din[7:4];
4'd3: hdma_target_h <= din[4:0];
4'd4: hdma_target_l <= din[7:4];
// writing the hdma register engages the dma engine
4'h5: begin
if (hdma_mode == 1 && hdma_enabled && !din[7]) begin //terminate an active H-Blank transfer by writing zero to Bit 7 of FF55
hdma_state <= wait_h;
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
end else begin //normal trigger
hdma_enabled <= 1'b1;
hdma_mode <= din[7];
hdma_length <= {1'b0,din[6:0]} + 8'd1;
hdma_cnt <= 14'd0;
hdma_16byte_cnt <= 6'h3f;
if (din[7] == 1) hdma_state <= wait_h;
end
end
endcase
end
if (hdma_enabled) begin
if(hdma_mode==0) begin //mode 0 GDMA do the transfer in one go
if(hdma_length != 0) begin
hdma_active <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt) begin
hdma_length <= hdma_length - 1'd1;
if (hdma_length == 1) begin
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
hdma_length <= 8'h80; //7f+1
end
end
end
end else begin //mode 1 HDMA transfer 1 block (16bytes) in each H-Blank only
case (hdma_state)
wait_h: begin
if (lcd_mode == 2'b00 ) // Mode 00: h-blank
localparam DELAY_SINGLE = 5'd10;
localparam DELAY_DOUBLE = DELAY_SINGLE/5'd2;
//ff51-ff55 HDMA1-5 (GBC)
reg [7:0] hdma_source_h; // ff51
reg [3:0] hdma_source_l; // ff52 only top 4 bits used
reg [4:0] hdma_target_h; // ff53 only lowest 5 bits used
reg [3:0] hdma_target_l; // ff54 only top 4 bits used
reg hdma_mode; // ff55 bit 7 - 1=General Purpose DMA 0=H-Blank DMA
reg hdma_enabled; // ff55 !bit 7 when read
reg [7:0] hdma_length; // ff55 bit 6:0 - dma transfer length (hdma_length+1)*16 bytes
// it takes about 8us to transfer a block of 16 bytes. -> 500ns per byte -> 2Mhz
// 32 cycles in Normal Speed Mode, and 64 'fast' cycles in Double Speed Mode
reg [13:0] hdma_cnt;
reg [5:0] hdma_16byte_cnt; //16bytes*4
//assign hdma_rd = hdma_active;
assign hdma_source_addr = { hdma_source_h,hdma_source_l,4'd0} + hdma_cnt[13:2];
assign hdma_target_addr = { 3'b100,hdma_target_h,hdma_target_l,4'd0} + hdma_cnt[13:2];
reg [4:0] dma_delay;
reg [1:0] hdma_state;
parameter active=2'd0,blocksent=2'd1,wait_h=2'd2;
always @(posedge clk) begin
if(reset) begin
hdma_active <= 1'b0;
hdma_state <= wait_h;
hdma_enabled <= 1'b0;
hdma_source_h <= 8'hFF;
hdma_source_l <= 4'hF;
hdma_target_h <= 5'h1F;
hdma_target_l <= 4'hF;
dma_delay <= 5'd0;
end else begin
if(sel_reg && wr) begin
case (addr)
4'd1: hdma_source_h <= din;
4'd2: hdma_source_l <= din[7:4];
4'd3: hdma_target_h <= din[4:0];
4'd4: hdma_target_l <= din[7:4];
// writing the hdma register engages the dma engine
4'h5: begin
if (hdma_mode == 1 && hdma_enabled && !din[7]) begin //terminate an active H-Blank transfer by writing zero to Bit 7 of FF55
hdma_state <= wait_h;
hdma_active <= 1'b0;
hdma_rd <= 1'b0;
hdma_enabled <= 1'b0;
end else begin //normal trigger
hdma_enabled <= 1'b1;
hdma_mode <= din[7];
dma_delay <= speed?DELAY_DOUBLE:DELAY_SINGLE;
hdma_length <= {1'b0,din[6:0]} + 8'd1;
hdma_cnt <= 14'd0;
hdma_16byte_cnt <= 6'h3f;
if (din[7] == 1) hdma_state <= wait_h;
end
end
endcase
end
if (hdma_enabled) begin
if(hdma_mode==0) begin //mode 0 GDMA do the transfer in one go after inital delay
hdma_active <= 1'b1;
if (dma_delay>0) begin
dma_delay <= dma_delay - 5'd1;
end else begin
if(hdma_length != 0) begin
hdma_rd <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt) begin
hdma_length <= hdma_length - 1'd1;
if (hdma_length == 1) begin
hdma_active <= 1'b0;
hdma_rd <= 1'b0;
hdma_enabled <= 1'b0;
hdma_length <= 8'h80; //7f+1
end
end
end
end
end else begin //mode 1 HDMA transfer 1 block (16bytes) in each H-Blank only
case (hdma_state)
wait_h: begin
if (lcd_mode == 2'b00 ) begin // Mode 00: h-blank
dma_delay <= speed?DELAY_DOUBLE:DELAY_SINGLE;
hdma_state <= active;
hdma_16byte_cnt <= 6'h3f;
hdma_active <= 1'b0;
end
blocksent: begin
if (hdma_length == 0) begin //check if finished
hdma_enabled <= 1'b0;
hdma_length <= 8'h80; //7f+1
end
if (lcd_mode == 2'b11) // wait for mode 3, mode before h-blank
hdma_state <= wait_h;
end
active: begin
end
hdma_16byte_cnt <= 6'h3f;
hdma_active <= 1'b0;
hdma_rd <= 1'b0;
end
blocksent: begin
if (hdma_length == 0) begin //check if finished
hdma_enabled <= 1'b0;
hdma_length <= 8'h80; //7f+1
end
if (lcd_mode == 2'b11) // wait for mode 3, mode before h-blank
hdma_state <= wait_h;
end
active: begin
if(hdma_length != 0) begin
hdma_active <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt) begin
hdma_length <= hdma_length - 1'd1;
hdma_state <= blocksent;
hdma_active <= 1'b0;
end
end else begin
hdma_active <= 1'b0;
hdma_enabled <= 1'b0;
hdma_length <= 8'h80; //7f+1
end
end
endcase
end
end
end
end
wire [7:0] length_m1 = hdma_length - 8'd1;
assign dout = sel_reg?
(addr==4'd5)?hdma_enabled?{1'b0,length_m1[6:0]}:
{1'b1,length_m1[6:0]}:
8'hFF:
8'hFF;
endmodule
`timescale 1 ns/100 ps // time-unit = 1 ns, precision = 100 ps
module hdma_tb;
// duration for each bit = 125 * timescale = 125 * 1 ns = 125ns // 8MHz
localparam period = 125;
reg reset = 1'd1;
reg clk = 1'd0;
// cpu register interface
reg sel_reg = 1'd0;
reg [3:0] addr = 4'd0;
reg wr = 1'd0;
wire [7:0] dout;
reg [7:0] din = 8'd0;
reg [1:0] lcd_mode = 2'd0;
// dma connection
wire hdma_rd;
wire [15:0] hdma_source_addr;
wire [15:0] hdma_target_addr;
hdma hdma(
.reset ( reset ),
.clk ( clk ),
// cpu register interface
.sel_reg ( sel_reg ),
.addr ( addr ),
.wr ( wr ),
.dout ( dout ),
.din ( din ),
.lcd_mode ( lcd_mode ),
// dma connection
.hdma_rd ( hdma_rd ),
.hdma_source_addr ( hdma_source_addr ),
.hdma_target_addr ( hdma_target_addr )
);
always #62 clk <= !clk;
initial begin
reset <= 1'b0;
sel_reg <= 1'b1;
addr <= 4'd4;
#1000
sel_reg <= 1'b1;
addr <= 4'd1; // source h
din <= 8'h20;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
sel_reg <= 1'b1;
addr <= 4'd2; // source l
din <= 8'h40;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
sel_reg <= 1'b1;
addr <= 4'd3; // target h
din <= 8'h82;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
sel_reg <= 1'b1;
addr <= 4'd4; // target l
din <= 8'h00;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
$display("GDMA");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger GDMA with length
din <= 8'h01; // 20h bytes
wr <= 1'd1;
#period
wr <= 1'd0;
#8000
lcd_mode <= 2'd1;
#2000
lcd_mode <= 2'd0;
#8000
$display("HDMA");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger HDMA with length
din <= 8'h82; // 30h bytes
wr <= 1'd1;
#period
wr <= 1'd0;
#16000
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
lcd_mode <= 2'd0;
#16000
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
lcd_mode <= 2'd0;
#16000
sel_reg <= 1'b1;
addr <= 4'd5;
$display("Check FF55");
#1000
$display("HDMA with cancel");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger HDMA with length
din <= 8'h82; // 30h bytes
wr <= 1'd1;
#period
wr <= 1'd0;
#16000
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
$display("canceling");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger HDMA with length
din <= 8'h00; // stop
wr <= 1'd1;
#period
wr <= 1'd0;
#16000
sel_reg <= 1'b1;
addr <= 4'd5;
$display("Check FF55");
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
$display("Test Complete");
end
hdma_active <= 1'b1;
if (dma_delay>0) begin
dma_delay <= dma_delay - 5'd1;
end else begin
hdma_rd <= 1'b1;
hdma_cnt <= hdma_cnt + 1'd1;
hdma_16byte_cnt <= hdma_16byte_cnt - 1'd1;
if (!hdma_16byte_cnt) begin
hdma_length <= hdma_length - 1'd1;
hdma_state <= blocksent;
hdma_active <= 1'b0;
hdma_rd <= 1'b0;
end
end
end else begin
hdma_active <= 1'b0;
hdma_rd <= 1'b0;
hdma_enabled <= 1'b0;
hdma_length <= 8'h80; //7f+1
end
end
endcase
end
end
end
end
wire [7:0] length_m1 = hdma_length - 8'd1;
assign dout = sel_reg?
(addr==4'd5)?hdma_enabled?{1'b0,length_m1[6:0]}:
{1'b1,length_m1[6:0]}:
8'hFF:
8'hFF;
endmodule
`timescale 1 ns/100 ps // time-unit = 1 ns, precision = 100 ps
module hdma_tb;
// duration for each bit = 125 * timescale = 125 * 1 ns = 125ns // 8MHz
localparam period = 125;
reg reset = 1'd1;
reg clk = 1'd0;
reg speed = 1'b0;
// cpu register interface
reg sel_reg = 1'd0;
reg [3:0] addr = 4'd0;
reg wr = 1'd0;
wire [7:0] dout;
reg [7:0] din = 8'd0;
reg [1:0] lcd_mode = 2'd0;
// dma connection
wire hdma_rd;
wire hdma_active;
wire [15:0] hdma_source_addr;
wire [15:0] hdma_target_addr;
hdma hdma(
.reset ( reset ),
.clk ( clk ),
.speed ( speed ),
// cpu register interface
.sel_reg ( sel_reg ),
.addr ( addr ),
.wr ( wr ),
.dout ( dout ),
.din ( din ),
.lcd_mode ( lcd_mode ),
// dma connection
.hdma_rd ( hdma_rd ),
.hdma_active ( hdma_active ),
.hdma_source_addr ( hdma_source_addr ),
.hdma_target_addr ( hdma_target_addr )
);
always #62 clk <= !clk;
initial begin
reset <= 1'b0;
sel_reg <= 1'b1;
addr <= 4'd4;
#1000
sel_reg <= 1'b1;
addr <= 4'd1; // source h
din <= 8'h20;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
sel_reg <= 1'b1;
addr <= 4'd2; // source l
din <= 8'h40;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
sel_reg <= 1'b1;
addr <= 4'd3; // target h
din <= 8'h82;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
sel_reg <= 1'b1;
addr <= 4'd4; // target l
din <= 8'h00;
wr <= 1'd1;
#period
wr <= 1'd0;
#period
$display("GDMA");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger GDMA with length
din <= 8'h01; // 20h bytes
wr <= 1'd1;
#period
wr <= 1'd0;
#8000
lcd_mode <= 2'd1;
#2000
lcd_mode <= 2'd0;
#8000
$display("HDMA");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger HDMA with length
din <= 8'h82; // 30h bytes
wr <= 1'd1;
#period
wr <= 1'd0;
#16000
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
lcd_mode <= 2'd0;
#16000
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
lcd_mode <= 2'd0;
#16000
sel_reg <= 1'b1;
addr <= 4'd5;
$display("Check FF55");
#1000
$display("HDMA with cancel");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger HDMA with length
din <= 8'h82; // 30h bytes
wr <= 1'd1;
#period
wr <= 1'd0;
#16000
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
$display("canceling");
sel_reg <= 1'b1;
addr <= 4'd5; // trigger HDMA with length
din <= 8'h00; // stop
wr <= 1'd1;
#period
wr <= 1'd0;
#16000
sel_reg <= 1'b1;
addr <= 4'd5;
$display("Check FF55");
lcd_mode <= 2'd2;
#2000
lcd_mode <= 2'd3;
#2000
$display("Test Complete");
end
endmodule

View File

@@ -49,10 +49,10 @@ wire resetdiv = cpu_sel && cpu_wr && (cpu_addr == 2'b00); //resetdiv also resets
reg [9:0] clk_div;
always @(posedge clk or posedge resetdiv)
if(resetdiv)
clk_div <= 10'd6;
clk_div <= 10'd2;
else
if (reset)
clk_div <= 10'd6;
clk_div <= 10'd8;
else
clk_div <= clk_div + 10'd1;

View File

@@ -38,6 +38,7 @@ module video (
output lcd_clkena,
output [14:0] lcd_data,
output reg irq,
output reg vblank_irq,
// vram connection
output [1:0] mode,
@@ -57,6 +58,7 @@ module video (
localparam STAGE2 = 9'd250; // oam + disp + pause
localparam OAM_LEN = 80;
localparam OAM_LEN16 = OAM_LEN/16;
localparam MODE3_OFFSET = 8'd16;
wire sprite_pixel_active;
wire [1:0] sprite_pixel_data;
@@ -108,7 +110,7 @@ sprites sprites (
// give dma access to oam
wire [7:0] oam_addr = dma_active?dma_addr[7:0]:cpu_addr;
wire oam_wr = dma_active?(dma_cnt[1:0] == 2):(cpu_wr && cpu_sel_oam);
wire oam_wr = dma_active?(dma_cnt[1:0] == 2):(cpu_wr && cpu_sel_oam && !(mode==3 || mode==2));
wire [7:0] oam_di = dma_active?dma_data:cpu_di;
@@ -140,6 +142,7 @@ wire [7:0] ly = v_cnt;
// ff45 line counter compare
wire lyc_match = (ly == lyc);
reg [7:0] lyc;
reg lyc_changed=0;
reg [7:0] bgp;
reg [7:0] obp0;
@@ -195,12 +198,14 @@ end
// ------------------------------- IRQs -------------------------------
// --------------------------------------------------------------------
always @(posedge clk_reg) begin //TODO: have to check if this is correct
always @(posedge clk_reg) begin
irq <= 1'b0;
vblank_irq <= 1'b0;
//TODO: investigate and fix timing of lyc=ly
// lyc=ly coincidence
if(stat[6] && (h_cnt == 1) && lyc_match)
if(stat[6] && h_cnt == 0 && lyc_match)
irq <= 1'b1;
if(stat[6] && lyc_changed == 1 && h_cnt > 0 && lyc_match)
irq <= 1'b1;
// begin of oam phase
@@ -208,8 +213,11 @@ always @(posedge clk_reg) begin //TODO: have to check if this is correct
irq <= 1'b1;
// begin of vblank
if(stat[4] && (h_cnt == 455) && (v_cnt == 143))
irq <= 1'b1;
if((h_cnt == 455) && (v_cnt == 143)) begin
if (stat[4])
irq <= 1'b1;
vblank_irq <= 1'b1;
end
// begin of hblank
if(stat[3] && (h_cnt == OAM_LEN + 160 + hextra))
@@ -243,6 +251,7 @@ always @(posedge clk_reg) begin
end
end else begin
lyc_changed<=0;
if(cpu_sel_reg && cpu_wr) begin
case(cpu_addr)
8'h40: lcdc <= cpu_di;
@@ -250,7 +259,10 @@ always @(posedge clk_reg) begin
8'h42: scy <= cpu_di;
8'h43: scx <= cpu_di;
// a write to 4 is supposed to reset the v_cnt
8'h45: lyc <= cpu_di;
8'h45: begin
lyc <= cpu_di;
lyc_changed<=1;
end
8'h46: dma <= cpu_di;
8'h47: bgp <= cpu_di;
8'h48: obp0 <= cpu_di;
@@ -264,20 +276,23 @@ always @(posedge clk_reg) begin
bgpi_ai <= cpu_di[7];
end
8'h69: begin
bgpd[bgpi] <= cpu_di;
if (bgpi_ai)
bgpi <= bgpi + 6'h1;
if (mode != 3) begin
bgpd[bgpi] <= cpu_di;
if (bgpi_ai)
bgpi <= bgpi + 6'h1;
end
end
8'h6A: begin
obpi <= cpu_di[5:0];
obpi_ai <= cpu_di[7];
end
8'h6B: begin
obpd[obpi] <= cpu_di;
if (obpi_ai)
obpi <= obpi + 6'h1;
if (mode != 3) begin
obpd[obpi] <= cpu_di;
if (obpi_ai)
obpi <= obpi + 6'h1;
end
end
endcase
end
end
@@ -299,9 +314,9 @@ assign cpu_do =
(cpu_addr == 8'h4b)?wx:
isGBC?
(cpu_addr == 8'h68)?{bgpi_ai,1'd0,bgpi}:
(cpu_addr == 8'h69)?bgpd[bgpi]:
(cpu_addr == 8'h69 && mode != 3)?bgpd[bgpi]:
(cpu_addr == 8'h6a)?{obpi_ai,1'd0,obpi}:
(cpu_addr == 8'h6b)?obpd[obpi]:
(cpu_addr == 8'h6b && mode != 3)?obpd[obpi]:
8'hff:
8'hff;
@@ -351,14 +366,25 @@ wire [14:0] sprite_pix = isGBC?{obpd[sprite_palette_index+1][6:0],obpd[sprite_pa
// - there's a sprite at the current position
// - the sprites prioroty bit is 0, or
// - the sprites priority is 1 and the backrgound color is 00
// - GBC : BG priority is 0
// - GBC : BG priority is 0
// - GBC : BG priority is 1 and the backrgound color is 00
wire bg_piority = isGBC&&stage2_bgp_buffer_pix[stage2_rptr][3];
wire sprite_pixel_visible =
sprite_pixel_active && lcdc_spr_ena && (~bg_piority) &&
((!sprite_pixel_prio) || (stage2_buffer[stage2_rptr] == 2'b00));
wire [1:0] bg_color = stage2_buffer[stage2_rptr];
reg sprite_pixel_visible;
always @(*) begin
sprite_pixel_visible = 1'b0;
if (sprite_pixel_active && lcdc_spr_ena) begin // pixel active and sprites enabled
if (bg_color == 2'b00) // background color = 0
sprite_pixel_visible = 1'b1;
else if (!bg_piority && !sprite_pixel_prio) // sprite has priority enabled and background priority disabled
sprite_pixel_visible = 1'b1;
end
end
always @(posedge clk) begin
if(h_cnt == 455) begin
stage2_wptr <= 8'h00;
@@ -462,9 +488,9 @@ wire vblank = (v_cnt >= 144);
// x scroll & 7 needs one more memory read per line
reg [1:0] hextra_tiles;
wire [7:0] hextra = { 3'b000, hextra_tiles, 3'b000 };
wire [7:0] hextra = { 3'b000, hextra_tiles, 3'b000 }+MODE3_OFFSET;
wire hblank = ((h_cnt < OAM_LEN) || (h_cnt >= 160+OAM_LEN+hextra));
wire oam = (h_cnt < OAM_LEN); // 80 clocks oam
wire oam = (h_cnt <= OAM_LEN); // 80 clocks oam
wire stage2 = ((h_cnt >= STAGE2) && (h_cnt < STAGE2+160)); // output out of stage2
// first valid pixels are delivered 8 clocks after end of hblank
@@ -546,6 +572,7 @@ wire bg_tile_obj_rd = (!vblank) && (!hblank) && (h_cnt[2:1] == 2'b11);
// Mode 10: oam
// Mode 11: oam and vram
assign mode =
(ly <= 144 && h_cnt<4)?2'b00: //AntonioND https://github.com/AntonioND/giibiiadvance/blob/master/docs/TCAGBD.pdf
!lcdc_on?2'b00:
vblank?2'b01:
oam?2'b10:
@@ -565,11 +592,11 @@ wire win_start = lcdc_win_ena && (v_cnt >= wy_r) && de && (wx_r >= 7) && (pcnt =
// each memory access takes two cycles
always @(negedge clk or negedge lcdc_on) begin
always @(negedge clk) begin
if (!lcdc_on) begin // don't increase counters if lcdoff
//reset counters
h_cnt <= 9'd0;
h_cnt <= 9'd6;
v_cnt <= 8'd0;
end else begin