diff --git a/cores/gameboy/gb.v b/cores/gameboy/gb.v index 91fa300..aa3d3e7 100644 --- a/cores/gameboy/gb.v +++ b/cores/gameboy/gb.v @@ -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 ) diff --git a/cores/gameboy/hdma.v b/cores/gameboy/hdma.v index aaac02d..8a86755 100644 --- a/cores/gameboy/hdma.v +++ b/cores/gameboy/hdma.v @@ -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 \ No newline at end of file diff --git a/cores/gameboy/timer.v b/cores/gameboy/timer.v index 65d5510..eab0ac3 100644 --- a/cores/gameboy/timer.v +++ b/cores/gameboy/timer.v @@ -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; diff --git a/cores/gameboy/video.v b/cores/gameboy/video.v index 76e2ff5..5ec2852 100644 --- a/cores/gameboy/video.v +++ b/cores/gameboy/video.v @@ -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