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

[Gameboy] Update from MiSTer

This commit is contained in:
Gyorgy Szombathelyi
2019-02-12 14:21:01 +01:00
parent 9ee3d190b5
commit 4807107c19
18 changed files with 1962 additions and 1203 deletions

170
cores/gameboy/dpram.vhd Normal file
View File

@@ -0,0 +1,170 @@
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
ENTITY dpram IS
generic (
addr_width : integer := 8;
data_width : integer := 8
);
PORT
(
clock_a : IN STD_LOGIC;
clken_a : IN STD_LOGIC := '1';
address_a : IN STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0);
data_a : IN STD_LOGIC_VECTOR (data_width-1 DOWNTO 0);
wren_a : IN STD_LOGIC := '0';
q_a : OUT STD_LOGIC_VECTOR (data_width-1 DOWNTO 0);
clock_b : IN STD_LOGIC;
clken_b : IN STD_LOGIC := '1';
address_b : IN STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0);
data_b : IN STD_LOGIC_VECTOR (data_width-1 DOWNTO 0) := (others => '0');
wren_b : IN STD_LOGIC := '0';
q_b : OUT STD_LOGIC_VECTOR (data_width-1 DOWNTO 0)
);
END dpram;
ARCHITECTURE SYN OF dpram IS
BEGIN
altsyncram_component : altsyncram
GENERIC MAP (
address_reg_b => "CLOCK1",
clock_enable_input_a => "NORMAL",
clock_enable_input_b => "NORMAL",
clock_enable_output_a => "BYPASS",
clock_enable_output_b => "BYPASS",
indata_reg_b => "CLOCK1",
intended_device_family => "Cyclone III",
lpm_type => "altsyncram",
numwords_a => 2**addr_width,
numwords_b => 2**addr_width,
operation_mode => "BIDIR_DUAL_PORT",
outdata_aclr_a => "NONE",
outdata_aclr_b => "NONE",
outdata_reg_a => "UNREGISTERED",
outdata_reg_b => "UNREGISTERED",
power_up_uninitialized => "FALSE",
read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ",
read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ",
widthad_a => addr_width,
widthad_b => addr_width,
width_a => data_width,
width_b => data_width,
width_byteena_a => 1,
width_byteena_b => 1,
wrcontrol_wraddress_reg_b => "CLOCK1"
)
PORT MAP (
address_a => address_a,
address_b => address_b,
clock0 => clock_a,
clock1 => clock_b,
clocken0 => clken_a,
clocken1 => clken_b,
data_a => data_a,
data_b => data_b,
wren_a => wren_a,
wren_b => wren_b,
q_a => q_a,
q_b => q_b
);
END SYN;
--------------------------------------------------------------
-- Dual port Block RAM different parameters on ports
--------------------------------------------------------------
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
entity dpram_dif is
generic (
addr_width_a : integer := 8;
data_width_a : integer := 8;
addr_width_b : integer := 8;
data_width_b : integer := 8;
mem_init_file : string := " "
);
PORT
(
clock : in STD_LOGIC;
address_a : in STD_LOGIC_VECTOR (addr_width_a-1 DOWNTO 0);
data_a : in STD_LOGIC_VECTOR (data_width_a-1 DOWNTO 0) := (others => '0');
enable_a : in STD_LOGIC := '1';
wren_a : in STD_LOGIC := '0';
q_a : out STD_LOGIC_VECTOR (data_width_a-1 DOWNTO 0);
cs_a : in std_logic := '1';
address_b : in STD_LOGIC_VECTOR (addr_width_b-1 DOWNTO 0) := (others => '0');
data_b : in STD_LOGIC_VECTOR (data_width_b-1 DOWNTO 0) := (others => '0');
enable_b : in STD_LOGIC := '1';
wren_b : in STD_LOGIC := '0';
q_b : out STD_LOGIC_VECTOR (data_width_b-1 DOWNTO 0);
cs_b : in std_logic := '1'
);
end entity;
ARCHITECTURE SYN OF dpram_dif IS
signal q0 : std_logic_vector((data_width_a - 1) downto 0);
signal q1 : std_logic_vector((data_width_b - 1) downto 0);
BEGIN
q_a<= q0 when cs_a = '1' else (others => '1');
q_b<= q1 when cs_b = '1' else (others => '1');
altsyncram_component : altsyncram
GENERIC MAP (
address_reg_b => "CLOCK1",
clock_enable_input_a => "NORMAL",
clock_enable_input_b => "NORMAL",
clock_enable_output_a => "BYPASS",
clock_enable_output_b => "BYPASS",
indata_reg_b => "CLOCK1",
intended_device_family => "Cyclone V",
lpm_type => "altsyncram",
numwords_a => 2**addr_width_a,
numwords_b => 2**addr_width_b,
operation_mode => "BIDIR_DUAL_PORT",
outdata_aclr_a => "NONE",
outdata_aclr_b => "NONE",
outdata_reg_a => "UNREGISTERED",
outdata_reg_b => "UNREGISTERED",
power_up_uninitialized => "FALSE",
read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ",
read_during_write_mode_port_b => "NEW_DATA_NO_NBE_READ",
init_file => mem_init_file,
widthad_a => addr_width_a,
widthad_b => addr_width_b,
width_a => data_width_a,
width_b => data_width_b,
width_byteena_a => 1,
width_byteena_b => 1,
wrcontrol_wraddress_reg_b => "CLOCK1"
)
PORT MAP (
address_a => address_a,
address_b => address_b,
clock0 => clock,
clock1 => clock,
clocken0 => enable_a,
clocken1 => enable_b,
data_a => data_a,
data_b => data_b,
wren_a => wren_a and cs_a,
wren_b => wren_b and cs_b,
q_a => q0,
q_b => q1
);
END SYN;

View File

@@ -171,22 +171,22 @@ set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF
set_global_assignment -name VERILOG_FILE gb_mist.v
set_global_assignment -name SYSTEMVERILOG_FILE sdram.sv
set_global_assignment -name VERILOG_FILE sigma_delta_dac.v
set_global_assignment -name VHDL_FILE gbc_snd.vhd
set_global_assignment -name VERILOG_FILE data_io.v
set_global_assignment -name VERILOG_FILE user_io.v
set_global_assignment -name VERILOG_FILE osd.v
set_global_assignment -name QIP_FILE pll.qip
set_global_assignment -name VERILOG_FILE gb.v
set_global_assignment -name VERILOG_FILE lcd.v
set_global_assignment -name QIP_FILE boot_rom.qip
set_global_assignment -name VHDL_FILE spram.vhd
set_global_assignment -name VHDL_FILE dpram.vhd
set_global_assignment -name VHDL_FILE gbc_snd.vhd
set_global_assignment -name VERILOG_FILE gb.v
set_global_assignment -name VERILOG_FILE video.v
set_global_assignment -name VERILOG_FILE hdma.v
set_global_assignment -name VERILOG_FILE sprites.v
set_global_assignment -name VERILOG_FILE sprite.v
set_global_assignment -name VERILOG_FILE sprite_sort.v
set_global_assignment -name VERILOG_FILE timer.v
set_global_assignment -name QIP_FILE iram.qip
set_global_assignment -name QIP_FILE vram.qip
set_global_assignment -name QIP_FILE zpram.qip
set_global_assignment -name QIP_FILE boot_rom.qip
set_global_assignment -name VHDL_FILE t80/T80.vhd
set_global_assignment -name VHDL_FILE t80/Z80.vhd
set_global_assignment -name VHDL_FILE t80/T80_Reg.vhd

View File

