mirror of
https://github.com/MiSTer-devel/Gameboy_MiSTer.git
synced 2026-04-19 03:04:09 +00:00
39
Gameboy.sv
39
Gameboy.sv
@@ -179,6 +179,7 @@ localparam CONF_STR5 = {
|
||||
"A,Save Backup RAM;",
|
||||
"-;",
|
||||
"O34,Aspect ratio,4:3,10:9,16:9;",
|
||||
"O5,Stabilize video(buffer),Off,On;",
|
||||
"O78,Stereo mix,none,25%,50%,100%;",
|
||||
"-;",
|
||||
"O2,Boot,Normal,Fast;",
|
||||
@@ -590,15 +591,16 @@ gb gb (
|
||||
// the lcd to vga converter
|
||||
wire [7:0] video_r, video_g, video_b;
|
||||
wire video_hs, video_vs, video_bl;
|
||||
wire ce_pix;
|
||||
|
||||
lcd lcd (
|
||||
.pclk ( clk_sys_old),
|
||||
.pce ( ce_pix ),
|
||||
.clk ( clk_cpu ),
|
||||
.clk_sys( clk_sys ),
|
||||
.ce_cpu ( ce_cpu ),
|
||||
.isGBC ( isGBC ),
|
||||
|
||||
.tint ( status[1] ),
|
||||
.inv ( status[12] ),
|
||||
.double_buffer( status[5]),
|
||||
|
||||
// Palettes
|
||||
.pal1 (palette[127:104]),
|
||||
@@ -617,11 +619,12 @@ lcd lcd (
|
||||
.blank ( video_bl ),
|
||||
.r ( video_r ),
|
||||
.g ( video_g ),
|
||||
.b ( video_b )
|
||||
.b ( video_b ),
|
||||
.ce_pix ( ce_pix )
|
||||
);
|
||||
|
||||
assign VGA_SL = 0;
|
||||
assign CE_PIXEL = ce_o & (direct_video | !line_cnt);
|
||||
assign CE_PIXEL = ce_o;
|
||||
|
||||
reg hs_o, vs_o, ce_o;
|
||||
always @(posedge CLK_VIDEO) begin
|
||||
@@ -657,20 +660,16 @@ gamma_fast gamma
|
||||
|
||||
//////////////////////////////// CE ////////////////////////////////////
|
||||
|
||||
wire clk_sys_old = clk_sys & ce_sys;
|
||||
wire ce_cpu2x = ce_pix;
|
||||
wire clk_cpu = clk_sys & ce_cpu;
|
||||
wire clk_cpu2x = clk_sys & ce_pix;
|
||||
wire clk_cpu2x = clk_sys & ce_cpu2x;
|
||||
|
||||
reg ce_pix, ce_pix2, ce_cpu,ce_sys;
|
||||
reg ce_pix2, ce_cpu, ce_cpu2x;
|
||||
always @(negedge clk_sys) begin
|
||||
reg [3:0] div = 0;
|
||||
reg [1:0] ce_pix_r;
|
||||
|
||||
div <= div + 1'd1;
|
||||
ce_sys <= !div[0];
|
||||
ce_pix <= !div[2:0];
|
||||
ce_cpu <= !div[3:0];
|
||||
div <= div + 1'd1;
|
||||
ce_cpu2x <= !div[2:0];
|
||||
ce_cpu <= !div[3:0];
|
||||
|
||||
ce_pix_r <= {ce_pix_r[0], ce_pix};
|
||||
ce_pix2 <= |ce_pix_r;
|
||||
@@ -864,16 +863,4 @@ always @(posedge clk_sys) begin
|
||||
end
|
||||
end
|
||||
|
||||
reg [1:0] line_cnt;
|
||||
always @(posedge clk_sys_old) begin
|
||||
reg old_hs;
|
||||
reg old_vs;
|
||||
|
||||
old_vs <= video_vs;
|
||||
old_hs <= video_hs;
|
||||
|
||||
if(old_hs & ~video_hs) line_cnt <= line_cnt + 1'd1;
|
||||
if(old_vs & ~video_vs) line_cnt <= 0;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
16
ReadMe.md
16
ReadMe.md
@@ -15,19 +15,7 @@ Core supports custom palettes (*.gbp) which should be placed into Gameboy folder
|
||||
To autoload custom palette at startup rename it to boot0.rom
|
||||
To autoload favorite game at startup rename it to boot2.rom
|
||||
|
||||
## Analog output
|
||||
Due to using a weird video resolution and frequencies (from a TV signal perspective) the core needs help from the scaler to output a 15KHz Signal.
|
||||
|
||||
For now you can append this to your MiSTer.ini configuration file (credit goes to ghogan42/soltan_g42) that enables the vga_scaler to be active when using this core
|
||||
|
||||
**Be aware that you will lose HDMI output for this core**:
|
||||
|
||||
```ini
|
||||
[Gameboy]
|
||||
video_mode=320,8,32,24,240,4,3,16,6048
|
||||
vga_scaler=1
|
||||
vsync_adjust=2
|
||||
vscale_mode=1
|
||||
```
|
||||
## Video output
|
||||
The Gameboy can disable video output at any time which causes problems with vsync_adjust=2 or analog video during screen transitions. Enable the Stabilize video option to fix this at the cost of some latency.
|
||||
|
||||
|
||||
|
||||
199
lcd.v
199
lcd.v
@@ -4,11 +4,13 @@
|
||||
// The gameboy lcd runs from a shift register which is filled at 4194304 pixels/sec
|
||||
|
||||
module lcd (
|
||||
input clk,
|
||||
input clk_sys, // 67.108864 MHz
|
||||
input ce_cpu, // 4.194304 Mhz
|
||||
input clkena,
|
||||
input [14:0] data,
|
||||
input [1:0] mode,
|
||||
input isGBC,
|
||||
input double_buffer,
|
||||
|
||||
//palette
|
||||
input [23:0] pal1,
|
||||
@@ -19,11 +21,10 @@ module lcd (
|
||||
input tint,
|
||||
input inv,
|
||||
|
||||
// pixel clock
|
||||
input pclk,
|
||||
input pce,
|
||||
input on,
|
||||
|
||||
|
||||
output reg ce_pix,
|
||||
|
||||
// VGA output
|
||||
output reg hs,
|
||||
output reg vs,
|
||||
@@ -35,90 +36,123 @@ module lcd (
|
||||
|
||||
|
||||
reg [14:0] vbuffer_inptr;
|
||||
reg vbuffer_write;
|
||||
|
||||
reg [14:0] vbuffer_outptr;
|
||||
reg [14:0] vbuffer_lineptr;
|
||||
reg vbuffer_in_bank, vbuffer_out_bank;
|
||||
|
||||
|
||||
//image buffer 160x144x2bits for now , later 15bits for cgb
|
||||
dpram #(15,15) vbuffer (
|
||||
.clock_a (clk),
|
||||
.address_a (vbuffer_inptr),
|
||||
//image buffer 160x144x15bits for cgb
|
||||
dpram #(16,15) vbuffer (
|
||||
.clock_a (clk_sys & ce_cpu),
|
||||
.address_a (vbuffer_in_bank ? (vbuffer_inptr+16'd23040) : vbuffer_inptr),
|
||||
.wren_a (clkena),
|
||||
.data_a (data),
|
||||
.q_a (),
|
||||
|
||||
.clock_b (pclk),
|
||||
.address_b (vbuffer_outptr),
|
||||
.clock_b (clk_sys & ce_pix),
|
||||
.address_b (vbuffer_out_bank ? (vbuffer_outptr+16'd23040) : vbuffer_outptr),
|
||||
.wren_b (1'b0), //only reads
|
||||
.data_b (),
|
||||
.q_b (pixel_reg)
|
||||
);
|
||||
|
||||
always @(posedge clk) begin
|
||||
if(!on || (mode==2'd01)) begin //lcd disabled of vsync restart pointer
|
||||
vbuffer_inptr <= 15'h0;
|
||||
end else begin
|
||||
|
||||
// end of vsync
|
||||
if(clkena) begin
|
||||
vbuffer_inptr <= vbuffer_inptr + 15'd1;
|
||||
end
|
||||
|
||||
end;
|
||||
end
|
||||
|
||||
|
||||
// Mode 00: h-blank
|
||||
// Mode 01: v-blank
|
||||
// Mode 10: oam
|
||||
// Mode 11: oam and vram
|
||||
|
||||
//
|
||||
parameter H = 160; // width of visible area
|
||||
parameter HFP = 16; // unused time before hsync
|
||||
parameter HS = 20; // width of hsync
|
||||
parameter HBP = 32; // unused time after hsync
|
||||
// total = 228
|
||||
parameter H = 160; // width of visible area
|
||||
parameter HFP = 103; // unused time before hsync
|
||||
parameter HS = 32; // width of hsync
|
||||
parameter HBP = 130; // unused time after hsync
|
||||
parameter HTOTAL = H+HFP+HS+HBP;
|
||||
// total = 425
|
||||
|
||||
parameter V = 576; // height of visible area
|
||||
parameter VFP = 2; // unused time before vsync
|
||||
parameter VS = 2; // width of vsync
|
||||
parameter VBP = 36; // unused time after vsync
|
||||
// total = 616
|
||||
parameter V = 144; // height of visible area
|
||||
parameter VS_START = 35; // start of vsync
|
||||
parameter VSTART = 105; // start of active video
|
||||
parameter VTOTAL = 264;
|
||||
|
||||
reg[7:0] h_cnt; // horizontal pixel counter
|
||||
reg[9:0] v_cnt; // vertical pixel counter
|
||||
reg[8:0] h_cnt; // horizontal pixel counter
|
||||
reg[8:0] v_cnt; // vertical pixel counter
|
||||
|
||||
// horizontal pixel counter
|
||||
reg [1:0] last_mode_h;
|
||||
always@(posedge pclk) begin
|
||||
if(pce) begin
|
||||
|
||||
if(h_cnt==H+HFP+HS+HBP-1) h_cnt <= 0;
|
||||
else h_cnt <= h_cnt + 1'd1;
|
||||
|
||||
// generate positive hsync signal
|
||||
if(h_cnt == H+HFP) hs <= 1'b1;
|
||||
if(h_cnt == H+HFP+HS) hs <= 1'b0;
|
||||
// (67108864 / 32 / 228 / 154) == (67108864 / 10 / 425.6 / 264) == 59.7275Hz
|
||||
// We need 4256 cycles per line so 1 pixel clock cycle needs to be 6 cycles longer.
|
||||
// 424x10 + 1x16 cycles
|
||||
reg [3:0] pix_div_cnt;
|
||||
always @(posedge clk_sys) begin
|
||||
pix_div_cnt <= pix_div_cnt + 1'd1;
|
||||
if (h_cnt != HTOTAL-1 && pix_div_cnt == 4'd9) // Longer cycle at the last pixel
|
||||
pix_div_cnt <= 0;
|
||||
|
||||
end
|
||||
ce_pix <= !pix_div_cnt;
|
||||
end
|
||||
|
||||
// veritical pixel counter
|
||||
reg [1:0] last_mode_v;
|
||||
always@(posedge pclk) begin
|
||||
if(pce) begin
|
||||
// the vertical counter is processed at the begin of each hsync
|
||||
if(h_cnt == H+HFP+HS+HBP-1) begin
|
||||
if(v_cnt==VS+VFP+V+VBP-1) v_cnt <= 0;
|
||||
else v_cnt <= v_cnt + 1'd1;
|
||||
wire lcd_off = !on || (mode == 2'd01);
|
||||
reg old_lcd_off;
|
||||
reg hb, vb;
|
||||
always @(posedge clk_sys) begin
|
||||
|
||||
if (ce_cpu) begin
|
||||
if(clkena) begin
|
||||
if (~lcd_off) begin
|
||||
vbuffer_inptr <= vbuffer_inptr + 1'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (!pix_div_cnt) begin
|
||||
// generate positive hsync signal
|
||||
if(h_cnt == H+HFP+HS) hs <= 0;
|
||||
if(h_cnt == H+HFP) begin
|
||||
hs <= 1;
|
||||
|
||||
// generate positive vsync signal
|
||||
if(v_cnt == V+VFP) vs <= 1'b1;
|
||||
if(v_cnt == V+VFP+VS) vs <= 1'b0;
|
||||
if(v_cnt == VS_START) vs <= 1;
|
||||
if(v_cnt == VS_START+3) vs <= 0;
|
||||
end
|
||||
|
||||
// Hblank
|
||||
if(h_cnt == 0) hb <= 0;
|
||||
if(h_cnt >= H) hb <= 1;
|
||||
|
||||
// Vblank
|
||||
if(v_cnt == VSTART) vb <= 0;
|
||||
if(v_cnt >= VSTART+V) vb <= 1;
|
||||
|
||||
end
|
||||
|
||||
if(ce_pix) begin
|
||||
|
||||
h_cnt <= h_cnt + 1'd1;
|
||||
if(h_cnt == HTOTAL-1) begin
|
||||
h_cnt <= 0;
|
||||
if(~&v_cnt) v_cnt <= v_cnt + 1'd1;
|
||||
if( (double_buffer || lcd_off) && v_cnt >= VTOTAL-1) v_cnt <= 0;
|
||||
|
||||
if(v_cnt == VSTART-1) begin
|
||||
vbuffer_outptr <= 0;
|
||||
// Read from write buffer if it is far enough ahead
|
||||
vbuffer_out_bank <= (vbuffer_inptr >= (160*60) || ~double_buffer) ? vbuffer_in_bank : ~vbuffer_in_bank;
|
||||
end
|
||||
end
|
||||
|
||||
// visible area?
|
||||
if(~hb & ~vb) begin
|
||||
vbuffer_outptr <= vbuffer_outptr + 1'd1;
|
||||
end
|
||||
end
|
||||
|
||||
old_lcd_off <= lcd_off;
|
||||
if(~old_lcd_off & lcd_off) begin //lcd disabled or vsync restart pointer
|
||||
vbuffer_inptr <= 0;
|
||||
vbuffer_in_bank <= ~vbuffer_in_bank;
|
||||
end
|
||||
|
||||
if (old_lcd_off & ~lcd_off & ~double_buffer & vb) begin // lcd enabled
|
||||
h_cnt <= 0;
|
||||
v_cnt <= 0;
|
||||
hs <= 0;
|
||||
vs <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -127,11 +161,11 @@ end
|
||||
// -------------------------------------------------------------------------------
|
||||
reg [14:0] pixel_reg;
|
||||
|
||||
always@(posedge pclk) begin
|
||||
always@(posedge clk_sys) begin
|
||||
reg blank_r;
|
||||
if(pce) begin
|
||||
if(ce_pix) begin
|
||||
// visible area?
|
||||
blank_r <= (v_cnt >= V) || (h_cnt >= H);
|
||||
blank_r <= (hb | vb);
|
||||
blank <= blank_r;
|
||||
r <= blank_r ? 8'd0 : (tint||isGBC) ? pal_r : grey;
|
||||
g <= blank_r ? 8'd0 : (tint||isGBC) ? pal_g : grey;
|
||||
@@ -139,39 +173,6 @@ always@(posedge pclk) begin
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
reg [7:0] currentpixel;
|
||||
reg [1:0] linecnt;
|
||||
always@(posedge pclk) begin
|
||||
|
||||
if(pce) begin
|
||||
if(h_cnt == H+HFP+HS+HBP-1) begin
|
||||
|
||||
//reset output at vsync
|
||||
if(v_cnt == V+VFP) begin
|
||||
vbuffer_outptr <= 15'd0;
|
||||
vbuffer_lineptr <= 15'd0;
|
||||
currentpixel <= 8'd0;
|
||||
linecnt <= 2'd3;
|
||||
end
|
||||
end else
|
||||
// visible area?
|
||||
if((v_cnt < V) && (h_cnt < H)) begin
|
||||
vbuffer_outptr <= vbuffer_lineptr + currentpixel;
|
||||
if (currentpixel + 8'd1 == 160) begin
|
||||
currentpixel <= 8'd0;
|
||||
linecnt <= linecnt - 2'd1;
|
||||
|
||||
//increment vbuffer_lineptr after 4 lines
|
||||
if (!linecnt)
|
||||
vbuffer_lineptr <= vbuffer_lineptr + 15'd160;
|
||||
end else
|
||||
currentpixel <= currentpixel + 8'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
wire [14:0] pixel = on?isGBC?pixel_reg:
|
||||
{13'd0,(pixel_reg[1:0] ^ {inv,inv})}: //invert gb only
|
||||
15'd0;
|
||||
|
||||
Reference in New Issue
Block a user