From 4807107c19d031038a7f2321ade800702a94f4a1 Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Tue, 12 Feb 2019 14:21:01 +0100 Subject: [PATCH] [Gameboy] Update from MiSTer --- cores/gameboy/dpram.vhd | 170 +++++ cores/gameboy/gb.qsf | 12 +- cores/gameboy/gb.v | 358 ++++++++-- cores/gameboy/gb_mist.v | 1 + cores/gameboy/gbc_snd.vhd | 1271 ++++++++++++++++++++++------------- cores/gameboy/hdma.v | 331 +++++++++ cores/gameboy/iram.qip | 3 - cores/gameboy/iram.v | 172 ----- cores/gameboy/spram.vhd | 55 ++ cores/gameboy/sprite.v | 30 +- cores/gameboy/sprite_sort.v | 2 +- cores/gameboy/sprites.v | 46 +- cores/gameboy/timer.v | 26 +- cores/gameboy/video.v | 338 +++++++--- cores/gameboy/vram.qip | 3 - cores/gameboy/vram.v | 172 ----- cores/gameboy/zpram.qip | 3 - cores/gameboy/zpram.v | 172 ----- 18 files changed, 1962 insertions(+), 1203 deletions(-) create mode 100644 cores/gameboy/dpram.vhd create mode 100644 cores/gameboy/hdma.v delete mode 100644 cores/gameboy/iram.qip delete mode 100644 cores/gameboy/iram.v create mode 100644 cores/gameboy/spram.vhd delete mode 100644 cores/gameboy/vram.qip delete mode 100644 cores/gameboy/vram.v delete mode 100644 cores/gameboy/zpram.qip delete mode 100644 cores/gameboy/zpram.v diff --git a/cores/gameboy/dpram.vhd b/cores/gameboy/dpram.vhd new file mode 100644 index 0000000..ce066f9 --- /dev/null +++ b/cores/gameboy/dpram.vhd @@ -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; diff --git a/cores/gameboy/gb.qsf b/cores/gameboy/gb.qsf index 626de96..d6f9229 100644 --- a/cores/gameboy/gb.qsf +++ b/cores/gameboy/gb.qsf @@ -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 diff --git a/cores/gameboy/gb.v b/cores/gameboy/gb.v index 67b804d..91fa300 100644 --- a/cores/gameboy/gb.v +++ b/cores/gameboy/gb.v @@ -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 diff --git a/cores/gameboy/gb_mist.v b/cores/gameboy/gb_mist.v index da2d5af..87e266c 100644 --- a/cores/gameboy/gb_mist.v +++ b/cores/gameboy/gb_mist.v @@ -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 ), diff --git a/cores/gameboy/gbc_snd.vhd b/cores/gameboy/gbc_snd.vhd index 7ed2824..dd1a35d 100644 --- a/cores/gameboy/gbc_snd.vhd +++ b/cores/gameboy/gbc_snd.vhd @@ -7,23 +7,19 @@ library work; entity gbc_snd is - generic - ( - CLK_FREQ : integer := 100000000 - ); port ( - clk : in std_logic; - reset : in std_logic; + clk : in std_logic; + reset : in std_logic; - s1_read : in std_logic; + s1_read : in std_logic; s1_write : in std_logic; - s1_addr : in std_logic_vector(5 downto 0); + s1_addr : in std_logic_vector(5 downto 0); s1_readdata : out std_logic_vector(7 downto 0); s1_writedata : in std_logic_vector(7 downto 0); snd_left : out std_logic_vector(15 downto 0); - snd_right : out std_logic_vector(15 downto 0) + snd_right : out std_logic_vector(15 downto 0) ); end gbc_snd; @@ -33,118 +29,128 @@ architecture SYN of gbc_snd is subtype wav_t is std_logic_vector(3 downto 0); type wav_arr_t is array(0 to 31) of wav_t; - --constant clk_freq : integer := 100000000; - constant snd_freq : integer := 4194304; - - signal en_snd : boolean; -- Enable at base sound frequency (4.19MHz) + signal en_snd : boolean; -- Enable at base sound frequency (4.19MHz) signal en_snd2 : boolean; -- Enable at clk/2 signal en_snd4 : boolean; -- Enable at clk/4 - signal en_512 : boolean; -- 512Hz enable + signal en_512 : boolean; -- 512Hz enable signal en_snden2 : boolean; -- Enable at clk/2 signal en_snden4 : boolean; -- Enable at clk/4 - signal en_len : boolean; -- Sample length - signal en_env : boolean; -- Envelope - signal en_sweep : boolean; -- Sweep + signal en_len : boolean; -- Sample length + signal en_len_r : boolean; + signal en_env : boolean; -- Envelope + signal en_sweep : boolean; -- Sweep signal snd_enable : std_logic; signal sq1_swper : std_logic_vector(2 downto 0); -- Sq1 sweep period - signal sq1_swdir : std_logic; -- Sq1 sweep direction + signal sq1_swdir : std_logic; -- Sq1 sweep direction + signal sq1_swdir_change : std_logic; -- Sq1 sweep direction-change signal sq1_swshift : std_logic_vector(2 downto 0); -- Sq1 sweep frequency shift - signal sq1_duty : std_logic_vector(1 downto 0); -- Sq1 duty cycle - signal sq1_slen : std_logic_vector(5 downto 0); -- Sq1 play length - signal sq1_svol : std_logic_vector(3 downto 0); -- Sq1 initial volume - signal sq1_envsgn : std_logic; -- Sq1 envelope sign - signal sq1_envper : std_logic_vector(2 downto 0); -- Sq1 envelope period - signal sq1_freq : std_logic_vector(10 downto 0); -- Sq1 frequency - signal sq1_trigger : std_logic; -- Sq1 trigger play note - signal sq1_lenchk : std_logic; -- Sq1 length check enable + signal sq1_duty : std_logic_vector(1 downto 0); -- Sq1 duty cycle + signal sq1_slen : std_logic_vector(6 downto 0); -- Sq1 play length + signal sq1_svol : std_logic_vector(3 downto 0); -- Sq1 initial volume - signal sq1_fr2 : std_logic_vector(10 downto 0); -- Sq1 frequency (shadow copy) + signal sq1_envsgn : std_logic; -- Sq1 envelope sign + signal sq1_envsgn_old: std_logic; -- Sq1 old envelope sign (used in zombie mode) + signal sq1_envper : std_logic_vector(2 downto 0); -- Sq1 envelope period + signal sq1_envper_old: std_logic_vector(2 downto 0); -- Sq1 old envelope period (used in zombie mode) + signal sq1_freq : std_logic_vector(10 downto 0); -- Sq1 frequency + signal sq1_trigger : std_logic; -- Sq1 trigger play note + signal sq1_lenchk : std_logic; -- Sq1 length check enable + signal sq1_len_en_change : std_logic; -- Sq1 length off -> on + + signal sq1_fr2 : std_logic_vector(10 downto 0); -- Sq1 frequency (shadow copy) signal sq1_vol : std_logic_vector(3 downto 0); -- Sq1 initial volume - signal sq1_playing : std_logic; -- Sq1 channel active + signal sq1_nr2change : std_logic; + signal sq1_lenchange : std_logic; + signal sq1_lenquirk : std_logic; + signal sq1_freqchange : std_logic; + + signal sq1_playing : std_logic; -- Sq1 channel active signal sq1_wav : std_logic_vector(5 downto 0); -- Sq1 output waveform - signal sq2_duty : std_logic_vector(1 downto 0); -- Sq2 duty cycle - signal sq2_slen : std_logic_vector(5 downto 0); -- Sq2 play length - signal sq2_svol : std_logic_vector(3 downto 0); -- Sq2 initial volume - signal sq2_envsgn : std_logic; -- Sq2 envelope sign + signal sq2_duty : std_logic_vector(1 downto 0); -- Sq2 duty cycle + signal sq2_slen : std_logic_vector(6 downto 0); -- Sq2 play length + signal sq2_svol : std_logic_vector(3 downto 0); -- Sq2 initial volume + signal sq2_nr2change : std_logic; + signal sq2_lenchange : std_logic; + signal sq2_lenquirk : std_logic; + signal sq2_envsgn : std_logic; -- Sq2 envelope sign signal sq2_envper : std_logic_vector(2 downto 0); -- Sq2 envelope period - signal sq2_freq : std_logic_vector(10 downto 0); -- Sq2 frequency - signal sq2_trigger : std_logic; -- Sq2 trigger play note - signal sq2_lenchk : std_logic; -- Sq2 length check enable + signal sq2_envsgn_old: std_logic; -- Sq2 old envelope sign (used in zombie mode) + signal sq2_envper_old: std_logic_vector(2 downto 0); -- Sq2 old envelope period (used in zombie mode) + signal sq2_freq : std_logic_vector(10 downto 0); -- Sq2 frequency + signal sq2_trigger : std_logic; -- Sq2 trigger play note + signal sq2_lenchk : std_logic; -- Sq2 length check enable - signal sq2_fr2 : std_logic_vector(10 downto 0); -- Sq2 frequency (shadow copy) signal sq2_vol : std_logic_vector(3 downto 0); -- Sq2 initial volume - signal sq2_playing : std_logic; -- Sq2 channel active + signal sq2_playing : std_logic; -- Sq2 channel active signal sq2_wav : std_logic_vector(5 downto 0); -- Sq2 output waveform - signal wav_enable : std_logic; -- Wave enable - signal wav_slen : std_logic_vector(7 downto 0); -- Wave play length + signal wav_enable : std_logic; -- Wave enable + signal wav_slen : std_logic_vector(8 downto 0); -- Wave play length + signal wav_lenchange : std_logic; + signal wav_lenquirk : std_logic; signal wav_volsh : std_logic_vector(1 downto 0); -- Wave volume shift - signal wav_freq : std_logic_vector(10 downto 0); -- Wave frequency - signal wav_trigger : std_logic; -- Wave trigger play note - signal wav_lenchk : std_logic; -- Wave length check enable + signal wav_freq : std_logic_vector(10 downto 0); -- Wave frequency + signal wav_trigger : std_logic; -- Wave trigger play note + signal wav_lenchk : std_logic; -- Wave length check enable - signal wav_fr2 : std_logic_vector(10 downto 0); -- Wave frequency (shadow copy) signal wav_playing : std_logic; signal wav_wav : std_logic_vector(5 downto 0); -- Wave output waveform - signal wav_ram : wav_arr_t; -- Wave table - signal wav_shift : boolean; + signal wav_ram : wav_arr_t; -- Wave table - signal noi_slen : std_logic_vector(5 downto 0); - signal noi_svol : std_logic_vector(3 downto 0); + signal noi_slen : std_logic_vector(6 downto 0); + signal noi_lenchange : std_logic; + signal noi_lenquirk : std_logic; + signal noi_svol : std_logic_vector(3 downto 0); + signal noi_nr2change : std_logic; signal noi_envsgn : std_logic; signal noi_envper : std_logic_vector(2 downto 0); + signal noi_envsgn_old: std_logic; -- noi old envelope sign (used in zombie mode) + signal noi_envper_old: std_logic_vector(2 downto 0); -- noi old envelope period (used in zombie mode) signal noi_freqsh : std_logic_vector(3 downto 0); + signal noi_freqchange: std_logic; signal noi_short : std_logic; signal noi_div : std_logic_vector(2 downto 0); signal noi_trigger : std_logic; signal noi_lenchk : std_logic; - signal noi_fr2 : std_logic_vector(10 downto 0); -- Noise frequency (shadow copy) signal noi_vol : std_logic_vector(3 downto 0); -- Noise initial volume - signal noi_playing : std_logic; -- Noise channel active + signal noi_playing : std_logic; -- Noise channel active signal noi_wav : std_logic_vector(5 downto 0); -- Noise output waveform + signal ch_map : std_logic_vector(7 downto 0); + signal ch_vol : std_logic_vector(7 downto 0); + signal framecnt : integer range 0 to 7 := 0; + begin en_snd2 <= en_snd and en_snden2; en_snd4 <= en_snd and en_snden4; - en_snd <= true; + process (clk,en_snd,reset) + begin + if reset= '1' then + en_snd <= false; + elsif rising_edge(clk) then + en_snd <= not en_snd; + end if; + end process; - -- Calculate base clock enable (4.194304MHz) --- process(clk, reset) --- --to_unsigned(snd_freq * 65536 / clk_freq, 16); - --constant clk_frac : unsigned(15 downto 0) := X"0ABD"; -- clk_freq=100MHz --- constant clk_frac : unsigned(15 downto 0) := X"1991"; -- clk_freq=42MHz --- variable divacc : unsigned(15 downto 0); --- variable acc : unsigned(16 downto 0); --- begin --- if reset = '1' then --- divacc := (others => '0'); --- elsif rising_edge(clk) then - -- Sound base divider clock enable --- acc := ('0'&divacc) + ('0'&clk_frac); --- en_snd <= (acc(16) = '1'); --- divacc := acc(15 downto 0); --- end if; --- end process; - -- Calculate divided and frame sequencer clock enables - process(clk, en_snd, reset) - variable clkcnt : unsigned(1 downto 0); + process(clk, en_snd, snd_enable, reset) + variable clkcnt : unsigned(1 downto 0); variable cnt_512 : unsigned(12 downto 0); variable temp_512 : unsigned(13 downto 0); - variable framecnt : integer range 0 to 7 := 0; begin - if reset = '1' then + if reset = '1' or snd_enable='0' then --only clock frame sequencer if sound is enabled, restart at 0 clkcnt := "00"; cnt_512 := (others => '0'); - framecnt := 0; + framecnt <= 0; + en_len_r <= false; elsif rising_edge(clk) then -- Base clock divider @@ -167,8 +173,10 @@ begin en_env <= false; en_sweep <= false; if en_512 then + en_len_r <= not en_len_r; if framecnt = 0 or framecnt = 2 or framecnt = 4 or framecnt = 6 then en_len <= true; + en_len_r <= not en_len_r; end if; if framecnt = 2 or framecnt = 6 then en_sweep <= true; @@ -178,9 +186,9 @@ begin end if; if framecnt < 7 then - framecnt := framecnt + 1; + framecnt <= framecnt + 1; else - framecnt := 0; + framecnt <= 0; end if; end if; @@ -196,15 +204,13 @@ begin -- Registers registers : process(clk, snd_enable, reset) - variable wav_shift_r : boolean; - variable wav_temp : wav_t; begin -- Registers if snd_enable = '0' then -- Reset register values - sq1_swper <= (others => '0'); - sq1_swdir <= '0'; + sq1_swper <= (others => '0'); + sq1_swdir <= '0'; sq1_swshift <= (others => '0'); sq1_duty <= (others => '0'); sq1_slen <= (others => '0'); @@ -225,21 +231,24 @@ begin sq2_trigger <= '0'; wav_enable <= '0'; - wav_volsh <= (others => '0'); + wav_volsh <= (others => '0'); wav_freq <= (others => '0'); wav_trigger <= '0'; wav_lenchk <= '0'; - wav_shift_r := false; + noi_slen <= (others => '0'); noi_svol <= (others => '0'); noi_envsgn <= '0'; noi_envper <= (others => '0'); noi_freqsh <= (others => '0'); - noi_short <= '0'; - noi_div <= (others => '0'); + noi_short <= '0'; + noi_div <= (others => '0'); noi_trigger <= '0'; noi_lenchk <= '0'; + + ch_map <= (others => '0'); + ch_vol <= (others => '0'); elsif rising_edge(clk) then if en_snd then @@ -248,14 +257,24 @@ begin wav_trigger <= '0'; noi_trigger <= '0'; end if; - - -- Rotate wave table on rising edge of wav_shift - if wav_shift and not wav_shift_r then - wav_temp := wav_ram(0); - for I in 0 to 30 loop - wav_ram(I) <= wav_ram(I+1); - end loop; - wav_ram(31) <= wav_temp; + + sq2_nr2change <= '0'; + sq1_nr2change <= '0'; + noi_nr2change <= '0'; + noi_freqchange<= '0'; + + sq2_lenchange <= '0'; + sq1_lenchange <= '0'; + wav_lenchange <= '0'; + noi_lenchange <= '0'; + sq1_lenquirk <= '0'; + sq2_lenquirk <= '0'; + wav_lenquirk <= '0'; + noi_lenquirk <= '0'; + sq1_swdir_change <= '0'; + + if sq1_freqchange = '1' then + sq1_freq <= sq1_fr2; end if; if s1_write = '1' then @@ -263,12 +282,21 @@ begin -- Square 1 when "010000" => -- NR10 FF10 -PPP NSSS Sweep period, negate, shift sq1_swper <= s1_writedata(6 downto 4); + if s1_writedata(3) = '0' then -- only neg to pos, 1 -> 0 + sq1_swdir_change <= '1'; + end if; sq1_swdir <= s1_writedata(3); sq1_swshift <= s1_writedata(2 downto 0); when "010001" => -- NR11 FF11 DDLL LLLL Duty, Length load (64-L) sq1_duty <= s1_writedata(7 downto 6); - sq1_slen <= s1_writedata(5 downto 0); + sq1_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0))); + sq1_lenchange <= '1'; when "010010" => -- NR12 FF12 VVVV APPP Starting volume, Envelope add mode, period + -- zombie mode copy old values + sq1_envsgn_old <= sq1_envsgn; + sq1_envper_old <= sq1_envper; + -- write to registers + sq1_nr2change <= '1'; sq1_svol <= s1_writedata(7 downto 4); sq1_envsgn <= s1_writedata(3); sq1_envper <= s1_writedata(2 downto 0); @@ -276,21 +304,33 @@ begin sq1_freq(7 downto 0) <= s1_writedata; when "010100" => -- NR14 FF14 TL-- -FFF Trigger, Length enable, Frequency MSB sq1_trigger <= s1_writedata(7); + if sq1_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + sq1_lenquirk <= '1'; + end if; sq1_lenchk <= s1_writedata(6); sq1_freq(10 downto 8) <= s1_writedata(2 downto 0); -- Square 2 when "010110" => -- NR21 FF16 DDLL LLLL Duty, Length load (64-L) sq2_duty <= s1_writedata(7 downto 6); - sq2_slen <= s1_writedata(5 downto 0); + sq2_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0))); + sq2_lenchange <= '1'; when "010111" => -- NR22 FF17 VVVV APPP Starting volume, Envelope add mode, period + -- zombie mode copy old values + sq2_envsgn_old <= sq2_envsgn; + sq2_envper_old <= sq2_envper; + -- write to registers sq2_svol <= s1_writedata(7 downto 4); + sq2_nr2change <= '1'; sq2_envsgn <= s1_writedata(3); sq2_envper <= s1_writedata(2 downto 0); when "011000" => -- NR23 FF18 FFFF FFFF Frequency LSB sq2_freq(7 downto 0) <= s1_writedata; when "011001" => -- NR24 FF19 TL-- -FFF Trigger, Length enable, Frequency MSB sq2_trigger <= s1_writedata(7); + if sq2_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + sq2_lenquirk <= '1'; + end if; sq2_lenchk <= s1_writedata(6); sq2_freq(10 downto 8) <= s1_writedata(2 downto 0); @@ -298,34 +338,49 @@ begin when "011010" => -- NR30 FF1A E--- ---- DAC power wav_enable <= s1_writedata(7); when "011011" => -- NR31 FF1B LLLL LLLL Length load (256-L) - wav_slen <= s1_writedata; + -- wav_slen <= s1_writedata; + wav_slen <= std_logic_vector("100000000" - unsigned(s1_writedata)); + wav_lenchange <= '1'; when "011100" => -- NR32 FF1C -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%) wav_volsh <= s1_writedata(6 downto 5); when "011101" => -- NR33 FF1D FFFF FFFF Frequency LSB wav_freq(7 downto 0) <= s1_writedata; when "011110" => -- NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB wav_trigger <= s1_writedata(7); + if wav_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + wav_lenquirk <= '1'; + end if; wav_lenchk <= s1_writedata(6); wav_freq(10 downto 8) <= s1_writedata(2 downto 0); -- Noise when "100000" => -- NR41 FF20 --LL LLLL Length load (64-L) - noi_slen <= s1_writedata(5 downto 0); + noi_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0))); + noi_lenchange <= '1'; when "100001" => -- NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period + -- zombie mode copy old values + noi_envsgn_old <= noi_envsgn; + noi_envper_old <= noi_envper; + -- write to registers noi_svol <= s1_writedata(7 downto 4); + noi_nr2change <= '1'; noi_envsgn <= s1_writedata(3); noi_envper <= s1_writedata(2 downto 0); when "100010" => -- NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code noi_freqsh <= s1_writedata(7 downto 4); noi_short <= s1_writedata(3); noi_div <= s1_writedata(2 downto 0); + noi_freqchange <= '1'; when "100011" => -- NR44 FF23 TL-- ---- Trigger, Length enable noi_trigger <= s1_writedata(7); + if noi_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then + noi_lenquirk <= '1'; + end if; noi_lenchk <= s1_writedata(6); -- -- Control/Status --- when "100100" => -- NR50 FF24 ALLL BRRR Vin L enable, Left vol, Vin R enable, Right vol --- when "100101" => -- NR51 FF25 NW21 NW21 Left enables, Right enables + when "100100" => ch_vol <= s1_writedata; -- NR50 FF24 + when "100101" => ch_map <= s1_writedata; -- NR51 FF25 -- -- Wave Table when "110000" => -- FF30 0000 1111 Samples 0 and 1 @@ -382,96 +437,6 @@ begin end case; end if; - if s1_read = '1' then - case s1_addr is - -- Square 1 - when "010000" => -- NR10 FF10 -PPP NSSS Sweep period, negate, shift - s1_readdata <= '1' & sq1_swper & sq1_swdir & sq1_swshift; - when "010001" => -- NR11 FF11 DDLL LLLL Duty, Length load (64-L) - s1_readdata <= sq1_duty & "111111"; - when "010010" => -- NR12 FF12 VVVV APPP Starting volume, Envelope add mode, period - s1_readdata <= sq1_vol & sq1_envsgn & sq1_envper; - when "010011" => -- NR13 FF13 FFFF FFFF Frequency LSB - s1_readdata <= X"FF"; - when "010100" => -- NR14 FF14 TL-- -FFF Trigger, Length enable, Frequency MSB - s1_readdata <= '0' & sq1_lenchk & "111111"; - - -- Square 2 - when "010110" => -- NR21 FF16 DDLL LLLL Duty, Length load (64-L) - s1_readdata <= sq2_duty & "111111"; - when "010111" => -- NR22 FF17 VVVV APPP Starting volume, Envelope add mode, period - s1_readdata <= sq2_vol & sq2_envsgn & sq2_envper; - when "011000" => -- NR23 FF18 FFFF FFFF Frequency LSB - s1_readdata <= X"FF"; - when "011001" => -- NR24 FF19 TL-- -FFF Trigger, Length enable, Frequency MSB - s1_readdata <= '0' & sq2_lenchk & "111111"; - - when "100110" => -- NR52 FF26 P--- NW21 Power control/status, Channel length statuses - s1_readdata <= snd_enable & "00000" & sq2_playing & sq1_playing; - - -- Wave - when "011010" => -- NR30 FF1A E--- ---- DAC power - s1_readdata <= wav_enable & "1111111"; - when "011011" => -- NR31 FF1B LLLL LLLL Length load (256-L) - s1_readdata <= X"FF"; - when "011100" => -- NR32 FF1C -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%) - s1_readdata <= '1' & wav_volsh & "11111"; - when "011101" => -- NR33 FF1D FFFF FFFF Frequency LSB - s1_readdata <= X"FF"; - when "011110" => -- NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB - s1_readdata <= wav_trigger & wav_lenchk & "111111"; - - -- Noise - when "100000" => -- NR41 FF20 --LL LLLL Length load (64-L) - s1_readdata <= X"FF"; - when "100001" => -- NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period - s1_readdata <= noi_svol & noi_envsgn & noi_envper; - when "100010" => -- NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code - s1_readdata <= noi_freqsh & noi_short & noi_div; - when "100011" => -- NR44 FF23 TL-- ---- Trigger, Length enable - s1_readdata <= noi_trigger & noi_lenchk & "111111"; - - -- Wave Table - when "110000" => -- FF30 0000 1111 Samples 0 and 1 - s1_readdata <= wav_ram(0) & wav_ram(1); - when "110001" => -- FF31 0000 1111 Samples 2 and 3 - s1_readdata <= wav_ram(2) & wav_ram(3); - when "110010" => -- FF32 0000 1111 Samples 4 and 5 - s1_readdata <= wav_ram(4) & wav_ram(5); - when "110011" => -- FF33 0000 1111 Samples 6 and 31 - s1_readdata <= wav_ram(6) & wav_ram(7); - when "110100" => -- FF34 0000 1111 Samples 8 and 31 - s1_readdata <= wav_ram(8) & wav_ram(9); - when "110101" => -- FF35 0000 1111 Samples 10 and 11 - s1_readdata <= wav_ram(10) & wav_ram(11); - when "110110" => -- FF36 0000 1111 Samples 12 and 13 - s1_readdata <= wav_ram(12) & wav_ram(13); - when "110111" => -- FF37 0000 1111 Samples 14 and 15 - s1_readdata <= wav_ram(14) & wav_ram(15); - when "111000" => -- FF38 0000 1111 Samples 16 and 17 - s1_readdata <= wav_ram(16) & wav_ram(17); - when "111001" => -- FF39 0000 1111 Samples 18 and 19 - s1_readdata <= wav_ram(18) & wav_ram(19); - when "111010" => -- FF3A 0000 1111 Samples 20 and 21 - s1_readdata <= wav_ram(20) & wav_ram(21); - when "111011" => -- FF3B 0000 1111 Samples 22 and 23 - s1_readdata <= wav_ram(22) & wav_ram(23); - when "111100" => -- FF3C 0000 1111 Samples 24 and 25 - s1_readdata <= wav_ram(24) & wav_ram(25); - when "111101" => -- FF3D 0000 1111 Samples 26 and 27 - s1_readdata <= wav_ram(26) & wav_ram(27); - when "111110" => -- FF3E 0000 1111 Samples 28 and 29 - s1_readdata <= wav_ram(28) & wav_ram(29); - when "111111" => -- FF3F 0000 1111 Samples 30 and 31 - s1_readdata <= wav_ram(30) & wav_ram(31); - - when others => - s1_readdata <= X"FF"; - end case; - - end if; - - wav_shift_r := wav_shift; end if; if reset = '1' then @@ -483,78 +448,206 @@ begin end if; end if; end process; + + process(s1_addr, sq1_swper, sq1_swdir, sq1_swshift, sq1_duty, sq1_svol, sq1_envsgn, sq1_envper, sq1_lenchk, + noi_playing, wav_playing, sq2_playing, sq1_playing, wav_enable, wav_volsh, wav_ram, + sq2_duty, sq2_svol, sq2_envsgn, sq2_envper, sq2_lenchk, snd_enable, wav_lenchk, noi_svol, noi_envsgn, noi_envper, + noi_freqsh, noi_short, noi_div, noi_lenchk, ch_vol, ch_map) + begin + case s1_addr is + -- Square 1 + when "010000" => -- NR10 FF10 -PPP NSSS Sweep period, negate, shift + s1_readdata <= '1' & sq1_swper & sq1_swdir & sq1_swshift; + when "010001" => -- NR11 FF11 DDLL LLLL Duty, Length load (64-L) + s1_readdata <= sq1_duty & "111111"; + when "010010" => -- NR12 FF12 VVVV APPP Starting volume, Envelope add mode, period + s1_readdata <= sq1_svol & sq1_envsgn & sq1_envper; + when "010011" => -- NR13 FF13 FFFF FFFF Frequency LSB + s1_readdata <= X"FF"; + when "010100" => -- NR14 FF14 TL-- -FFF Trigger, Length enable, Frequency MSB + s1_readdata <= '1' & sq1_lenchk & "111111"; + + -- Square 2 + when "010110" => -- NR21 FF16 DDLL LLLL Duty, Length load (64-L) + s1_readdata <= sq2_duty & "111111"; + when "010111" => -- NR22 FF17 VVVV APPP Starting volume, Envelope add mode, period + s1_readdata <= sq2_svol & sq2_envsgn & sq2_envper; + when "011000" => -- NR23 FF18 FFFF FFFF Frequency LSB + s1_readdata <= X"FF"; + when "011001" => -- NR24 FF19 TL-- -FFF Trigger, Length enable, Frequency MSB + s1_readdata <= '1' & sq2_lenchk & "111111"; + + when "100110" => -- NR52 FF26 P--- NW21 Power control/status, Channel length statuses + s1_readdata <= snd_enable & "111" & noi_playing & wav_playing & sq2_playing & sq1_playing; + + -- Wave + when "011010" => -- NR30 FF1A E--- ---- DAC power + s1_readdata <= wav_enable & "1111111"; + when "011011" => -- NR31 FF1B LLLL LLLL Length load (256-L) + s1_readdata <= X"FF"; + when "011100" => -- NR32 FF1C -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%) + s1_readdata <= '1' & wav_volsh & "11111"; + when "011101" => -- NR33 FF1D FFFF FFFF Frequency LSB + s1_readdata <= X"FF"; + when "011110" => -- NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB + s1_readdata <= '1' & wav_lenchk & "111111"; + + -- Noise + when "100000" => -- NR41 FF20 --LL LLLL Length load (64-L) + s1_readdata <= X"FF"; + when "100001" => -- NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period + s1_readdata <= noi_svol & noi_envsgn & noi_envper; + when "100010" => -- NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code + s1_readdata <= noi_freqsh & noi_short & noi_div; + when "100011" => -- NR44 FF23 TL-- ---- Trigger, Length enable + s1_readdata <= '1' & noi_lenchk & "111111"; + + -- Wave Table + when "110000" => -- FF30 0000 1111 Samples 0 and 1 + s1_readdata <= wav_ram(0) & wav_ram(1); + when "110001" => -- FF31 0000 1111 Samples 2 and 3 + s1_readdata <= wav_ram(2) & wav_ram(3); + when "110010" => -- FF32 0000 1111 Samples 4 and 5 + s1_readdata <= wav_ram(4) & wav_ram(5); + when "110011" => -- FF33 0000 1111 Samples 6 and 31 + s1_readdata <= wav_ram(6) & wav_ram(7); + when "110100" => -- FF34 0000 1111 Samples 8 and 31 + s1_readdata <= wav_ram(8) & wav_ram(9); + when "110101" => -- FF35 0000 1111 Samples 10 and 11 + s1_readdata <= wav_ram(10) & wav_ram(11); + when "110110" => -- FF36 0000 1111 Samples 12 and 13 + s1_readdata <= wav_ram(12) & wav_ram(13); + when "110111" => -- FF37 0000 1111 Samples 14 and 15 + s1_readdata <= wav_ram(14) & wav_ram(15); + when "111000" => -- FF38 0000 1111 Samples 16 and 17 + s1_readdata <= wav_ram(16) & wav_ram(17); + when "111001" => -- FF39 0000 1111 Samples 18 and 19 + s1_readdata <= wav_ram(18) & wav_ram(19); + when "111010" => -- FF3A 0000 1111 Samples 20 and 21 + s1_readdata <= wav_ram(20) & wav_ram(21); + when "111011" => -- FF3B 0000 1111 Samples 22 and 23 + s1_readdata <= wav_ram(22) & wav_ram(23); + when "111100" => -- FF3C 0000 1111 Samples 24 and 25 + s1_readdata <= wav_ram(24) & wav_ram(25); + when "111101" => -- FF3D 0000 1111 Samples 26 and 27 + s1_readdata <= wav_ram(26) & wav_ram(27); + when "111110" => -- FF3E 0000 1111 Samples 28 and 29 + s1_readdata <= wav_ram(28) & wav_ram(29); + when "111111" => -- FF3F 0000 1111 Samples 30 and 31 + s1_readdata <= wav_ram(30) & wav_ram(31); + + -- Control/Status + when "100100" => + s1_readdata <= ch_vol; -- NR50 FF24 + when "100101" => + s1_readdata <= ch_map; -- NR51 FF25 + + + when others => + s1_readdata <= X"FF"; + end case; + + end process; sound : process(clk, snd_enable, en_snd, en_len, en_env, en_sweep) - constant duty_0 : std_logic_vector(0 to 7) := "00000001"; - constant duty_1 : std_logic_vector(0 to 7) := "10000001"; - constant duty_2 : std_logic_vector(0 to 7) := "10000111"; - constant duty_3 : std_logic_vector(0 to 7) := "01111110"; + constant duty_0 : std_logic_vector(0 to 7) := "00000001"; + constant duty_1 : std_logic_vector(0 to 7) := "10000001"; + constant duty_2 : std_logic_vector(0 to 7) := "10000111"; + constant duty_3 : std_logic_vector(0 to 7) := "01111110"; variable sq1_fcnt : unsigned(10 downto 0); variable sq1_phase : integer range 0 to 7; variable sq1_len : std_logic_vector(6 downto 0); - variable sq1_envcnt : std_logic_vector(2 downto 0); -- Sq1 envelope timer count - variable sq1_swcnt : std_logic_vector(2 downto 0); -- Sq1 sweep timer count + variable sq1_envcnt : std_logic_vector(3 downto 0); -- Sq1 envelope timer count + variable sq1_swcnt : std_logic_vector(3 downto 0); -- Sq1 sweep timer count variable sq1_swoffs : unsigned(11 downto 0); variable sq1_swfr : unsigned(11 downto 0); variable sq1_out : std_logic; + variable sq1_sweep_en: boolean; + variable sweep_calculate: boolean; + variable sweep_update: boolean; + variable sweep_negate: boolean; + variable sq1_envoff : boolean; -- check if envelope is on (zombiemode) variable sq2_fcnt : unsigned(10 downto 0); variable sq2_phase : integer range 0 to 7; variable sq2_len : std_logic_vector(6 downto 0); - variable sq2_envcnt : std_logic_vector(2 downto 0); -- Sq2 envelope timer count + variable sq2_envcnt : std_logic_vector(3 downto 0); -- Sq2 envelope timer count + variable sq2_envoff : boolean; -- check if envelope is on (zombiemode) variable sq2_out : std_logic; variable wav_fcnt : unsigned(10 downto 0); variable wav_len : std_logic_vector(8 downto 0); + variable wav_shift_r : boolean; + variable wav_shift : boolean; + variable wav_index : unsigned(4 downto 0); - variable noi_divisor: unsigned(10 downto 0); -- Noise frequency divisor - variable noi_freq : unsigned(10 downto 0); -- Noise frequency (calculated) + variable noi_divisor : unsigned(10 downto 0); -- Noise frequency divisor + variable noi_period : unsigned(10 downto 0); -- Noise period (calculated) variable noi_fcnt : unsigned(10 downto 0); - variable noi_lfsr : unsigned(15 downto 0); + variable noi_lfsr : unsigned(14 downto 0); -- 15 bits variable noi_len : std_logic_vector(6 downto 0); - variable noi_envcnt : std_logic_vector(2 downto 0); -- Noise envelope timer count + variable noi_envcnt : std_logic_vector(3 downto 0); -- Noise envelope timer count + variable noi_envoff : boolean; -- check if envelope is on (zombiemode) variable noi_out : std_logic; + variable noi_xor : std_logic; + + variable tmp_volume : unsigned(7 downto 0); -- used in zombie mode variable acc_fcnt : unsigned(11 downto 0); begin -- Sound processing if snd_enable = '0' then sq1_playing <= '0'; - sq1_fr2 <= (others => '0'); + sq1_fr2 <= (others => '0'); sq1_fcnt := (others => '0'); - sq1_phase := 0; - sq1_len := (others => '0'); - sq1_vol <= "0000"; - sq1_envcnt := "000"; - sq1_swcnt := "000"; + sq1_phase := 0; + sq1_len := (others => '0'); + sq1_vol <= "0000"; + sq1_envcnt := "0000"; + sq1_swcnt := "0000"; sq1_swoffs := (others => '0'); sq1_swfr := (others => '0'); - sq1_out := '0'; + sq1_out := '0'; + sq2_playing <= '0'; - sq2_fr2 <= (others => '0'); sq2_fcnt := (others => '0'); - sq2_phase := 0; - sq2_len := (others => '0'); - sq2_vol <= "0000"; - sq2_envcnt := "000"; - sq2_out := '0'; + sq2_phase := 0; + sq2_len := (others => '0'); + sq2_vol <= "0000"; + sq2_envcnt := "0000"; + sq2_out := '0'; + wav_playing <= '0'; wav_fcnt := (others => '0'); - wav_len := (others => '0'); - + wav_len := (others => '0'); + wav_shift_r := false; + wav_index := (others => '0'); + noi_playing <= '0'; - noi_fr2 <= (others => '0'); noi_fcnt := (others => '0'); noi_lfsr := (others => '1'); - noi_len := (others => '0'); - noi_vol <= "0000"; - noi_envcnt := "000"; - noi_out := '0'; + noi_len := (others => '0'); + noi_vol <= "0000"; + noi_envcnt := "0000"; + noi_out := '0'; + sweep_calculate:= false; + sweep_update := false; + sq1_sweep_en := false; + sweep_negate := false; + + -- zombie mode check if env is still updating + sq2_envoff := false; + sq1_envoff := false; + noi_envoff := false; elsif rising_edge(clk) then + + + ----------------------- Square channel 1 ----------------------------- + if en_snd4 then - -- Sq1 frequency timer + -- Sq1 frequency timer Frequency = 131072/(2048-x) Hz if sq1_playing = '1' then acc_fcnt := ('0'&sq1_fcnt) + to_unsigned(1, acc_fcnt'length); if acc_fcnt(acc_fcnt'high) = '1' then @@ -563,45 +656,12 @@ begin else sq1_phase := 0; end if; - sq1_fcnt := unsigned(sq1_fr2); + sq1_fcnt := unsigned(sq1_freq); else sq1_fcnt := acc_fcnt(sq1_fcnt'range); end if; end if; - - -- Sq2 frequency timer - if sq2_playing = '1' then - acc_fcnt := ('0'&sq2_fcnt) + to_unsigned(1, acc_fcnt'length); - if acc_fcnt(acc_fcnt'high) = '1' then - if sq2_phase < 7 then - sq2_phase := sq2_phase + 1; - else - sq2_phase := 0; - end if; - sq2_fcnt := unsigned(sq2_fr2); - else - sq2_fcnt := acc_fcnt(sq2_fcnt'range); - end if; - end if; - - -- Noi frequency timer - if noi_playing = '1' then - acc_fcnt := ('0'&noi_fcnt) + to_unsigned(1, acc_fcnt'length); - if acc_fcnt(acc_fcnt'high) = '1' then - -- Noise LFSR - if noi_short = '1' then - noi_lfsr := (noi_lfsr(0) xor noi_lfsr(1)) & noi_lfsr(15 downto 8) & (noi_lfsr(0) xor noi_lfsr(1)) & noi_lfsr(6 downto 1); - else - noi_lfsr := (noi_lfsr(0) xor noi_lfsr(1)) & noi_lfsr(15 downto 1); - end if; - - noi_out := not noi_lfsr(0); - noi_fcnt := unsigned(noi_fr2); - else - noi_fcnt := acc_fcnt(noi_fcnt'range); - end if; - end if; - + case sq1_duty is when "00" => sq1_out := duty_0(sq1_phase); when "01" => sq1_out := duty_1(sq1_phase); @@ -614,6 +674,210 @@ begin sq1_wav <= sq1_vol & "00"; else sq1_wav <= "000000"; + end if; + end if; + + -- Length counter + if en_len or sq1_lenquirk = '1' then + if sq1_len > 0 and sq1_lenchk = '1' then + sq1_len := std_logic_vector(unsigned(sq1_len) - 1); + end if; + end if; + + -- Sweep processing + + -- sweep counter + if en_sweep then + sq1_swcnt := std_logic_vector(unsigned(sq1_swcnt) - 1); + if sq1_swcnt = 0 then + + -- reload counter with period + if sq1_swper = "000" then + sq1_swcnt := "1000"; -- set to 8 + else + sq1_swcnt := '0' & sq1_swper; -- set to period + end if; + + -- check if update needed + if sq1_sweep_en and sq1_swper /= "000" then + sweep_calculate:= true; + sweep_update:=true; + end if; + end if; + end if; + + sq1_freqchange <= '0'; + + if sq1_sweep_en then + + -- Calculate next sweep frequency + if sweep_calculate then + case sq1_swshift is + when "000" => sq1_swoffs := unsigned('0' & sq1_fr2); + when "001" => sq1_swoffs := "00" & unsigned(sq1_fr2(10 downto 1)); + when "010" => sq1_swoffs := "000" & unsigned(sq1_fr2(10 downto 2)); + when "011" => sq1_swoffs := "0000" & unsigned(sq1_fr2(10 downto 3)); + when "100" => sq1_swoffs := "00000" & unsigned(sq1_fr2(10 downto 4)); + when "101" => sq1_swoffs := "000000" & unsigned(sq1_fr2(10 downto 5)); + when "110" => sq1_swoffs := "0000000" & unsigned(sq1_fr2(10 downto 6)); + when "111" => sq1_swoffs := "00000000" & unsigned(sq1_fr2(10 downto 7)); + when others => sq1_swoffs := unsigned('0' & sq1_fr2); + end case; + if sq1_swdir = '1' then + sq1_swfr := ('0' & unsigned(sq1_fr2)) - sq1_swoffs; + sweep_negate := true; + else + sq1_swfr := ('0' & unsigned(sq1_fr2)) + sq1_swoffs; + sweep_negate := false; + end if; + sweep_calculate:= false; + end if; + + -- update registers, and calculate next frequency + if sweep_update then + sweep_update := false; + if (sq1_swper /= "000" and sq1_swshift /= "000") then + sq1_fr2 <= std_logic_vector(sq1_swfr(10 downto 0)); + sq1_freqchange <= '1'; + sweep_calculate:= true; -- when updating calculate 2nd time + end if; + end if; + + end if; + + if sq1_playing = '1' then + + -- Envelope counter + if en_env and sq1_envper /= "000" then + + sq1_envcnt := std_logic_vector(unsigned(sq1_envcnt) - 1); -- decrement counter + + if sq1_envcnt = 0 then + if sq1_envsgn = '1' then + if sq1_vol /= "1111" then -- sq1_vol < 15 + sq1_vol <= std_logic_vector(unsigned(sq1_vol) + 1); + else + sq1_envoff := true; -- envelope done + end if; + else + if sq1_vol /= "0000" then -- sq1_vol > + sq1_vol <= std_logic_vector(unsigned(sq1_vol) - 1); + else + sq1_envoff := true; -- envelope done + end if; + end if; + + -- reload counter with period + if sq1_envper = "000" then + sq1_envcnt := "1000"; -- set to 8 + else + sq1_envcnt := '0' & sq1_envper; -- set to period + end if; + + end if; + end if; + + -- Check for end of playing conditions + if (sq1_lenchk = '1' and sq1_len = 0) -- Play length timer overrun + or (sq1_sweep_en and sq1_swfr(11) = '1') -- Sweep frequency overrun + or (sq1_sweep_en and sq1_swdir_change = '1' and sweep_negate) -- sweep direction change after trigger + then + sq1_playing <= '0'; + sq1_envcnt := (others => '0'); + sq1_swcnt := (others => '0'); + sq1_swfr := (others => '0'); + sq1_sweep_en := false; + end if; + end if; + + if sq1_trigger = '1' or sq1_nr2change = '1' then + + -- "zombie" mode + tmp_volume := "0000"&unsigned(sq1_vol); + if sq1_envper_old="000" and not sq1_envoff then + tmp_volume := "0000"&unsigned(sq1_vol) + 1; + else + if sq1_envsgn_old = '0' then + tmp_volume := "0000"&unsigned(sq1_vol) + 2; + end if; + end if; + + if (sq1_envsgn xor sq1_envsgn_old) = '1' then + tmp_volume := X"10" - tmp_volume; + end if; + + sq1_vol <= std_logic_vector(tmp_volume(3 downto 0)); + + -- check if dac is enabled + if sq1_svol = "00000" and sq1_envsgn = '0' then -- dac disabled + sq1_playing <= '0'; + end if; + end if; + + + if sq1_lenchange = '1' then + sq1_len := sq1_slen; + end if; + + + -- Check sample trigger and start playing + if sq1_trigger = '1' then + sq1_vol <= sq1_svol; + sq1_fr2 <= sq1_freq; -- shadow frequency register for sweep unit + sq1_sweep_en := sq1_swper /= "000" or sq1_swshift /= "000" ; -- sweep unit enabled ? + ---- sweep quirks --- + if sq1_swshift /= "000" then + sweep_calculate := true; + end if; + if sq1_swper = "000" then + sq1_swcnt := "1000"; -- set to 8 + else + sq1_swcnt := '0' & sq1_swper; -- set to period + end if; + sweep_negate := false; + ---- sweep quirks --- + + sq1_fcnt := unsigned(sq1_freq); + if not (sq1_svol = "00000" and sq1_envsgn = '0') then -- dac enabled + sq1_playing <= '1'; + end if; + + -- reload envelope counter with period + if sq1_envper = "000" then + sq1_envcnt := "1000"; -- set to 8 + else + sq1_envcnt := '0' & sq1_envper; -- set to period + end if; + sq1_envoff := false; + + + sq1_phase := 0; + if sq1_len = 0 then -- trigger quirks + if sq1_lenchk = '1' and en_len_r then + sq1_len := "0111111"; -- 63 + else + sq1_len := "1000000"; -- 64 + end if; + end if; + + end if; + + ----------------------- Square channel 2 ----------------------------- + + if en_snd4 then + -- Sq2 frequency timer Frequency = 131072/(2048-x) Hz + if sq2_playing = '1' then + acc_fcnt := ('0'&sq2_fcnt) + to_unsigned(1, acc_fcnt'length); + if acc_fcnt(acc_fcnt'high) = '1' then + if sq2_phase < 7 then + sq2_phase := sq2_phase + 1; + else + sq2_phase := 0; + end if; + sq2_fcnt := unsigned(sq2_freq); + else + sq2_fcnt := acc_fcnt(sq2_fcnt'range); + end if; end if; case sq2_duty is @@ -629,268 +893,332 @@ begin else sq2_wav <= "000000"; end if; + end if; + + -- Length counter + if en_len or sq2_lenquirk = '1' then + if sq2_len > 0 and sq2_lenchk = '1' then + sq2_len := std_logic_vector(unsigned(sq2_len) - 1); + end if; + end if; + if sq2_playing = '1' then + + -- Envelope counter + if en_env and sq2_envper /= "000" then + + sq2_envcnt := std_logic_vector(unsigned(sq2_envcnt) - 1); -- decrement counter + + if sq2_envcnt = 0 then + if sq2_envsgn = '1' then + if sq2_vol /= "1111" then -- sq2_vol < 15 + sq2_vol <= std_logic_vector(unsigned(sq2_vol) + 1); + else + sq2_envoff := true; + end if; + else + if sq2_vol /= "0000" then -- sq2_vol > 0 + sq2_vol <= std_logic_vector(unsigned(sq2_vol) - 1); + else + sq2_envoff := true; + end if; + end if; + + -- reload counter with period + if sq2_envper = "000" then + sq2_envcnt := "1000"; -- set to 8 + else + sq2_envcnt := '0' & sq2_envper; -- set to period + end if; + end if; + end if; + + -- Check for end of playing conditions + -- if sq2_vol = X"0" -- Volume == 0 + if sq2_lenchk = '1' and sq2_len = 0 -- Play length timer overrun + then + sq2_playing <= '0'; + sq2_envcnt := "0000"; + end if; + end if; + + if sq2_nr2change ='1' or sq2_trigger= '1' then + + -- "zombie" mode + tmp_volume := "0000"&unsigned(sq2_vol); + if sq2_envper_old="000" and not sq2_envoff then + tmp_volume := "0000"&unsigned(sq2_vol) + 1; + else + if sq2_envsgn_old = '0' then + tmp_volume := "0000"&unsigned(sq2_vol) + 2; + end if; + end if; + + if (sq2_envsgn xor sq2_envsgn_old) = '1' then + tmp_volume := X"10" - tmp_volume; + end if; + + sq2_vol <= std_logic_vector(tmp_volume(3 downto 0)); + + -- check if dac is enabled + if sq2_svol = "00000" and sq2_envsgn = '0' then -- dac disabled + sq2_playing <= '0'; + end if; + end if; + + if sq2_lenchange = '1' then + sq2_len := sq2_slen; + end if; + + -- Check sample trigger and start playing + if sq2_trigger = '1' then + sq2_vol <= sq2_svol; + + -- sq2_fr2 <= sq2_freq; + sq2_fcnt := unsigned(sq2_freq); + + if not (sq2_svol = "00000" and sq2_envsgn = '0') then -- dac enabled + sq2_playing <= '1'; + end if; + + -- reload envelope counter with period + if sq2_envper = "000" then + sq2_envcnt := "1000"; -- set to 8 + else + sq2_envcnt := '0' & sq2_envper; -- set to period + end if; + sq2_envoff := false; + + sq2_phase := 0; + if sq2_len = 0 then -- trigger quirks + if sq2_lenchk = '1' and en_len_r then + sq2_len := "0111111"; -- 63 + else + sq2_len := "1000000"; -- 64 + end if; + end if; + end if; + + + ----------------------- Noise channel ----------------------------- + + -- Noi frequency timer Frequency = 524288 Hz / r / 2^(s+1) ;For r=0 assume r=0.5 instead + if en_snd4 then + if noi_playing = '1' then + acc_fcnt := ('0'&noi_fcnt) - to_unsigned(1, acc_fcnt'length); + if acc_fcnt = 0 then + -- Noise LFSR + noi_xor := noi_lfsr(0) xor noi_lfsr(1); + noi_lfsr := noi_xor & noi_lfsr(14 downto 1); + + if noi_short = '1' then + noi_lfsr(6) := noi_xor; + end if; + + noi_out := not noi_lfsr(0); + noi_fcnt := noi_period; + else + noi_fcnt := acc_fcnt(noi_fcnt'range); + end if; + end if; + if noi_out = '1' then noi_wav <= noi_vol & "00"; else noi_wav <= "000000"; end if; - end if; - - -- Square channel 1 - if sq1_playing = '1' then - -- Length counter - if en_len then - if sq1_len(6) = '0' then - sq1_len := std_logic_vector(unsigned(sq1_len) + to_unsigned(1, sq1_len'length)); - end if; - end if; - - -- Envelope counter - if en_env then - if sq1_envper /= "000" then - sq1_envcnt := std_logic_vector(unsigned(sq1_envcnt) + to_unsigned(1, sq1_envcnt'length)); - if sq1_envcnt = sq1_envper then - if sq1_envsgn = '1' then - if sq1_vol /= "1111" then - sq1_vol <= std_logic_vector(unsigned(sq1_vol) + to_unsigned(1, sq1_vol'length)); - end if; - else - if sq1_vol /= "0000" then - sq1_vol <= std_logic_vector(unsigned(sq1_vol) - to_unsigned(1, sq1_vol'length)); - end if; - end if; - sq1_envcnt := "000"; - end if; - end if; - end if; - - -- Sweep processing - if en_sweep or sq1_trigger = '1' then - case sq1_swshift is - when "000" => sq1_swoffs := unsigned('0' & sq1_fr2); - when "001" => sq1_swoffs := "00" & unsigned(sq1_fr2(10 downto 1)); - when "010" => sq1_swoffs := "000" & unsigned(sq1_fr2(10 downto 2)); - when "011" => sq1_swoffs := "0000" & unsigned(sq1_fr2(10 downto 3)); - when "100" => sq1_swoffs := "00000" & unsigned(sq1_fr2(10 downto 4)); - when "101" => sq1_swoffs := "000000" & unsigned(sq1_fr2(10 downto 5)); - when "110" => sq1_swoffs := "0000000" & unsigned(sq1_fr2(10 downto 6)); - when "111" => sq1_swoffs := "00000000" & unsigned(sq1_fr2(10 downto 7)); - when others => sq1_swoffs := unsigned('0' & sq1_fr2); - end case; - - -- Calculate next sweep frequency - if sq1_swper /= "000" then - if sq1_swdir = '0' then - sq1_swfr := ('0' & unsigned(sq1_fr2)) - sq1_swoffs; - else - sq1_swfr := ('0' & unsigned(sq1_fr2)) + sq1_swoffs; - end if; - else -- Sweep disabled - sq1_swfr := unsigned('0' & sq1_fr2); - end if; - - -- Sweep counter - if sq1_swper /= "000" then - sq1_swcnt := std_logic_vector(unsigned(sq1_swcnt) + to_unsigned(1, sq1_swcnt'length)); - if sq1_swcnt = sq1_swper then - sq1_fr2 <= std_logic_vector(sq1_swfr(10 downto 0)); - sq1_swcnt := "000"; - end if; - end if; - end if; - - -- Check for end of playing conditions - if sq1_vol = X"0" -- Volume == 0 - or (sq1_lenchk = '1' and sq1_len(6) = '1') -- Play length timer overrun - or (sq1_swper /= "000" and sq1_swfr(11) = '1') -- Sweep frequency overrun - then - sq1_playing <= '0'; - sq1_envcnt := "000"; - sq1_swcnt := "000"; - --sq1_wav <= "000000"; + + -- Length counter + if en_len or noi_lenquirk = '1' then + if noi_len > 0 and noi_lenchk = '1' then + noi_len := std_logic_vector(unsigned(noi_len) - 1); end if; end if; - -- Check sample trigger and start playing - if sq1_trigger = '1' then - sq1_fr2 <= sq1_freq; - sq1_fcnt := unsigned(sq1_freq); - noi_lfsr := (others => '1'); - sq1_playing <= '1'; - sq1_vol <= sq1_svol; - sq1_envcnt := "000"; - sq1_swcnt := "000"; - sq1_len := '0' & sq1_slen; - sq1_phase := 0; - end if; - - -- Square channel 2 - if sq2_playing = '1' then - -- Length counter - if en_len then - if sq2_len(6) = '0' then - sq2_len := std_logic_vector(unsigned(sq2_len) + to_unsigned(1, sq2_len'length)); - end if; - end if; - - -- Envelope counter - if en_env then - if sq2_envper /= "000" then - sq2_envcnt := std_logic_vector(unsigned(sq2_envcnt) + to_unsigned(1, sq2_envcnt'length)); - if sq2_envcnt = sq2_envper then - if sq2_envsgn = '1' then - if sq2_vol /= "1111" then - sq2_vol <= std_logic_vector(unsigned(sq2_vol) + to_unsigned(1, sq2_vol'length)); - end if; - else - if sq2_vol /= "0000" then - sq2_vol <= std_logic_vector(unsigned(sq2_vol) - to_unsigned(1, sq2_vol'length)); - end if; - end if; - sq2_envcnt := "000"; - end if; - end if; - end if; - - -- Check for end of playing conditions - if sq2_vol = X"0" -- Volume == 0 - or (sq2_lenchk = '1' and sq2_len(6) = '1') -- Play length timer overrun - then - sq2_playing <= '0'; - sq2_envcnt := "000"; - --sq2_wav <= "000000"; - end if; - end if; - - -- Check sample trigger and start playing - if sq2_trigger = '1' then - sq2_fr2 <= sq2_freq; - sq2_fcnt := unsigned(sq2_freq); - sq2_playing <= '1'; - sq2_vol <= sq2_svol; - sq2_envcnt := "000"; - sq2_len := '0' & sq2_slen; - sq2_phase := 0; - end if; - - -- Noise channel if noi_playing = '1' then - -- Length counter - if en_len then - if noi_len(6) = '0' then - noi_len := std_logic_vector(unsigned(noi_len) + to_unsigned(1, noi_len'length)); - end if; - end if; -- Envelope counter - if en_env then - if noi_envper /= "000" then - noi_envcnt := std_logic_vector(unsigned(noi_envcnt) + to_unsigned(1, noi_envcnt'length)); - if noi_envcnt = noi_envper then + if en_env and noi_envper /= "000" then + + noi_envcnt := std_logic_vector(unsigned(noi_envcnt) - 1); -- decrement counter + + if noi_envcnt = 0 then if noi_envsgn = '1' then - if noi_vol /= "1111" then - noi_vol <= std_logic_vector(unsigned(noi_vol) + to_unsigned(1, noi_vol'length)); + if noi_vol /= "1111" then -- noi_vol < 15 + noi_vol <= std_logic_vector(unsigned(noi_vol) + 1); + else + noi_envoff := true; end if; else - if noi_vol /= "0000" then - noi_vol <= std_logic_vector(unsigned(noi_vol) - to_unsigned(1, noi_vol'length)); + if noi_vol /= "0000" then -- noi_vol > 0 + noi_vol <= std_logic_vector(unsigned(noi_vol) - 1); + else + noi_envoff := true; end if; end if; - noi_envcnt := "000"; + + -- reload counter with period + if noi_envper = "000" then + noi_envcnt := "1000"; -- set to 8 + else + noi_envcnt := '0' & noi_envper; -- set to period + end if; end if; - end if; end if; - -- Check for end of playing conditions - if noi_vol = X"0" -- Volume == 0 - or (noi_lenchk = '1' and noi_len(6) = '1') -- Play length timer overrun + -- Check for end of playing conditions + if noi_lenchk = '1' and noi_len = 0 -- Play length timer overrun then noi_playing <= '0'; - noi_envcnt := "000"; + noi_envcnt := "0000"; --sq2_wav <= "000000"; end if; end if; - - -- Check sample trigger and start playing - if noi_trigger = '1' then + + if noi_nr2change ='1' or noi_trigger= '1' then + + -- "zombie" mode + tmp_volume := "0000"&unsigned(noi_vol); + if noi_envper_old="000" and not noi_envoff then + tmp_volume := "0000"&unsigned(noi_vol) + 1; + else + if noi_envsgn_old = '0' then + tmp_volume := "0000"&unsigned(noi_vol) + 2; + end if; + end if; + + if (noi_envsgn xor noi_envsgn_old) = '1' then + tmp_volume := X"10" - tmp_volume; + end if; + + noi_vol <= std_logic_vector(tmp_volume(3 downto 0)); + + -- check if dac is enabled + if noi_svol = "00000" and noi_envsgn = '0' then -- dac disabled + noi_playing <= '0'; + end if; + end if; + + if noi_lenchange = '1' then + noi_len := noi_slen; + end if; + + if noi_trigger = '1' or noi_freqchange = '1' then + -- Calculate noise frequency case noi_div is - when "000" => noi_divisor := to_unsigned(2048 - 8, noi_divisor'length); - when "001" => noi_divisor := to_unsigned(2048 - 16, noi_divisor'length); - when "010" => noi_divisor := to_unsigned(2048 - 32, noi_divisor'length); - when "011" => noi_divisor := to_unsigned(2048 - 48, noi_divisor'length); - when "100" => noi_divisor := to_unsigned(2048 - 64, noi_divisor'length); - when "101" => noi_divisor := to_unsigned(2048 - 80, noi_divisor'length); - when "110" => noi_divisor := to_unsigned(2048 - 96, noi_divisor'length); - when others => noi_divisor := to_unsigned(2048 - 112, noi_divisor'length); + when "000" => noi_period := to_unsigned(2, noi_period'length); + when "001" => noi_period := to_unsigned(4, noi_period'length); + when "010" => noi_period := to_unsigned(8, noi_period'length); + when "011" => noi_period := to_unsigned(12, noi_period'length); + when "100" => noi_period := to_unsigned(16, noi_period'length); + when "101" => noi_period := to_unsigned(20, noi_period'length); + when "110" => noi_period := to_unsigned(24, noi_period'length); + when others => noi_period := to_unsigned(28, noi_divisor'length); end case; - --- case noi_freqsh is --- when "000" => noi_freq := unsigned(noi_divisor); --- when "001" => noi_freq := '0' & unsigned(noi_divisor(10 downto 1)); --- when "010" => noi_freq := "00" & unsigned(noi_divisor(10 downto 2)); --- when "011" => noi_freq := "000" & unsigned(noi_divisor(10 downto 3)); --- when "100" => noi_freq := "0000" & unsigned(noi_divisor(10 downto 4)); --- when "101" => noi_freq := "00000" & unsigned(noi_divisor(10 downto 5)); --- when "110" => noi_freq := "000000" & unsigned(noi_divisor(10 downto 6)); --- when "111" => noi_freq := "0000000" & unsigned(noi_divisor(10 downto 7)); --- when others => noi_freq := unsigned(noi_divisor); --- end case; - noi_freq := noi_divisor sll to_integer(unsigned(noi_freqsh)); - - noi_fr2 <= std_logic_vector(noi_freq); - noi_fcnt := noi_freq; - noi_playing <= '1'; - noi_vol <= noi_svol; - noi_envcnt := "000"; - noi_len := '0' & noi_slen; + + noi_period := noi_period sll to_integer(unsigned(noi_freqsh)); + noi_fcnt := noi_period; + end if; - + + + -- Check sample trigger and start playing + if noi_trigger = '1' then + noi_vol <= noi_svol; + + noi_lfsr := (others => '1'); + + -- reload envelope counter with period + if noi_envper = "000" then + noi_envcnt := "1000"; -- set to 8 + else + noi_envcnt := '0' & noi_envper; -- set to period + end if; + noi_envoff := false; + + if not (noi_svol = "00000" and noi_envsgn = '0') then -- dac enabled + noi_playing <= '1'; + end if; + + if noi_len = 0 then -- trigger quirks + if noi_lenchk = '1' and en_len_r then + noi_len := "0111111"; -- 63 + else + noi_len := "1000000"; -- 64 + end if; + end if; + end if; + + ----------------------------- Wave channel ----------------------------------- + if en_snd2 then - -- Wave frequency timer - wav_shift <= false; + -- Wave frequency timer Frequency = 4194304/(64*(2048-x)) Hz = 65536/(2048-x) Hz + wav_shift := false; if wav_playing = '1' then acc_fcnt := ('0'&wav_fcnt) + to_unsigned(1, acc_fcnt'length); if acc_fcnt(acc_fcnt'high) = '1' then - wav_shift <= true; - wav_fcnt := unsigned(wav_fr2); + wav_shift := true; + wav_fcnt := unsigned(wav_freq); else wav_fcnt := acc_fcnt(wav_fcnt'range); end if; end if; end if; - - -- Wave channel - if wav_playing = '1' then - -- Length counter - if en_len then - if wav_len(8) = '0' then - wav_len := std_logic_vector(unsigned(wav_len) + to_unsigned(1, wav_len'length)); - end if; + + if wav_trigger = '1' then + wav_index := (others => '0'); + wav_shift_r := false; + else + -- Rotate wave table on rising edge of wav_shift + if wav_shift and not wav_shift_r then + wav_index := wav_index + 1; end if; + wav_shift_r := wav_shift; + end if; + + -- Length counter + if en_len or wav_lenquirk = '1' then + if wav_len > 0 and wav_lenchk = '1' then + wav_len := std_logic_vector(unsigned(wav_len) - 1); + end if; + end if; + if wav_playing = '1' then -- Check for end of playing conditions - if (wav_lenchk = '1' and wav_len(8) = '1') then + if (wav_lenchk = '1' and wav_len = 0) or wav_enable = '0' then wav_playing <= '0'; wav_wav <= "000000"; end if; end if; + + if wav_lenchange = '1' then + wav_len := wav_slen; + end if; -- Check sample trigger and start playing if wav_trigger = '1' then - wav_fr2 <= wav_freq; wav_fcnt := unsigned(wav_freq); wav_playing <= '1'; - wav_len := '0' & wav_slen; + if wav_len = 0 then -- trigger quirks + if wav_lenchk = '1' and en_len_r then + wav_len := "011111111"; -- 255 + else + wav_len := "100000000"; -- 256 + end if; + end if; + end if; if wav_enable = '1' and wav_volsh /= "00" then case wav_volsh is - when "01" => wav_wav <= wav_ram(0) & "00"; - when "10" => wav_wav <= '0' & wav_ram(0) & '0'; - when "11" => wav_wav <= "00" & wav_ram(0); +-- when "01" => wav_wav <= wav_ram(0) & "00"; +-- when "10" => wav_wav <= '0' & wav_ram(0) & '0'; +-- when "11" => wav_wav <= "00" & wav_ram(0); + when "01" => wav_wav <= wav_ram(to_integer(wav_index)) & "00"; + when "10" => wav_wav <= '0' & wav_ram(to_integer(wav_index)) & '0'; + when "11" => wav_wav <= "00" & wav_ram(to_integer(wav_index)); when others => wav_wav <= (others => 'X'); end case; else @@ -916,20 +1244,25 @@ begin end process; -- Mixer - mixer : process(sq1_wav, sq2_wav, noi_wav, wav_wav) + mixer : process(sq1_wav, sq2_wav, noi_wav, wav_wav, ch_map, ch_vol) variable snd_left_in : unsigned(7 downto 0); - variable snd_right_in : unsigned(7 downto 0); + variable snd_right_in : unsigned(7 downto 0); begin snd_left_in := (others => '0'); snd_right_in := (others => '0'); - snd_left_in := snd_left_in + ("00"&unsigned(sq1_wav)); - snd_left_in := snd_left_in + ("00"&unsigned(wav_wav)); - snd_right_in := snd_right_in + ("00"&unsigned(sq2_wav)); - snd_right_in := snd_right_in + ("00"&unsigned(noi_wav)); + if ch_map(0) = '1' then snd_right_in := snd_right_in + ("00"&unsigned(sq1_wav)); end if; + if ch_map(1) = '1' then snd_right_in := snd_right_in + ("00"&unsigned(sq2_wav)); end if; + if ch_map(2) = '1' then snd_right_in := snd_right_in + ("00"&unsigned(wav_wav)); end if; + if ch_map(3) = '1' then snd_right_in := snd_right_in + ("00"&unsigned(noi_wav)); end if; - snd_left <= std_logic_vector(snd_left_in) & X"00"; - snd_right <= std_logic_vector(snd_right_in) & X"00"; + if ch_map(4) = '1' then snd_left_in := snd_left_in + ("00"&unsigned(sq1_wav)); end if; + if ch_map(5) = '1' then snd_left_in := snd_left_in + ("00"&unsigned(sq2_wav)); end if; + if ch_map(6) = '1' then snd_left_in := snd_left_in + ("00"&unsigned(wav_wav)); end if; + if ch_map(7) = '1' then snd_left_in := snd_left_in + ("00"&unsigned(noi_wav)); end if; + + snd_right <= (std_logic_vector(snd_right_in) & "00000") * ch_vol(2 downto 0); + snd_left <= (std_logic_vector(snd_left_in) & "00000") * ch_vol(6 downto 4); end process; end SYN; diff --git a/cores/gameboy/hdma.v b/cores/gameboy/hdma.v new file mode 100644 index 0000000..aaac02d --- /dev/null +++ b/cores/gameboy/hdma.v @@ -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 \ No newline at end of file diff --git a/cores/gameboy/iram.qip b/cores/gameboy/iram.qip deleted file mode 100644 index ebae6b9..0000000 --- a/cores/gameboy/iram.qip +++ /dev/null @@ -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"] diff --git a/cores/gameboy/iram.v b/cores/gameboy/iram.v deleted file mode 100644 index 744357d..0000000 --- a/cores/gameboy/iram.v +++ /dev/null @@ -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 diff --git a/cores/gameboy/spram.vhd b/cores/gameboy/spram.vhd new file mode 100644 index 0000000..8767480 --- /dev/null +++ b/cores/gameboy/spram.vhd @@ -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; diff --git a/cores/gameboy/sprite.v b/cores/gameboy/sprite.v index 81ed82f..ee0ea05 100644 --- a/cores/gameboy/sprite.v +++ b/cores/gameboy/sprite.v @@ -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 diff --git a/cores/gameboy/sprite_sort.v b/cores/gameboy/sprite_sort.v index a3626ee..cb1660d 100644 --- a/cores/gameboy/sprite_sort.v +++ b/cores/gameboy/sprite_sort.v @@ -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]; diff --git a/cores/gameboy/sprites.v b/cores/gameboy/sprites.v index 776e25b..6eec371 100644 --- a/cores/gameboy/sprites.v +++ b/cores/gameboy/sprites.v @@ -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= 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 diff --git a/cores/gameboy/vram.qip b/cores/gameboy/vram.qip deleted file mode 100644 index c2a4bc5..0000000 --- a/cores/gameboy/vram.qip +++ /dev/null @@ -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"] diff --git a/cores/gameboy/vram.v b/cores/gameboy/vram.v deleted file mode 100644 index 1fe1f97..0000000 --- a/cores/gameboy/vram.v +++ /dev/null @@ -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 diff --git a/cores/gameboy/zpram.qip b/cores/gameboy/zpram.qip deleted file mode 100644 index a19af81..0000000 --- a/cores/gameboy/zpram.qip +++ /dev/null @@ -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"] diff --git a/cores/gameboy/zpram.v b/cores/gameboy/zpram.v deleted file mode 100644 index 5714f7b..0000000 --- a/cores/gameboy/zpram.v +++ /dev/null @@ -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