@@ -21,11 +21,14 @@
module gb (
input reset,
input clk,
input clk,
input clk2x,
input fast_boot,
input [7:0] joystick,
input isGBC,
input isGBC_game,
// cartridge interface
// can adress up to 1MB ROM
output [15:0] cart_addr,
@@ -33,16 +36,22 @@ module gb (
output cart_wr,
input [7:0] cart_do,
output [7:0] cart_di,
//gbc bios interface
output [11:0] gbc_bios_addr,
input [7:0] gbc_bios_do,
// audio
output [15:0] audio_l,
output [15:0] audio_r,
// lcd interface
output lcd_clkena,
output [1:0] lcd_data,
output [14:0] lcd_data,
output [1:0] lcd_mode,
output lcd_on
output lcd_on,
output speed //GBC
);
// include cpu
@@ -50,9 +59,11 @@ wire [15:0] cpu_addr;
wire [7:0] cpu_do;
wire sel_timer = (cpu_addr[15:4] == 12'hff0) && (cpu_addr[3:2] == 2'b01);
wire sel_video_reg = cpu_addr[15:4] == 12'hff4;
wire sel_video_reg = (cpu_addr[15:4] == 12'hff4) || (isGBC && (cpu_addr[15:2] == 14'h3fda)); //video and oam dma (+ ff68-ff6B when gbc)
wire sel_video_oam = cpu_addr[15:8] == 8'hfe;
wire sel_joy = cpu_addr == 16'hff00; // joystick controller
wire sel_sb = cpu_addr == 16'hff01; // serial SB - Serial transfer data
wire sel_sc = cpu_addr == 16'hff02; // SC - Serial Transfer Control (R/W)
wire sel_rom = !cpu_addr[15]; // lower 32k are rom
wire sel_cram = cpu_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire sel_vram = cpu_addr[15:13] == 3'b100; // 8k video ram at $8000
@@ -63,26 +74,55 @@ wire sel_zpram = (cpu_addr[15:7] == 9'b111111111) && // 127 bytes zero pageram a
(cpu_addr != 16'hffff);
wire sel_audio = (cpu_addr[15:8] == 8'hff) && // audio reg ff10 - ff3f
((cpu_addr[7:5] == 3'b001) || (cpu_addr[7:4] == 4'b0001));
//DMA can select from $0000 to $F100
wire dma_sel_rom = !dma_addr[15]; // lower 32k are rom
wire dma_sel_cram = dma_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire dma_sel_vram = dma_addr[15:13] == 3'b100; // 8k video ram at $8000
wire dma_sel_iram = (dma_addr[15:14] == 2'b11) && (dma_addr[15:8] != 8'hff); // 8k internal ram at $c000
//CGB
wire sel_vram_bank = (cpu_addr==16'hff4f);
wire sel_iram_bank = (cpu_addr==16'hff70);
wire sel_hdma = (cpu_addr[15:4]==12'hff5) &&
((cpu_addr[3:0]!=4'd0)&&(cpu_addr[3:0]< 4'd6)); //HDMA FF51-FF55
wire sel_key1 = cpu_addr == 16'hff4d; // KEY1 - CGB Mode Only - Prepare Speed Switch
wire sel_rp = cpu_addr == 16'hff56; //FF56 - RP - CGB Mode Only - Infrared Communications Port
//HDMA can select from $0000 to $7ff0 or A000-DFF0
wire hdma_sel_rom = !hdma_source_addr[15]; // lower 32k are rom
wire hdma_sel_cram = hdma_source_addr[15:13] == 3'b101; // 8k cart ram at $a000
wire hdma_sel_iram = hdma_source_addr[15:13] == 3'b110; // 8k internal ram at $c000-$dff0
// the boot roms sees a special $42 flag in $ff50 if it's supposed to to a fast boot
wire sel_fast = fast_boot && cpu_addr == 16'hff50 && boot_rom_enabled;
wire [7:0] sc_r = {sc_start,6'h3F,sc_shiftclock};
// http://gameboy.mongenel.com/dmg/asmmemmap.html
wire [7:0] cpu_di =
irq_ack?irq_vec:
sel_fast?8'h42: // fast boot flag
sel_if?{3'b111, if_r}: // interrupt flag register
isGBC&&sel_rp?8'h02:
isGBC&&sel_iram_bank?{5'h1f,iram_bank}:
isGBC&&sel_vram_bank?{7'h7f,vram_bank}:
isGBC&&sel_hdma?{hdma_do}: //hdma GBC
isGBC&&sel_key1?{cpu_speed,6'h3f,prepare_switch}: //key1 cpu speed register(GBC)
sel_joy?joy_do: // joystick register
sel_sb?8'hFF: // serial transfer data register
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_audio?audio_do: // audio registers
sel_rom?rom_do: // boot rom + cartridge rom
sel_cram?rom_do: // cartridge ram
sel_vram?vram_do: // vram
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_zpram?zpram_do: // zero page ram
sel_iram?iram_do: // internal ram
sel_ie?{3'b000, ie_r}: // interrupt enable register
sel_if?{3'b000, if_r}: // interrupt flag register
8'hff;
wire cpu_wr_n;
@@ -90,11 +130,14 @@ wire cpu_rd_n;
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_stop;
GBse cpu (
.RESET_n ( !reset ),
.CLK_n ( clk ),
.CLKEN ( 1'b1 ),
.CLK_n ( clk_cpu ),
.CLKEN ( cpu_clken ),
.WAIT_n ( 1'b1 ),
.INT_n ( irq_n ),
.NMI_n ( 1'b1 ),
@@ -109,9 +152,34 @@ GBse cpu (
.BUSAK_n ( ),
.A ( cpu_addr ),
.DI ( cpu_di ),
.DO ( cpu_do )
.DO ( cpu_do ),
.STOP ( cpu_stop )
);
// --------------------------------------------------------------------
// --------------------- Speed Toggle KEY1 (GBC)-----------------------
// --------------------------------------------------------------------
wire clk_cpu = cpu_speed?clk2x:clk;
//wire clk_cpu = clk;
reg cpu_speed; // - 0 Normal mode (4MHz) - 1 Double Speed Mode (8MHz)
reg prepare_switch; // set to 1 to toggle speed
assign speed = cpu_speed;
always @(posedge clk2x) begin
if(reset) begin
cpu_speed <= 1'b0;
prepare_switch <= 1'b0;
end else if (sel_key1 && !cpu_wr_n && isGBC)begin
prepare_switch <= cpu_do[0];
end
if (isGBC && prepare_switch && cpu_stop) begin
cpu_speed <= !cpu_speed;
prepare_switch <= 1'b0;
end
end
// --------------------------------------------------------------------
// ------------------------------ audio -------------------------------
// --------------------------------------------------------------------
@@ -121,7 +189,7 @@ wire audio_wr = !cpu_wr_n && sel_audio;
wire [7:0] audio_do;
gbc_snd audio (
.clk ( clk ),
.clk ( clk2x ),
.reset ( reset ),
.s1_read ( audio_rd ),
@@ -134,23 +202,62 @@ gbc_snd audio (
.snd_right ( audio_r )
);
// --------------------------------------------------------------------
// -----------------------serial port(dummy)---------------------------
// --------------------------------------------------------------------
reg [3:0] serial_counter;
reg sc_start,sc_shiftclock;
reg serial_irq;
reg [8:0] serial_clk_div; //8192Hz
always @(posedge clk_cpu) begin
serial_irq <= 1'b0;
if(reset) begin
sc_start <= 1'b0;
sc_shiftclock <= 1'b0;
end else if (sel_sc && !cpu_wr_n) begin //cpu write
sc_start <= cpu_do[7];
sc_shiftclock <= cpu_do[0];
if (cpu_do[7]) begin //enable transfer
serial_clk_div <= 9'h1FF;
serial_counter <= 4'd8;
end
end else if (sc_start && sc_shiftclock) begin // serial transfer and serial clock enabled
serial_clk_div <= serial_clk_div - 9'd1;
if (serial_clk_div == 9'd0 && serial_counter)
serial_counter <= serial_counter - 4'd1;
if (!serial_counter) begin
serial_irq <= 1'b1; //trigger interrupt
sc_start <= 1'b0; //reset transfer state
serial_clk_div <= 9'h1FF;
serial_counter <= 4'd8;
end
end
end
// --------------------------------------------------------------------
// ------------------------------ inputs ------------------------------
// --------------------------------------------------------------------
wire [3:0] joy_p4 = { !joystick[2], !joystick[3], !joystick[1], !joystick[0] };
wire [3:0] joy_p5 = { !joystick[7], !joystick[6], !joystick[5], !joystick[4] };
reg [1:0] p54;
wire [3:0] joy_p4 = ~{ joystick[2], joystick[3], joystick[1], joystick[0] } | {4{p54[0]}};
wire [3:0] joy_p5 = ~{ joystick[7], joystick[6], joystick[5], joystick[4] } | {4{p54[1]}};
reg [1:0] p54;
always @(posedge clk) begin
if(reset)
p54 <= 2'b11;
else if(sel_joy && !cpu_wr_n)
p54 <= cpu_do[5:4];
always @(posedge clk_cpu) begin
if(reset) p54 <= 2'b00;
else if(sel_joy && !cpu_wr_n) p54 <= cpu_do[5:4];
end
wire [7:0] joy_do = { 2'b11, p54,
((!p54[0])?joy_p4:4'hf) & ((!p54[1])?joy_p5:4'hf) };
wire [7:0] joy_do = { 2'b11, p54, joy_p4 & joy_p5 };
// --------------------------------------------------------------------
// ---------------------------- interrupts ----------------------------
@@ -162,27 +269,27 @@ wire [7:0] joy_do = { 2'b11, p54,
wire irq_ack = !cpu_iorq_n && !cpu_m1_n;
// latch irq vector at the begin of the irq ack
reg [7:0] irq_vec;
always @(posedge irq_ack)
irq_vec <=
if_r[0]?8'h40: // vsync
if_r[1]?8'h48: // lcdc
if_r[2]?8'h50: // timer
if_r[3]?8'h58: // serial
if_r[4]?8'h60: // input
8'h55;
// irq vector
wire [7:0] irq_vec =
if_r[0]&&ie_r[0]?8'h40: // vsync
if_r[1]&&ie_r[1]?8'h48: // lcdc
if_r[2]&&ie_r[2]?8'h50: // timer
if_r[3]&&ie_r[3]?8'h58: // serial
if_r[4]&&ie_r[4]?8'h60: // input
8'h55;
wire vs = (lcd_mode == 2'b01);
reg vsD, vsD2;
reg [3:0] inputD, inputD2;
reg [7:0] inputD, inputD2;
// irq is low when an enable irq is active
wire irq_n = !(ie_r & if_r);
reg [4:0] if_r;
reg [4:0] ie_r; // writing $ffff sets the irq enable mask
always @(posedge clk) begin
always @(negedge clk_cpu) begin //negedge to trigger interrupt earlier
reg old_ack = 0;
if(reset) begin
ie_r <= 5'h00;
if_r <= 5'h00;
@@ -198,15 +305,20 @@ always @(posedge clk) begin
// timer_irq already is a 1 clock event
if(timer_irq) if_r[2] <= 1'b1;
// serial irq already is a 1 clock event
if(serial_irq) if_r[3] <= 1'b1;
// falling edge on any input line P10..P13
inputD <= joy_p4 | joy_p5;
inputD <= {joy_p4, joy_p5};
inputD2 <= inputD;
if(~inputD & inputD2) if_r[4] <= 1'b1;
// cpu acknowledges irq. this clears the active irq with hte
// highest priority
if(irq_ack) begin
old_ack <= irq_ack;
if(old_ack & ~irq_ack) begin
if(if_r[0] && ie_r[0]) if_r[0] <= 1'b0;
else if(if_r[1] && ie_r[1]) if_r[1] <= 1'b0;
else if(if_r[2] && ie_r[2]) if_r[2] <= 1'b0;
@@ -231,7 +343,7 @@ wire timer_irq;
wire [7:0] timer_do;
timer timer (
.reset ( reset ),
.clk ( clk ),
.clk ( clk_cpu ), //2x in fast mode
.irq ( timer_irq ),
@@ -252,11 +364,15 @@ wire [7:0] video_do;
wire [12:0] video_addr;
wire [15:0] dma_addr;
wire video_rd, dma_rd;
wire [7:0] dma_data = (dma_addr[15:14]==2'b11)?iram_do:cart_do;
wire [7:0] dma_data = dma_sel_iram?iram_do:dma_sel_vram?(isGBC&&vram_bank)?vram1_do:vram_do:cart_do;
video video (
.reset ( reset ),
.clk ( clk ),
.clk_reg ( clk_cpu ), //can be 2x in cgb double speed mode
.isGBC ( isGBC ),
.irq ( video_irq ),
@@ -276,23 +392,86 @@ video video (
.vram_addr ( video_addr ),
.vram_data ( vram_do ),
// vram connection bank1 (GBC)
.vram1_data ( vram1_do ),
.dma_rd ( dma_rd ),
.dma_addr ( dma_addr ),
.dma_data ( dma_data )
);
// total 8k vram from $8000 to $9fff
// total 8k/16k (CGB) vram from $8000 to $9fff
wire cpu_wr_vram = sel_vram && !cpu_wr_n;
wire [7:0] vram_do;
wire vram_wren = video_rd?1'b0:cpu_wr_vram;
wire [12:0] vram_addr = video_rd?video_addr:cpu_addr[12:0];
vram vram (
.clock ( clk ),
.address ( vram_addr ),
.wren ( vram_wren ),
.data ( cpu_do ),
.q ( vram_do )
reg vram_bank; //0-1 FF4F - VBK
wire [7:0] vram_do,vram1_do;
wire [7:0] vram_di = (hdma_rd&&isGBC)?
hdma_sel_iram?iram_do:
is_hdma_cart_addr?cart_do:
8'hFF:
cpu_do;
wire vram_wren = video_rd?1'b0:!vram_bank&&((hdma_rd&&isGBC)||cpu_wr_vram);
wire vram1_wren = video_rd?1'b0:vram_bank&&((hdma_rd&&isGBC)||cpu_wr_vram);
wire [12:0] vram_addr = video_rd?video_addr:(hdma_rd&&isGBC)?hdma_target_addr[12:0]:(dma_rd&&dma_sel_vram)?dma_addr[12:0]:cpu_addr[12:0];
spram #(13) vram0 (
.clock ( clk_cpu ),
.address ( vram_addr ),
.wren ( vram_wren ),
.data ( vram_di ),
.q ( vram_do )
);
//separate 8k for vbank1 for gbc because of BG reads
spram #(13) vram1 (
.clock ( clk_cpu ),
.address ( vram_addr ),
.wren ( vram1_wren ),
.data ( vram_di ),
.q ( vram1_do )
);
//GBC VRAM banking
always @(posedge clk_cpu) begin
if(reset)
vram_bank <= 1'd0;
else if((cpu_addr == 16'hff4f) && !cpu_wr_n && isGBC)
vram_bank <= cpu_do[0];
end
// --------------------------------------------------------------------
// -------------------------- HDMA engine(GBC) ------------------------
// --------------------------------------------------------------------
wire [15:0] hdma_source_addr;
wire [15:0] hdma_target_addr;
wire [7:0] hdma_do;
wire hdma_rd;
hdma hdma(
.reset ( reset ),
.clk ( clk2x ),
// cpu register interface
.sel_reg ( sel_hdma ),
.addr ( cpu_addr[3:0] ),
.wr ( !cpu_wr_n ),
.dout ( hdma_do ),
.din ( cpu_do ),
.lcd_mode ( lcd_mode ),
// dma connection
.hdma_rd ( hdma_rd ),
.hdma_source_addr ( hdma_source_addr ),
.hdma_target_addr ( hdma_target_addr )
);
// --------------------------------------------------------------------
@@ -302,8 +481,8 @@ vram vram (
// 127 bytes internal zero page ram from $ff80 to $fffe
wire cpu_wr_zpram = sel_zpram && !cpu_wr_n;
wire [7:0] zpram_do;
zpram zpram (
.clock ( clk ),
spram #(7) zpram (
.clock ( clk_cpu ),
.address ( cpu_addr[6:0] ),
.wren ( cpu_wr_zpram ),
.data ( cpu_do ),
@@ -311,49 +490,84 @@ zpram zpram (
);
// --------------------------------------------------------------------
// ------------------------- 8k internal ram --------------------------
// ------------------------ 8k/32k(GBC) internal ram -----------------
// --------------------------------------------------------------------
wire iram_wren = dma_rd?1'b0:cpu_wr_iram;
wire [12:0] iram_addr = dma_rd?dma_addr[12:0]:cpu_addr[12:0];
reg [2:0] iram_bank; //1-7 FF70 - SVBK
wire iram_wren = (dma_rd&&dma_sel_iram)||(isGBC&&hdma_rd&&hdma_sel_iram)?1'b0:cpu_wr_iram;
wire [14:0] iram_addr = (isGBC&&hdma_rd&&hdma_sel_iram)? //hdma transfer?
(hdma_source_addr[12])?{iram_bank,hdma_source_addr[11:0]}: //bank 1-7 D000-DFFF
{3'd0,hdma_source_addr[11:0]}: //bank 0
(dma_rd&&dma_sel_iram)? //dma transfer?
(dma_addr[12])?{iram_bank,dma_addr[11:0]}: //bank 1-7
{3'd0,dma_addr[11:0]}: //bank 0
//cpu
(cpu_addr[12])?{iram_bank,cpu_addr[11:0]}: //bank 1-7
{3'd0,cpu_addr[11:0]}; //bank 0
wire cpu_wr_iram = sel_iram && !cpu_wr_n;
wire [7:0] iram_do;
iram iram (
.clock ( clk ),
.address ( iram_addr[12:0]),
spram #(15) iram (
.clock ( clk_cpu ),
.address ( iram_addr ),
.wren ( iram_wren ),
.data ( cpu_do ),
.q ( iram_do )
);
//GBC WRAM banking
always @(posedge clk_cpu) begin
if(reset)
iram_bank <= 3'd1;
else if((cpu_addr == 16'hff70) && !cpu_wr_n && isGBC) begin
if (cpu_do[2:0]==3'd0) // 0 -> 1;
iram_bank <= 3'd1;
else
iram_bank <= cpu_do[2:0];
end
end
// --------------------------------------------------------------------
// ------------------------ internal boot rom -------------------------
// --------------------------------------------------------------------
// writing 01 to $ff50 disables the internal rom
// writing 01(GB) or 11(GBC) to $ff50 disables the internal rom
reg boot_rom_enabled;
always @(posedge clk) begin
if(reset)
boot_rom_enabled <= 1'b1;
else if((cpu_addr == 16'hff50) && !cpu_wr_n && cpu_do[0])
boot_rom_enabled <= 1'b0;
else if((cpu_addr == 16'hff50) && !cpu_wr_n)
if ((isGBC && cpu_do[7:0]==8'h11) || (!isGBC && cpu_do[0]))
boot_rom_enabled <= 1'b0;
end
// combine boot rom data with cartridge data
wire [7:0] rom_do = ((cpu_addr[14:8] == 7'h00) && boot_rom_enabled)?boot_rom_do:cart_do;
wire [7:0] rom_do = isGBC? //GameBoy Color?
(((cpu_addr[14:8] == 7'h00) || (hdma_rd&& hdma_source_addr[14:8] == 7'h00))&& boot_rom_enabled)?gbc_bios_do: //0-FF bootrom 1st part
((cpu_addr[14:9] == 6'h00) || (hdma_rd&& hdma_source_addr[14:9] == 6'h00))? cart_do: //100-1FF Cart Header
(((cpu_addr[14:12] == 3'h0) || (hdma_rd&& hdma_source_addr[14:12] == 3'h0)) && boot_rom_enabled)?gbc_bios_do: //200-8FF bootrom 2nd part
cart_do: //rest of card
((cpu_addr[14:8] == 7'h00) && boot_rom_enabled)?boot_rom_do:cart_do; //GB
wire is_dma_cart_addr = (dma_sel_rom || dma_sel_cram); //rom or external ram
wire is_hdma_cart_addr = (hdma_sel_rom || hdma_sel_cram); //rom or external ram
assign cart_di = cpu_do;
assign cart_addr = dma_rd?dma_addr:cpu_addr;
assign cart_rd = dma_rd || ((sel_rom || sel_cram) && !cpu_rd_n);
assign cart_wr = (sel_rom || sel_cram) && !cpu_wr_n;
assign cart_addr = (isGBC&&hdma_rd&&is_hdma_cart_addr)?hdma_source_addr:(dma_rd&&is_dma_cart_addr)?dma_addr:cpu_addr;
assign cart_rd = (isGBC&&hdma_rd&&is_hdma_cart_addr) || (dma_rd&&is_dma_cart_addr) || ((sel_rom || sel_cram) && !cpu_rd_n);
assign cart_wr = (sel_rom || sel_cram) && !cpu_wr_n && !hdma_rd;
assign gbc_bios_addr = hdma_rd?hdma_source_addr[11:0]:cpu_addr[11:0];
wire [7:0] boot_rom_do;
boot_rom boot_rom (
.address ( cpu_addr[7:0] ),
.clock ( clk ),
.q ( boot_rom_do )
.address ( cpu_addr[7:0] ),
.clock ( clk ),
.q ( boot_rom_do )
);
endmodule

View File

@@ -296,6 +296,7 @@ wire [15:0] audio_right;
gb gb (
.reset ( reset ),
.clk ( clk4 ), // the whole gameboy runs on 4mhnz
.clk2x ( clk8 ),
.fast_boot ( status[2] ),
.joystick ( joystick ),

File diff suppressed because it is too large Load Diff

331
cores/gameboy/hdma.v Normal file
View File

@@ -0,0 +1,331 @@
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
);
//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
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
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
endmodule

View File

@@ -1,3 +0,0 @@
set_global_assignment -name IP_TOOL_NAME "RAM: 1-PORT"
set_global_assignment -name IP_TOOL_VERSION "13.1"
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "iram.v"]

View File

@@ -1,172 +0,0 @@
// megafunction wizard: %RAM: 1-PORT%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: altsyncram
// ============================================================
// File Name: iram.v
// Megafunction Name(s):
// altsyncram
//
// Simulation Library Files(s):
// altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.1.4 Build 182 03/12/2014 SJ Web Edition
// ************************************************************
//Copyright (C) 1991-2014 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions
//and other software and tools, and its AMPP partner logic
//functions, and any output files from any of the foregoing
//(including device programming or simulation files), and any
//associated documentation or information are expressly subject
//to the terms and conditions of the Altera Program License
//Subscription Agreement, Altera MegaCore Function License
//Agreement, or other applicable license agreement, including,
//without limitation, that your use is for the sole purpose of
//programming logic devices manufactured by Altera and sold by
//Altera or its authorized distributors. Please refer to the
//applicable agreement for further details.
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module iram (
address,
clock,
data,
wren,
q);
input [12:0] address;
input clock;
input [7:0] data;
input wren;
output [7:0] q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri1 clock;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [7:0] sub_wire0;
wire [7:0] q = sub_wire0[7:0];
altsyncram altsyncram_component (
.address_a (address),
.clock0 (clock),
.data_a (data),
.wren_a (wren),
.q_a (sub_wire0),
.aclr0 (1'b0),
.aclr1 (1'b0),
.address_b (1'b1),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.data_b (1'b1),
.eccstatus (),
.q_b (),
.rden_a (1'b1),
.rden_b (1'b1),
.wren_b (1'b0));
defparam
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone III",
altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 8192,
altsyncram_component.operation_mode = "SINGLE_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_reg_a = "UNREGISTERED",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
altsyncram_component.widthad_a = 13,
altsyncram_component.width_a = 8,
altsyncram_component.width_byteena_a = 1;
endmodule
// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0"
// Retrieval info: PRIVATE: AclrAddr NUMERIC "0"
// Retrieval info: PRIVATE: AclrByte NUMERIC "0"
// Retrieval info: PRIVATE: AclrData NUMERIC "0"
// Retrieval info: PRIVATE: AclrOutput NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8"
// Retrieval info: PRIVATE: BlankMemory NUMERIC "1"
// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: Clken NUMERIC "0"
// Retrieval info: PRIVATE: DataBusSeparated NUMERIC "1"
// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0"
// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A"
// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0"
// Retrieval info: PRIVATE: JTAG_ID STRING "NONE"
// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0"
// Retrieval info: PRIVATE: MIFfilename STRING ""
// Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "8192"
// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "3"
// Retrieval info: PRIVATE: RegAddr NUMERIC "1"
// Retrieval info: PRIVATE: RegData NUMERIC "1"
// Retrieval info: PRIVATE: RegOutput NUMERIC "0"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: SingleClock NUMERIC "1"
// Retrieval info: PRIVATE: UseDQRAM NUMERIC "1"
// Retrieval info: PRIVATE: WRCONTROL_ACLR_A NUMERIC "0"
// Retrieval info: PRIVATE: WidthAddr NUMERIC "13"
// Retrieval info: PRIVATE: WidthData NUMERIC "8"
// Retrieval info: PRIVATE: rden NUMERIC "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO"
// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram"
// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "8192"
// Retrieval info: CONSTANT: OPERATION_MODE STRING "SINGLE_PORT"
// Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE"
// Retrieval info: CONSTANT: OUTDATA_REG_A STRING "UNREGISTERED"
// Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE"
// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_PORT_A STRING "NEW_DATA_NO_NBE_READ"
// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "13"
// Retrieval info: CONSTANT: WIDTH_A NUMERIC "8"
// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1"
// Retrieval info: USED_PORT: address 0 0 13 0 INPUT NODEFVAL "address[12..0]"
// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock"
// Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]"
// Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL "q[7..0]"
// Retrieval info: USED_PORT: wren 0 0 0 0 INPUT NODEFVAL "wren"
// Retrieval info: CONNECT: @address_a 0 0 13 0 address 0 0 13 0
// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0
// Retrieval info: CONNECT: @data_a 0 0 8 0 data 0 0 8 0
// Retrieval info: CONNECT: @wren_a 0 0 0 0 wren 0 0 0 0
// Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0
// Retrieval info: GEN_FILE: TYPE_NORMAL iram.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL iram.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL iram.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL iram.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL iram_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL iram_bb.v FALSE
// Retrieval info: LIB_FILE: altera_mf

55
cores/gameboy/spram.vhd Normal file
View File

@@ -0,0 +1,55 @@
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
ENTITY spram IS
generic (
addr_width : integer := 8;
data_width : integer := 8
);
PORT
(
clock : IN STD_LOGIC;
clken : IN STD_LOGIC := '1';
address : IN STD_LOGIC_VECTOR (addr_width-1 DOWNTO 0);
data : IN STD_LOGIC_VECTOR (data_width-1 DOWNTO 0);
wren : IN STD_LOGIC := '0';
q : OUT STD_LOGIC_VECTOR (data_width-1 DOWNTO 0)
);
END spram;
ARCHITECTURE SYN OF spram IS
BEGIN
altsyncram_component : altsyncram
GENERIC MAP (
clock_enable_input_a => "NORMAL",
clock_enable_output_a => "BYPASS",
intended_device_family => "Cyclone III",
lpm_hint => "ENABLE_RUNTIME_MOD=NO",
lpm_type => "altsyncram",
numwords_a => 2**addr_width,
operation_mode => "SINGLE_PORT",
outdata_aclr_a => "NONE",
outdata_reg_a => "UNREGISTERED",
power_up_uninitialized => "FALSE",
read_during_write_mode_port_a => "NEW_DATA_NO_NBE_READ",
widthad_a => addr_width,
width_a => data_width,
width_byteena_a => 1
)
PORT MAP (
address_a => address,
clock0 => clock,
clocken0 => clken,
data_a => data,
wren_a => wren,
q_a => q
);
END SYN;

View File

@@ -32,12 +32,17 @@ module sprite (
output [10:0] addr,
input [1:0] ds,
input [7:0] data,
input [7:0] data_1,
output pixel_active,
output pixel_cmap,
output pixel_prio,
output [1:0] pixel_data,
//gbc
output [2:0] pixel_cmap_gbc,
output tile_vbank,
input oam_wr,
input [1:0] oam_addr,
input [7:0] oam_di,
@@ -53,8 +58,8 @@ reg [7:0] data0;
reg [7:0] data1;
always @(posedge clk) begin
if(ds[0]) data0 <= data;
if(ds[1]) data1 <= data;
if(ds[0]) data0 <= flags[3]?data_1:data;
if(ds[1]) data1 <= flags[3]?data_1:data;
end
wire [7:0] height = size16?8'd16:8'd8;
@@ -63,15 +68,15 @@ wire v_visible = (v_cnt + 8'd16 >= y_pos) && (v_cnt + 8'd16 < y_pos + height);
wire visible = v_visible && (h_cnt + 8'd8 >= x_pos) && (h_cnt < x_pos);
// x position within sprite, mirror horizontally if required
wire [2:0] col_n = h_cnt - x_pos;
wire [2:0] col = flags[1]?col_n:~col_n;
wire [7:0] col_n = h_cnt - x_pos;
wire [2:0] col = flags[5]?col_n[2:0]:~col_n[2:0];
assign pixel_data = { data1[col], data0[col] };
assign pixel_active = (pixel_data != 0) && visible;
// y position within sprite, mirror vertically if required
wire [3:0] row_n = v_cnt - y_pos;
wire [3:0] row = flags[2]?~row_n:row_n;
wire [7:0] row_n = v_cnt - y_pos;
wire [3:0] row = flags[6]?~row_n[3:0]:row_n[3:0];
// 16 pixel tall sprites use one more rwo counter bit and the lsb
// of the tile index is ignored
@@ -79,13 +84,16 @@ wire [10:0] addr8 = { tile , row[2:0]};
wire [10:0] addr16 = { tile[7:1] , row};
assign addr = size16?addr16:addr8;
assign pixel_cmap = flags[0];
assign pixel_prio = flags[3];
assign pixel_cmap = flags[4];
assign pixel_prio = flags[7];
assign pixel_cmap_gbc = flags[2:0];
assign tile_vbank = flags[3];
reg [7:0] y_pos;
reg [7:0] x_pos;
reg [7:0] tile;
reg [3:0] flags;
reg [7:0] flags;
always @(posedge clk) begin
if(oam_wr) begin
@@ -93,7 +101,7 @@ always @(posedge clk) begin
0: y_pos <= oam_di;
1: x_pos <= oam_di;
2: tile <= oam_di;
3: flags <= oam_di[7:4];
3: flags <= oam_di;
endcase
end
end
@@ -102,6 +110,6 @@ assign oam_do =
(oam_addr == 0)?y_pos:
(oam_addr == 1)?x_pos:
(oam_addr == 2)?tile:
{ flags, 4'h0 };
flags;
endmodule

View File

@@ -87,7 +87,7 @@ generate
always @(posedge clk) begin
if(load) begin
values[i] <= in[i];
index[i] <= i;
index[i] <= i[5:0];
end else begin
values[i] <= sort_val[i];
index[i] <= sort_idx[i];

View File

@@ -21,6 +21,7 @@
module sprites (
input clk,
input clk_reg,
input size16,
// pixel position input which the current pixel is generated for
@@ -33,11 +34,16 @@ module sprites (
output pixel_cmap,
output pixel_prio,
//gbc
output [2:0] pixel_cmap_gbc,
output tile_vbank,
input sort,
input [3:0] index, // index of sprite which video wants to read data for
output [10:0] addr,
input [1:0] dvalid,
input [7:0] data,
input [7:0] data1,
// oam memory interface
input oam_wr,
@@ -79,6 +85,9 @@ wire [5:0] sprite_idx_array [SPRITES-1:0];
wire [5:0] prio_index = sprite_idx_array[index];
assign addr = sprite_addr[prio_index];
//gbc
wire [2:0] sprite_pixel_cmap_gbc [SPRITES-1:0];
wire sprite_tile_vbank [SPRITES-1:0];
generate
genvar i;
@@ -87,7 +96,7 @@ for(i=0;i<SPRITES;i=i+1) begin : spr
assign sprite_idx_array[i] = sprite_idx[6*i+5:6*i];
sprite sprite (
.clk ( clk ),
.clk ( clk_reg ),
.size16 ( size16 ),
.v_cnt ( v_cnt ),
@@ -97,11 +106,17 @@ for(i=0;i<SPRITES;i=i+1) begin : spr
.addr ( sprite_addr[i] ),
.ds ( (prio_index == i)?dvalid:2'b00),
.data ( data ),
.data_1 ( data1 ),
.pixel_cmap ( sprite_pixel_cmap[i] ),
.pixel_prio ( sprite_pixel_prio[i] ),
.pixel_active ( sprite_pixel_active[i] ),
.pixel_data ( sprite_pixel_data[i] ),
//gbc
.pixel_cmap_gbc ( sprite_pixel_cmap_gbc[i] ),
.tile_vbank ( sprite_tile_vbank[i] ),
.oam_wr ( oam_wr && (oam_addr[7:2] == i) ),
.oam_addr ( oam_addr[1:0] ),
@@ -171,6 +186,35 @@ assign pixel_cmap =
sprite_pixel_active[spr9]?sprite_pixel_cmap[spr9]:
1'b0;
// get the colormap of the leftmost sprite gbc
assign pixel_cmap_gbc =
sprite_pixel_active[spr0]?sprite_pixel_cmap_gbc[spr0]:
sprite_pixel_active[spr1]?sprite_pixel_cmap_gbc[spr1]:
sprite_pixel_active[spr2]?sprite_pixel_cmap_gbc[spr2]:
sprite_pixel_active[spr3]?sprite_pixel_cmap_gbc[spr3]:
sprite_pixel_active[spr4]?sprite_pixel_cmap_gbc[spr4]:
sprite_pixel_active[spr5]?sprite_pixel_cmap_gbc[spr5]:
sprite_pixel_active[spr6]?sprite_pixel_cmap_gbc[spr6]:
sprite_pixel_active[spr7]?sprite_pixel_cmap_gbc[spr7]:
sprite_pixel_active[spr8]?sprite_pixel_cmap_gbc[spr8]:
sprite_pixel_active[spr9]?sprite_pixel_cmap_gbc[spr9]:
1'b0;
// get the tile vbank of the leftmost sprite
assign tile_vbank =
sprite_pixel_active[spr0]?sprite_tile_vbank[spr0]:
sprite_pixel_active[spr1]?sprite_tile_vbank[spr1]:
sprite_pixel_active[spr2]?sprite_tile_vbank[spr2]:
sprite_pixel_active[spr3]?sprite_tile_vbank[spr3]:
sprite_pixel_active[spr4]?sprite_tile_vbank[spr4]:
sprite_pixel_active[spr5]?sprite_tile_vbank[spr5]:
sprite_pixel_active[spr6]?sprite_tile_vbank[spr6]:
sprite_pixel_active[spr7]?sprite_tile_vbank[spr7]:
sprite_pixel_active[spr8]?sprite_tile_vbank[spr8]:
sprite_pixel_active[spr9]?sprite_tile_vbank[spr9]:
1'b0;
// get the priority of the leftmost sprite
assign pixel_prio =
sprite_pixel_active[spr0]?sprite_pixel_prio[spr0]:

View File

@@ -22,7 +22,6 @@
module timer (
input reset,
input clk, // 4 Mhz cpu clock
output reg irq,
// cpu register interface
@@ -45,9 +44,17 @@ module timer (
// clk_div[8] = 8khz
// clk_div[9] = 4khz
wire resetdiv = cpu_sel && cpu_wr && (cpu_addr == 2'b00); //resetdiv also resets internal counter
reg [9:0] clk_div;
always @(posedge clk)
clk_div <= clk_div + 10'd1;
always @(posedge clk or posedge resetdiv)
if(resetdiv)
clk_div <= 10'd6;
else
if (reset)
clk_div <= 10'd6;
else
clk_div <= clk_div + 10'd1;
reg [7:0] div;
reg [7:0] tma;
@@ -56,12 +63,13 @@ reg [2:0] tac;
always @(posedge clk) begin
if(reset) begin
tima <= 8'h00;
tma <= 8'h00;
tac <= 8'h00;
irq <= 1'b0;
tima <= 0;
tma <= 0;
tac <= 0;
irq <= 0;
div <= 0;
end else begin
irq <= 1'b0;
irq <= 0;
if(clk_div[7:0] == 0) // 16kHz
div <= div + 8'd1;
@@ -98,6 +106,6 @@ assign cpu_do =
(cpu_addr == 2'b00)?div:
(cpu_addr == 2'b01)?tima:
(cpu_addr == 2'b10)?tma:
{5'b00000, tac};
{5'b11111, tac};
endmodule

View File

@@ -22,6 +22,8 @@
module video (
input reset,
input clk, // 4 Mhz cpu clock
input clk_reg,
input isGBC,
// cpu register adn oam interface
input cpu_sel_oam,
@@ -34,7 +36,7 @@ module video (
// output to lcd
output lcd_on,
output lcd_clkena,
output [1:0] lcd_data,
output [14:0] lcd_data,
output reg irq,
// vram connection
@@ -42,6 +44,9 @@ module video (
output vram_rd,
output [12:0] vram_addr,
input [7:0] vram_data,
// vram connection bank1 (GBC)
input [7:0] vram1_data,
// dma connection
output dma_rd,
@@ -51,6 +56,7 @@ module video (
localparam STAGE2 = 9'd250; // oam + disp + pause
localparam OAM_LEN = 80;
localparam OAM_LEN16 = OAM_LEN/16;
wire sprite_pixel_active;
wire [1:0] sprite_pixel_data;
@@ -58,9 +64,13 @@ wire sprite_pixel_cmap;
wire sprite_pixel_prio;
wire [7:0] oam_do;
wire [3:0] sprite_index = h_cnt[7:4]-(OAM_LEN/16); // memory io starts at h_cnt == 16
wire [3:0] sprite_index = h_cnt[7:4] - OAM_LEN16[3:0]; // memory io starts at h_cnt == 16
wire [10:0] sprite_addr;
//gbc
wire [2:0] sprite_pixel_cmap_gbc;
wire sprite_tile_vbank;
// "data strobe" for the two bytes each sprite line consists of
wire [1:0] sprite_dvalid = {
(h_cnt[3:0] == 4'hf) && !vblank && !hblank,
@@ -68,6 +78,7 @@ wire [1:0] sprite_dvalid = {
sprites sprites (
.clk ( clk ),
.clk_reg ( clk_reg ),
.size16 ( lcdc_spr_siz ),
.v_cnt ( v_cnt ),
@@ -79,10 +90,15 @@ sprites sprites (
.pixel_cmap ( sprite_pixel_cmap ),
.pixel_prio ( sprite_pixel_prio ),
.index ( sprite_index ),
.addr ( sprite_addr ),
.dvalid ( sprite_dvalid),
.data ( vram_data ),
.index ( sprite_index ),
.addr ( sprite_addr ),
.dvalid ( sprite_dvalid ),
.data ( vram_data ),
.data1 ( isGBC?vram1_data:vram_data ),
//gbc
.pixel_cmap_gbc ( sprite_pixel_cmap_gbc ),
.tile_vbank ( sprite_tile_vbank ),
.oam_wr ( oam_wr ),
.oam_addr ( oam_addr ),
@@ -119,7 +135,7 @@ reg [7:0] scx;
reg [7:0] scx_r; // stable over line
// ff44 line counter
reg [7:0] ly;
wire [7:0] ly = v_cnt;
// ff45 line counter compare
wire lyc_match = (ly == lyc);
@@ -134,6 +150,22 @@ reg [7:0] wy_r; // stable over entire image
reg [7:0] wx;
reg [7:0] wx_r; // stable over line
//ff68-ff6A GBC
//FF68 - BCPS/BGPI - Background Palette Index
reg [5:0] bgpi; //Bit 0-5 Index (00-3F)
reg bgpi_ai; //Bit 7 Auto Increment (0=Disabled, 1=Increment after Writing)
//FF69 - BCPD/BGPD - Background Palette Data
reg[7:0] bgpd [63:0]; //64 bytes
//FF6A - OCPS/OBPI - Sprite Palette Index
reg [5:0] obpi; //Bit 0-5 Index (00-3F)
reg obpi_ai; //Bit 7 Auto Increment (0=Disabled, 1=Increment after Writing)
//FF6B - OCPD/OBPD - Sprite Palette Data
reg[7:0] obpd [63:0]; //64 bytes
// --------------------------------------------------------------------
// ----------------------------- DMA engine ---------------------------
// --------------------------------------------------------------------
@@ -144,12 +176,12 @@ assign dma_rd = dma_active;
reg dma_active;
reg [7:0] dma;
reg [9:0] dma_cnt; // dma runs 4*160 clock cycles = 160us @ 4MHz
always @(posedge clk) begin
always @(posedge clk_reg) begin
if(reset)
dma_active <= 1'b0;
else begin
// writing the dma register engages the dma engine
if(cpu_sel_reg && cpu_wr && (cpu_addr[3:0] == 4'h6)) begin
if(cpu_sel_reg && cpu_wr && (cpu_addr == 8'h46)) begin
dma_active <= 1'b1;
dma_cnt <= 10'd0;
end else if(dma_cnt != 160*4-1)
@@ -163,11 +195,12 @@ end
// ------------------------------- IRQs -------------------------------
// --------------------------------------------------------------------
always @(posedge clk) begin
always @(posedge clk_reg) begin //TODO: have to check if this is correct
irq <= 1'b0;
//TODO: investigate and fix timing of lyc=ly
// lyc=ly coincidence
if(stat[6] && (h_cnt == 0) && lyc_match)
if(stat[6] && (h_cnt == 1) && lyc_match)
irq <= 1'b1;
// begin of oam phase
@@ -186,32 +219,65 @@ end
// --------------------------------------------------------------------
// --------------------- CPU register interface -----------------------
// --------------------------------------------------------------------
always @(posedge clk) begin
integer ii=0;
always @(posedge clk_reg) begin
if(reset) begin
lcdc <= 8'h00; // screen must be off since dmg rom writes to vram
scy <= 8'h00;
scx <= 8'h00;
wy <= 8'h00;
wx <= 8'h00;
stat <= 8'h00;
bgp <= 8'hfc;
obp0 <= 8'hff;
obp1 <= 8'hff;
bgpi <= 6'h0;
obpi <= 6'h0;
bgpi_ai <= 1'b0;
obpi_ai <= 1'b0;
for (ii=0;ii<64;ii=ii+1)begin
bgpd[ii] <= 8'h00;
obpd[ii] <= 8'h00;
end
end else begin
if(cpu_sel_reg && cpu_wr) begin
case(cpu_addr[3:0])
4'h0: lcdc <= cpu_di;
4'h1: stat <= cpu_di;
4'h2: scy <= cpu_di;
4'h3: scx <= cpu_di;
case(cpu_addr)
8'h40: lcdc <= cpu_di;
8'h41: stat <= cpu_di;
8'h42: scy <= cpu_di;
8'h43: scx <= cpu_di;
// a write to 4 is supposed to reset the v_cnt
4'h5: lyc <= cpu_di;
4'h6: dma <= cpu_di;
4'h7: bgp <= cpu_di;
4'h8: obp0 <= cpu_di;
4'h9: obp1 <= cpu_di;
4'ha: wy <= cpu_di;
4'hb: wx <= cpu_di;
8'h45: lyc <= cpu_di;
8'h46: dma <= cpu_di;
8'h47: bgp <= cpu_di;
8'h48: obp0 <= cpu_di;
8'h49: obp1 <= cpu_di;
8'h4a: wy <= cpu_di;
8'h4b: wx <= cpu_di;
//gbc
8'h68: begin
bgpi <= cpu_di[5:0];
bgpi_ai <= cpu_di[7];
end
8'h69: begin
bgpd[bgpi] <= cpu_di;
if (bgpi_ai)
bgpi <= bgpi + 6'h1;
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;
end
endcase
end
end
@@ -219,18 +285,24 @@ end
assign cpu_do =
cpu_sel_oam?oam_do:
(cpu_addr[3:0] == 4'h0)?lcdc:
(cpu_addr[3:0] == 4'h1)?{stat[7:3], lyc_match, mode}:
(cpu_addr[3:0] == 4'h2)?scy:
(cpu_addr[3:0] == 4'h3)?scx:
(cpu_addr[3:0] == 4'h4)?ly:
(cpu_addr[3:0] == 4'h5)?lyc:
(cpu_addr[3:0] == 4'h6)?dma:
(cpu_addr[3:0] == 4'h7)?bgp:
(cpu_addr[3:0] == 4'h8)?obp0:
(cpu_addr[3:0] == 4'h9)?obp1:
(cpu_addr[3:0] == 4'ha)?wy:
(cpu_addr[3:0] == 4'hb)?wx:
(cpu_addr == 8'h40)?lcdc:
(cpu_addr == 8'h41)?{1'b1,stat[6:3], lyc_match, mode}:
(cpu_addr == 8'h42)?scy:
(cpu_addr == 8'h43)?scx:
(cpu_addr == 8'h44)?ly:
(cpu_addr == 8'h45)?lyc:
(cpu_addr == 8'h46)?dma:
(cpu_addr == 8'h47)?bgp:
(cpu_addr == 8'h48)?obp0:
(cpu_addr == 8'h49)?obp1:
(cpu_addr == 8'h4a)?wy:
(cpu_addr == 8'h4b)?wx:
isGBC?
(cpu_addr == 8'h68)?{bgpi_ai,1'd0,bgpi}:
(cpu_addr == 8'h69)?bgpd[bgpi]:
(cpu_addr == 8'h6a)?{obpi_ai,1'd0,obpi}:
(cpu_addr == 8'h6b)?obpd[obpi]:
8'hff:
8'hff;
// --------------------------------------------------------------------
@@ -240,36 +312,51 @@ assign cpu_do =
assign lcd_data = stage2_data;
assign lcd_clkena = stage2_clkena;
reg [1:0] stage2_data;
reg [14:0] stage2_data;
reg stage2_clkena;
reg [1:0] stage2_buffer [159:0];
reg [3:0] stage2_bgp_buffer_pix [159:0]; //GBC keep record of palette used for tile and priority
reg [7:0] stage2_wptr;
reg [7:0] stage2_rptr;
// apply bg palette
wire [1:0] stage2_bg_pix = (!lcdc_bg_ena && !window_ena)?2'b11: // background off?
(stage2_buffer[stage2_rptr] == 2'b00)?bgp[1:0]:
(stage2_buffer[stage2_rptr] == 2'b01)?bgp[3:2]:
(stage2_buffer[stage2_rptr] == 2'b10)?bgp[5:4]:
bgp[7:6];
wire [5:0] palette_index = (stage2_bgp_buffer_pix[stage2_rptr][2:0] << 3) + (stage2_buffer[stage2_rptr]<<1); //gbc
// apply bg palette
wire [14:0] stage2_bg_pix = (!lcdc_bg_ena && !window_ena)?15'h7FFF: // background off?
isGBC?{bgpd[palette_index+1][6:0],bgpd[palette_index]}: //gbc
(stage2_buffer[stage2_rptr] == 2'b00)?{13'd0,bgp[1:0]}:
(stage2_buffer[stage2_rptr] == 2'b01)?{13'd0,bgp[3:2]}:
(stage2_buffer[stage2_rptr] == 2'b10)?{13'd0,bgp[5:4]}:
{13'd0,bgp[7:6]};
// apply sprite palette
wire [5:0] sprite_palette_index = (sprite_pixel_cmap_gbc << 3) + (sprite_pixel_data<<1); //gbc
wire [7:0] obp = sprite_pixel_cmap?obp1:obp0;
wire [1:0] sprite_pix =
(sprite_pixel_data == 2'b00)?obp[1:0]:
(sprite_pixel_data == 2'b01)?obp[3:2]:
(sprite_pixel_data == 2'b10)?obp[5:4]:
obp[7:6];
wire [14:0] sprite_pix = isGBC?{obpd[sprite_palette_index+1][6:0],obpd[sprite_palette_index]}: //gbc
(sprite_pixel_data == 2'b00)?{13'd0,obp[1:0]}:
(sprite_pixel_data == 2'b01)?{13'd0,obp[3:2]}:
(sprite_pixel_data == 2'b10)?{13'd0,obp[5:4]}:
{13'd0,obp[7:6]};
// a sprite pixel is visible if
// - sprites are enabled
// - there's a sprite at the current position
// - the sprites prioroty bit is 0, or
// - the prites priority is 1 and the backrgound color is 00
// - the sprites priority is 1 and the backrgound color is 00
// - GBC : BG priority is 0
wire bg_piority = isGBC&&stage2_bgp_buffer_pix[stage2_rptr][3];
wire sprite_pixel_visible =
sprite_pixel_active && lcdc_spr_ena &&
sprite_pixel_active && lcdc_spr_ena && (~bg_piority) &&
((!sprite_pixel_prio) || (stage2_buffer[stage2_rptr] == 2'b00));
always @(posedge clk) begin
@@ -280,6 +367,7 @@ always @(posedge clk) begin
if(stage1_clkena) begin
stage2_buffer[stage2_wptr] <= stage1_data;
stage2_bgp_buffer_pix[stage2_wptr] <= {bg_tile_attr_old[7],bg_tile_attr_old[2:0]}; //GBC: buffer palette and Priority Bit
stage2_wptr <= stage2_wptr + 8'd1;
end
@@ -289,7 +377,7 @@ always @(posedge clk) begin
if(sprite_pixel_visible) stage2_data <= sprite_pix;
else stage2_data <= stage2_bg_pix;
stage2_rptr <= stage2_rptr + 8'd1;
stage2_rptr <= stage2_rptr + 8'd1;
end
end
@@ -303,31 +391,53 @@ reg window_ena;
reg [7:0] tile_shift_0;
reg [7:0] tile_shift_1;
reg [7:0] tile_shift_0_x;
reg [7:0] tile_shift_1_x;
reg [7:0] bg_tile;
reg [7:0] bg_tile_data0;
reg [7:0] bg_tile_data1;
wire stage1_clkena = !vblank && hdvalid;
wire [1:0] stage1_data = { tile_shift_1[7], tile_shift_0[7] };
wire [1:0] stage1_data = (isGBC&&bg_tile_attr_old[5])?{ tile_shift_1_x[0], tile_shift_0_x[0] }:{ tile_shift_1[7], tile_shift_0[7] };
wire [7:0] vram_gbc_data = bg_tile_attr_new[3]?vram1_data:vram_data; //gbc check tile bank
reg [7:0] bg_tile_attr_old,bg_tile_attr_new; //GBC
// read data half a clock cycle after ram has been selected
always @(posedge clk) begin
// every memory access is two pixel cycles
if(h_cnt[0]) begin
if(bg_tile_map_rd) bg_tile <= vram_data;
if(bg_tile_data0_rd) bg_tile_data0 <= vram_data;
if(bg_tile_data1_rd) bg_tile_data1 <= vram_data;
if (isGBC) begin
if(bg_tile_map_rd) bg_tile_attr_new <= vram1_data; //get tile attr from vram bank1
if(bg_tile_data0_rd) bg_tile_data0 <= vram_gbc_data;
if(bg_tile_data1_rd) bg_tile_data1 <= vram_gbc_data;
end else begin
if(bg_tile_data0_rd) bg_tile_data0 <= vram_data;
if(bg_tile_data1_rd) bg_tile_data1 <= vram_data;
end
// sprite data is evaluated inside the sprite engine
end
// shift bg/window pixels out
if(bg_tile_obj_rd && h_cnt[0]) begin
bg_tile_attr_old <= bg_tile_attr_new;
tile_shift_0 <= bg_tile_data0;
tile_shift_1 <= bg_tile_data1;
end else begin
tile_shift_0_x <= bg_tile_data0;
tile_shift_1_x <= bg_tile_data1;
end else begin
tile_shift_0 <= { tile_shift_0[6:0], 1'b0 };
tile_shift_1 <= { tile_shift_1[6:0], 1'b0 };
//GBC x-flip
tile_shift_0_x <= { 1'b0,tile_shift_0_x[7:1]};
tile_shift_1_x <= { 1'b0,tile_shift_1_x[7:1]};
end
end
@@ -338,10 +448,12 @@ wire bg_tile_a12 = !lcdc_tile_data_sel?(~bg_tile[7]):1'b0;
wire tile_map_sel = window_ena?lcdc_win_tile_map_sel:lcdc_bg_tile_map_sel;
wire [2:0] tile_line_gbc = bg_tile_attr_new[6]? (3'b111 - tile_line): tile_line; //GBC: check if flipped y
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, 1'b0}:
bg_tile_data1_rd?{bg_tile_a12, bg_tile, tile_line, 1'b1}:
bg_tile_data0_rd?{bg_tile_a12, bg_tile, isGBC?tile_line_gbc:tile_line, 1'b0}:
bg_tile_data1_rd?{bg_tile_a12, bg_tile, isGBC?tile_line_gbc:tile_line, 1'b1}:
{1'b0, sprite_addr, h_cnt[3]};
reg [9:0] bg_tile_map_addr;
@@ -434,6 +546,7 @@ wire bg_tile_obj_rd = (!vblank) && (!hblank) && (h_cnt[2:1] == 2'b11);
// Mode 10: oam
// Mode 11: oam and vram
assign mode =
!lcdc_on?2'b00:
vblank?2'b01:
oam?2'b10:
hblank?2'b00:
@@ -449,63 +562,70 @@ wire [2:0] tile_line = window_ena?win_line[2:0]:bg_line[2:0];
wire win_start = lcdc_win_ena && (v_cnt >= wy_r) && de && (wx_r >= 7) && (pcnt == wx_r-8);
// each memory access takes two cycles
always @(negedge clk) begin
// this ly change h_cnt is wrong!!!
if(h_cnt == 0)
ly <= (v_cnt >= 153)?(v_cnt-8'd153):(v_cnt+8'd1);
always @(negedge clk or negedge lcdc_on) begin
if(h_cnt != 455) begin
h_cnt <= h_cnt + 9'd1;
// make sure sginals don't change during the line
// latch at latest possible moment (one clock before display starts)
if(h_cnt == OAM_LEN-2) begin
scx_r <= scx;
wx_r <= wx;
scy_r <= scy;
end
// increment address at the end of each 8-pixel-cycle. But don't
// increment while waiting for current cycle to end due to window start
if(!hblank && h_cnt[2:0] == 3'b111 && (skip <= 8))
bg_tile_map_addr[4:0] <= bg_tile_map_addr[4:0] + 10'd1;
// begin of line
if(h_cnt == OAM_LEN-1) begin
// set tile map address for this line, assume there is no window
bg_tile_map_addr[9:5] <= bg_line[7:3];
bg_tile_map_addr[4:0] <= scx_r[7:3];
if (!lcdc_on) begin // don't increase counters if lcdoff
//reset counters
h_cnt <= 9'd0;
v_cnt <= 8'd0;
// special case wx < 8: line starts with window, no background
// visible at all
if(lcdc_win_ena && (v_cnt >= wy_r) && (wx_r < 8)) begin
end else begin
if(h_cnt != 455) begin
h_cnt <= h_cnt + 9'd1;
// make sure sginals don't change during the line
// latch at latest possible moment (one clock before display starts)
if(h_cnt == OAM_LEN-2) begin
scx_r <= scx;
wx_r <= wx;
scy_r <= scy;
end
// increment address at the end of each 8-pixel-cycle. But don't
// increment while waiting for current cycle to end due to window start
if(!hblank && h_cnt[2:0] == 3'b111 && (skip <= 8))
bg_tile_map_addr[4:0] <= bg_tile_map_addr[4:0] + 1'd1;
// begin of line
if(h_cnt == OAM_LEN-1) begin
// set tile map address for this line, assume there is no window
bg_tile_map_addr[9:5] <= bg_line[7:3];
bg_tile_map_addr[4:0] <= scx_r[7:3];
// special case wx < 8: line starts with window, no background
// visible at all
if(lcdc_win_ena && (v_cnt >= wy_r) && (wx_r < 8)) begin
window_ena <= 1'b1;
bg_tile_map_addr[9:5] <= win_line[7:3];
bg_tile_map_addr[4:0] <= 5'd0; // window always start with its very left
end
end
// check if the window starts here
if(win_start) begin
window_ena <= 1'b1;
bg_tile_map_addr[9:5] <= win_line[7:3];
bg_tile_map_addr[4:0] <= 5'd0; // window always start with its very left
end
end
end else begin
window_ena <= 1'b0; // next line starts with background
// check if the window starts here
if(win_start) begin
window_ena <= 1'b1;
bg_tile_map_addr[9:5] <= win_line[7:3];
bg_tile_map_addr[4:0] <= 5'd0; // window always start with its very left
end
end else begin
window_ena <= 1'b0; // next line starts with background
// end of line reached
h_cnt <= 9'd0;
if(v_cnt != 153)
v_cnt <= v_cnt + 8'd1;
else begin
// start of new image
v_cnt <= 8'd0;
// make sure sginals don't change during the image
wy_r <= wy;
// end of line reached
h_cnt <= 9'd0;
if(v_cnt != 153)
v_cnt <= v_cnt + 8'd1;
else begin
// start of new image
v_cnt <= 8'd0;
// make sure sginals don't change during the image
wy_r <= wy;
end
end
end
end

View File

@@ -1,3 +0,0 @@
set_global_assignment -name IP_TOOL_NAME "RAM: 1-PORT"
set_global_assignment -name IP_TOOL_VERSION "13.1"
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "vram.v"]

View File

@@ -1,172 +0,0 @@
// megafunction wizard: %RAM: 1-PORT%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: altsyncram
// ============================================================
// File Name: vram.v
// Megafunction Name(s):
// altsyncram
//
// Simulation Library Files(s):
// altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.1.4 Build 182 03/12/2014 SJ Web Edition
// ************************************************************
//Copyright (C) 1991-2014 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions
//and other software and tools, and its AMPP partner logic
//functions, and any output files from any of the foregoing
//(including device programming or simulation files), and any
//associated documentation or information are expressly subject
//to the terms and conditions of the Altera Program License
//Subscription Agreement, Altera MegaCore Function License
//Agreement, or other applicable license agreement, including,
//without limitation, that your use is for the sole purpose of
//programming logic devices manufactured by Altera and sold by
//Altera or its authorized distributors. Please refer to the
//applicable agreement for further details.
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module vram (
address,
clock,
data,
wren,
q);
input [12:0] address;
input clock;
input [7:0] data;
input wren;
output [7:0] q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri1 clock;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [7:0] sub_wire0;
wire [7:0] q = sub_wire0[7:0];
altsyncram altsyncram_component (
.address_a (address),
.clock0 (clock),
.data_a (data),
.wren_a (wren),
.q_a (sub_wire0),
.aclr0 (1'b0),
.aclr1 (1'b0),
.address_b (1'b1),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.data_b (1'b1),
.eccstatus (),
.q_b (),
.rden_a (1'b1),
.rden_b (1'b1),
.wren_b (1'b0));
defparam
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone III",
altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 8192,
altsyncram_component.operation_mode = "SINGLE_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_reg_a = "UNREGISTERED",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
altsyncram_component.widthad_a = 13,
altsyncram_component.width_a = 8,
altsyncram_component.width_byteena_a = 1;
endmodule
// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0"
// Retrieval info: PRIVATE: AclrAddr NUMERIC "0"
// Retrieval info: PRIVATE: AclrByte NUMERIC "0"
// Retrieval info: PRIVATE: AclrData NUMERIC "0"
// Retrieval info: PRIVATE: AclrOutput NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8"
// Retrieval info: PRIVATE: BlankMemory NUMERIC "1"
// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: Clken NUMERIC "0"
// Retrieval info: PRIVATE: DataBusSeparated NUMERIC "1"
// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0"
// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A"
// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0"
// Retrieval info: PRIVATE: JTAG_ID STRING "NONE"
// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0"
// Retrieval info: PRIVATE: MIFfilename STRING ""
// Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "8192"
// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "3"
// Retrieval info: PRIVATE: RegAddr NUMERIC "1"
// Retrieval info: PRIVATE: RegData NUMERIC "1"
// Retrieval info: PRIVATE: RegOutput NUMERIC "0"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: SingleClock NUMERIC "1"
// Retrieval info: PRIVATE: UseDQRAM NUMERIC "1"
// Retrieval info: PRIVATE: WRCONTROL_ACLR_A NUMERIC "0"
// Retrieval info: PRIVATE: WidthAddr NUMERIC "13"
// Retrieval info: PRIVATE: WidthData NUMERIC "8"
// Retrieval info: PRIVATE: rden NUMERIC "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO"
// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram"
// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "8192"
// Retrieval info: CONSTANT: OPERATION_MODE STRING "SINGLE_PORT"
// Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE"
// Retrieval info: CONSTANT: OUTDATA_REG_A STRING "UNREGISTERED"
// Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE"
// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_PORT_A STRING "NEW_DATA_NO_NBE_READ"
// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "13"
// Retrieval info: CONSTANT: WIDTH_A NUMERIC "8"
// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1"
// Retrieval info: USED_PORT: address 0 0 13 0 INPUT NODEFVAL "address[12..0]"
// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock"
// Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]"
// Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL "q[7..0]"
// Retrieval info: USED_PORT: wren 0 0 0 0 INPUT NODEFVAL "wren"
// Retrieval info: CONNECT: @address_a 0 0 13 0 address 0 0 13 0
// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0
// Retrieval info: CONNECT: @data_a 0 0 8 0 data 0 0 8 0
// Retrieval info: CONNECT: @wren_a 0 0 0 0 wren 0 0 0 0
// Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0
// Retrieval info: GEN_FILE: TYPE_NORMAL vram.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL vram.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL vram.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL vram.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL vram_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL vram_bb.v FALSE
// Retrieval info: LIB_FILE: altera_mf

View File

@@ -1,3 +0,0 @@
set_global_assignment -name IP_TOOL_NAME "RAM: 1-PORT"
set_global_assignment -name IP_TOOL_VERSION "13.1"
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) "zpram.v"]

View File

@@ -1,172 +0,0 @@
// megafunction wizard: %RAM: 1-PORT%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: altsyncram
// ============================================================
// File Name: zpram.v
// Megafunction Name(s):
// altsyncram
//
// Simulation Library Files(s):
// altera_mf
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.1.4 Build 182 03/12/2014 SJ Web Edition
// ************************************************************
//Copyright (C) 1991-2014 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions
//and other software and tools, and its AMPP partner logic
//functions, and any output files from any of the foregoing
//(including device programming or simulation files), and any
//associated documentation or information are expressly subject
//to the terms and conditions of the Altera Program License
//Subscription Agreement, Altera MegaCore Function License
//Agreement, or other applicable license agreement, including,
//without limitation, that your use is for the sole purpose of
//programming logic devices manufactured by Altera and sold by
//Altera or its authorized distributors. Please refer to the
//applicable agreement for further details.
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module zpram (
address,
clock,
data,
wren,
q);
input [7:0] address;
input clock;
input [7:0] data;
input wren;
output [7:0] q;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_off
`endif
tri1 clock;
`ifndef ALTERA_RESERVED_QIS
// synopsys translate_on
`endif
wire [7:0] sub_wire0;
wire [7:0] q = sub_wire0[7:0];
altsyncram altsyncram_component (
.address_a (address),
.clock0 (clock),
.data_a (data),
.wren_a (wren),
.q_a (sub_wire0),
.aclr0 (1'b0),
.aclr1 (1'b0),
.address_b (1'b1),
.addressstall_a (1'b0),
.addressstall_b (1'b0),
.byteena_a (1'b1),
.byteena_b (1'b1),
.clock1 (1'b1),
.clocken0 (1'b1),
.clocken1 (1'b1),
.clocken2 (1'b1),
.clocken3 (1'b1),
.data_b (1'b1),
.eccstatus (),
.q_b (),
.rden_a (1'b1),
.rden_b (1'b1),
.wren_b (1'b0));
defparam
altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone III",
altsyncram_component.lpm_hint = "ENABLE_RUNTIME_MOD=NO",
altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.numwords_a = 256,
altsyncram_component.operation_mode = "SINGLE_PORT",
altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_reg_a = "UNREGISTERED",
altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
altsyncram_component.widthad_a = 8,
altsyncram_component.width_a = 8,
altsyncram_component.width_byteena_a = 1;
endmodule
// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0"
// Retrieval info: PRIVATE: AclrAddr NUMERIC "0"
// Retrieval info: PRIVATE: AclrByte NUMERIC "0"
// Retrieval info: PRIVATE: AclrData NUMERIC "0"
// Retrieval info: PRIVATE: AclrOutput NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0"
// Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8"
// Retrieval info: PRIVATE: BlankMemory NUMERIC "1"
// Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0"
// Retrieval info: PRIVATE: Clken NUMERIC "0"
// Retrieval info: PRIVATE: DataBusSeparated NUMERIC "1"
// Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0"
// Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A"
// Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0"
// Retrieval info: PRIVATE: JTAG_ID STRING "NONE"
// Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0"
// Retrieval info: PRIVATE: MIFfilename STRING ""
// Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "256"
// Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
// Retrieval info: PRIVATE: READ_DURING_WRITE_MODE_PORT_A NUMERIC "3"
// Retrieval info: PRIVATE: RegAddr NUMERIC "1"
// Retrieval info: PRIVATE: RegData NUMERIC "1"
// Retrieval info: PRIVATE: RegOutput NUMERIC "0"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: SingleClock NUMERIC "1"
// Retrieval info: PRIVATE: UseDQRAM NUMERIC "1"
// Retrieval info: PRIVATE: WRCONTROL_ACLR_A NUMERIC "0"
// Retrieval info: PRIVATE: WidthAddr NUMERIC "8"
// Retrieval info: PRIVATE: WidthData NUMERIC "8"
// Retrieval info: PRIVATE: rden NUMERIC "0"
// Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
// Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS"
// Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone III"
// Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO"
// Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram"
// Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "256"
// Retrieval info: CONSTANT: OPERATION_MODE STRING "SINGLE_PORT"
// Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE"
// Retrieval info: CONSTANT: OUTDATA_REG_A STRING "UNREGISTERED"
// Retrieval info: CONSTANT: POWER_UP_UNINITIALIZED STRING "FALSE"
// Retrieval info: CONSTANT: READ_DURING_WRITE_MODE_PORT_A STRING "NEW_DATA_NO_NBE_READ"
// Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "8"
// Retrieval info: CONSTANT: WIDTH_A NUMERIC "8"
// Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1"
// Retrieval info: USED_PORT: address 0 0 8 0 INPUT NODEFVAL "address[7..0]"
// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT VCC "clock"
// Retrieval info: USED_PORT: data 0 0 8 0 INPUT NODEFVAL "data[7..0]"
// Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL "q[7..0]"
// Retrieval info: USED_PORT: wren 0 0 0 0 INPUT NODEFVAL "wren"
// Retrieval info: CONNECT: @address_a 0 0 8 0 address 0 0 8 0
// Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0
// Retrieval info: CONNECT: @data_a 0 0 8 0 data 0 0 8 0
// Retrieval info: CONNECT: @wren_a 0 0 0 0 wren 0 0 0 0
// Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0
// Retrieval info: GEN_FILE: TYPE_NORMAL zpram.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL zpram.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL zpram.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL zpram.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL zpram_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL zpram_bb.v FALSE
// Retrieval info: LIB_FILE: altera_mf