diff --git a/TSConf-lite.qsf b/TSConf-lite.qsf
index 7eb59b5..9e655fe 100644
--- a/TSConf-lite.qsf
+++ b/TSConf-lite.qsf
@@ -372,8 +372,10 @@ set_global_assignment -name VERILOG_FILE src/cpu/cache_addr.v
set_global_assignment -name VHDL_FILE src/rtc/CMOS.vhd
set_global_assignment -name VHDL_FILE src/rtc/mc146818a.vhd
set_global_assignment -name VHDL_FILE src/sound/soundrive.vhd
-set_global_assignment -name VHDL_FILE src/sound/turbosound.vhd
-set_global_assignment -name VHDL_FILE src/sound/ay8910.vhd
+set_global_assignment -name QIP_FILE src/sound/jt12/jt12.qip
+set_global_assignment -name SYSTEMVERILOG_FILE src/sound/ym2149.sv
+set_global_assignment -name SYSTEMVERILOG_FILE src/sound/ym2203.sv
+set_global_assignment -name SYSTEMVERILOG_FILE src/sound/turbosound.sv
set_global_assignment -name VERILOG_FILE src/sound/gs.v
set_global_assignment -name SYSTEMVERILOG_FILE src/sound/saa1099.sv
set_global_assignment -name VERILOG_FILE src/memory/dma.v
diff --git a/src/sound/ay8910.vhd b/src/sound/ay8910.vhd
deleted file mode 100644
index 1b925cd..0000000
--- a/src/sound/ay8910.vhd
+++ /dev/null
@@ -1,314 +0,0 @@
--------------------------------------------------------------------[07.09.2013]
--- AY3-8910
--------------------------------------------------------------------------------
--- V0.1 15.10.2011 первая версия
-
-library IEEE;
-use IEEE.std_logic_1164.all;
-use IEEE.numeric_std.all;
-
-entity ay8910 is
- port(
- CLK : in std_logic; -- System Clock
- ENA : in std_logic; -- PSG Clock
- RESET : in std_logic; -- Chip Reset (set all Registers to '0', active hi)
- BDIR : in std_logic; -- Bus Direction (0 - read , 1 - write)
- CS : in std_logic; -- Chip Select (active hi)
- BC : in std_logic; -- Bus control
- DI : in std_logic_vector(7 downto 0); -- Data In
- DO : out std_logic_vector(7 downto 0); -- Data Out
- OUT_A : out std_logic_vector(7 downto 0); -- PSG Output channel A
- OUT_B : out std_logic_vector(7 downto 0); -- PSG Output channel B
- OUT_C : out std_logic_vector(7 downto 0) -- PSG Output channel C
- );
-end ay8910;
-
-architecture rtl of ay8910 is
-
- signal ClockDiv : unsigned (3 downto 0); -- Divide ENA
-
--- AY Registers
- signal Period_A : std_logic_vector (11 downto 0); -- Channel A Tone Period (R1:R0)
- signal Period_B : std_logic_vector (11 downto 0); -- Channel B Tone Period (R3:R2)
- signal Period_C : std_logic_vector (11 downto 0); -- Channel C Tone Period (R5:R4)
- signal Period_N : std_logic_vector (4 downto 0); -- Noise Period (R6)
- signal Enable : std_logic_vector (7 downto 0); -- Enable (R7)
- signal Volume_A : std_logic_vector (4 downto 0); -- Channel A Amplitude (R10)
- signal Volume_B : std_logic_vector (4 downto 0); -- Channel B Amplitude (R11)
- signal Volume_C : std_logic_vector (4 downto 0); -- Channel C Amplitude (R12)
- signal Period_E : std_logic_vector (15 downto 0); -- Envelope Period (R14:R13)
- signal Shape : std_logic_vector (3 downto 0); -- Envelope Shape/Cycle (R15)
--- signal Port_A : std_logic_vector (7 downto 0); -- I/O Port A Data Store (R16)
--- signal Port_B : std_logic_vector (7 downto 0); -- I/O Port B Data Store (R17)
---
- signal Address : std_logic_vector (3 downto 0); -- Selected Register
-
- alias Continue : std_logic is Shape(3); -- Envelope Control
- alias Attack : std_logic is Shape(2);
- alias Alternate : std_logic is Shape(1);
- alias Hold : std_logic is Shape(0);
-
- signal Reset_Req : std_logic; -- Envelope Reset Required
- signal Reset_Ack : std_logic; -- Envelope Reset Acknoledge
- signal Volume_E : std_logic_vector (3 downto 0); -- Envelope Volume
-
- signal Freq_A : std_logic; -- Tone Generator A Output
- signal Freq_B : std_logic; -- Tone Generator B Output
- signal Freq_C : std_logic; -- Tone Generator C Output
- signal Freq_N : std_logic; -- Noise Generator Output
-
- function VolumeTable (value : std_logic_vector(3 downto 0)) return std_logic_vector is
- variable result : std_logic_vector (7 downto 0);
- begin
- case value is -- Volume Table
- when "1111" => result := "11111111";
- when "1110" => result := "10110100";
- when "1101" => result := "01111111";
- when "1100" => result := "01011010";
- when "1011" => result := "00111111";
- when "1010" => result := "00101101";
- when "1001" => result := "00011111";
- when "1000" => result := "00010110";
- when "0111" => result := "00001111";
- when "0110" => result := "00001011";
- when "0101" => result := "00000111";
- when "0100" => result := "00000101";
- when "0011" => result := "00000011";
- when "0010" => result := "00000010";
- when "0001" => result := "00000001";
- when "0000" => result := "00000000";
- when others => null;
- end case;
- return result;
- end VolumeTable;
-
-begin
-
--- Write to AY
-process (RESET , CLK)
-begin
- if RESET = '1' then
- Address <= "0000";
- Period_A <= "000000000000";
- Period_B <= "000000000000";
- Period_C <= "000000000000";
- Period_N <= "00000";
- Enable <= "00000000";
- Volume_A <= "00000";
- Volume_B <= "00000";
- Volume_C <= "00000";
- Period_E <= "0000000000000000";
- Shape <= "0000";
--- Port_A <= "00000000";
--- Port_B <= "00000000";
- Reset_Req <= '0';
- elsif rising_edge(CLK) then
- if CS = '1' and BDIR = '1' then
- if BC = '1' then
- Address <= DI (3 downto 0); -- Latch Address
- else
- case Address is -- Latch Registers
- when "0000" => Period_A (7 downto 0) <= DI;
- when "0001" => Period_A (11 downto 8) <= DI (3 downto 0);
- when "0010" => Period_B (7 downto 0) <= DI;
- when "0011" => Period_B (11 downto 8) <= DI (3 downto 0);
- when "0100" => Period_C (7 downto 0) <= DI;
- when "0101" => Period_C (11 downto 8) <= DI (3 downto 0);
- when "0110" => Period_N <= DI (4 downto 0);
- when "0111" => Enable <= DI;
- when "1000" => Volume_A <= DI (4 downto 0);
- when "1001" => Volume_B <= DI (4 downto 0);
- when "1010" => Volume_C <= DI (4 downto 0);
- when "1011" => Period_E (7 downto 0) <= DI;
- when "1100" => Period_E (15 downto 8) <= DI;
- when "1101" => Shape <= DI (3 downto 0);
- Reset_Req <= not Reset_Ack; -- Reset Envelope Generator
--- when "1110" => Port_A <= DI;
--- when "1111" => Port_B <= DI;
- when others => null;
- end case;
- end if;
- end if;
- end if;
-end process;
-
--- Read from AY
-DO <= Period_A (7 downto 0) when Address = "0000" and CS = '1' else
- "0000" & Period_A (11 downto 8) when Address = "0001" and CS = '1' else
- Period_B (7 downto 0) when Address = "0010" and CS = '1' else
- "0000" & Period_B (11 downto 8) when Address = "0011" and CS = '1' else
- Period_C (7 downto 0) when Address = "0100" and CS = '1' else
- "0000" & Period_C (11 downto 8) when Address = "0101" and CS = '1' else
- "000" & Period_N when Address = "0110" and CS = '1' else
- Enable when Address = "0111" and CS = '1' else
- "000" & Volume_A when Address = "1000" and CS = '1' else
- "000" & Volume_B when Address = "1001" and CS = '1' else
- "000" & Volume_C when Address = "1010" and CS = '1' else
- Period_E (7 downto 0) when Address = "1011" and CS = '1' else
- Period_E (15 downto 8) when Address = "1100" and CS = '1' else
- "0000" & Shape when Address = "1101" and CS = '1' else
- "11111111";
-
--- Divide ENA
-process (RESET, CLK)
-begin
- if RESET = '1' then
- ClockDiv <= "0000";
- elsif rising_edge(CLK) then
- if ENA = '1' then
- ClockDiv <= ClockDiv - 1;
- end if;
- end if;
-end process;
-
--- Tone Generator
-process (RESET, CLK)
- variable Counter_A : unsigned (11 downto 0);
- variable Counter_B : unsigned (11 downto 0);
- variable Counter_C : unsigned (11 downto 0);
-begin
- if RESET = '1' then
- Counter_A := "000000000000";
- Counter_B := "000000000000";
- Counter_C := "000000000000";
- Freq_A <= '0';
- Freq_B <= '0';
- Freq_C <= '0';
- elsif rising_edge(CLK) then
- if ClockDiv(2 downto 0) = "000" and ENA = '1' then
-
- -- Channel A Counter
- if (Counter_A /= X"000") then
- Counter_A := Counter_A - 1;
- elsif (Period_A /= X"000") then
- Counter_A := unsigned(Period_A) - 1;
- end if;
- if (Counter_A = X"000") then
- Freq_A <= not Freq_A;
- end if;
-
- -- Channel B Counter
- if (Counter_B /= X"000") then
- Counter_B := Counter_B - 1;
- elsif (Period_B /= X"000") then
- Counter_B := unsigned(Period_B) - 1;
- end if;
- if (Counter_B = X"000") then
- Freq_B <= not Freq_B;
- end if;
-
- -- Channel C Counter
- if (Counter_C /= X"000") then
- Counter_C := Counter_C - 1;
- elsif (Period_C /= X"000") then
- Counter_C := unsigned(Period_C) - 1;
- end if;
- if (Counter_C = X"000") then
- Freq_C <= not Freq_C;
- end if;
-
- end if;
- end if;
-end process;
-
--- Noise Generator
-process (RESET, CLK)
- variable NoiseShift : unsigned (16 downto 0);
- variable Counter_N : unsigned (4 downto 0);
-begin
- if RESET = '1' then
- Counter_N := "00000";
- NoiseShift := "00000000000000001";
- elsif rising_edge(CLK) then
- if ClockDiv(2 downto 0) = "000" and ENA = '1' then
- if (Counter_N /= "00000") then
- Counter_N := Counter_N - 1;
- elsif (Period_N /= "00000") then
- Counter_N := unsigned(Period_N) - 1;
- end if;
- if Counter_N = "00000" then
- NoiseShift := (NoiseShift(0) xor NoiseShift(2)) & NoiseShift(16 downto 1);
- end if;
- Freq_N <= NoiseShift(0);
- end if;
- end if;
-end process;
-
--- Envelope Generator
-process (RESET , CLK)
- variable EnvCounter : unsigned(15 downto 0);
- variable EnvWave : unsigned(4 downto 0);
-begin
- if RESET = '1' then
- EnvCounter := "0000000000000000";
- EnvWave := "11111";
- Volume_E <= "0000";
- Reset_Ack <= '0';
- elsif rising_edge(CLK) then
- if ClockDiv = "0000" and ENA = '1' then
- -- Envelope Period Counter
- if (EnvCounter /= X"0000" and Reset_Req = Reset_Ack) then
- EnvCounter := EnvCounter - 1;
- elsif (Period_E /= X"0000") then
- EnvCounter := unsigned(Period_E) - 1;
- end if;
-
- -- Envelope Phase Counter
- if (Reset_Req /= Reset_Ack) then
- EnvWave := (others => '1');
- elsif (EnvCounter = X"0000" and (EnvWave(4) = '1' or (Hold = '0' and Continue = '1'))) then
- EnvWave := EnvWave - 1;
- end if;
-
- -- Envelope Amplitude Counter
- for I in 3 downto 0 loop
- if (EnvWave(4) = '0' and Continue = '0') then
- Volume_E(I) <= '0';
- elsif (EnvWave(4) = '1' or (Alternate xor Hold) = '0') then
- Volume_E(I) <= EnvWave(I) xor Attack;
- else
- Volume_E(I) <= EnvWave(I) xor Attack xor '1';
- end if;
- end loop;
- Reset_Ack <= Reset_Req;
- end if;
- end if;
-end process;
-
--- Mixer
-process (RESET , CLK)
-begin
- if RESET = '1' then
- OUT_A <= "00000000";
- OUT_B <= "00000000";
- OUT_C <= "00000000";
- elsif rising_edge(CLK) then
- if ENA = '1' then
- if (((Enable(0) or Freq_A) and (Enable(3) or Freq_N)) = '0') then
- OUT_A <= "00000000";
- elsif (Volume_A(4) = '0') then
- OUT_A <= VolumeTable(Volume_A(3 downto 0));
- else
- OUT_A <= VolumeTable(Volume_E);
- end if;
-
- if (((Enable(1) or Freq_B) and (Enable(4) or Freq_N)) = '0') then
- OUT_B <= "00000000";
- elsif (Volume_B(4) = '0') then
- OUT_B <= VolumeTable(Volume_B(3 downto 0));
- else
- OUT_B <= VolumeTable(Volume_E);
- end if;
-
- if (((Enable(2) or Freq_C) and (Enable(5) or Freq_N)) = '0') then
- OUT_C <= "00000000";
- elsif (Volume_C(4) = '0') then
- OUT_C <= VolumeTable(Volume_C(3 downto 0));
- else
- OUT_C <= VolumeTable(Volume_E);
- end if;
- end if;
- end if;
-end process;
-
-end rtl;
\ No newline at end of file
diff --git a/src/sound/jt12/jt12.qip b/src/sound/jt12/jt12.qip
new file mode 100644
index 0000000..6225f7d
--- /dev/null
+++ b/src/sound/jt12/jt12.qip
@@ -0,0 +1,21 @@
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_acc.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_clksync.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_eg.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_exprom.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_kon.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_lfo.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_limitamp.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_mmr.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_mod.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_op.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_opram.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_pg.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_phrom.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_reg.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_sh.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_sh_rst.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_sh24.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_sumch.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_syn.v
+set_global_assignment -name VERILOG_FILE src/sound/jt12/jt12_timers.v
diff --git a/src/sound/jt12/jt12.v b/src/sound/jt12/jt12.v
new file mode 100644
index 0000000..caa242b
--- /dev/null
+++ b/src/sound/jt12/jt12.v
@@ -0,0 +1,116 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Based on information provided by
+ Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+ Nemesis reports, based on measurements
+ Comparisons with real hardware lent by Mikes (Va de retro)
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 1-4-2017
+
+ Use tab = 4 spaces
+
+*/
+
+// syn_clk "1.285714"
+
+module jt12(
+ input rst,
+ // CPU interface
+ input cpu_clk,
+ input [7:0] cpu_din,
+ input [1:0] cpu_addr,
+ input cpu_cs_n,
+ input cpu_wr_n,
+ input cpu_limiter_en,
+
+ output [7:0] cpu_dout,
+ output cpu_irq_n,
+ // Synthesizer clock domain
+ input syn_clk,
+ // combined output
+ output signed [11:0] syn_snd_right,
+ output signed [11:0] syn_snd_left,
+ output syn_snd_sample,
+ // multiplexed output
+ output signed [8:0] syn_mux_right,
+ output signed [8:0] syn_mux_left,
+ output syn_mux_sample
+);
+
+// Timers
+wire syn_flag_A, syn_flag_B;
+
+
+wire [7:0] syn_din;
+wire [1:0] syn_addr;
+wire syn_write, syn_limiter_en, syn_busy;
+wire syn_rst, syn_irq_n;
+
+jt12_clksync u_clksync(
+ .rst ( rst ),
+ .cpu_clk ( cpu_clk ),
+ .syn_clk ( syn_clk ),
+
+ // CPU interface
+ .cpu_din ( cpu_din ),
+ .cpu_addr ( cpu_addr ),
+ .cpu_dout ( cpu_dout ),
+ .cpu_cs_n ( cpu_cs_n ),
+ .cpu_wr_n ( cpu_wr_n ),
+ .cpu_irq_n ( cpu_irq_n ),
+ .cpu_limiter_en( cpu_limiter_en ),
+
+ // Synthesizer interface
+ .syn_rst ( syn_rst ),
+ .syn_write ( syn_write ),
+ .syn_din ( syn_din ),
+ .syn_addr ( syn_addr ),
+ .syn_limiter_en( syn_limiter_en ),
+
+ .syn_busy ( syn_busy ),
+ .syn_flag_A ( syn_flag_A),
+ .syn_flag_B ( syn_flag_B),
+ .syn_irq_n ( syn_irq_n )
+);
+
+jt12_syn u_syn(
+ .rst ( syn_rst ),
+ .clk ( syn_clk ),
+ .din ( syn_din ),
+ .addr ( syn_addr ),
+ .busy ( syn_busy ),
+ .flag_A ( syn_flag_A),
+ .flag_B ( syn_flag_B),
+ .write ( syn_write ),
+ .limiter_en ( syn_limiter_en),
+ .irq_n ( syn_irq_n ),
+ // Mixed sound
+ .snd_right ( syn_snd_right ),
+ .snd_left ( syn_snd_left ),
+ .snd_sample ( syn_snd_sample),
+ // Multiplexed sound
+ .mux_right ( syn_mux_right ),
+ .mux_left ( syn_mux_left ),
+ .mux_sample ( syn_mux_sample)
+);
+
+endmodule
diff --git a/src/sound/jt12/jt12_acc.v b/src/sound/jt12/jt12_acc.v
new file mode 100644
index 0000000..e2e7047
--- /dev/null
+++ b/src/sound/jt12/jt12_acc.v
@@ -0,0 +1,178 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+ Each channel can use the full range of the DAC as they do not
+ get summed in the real chip.
+
+*/
+
+
+
+`timescale 1ns / 1ps
+
+module jt12_acc
+(
+ input rst,
+ input clk,
+ input signed [8:0] op_result,
+ input [ 1:0] rl,
+ input limiter_en, // enables the limiter on
+ // the accumulator to prevent overfow.
+ // I reckon that:
+ // YM2612 had a limiter
+ // YM3438 did not
+ // note that the order changes to deal
+ // with the operator pipeline delay
+ input s1_enters,
+ input s2_enters,
+ input s3_enters,
+ input s4_enters,
+ input ch6op,
+ input [2:0] alg,
+ input pcm_en, // only enabled for channel 6
+ input [8:0] pcm,
+ // combined output
+ output reg signed [11:0] left,
+ output reg signed [11:0] right,
+ output sample,
+ // multiplexed output
+ output reg signed [8:0] mux_left,
+ output reg signed [8:0] mux_right,
+ output reg mux_sample
+);
+
+reg signed [11:0] pre_left, pre_right;
+wire signed [8:0] total;
+reg sum_en;
+
+always @(*) begin
+ case ( alg )
+ default: sum_en <= s4_enters;
+ 3'd4: sum_en <= s2_enters | s4_enters;
+ 3'd5,3'd6: sum_en <= ~s1_enters;
+ 3'd7: sum_en <= 1'b1;
+ endcase
+end
+
+reg sum_all;
+assign sample = ~sum_all;
+
+wire [8:0] buffer;
+reg [8:0] buffer2;
+reg [8:0] buf_mux;
+reg [1:0] rl2,rl3;
+reg last_s1;
+reg [1:0] mux_cnt;
+
+wire signed [11:0] total_signext = { {3{total[8]}}, total };
+
+always @(posedge clk) begin : mux_dac_input
+ buffer2 <= buffer;
+ rl2 <= rl;
+ last_s1 <= s1_enters;
+ mux_cnt <= last_s1 && s3_enters ? 2'd01 : mux_cnt + 1'b1;
+ if( mux_cnt==2'b11 ) begin
+ if( s1_enters || s3_enters ) begin
+ buf_mux <= buffer2;
+ rl3 <= rl2;
+ end
+ else begin
+ buf_mux <= buffer;
+ rl3 <= rl;
+ end
+ end
+ if( mux_cnt==2'd0 ) begin
+ mux_left <= rl3[1] ? buf_mux : 9'd0;
+ mux_right<= rl3[0] ? buf_mux : 9'd0;
+ mux_sample <= 1'b1;
+ end
+ else
+ mux_sample <= 1'b0;
+end
+
+always @(posedge clk) begin
+ if( rst ) begin
+ sum_all <= 1'b0;
+ end
+ else begin
+ if( s3_enters ) begin
+ sum_all <= 1'b1;
+ if( !sum_all ) begin
+ pre_right <= rl[0] ? total_signext : 12'd0;
+ pre_left <= rl[1] ? total_signext : 12'd0;
+ end
+ else begin
+ pre_right <= pre_right + (rl[0] ? total_signext : 12'd0);
+ pre_left <= pre_left + (rl[1] ? total_signext : 12'd0);
+ end
+ end
+ if( s2_enters ) begin
+ sum_all <= 1'b0;
+ left <= pre_left;
+ right <= pre_right;
+ `ifdef DUMPSOUND
+ $strobe("%d\t%d", left, right);
+ `endif
+ end
+ end
+end
+
+reg signed [8:0] next, opsum, prev;
+wire signed [9:0] opsum10 = next+total;
+
+always @(*) begin
+ next <= sum_en ? op_result : 9'd0;
+ if( s3_enters )
+ opsum <= (ch6op && pcm_en) ? { ~pcm[8], pcm[7:0] } : next;
+ else begin
+ if( sum_en && !(ch6op && pcm_en) )
+ if( limiter_en ) begin
+ if( opsum10[9]==opsum10[8] )
+ opsum <= opsum10[8:0];
+ else begin
+ opsum <= opsum10[9] ? 9'h100 : 9'h0ff;
+ end
+ end
+ else begin
+ // MSB is discarded according to
+ // YM3438 application notes
+ opsum <= opsum10[8:0];
+ end
+ else
+ opsum <= total;
+ end
+end
+
+jt12_sh #(.width(9),.stages(6)) u_acc(
+ .clk ( clk ),
+ .din ( opsum ),
+ .drop ( total )
+);
+
+jt12_sh #(.width(9),.stages(6)) u_buffer(
+ .clk ( clk ),
+ .din ( s3_enters ? total : buffer ),
+ .drop ( buffer )
+);
+
+endmodule
diff --git a/src/sound/jt12/jt12_clksync.v b/src/sound/jt12/jt12_clksync.v
new file mode 100644
index 0000000..3ad3a7c
--- /dev/null
+++ b/src/sound/jt12/jt12_clksync.v
@@ -0,0 +1,106 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 14-2-2017
+
+*/
+
+module jt12_clksync(
+ input rst,
+ input cpu_clk,
+ input syn_clk,
+
+ // CPU interface
+ input [7:0] cpu_din,
+ input [1:0] cpu_addr,
+ output[7:0] cpu_dout,
+ input cpu_cs_n,
+ input cpu_wr_n,
+ output cpu_irq_n,
+ input cpu_limiter_en,
+
+ // Synthesizer interface
+ output reg [7:0] syn_din,
+ output reg [1:0] syn_addr,
+ output reg syn_rst,
+ output reg syn_write,
+ output syn_limiter_en,
+
+ input syn_busy,
+ input syn_flag_A,
+ input syn_flag_B,
+ input syn_irq_n
+);
+
+// reset generation
+reg rst_aux;
+
+assign syn_limiter_en = cpu_limiter_en;
+
+always @(negedge syn_clk or posedge rst)
+ if( rst ) begin
+ syn_rst <= 1'b1;
+ rst_aux <= 1'b1;
+ end
+ else begin
+ syn_rst <= rst_aux;
+ rst_aux <= 1'b0;
+ end
+
+reg cpu_busy;
+wire cpu_flag_B, cpu_flag_A;
+
+assign cpu_dout = cpu_cs_n ? 8'hFF : { cpu_busy, 5'h0, cpu_flag_B, cpu_flag_A };
+
+wire write_raw = !cpu_cs_n && !cpu_wr_n;
+
+reg [1:0]busy_sh;
+always @(posedge cpu_clk) begin
+ busy_sh <= { busy_sh[0], syn_busy };
+end
+
+jt12_sh #(.width(3),.stages(2) ) u_syn2cpu(
+ .clk ( cpu_clk ),
+ .din ( { syn_flag_B, syn_flag_A, syn_irq_n } ),
+ .drop ( { cpu_flag_B, cpu_flag_A, cpu_irq_n } )
+);
+
+always @(posedge cpu_clk) begin
+ reg old_write;
+
+ old_write <= write_raw;
+
+ if( rst ) begin
+ cpu_busy <= 1'b0;
+ end
+ else begin
+ if( ~old_write & write_raw ) begin
+ cpu_busy <= 1;
+ syn_write <= ~syn_write;
+ syn_addr <= cpu_addr;
+ syn_din <= cpu_din;
+ end
+
+ if(cpu_busy && busy_sh==2'b10) cpu_busy <= 0;
+ end
+end
+
+endmodule
diff --git a/src/sound/jt12/jt12_eg.v b/src/sound/jt12/jt12_eg.v
new file mode 100644
index 0000000..bccf571
--- /dev/null
+++ b/src/sound/jt12/jt12_eg.v
@@ -0,0 +1,621 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 14-2-2017
+
+ The non SSG-EG section is basically that of JT51's jt51_envelope.v
+ adapted to 6 channels, instead of the eight.
+
+ The SSG-EG is based on the works of Nemesis, published on
+ http://gendev.spritesmind.net/forum/search.php?st=0&sk=t&sd=d&sr=posts&keywords=SSG-EG&t=386&sf=msgonly&ch=-1&start=15
+
+ This module tries to replicate exactly the original YM2612 waveforms.
+ Nonetheless, the verilog code is not a replica of the original circuit
+ as there is no reverse engineering description of it.
+
+ Questions:
+ -I latch the SSG enable bit on keyon. I have to do this in order to ignore the inversion bit before the keyon
+ -If SSG enable is set to 0 by software before issueing a keyoff it will be ignored
+ -However, the other SSG bits will have an effect if changed in the meantime between keyon and keyoff
+ -Was YM2612 like that?
+
+ */
+
+`timescale 1ns / 1ps
+
+/*
+
+ tab size 4
+
+*/
+
+module jt12_eg (
+ `ifdef TEST_SUPPORT
+ input test_eg,
+ `endif
+ input rst,
+ input clk,
+ input zero,
+ input eg_stop,
+ // envelope configuration
+ input [4:0] keycode_III,
+ input [4:0] arate_II, // attack rate
+ input [4:0] rate1_II, // decay rate
+ input [4:0] rate2_II, // sustain rate
+ input [3:0] rrate_II, // release rate
+ input [3:0] d1l, // sustain level
+ input [1:0] ks_III, // key scale
+ // SSG operation
+ input ssg_en_II,
+ input [2:0] ssg_eg_II,
+ // envelope operation
+ input keyon_II,
+ // envelope number
+ input [6:0] am,
+ input [6:0] tl_VII,
+ input [1:0] ams_VII,
+ input amsen_VII,
+
+ output reg [9:0] eg_IX,
+ output reg pg_rst_III
+);
+
+ // eg[9:6] -> direct attenuation (divide by 2)
+ // eg[5:0] -> mantisa attenuation (uses LUT)
+ // 1 LSB of eg is -0.09257 dB
+
+wire ssg_inv_II = ssg_eg_II[2] & ssg_en_II;
+wire ssg_alt_II = ssg_eg_II[1] & ssg_en_II;
+wire ssg_hold_II = ssg_eg_II[0] & ssg_en_II;
+
+parameter ATTACK=3'd0, DECAY1=3'd1, DECAY2=3'd2, RELEASE=3'd7, HOLD=3'd3;
+
+reg [4:0] d1level_II;
+reg [9:0] eg_III, eg_IV, eg_V;
+wire [9:0] eg_II;
+
+reg step_V;
+reg sum_up;
+reg [5:0] rate_V;
+
+// remember: { log_msb, pow_addr } <= log_val[11:0] + { tl, 5'd0 } + { eg, 2'd0 };
+
+reg [1:0] eg_cnt_base;
+reg [14:0] eg_cnt;
+
+always @(posedge clk) begin : envelope_counter
+ if( rst ) begin
+ eg_cnt_base <= 2'd0;
+ eg_cnt <=15'd0;
+ end
+ else begin
+ if( zero ) begin
+ // envelope counter increases every 3 output samples,
+ // there is one sample every 32 clock ticks
+ if( eg_cnt_base == 2'd2 ) begin
+ eg_cnt <= eg_cnt + 1'b1;
+ eg_cnt_base <= 2'd0;
+ end
+ else eg_cnt_base <= eg_cnt_base + 1'b1;
+ end
+ end
+end
+
+wire cnt_out; // = all_cnt_last[3*31-1:3*30];
+
+
+reg [7:0] step_idx;
+reg [2:0] state_III, state_IV, state_V, state_VI, state_VII, state_VIII;
+wire [2:0] state_II;
+
+
+wire ar_off_VI;
+reg ar_off_III;
+
+// Register Cycle I
+
+always @(posedge clk) begin
+ if( d1l == 4'd15 )
+ d1level_II <= 5'h1f; // 93dB
+ else
+ d1level_II <= d1l;
+end
+
+// Register Cycle II
+wire ssg_invertion_II, ssg_invertion_VIII;
+reg ssg_invertion_III;
+reg [4:0] cfg_III;
+wire ssg_pg_rst = eg_II>=10'h200 && ssg_en_II &&
+ !( ssg_alt_II || ssg_hold_II );
+wire ssg_en_out;
+wire keyon_last_II;
+reg ssg_en_in_II;
+
+always @(*) begin
+ if( state_II==RELEASE )
+ ssg_en_in_II <= 1'b0;
+ else
+ ssg_en_in_II <= keyon_II ? ssg_en_II : ssg_en_out;
+end
+
+wire ar_off_II = arate_II == 5'h1f;
+
+wire keyon_now_II = !keyon_last_II && keyon_II;
+wire keyoff_now_II = keyon_last_II && !keyon_II;
+
+always @(posedge clk) begin
+ // ar_off_III <= arate_II == 5'h1f;
+ // trigger release
+ if( keyoff_now_II ) begin
+ cfg_III <= { rrate_II, 1'b1 };
+ state_III <= RELEASE;
+ pg_rst_III <= 1'b0;
+ ar_off_III <= 1'b0;
+ end
+ else begin
+ // trigger 1st decay
+ if( keyon_now_II ) begin
+ cfg_III <= arate_II;
+ state_III <= ATTACK;
+ pg_rst_III <= 1'b1;
+ ar_off_III <= ar_off_II;
+ end
+ else begin : sel_rate
+ pg_rst_III <= (eg_II==10'h3FF) ||ssg_pg_rst;
+ if( (state_II==DECAY1 ||state_II==DECAY2) && ssg_en_II && eg_II >= 10'h200 ) begin
+ ssg_invertion_III <= ssg_alt_II ^ ssg_invertion_II;
+ if( ssg_hold_II ) begin
+ cfg_III <= 5'd0;
+ state_III <= HOLD; // repeats!
+ ar_off_III <= 1'b0;
+ end
+ else begin
+ cfg_III <= rate2_II;
+ state_III <= ATTACK; // repeats!
+ ar_off_III <= 1'b1;
+ end
+ end
+ else begin
+ ssg_invertion_III <= state_II==RELEASE ? 1'b0 : ssg_invertion_II;
+ case ( state_II )
+ ATTACK: begin
+ if( eg_II==10'd0 ) begin
+ state_III <= DECAY1;
+ cfg_III <= rate1_II;
+ end
+ else begin
+ state_III <= state_II; // attack
+ cfg_III <= arate_II;
+ end
+ ar_off_III <= 1'b0;
+ end
+ DECAY1: begin
+ if( eg_II[9:5] >= d1level_II ) begin
+ cfg_III <= rate2_II;
+ state_III <= DECAY2;
+ end
+ else begin
+ cfg_III <= rate1_II;
+ state_III <= state_II; // decay1
+ end
+ ar_off_III <= 1'b0;
+ end
+ DECAY2:
+ begin
+ cfg_III <= rate2_II;
+ state_III <= state_II; // decay2
+ ar_off_III <= 1'b0;
+ end
+ RELEASE: begin
+ cfg_III <= { rrate_II, 1'b1 };
+ state_III <= state_II; // release
+ ar_off_III <= 1'b0;
+ end
+ HOLD: begin
+ cfg_III <= 5'd0;
+ state_III <= HOLD; // repeats!
+ ar_off_III <= 1'b0;
+ end
+ endcase
+ end
+ end
+ end
+
+ eg_III <= eg_II;
+end
+
+///////////////////////////////////////////////////////////////////
+// Register Cycle III
+reg [6:0] pre_rate_III;
+reg [5:0] rate_IV;
+
+always @(*) begin : pre_rate_calc
+ if( cfg_III == 5'd0 )
+ pre_rate_III <= 6'd0;
+ else
+ case( ks_III )
+ 2'd3: pre_rate_III <= { cfg_III, 1'b0 } + keycode_III;
+ 2'd2: pre_rate_III <= { cfg_III, 1'b0 } + { 1'b0, keycode_III[4:1] };
+ 2'd1: pre_rate_III <= { cfg_III, 1'b0 } + { 2'b0, keycode_III[4:2] };
+ 2'd0: pre_rate_III <= { cfg_III, 1'b0 } + { 3'b0, keycode_III[4:3] };
+ endcase
+end
+
+always @(posedge clk) begin
+ if( rst ) begin
+ state_IV <= RELEASE;
+ eg_IV <= 10'h3ff;
+ rate_IV <= 5'h1F;
+ end
+ else begin
+ state_IV <= state_III;
+ eg_IV <= eg_III;
+ rate_IV <= pre_rate_III[6] ? 6'd63 : pre_rate_III[5:0];
+ end
+end
+///////////////////////////////////////////////////////////////////
+// Register Cycle IV
+reg [2:0] cnt_V;
+
+always @(posedge clk) begin
+ if( rst ) begin
+ state_V <= RELEASE;
+ rate_V <= 5'h1f;
+ eg_V <= 10'h3ff;
+ //cnt_V<= 3'd0;
+ end
+ else begin
+ state_V <= state_IV;
+ rate_V <= rate_IV;
+ eg_V <= eg_IV;
+ if( state_IV == ATTACK )
+ case( rate_IV[5:2] )
+ 4'h0: cnt_V <= eg_cnt[13:11];
+ 4'h1: cnt_V <= eg_cnt[12:10];
+ 4'h2: cnt_V <= eg_cnt[11: 9];
+ 4'h3: cnt_V <= eg_cnt[10: 8];
+ 4'h4: cnt_V <= eg_cnt[ 9: 7];
+ 4'h5: cnt_V <= eg_cnt[ 8: 6];
+ 4'h6: cnt_V <= eg_cnt[ 7: 5];
+ 4'h7: cnt_V <= eg_cnt[ 6: 4];
+ 4'h8: cnt_V <= eg_cnt[ 5: 3];
+ 4'h9: cnt_V <= eg_cnt[ 4: 2];
+ 4'ha: cnt_V <= eg_cnt[ 3: 1];
+ default: cnt_V <= eg_cnt[ 2: 0];
+ endcase
+ else
+ case( rate_IV[5:2] )
+ 4'h0: cnt_V <= eg_cnt[14:12];
+ 4'h1: cnt_V <= eg_cnt[13:11];
+ 4'h2: cnt_V <= eg_cnt[12:10];
+ 4'h3: cnt_V <= eg_cnt[11: 9];
+ 4'h4: cnt_V <= eg_cnt[10: 8];
+ 4'h5: cnt_V <= eg_cnt[ 9: 7];
+ 4'h6: cnt_V <= eg_cnt[ 8: 6];
+ 4'h7: cnt_V <= eg_cnt[ 7: 5];
+ 4'h8: cnt_V <= eg_cnt[ 6: 4];
+ 4'h9: cnt_V <= eg_cnt[ 5: 3];
+ 4'ha: cnt_V <= eg_cnt[ 4: 2];
+ 4'hb: cnt_V <= eg_cnt[ 3: 1];
+ default: cnt_V <= eg_cnt[ 2: 0];
+ endcase
+ end
+end
+//////////////////////////////////////////////////////////////
+// Register Cycle V
+always @(*) begin : rate_step
+ if( rate_V[5:4]==2'b11 ) begin // 0 means 1x, 1 means 2x
+ if( rate_V[5:2]==4'hf && state_V == ATTACK)
+ step_idx <= 8'b11111111; // Maximum attack speed, rates 60&61
+ else
+ case( rate_V[1:0] )
+ 2'd0: step_idx <= 8'b00000000;
+ 2'd1: step_idx <= 8'b10001000; // 2
+ 2'd2: step_idx <= 8'b10101010; // 4
+ 2'd3: step_idx <= 8'b11101110; // 6
+ endcase
+ end
+ else begin
+ if( rate_V[5:2]==4'd0 && state_V != ATTACK)
+ step_idx <= 8'b11111110; // limit slowest decay rate_IV
+ else
+ case( rate_V[1:0] )
+ 2'd0: step_idx <= 8'b10101010; // 4
+ 2'd1: step_idx <= 8'b11101010; // 5
+ 2'd2: step_idx <= 8'b11101110; // 6
+ 2'd3: step_idx <= 8'b11111110; // 7
+ endcase
+ end
+ // a rate_IV of zero keeps the level still
+ step_V <= rate_V[5:1]==5'd0 ? 1'b0 : step_idx[ cnt_V ];
+end
+
+reg [5:1] rate_VI;
+reg [9:0] eg_VI;
+reg step_VI;
+
+always @(posedge clk) begin
+ if( rst ) begin
+ state_VI <= RELEASE;
+ rate_VI <= 5'd1;
+ eg_VI <= 10'h3ff;
+ sum_up <= 1'b0;
+ step_VI <= 1'b0;
+ end
+ else begin
+ state_VI <= state_V;
+ rate_VI <= rate_V[5:1];
+ eg_VI <= eg_V;
+ sum_up <= cnt_V[0] != cnt_out;
+ step_VI <= step_V;
+ end
+end
+//////////////////////////////////////////////////////////////
+// Register cycle VI
+reg [3:0] preatt_VI;
+reg [5:0] att_VI;
+wire ssg_en_VI;
+reg [9:0] eg_VII, eg_stopped_VII;
+reg [10:0] egatt_VI;
+
+always @(*) begin
+ case( rate_VI[5:2] )
+ 4'b1100: preatt_VI <= { step_VI, ~step_VI }; // 12
+ 4'b1101: preatt_VI <= { step_VI, ~step_VI, 1'b0 }; // 13
+ 4'b1110: preatt_VI <= { step_VI, ~step_VI, 2'b0 }; // 14
+ 4'b1111: preatt_VI <= 4'd8;// 15
+ default: preatt_VI <= { step_VI, 1'b0 };
+ endcase
+ att_VI <= ssg_en_VI ? { preatt_VI, 2'd0 } : { 2'd0, preatt_VI };
+ egatt_VI <= att_VI + eg_VI;
+ eg_stopped_VII <= eg_VI;
+end
+
+reg [8:0] ar_sum0;
+reg [9:0] ar_result, ar_sum;
+
+always @(*) begin : ar_calculation
+ casex( rate_VI[5:2] )
+ default: ar_sum0 <= eg_VI[9:4] + 1'd1;
+ 4'b1100: ar_sum0 <= eg_VI[9:4] + 1'd1;
+ 4'b1101: ar_sum0 <= eg_VI[9:3] + 1'd1;
+ 4'b111x: ar_sum0 <= eg_VI[9:2] + 1'd1;
+ endcase
+ if( rate_VI[5:4] == 2'b11 )
+ ar_sum <= step_VI ? { ar_sum0, 1'b0 } : { 1'b0, ar_sum0 };
+ else
+ ar_sum <= step_VI ? { 1'b0, ar_sum0 } : 10'd0;
+ ar_result <= ar_sum 2'b0 ? {10{1'b1}} : sum_eg_tl[9:0];
+ state_VIII <= state_VII;
+ end
+end
+
+//////////////////////////////////////////////////////////////
+// Register cycle VIII
+wire ssg_inv_VIII, ssg_en_VIII;
+//reg [9:0] eg_IX;
+always @(posedge clk) begin
+ if( rst )
+ eg_IX <= 10'h3ff;
+ else begin
+ if( ssg_en_VIII && (ssg_invertion_VIII ^^ ssg_inv_VIII) )
+ eg_IX <= eg_internal_VIII>=10'h200 ? 10'h0 : (10'h200 - eg_internal_VIII);
+ else
+ eg_IX <= eg_internal_VIII;
+ end
+end
+
+//////////////////////////////////////////////////////////////
+// Register cycle IX-XII
+/*
+jt12_sh #(.width(10), .stages(12-8)) u_padding(
+ .clk ( clk ),
+ .din ( eg_IX ),
+ .drop ( eg_XII )
+);
+*/
+//////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////
+// Shift registers
+
+jt12_sh24 #( .width(1) ) u_ssgen(
+ .clk ( clk ),
+ .din ( ssg_en_in_II ),
+ .st4 ( ssg_en_VI ),
+ .st6 ( ssg_en_VIII ), // note that din is *_II
+ .st24 ( ssg_en_out )
+);
+
+jt12_sh #( .width(1), .stages(6) ) u_ssgattsh(
+ .clk ( clk ),
+ .din ( ssg_inv_II ),
+ .drop ( ssg_inv_VIII )
+);
+
+jt12_sh #( .width(1), .stages(3) ) u_aroffsh(
+ .clk ( clk ),
+ .din ( ar_off_III),
+ .drop ( ar_off_VI )
+);
+
+jt12_sh #( .width(1), .stages(5) ) u_ssg1sh(
+ .clk ( clk ),
+ .din ( ssg_invertion_III ),
+ .drop ( ssg_invertion_VIII )
+);
+
+
+jt12_sh #( .width(1), .stages(18) ) u_ssg2sh(
+ .clk ( clk ),
+ .din ( ssg_invertion_VIII ),
+ .drop ( ssg_invertion_II )
+);
+
+
+/* Had 27 stages in JT51, for 32 operators
+ Has 19 stages here, for 24 operators
+ plus 1 extra stage. 20 stages does not work well.
+ Maybe JT51 should be 26!! */
+jt12_sh/*_rst*/ #( .width(10), .stages(19)/*, .rstval(1'b1)*/ ) u_egsh(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( eg_stop ? eg_stopped_VII : eg_VII ),
+ .drop ( eg_II )
+);
+
+/* Had 32 stages in JT51, for 32 operators
+ Has 24 stages here, for 24 operators */
+jt12_sh/*_rst*/ #( .width(1), .stages(24) ) u_cntsh(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( cnt_V[0] ),
+ .drop ( cnt_out )
+);
+
+jt12_sh_rst #( .width(1), .stages(24) ) u_konsh(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( keyon_II ),
+ .drop ( keyon_last_II )
+);
+
+/* Had 31 stages in JT51, for 32 operators
+ Has 23 stages here, for 24 operators */
+jt12_sh/*_rst*/ #( .width(3), .stages(18)/*, .rstval(1'b1)*/ ) u_statesh(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( state_VIII),
+ .drop ( state_II )
+);
+
+`ifdef SIMULATION
+reg [4:0] sep24_cnt;
+
+wire [2:0] state_ch0s1, state_ch1s1, state_ch2s1, state_ch3s1,
+ state_ch4s1, state_ch5s1, state_ch0s2, state_ch1s2,
+ state_ch2s2, state_ch3s2, state_ch4s2, state_ch5s2,
+ state_ch0s3, state_ch1s3, state_ch2s3, state_ch3s3,
+ state_ch4s3, state_ch5s3, state_ch0s4, state_ch1s4,
+ state_ch2s4, state_ch3s4, state_ch4s4, state_ch5s4;
+
+
+always @(posedge clk)
+ sep24_cnt <= !zero ? sep24_cnt+1'b1 : 5'd0;
+
+sep24 #( .width(3), .pos0(0) ) stsep
+(
+ .clk ( clk ),
+ .mixed ( state_II ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (state_ch0s1),
+ .ch1s1 (state_ch1s1),
+ .ch2s1 (state_ch2s1),
+ .ch3s1 (state_ch3s1),
+ .ch4s1 (state_ch4s1),
+ .ch5s1 (state_ch5s1),
+
+ .ch0s2 (state_ch0s2),
+ .ch1s2 (state_ch1s2),
+ .ch2s2 (state_ch2s2),
+ .ch3s2 (state_ch3s2),
+ .ch4s2 (state_ch4s2),
+ .ch5s2 (state_ch5s2),
+
+ .ch0s3 (state_ch0s3),
+ .ch1s3 (state_ch1s3),
+ .ch2s3 (state_ch2s3),
+ .ch3s3 (state_ch3s3),
+ .ch4s3 (state_ch4s3),
+ .ch5s3 (state_ch5s3),
+
+ .ch0s4 (state_ch0s4),
+ .ch1s4 (state_ch1s4),
+ .ch2s4 (state_ch2s4),
+ .ch3s4 (state_ch3s4),
+ .ch4s4 (state_ch4s4),
+ .ch5s4 (state_ch5s4)
+);
+
+`endif
+
+endmodule
+
diff --git a/src/sound/jt12/jt12_exprom.v b/src/sound/jt12/jt12_exprom.v
new file mode 100644
index 0000000..a564725
--- /dev/null
+++ b/src/sound/jt12/jt12_exprom.v
@@ -0,0 +1,76 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+// altera message_off 10030
+
+module jt12_exprom
+(
+ input [4:0] addr,
+ input clk,
+ output reg [44:0] exp
+);
+
+ reg [44:0] explut_jt51[31:0];
+ initial
+ begin
+ explut_jt51[0] <= 45'b111110101011010110001011010000010010111011011;
+ explut_jt51[1] <= 45'b111101010011010101000011001100101110110101011;
+ explut_jt51[2] <= 45'b111011111011010011110111001000110010101110011;
+ explut_jt51[3] <= 45'b111010100101010010101111000100110010101000011;
+ explut_jt51[4] <= 45'b111001001101010001100111000000110010100001011;
+ explut_jt51[5] <= 45'b110111111011010000011110111101010010011011011;
+ explut_jt51[6] <= 45'b110110100011001111010110111001010010010100100;
+ explut_jt51[7] <= 45'b110101001011001110001110110101110010001110011;
+ explut_jt51[8] <= 45'b110011111011001101000110110001110010001000011;
+ explut_jt51[9] <= 45'b110010100011001011111110101110010010000010011;
+ explut_jt51[10] <= 45'b110001010011001010111010101010010001111011011;
+ explut_jt51[11] <= 45'b101111111011001001110010100110110001110101011;
+ explut_jt51[12] <= 45'b101110101011001000101010100011001101101111011;
+ explut_jt51[13] <= 45'b101101010101000111100110011111010001101001011;
+ explut_jt51[14] <= 45'b101100000011000110100010011011110001100011011;
+ explut_jt51[15] <= 45'b101010110011000101011110011000010001011101011;
+ explut_jt51[16] <= 45'b101001100011000100011010010100101101010111011;
+ explut_jt51[17] <= 45'b101000010011000011010010010001001101010001011;
+ explut_jt51[18] <= 45'b100111000011000010010010001101101101001011011;
+ explut_jt51[19] <= 45'b100101110011000001001110001010001101000101011;
+ explut_jt51[20] <= 45'b100100100011000000001010000110010000111111011;
+ explut_jt51[21] <= 45'b100011010010111111001010000011001100111001011;
+ explut_jt51[22] <= 45'b100010000010111110000101111111101100110011011;
+ explut_jt51[23] <= 45'b100000110010111101000001111100001100101101011;
+ explut_jt51[24] <= 45'b011111101010111100000001111000101100101000010;
+ explut_jt51[25] <= 45'b011110011010111011000001110101001100100010011;
+ explut_jt51[26] <= 45'b011101001010111010000001110001110000011100011;
+ explut_jt51[27] <= 45'b011100000010111001000001101110010000010110011;
+ explut_jt51[28] <= 45'b011010110010111000000001101011001100010001011;
+ explut_jt51[29] <= 45'b011001101010110111000001100111101100001011011;
+ explut_jt51[30] <= 45'b011000100000110110000001100100010000000110010;
+ explut_jt51[31] <= 45'b010111010010110101000001100001001100000000011;
+ end
+
+ always @ (posedge clk)
+ exp <= explut_jt51[addr];
+
+endmodule
diff --git a/src/sound/jt12/jt12_kon.v b/src/sound/jt12/jt12_kon.v
new file mode 100644
index 0000000..78d41d2
--- /dev/null
+++ b/src/sound/jt12/jt12_kon.v
@@ -0,0 +1,68 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+
+module jt12_kon(
+ input rst,
+ input clk,
+ input [3:0] keyon_op,
+ input [2:0] keyon_ch,
+ input [1:0] cur_op,
+ input [2:0] cur_ch,
+ input up_keyon,
+ input csm,
+ input flag_A,
+ input overflow_A,
+
+ output reg keyon_II
+);
+
+//reg csm_copy;
+
+reg din;
+wire drop;
+
+reg [3:0] cur_op_hot;
+
+always @(posedge clk)
+ keyon_II <= (csm&&cur_ch==2'd2&&overflow_A) || drop;
+
+always @(*) begin
+ case( cur_op )
+ 2'd0: cur_op_hot <= 4'b0001; // S1
+ 2'd1: cur_op_hot <= 4'b0100; // S3
+ 2'd2: cur_op_hot <= 4'b0010; // S2
+ 2'd3: cur_op_hot <= 4'b1000; // S4
+ endcase
+ din <= keyon_ch==cur_ch && up_keyon ? |(keyon_op&cur_op_hot) : drop;
+end
+
+jt12_sh_rst #(.width(1),.stages(24)) u_konch(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( din ),
+ .drop ( drop )
+);
+
+endmodule
diff --git a/src/sound/jt12/jt12_lfo.v b/src/sound/jt12/jt12_lfo.v
new file mode 100644
index 0000000..26c3e24
--- /dev/null
+++ b/src/sound/jt12/jt12_lfo.v
@@ -0,0 +1,68 @@
+/* This file is part of jt12.
+
+ jt12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ jt12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with jt12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 25-2-2017
+ */
+
+`timescale 1ns / 1ps
+
+/*
+
+ tab size 4
+
+*/
+
+module jt12_lfo(
+ input rst,
+ input clk,
+ input zero,
+ input lfo_rst,
+ input lfo_en,
+ input [2:0] lfo_freq,
+ output reg [6:0] lfo_mod
+);
+
+reg [6:0] cnt, limit;
+
+always @(*)
+ case( lfo_freq )
+ 3'd0: limit <= 7'd108;
+ 3'd1: limit <= 7'd78;
+ 3'd2: limit <= 7'd71;
+ 3'd3: limit <= 7'd67;
+ 3'd4: limit <= 7'd62;
+ 3'd5: limit <= 7'd44;
+ 3'd6: limit <= 7'd8;
+ 3'd7: limit <= 7'd5;
+ endcase
+
+always @(posedge clk) begin
+ if( rst || !lfo_en )
+ { lfo_mod, cnt } <= 14'd0;
+ else begin
+ if(zero) begin
+ if( cnt == limit ) begin
+ cnt <= 7'd0;
+ lfo_mod <= lfo_mod + 1'b1;
+ end
+ else
+ cnt <= cnt + 1'b1;
+ end
+ end
+end
+
+endmodule
diff --git a/src/sound/jt12/jt12_limitamp.v b/src/sound/jt12/jt12_limitamp.v
new file mode 100644
index 0000000..d6ff147
--- /dev/null
+++ b/src/sound/jt12/jt12_limitamp.v
@@ -0,0 +1,42 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: March, 10th 2017
+ */
+
+/* Limiting amplifier by 3dB * shift */
+
+`timescale 1ns / 1ps
+
+module jt12_limitamp #( parameter width=20, shift=5 ) (
+ input signed [width-1:0] left_in,
+ input signed [width-1:0] right_in,
+ output reg signed [width-1:0] left_out,
+ output reg signed [width-1:0] right_out
+);
+
+always @(*) begin
+ left_out <= ^left_in[width-1:width-1-shift] ?
+ { left_in[width-1], {(width-1){~left_in[width-1]}}} :
+ left_in <<< shift;
+
+ right_out <= ^right_in[width-1:width-1-shift] ?
+ { right_in[width-1], {(width-1){~right_in[width-1]}}} :
+ right_in <<< shift;
+end
+
+endmodule
diff --git a/src/sound/jt12/jt12_mmr.v b/src/sound/jt12/jt12_mmr.v
new file mode 100644
index 0000000..aea9c4a
--- /dev/null
+++ b/src/sound/jt12/jt12_mmr.v
@@ -0,0 +1,370 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 14-2-2017
+ */
+
+`timescale 1ns / 1ps
+
+module jt12_mmr(
+ input rst,
+ input clk,
+ input [7:0] din,
+ input write,
+ input [1:0] addr,
+ output reg busy,
+ output ch6op,
+ // LFO
+ output reg [2:0] lfo_freq,
+ output reg lfo_en,
+ // Timers
+ output reg [9:0] value_A,
+ output reg [7:0] value_B,
+ output reg load_A,
+ output reg load_B,
+ output reg enable_irq_A,
+ output reg enable_irq_B,
+ output reg clr_flag_A,
+ output reg clr_flag_B,
+ output reg fast_timers,
+ input flag_A,
+ input overflow_A,
+ // PCM
+ output reg [8:0] pcm,
+ output reg pcm_en,
+
+ `ifdef TEST_SUPPORT
+ // Test
+ output reg test_eg,
+ output reg test_op0,
+ `endif
+ // Operator
+ output use_prevprev1,
+ output use_internal_x,
+ output use_internal_y,
+ output use_prev2,
+ output use_prev1,
+ // PG
+ output [10:0] fnum_I,
+ output [ 2:0] block_I,
+ output reg pg_stop,
+ // REG
+ output [ 1:0] rl,
+ output [ 2:0] fb_II,
+ output [ 2:0] alg,
+ output [ 2:0] pms,
+ output [ 1:0] ams_VII,
+ output amsen_VII,
+ output [ 2:0] dt1_II,
+ output [ 3:0] mul_V,
+ output [ 6:0] tl_VII,
+ output reg eg_stop,
+
+ output [ 4:0] ar_II,
+ output [ 4:0] d1r_II,
+ output [ 4:0] d2r_II,
+ output [ 3:0] rr_II,
+ output [ 3:0] d1l,
+ output [ 1:0] ks_III,
+ // SSG operation
+ output ssg_en_II,
+ output [2:0] ssg_eg_II,
+
+ output keyon_II,
+
+// output [ 1:0] cur_op,
+ // Operator
+ output zero,
+ output s1_enters,
+ output s2_enters,
+ output s3_enters,
+ output s4_enters
+);
+
+reg [7:0] selected_register;
+
+//reg sch; // 0 => CH1~CH3 only available. 1=>CH4~CH6
+/*
+reg irq_zero_en, irq_brdy_en, irq_eos_en,
+ irq_tb_en, irq_ta_en;
+ */
+reg up_clr;
+reg up_alg;
+
+reg up_block;
+reg up_fnumlo;
+reg up_pms;
+reg up_dt1;
+reg up_tl;
+reg up_ks_ar;
+reg up_amen_d1r;
+reg up_d2r;
+reg up_d1l;
+reg up_ssgeg;
+reg up_keyon;
+
+wire busy_reg;
+
+parameter REG_TEST = 8'h01,
+ REG_TEST2 = 8'h02,
+ REG_TESTYM = 8'h21,
+ REG_LFO = 8'h22,
+ REG_CLKA1 = 8'h24,
+ REG_CLKA2 = 8'h25,
+ REG_CLKB = 8'h26,
+ REG_TIMER = 8'h27,
+ REG_KON = 8'h28,
+ REG_IRQMASK = 8'h29,
+ REG_PCM = 8'h2A,
+ REG_PCM_EN = 8'h2B,
+ REG_DACTEST = 8'h2C,
+ REG_CLK_N6 = 8'h2D,
+ REG_CLK_N3 = 8'h2E,
+ REG_CLK_N2 = 8'h2F;
+
+
+reg csm, effect;
+
+reg [ 2:0] block_ch3op2, block_ch3op3, block_ch3op1;
+reg [10:0] fnum_ch3op2, fnum_ch3op3, fnum_ch3op1;
+reg [ 5:0] latch_ch3op2, latch_ch3op3, latch_ch3op1;
+
+
+reg [2:0] up_ch;
+reg [1:0] up_op;
+
+`include "jt12_mmr_sim.vh"
+
+always @(posedge clk) begin : memory_mapped_registers
+ reg old_write;
+ old_write <= write;
+
+ if( rst ) begin
+ selected_register <= 8'h0;
+ busy <= 1'b0;
+ up_ch <= 3'd0;
+ up_op <= 2'd0;
+ { up_keyon, up_alg, up_block, up_fnumlo,
+ up_pms, up_dt1, up_tl, up_ks_ar,
+ up_amen_d1r,up_d2r, up_d1l, up_ssgeg } <= 12'd0;
+ `ifdef TEST_SUPPORT
+ { test_eg, test_op0 } <= 2'd0;
+ `endif
+ // IRQ Mask
+ /*{ irq_zero_en, irq_brdy_en, irq_eos_en,
+ irq_tb_en, irq_ta_en } = 5'h1f; */
+ // timers
+ { value_A, value_B } <= 18'd0;
+ { clr_flag_B, clr_flag_A,
+ enable_irq_B, enable_irq_A, load_B, load_A } <= 6'd0;
+ up_clr <= 1'b0;
+ fast_timers <= 1'b0;
+ // LFO
+ lfo_freq <= 3'd0;
+ lfo_en <= 1'b0;
+ csm <= 1'b0;
+ effect <= 1'b0;
+ // PCM
+ pcm <= 9'h0;
+ pcm_en <= 1'b0;
+ // sch <= 1'b0;
+ // Original test features
+ eg_stop <= 1'b0;
+ pg_stop <= 1'b0;
+ `ifdef SIMULATION
+ mmr_dump <= 1'b0;
+ `endif
+ end else begin
+ // WRITE IN REGISTERS
+ if( old_write ^ write ) begin
+ busy <= 1'b1;
+ if( !addr[0] ) begin
+ selected_register <= din;
+ up_ch <= {addr[1], din[1:0]};
+ up_op <= din[3:2]; // 0=S1,1=S3,2=S2,3=S4
+ end else begin
+ // Global registers
+ if( selected_register < 8'h30 ) begin
+ case( selected_register)
+ // registros especiales
+ //REG_TEST: lfo_rst <= 1'b1; // regardless of din
+ `ifdef TEST_SUPPORT
+ REG_TEST2: { mmr_dump, test_op0, test_eg } <= din[2:0];
+ `endif
+ REG_TESTYM: begin
+ eg_stop <= din[5];
+ pg_stop <= din[3];
+ fast_timers <= din[2];
+ end
+ REG_KON: up_keyon <= 1'b1;
+ REG_CLKA1: value_A[9:2]<= din;
+ REG_CLKA2: value_A[1:0]<= din[1:0];
+ REG_CLKB: value_B <= din;
+ REG_TIMER: begin
+ effect <= |din[7:6];
+ csm <= din[7:6] == 2'b10;
+ { clr_flag_B, clr_flag_A,
+ enable_irq_B, enable_irq_A,
+ load_B, load_A } <= din[5:0];
+ end
+ REG_LFO: { lfo_en, lfo_freq } <= din[3:0];
+ REG_DACTEST:pcm[0] <= din[3];
+ REG_PCM: pcm[8:1]<= din;
+ REG_PCM_EN: pcm_en <= din[7];
+ endcase
+ end
+ else if( selected_register[1:0]!=2'b11 ) begin
+ // channel registers
+ if( selected_register >= 8'hA0 ) begin
+ case( selected_register )
+ 8'hA0, 8'hA1, 8'hA2: up_fnumlo <= 1'b1;
+ 8'hA4, 8'hA5, 8'hA6: up_block <= 1'b1;
+ // CH3 special registers
+ 8'hA9: { block_ch3op1, fnum_ch3op1 } <= { latch_ch3op1, din };
+ 8'hA8: { block_ch3op3, fnum_ch3op3 } <= { latch_ch3op3, din };
+ 8'hAA: { block_ch3op2, fnum_ch3op2 } <= { latch_ch3op2, din };
+ 8'hAD: latch_ch3op1 <= din[5:0];
+ 8'hAC: latch_ch3op3 <= din[5:0];
+ 8'hAE: latch_ch3op2 <= din[5:0];
+ // FB + Algorithm
+ 8'hB0, 8'hB1, 8'hB2: up_alg <= 1'b1;
+ 8'hB4, 8'hB5, 8'hB6: up_pms <= 1'b1;
+ endcase
+ end
+ else
+ // operator registers
+ begin
+ case( selected_register[7:4] )
+ 4'h3: up_dt1 <= 1'b1;
+ 4'h4: up_tl <= 1'b1;
+ 4'h5: up_ks_ar <= 1'b1;
+ 4'h6: up_amen_d1r <= 1'b1;
+ 4'h7: up_d2r <= 1'b1;
+ 4'h8: up_d1l <= 1'b1;
+ 4'h9: up_ssgeg <= 1'b1;
+ endcase
+ end
+ end
+ end
+ end
+ else begin /* clear once-only bits */
+ // csm <= 1'b0;
+ // lfo_rst <= 1'b0;
+ { clr_flag_B, clr_flag_A, load_B, load_A } <= 4'd0;
+ `ifdef SIMULATION
+ mmr_dump <= 1'b0;
+ `endif
+ up_keyon <= 1'b0;
+ if( |{ up_keyon, up_alg, up_block, up_fnumlo,
+ up_pms, up_dt1, up_tl, up_ks_ar,
+ up_amen_d1r,up_d2r, up_d1l, up_ssgeg } == 1'b0 )
+ busy <= 0;
+ else
+ busy <= 1'b1;
+
+ if( busy_reg ) begin
+ up_clr <= 1'b1;
+ end
+ else begin
+ up_clr <= 1'b0;
+ if( up_clr )
+ { up_alg, up_block, up_fnumlo,
+ up_pms, up_dt1, up_tl, up_ks_ar,
+ up_amen_d1r,up_d2r, up_d1l, up_ssgeg } <= 11'd0;
+ end
+ end
+ end
+end
+
+jt12_reg u_reg(
+ .rst ( rst ),
+ .clk ( clk ), // P1
+ .din ( din ),
+
+ .up_keyon ( up_keyon ),
+ .up_alg ( up_alg ),
+ .up_block ( up_block ),
+ .up_fnumlo ( up_fnumlo ),
+ .up_pms ( up_pms ),
+ .up_dt1 ( up_dt1 ),
+ .up_tl ( up_tl ),
+ .up_ks_ar ( up_ks_ar ),
+ .up_amen_d1r(up_amen_d1r),
+ .up_d2r ( up_d2r ),
+
+ .up_d1l ( up_d1l ),
+ .up_ssgeg ( up_ssgeg ),
+
+ .op ( up_op ), // operator to update
+ .ch ( up_ch ), // channel to update
+
+ .csm ( csm ),
+ .flag_A ( flag_A ),
+ .overflow_A ( overflow_A),
+
+ .busy ( busy_reg ),
+ .ch6op ( ch6op ),
+ // CH3 Effect-mode operation
+ .effect ( effect ), // allows independent freq. for CH 3
+ .fnum_ch3op2( fnum_ch3op2 ),
+ .fnum_ch3op3( fnum_ch3op3 ),
+ .fnum_ch3op1( fnum_ch3op1 ),
+ .block_ch3op2( block_ch3op2 ),
+ .block_ch3op3( block_ch3op3 ),
+ .block_ch3op1( block_ch3op1 ),
+ // Operator
+ .use_prevprev1(use_prevprev1),
+ .use_internal_x(use_internal_x),
+ .use_internal_y(use_internal_y),
+ .use_prev2 ( use_prev2 ),
+ .use_prev1 ( use_prev1 ),
+ // PG
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .mul_V ( mul_V ),
+ .dt1_II ( dt1_II ),
+
+ // EG
+ .ar_II (ar_II ), // attack rate
+ .d1r_II (d1r_II ), // decay rate
+ .d2r_II (d2r_II ), // sustain rate
+ .rr_II (rr_II ), // release rate
+ .d1l (d1l ), // sustain level
+ .ks_III (ks_III ), // key scale
+ // SSG operation
+ .ssg_en_II ( ssg_en_II ),
+ .ssg_eg_II ( ssg_eg_II ),
+ // envelope number
+ .tl_VII (tl_VII ),
+ .pms (pms ),
+ .ams_VII (ams_VII ),
+ .amsen_VII (amsen_VII ),
+ // channel configuration
+ .rl ( rl ),
+ .fb_II ( fb_II ),
+ .alg ( alg ),
+ .keyon_II ( keyon_II ),
+
+ //.cur_op ( cur_op ),
+ .zero ( zero ),
+ .s1_enters ( s1_enters ),
+ .s2_enters ( s2_enters ),
+ .s3_enters ( s3_enters ),
+ .s4_enters ( s4_enters )
+);
+
+endmodule
diff --git a/src/sound/jt12/jt12_mmr_sim.vh b/src/sound/jt12/jt12_mmr_sim.vh
new file mode 100644
index 0000000..5bddb08
--- /dev/null
+++ b/src/sound/jt12/jt12_mmr_sim.vh
@@ -0,0 +1,836 @@
+`ifdef SIMULATION
+
+reg [4:0] sep24_cnt;
+reg mmr_dump;
+
+always @(posedge clk )
+ sep24_cnt <= !zero ? sep24_cnt+1'b1 : 5'd0;
+
+wire [10:0] fnum_ch0s1, fnum_ch1s1, fnum_ch2s1, fnum_ch3s1,
+ fnum_ch4s1, fnum_ch5s1, fnum_ch0s2, fnum_ch1s2,
+ fnum_ch2s2, fnum_ch3s2, fnum_ch4s2, fnum_ch5s2,
+ fnum_ch0s3, fnum_ch1s3, fnum_ch2s3, fnum_ch3s3,
+ fnum_ch4s3, fnum_ch5s3, fnum_ch0s4, fnum_ch1s4,
+ fnum_ch2s4, fnum_ch3s4, fnum_ch4s4, fnum_ch5s4;
+
+sep24 #( .width(11), .pos0(1) ) fnum_sep
+(
+ .clk ( clk ),
+ .mixed ( fnum_I ),
+ .mask ( 11'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (fnum_ch0s1),
+ .ch1s1 (fnum_ch1s1),
+ .ch2s1 (fnum_ch2s1),
+ .ch3s1 (fnum_ch3s1),
+ .ch4s1 (fnum_ch4s1),
+ .ch5s1 (fnum_ch5s1),
+
+ .ch0s2 (fnum_ch0s2),
+ .ch1s2 (fnum_ch1s2),
+ .ch2s2 (fnum_ch2s2),
+ .ch3s2 (fnum_ch3s2),
+ .ch4s2 (fnum_ch4s2),
+ .ch5s2 (fnum_ch5s2),
+
+ .ch0s3 (fnum_ch0s3),
+ .ch1s3 (fnum_ch1s3),
+ .ch2s3 (fnum_ch2s3),
+ .ch3s3 (fnum_ch3s3),
+ .ch4s3 (fnum_ch4s3),
+ .ch5s3 (fnum_ch5s3),
+
+ .ch0s4 (fnum_ch0s4),
+ .ch1s4 (fnum_ch1s4),
+ .ch2s4 (fnum_ch2s4),
+ .ch3s4 (fnum_ch3s4),
+ .ch4s4 (fnum_ch4s4),
+ .ch5s4 (fnum_ch5s4)
+);
+
+wire [2:0] block_ch0s1, block_ch1s1, block_ch2s1, block_ch3s1,
+ block_ch4s1, block_ch5s1, block_ch0s2, block_ch1s2,
+ block_ch2s2, block_ch3s2, block_ch4s2, block_ch5s2,
+ block_ch0s3, block_ch1s3, block_ch2s3, block_ch3s3,
+ block_ch4s3, block_ch5s3, block_ch0s4, block_ch1s4,
+ block_ch2s4, block_ch3s4, block_ch4s4, block_ch5s4;
+
+sep24 #( .width(3), .pos0(1) ) block_sep
+(
+ .clk ( clk ),
+ .mixed ( block_I ),
+ .mask ( 3'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (block_ch0s1),
+ .ch1s1 (block_ch1s1),
+ .ch2s1 (block_ch2s1),
+ .ch3s1 (block_ch3s1),
+ .ch4s1 (block_ch4s1),
+ .ch5s1 (block_ch5s1),
+
+ .ch0s2 (block_ch0s2),
+ .ch1s2 (block_ch1s2),
+ .ch2s2 (block_ch2s2),
+ .ch3s2 (block_ch3s2),
+ .ch4s2 (block_ch4s2),
+ .ch5s2 (block_ch5s2),
+
+ .ch0s3 (block_ch0s3),
+ .ch1s3 (block_ch1s3),
+ .ch2s3 (block_ch2s3),
+ .ch3s3 (block_ch3s3),
+ .ch4s3 (block_ch4s3),
+ .ch5s3 (block_ch5s3),
+
+ .ch0s4 (block_ch0s4),
+ .ch1s4 (block_ch1s4),
+ .ch2s4 (block_ch2s4),
+ .ch3s4 (block_ch3s4),
+ .ch4s4 (block_ch4s4),
+ .ch5s4 (block_ch5s4)
+);
+
+wire [1:0] rl_ch0s1, rl_ch1s1, rl_ch2s1, rl_ch3s1,
+ rl_ch4s1, rl_ch5s1, rl_ch0s2, rl_ch1s2,
+ rl_ch2s2, rl_ch3s2, rl_ch4s2, rl_ch5s2,
+ rl_ch0s3, rl_ch1s3, rl_ch2s3, rl_ch3s3,
+ rl_ch4s3, rl_ch5s3, rl_ch0s4, rl_ch1s4,
+ rl_ch2s4, rl_ch3s4, rl_ch4s4, rl_ch5s4;
+
+sep24 #( .width(2), .pos0(1) ) rl_sep
+(
+ .clk ( clk ),
+ .mixed ( rl ),
+ .mask ( 2'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (rl_ch0s1),
+ .ch1s1 (rl_ch1s1),
+ .ch2s1 (rl_ch2s1),
+ .ch3s1 (rl_ch3s1),
+ .ch4s1 (rl_ch4s1),
+ .ch5s1 (rl_ch5s1),
+
+ .ch0s2 (rl_ch0s2),
+ .ch1s2 (rl_ch1s2),
+ .ch2s2 (rl_ch2s2),
+ .ch3s2 (rl_ch3s2),
+ .ch4s2 (rl_ch4s2),
+ .ch5s2 (rl_ch5s2),
+
+ .ch0s3 (rl_ch0s3),
+ .ch1s3 (rl_ch1s3),
+ .ch2s3 (rl_ch2s3),
+ .ch3s3 (rl_ch3s3),
+ .ch4s3 (rl_ch4s3),
+ .ch5s3 (rl_ch5s3),
+
+ .ch0s4 (rl_ch0s4),
+ .ch1s4 (rl_ch1s4),
+ .ch2s4 (rl_ch2s4),
+ .ch3s4 (rl_ch3s4),
+ .ch4s4 (rl_ch4s4),
+ .ch5s4 (rl_ch5s4)
+);
+
+wire [2:0] fb_ch0s1, fb_ch1s1, fb_ch2s1, fb_ch3s1,
+ fb_ch4s1, fb_ch5s1, fb_ch0s2, fb_ch1s2,
+ fb_ch2s2, fb_ch3s2, fb_ch4s2, fb_ch5s2,
+ fb_ch0s3, fb_ch1s3, fb_ch2s3, fb_ch3s3,
+ fb_ch4s3, fb_ch5s3, fb_ch0s4, fb_ch1s4,
+ fb_ch2s4, fb_ch3s4, fb_ch4s4, fb_ch5s4;
+
+sep24 #( .width(3), .pos0(0) ) fb_sep
+(
+ .clk ( clk ),
+ .mixed ( fb_II ),
+ .mask ( 3'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (fb_ch0s1),
+ .ch1s1 (fb_ch1s1),
+ .ch2s1 (fb_ch2s1),
+ .ch3s1 (fb_ch3s1),
+ .ch4s1 (fb_ch4s1),
+ .ch5s1 (fb_ch5s1),
+
+ .ch0s2 (fb_ch0s2),
+ .ch1s2 (fb_ch1s2),
+ .ch2s2 (fb_ch2s2),
+ .ch3s2 (fb_ch3s2),
+ .ch4s2 (fb_ch4s2),
+ .ch5s2 (fb_ch5s2),
+
+ .ch0s3 (fb_ch0s3),
+ .ch1s3 (fb_ch1s3),
+ .ch2s3 (fb_ch2s3),
+ .ch3s3 (fb_ch3s3),
+ .ch4s3 (fb_ch4s3),
+ .ch5s3 (fb_ch5s3),
+
+ .ch0s4 (fb_ch0s4),
+ .ch1s4 (fb_ch1s4),
+ .ch2s4 (fb_ch2s4),
+ .ch3s4 (fb_ch3s4),
+ .ch4s4 (fb_ch4s4),
+ .ch5s4 (fb_ch5s4)
+);
+
+wire [2:0] alg_ch0s1, alg_ch1s1, alg_ch2s1, alg_ch3s1,
+ alg_ch4s1, alg_ch5s1, alg_ch0s2, alg_ch1s2,
+ alg_ch2s2, alg_ch3s2, alg_ch4s2, alg_ch5s2,
+ alg_ch0s3, alg_ch1s3, alg_ch2s3, alg_ch3s3,
+ alg_ch4s3, alg_ch5s3, alg_ch0s4, alg_ch1s4,
+ alg_ch2s4, alg_ch3s4, alg_ch4s4, alg_ch5s4;
+
+sep24 #( .width(3), .pos0(1) ) alg_sep
+(
+ .clk ( clk ),
+ .mixed ( alg ),
+ .mask ( 3'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (alg_ch0s1),
+ .ch1s1 (alg_ch1s1),
+ .ch2s1 (alg_ch2s1),
+ .ch3s1 (alg_ch3s1),
+ .ch4s1 (alg_ch4s1),
+ .ch5s1 (alg_ch5s1),
+
+ .ch0s2 (alg_ch0s2),
+ .ch1s2 (alg_ch1s2),
+ .ch2s2 (alg_ch2s2),
+ .ch3s2 (alg_ch3s2),
+ .ch4s2 (alg_ch4s2),
+ .ch5s2 (alg_ch5s2),
+
+ .ch0s3 (alg_ch0s3),
+ .ch1s3 (alg_ch1s3),
+ .ch2s3 (alg_ch2s3),
+ .ch3s3 (alg_ch3s3),
+ .ch4s3 (alg_ch4s3),
+ .ch5s3 (alg_ch5s3),
+
+ .ch0s4 (alg_ch0s4),
+ .ch1s4 (alg_ch1s4),
+ .ch2s4 (alg_ch2s4),
+ .ch3s4 (alg_ch3s4),
+ .ch4s4 (alg_ch4s4),
+ .ch5s4 (alg_ch5s4)
+);
+
+wire [2:0] dt1_ch0s1, dt1_ch1s1, dt1_ch2s1, dt1_ch3s1,
+ dt1_ch4s1, dt1_ch5s1, dt1_ch0s2, dt1_ch1s2,
+ dt1_ch2s2, dt1_ch3s2, dt1_ch4s2, dt1_ch5s2,
+ dt1_ch0s3, dt1_ch1s3, dt1_ch2s3, dt1_ch3s3,
+ dt1_ch4s3, dt1_ch5s3, dt1_ch0s4, dt1_ch1s4,
+ dt1_ch2s4, dt1_ch3s4, dt1_ch4s4, dt1_ch5s4;
+
+sep24 #( .width(3), .pos0(0) ) dt1_sep
+(
+ .clk ( clk ),
+ .mixed ( dt1_II ),
+ .mask ( 3'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (dt1_ch0s1),
+ .ch1s1 (dt1_ch1s1),
+ .ch2s1 (dt1_ch2s1),
+ .ch3s1 (dt1_ch3s1),
+ .ch4s1 (dt1_ch4s1),
+ .ch5s1 (dt1_ch5s1),
+
+ .ch0s2 (dt1_ch0s2),
+ .ch1s2 (dt1_ch1s2),
+ .ch2s2 (dt1_ch2s2),
+ .ch3s2 (dt1_ch3s2),
+ .ch4s2 (dt1_ch4s2),
+ .ch5s2 (dt1_ch5s2),
+
+ .ch0s3 (dt1_ch0s3),
+ .ch1s3 (dt1_ch1s3),
+ .ch2s3 (dt1_ch2s3),
+ .ch3s3 (dt1_ch3s3),
+ .ch4s3 (dt1_ch4s3),
+ .ch5s3 (dt1_ch5s3),
+
+ .ch0s4 (dt1_ch0s4),
+ .ch1s4 (dt1_ch1s4),
+ .ch2s4 (dt1_ch2s4),
+ .ch3s4 (dt1_ch3s4),
+ .ch4s4 (dt1_ch4s4),
+ .ch5s4 (dt1_ch5s4)
+);
+
+wire [3:0] mul_ch0s1, mul_ch1s1, mul_ch2s1, mul_ch3s1,
+ mul_ch4s1, mul_ch5s1, mul_ch0s2, mul_ch1s2,
+ mul_ch2s2, mul_ch3s2, mul_ch4s2, mul_ch5s2,
+ mul_ch0s3, mul_ch1s3, mul_ch2s3, mul_ch3s3,
+ mul_ch4s3, mul_ch5s3, mul_ch0s4, mul_ch1s4,
+ mul_ch2s4, mul_ch3s4, mul_ch4s4, mul_ch5s4;
+
+sep24 #( .width(4), .pos0(21) ) mul_sep
+(
+ .clk ( clk ),
+ .mixed ( mul_V ),
+ .mask ( 4'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (mul_ch0s1),
+ .ch1s1 (mul_ch1s1),
+ .ch2s1 (mul_ch2s1),
+ .ch3s1 (mul_ch3s1),
+ .ch4s1 (mul_ch4s1),
+ .ch5s1 (mul_ch5s1),
+
+ .ch0s2 (mul_ch0s2),
+ .ch1s2 (mul_ch1s2),
+ .ch2s2 (mul_ch2s2),
+ .ch3s2 (mul_ch3s2),
+ .ch4s2 (mul_ch4s2),
+ .ch5s2 (mul_ch5s2),
+
+ .ch0s3 (mul_ch0s3),
+ .ch1s3 (mul_ch1s3),
+ .ch2s3 (mul_ch2s3),
+ .ch3s3 (mul_ch3s3),
+ .ch4s3 (mul_ch4s3),
+ .ch5s3 (mul_ch5s3),
+
+ .ch0s4 (mul_ch0s4),
+ .ch1s4 (mul_ch1s4),
+ .ch2s4 (mul_ch2s4),
+ .ch3s4 (mul_ch3s4),
+ .ch4s4 (mul_ch4s4),
+ .ch5s4 (mul_ch5s4)
+);
+
+wire [6:0] tl_ch0s1, tl_ch1s1, tl_ch2s1, tl_ch3s1,
+ tl_ch4s1, tl_ch5s1, tl_ch0s2, tl_ch1s2,
+ tl_ch2s2, tl_ch3s2, tl_ch4s2, tl_ch5s2,
+ tl_ch0s3, tl_ch1s3, tl_ch2s3, tl_ch3s3,
+ tl_ch4s3, tl_ch5s3, tl_ch0s4, tl_ch1s4,
+ tl_ch2s4, tl_ch3s4, tl_ch4s4, tl_ch5s4;
+
+sep24 #( .width(7), .pos0(19) ) tl_step
+(
+ .clk ( clk ),
+ .mixed ( tl_VII ),
+ .mask ( 7'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (tl_ch0s1),
+ .ch1s1 (tl_ch1s1),
+ .ch2s1 (tl_ch2s1),
+ .ch3s1 (tl_ch3s1),
+ .ch4s1 (tl_ch4s1),
+ .ch5s1 (tl_ch5s1),
+
+ .ch0s2 (tl_ch0s2),
+ .ch1s2 (tl_ch1s2),
+ .ch2s2 (tl_ch2s2),
+ .ch3s2 (tl_ch3s2),
+ .ch4s2 (tl_ch4s2),
+ .ch5s2 (tl_ch5s2),
+
+ .ch0s3 (tl_ch0s3),
+ .ch1s3 (tl_ch1s3),
+ .ch2s3 (tl_ch2s3),
+ .ch3s3 (tl_ch3s3),
+ .ch4s3 (tl_ch4s3),
+ .ch5s3 (tl_ch5s3),
+
+ .ch0s4 (tl_ch0s4),
+ .ch1s4 (tl_ch1s4),
+ .ch2s4 (tl_ch2s4),
+ .ch3s4 (tl_ch3s4),
+ .ch4s4 (tl_ch4s4),
+ .ch5s4 (tl_ch5s4)
+);
+
+wire [4:0] ar_ch0s1, ar_ch1s1, ar_ch2s1, ar_ch3s1,
+ ar_ch4s1, ar_ch5s1, ar_ch0s2, ar_ch1s2,
+ ar_ch2s2, ar_ch3s2, ar_ch4s2, ar_ch5s2,
+ ar_ch0s3, ar_ch1s3, ar_ch2s3, ar_ch3s3,
+ ar_ch4s3, ar_ch5s3, ar_ch0s4, ar_ch1s4,
+ ar_ch2s4, ar_ch3s4, ar_ch4s4, ar_ch5s4;
+
+sep24 #( .width(5), .pos0(0) ) ar_step
+(
+ .clk ( clk ),
+ .mixed ( ar_II ),
+ .mask ( 5'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (ar_ch0s1),
+ .ch1s1 (ar_ch1s1),
+ .ch2s1 (ar_ch2s1),
+ .ch3s1 (ar_ch3s1),
+ .ch4s1 (ar_ch4s1),
+ .ch5s1 (ar_ch5s1),
+
+ .ch0s2 (ar_ch0s2),
+ .ch1s2 (ar_ch1s2),
+ .ch2s2 (ar_ch2s2),
+ .ch3s2 (ar_ch3s2),
+ .ch4s2 (ar_ch4s2),
+ .ch5s2 (ar_ch5s2),
+
+ .ch0s3 (ar_ch0s3),
+ .ch1s3 (ar_ch1s3),
+ .ch2s3 (ar_ch2s3),
+ .ch3s3 (ar_ch3s3),
+ .ch4s3 (ar_ch4s3),
+ .ch5s3 (ar_ch5s3),
+
+ .ch0s4 (ar_ch0s4),
+ .ch1s4 (ar_ch1s4),
+ .ch2s4 (ar_ch2s4),
+ .ch3s4 (ar_ch3s4),
+ .ch4s4 (ar_ch4s4),
+ .ch5s4 (ar_ch5s4)
+);
+
+wire [4:0] d1r_ch0s1, d1r_ch1s1, d1r_ch2s1, d1r_ch3s1,
+ d1r_ch4s1, d1r_ch5s1, d1r_ch0s2, d1r_ch1s2,
+ d1r_ch2s2, d1r_ch3s2, d1r_ch4s2, d1r_ch5s2,
+ d1r_ch0s3, d1r_ch1s3, d1r_ch2s3, d1r_ch3s3,
+ d1r_ch4s3, d1r_ch5s3, d1r_ch0s4, d1r_ch1s4,
+ d1r_ch2s4, d1r_ch3s4, d1r_ch4s4, d1r_ch5s4;
+
+sep24 #( .width(5), .pos0(0) ) d1r_step
+(
+ .clk ( clk ),
+ .mixed ( d1r_II ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (d1r_ch0s1),
+ .ch1s1 (d1r_ch1s1),
+ .ch2s1 (d1r_ch2s1),
+ .ch3s1 (d1r_ch3s1),
+ .ch4s1 (d1r_ch4s1),
+ .ch5s1 (d1r_ch5s1),
+
+ .ch0s2 (d1r_ch0s2),
+ .ch1s2 (d1r_ch1s2),
+ .ch2s2 (d1r_ch2s2),
+ .ch3s2 (d1r_ch3s2),
+ .ch4s2 (d1r_ch4s2),
+ .ch5s2 (d1r_ch5s2),
+
+ .ch0s3 (d1r_ch0s3),
+ .ch1s3 (d1r_ch1s3),
+ .ch2s3 (d1r_ch2s3),
+ .ch3s3 (d1r_ch3s3),
+ .ch4s3 (d1r_ch4s3),
+ .ch5s3 (d1r_ch5s3),
+
+ .ch0s4 (d1r_ch0s4),
+ .ch1s4 (d1r_ch1s4),
+ .ch2s4 (d1r_ch2s4),
+ .ch3s4 (d1r_ch3s4),
+ .ch4s4 (d1r_ch4s4),
+ .ch5s4 (d1r_ch5s4)
+);
+
+wire [4:0] d2r_ch0s1, d2r_ch1s1, d2r_ch2s1, d2r_ch3s1,
+ d2r_ch4s1, d2r_ch5s1, d2r_ch0s2, d2r_ch1s2,
+ d2r_ch2s2, d2r_ch3s2, d2r_ch4s2, d2r_ch5s2,
+ d2r_ch0s3, d2r_ch1s3, d2r_ch2s3, d2r_ch3s3,
+ d2r_ch4s3, d2r_ch5s3, d2r_ch0s4, d2r_ch1s4,
+ d2r_ch2s4, d2r_ch3s4, d2r_ch4s4, d2r_ch5s4;
+
+sep24 #( .width(5), .pos0(0) ) d2r_step
+(
+ .clk ( clk ),
+ .mixed ( d2r_II ),
+ .mask ( 5'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (d2r_ch0s1),
+ .ch1s1 (d2r_ch1s1),
+ .ch2s1 (d2r_ch2s1),
+ .ch3s1 (d2r_ch3s1),
+ .ch4s1 (d2r_ch4s1),
+ .ch5s1 (d2r_ch5s1),
+
+ .ch0s2 (d2r_ch0s2),
+ .ch1s2 (d2r_ch1s2),
+ .ch2s2 (d2r_ch2s2),
+ .ch3s2 (d2r_ch3s2),
+ .ch4s2 (d2r_ch4s2),
+ .ch5s2 (d2r_ch5s2),
+
+ .ch0s3 (d2r_ch0s3),
+ .ch1s3 (d2r_ch1s3),
+ .ch2s3 (d2r_ch2s3),
+ .ch3s3 (d2r_ch3s3),
+ .ch4s3 (d2r_ch4s3),
+ .ch5s3 (d2r_ch5s3),
+
+ .ch0s4 (d2r_ch0s4),
+ .ch1s4 (d2r_ch1s4),
+ .ch2s4 (d2r_ch2s4),
+ .ch3s4 (d2r_ch3s4),
+ .ch4s4 (d2r_ch4s4),
+ .ch5s4 (d2r_ch5s4)
+);
+
+wire [3:0] rr_ch0s1, rr_ch1s1, rr_ch2s1, rr_ch3s1,
+ rr_ch4s1, rr_ch5s1, rr_ch0s2, rr_ch1s2,
+ rr_ch2s2, rr_ch3s2, rr_ch4s2, rr_ch5s2,
+ rr_ch0s3, rr_ch1s3, rr_ch2s3, rr_ch3s3,
+ rr_ch4s3, rr_ch5s3, rr_ch0s4, rr_ch1s4,
+ rr_ch2s4, rr_ch3s4, rr_ch4s4, rr_ch5s4;
+
+sep24 #( .width(4), .pos0(0) ) rr_step
+(
+ .clk ( clk ),
+ .mixed ( rr_II ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (rr_ch0s1),
+ .ch1s1 (rr_ch1s1),
+ .ch2s1 (rr_ch2s1),
+ .ch3s1 (rr_ch3s1),
+ .ch4s1 (rr_ch4s1),
+ .ch5s1 (rr_ch5s1),
+
+ .ch0s2 (rr_ch0s2),
+ .ch1s2 (rr_ch1s2),
+ .ch2s2 (rr_ch2s2),
+ .ch3s2 (rr_ch3s2),
+ .ch4s2 (rr_ch4s2),
+ .ch5s2 (rr_ch5s2),
+
+ .ch0s3 (rr_ch0s3),
+ .ch1s3 (rr_ch1s3),
+ .ch2s3 (rr_ch2s3),
+ .ch3s3 (rr_ch3s3),
+ .ch4s3 (rr_ch4s3),
+ .ch5s3 (rr_ch5s3),
+
+ .ch0s4 (rr_ch0s4),
+ .ch1s4 (rr_ch1s4),
+ .ch2s4 (rr_ch2s4),
+ .ch3s4 (rr_ch3s4),
+ .ch4s4 (rr_ch4s4),
+ .ch5s4 (rr_ch5s4)
+);
+
+wire [3:0] d1l_ch0s1, d1l_ch1s1, d1l_ch2s1, d1l_ch3s1,
+ d1l_ch4s1, d1l_ch5s1, d1l_ch0s2, d1l_ch1s2,
+ d1l_ch2s2, d1l_ch3s2, d1l_ch4s2, d1l_ch5s2,
+ d1l_ch0s3, d1l_ch1s3, d1l_ch2s3, d1l_ch3s3,
+ d1l_ch4s3, d1l_ch5s3, d1l_ch0s4, d1l_ch1s4,
+ d1l_ch2s4, d1l_ch3s4, d1l_ch4s4, d1l_ch5s4;
+
+sep24 #( .width(4), .pos0(1) ) d1l_step
+(
+ .clk ( clk ),
+ .mixed ( d1l ),
+ .mask ( 4'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (d1l_ch0s1),
+ .ch1s1 (d1l_ch1s1),
+ .ch2s1 (d1l_ch2s1),
+ .ch3s1 (d1l_ch3s1),
+ .ch4s1 (d1l_ch4s1),
+ .ch5s1 (d1l_ch5s1),
+
+ .ch0s2 (d1l_ch0s2),
+ .ch1s2 (d1l_ch1s2),
+ .ch2s2 (d1l_ch2s2),
+ .ch3s2 (d1l_ch3s2),
+ .ch4s2 (d1l_ch4s2),
+ .ch5s2 (d1l_ch5s2),
+
+ .ch0s3 (d1l_ch0s3),
+ .ch1s3 (d1l_ch1s3),
+ .ch2s3 (d1l_ch2s3),
+ .ch3s3 (d1l_ch3s3),
+ .ch4s3 (d1l_ch4s3),
+ .ch5s3 (d1l_ch5s3),
+
+ .ch0s4 (d1l_ch0s4),
+ .ch1s4 (d1l_ch1s4),
+ .ch2s4 (d1l_ch2s4),
+ .ch3s4 (d1l_ch3s4),
+ .ch4s4 (d1l_ch4s4),
+ .ch5s4 (d1l_ch5s4)
+);
+
+wire [1:0] ks_ch0s1, ks_ch1s1, ks_ch2s1, ks_ch3s1,
+ ks_ch4s1, ks_ch5s1, ks_ch0s2, ks_ch1s2,
+ ks_ch2s2, ks_ch3s2, ks_ch4s2, ks_ch5s2,
+ ks_ch0s3, ks_ch1s3, ks_ch2s3, ks_ch3s3,
+ ks_ch4s3, ks_ch5s3, ks_ch0s4, ks_ch1s4,
+ ks_ch2s4, ks_ch3s4, ks_ch4s4, ks_ch5s4;
+
+sep24 #( .width(2), .pos0(23) ) ks_step
+(
+ .clk ( clk ),
+ .mixed ( ks_III ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (ks_ch0s1),
+ .ch1s1 (ks_ch1s1),
+ .ch2s1 (ks_ch2s1),
+ .ch3s1 (ks_ch3s1),
+ .ch4s1 (ks_ch4s1),
+ .ch5s1 (ks_ch5s1),
+
+ .ch0s2 (ks_ch0s2),
+ .ch1s2 (ks_ch1s2),
+ .ch2s2 (ks_ch2s2),
+ .ch3s2 (ks_ch3s2),
+ .ch4s2 (ks_ch4s2),
+ .ch5s2 (ks_ch5s2),
+
+ .ch0s3 (ks_ch0s3),
+ .ch1s3 (ks_ch1s3),
+ .ch2s3 (ks_ch2s3),
+ .ch3s3 (ks_ch3s3),
+ .ch4s3 (ks_ch4s3),
+ .ch5s3 (ks_ch5s3),
+
+ .ch0s4 (ks_ch0s4),
+ .ch1s4 (ks_ch1s4),
+ .ch2s4 (ks_ch2s4),
+ .ch3s4 (ks_ch3s4),
+ .ch4s4 (ks_ch4s4),
+ .ch5s4 (ks_ch5s4)
+);
+
+wire [3:0] ssg_II = {ssg_en_II, ssg_eg_II};
+
+wire [3:0] ssg_ch0s1, ssg_ch1s1, ssg_ch2s1, ssg_ch3s1,
+ ssg_ch4s1, ssg_ch5s1, ssg_ch0s2, ssg_ch1s2,
+ ssg_ch2s2, ssg_ch3s2, ssg_ch4s2, ssg_ch5s2,
+ ssg_ch0s3, ssg_ch1s3, ssg_ch2s3, ssg_ch3s3,
+ ssg_ch4s3, ssg_ch5s3, ssg_ch0s4, ssg_ch1s4,
+ ssg_ch2s4, ssg_ch3s4, ssg_ch4s4, ssg_ch5s4;
+
+sep24 #( .width(4), .pos0(0) ) ssg_step
+(
+ .clk ( clk ),
+ .mixed ( ssg_II ),
+ .mask ( 4'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (ssg_ch0s1),
+ .ch1s1 (ssg_ch1s1),
+ .ch2s1 (ssg_ch2s1),
+ .ch3s1 (ssg_ch3s1),
+ .ch4s1 (ssg_ch4s1),
+ .ch5s1 (ssg_ch5s1),
+
+ .ch0s2 (ssg_ch0s2),
+ .ch1s2 (ssg_ch1s2),
+ .ch2s2 (ssg_ch2s2),
+ .ch3s2 (ssg_ch3s2),
+ .ch4s2 (ssg_ch4s2),
+ .ch5s2 (ssg_ch5s2),
+
+ .ch0s3 (ssg_ch0s3),
+ .ch1s3 (ssg_ch1s3),
+ .ch2s3 (ssg_ch2s3),
+ .ch3s3 (ssg_ch3s3),
+ .ch4s3 (ssg_ch4s3),
+ .ch5s3 (ssg_ch5s3),
+
+ .ch0s4 (ssg_ch0s4),
+ .ch1s4 (ssg_ch1s4),
+ .ch2s4 (ssg_ch2s4),
+ .ch3s4 (ssg_ch3s4),
+ .ch4s4 (ssg_ch4s4),
+ .ch5s4 (ssg_ch5s4)
+);
+
+wire kon_ch0s1, kon_ch1s1, kon_ch2s1, kon_ch3s1,
+ kon_ch4s1, kon_ch5s1, kon_ch0s2, kon_ch1s2,
+ kon_ch2s2, kon_ch3s2, kon_ch4s2, kon_ch5s2,
+ kon_ch0s3, kon_ch1s3, kon_ch2s3, kon_ch3s3,
+ kon_ch4s3, kon_ch5s3, kon_ch0s4, kon_ch1s4,
+ kon_ch2s4, kon_ch3s4, kon_ch4s4, kon_ch5s4;
+
+sep24 #( .width(1), .pos0(0) ) konstep
+(
+ .clk ( clk ),
+ .mixed ( keyon_II ),
+ .mask ( 1'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (kon_ch0s1),
+ .ch1s1 (kon_ch1s1),
+ .ch2s1 (kon_ch2s1),
+ .ch3s1 (kon_ch3s1),
+ .ch4s1 (kon_ch4s1),
+ .ch5s1 (kon_ch5s1),
+
+ .ch0s2 (kon_ch0s2),
+ .ch1s2 (kon_ch1s2),
+ .ch2s2 (kon_ch2s2),
+ .ch3s2 (kon_ch3s2),
+ .ch4s2 (kon_ch4s2),
+ .ch5s2 (kon_ch5s2),
+
+ .ch0s3 (kon_ch0s3),
+ .ch1s3 (kon_ch1s3),
+ .ch2s3 (kon_ch2s3),
+ .ch3s3 (kon_ch3s3),
+ .ch4s3 (kon_ch4s3),
+ .ch5s3 (kon_ch5s3),
+
+ .ch0s4 (kon_ch0s4),
+ .ch1s4 (kon_ch1s4),
+ .ch2s4 (kon_ch2s4),
+ .ch3s4 (kon_ch3s4),
+ .ch4s4 (kon_ch4s4),
+ .ch5s4 (kon_ch5s4)
+);
+
+/* Dump all registers on request */
+integer fmmr;
+initial begin
+ fmmr=$fopen("mmr_dump.log");
+end
+
+always @(posedge clk )
+if (mmr_dump ) begin
+ $fdisplay( fmmr, "-------------------------------");
+ // Channel 0
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch0s1, fnum_ch0s1, rl_ch0s1, fb_ch0s1, alg_ch0s1,
+ dt1_ch0s1, mul_ch0s1, tl_ch0s1, ar_ch0s1, d1r_ch0s1,
+ d2r_ch0s1, rr_ch0s1, d1l_ch0s1, ks_ch0s1, ssg_ch0s1,
+ kon_ch0s1 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch0s2, fnum_ch0s2, rl_ch0s2, fb_ch0s2, alg_ch0s2,
+ dt1_ch0s2, mul_ch0s2, tl_ch0s2, ar_ch0s2, d1r_ch0s2,
+ d2r_ch0s2, rr_ch0s2, d1l_ch0s2, ks_ch0s2, ssg_ch0s2,
+ kon_ch0s2 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch0s1, fnum_ch0s3, rl_ch0s3, fb_ch0s3, alg_ch0s3,
+ dt1_ch0s3, mul_ch0s3, tl_ch0s3, ar_ch0s3, d1r_ch0s3,
+ d2r_ch0s3, rr_ch0s3, d1l_ch0s3, ks_ch0s3, ssg_ch0s3,
+ kon_ch0s3 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch0s4, fnum_ch0s4, rl_ch0s4, fb_ch0s4, alg_ch0s4,
+ dt1_ch0s4, mul_ch0s4, tl_ch0s4, ar_ch0s4, d1r_ch0s4,
+ d2r_ch0s4, rr_ch0s4, d1l_ch0s4, ks_ch0s4, ssg_ch0s4,
+ kon_ch0s4 );
+ // Channel 1
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch1s1, fnum_ch1s1, rl_ch1s1, fb_ch1s1, alg_ch1s1,
+ dt1_ch1s1, mul_ch1s1, tl_ch1s1, ar_ch1s1, d1r_ch1s1,
+ d2r_ch1s1, rr_ch1s1, d1l_ch1s1, ks_ch1s1, ssg_ch1s1,
+ kon_ch1s1 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch1s2, fnum_ch1s2, rl_ch1s2, fb_ch1s2, alg_ch1s2,
+ dt1_ch1s2, mul_ch1s2, tl_ch1s2, ar_ch1s2, d1r_ch1s2,
+ d2r_ch1s2, rr_ch1s2, d1l_ch1s2, ks_ch1s2, ssg_ch1s2,
+ kon_ch1s2 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch1s3, fnum_ch1s3, rl_ch1s3, fb_ch1s3, alg_ch1s3,
+ dt1_ch1s3, mul_ch1s3, tl_ch1s3, ar_ch1s3, d1r_ch1s3,
+ d2r_ch1s3, rr_ch1s3, d1l_ch1s3, ks_ch1s3, ssg_ch1s3,
+ kon_ch1s3 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch1s4, fnum_ch1s4, rl_ch1s4, fb_ch1s4, alg_ch1s4,
+ dt1_ch1s4, mul_ch1s4, tl_ch1s4, ar_ch1s4, d1r_ch1s4,
+ d2r_ch1s4, rr_ch1s4, d1l_ch1s4, ks_ch1s4, ssg_ch1s4,
+ kon_ch1s4 );
+ // Channel 2
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch2s1, fnum_ch2s1, rl_ch2s1, fb_ch2s1, alg_ch2s1,
+ dt1_ch2s1, mul_ch2s1, tl_ch2s1, ar_ch2s1, d1r_ch2s1,
+ d2r_ch2s1, rr_ch2s1, d1l_ch2s1, ks_ch2s1, ssg_ch2s1,
+ kon_ch2s1 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch2s2, fnum_ch2s2, rl_ch2s2, fb_ch2s2, alg_ch2s2,
+ dt1_ch2s2, mul_ch2s2, tl_ch2s2, ar_ch2s2, d1r_ch2s2,
+ d2r_ch2s2, rr_ch2s2, d1l_ch2s2, ks_ch2s2, ssg_ch2s2,
+ kon_ch2s2 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch2s3, fnum_ch2s3, rl_ch2s3, fb_ch2s3, alg_ch2s3,
+ dt1_ch2s3, mul_ch2s3, tl_ch2s3, ar_ch2s3, d1r_ch2s3,
+ d2r_ch2s3, rr_ch2s3, d1l_ch2s3, ks_ch2s3, ssg_ch2s3,
+ kon_ch2s3 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch2s4, fnum_ch2s4, rl_ch2s4, fb_ch2s4, alg_ch2s4,
+ dt1_ch2s4, mul_ch2s4, tl_ch2s4, ar_ch2s4, d1r_ch2s4,
+ d2r_ch2s4, rr_ch2s4, d1l_ch2s4, ks_ch2s4, ssg_ch2s4,
+ kon_ch2s4 );
+ // Channel 3
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch3s1, fnum_ch3s1, rl_ch3s1, fb_ch3s1, alg_ch3s1,
+ dt1_ch3s1, mul_ch3s1, tl_ch3s1, ar_ch3s1, d1r_ch3s1,
+ d2r_ch3s1, rr_ch3s1, d1l_ch3s1, ks_ch3s1, ssg_ch3s1,
+ kon_ch3s1 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch3s2, fnum_ch3s2, rl_ch3s2, fb_ch3s2, alg_ch3s2,
+ dt1_ch3s2, mul_ch3s2, tl_ch3s2, ar_ch3s2, d1r_ch3s2,
+ d2r_ch3s2, rr_ch3s2, d1l_ch3s2, ks_ch3s2, ssg_ch3s2,
+ kon_ch3s2 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch3s3, fnum_ch3s3, rl_ch3s3, fb_ch3s3, alg_ch3s3,
+ dt1_ch3s3, mul_ch3s3, tl_ch3s3, ar_ch3s3, d1r_ch3s3,
+ d2r_ch3s3, rr_ch3s3, d1l_ch3s3, ks_ch3s3, ssg_ch3s3,
+ kon_ch3s3 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch3s4, fnum_ch3s4, rl_ch3s4, fb_ch3s4, alg_ch3s4,
+ dt1_ch3s4, mul_ch3s4, tl_ch3s4, ar_ch3s4, d1r_ch3s4,
+ d2r_ch3s4, rr_ch3s4, d1l_ch3s4, ks_ch3s4, ssg_ch3s4,
+ kon_ch3s4 );
+ // Channel 4
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch4s1, fnum_ch4s1, rl_ch4s1, fb_ch4s1, alg_ch4s1,
+ dt1_ch4s1, mul_ch4s1, tl_ch4s1, ar_ch4s1, d1r_ch4s1,
+ d2r_ch4s1, rr_ch4s1, d1l_ch4s1, ks_ch4s1, ssg_ch4s1,
+ kon_ch4s1 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch4s2, fnum_ch4s2, rl_ch4s2, fb_ch4s2, alg_ch4s2,
+ dt1_ch4s2, mul_ch4s2, tl_ch4s2, ar_ch4s2, d1r_ch4s2,
+ d2r_ch4s2, rr_ch4s2, d1l_ch4s2, ks_ch4s2, ssg_ch4s2,
+ kon_ch4s2 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch4s3, fnum_ch4s3, rl_ch4s3, fb_ch4s3, alg_ch4s3,
+ dt1_ch4s3, mul_ch4s3, tl_ch4s3, ar_ch4s3, d1r_ch4s3,
+ d2r_ch4s3, rr_ch4s3, d1l_ch4s3, ks_ch4s3, ssg_ch4s3,
+ kon_ch4s3 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch4s4, fnum_ch4s4, rl_ch4s4, fb_ch4s4, alg_ch4s4,
+ dt1_ch4s4, mul_ch4s4, tl_ch4s4, ar_ch4s4, d1r_ch4s4,
+ d2r_ch4s4, rr_ch4s4, d1l_ch4s4, ks_ch4s4, ssg_ch4s4,
+ kon_ch4s4 );
+ // Channel 5
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch5s1, fnum_ch5s1, rl_ch5s1, fb_ch5s1, alg_ch5s1,
+ dt1_ch5s1, mul_ch5s1, tl_ch5s1, ar_ch5s1, d1r_ch5s1,
+ d2r_ch5s1, rr_ch5s1, d1l_ch5s1, ks_ch5s1, ssg_ch5s1,
+ kon_ch5s1 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch5s2, fnum_ch5s2, rl_ch5s2, fb_ch5s2, alg_ch5s2,
+ dt1_ch5s2, mul_ch5s2, tl_ch5s2, ar_ch5s2, d1r_ch5s2,
+ d2r_ch5s2, rr_ch5s2, d1l_ch5s2, ks_ch5s2, ssg_ch5s2,
+ kon_ch5s2 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch5s3, fnum_ch5s3, rl_ch5s3, fb_ch5s3, alg_ch5s3,
+ dt1_ch5s3, mul_ch5s3, tl_ch5s3, ar_ch5s3, d1r_ch5s3,
+ d2r_ch5s3, rr_ch5s3, d1l_ch5s3, ks_ch5s3, ssg_ch5s3,
+ kon_ch5s3 );
+ $fdisplay( fmmr, "%x %x %x %x %x, %x %x %x %x %x %x, %x %x %x %x %x",
+ block_ch5s4, fnum_ch5s4, rl_ch5s4, fb_ch5s4, alg_ch5s4,
+ dt1_ch5s4, mul_ch5s4, tl_ch5s4, ar_ch5s4, d1r_ch5s4,
+ d2r_ch5s4, rr_ch5s4, d1l_ch5s4, ks_ch5s4, ssg_ch5s4,
+ kon_ch5s4 );
+end
+`endif
+
diff --git a/src/sound/jt12/jt12_mod.v b/src/sound/jt12/jt12_mod.v
new file mode 100644
index 0000000..9e41603
--- /dev/null
+++ b/src/sound/jt12/jt12_mod.v
@@ -0,0 +1,122 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+
+module jt12_mod(
+ input s1_enters,
+ input s2_enters,
+ input s3_enters,
+ input s4_enters,
+
+ input [2:0] alg_I,
+
+ output reg use_prevprev1,
+ output reg use_internal_x,
+ output reg use_internal_y,
+ output reg use_prev2,
+ output reg use_prev1
+);
+
+reg [7:0] alg_hot;
+
+always @(*) begin
+ case( alg_I )
+ 3'd0: alg_hot <= 8'h1; // D0
+ 3'd1: alg_hot <= 8'h2; // D1
+ 3'd2: alg_hot <= 8'h4; // D2
+ 3'd3: alg_hot <= 8'h8; // D3
+ 3'd4: alg_hot <= 8'h10; // D4
+ 3'd5: alg_hot <= 8'h20; // D5
+ 3'd6: alg_hot <= 8'h40; // D6
+ 3'd7: alg_hot <= 8'h80; // D7
+ endcase
+end
+
+always @(*) begin
+ use_prevprev1 <= s1_enters | (s3_enters&alg_hot[5]);
+ use_prev2 <= (s3_enters&(|alg_hot[2:0])) | (s4_enters&alg_hot[3]);
+ use_internal_x <= s4_enters & alg_hot[2];
+ use_internal_y <= s4_enters & (|{alg_hot[4:3],alg_hot[1:0]});
+ use_prev1 <= s1_enters | (s3_enters&alg_hot[1]) |
+ (s2_enters&(|{alg_hot[6:3],alg_hot[0]}) )|
+ (s4_enters&(|{alg_hot[5],alg_hot[2]}));
+end
+
+/*
+always @(*) begin
+ use_prevprev1 <= s1_enters || (s3_enters&&(alg_I==3'd5));
+ use_prev2 <= (s3_enters&&(alg_I<=3'd2)) || (s4_enters&&(alg_I==3'd3));
+ use_internal_x <= s4_enters && (alg_I==3'd2);
+ use_internal_y <= s4_enters && (alg_I<=3'd4 && alg_I!=3'd2);
+ use_prev1 <= s1_enters || (s3_enters&&(alg_I==3'd1)) ||
+ (s2_enters&&(alg_I==3'd0 ||
+ alg_I==3'd3 || alg_I==3'd4 ||
+ alg_I==3'd5 || alg_I==3'd6)) ||
+ (s4_enters&&(alg_I==3'd2 || alg_I==3'd5));
+end
+
+always @(*) begin
+case( {s1_enters, s3_enters, s2_enters, s4_enters} ) // synthesis parallel_case
+ 4'b1000: begin // S1
+ use_prevprev1 <= 1'b1;
+ use_prev2 <= 1'b0;
+ use_internal_x<= 1'b0;
+ use_internal_y<= 1'b0;
+ use_prev1 <= 1'b1;
+ end
+ 4'b0100: begin // S3
+ use_prevprev1 <= alg_I==3'd5;
+ use_prev2 <= (alg_I<=3'd2);
+ use_internal_x<= 1'b0;
+ use_internal_y<= 1'b0;
+ use_prev1 <= alg_I==3'd1;
+ end
+ 4'b0010: begin // S2
+ use_prevprev1 <= 1'b0;
+ use_prev2 <= 1'b0;
+ use_internal_x<= 1'b0;
+ use_prev1 <= (alg_I==3'd0 ||
+ alg_I==3'd3 || alg_I==3'd4 ||
+ alg_I==3'd5 || alg_I==3'd6 );
+ use_internal_y<= 1'b0;
+ end
+ 4'b0001: begin // S4
+ use_prevprev1 <= 1'b0;
+ use_prev2 <= ( alg_I==3'd3 );
+ use_internal_x <= alg_I==3'd2;
+ use_prev1 <= (alg_I==3'd2 || alg_I==3'd5);
+ use_internal_y <= ( alg_I<=3'd4 && alg_I!=3'd2);
+ end
+ default:begin
+ use_prevprev1 <= 1'bx;
+ use_prev2 <= 1'bx;
+ use_internal_x<= 1'bx;
+ use_prev1 <= 1'bx;
+ use_internal_y<= 1'bx;
+ end
+ endcase
+end
+*/
+
+endmodule
diff --git a/src/sound/jt12/jt12_op.v b/src/sound/jt12/jt12_op.v
new file mode 100644
index 0000000..9dfd931
--- /dev/null
+++ b/src/sound/jt12/jt12_op.v
@@ -0,0 +1,536 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+
+
+module jt12_op(
+ input rst,
+ input clk,
+ input [9:0] pg_phase_VIII,
+ input [9:0] eg_atten_IX, // output from envelope generator
+ input [2:0] fb_II, // voice feedback
+ input use_prevprev1,
+ input use_internal_x,
+ input use_internal_y,
+ input use_prev2,
+ input use_prev1,
+ input test_214,
+
+ input s1_enters,
+ input s2_enters,
+ input s3_enters,
+ input s4_enters,
+ input zero,
+
+ output signed [8:0] op_result
+);
+
+/* enters exits
+ S1 S2
+ S3 S4
+ S2 S1
+ S4 S3
+*/
+
+reg [13:0] op_result_internal, op_XII;
+reg [11:0] atten_internal_IX;
+
+assign op_result = op_result_internal[13:5];
+
+parameter NUM_VOICES = 6;
+
+reg signbit_IX, signbit_X, signbit_XI;
+reg [11:0] totalatten_X;
+
+wire [13:0] prev1, prevprev1, prev2;
+
+jt12_sh/*_rst*/ #( .width(14), .stages(NUM_VOICES)) prev1_buffer(
+// .rst ( rst ),
+ .clk ( clk ),
+ .din ( s2_enters ? op_result_internal : prev1 ),
+ .drop ( prev1 )
+);
+
+jt12_sh/*_rst*/ #( .width(14), .stages(NUM_VOICES)) prevprev1_buffer(
+// .rst ( rst ),
+ .clk ( clk ),
+ .din ( s2_enters ? prev1 : prevprev1 ),
+ .drop ( prevprev1 )
+);
+
+jt12_sh/*_rst*/ #( .width(14), .stages(NUM_VOICES)) prev2_buffer(
+// .rst ( rst ),
+ .clk ( clk ),
+ .din ( s1_enters ? op_result_internal : prev2 ),
+ .drop ( prev2 )
+);
+
+
+reg [18:0] stb;
+reg [10:0] stf, stg;
+reg [11:0] logsin;
+reg [10:0] subtresult;
+
+reg [12:0] etb;
+reg [ 9:0] etf, etg, mantissa_XI;
+reg [ 3:0] exponent_XI;
+
+reg [12:0] shifter, shifter_2, shifter_3;
+
+// REGISTER/CYCLE 1
+// Creation of phase modulation (FM) feedback signal, before shifting
+reg [13:0] x, y;
+reg [14:0] xs, ys, pm_preshift_II;
+reg s1_II;
+
+always @(*) begin
+
+ x <= ( {14{use_prevprev1}} & prevprev1 ) |
+ ( {14{use_internal_x}} & op_result_internal ) |
+ ( {14{use_prev2}} & prev2 );
+ y <= ( {14{use_prev1}} & prev1 ) |
+ ( {14{use_internal_y}} & op_result_internal );
+ xs <= { x[13], x }; // sign-extend
+ ys <= { y[13], y }; // sign-extend
+end
+
+always @(posedge clk) begin
+ pm_preshift_II <= xs + ys; // carry is discarded
+ s1_II <= s1_enters;
+end
+
+/* REGISTER/CYCLE 2-7 (also YM2612 extra cycles 1-6)
+ Shifting of FM feedback signal, adding phase from PG to FM phase
+ In YM2203, phasemod_II is not registered at all, it is latched on the first edge
+ in add_pg_phase and the second edge is the output of add_pg_phase. In the YM2612, there
+ are 6 cycles worth of registers between the generated (non-registered) phasemod_II signal
+ and the input to add_pg_phase. */
+
+reg [9:0] phasemod_II;
+wire [9:0] phasemod_VIII;
+
+always @(*) begin
+ // Shift FM feedback signal
+ if (!s1_II ) // Not S1
+ phasemod_II <= pm_preshift_II[10:1]; // Bit 0 of pm_preshift_II is never used
+ else // S1
+ case( fb_II )
+ 3'd0: phasemod_II <= 10'd0;
+ 3'd1: phasemod_II <= { {4{pm_preshift_II[14]}}, pm_preshift_II[14:9] };
+ 3'd2: phasemod_II <= { {3{pm_preshift_II[14]}}, pm_preshift_II[14:8] };
+ 3'd3: phasemod_II <= { {2{pm_preshift_II[14]}}, pm_preshift_II[14:7] };
+ 3'd4: phasemod_II <= { pm_preshift_II[14], pm_preshift_II[14:6] };
+ 3'd5: phasemod_II <= pm_preshift_II[14:5];
+ 3'd6: phasemod_II <= pm_preshift_II[13:4];
+ 3'd7: phasemod_II <= pm_preshift_II[12:3];
+ endcase
+end
+
+// REGISTER/CYCLE 2-7
+jt12_sh #( .width(10), .stages(NUM_VOICES)) phasemod_sh(
+ .clk ( clk ),
+ .din ( phasemod_II ),
+ .drop ( phasemod_VIII )
+);
+
+// REGISTER/CYCLE 8
+reg [ 9:0] phase;
+// Sets the maximum number of fanouts for a register or combinational
+// cell. The Quartus II software will replicate the cell and split
+// the fanouts among the duplicates until the fanout of each cell
+// is below the maximum.
+
+reg [ 7:0] phaselo_IX, aux_VIII;
+
+always @(*) begin
+ phase <= phasemod_VIII + pg_phase_VIII;
+ aux_VIII<= phase[7:0] ^ {8{~phase[8]}};
+end
+
+always @(posedge clk) begin
+ phaselo_IX <= aux_VIII;
+ signbit_IX <= phase[9];
+
+end
+
+wire [45:0] sta_IX;
+
+jt12_phrom u_phrom(
+ .clk ( clk ),
+ .addr ( aux_VIII[5:1] ),
+ .ph ( sta_IX )
+);
+
+// REGISTER/CYCLE 9
+// Sine table
+// Main sine table body
+
+
+always @(*) begin
+ //sta_IX <= sinetable[ phaselo_IX[5:1] ];
+ // 2-bit row chooser
+ case( phaselo_IX[7:6] )
+ 2'b00: stb <= { 10'b0, sta_IX[29], sta_IX[25], 2'b0, sta_IX[18],
+ sta_IX[14], 1'b0, sta_IX[7] , sta_IX[3] };
+ 2'b01: stb <= { 6'b0 , sta_IX[37], sta_IX[34], 2'b0, sta_IX[28],
+ sta_IX[24], 2'b0, sta_IX[17], sta_IX[13], sta_IX[10], sta_IX[6], sta_IX[2] };
+ 2'b10: stb <= { 2'b0, sta_IX[43], sta_IX[41], 2'b0, sta_IX[36],
+ sta_IX[33], 2'b0, sta_IX[27], sta_IX[23], 1'b0, sta_IX[20],
+ sta_IX[16], sta_IX[12], sta_IX[9], sta_IX[5], sta_IX[1] };
+ default: stb <= {
+ sta_IX[45], sta_IX[44], sta_IX[42], sta_IX[40]
+ , sta_IX[39], sta_IX[38], sta_IX[35], sta_IX[32]
+ , sta_IX[31], sta_IX[30], sta_IX[26], sta_IX[22]
+ , sta_IX[21], sta_IX[19], sta_IX[15], sta_IX[11]
+ , sta_IX[8], sta_IX[4], sta_IX[0] };
+ endcase
+ // Fixed value to sum
+ stf <= { stb[18:15], stb[12:11], stb[8:7], stb[4:3], stb[0] };
+ // Gated value to sum; bit 14 is indeed used twice
+ if( phaselo_IX[0] )
+ stg <= { 2'b0, stb[14], stb[14:13], stb[10:9], stb[6:5], stb[2:1] };
+ else
+ stg <= 11'd0;
+ // Sum to produce final logsin value
+ logsin <= stf + stg; // Carry-out of 11-bit addition becomes 12th bit
+ // Invert-subtract logsin value from EG attenuation value, with inverted carry
+ // In the actual chip, the output of the above logsin sum is already inverted.
+ // The two LSBs go through inverters (so they're non-inverted); the eg_atten_IX signal goes through inverters.
+ // The adder is normal except the carry-in is 1. It's a 10-bit adder.
+ // The outputs are inverted outputs, including the carry bit.
+ //subtresult <= not (('0' & not eg_atten_IX) - ('1' & logsin([11:2])));
+ // After a little pencil-and-paper, turns out this is equivalent to a regular adder!
+ subtresult <= eg_atten_IX + logsin[11:2];
+ // Place all but carry bit into result; also two LSBs of logsin
+ // If addition overflowed, make it the largest value (saturate)
+ atten_internal_IX <= { subtresult[9:0], logsin[1:0] } | {12{subtresult[10]}};
+end
+
+wire [44:0] exp_X;
+
+jt12_exprom u_exprom(
+ .clk ( clk ),
+ .addr ( atten_internal_IX[5:1] ),
+ .exp ( exp_X )
+);
+
+always @(posedge clk) begin
+ totalatten_X <= atten_internal_IX;
+ signbit_X <= signbit_IX;
+end
+
+//wire [1:0] et_sel = totalatten_X[7:6];
+//wire [4:0] et_fine = totalatten_X[5:1];
+
+// REGISTER/CYCLE 10
+// Exponential table
+// Main sine table body
+always @(*) begin
+ //eta <= explut_jt51[ totalatten_X[5:1] ];
+ // 2-bit row chooser
+ case( totalatten_X[7:6] )
+ 2'b00: begin
+ etf <= { 1'b1, exp_X[44:36] };
+ etg <= { 1'b1, exp_X[35:34] };
+ end
+ 2'b01: begin
+ etf <= exp_X[33:24];
+ etg <= { 2'b10, exp_X[23] };
+ end
+ 2'b10: begin
+ etf <= { 1'b0, exp_X[22:14] };
+ etg <= exp_X[13:11];
+ end
+ 2'b11: begin
+ etf <= { 2'b00, exp_X[10:3] };
+ etg <= exp_X[2:0];
+ end
+
+ endcase
+end
+
+always @(posedge clk) begin
+ //RESULT
+ mantissa_XI <= etf + ( totalatten_X[0] ? 3'd0 : etg ); //carry-out discarded
+ exponent_XI <= totalatten_X[11:8];
+ signbit_XI <= signbit_X;
+end
+
+// REGISTER/CYCLE 11
+// Introduce test bit as MSB, 2's complement & Carry-out discarded
+
+always @(*) begin
+ // Floating-point to integer, and incorporating sign bit
+ // Two-stage shifting of mantissa_XI by exponent_XI
+ shifter <= { 3'b001, mantissa_XI };
+ case( ~exponent_XI[1:0] )
+ 2'b00: shifter_2 <= { 1'b0, shifter[12:1] }; // LSB discarded
+ 2'b01: shifter_2 <= shifter;
+ 2'b10: shifter_2 <= { shifter[11:0], 1'b0 };
+ 2'b11: shifter_2 <= { shifter[10:0], 2'b0 };
+ endcase
+ case( ~exponent_XI[3:2] )
+ 2'b00: shifter_3 <= {12'b0, shifter_2[12] };
+ 2'b01: shifter_3 <= { 8'b0, shifter_2[12:8] };
+ 2'b10: shifter_3 <= { 4'b0, shifter_2[12:4] };
+ 2'b11: shifter_3 <= shifter_2;
+ endcase
+end
+
+always @(posedge clk) begin
+ // REGISTER CYCLE 11
+ op_XII <= ({ test_214, shifter_3 } ^ {14{signbit_XI}}) + signbit_XI;
+ // REGISTER CYCLE 12
+ // Extra register, take output after here
+ op_result_internal <= op_XII;
+end
+
+`ifdef SIMULATION
+reg [4:0] sep24_cnt;
+
+wire signed [13:0] op_ch0s1, op_ch1s1, op_ch2s1, op_ch3s1,
+ op_ch4s1, op_ch5s1, op_ch0s2, op_ch1s2,
+ op_ch2s2, op_ch3s2, op_ch4s2, op_ch5s2,
+ op_ch0s3, op_ch1s3, op_ch2s3, op_ch3s3,
+ op_ch4s3, op_ch5s3, op_ch0s4, op_ch1s4,
+ op_ch2s4, op_ch3s4, op_ch4s4, op_ch5s4;
+
+always @(posedge clk )
+ sep24_cnt <= !zero ? sep24_cnt+1'b1 : 5'd0;
+
+sep24 #( .width(14), .pos0(13)) opsep
+(
+ .clk ( clk ),
+ .mixed ( op_result_internal ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (op_ch0s1),
+ .ch1s1 (op_ch1s1),
+ .ch2s1 (op_ch2s1),
+ .ch3s1 (op_ch3s1),
+ .ch4s1 (op_ch4s1),
+ .ch5s1 (op_ch5s1),
+
+ .ch0s2 (op_ch0s2),
+ .ch1s2 (op_ch1s2),
+ .ch2s2 (op_ch2s2),
+ .ch3s2 (op_ch3s2),
+ .ch4s2 (op_ch4s2),
+ .ch5s2 (op_ch5s2),
+
+ .ch0s3 (op_ch0s3),
+ .ch1s3 (op_ch1s3),
+ .ch2s3 (op_ch2s3),
+ .ch3s3 (op_ch3s3),
+ .ch4s3 (op_ch4s3),
+ .ch5s3 (op_ch5s3),
+
+ .ch0s4 (op_ch0s4),
+ .ch1s4 (op_ch1s4),
+ .ch2s4 (op_ch2s4),
+ .ch3s4 (op_ch3s4),
+ .ch4s4 (op_ch4s4),
+ .ch5s4 (op_ch5s4)
+);
+
+wire signed [8:0] acc_ch0s1, acc_ch1s1, acc_ch2s1, acc_ch3s1,
+ acc_ch4s1, acc_ch5s1, acc_ch0s2, acc_ch1s2,
+ acc_ch2s2, acc_ch3s2, acc_ch4s2, acc_ch5s2,
+ acc_ch0s3, acc_ch1s3, acc_ch2s3, acc_ch3s3,
+ acc_ch4s3, acc_ch5s3, acc_ch0s4, acc_ch1s4,
+ acc_ch2s4, acc_ch3s4, acc_ch4s4, acc_ch5s4;
+
+sep24 #( .width(9), .pos0(13)) accsep
+(
+ .clk ( clk ),
+ .mixed ( op_result_internal[13:5] ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (acc_ch0s1),
+ .ch1s1 (acc_ch1s1),
+ .ch2s1 (acc_ch2s1),
+ .ch3s1 (acc_ch3s1),
+ .ch4s1 (acc_ch4s1),
+ .ch5s1 (acc_ch5s1),
+
+ .ch0s2 (acc_ch0s2),
+ .ch1s2 (acc_ch1s2),
+ .ch2s2 (acc_ch2s2),
+ .ch3s2 (acc_ch3s2),
+ .ch4s2 (acc_ch4s2),
+ .ch5s2 (acc_ch5s2),
+
+ .ch0s3 (acc_ch0s3),
+ .ch1s3 (acc_ch1s3),
+ .ch2s3 (acc_ch2s3),
+ .ch3s3 (acc_ch3s3),
+ .ch4s3 (acc_ch4s3),
+ .ch5s3 (acc_ch5s3),
+
+ .ch0s4 (acc_ch0s4),
+ .ch1s4 (acc_ch1s4),
+ .ch2s4 (acc_ch2s4),
+ .ch3s4 (acc_ch3s4),
+ .ch4s4 (acc_ch4s4),
+ .ch5s4 (acc_ch5s4)
+);
+
+wire signed [9:0] pm_ch0s1, pm_ch1s1, pm_ch2s1, pm_ch3s1,
+ pm_ch4s1, pm_ch5s1, pm_ch0s2, pm_ch1s2,
+ pm_ch2s2, pm_ch3s2, pm_ch4s2, pm_ch5s2,
+ pm_ch0s3, pm_ch1s3, pm_ch2s3, pm_ch3s3,
+ pm_ch4s3, pm_ch5s3, pm_ch0s4, pm_ch1s4,
+ pm_ch2s4, pm_ch3s4, pm_ch4s4, pm_ch5s4;
+
+
+sep24 #( .width(10), .pos0( 18 ) ) pmsep
+(
+ .clk ( clk ),
+ .mixed ( phasemod_VIII ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (pm_ch0s1),
+ .ch1s1 (pm_ch1s1),
+ .ch2s1 (pm_ch2s1),
+ .ch3s1 (pm_ch3s1),
+ .ch4s1 (pm_ch4s1),
+ .ch5s1 (pm_ch5s1),
+
+ .ch0s2 (pm_ch0s2),
+ .ch1s2 (pm_ch1s2),
+ .ch2s2 (pm_ch2s2),
+ .ch3s2 (pm_ch3s2),
+ .ch4s2 (pm_ch4s2),
+ .ch5s2 (pm_ch5s2),
+
+ .ch0s3 (pm_ch0s3),
+ .ch1s3 (pm_ch1s3),
+ .ch2s3 (pm_ch2s3),
+ .ch3s3 (pm_ch3s3),
+ .ch4s3 (pm_ch4s3),
+ .ch5s3 (pm_ch5s3),
+
+ .ch0s4 (pm_ch0s4),
+ .ch1s4 (pm_ch1s4),
+ .ch2s4 (pm_ch2s4),
+ .ch3s4 (pm_ch3s4),
+ .ch4s4 (pm_ch4s4),
+ .ch5s4 (pm_ch5s4)
+);
+
+wire [9:0] phase_ch0s1, phase_ch1s1, phase_ch2s1, phase_ch3s1,
+ phase_ch4s1, phase_ch5s1, phase_ch0s2, phase_ch1s2,
+ phase_ch2s2, phase_ch3s2, phase_ch4s2, phase_ch5s2,
+ phase_ch0s3, phase_ch1s3, phase_ch2s3, phase_ch3s3,
+ phase_ch4s3, phase_ch5s3, phase_ch0s4, phase_ch1s4,
+ phase_ch2s4, phase_ch3s4, phase_ch4s4, phase_ch5s4;
+
+
+sep24 #( .width(10), .pos0( 18 ) ) phsep
+(
+ .clk ( clk ),
+ .mixed ( phase ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (phase_ch0s1),
+ .ch1s1 (phase_ch1s1),
+ .ch2s1 (phase_ch2s1),
+ .ch3s1 (phase_ch3s1),
+ .ch4s1 (phase_ch4s1),
+ .ch5s1 (phase_ch5s1),
+
+ .ch0s2 (phase_ch0s2),
+ .ch1s2 (phase_ch1s2),
+ .ch2s2 (phase_ch2s2),
+ .ch3s2 (phase_ch3s2),
+ .ch4s2 (phase_ch4s2),
+ .ch5s2 (phase_ch5s2),
+
+ .ch0s3 (phase_ch0s3),
+ .ch1s3 (phase_ch1s3),
+ .ch2s3 (phase_ch2s3),
+ .ch3s3 (phase_ch3s3),
+ .ch4s3 (phase_ch4s3),
+ .ch5s3 (phase_ch5s3),
+
+ .ch0s4 (phase_ch0s4),
+ .ch1s4 (phase_ch1s4),
+ .ch2s4 (phase_ch2s4),
+ .ch3s4 (phase_ch3s4),
+ .ch4s4 (phase_ch4s4),
+ .ch5s4 (phase_ch5s4)
+);
+
+wire [9:0] eg_ch0s1, eg_ch1s1, eg_ch2s1, eg_ch3s1, eg_ch4s1, eg_ch5s1,
+ eg_ch0s2, eg_ch1s2, eg_ch2s2, eg_ch3s2, eg_ch4s2, eg_ch5s2,
+ eg_ch0s3, eg_ch1s3, eg_ch2s3, eg_ch3s3, eg_ch4s3, eg_ch5s3,
+ eg_ch0s4, eg_ch1s4, eg_ch2s4, eg_ch3s4, eg_ch4s4, eg_ch5s4;
+
+
+sep24 #( .width(10), .pos0(17) ) egsep
+(
+ .clk ( clk ),
+ .mixed ( eg_atten_IX ),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (eg_ch0s1),
+ .ch1s1 (eg_ch1s1),
+ .ch2s1 (eg_ch2s1),
+ .ch3s1 (eg_ch3s1),
+ .ch4s1 (eg_ch4s1),
+ .ch5s1 (eg_ch5s1),
+
+ .ch0s2 (eg_ch0s2),
+ .ch1s2 (eg_ch1s2),
+ .ch2s2 (eg_ch2s2),
+ .ch3s2 (eg_ch3s2),
+ .ch4s2 (eg_ch4s2),
+ .ch5s2 (eg_ch5s2),
+
+ .ch0s3 (eg_ch0s3),
+ .ch1s3 (eg_ch1s3),
+ .ch2s3 (eg_ch2s3),
+ .ch3s3 (eg_ch3s3),
+ .ch4s3 (eg_ch4s3),
+ .ch5s3 (eg_ch5s3),
+
+ .ch0s4 (eg_ch0s4),
+ .ch1s4 (eg_ch1s4),
+ .ch2s4 (eg_ch2s4),
+ .ch3s4 (eg_ch3s4),
+ .ch4s4 (eg_ch4s4),
+ .ch5s4 (eg_ch5s4)
+);
+
+`endif
+
+
+endmodule
diff --git a/src/sound/jt12/jt12_opram.v b/src/sound/jt12/jt12_opram.v
new file mode 100644
index 0000000..e2e3c63
--- /dev/null
+++ b/src/sound/jt12/jt12_opram.v
@@ -0,0 +1,79 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+
+module jt12_opram
+(
+ input [4:0] wr_addr,
+ input [4:0] rd_addr,
+ input clk,
+ input [43:0] data,
+ output reg [43:0] q
+);
+
+ reg [43:0] ram[31:0];
+ initial
+ begin
+ ram[0] = { ~7'd0, 37'd0 };
+ ram[1] = { ~7'd0, 37'd0 };
+ ram[2] = { ~7'd0, 37'd0 };
+ ram[3] = { ~7'd0, 37'd0 };
+ ram[4] = { ~7'd0, 37'd0 };
+ ram[5] = { ~7'd0, 37'd0 };
+ ram[6] = { ~7'd0, 37'd0 };
+ ram[7] = { ~7'd0, 37'd0 };
+ ram[8] = { ~7'd0, 37'd0 };
+ ram[9] = { ~7'd0, 37'd0 };
+ ram[10] = { ~7'd0, 37'd0 };
+ ram[11] = { ~7'd0, 37'd0 };
+ ram[12] = { ~7'd0, 37'd0 };
+ ram[13] = { ~7'd0, 37'd0 };
+ ram[14] = { ~7'd0, 37'd0 };
+ ram[15] = { ~7'd0, 37'd0 };
+ ram[16] = { ~7'd0, 37'd0 };
+ ram[17] = { ~7'd0, 37'd0 };
+ ram[18] = { ~7'd0, 37'd0 };
+ ram[19] = { ~7'd0, 37'd0 };
+ ram[20] = { ~7'd0, 37'd0 };
+ ram[21] = { ~7'd0, 37'd0 };
+ ram[22] = { ~7'd0, 37'd0 };
+ ram[23] = { ~7'd0, 37'd0 };
+ ram[24] = { ~7'd0, 37'd0 };
+ ram[25] = { ~7'd0, 37'd0 };
+ ram[26] = { ~7'd0, 37'd0 };
+ ram[27] = { ~7'd0, 37'd0 };
+ ram[28] = { ~7'd0, 37'd0 };
+ ram[29] = { ~7'd0, 37'd0 };
+ ram[30] = { ~7'd0, 37'd0 };
+ ram[31] = { ~7'd0, 37'd0 };
+ end
+
+ always @ (posedge clk) begin
+ q <= ram[rd_addr];
+ ram[wr_addr] <= data;
+ end
+
+endmodule
diff --git a/src/sound/jt12/jt12_pg.v b/src/sound/jt12/jt12_pg.v
new file mode 100644
index 0000000..cb73d0c
--- /dev/null
+++ b/src/sound/jt12/jt12_pg.v
@@ -0,0 +1,412 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 14-2-2016
+
+ Based on information posted by Nemesis on:
+http://gendev.spritesmind.net/forum/viewtopic.php?t=386&postdays=0&postorder=asc&start=167
+
+ Based on jt51_phasegen.v, from JT51
+
+ */
+
+`timescale 1ns / 1ps
+
+/*
+
+ tab size 4
+
+*/
+
+module jt12_pg(
+ input clk,
+ input rst,
+ // Channel frequency
+ input [10:0] fnum_I,
+ input [ 2:0] block_I,
+ // Operator multiplying
+ input [ 3:0] mul_V,
+ // Operator detuning
+ input [ 2:0] dt1_II, // same as JT51's DT1
+ // phase modulation from LFO
+ //input [ 7:0] pm,
+ //input [ 2:0] pms,
+ // phase operation
+ input pg_rst_III,
+ input zero,
+ input pg_stop,
+
+ output reg [ 4:0] keycode_III,
+ output reg [ 9:0] phase_VIII
+);
+
+/*
+reg signed [8:0] mod;
+
+always @(*) begin
+ case( pms ) // comprobar en silicio
+ 3'd0: mod <= 9'd0;
+ 3'd1: mod <= { 7'd0, pm[6:5] };
+ 3'd2: mod <= { 6'd0, pm[6:4] };
+ 3'd3: mod <= { 5'd0, pm[6:3] };
+ 3'd4: mod <= { 4'd0, pm[6:2] };
+ 3'd5: mod <= { 3'd0, pm[6:1] };
+ 3'd6: mod <= { 1'd0, pm[6:0], 1'b0 };
+ 3'd7: mod <= { pm[6:0], 2'b0 };
+ endcase
+end
+
+jt12_pm u_pm(
+ // Channel frequency
+ .kc(kc),
+ .kf(kf),
+ .add(~pm[7]),
+ .mod(mod),
+ .kcex(keycode_I)
+);
+*/
+
+wire pg_rst_VI;
+
+//////////////////////////////////////////////////
+// I
+reg [4:0] keycode_II;
+reg [16:0] phinc_II;
+
+always @(posedge clk) begin : phase_calculation_I
+ case ( block_I )
+ 3'd0: phinc_II <= { 7'd0, fnum_I[10:1] };
+ 3'd1: phinc_II <= { 6'd0, fnum_I };
+ 3'd2: phinc_II <= { 5'd0, fnum_I, 1'd0 };
+ 3'd3: phinc_II <= { 4'd0, fnum_I, 2'd0 };
+ 3'd4: phinc_II <= { 3'd0, fnum_I, 3'd0 };
+ 3'd5: phinc_II <= { 2'd0, fnum_I, 4'd0 };
+ 3'd6: phinc_II <= { 1'd0, fnum_I, 5'd0 };
+ 3'd7: phinc_II <= { fnum_I, 6'd0 };
+ endcase
+ keycode_II <= { block_I, fnum_I[10], fnum_I[10] ? (|fnum_I[9:7]) : (&fnum_I[9:7])};
+end
+
+//////////////////////////////////////////////////
+// II
+reg [ 5:0] dt1_kf_III;
+reg [16:0] phinc_III;
+reg [ 2:0] dt1_III;
+
+always @(posedge clk) begin : phase_calculation_II
+ case( dt1_II[1:0] )
+ 2'd1: dt1_kf_III <= { 1'b0, keycode_II } - 6'd4;
+ 2'd2: dt1_kf_III <= { 1'b0, keycode_II } + 6'd4;
+ 2'd3: dt1_kf_III <= { 1'b0, keycode_II } + 6'd8;
+ default:dt1_kf_III <= { 1'b0, keycode_II };
+ endcase
+ dt1_III <= dt1_II;
+ phinc_III <= phinc_II;
+ keycode_III <= keycode_II;
+end
+
+//////////////////////////////////////////////////
+// III
+reg [16:0] phinc_IV;
+reg [ 4:0] pow2;
+reg [ 2:0] dt1_IV;
+
+always @(*) begin : calcpow2
+ case( dt1_kf_III[2:0] )
+ 3'd0: pow2 <= 5'd16;
+ 3'd1: pow2 <= 5'd17;
+ 3'd2: pow2 <= 5'd19;
+ 3'd3: pow2 <= 5'd20;
+ 3'd4: pow2 <= 5'd22;
+ 3'd5: pow2 <= 5'd24;
+ 3'd6: pow2 <= 5'd26;
+ 3'd7: pow2 <= 5'd29;
+ endcase
+end
+
+reg [5:0] dt1_unlimited;
+reg [4:0] dt1_limit, dt1_offset_IV;
+
+always @(*) begin : dt1_limit_mux
+ case( dt1_III[1:0] )
+ default: dt1_limit <= 5'd8;
+ 2'd1: dt1_limit <= 5'd8;
+ 2'd2: dt1_limit <= 5'd16;
+ 2'd3: dt1_limit <= 5'd22;
+ endcase
+ case( dt1_kf_III[5:3] )
+ 3'd0: dt1_unlimited <= { 5'd0, pow2[4] }; // <2
+ 3'd1: dt1_unlimited <= { 4'd0, pow2[4:3] }; // <4
+ 3'd2: dt1_unlimited <= { 3'd0, pow2[4:2] }; // <8
+ 3'd3: dt1_unlimited <= { 2'd0, pow2[4:1] };
+ 3'd4: dt1_unlimited <= { 1'd0, pow2[4:0] };
+ 3'd5: dt1_unlimited <= { pow2[4:0], 1'd0 };
+ default:dt1_unlimited <= 6'd0;
+ endcase
+end
+
+always @(posedge clk) begin : phase_calculation_III
+ dt1_offset_IV <= dt1_unlimited > dt1_limit ?
+ dt1_limit : dt1_unlimited[4:0];
+ dt1_IV <= dt1_III;
+ phinc_IV <= phinc_III;
+end
+
+//////////////////////////////////////////////////
+// IV
+reg [16:0] phinc_V;
+
+always @(posedge clk) begin : phase_calculation_IV
+ if( dt1_IV[1:0]==2'd0 )
+ phinc_V <= phinc_IV;
+ else begin
+ if( !dt1_IV[2] )
+ phinc_V <= phinc_IV + dt1_offset_IV;
+ else
+ phinc_V <= phinc_IV - dt1_offset_IV;
+ end
+end
+
+//////////////////////////////////////////////////
+// V APPLY_MUL
+reg [16:0] phinc_VI;
+always @(posedge clk) begin : phase_calculation_V
+ if( mul_V==4'd0 )
+ phinc_VI <= { 1'b0, phinc_V[16:1] };
+ else
+ phinc_VI <= phinc_V * mul_V;
+end
+
+//////////////////////////////////////////////////
+// VI add phinc to the phase
+wire keyon_VI;
+wire [19:0] phase_drop;
+reg [19:0] phase_in;
+reg [ 9:0] phase_VII;
+
+always @(*)
+ phase_in <= pg_rst_VI ? 20'd0 :
+ ( pg_stop ? phase_drop : phase_drop + phinc_VI);
+
+always @(posedge clk) begin : phase_calculation_VI
+ phase_VII <= phase_in[19:10];
+end
+
+//////////////////////////////////////////////////
+// VIII padding
+
+always @(posedge clk)
+ phase_VIII <= phase_VII;
+
+jt12_sh #( .width(20), .stages(24) ) u_phsh(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( phase_in ),
+ .drop ( phase_drop)
+);
+
+jt12_sh #( .width(1), .stages(3) ) u_rstsh(
+ .clk ( clk ),
+ .din ( pg_rst_III),
+ .drop ( pg_rst_VI )
+);
+
+
+
+`ifdef SIMULATION
+reg [4:0] sep24_cnt;
+
+wire [9:0] pg_ch0s1, pg_ch1s1, pg_ch2s1, pg_ch3s1,
+ pg_ch4s1, pg_ch5s1, pg_ch0s2, pg_ch1s2,
+ pg_ch2s2, pg_ch3s2, pg_ch4s2, pg_ch5s2,
+ pg_ch0s3, pg_ch1s3, pg_ch2s3, pg_ch3s3,
+ pg_ch4s3, pg_ch5s3, pg_ch0s4, pg_ch1s4,
+ pg_ch2s4, pg_ch3s4, pg_ch4s4, pg_ch5s4;
+
+always @(posedge clk)
+ sep24_cnt <= !zero ? sep24_cnt+1'b1 : 5'd0;
+
+sep24 #( .width(10), .pos0(18)) stsep
+(
+ .clk ( clk ),
+ .mixed ( phase_VIII),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (pg_ch0s1),
+ .ch1s1 (pg_ch1s1),
+ .ch2s1 (pg_ch2s1),
+ .ch3s1 (pg_ch3s1),
+ .ch4s1 (pg_ch4s1),
+ .ch5s1 (pg_ch5s1),
+
+ .ch0s2 (pg_ch0s2),
+ .ch1s2 (pg_ch1s2),
+ .ch2s2 (pg_ch2s2),
+ .ch3s2 (pg_ch3s2),
+ .ch4s2 (pg_ch4s2),
+ .ch5s2 (pg_ch5s2),
+
+ .ch0s3 (pg_ch0s3),
+ .ch1s3 (pg_ch1s3),
+ .ch2s3 (pg_ch2s3),
+ .ch3s3 (pg_ch3s3),
+ .ch4s3 (pg_ch4s3),
+ .ch5s3 (pg_ch5s3),
+
+ .ch0s4 (pg_ch0s4),
+ .ch1s4 (pg_ch1s4),
+ .ch2s4 (pg_ch2s4),
+ .ch3s4 (pg_ch3s4),
+ .ch4s4 (pg_ch4s4),
+ .ch5s4 (pg_ch5s4)
+);
+
+wire [16:0] phinc_ch0s1, phinc_ch1s1, phinc_ch2s1, phinc_ch3s1,
+ phinc_ch4s1, phinc_ch5s1, phinc_ch0s2, phinc_ch1s2,
+ phinc_ch2s2, phinc_ch3s2, phinc_ch4s2, phinc_ch5s2,
+ phinc_ch0s3, phinc_ch1s3, phinc_ch2s3, phinc_ch3s3,
+ phinc_ch4s3, phinc_ch5s3, phinc_ch0s4, phinc_ch1s4,
+ phinc_ch2s4, phinc_ch3s4, phinc_ch4s4, phinc_ch5s4;
+
+sep24 #( .width(17), .pos0(3+6)) pisep
+(
+ .clk ( clk ),
+ .mixed ( phinc_VI),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (phinc_ch0s1),
+ .ch1s1 (phinc_ch1s1),
+ .ch2s1 (phinc_ch2s1),
+ .ch3s1 (phinc_ch3s1),
+ .ch4s1 (phinc_ch4s1),
+ .ch5s1 (phinc_ch5s1),
+
+ .ch0s2 (phinc_ch0s2),
+ .ch1s2 (phinc_ch1s2),
+ .ch2s2 (phinc_ch2s2),
+ .ch3s2 (phinc_ch3s2),
+ .ch4s2 (phinc_ch4s2),
+ .ch5s2 (phinc_ch5s2),
+
+ .ch0s3 (phinc_ch0s3),
+ .ch1s3 (phinc_ch1s3),
+ .ch2s3 (phinc_ch2s3),
+ .ch3s3 (phinc_ch3s3),
+ .ch4s3 (phinc_ch4s3),
+ .ch5s3 (phinc_ch5s3),
+
+ .ch0s4 (phinc_ch0s4),
+ .ch1s4 (phinc_ch1s4),
+ .ch2s4 (phinc_ch2s4),
+ .ch3s4 (phinc_ch3s4),
+ .ch4s4 (phinc_ch4s4),
+ .ch5s4 (phinc_ch5s4)
+);
+
+wire [10:0] fnum_ch0s1, fnum_ch1s1, fnum_ch2s1, fnum_ch3s1,
+ fnum_ch4s1, fnum_ch5s1, fnum_ch0s2, fnum_ch1s2,
+ fnum_ch2s2, fnum_ch3s2, fnum_ch4s2, fnum_ch5s2,
+ fnum_ch0s3, fnum_ch1s3, fnum_ch2s3, fnum_ch3s3,
+ fnum_ch4s3, fnum_ch5s3, fnum_ch0s4, fnum_ch1s4,
+ fnum_ch2s4, fnum_ch3s4, fnum_ch4s4, fnum_ch5s4;
+
+sep24 #( .width(11), .pos0(3+1)) fnsep
+(
+ .clk ( clk ),
+ .mixed ( fnum_I),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (fnum_ch0s1),
+ .ch1s1 (fnum_ch1s1),
+ .ch2s1 (fnum_ch2s1),
+ .ch3s1 (fnum_ch3s1),
+ .ch4s1 (fnum_ch4s1),
+ .ch5s1 (fnum_ch5s1),
+
+ .ch0s2 (fnum_ch0s2),
+ .ch1s2 (fnum_ch1s2),
+ .ch2s2 (fnum_ch2s2),
+ .ch3s2 (fnum_ch3s2),
+ .ch4s2 (fnum_ch4s2),
+ .ch5s2 (fnum_ch5s2),
+
+ .ch0s3 (fnum_ch0s3),
+ .ch1s3 (fnum_ch1s3),
+ .ch2s3 (fnum_ch2s3),
+ .ch3s3 (fnum_ch3s3),
+ .ch4s3 (fnum_ch4s3),
+ .ch5s3 (fnum_ch5s3),
+
+ .ch0s4 (fnum_ch0s4),
+ .ch1s4 (fnum_ch1s4),
+ .ch2s4 (fnum_ch2s4),
+ .ch3s4 (fnum_ch3s4),
+ .ch4s4 (fnum_ch4s4),
+ .ch5s4 (fnum_ch5s4)
+);
+
+wire pgrst_III_ch0s1, pgrst_III_ch1s1, pgrst_III_ch2s1, pgrst_III_ch3s1,
+ pgrst_III_ch4s1, pgrst_III_ch5s1, pgrst_III_ch0s2, pgrst_III_ch1s2,
+ pgrst_III_ch2s2, pgrst_III_ch3s2, pgrst_III_ch4s2, pgrst_III_ch5s2,
+ pgrst_III_ch0s3, pgrst_III_ch1s3, pgrst_III_ch2s3, pgrst_III_ch3s3,
+ pgrst_III_ch4s3, pgrst_III_ch5s3, pgrst_III_ch0s4, pgrst_III_ch1s4,
+ pgrst_III_ch2s4, pgrst_III_ch3s4, pgrst_III_ch4s4, pgrst_III_ch5s4;
+
+sep24 #( .width(1), .pos0(23)) pgrstsep
+(
+ .clk ( clk ),
+ .mixed ( pg_rst_III),
+ .mask ( 0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (pgrst_III_ch0s1),
+ .ch1s1 (pgrst_III_ch1s1),
+ .ch2s1 (pgrst_III_ch2s1),
+ .ch3s1 (pgrst_III_ch3s1),
+ .ch4s1 (pgrst_III_ch4s1),
+ .ch5s1 (pgrst_III_ch5s1),
+
+ .ch0s2 (pgrst_III_ch0s2),
+ .ch1s2 (pgrst_III_ch1s2),
+ .ch2s2 (pgrst_III_ch2s2),
+ .ch3s2 (pgrst_III_ch3s2),
+ .ch4s2 (pgrst_III_ch4s2),
+ .ch5s2 (pgrst_III_ch5s2),
+
+ .ch0s3 (pgrst_III_ch0s3),
+ .ch1s3 (pgrst_III_ch1s3),
+ .ch2s3 (pgrst_III_ch2s3),
+ .ch3s3 (pgrst_III_ch3s3),
+ .ch4s3 (pgrst_III_ch4s3),
+ .ch5s3 (pgrst_III_ch5s3),
+
+ .ch0s4 (pgrst_III_ch0s4),
+ .ch1s4 (pgrst_III_ch1s4),
+ .ch2s4 (pgrst_III_ch2s4),
+ .ch3s4 (pgrst_III_ch3s4),
+ .ch4s4 (pgrst_III_ch4s4),
+ .ch5s4 (pgrst_III_ch5s4)
+);
+
+
+`endif
+
+endmodule
+
diff --git a/src/sound/jt12/jt12_phrom.v b/src/sound/jt12/jt12_phrom.v
new file mode 100644
index 0000000..2ca926c
--- /dev/null
+++ b/src/sound/jt12/jt12_phrom.v
@@ -0,0 +1,77 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+// altera message_off 10030
+
+module jt12_phrom
+(
+ input [4:0] addr,
+ input clk,
+ output reg [45:0] ph
+);
+
+ reg [45:0] sinetable[31:0];
+ initial
+ begin
+ sinetable[5'd0 ] <= 46'b0001100000100100010001000010101010101001010010;
+ sinetable[5'd1 ] <= 46'b0001100000110100000100000010010001001101000001;
+ sinetable[5'd2 ] <= 46'b0001100000110100000100110010001011001101100000;
+ sinetable[5'd3 ] <= 46'b0001110000010000000000110010110001001101110010;
+ sinetable[5'd4 ] <= 46'b0001110000010000001100000010111010001101101001;
+ sinetable[5'd5 ] <= 46'b0001110000010100001001100010000000101101111010;
+ sinetable[5'd6 ] <= 46'b0001110000010100001101100010010011001101011010;
+ sinetable[5'd7 ] <= 46'b0001110000011100000101010010111000101111111100;
+ sinetable[5'd8 ] <= 46'b0001110000111000000001110010101110001101110111;
+ sinetable[5'd9 ] <= 46'b0001110000111000010100111000011101011010100110;
+ sinetable[5'd10] <= 46'b0001110000111100011000011000111100001001111010;
+ sinetable[5'd11] <= 46'b0001110000111100011100111001101011001001110111;
+ sinetable[5'd12] <= 46'b0100100001010000010001011001001000111010110111;
+ sinetable[5'd13] <= 46'b0100100001010100010001001001110001111100101010;
+ sinetable[5'd14] <= 46'b0100100001010100010101101101111110100101000110;
+ sinetable[5'd15] <= 46'b0100100011100000001000011001010110101101111001;
+ sinetable[5'd16] <= 46'b0100100011100100001000101011100101001011101111;
+ sinetable[5'd17] <= 46'b0100100011101100000111011010000001011010110001;
+ sinetable[5'd18] <= 46'b0100110011001000000111101010000010111010111111;
+ sinetable[5'd19] <= 46'b0100110011001100001011011110101110110110000001;
+ sinetable[5'd20] <= 46'b0100110011101000011010111011001010001101110001;
+ sinetable[5'd21] <= 46'b0100110011101101011010110101111001010100001111;
+ sinetable[5'd22] <= 46'b0111000010000001010111000101010101010110010111;
+ sinetable[5'd23] <= 46'b0111000010000101010111110111110101010010111011;
+ sinetable[5'd24] <= 46'b0111000010110101101000101100001000010000011001;
+ sinetable[5'd25] <= 46'b0111010010011001100100011110100100010010010010;
+ sinetable[5'd26] <= 46'b0111010010111010100101100101000000110100100011;
+ sinetable[5'd27] <= 46'b1010000010011010101101011101100001110010011010;
+ sinetable[5'd28] <= 46'b1010000010111111111100100111010100010000111001;
+ sinetable[5'd29] <= 46'b1010010111110100110010001100111001010110100000;
+ sinetable[5'd30] <= 46'b1011010111010011111011011110000100110010100001;
+ sinetable[5'd31] <= 46'b1110011011110001111011100111100001110110100111;
+
+ end
+
+ always @ (posedge clk)
+ ph <= sinetable[addr];
+
+endmodule
diff --git a/src/sound/jt12/jt12_reg.v b/src/sound/jt12/jt12_reg.v
new file mode 100644
index 0000000..7295871
--- /dev/null
+++ b/src/sound/jt12/jt12_reg.v
@@ -0,0 +1,363 @@
+`timescale 1ns / 1ps
+
+
+/* This file is part of JT12.
+
+
+ JT12 program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 14-2-2017
+
+*/
+
+
+module jt12_reg(
+ input rst,
+ input clk,
+ input [7:0] din,
+
+ input [2:0] ch,
+ input [1:0] op,
+
+ input csm,
+ input flag_A,
+ input overflow_A,
+
+ input up_keyon,
+ input up_alg,
+ input up_block,
+ input up_fnumlo,
+ input up_pms,
+ input up_dt1,
+ input up_tl,
+ input up_ks_ar,
+ input up_amen_d1r,
+ input up_d2r,
+
+ input up_d1l,
+ input up_ssgeg,
+
+ output busy,
+ output reg ch6op, // 1 when the operator belongs to CH6
+
+ // CH3 Effect-mode operation
+ input effect,
+ input [10:0] fnum_ch3op2,
+ input [10:0] fnum_ch3op3,
+ input [10:0] fnum_ch3op1,
+ input [ 2:0] block_ch3op2,
+ input [ 2:0] block_ch3op3,
+ input [ 2:0] block_ch3op1,
+ // Pipeline order
+ output reg zero,
+ output s1_enters,
+ output s2_enters,
+ output s3_enters,
+ output s4_enters,
+
+ // Operator
+ output use_prevprev1,
+ output use_internal_x,
+ output use_internal_y,
+ output use_prev2,
+ output use_prev1,
+
+ // PG
+ output [10:0] fnum_I,
+ output [ 2:0] block_I,
+ // channel configuration
+ output [1:0] rl,
+ output reg [2:0] fb_II,
+ output [2:0] alg,
+ // Operator multiplying
+ output [ 3:0] mul_V,
+ // Operator detuning
+ output [ 2:0] dt1_II,
+
+ // EG
+ output [4:0] ar_II, // attack rate
+ output [4:0] d1r_II, // decay rate
+ output [4:0] d2r_II, // sustain rate
+ output [3:0] rr_II, // release rate
+ output [3:0] d1l, // sustain level
+ output [1:0] ks_III, // key scale
+ output ssg_en_II,
+ output [2:0] ssg_eg_II,
+ output [6:0] tl_VII,
+ output [2:0] pms,
+ output [1:0] ams_VII,
+ output amsen_VII,
+
+ // envelope operation
+ output keyon_II
+);
+
+
+reg [4:0] cnt;
+reg [1:0] next_op, cur_op;
+reg [2:0] next_ch, cur_ch;
+
+assign s1_enters = cur_op == 2'b00;
+assign s3_enters = cur_op == 2'b01;
+assign s2_enters = cur_op == 2'b10;
+assign s4_enters = cur_op == 2'b11;
+
+wire [4:0] next = { next_op, next_ch };
+wire [4:0] cur = { cur_op, cur_ch };
+
+wire [2:0] fb_I;
+
+always @(posedge clk) begin
+ fb_II <= fb_I;
+ ch6op <= next_ch==3'd6;
+end
+
+// FNUM and BLOCK
+wire [10:0] fnum_I_raw;
+wire [ 2:0] block_I_raw;
+wire effect_on = effect && (cur_ch==3'd2);
+wire effect_on_s1 = effect_on && (cur_op == 2'd0 );
+wire effect_on_s3 = effect_on && (cur_op == 2'd1 );
+wire effect_on_s2 = effect_on && (cur_op == 2'd2 );
+wire noeffect = ~|{effect_on_s1, effect_on_s3, effect_on_s2};
+assign fnum_I = ( {11{effect_on_s1}} & fnum_ch3op1 ) |
+ ( {11{effect_on_s2}} & fnum_ch3op2 ) |
+ ( {11{effect_on_s3}} & fnum_ch3op3 ) |
+ ( {11{noeffect}} & fnum_I_raw );
+
+assign block_I =( {3{effect_on_s1}} & block_ch3op1 ) |
+ ( {3{effect_on_s2}} & block_ch3op2 ) |
+ ( {3{effect_on_s3}} & block_ch3op3 ) |
+ ( {3{noeffect}} & block_I_raw );
+
+wire [2:0] ch_II, ch_III, ch_IV, ch_V, ch_VI;
+
+wire [4:0] req_opch_I = { op, ch };
+wire [4:0] req_opch_II, req_opch_III,
+ req_opch_IV, req_opch_V, req_opch_VI;
+
+jt12_sumch u_opch_II ( .chin(req_opch_I ), .chout(req_opch_II) );
+jt12_sumch u_opch_III( .chin(req_opch_II ), .chout(req_opch_III) );
+jt12_sumch u_opch_IV ( .chin(req_opch_III), .chout(req_opch_IV) );
+jt12_sumch u_opch_V ( .chin(req_opch_IV ), .chout(req_opch_V) );
+jt12_sumch u_opch_VI ( .chin(req_opch_V ), .chout(req_opch_VI) );
+
+wire update_op_I = cur == req_opch_I;
+wire update_op_II = cur == req_opch_II;
+wire update_op_III= cur == req_opch_III;
+// wire update_op_IV = cur == opch_IV;
+wire update_op_V = cur == req_opch_V;
+// wire update_op_VI = cur == opch_VI;
+wire [2:0] op_plus1 = op+2'd1;
+wire update_op_VII= cur == { op_plus1[1:0], ch };
+
+// key on/off
+wire [3:0] keyon_op = din[7:4];
+wire [2:0] keyon_ch = din[2:0];
+// channel data
+wire [1:0] rl_in = din[7:6];
+wire [2:0] fb_in = din[5:3];
+wire [2:0] alg_in = din[2:0];
+wire [2:0] pms_in = din[2:0];
+wire [1:0] ams_in = din[5:4];
+wire [2:0] block_in= din[5:3];
+wire [2:0] fnhi_in = din[2:0];
+wire [7:0] fnlo_in = din;
+// operator data
+wire [2:0] dt1_in = din[6:4];
+wire [3:0] mul_in = din[3:0];
+wire [6:0] tl_in = din[6:0];
+wire [1:0] ks_in = din[7:6];
+wire [4:0] ar_in = din[4:0];
+wire amen_in = din[7];
+wire [4:0] d1r_in = din[4:0];
+wire [4:0] d2r_in = din[4:0];
+wire [3:0] d1l_in = din[7:4];
+wire [3:0] rr_in = din[3:0];
+wire [3:0] ssg_in = din[3:0];
+
+wire [3:0] ssg;
+
+reg last;
+
+wire update_ch_I = cur_ch == ch;
+
+wire up_alg_ch = up_alg & update_ch_I;
+wire up_block_ch= up_block & update_ch_I;
+wire up_fnumlo_ch=up_fnumlo & update_ch_I;
+wire up_pms_ch = up_pms & update_ch_I;
+
+// DT1 & MUL
+wire up_dt1_op = up_dt1 & update_op_II;
+wire up_mul_op = up_dt1 & update_op_V;
+// TL
+wire up_tl_op = up_tl & update_op_VII;
+// KS & AR
+wire up_ks_op = up_ks_ar & update_op_III;
+wire up_ar_op = up_ks_ar & update_op_II;
+// AM ON, D1R
+wire up_amen_op = up_amen_d1r & update_op_VII;
+wire up_d1r_op = up_amen_d1r & update_op_II;
+// Sustain Rate (D2R)
+wire up_d2r_op = up_d2r & update_op_II;
+// D1L & RR
+wire up_d1l_op = up_d1l & update_op_I;
+wire up_rr_op = up_d1l & update_op_II;
+// SSG
+//wire up_ssgen_op = up_ssgeg & update_op_I;
+wire up_ssg_op = up_ssgeg & update_op_II;
+
+wire up = up_alg | up_block | up_fnumlo | up_pms |
+ up_dt1 | up_tl | up_ks_ar | up_amen_d1r |
+ up_d2r | up_d1l | up_ssgeg | up_keyon;
+
+always @(*) begin
+ // next <= cur==5'd23 ? 5'd0 : cur +1'b1;
+ next_op <= cur_ch==3'd6 ? cur_op+1'b1 : cur_op;
+ next_ch <= cur_ch[1:0]==2'b10 ? cur_ch+2'd2 : cur_ch+1'd1;
+end
+
+reg busy_op;
+reg up_keyon_long;
+
+assign busy = busy_op;
+
+
+always @(posedge clk) begin : up_counter
+ if( rst ) begin
+ cnt <= 5'h0;
+ last <= 1'b0;
+ zero <= 1'b1;
+ busy_op <= 1'b0;
+ up_keyon_long <= 1'b0;
+ cur_op <= 2'd0;
+ cur_ch <= 3'd0;
+ end
+ else begin
+ { cur_op, cur_ch } <= { next_op, next_ch };
+ zero <= next == 5'd0;
+ last <= up;
+ if( up && !last ) begin
+ cnt <= cur;
+ busy_op <= 1'b1;
+ up_keyon_long <= up_keyon;
+ end
+ else if( cnt == cur ) begin
+ busy_op <= 1'b0;
+ up_keyon_long <= 1'b0;
+ end
+ end
+end
+
+jt12_kon u_kon(
+ .rst ( rst ),
+ .clk ( clk ),
+ .keyon_op ( keyon_op ),
+ .keyon_ch ( keyon_ch ),
+ .cur_op ( cur_op ),
+ .cur_ch ( cur_ch ),
+ .up_keyon ( up_keyon_long ),
+ .csm ( csm ),
+ .flag_A ( flag_A ),
+ .overflow_A ( overflow_A),
+
+ .keyon_II ( keyon_II )
+);
+
+jt12_mod u_mod(
+ .alg_I ( alg ),
+ .s1_enters ( s1_enters ),
+ .s3_enters ( s3_enters ),
+ .s2_enters ( s2_enters ),
+ .s4_enters ( s4_enters ),
+
+ .use_prevprev1 ( use_prevprev1 ),
+ .use_internal_x( use_internal_x ),
+ .use_internal_y( use_internal_y ),
+ .use_prev2 ( use_prev2 ),
+ .use_prev1 ( use_prev1 )
+);
+
+// memory for OP registers
+parameter regop_width=44;
+
+wire [regop_width-1:0] regop_in, regop_out;
+
+jt12_opram u_opram(
+ .clk ( clk ),
+ .wr_addr ( cur ),
+ .rd_addr ( next ),
+ .data ( regop_in ),
+ .q ( regop_out )
+);
+
+assign regop_in = {
+ up_tl_op ? tl_in : tl_VII, // 7
+ up_dt1_op ? dt1_in : dt1_II, // 3
+ up_mul_op ? mul_in : mul_V, // 4 - 7
+ up_ks_op ? ks_in : ks_III, // 2 - 16
+ up_ar_op ? ar_in : ar_II, // 5 - 21
+ up_amen_op ? amen_in: amsen_VII,// 1 - 22
+ up_d1r_op ? d1r_in : d1r_II, // 5 - 25
+ up_d2r_op ? d2r_in : d2r_II, // 5 - 30
+ up_d1l_op ? d1l_in : d1l, // 4 - 34
+ up_rr_op ? rr_in : rr_II, // 4 - 38
+ up_ssg_op ? ssg_in[3] : ssg_en_II, // 1 - 39
+ up_ssg_op ? ssg_in[2:0] : ssg_eg_II // 3 - 42
+};
+
+assign { tl_VII, dt1_II, mul_V, ks_III,
+ ar_II, amsen_VII, d1r_II, d2r_II, d1l, rr_II,
+ ssg_en_II, ssg_eg_II } = regop_out;
+
+
+wire [2:0] block_latch, fnum_latch;
+
+// memory for CH registers
+// Block/fnum data is latched until fnum low byte is written to
+// Trying to synthesize this memory as M-9K RAM in Altera devices
+// turns out worse in terms of resource utilization. Probably because
+// this memory is already very small. It is better to leave it as it is.
+parameter regch_width=31;
+wire [regch_width-1:0] regch_out;
+wire [regch_width-1:0] regch_in = {
+ up_block_ch ? { block_in, fnhi_in } : { block_latch, fnum_latch }, // 3+3
+ up_fnumlo_ch? { block_latch, fnum_latch, fnlo_in } : { block_I_raw, fnum_I_raw }, // 14
+ up_alg_ch ? { fb_in, alg_in } : { fb_I, alg },//3+3
+ up_pms_ch ? { ams_in, pms_in } : { ams_VII, pms }//2+2+3
+};
+
+assign { block_latch, fnum_latch,
+ block_I_raw, fnum_I_raw,
+ fb_I, alg, ams_VII, pms } = regch_out;
+
+jt12_sh #(.width(regch_width),.stages(6)) u_regch(
+ .clk ( clk ),
+ //.rst ( rst ),
+ .din ( regch_in ),
+ .drop ( regch_out )
+);
+
+// RL is on a different register to
+// have the reset to 1
+jt12_sh_rst #(.width(2),.stages(6),.rstval(1'b1)) u_regch_rl(
+ .clk ( clk ),
+// .rst ( rst ),
+ .din ( up_pms_ch ? rl_in : rl ),
+ .drop ( rl )
+);
+
+endmodule
diff --git a/src/sound/jt12/jt12_sh.v b/src/sound/jt12/jt12_sh.v
new file mode 100644
index 0000000..ac2aeb9
--- /dev/null
+++ b/src/sound/jt12/jt12_sh.v
@@ -0,0 +1,45 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 1-31-2017
+ */
+
+`timescale 1ns / 1ps
+
+module jt12_sh #(parameter width=5, stages=24 )
+(
+ input clk,
+ input [width-1:0] din,
+ output [width-1:0] drop
+);
+
+reg [stages-1:0] bits[width-1:0];
+
+genvar i;
+generate
+ for (i=0; i < width; i=i+1) begin: bit_shifter
+ always @(posedge clk) begin
+ if( stages> 1 )
+ bits[i] <= {bits[i][stages-2:0], din[i]};
+ else
+ bits[i] <= din[i];
+ end
+ assign drop[i] = bits[i][stages-1];
+ end
+endgenerate
+
+endmodule
diff --git a/src/sound/jt12/jt12_sh24.v b/src/sound/jt12/jt12_sh24.v
new file mode 100644
index 0000000..dfb1596
--- /dev/null
+++ b/src/sound/jt12/jt12_sh24.v
@@ -0,0 +1,80 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 1-31-2017
+ */
+
+`timescale 1ns / 1ps
+
+module jt12_sh24 #(parameter width=5 )
+(
+ input clk,
+ input [width-1:0] din,
+ output reg [width-1:0] st1,
+ output reg [width-1:0] st2,
+ output reg [width-1:0] st3,
+ output reg [width-1:0] st4,
+ output reg [width-1:0] st5,
+ output reg [width-1:0] st6,
+ output reg [width-1:0] st7,
+ output reg [width-1:0] st8,
+ output reg [width-1:0] st9,
+ output reg [width-1:0] st10,
+ output reg [width-1:0] st11,
+ output reg [width-1:0] st12,
+ output reg [width-1:0] st13,
+ output reg [width-1:0] st14,
+ output reg [width-1:0] st15,
+ output reg [width-1:0] st16,
+ output reg [width-1:0] st17,
+ output reg [width-1:0] st18,
+ output reg [width-1:0] st19,
+ output reg [width-1:0] st20,
+ output reg [width-1:0] st21,
+ output reg [width-1:0] st22,
+ output reg [width-1:0] st23,
+ output reg [width-1:0] st24
+);
+
+always @(posedge clk) begin
+ st24<= st23;
+ st23<= st22;
+ st22<= st21;
+ st21<= st20;
+ st20<= st19;
+ st19<= st18;
+ st18<= st17;
+ st17<= st16;
+ st16<= st15;
+ st15<= st14;
+ st14<= st13;
+ st13<= st12;
+ st12<= st11;
+ st11<= st10;
+ st10<= st9;
+ st9 <= st8;
+ st8 <= st7;
+ st7 <= st6;
+ st6 <= st5;
+ st5 <= st4;
+ st4 <= st3;
+ st3 <= st2;
+ st2 <= st1;
+ st1 <= din;
+end
+
+endmodule
diff --git a/src/sound/jt12/jt12_sh_rst.v b/src/sound/jt12/jt12_sh_rst.v
new file mode 100644
index 0000000..3e2cc13
--- /dev/null
+++ b/src/sound/jt12/jt12_sh_rst.v
@@ -0,0 +1,54 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 1-31-2017
+ */
+
+`timescale 1ns / 1ps
+
+module jt12_sh_rst #(parameter width=5, stages=32, rstval=1'b0 )
+(
+// input rst,
+ input clk,
+ input [width-1:0] din,
+ output [width-1:0] drop
+);
+
+reg [stages-1:0] bits[width-1:0];
+
+genvar i;
+integer k;
+generate
+initial
+ for (k=0; k < width; k=k+1) begin
+ bits[k] <= { stages{rstval}};
+ end
+endgenerate
+
+generate
+ for (i=0; i < width; i=i+1) begin: bit_shifter
+ always @(posedge clk) begin
+ if( stages> 1 )
+ bits[i] <= {bits[i][stages-2:0], din[i]};
+ else
+ bits[i] <= din[i];
+ end
+ assign drop[i] = bits[i][stages-1];
+ end
+endgenerate
+
+endmodule
diff --git a/src/sound/jt12/jt12_sumch.v b/src/sound/jt12/jt12_sumch.v
new file mode 100644
index 0000000..d3713ed
--- /dev/null
+++ b/src/sound/jt12/jt12_sumch.v
@@ -0,0 +1,37 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 1-31-2017
+ */
+
+`timescale 1ns / 1ps
+
+module jt12_sumch
+(
+ input [4:0] chin,
+ output reg [4:0] chout
+);
+
+reg [2:0] aux;
+
+always @(*) begin
+ aux <= chin[2:0] + 3'd1;
+ chout[2:0] <= aux[1:0]==2'b11 ? aux+3'd1 : aux;
+ chout[4:3] <= chin[2:0]==3'd6 ? chin[4:3]+2'd1 : chin[4:3];
+end
+
+endmodule
diff --git a/src/sound/jt12/jt12_syn.v b/src/sound/jt12/jt12_syn.v
new file mode 100644
index 0000000..5ee5a0c
--- /dev/null
+++ b/src/sound/jt12/jt12_syn.v
@@ -0,0 +1,338 @@
+module jt12_syn(
+ input rst,
+ input clk, // cpu_clk/6 ~ 1.3MHz
+ input [7:0] din,
+ input [1:0] addr,
+ input write,
+ input limiter_en,
+
+ output busy,
+ output flag_A,
+ output flag_B,
+ output irq_n,
+ // combined output
+ output signed [11:0] snd_right,
+ output signed [11:0] snd_left,
+ output snd_sample,
+ // multiplexed output
+ output signed [8:0] mux_right,
+ output signed [8:0] mux_left,
+ output mux_sample
+);
+// Timers
+wire [9:0] value_A;
+wire [7:0] value_B;
+wire load_A, load_B;
+wire enable_irq_A, enable_irq_B;
+wire clr_flag_A, clr_flag_B;
+wire overflow_A;
+wire fast_timers;
+
+wire zero; // Single-clock pulse at the begginig of s1_enters
+// LFO
+wire [2:0] lfo_freq;
+wire lfo_en;
+// Operators
+wire amsen_VII;
+wire [ 2:0] dt1_II;
+wire [ 3:0] mul_V;
+wire [ 6:0] tl_VII;
+
+wire [4:0] keycode_III;
+wire [ 4:0] ar_II;
+wire [ 4:0] d1r_II;
+wire [ 4:0] d2r_II;
+wire [ 3:0] rr_II;
+wire [ 3:0] d1l;
+wire [ 1:0] ks_III;
+// SSG operation
+wire ssg_en_II;
+wire [2:0] ssg_eg_II;
+// envelope operation
+wire keyon_II;
+wire [9:0] eg_IX;
+wire pg_rst_III;
+// Channel
+wire [10:0] fnum_I;
+wire [ 2:0] block_I;
+wire [ 1:0] rl;
+wire [ 2:0] fb_II;
+wire [ 2:0] alg;
+wire [ 2:0] pms;
+wire [ 1:0] ams_VII;
+// PCM
+wire pcm_en;
+wire [ 8:0] pcm;
+// Test
+wire pg_stop, eg_stop;
+
+wire ch6op;
+
+// Operator
+wire use_internal_x, use_internal_y;
+wire use_prevprev1, use_prev2, use_prev1;
+wire [ 9:0] phase_VIII;
+wire s1_enters, s2_enters, s3_enters, s4_enters;
+wire rst_int;
+// LFO
+wire [6:0] lfo_mod;
+wire lfo_rst;
+
+`ifdef TEST_SUPPORT
+// Test bits
+wire test_eg, test_op0;
+`endif
+
+jt12_mmr u_mmr(
+ .rst ( rst ),
+ .clk ( clk ),
+ .din ( din ),
+ .write ( write ),
+ .addr ( addr ),
+ .busy ( busy ),
+ .ch6op ( ch6op ),
+ // LFO
+ .lfo_freq ( lfo_freq ),
+ .lfo_en ( lfo_en ),
+ // Timers
+ .value_A ( value_A ),
+ .value_B ( value_B ),
+ .load_A ( load_A ),
+ .load_B ( load_B ),
+ .enable_irq_A ( enable_irq_A ),
+ .enable_irq_B ( enable_irq_B ),
+ .clr_flag_A ( clr_flag_A ),
+ .clr_flag_B ( clr_flag_B ),
+ .flag_A ( flag_A ),
+ .overflow_A ( overflow_A ),
+ .fast_timers( fast_timers ),
+ // PCM
+ .pcm ( pcm ),
+ .pcm_en ( pcm_en ),
+
+ `ifdef TEST_SUPPORT
+ // Test
+ .test_eg ( test_eg ),
+ .test_op0 ( test_op0 ),
+ `endif
+ // Operator
+ .use_prevprev1 ( use_prevprev1 ),
+ .use_internal_x ( use_internal_x ),
+ .use_internal_y ( use_internal_y ),
+ .use_prev2 ( use_prev2 ),
+ .use_prev1 ( use_prev1 ),
+ // PG
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ .pg_stop ( pg_stop ),
+ // EG
+ .rl ( rl ),
+ .fb_II ( fb_II ),
+ .alg ( alg ),
+ .pms ( pms ),
+ .ams_VII ( ams_VII ),
+ .amsen_VII ( amsen_VII ),
+ .dt1_II ( dt1_II ),
+ .mul_V ( mul_V ),
+ .tl_VII ( tl_VII ),
+
+ .ar_II ( ar_II ),
+ .d1r_II ( d1r_II ),
+ .d2r_II ( d2r_II ),
+ .rr_II ( rr_II ),
+ .d1l ( d1l ),
+ .ks_III ( ks_III ),
+
+ .eg_stop ( eg_stop ),
+ // SSG operation
+ .ssg_en_II ( ssg_en_II ),
+ .ssg_eg_II ( ssg_eg_II ),
+
+ .keyon_II ( keyon_II ),
+ // Operator
+ .zero ( zero ),
+ .s1_enters ( s1_enters ),
+ .s2_enters ( s2_enters ),
+ .s3_enters ( s3_enters ),
+ .s4_enters ( s4_enters )
+);
+
+jt12_timers u_timers(
+ .clk ( clk ),
+ .rst ( rst ),
+ .clk_en ( zero ),
+ .fast_timers( fast_timers ), // fix this to work well with clock enable signals
+ .value_A ( value_A ),
+ .value_B ( value_B ),
+ .load_A ( load_A ),
+ .load_B ( load_B ),
+ .enable_irq_A( enable_irq_B ),
+ .enable_irq_B( enable_irq_A ),
+ .clr_flag_A ( clr_flag_A ),
+ .clr_flag_B ( clr_flag_B ),
+ .flag_A ( flag_A ),
+ .flag_B ( flag_B ),
+ .overflow_A ( overflow_A ),
+ .irq_n ( irq_n )
+);
+
+jt12_lfo u_lfo(
+ .rst ( rst ),
+ .clk ( clk ),
+ .zero ( zero ),
+ .lfo_rst ( 1'b0 ),
+ .lfo_en ( lfo_en ),
+ .lfo_freq ( lfo_freq ),
+ .lfo_mod ( lfo_mod )
+);
+
+`ifndef TIMERONLY
+
+jt12_pg u_pg(
+ .clk ( clk ),
+ .rst ( rst ),
+ // Channel frequency
+ .fnum_I ( fnum_I ),
+ .block_I ( block_I ),
+ // Operator multiplying
+ .mul_V ( mul_V ),
+ // Operator detuning
+ .dt1_II ( dt1_II ), // same as JT51's DT1
+ // phase operation
+ .pg_rst_III ( pg_rst_III ),
+ .zero ( zero ),
+ .pg_stop ( pg_stop ),
+ .keycode_III( keycode_III ),
+ .phase_VIII ( phase_VIII )
+);
+
+jt12_eg u_eg(
+ `ifdef TEST_SUPPORT
+ .test_eg ( test_eg ),
+ `endif
+ .rst ( rst ),
+ .clk ( clk ),
+ .zero ( zero ),
+ .eg_stop ( eg_stop ),
+ // envelope configuration
+ .keycode_III ( keycode_III ),
+ .arate_II ( ar_II ), // attack rate
+ .rate1_II ( d1r_II ), // decay rate
+ .rate2_II ( d2r_II ), // sustain rate
+ .rrate_II ( rr_II ), // release rate
+ .d1l ( d1l ), // sustain level
+ .ks_III ( ks_III ), // key scale
+ // SSG operation
+ .ssg_en_II ( ssg_en_II ),
+ .ssg_eg_II ( ssg_eg_II ),
+ // envelope operation
+ .keyon_II ( keyon_II ),
+ // envelope number
+ .am ( lfo_mod ),
+ .tl_VII ( tl_VII ),
+ .ams_VII ( ams_VII ),
+ .amsen_VII ( amsen_VII ),
+
+ .eg_IX ( eg_IX ),
+ .pg_rst_III ( pg_rst_III )
+);
+
+wire [8:0] op_result;
+
+jt12_op u_op(
+ .rst ( rst ),
+ .clk ( clk ),
+ .pg_phase_VIII ( phase_VIII ),
+ .eg_atten_IX ( eg_IX ),
+ .fb_II ( fb_II ),
+
+ .test_214 ( 1'b0 ),
+ .s1_enters ( s1_enters ),
+ .s2_enters ( s2_enters ),
+ .s3_enters ( s3_enters ),
+ .s4_enters ( s4_enters ),
+ .use_prevprev1 ( use_prevprev1 ),
+ .use_internal_x ( use_internal_x),
+ .use_internal_y ( use_internal_y),
+ .use_prev2 ( use_prev2 ),
+ .use_prev1 ( use_prev1 ),
+ .zero ( zero ),
+ .op_result ( op_result )
+);
+
+jt12_acc u_acc(
+ .rst ( rst ),
+ .clk ( clk ),
+ .op_result ( op_result ),
+ .rl ( rl ),
+ .limiter_en ( limiter_en),
+ // note that the order changes to deal
+ // with the operator pipeline delay
+ .s1_enters ( s2_enters ),
+ .s2_enters ( s1_enters ),
+ .s3_enters ( s4_enters ),
+ .s4_enters ( s3_enters ),
+ .ch6op ( ch6op ),
+ .pcm_en ( pcm_en ), // only enabled for channel 6
+ .pcm ( pcm ),
+ .alg ( alg ),
+ // combined output
+ .left ( snd_left ),
+ .right ( snd_right ),
+ .sample ( snd_sample),
+ // muxed output
+ .mux_left ( mux_left ),
+ .mux_right ( mux_right ),
+ .mux_sample ( mux_sample)
+);
+
+`ifdef SIMULATION
+reg [4:0] sep24_cnt;
+
+wire [9:0] eg_ch0s1, eg_ch1s1, eg_ch2s1, eg_ch3s1, eg_ch4s1, eg_ch5s1,
+ eg_ch0s2, eg_ch1s2, eg_ch2s2, eg_ch3s2, eg_ch4s2, eg_ch5s2,
+ eg_ch0s3, eg_ch1s3, eg_ch2s3, eg_ch3s3, eg_ch4s3, eg_ch5s3,
+ eg_ch0s4, eg_ch1s4, eg_ch2s4, eg_ch3s4, eg_ch4s4, eg_ch5s4;
+
+always @(posedge clk)
+ sep24_cnt <= !zero ? sep24_cnt+1'b1 : 5'd0;
+
+sep24 #( .width(10), .pos0(5'd0)) egsep
+(
+ .clk ( clk ),
+ .mixed ( eg_IX ),
+ .mask ( 10'd0 ),
+ .cnt ( sep24_cnt ),
+
+ .ch0s1 (eg_ch0s1),
+ .ch1s1 (eg_ch1s1),
+ .ch2s1 (eg_ch2s1),
+ .ch3s1 (eg_ch3s1),
+ .ch4s1 (eg_ch4s1),
+ .ch5s1 (eg_ch5s1),
+
+ .ch0s2 (eg_ch0s2),
+ .ch1s2 (eg_ch1s2),
+ .ch2s2 (eg_ch2s2),
+ .ch3s2 (eg_ch3s2),
+ .ch4s2 (eg_ch4s2),
+ .ch5s2 (eg_ch5s2),
+
+ .ch0s3 (eg_ch0s3),
+ .ch1s3 (eg_ch1s3),
+ .ch2s3 (eg_ch2s3),
+ .ch3s3 (eg_ch3s3),
+ .ch4s3 (eg_ch4s3),
+ .ch5s3 (eg_ch5s3),
+
+ .ch0s4 (eg_ch0s4),
+ .ch1s4 (eg_ch1s4),
+ .ch2s4 (eg_ch2s4),
+ .ch3s4 (eg_ch3s4),
+ .ch4s4 (eg_ch4s4),
+ .ch5s4 (eg_ch5s4)
+);
+`endif
+
+`endif
+endmodule
diff --git a/src/sound/jt12/jt12_timers.v b/src/sound/jt12/jt12_timers.v
new file mode 100644
index 0000000..4fb2bd8
--- /dev/null
+++ b/src/sound/jt12/jt12_timers.v
@@ -0,0 +1,112 @@
+/* This file is part of JT12.
+
+ JT12 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ JT12 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with JT12. If not, see .
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 14-2-2017
+
+ YM3438_APL.pdf
+ Timer A = 144*(1024-NA)/Phi M
+ Timer B = 2304*(256-NB)/Phi M
+ */
+
+`timescale 1ns / 1ps
+
+module jt12_timers(
+ input clk,
+ input rst,
+ input clk_en, // clock enable
+ input fast_timers,
+ input [9:0] value_A,
+ input [7:0] value_B,
+ input load_A,
+ input load_B,
+ input clr_flag_A,
+ input clr_flag_B,
+ input enable_irq_A,
+ input enable_irq_B,
+ output flag_A,
+ output flag_B,
+ output overflow_A,
+ output irq_n
+);
+
+assign irq_n = ~( (flag_A&enable_irq_A) | (flag_B&enable_irq_B) );
+
+jt12_timer #(.mult_width(1), .mult_max(0), .counter_width(10))
+timer_A(
+ .clk ( clk ),
+ .rst ( rst ),
+ .clk_en ( clk_en | fast_timers ),
+ .start_value( value_A ),
+ .load ( load_A ),
+ .clr_flag ( clr_flag_A),
+ .flag ( flag_A ),
+ .overflow ( overflow_A)
+);
+
+jt12_timer #(.mult_width(4), .mult_max(15), .counter_width(8))
+timer_B(
+ .clk ( clk ),
+ .rst ( rst ),
+ .clk_en ( clk_en | fast_timers ),
+ .start_value( value_B ),
+ .load ( load_B ),
+ .clr_flag ( clr_flag_B),
+ .flag ( flag_B ),
+ .overflow ( )
+);
+
+endmodule
+
+module jt12_timer #(parameter counter_width = 10, mult_width=5, mult_max=4 )
+(
+ input clk,
+ input rst,
+(* direct_enable *) input clk_en,
+ input [counter_width-1:0] start_value,
+ input load,
+ input clr_flag,
+ output reg flag,
+ output reg overflow
+);
+
+reg [ mult_width-1:0] mult;
+reg [counter_width-1:0] cnt;
+
+always@(posedge clk)
+ if( clr_flag || rst)
+ flag <= 1'b0;
+ else if(overflow) flag<=1'b1;
+
+reg [mult_width+counter_width-1:0] next, init;
+
+always @(*) begin
+ if( mult.
+
+ Based on Sauraen VHDL version of OPN/OPN2, which is based on die shots.
+
+ Author: Jose Tejada Gomez. Twitter: @topapate
+ Version: 1.0
+ Date: 27-1-2017
+
+*/
+
+
+initial begin
+ sinetable[5'd0 ] <= 46'b0001100000100100010001000010101010101001010010;
+ sinetable[5'd1 ] <= 46'b0001100000110100000100000010010001001101000001;
+ sinetable[5'd2 ] <= 46'b0001100000110100000100110010001011001101100000;
+ sinetable[5'd3 ] <= 46'b0001110000010000000000110010110001001101110010;
+ sinetable[5'd4 ] <= 46'b0001110000010000001100000010111010001101101001;
+ sinetable[5'd5 ] <= 46'b0001110000010100001001100010000000101101111010;
+ sinetable[5'd6 ] <= 46'b0001110000010100001101100010010011001101011010;
+ sinetable[5'd7 ] <= 46'b0001110000011100000101010010111000101111111100;
+ sinetable[5'd8 ] <= 46'b0001110000111000000001110010101110001101110111;
+ sinetable[5'd9 ] <= 46'b0001110000111000010100111000011101011010100110;
+ sinetable[5'd10] <= 46'b0001110000111100011000011000111100001001111010;
+ sinetable[5'd11] <= 46'b0001110000111100011100111001101011001001110111;
+ sinetable[5'd12] <= 46'b0100100001010000010001011001001000111010110111;
+ sinetable[5'd13] <= 46'b0100100001010100010001001001110001111100101010;
+ sinetable[5'd14] <= 46'b0100100001010100010101101101111110100101000110;
+ sinetable[5'd15] <= 46'b0100100011100000001000011001010110101101111001;
+ sinetable[5'd16] <= 46'b0100100011100100001000101011100101001011101111;
+ sinetable[5'd17] <= 46'b0100100011101100000111011010000001011010110001;
+ sinetable[5'd18] <= 46'b0100110011001000000111101010000010111010111111;
+ sinetable[5'd19] <= 46'b0100110011001100001011011110101110110110000001;
+ sinetable[5'd20] <= 46'b0100110011101000011010111011001010001101110001;
+ sinetable[5'd21] <= 46'b0100110011101101011010110101111001010100001111;
+ sinetable[5'd22] <= 46'b0111000010000001010111000101010101010110010111;
+ sinetable[5'd23] <= 46'b0111000010000101010111110111110101010010111011;
+ sinetable[5'd24] <= 46'b0111000010110101101000101100001000010000011001;
+ sinetable[5'd25] <= 46'b0111010010011001100100011110100100010010010010;
+ sinetable[5'd26] <= 46'b0111010010111010100101100101000000110100100011;
+ sinetable[5'd27] <= 46'b1010000010011010101101011101100001110010011010;
+ sinetable[5'd28] <= 46'b1010000010111111111100100111010100010000111001;
+ sinetable[5'd29] <= 46'b1010010111110100110010001100111001010110100000;
+ sinetable[5'd30] <= 46'b1011010111010011111011011110000100110010100001;
+ sinetable[5'd31] <= 46'b1110011011110001111011100111100001110110100111;
+
+ explut_jt51[0] <= 45'b111110101011010110001011010000010010111011011;
+ explut_jt51[1] <= 45'b111101010011010101000011001100101110110101011;
+ explut_jt51[2] <= 45'b111011111011010011110111001000110010101110011;
+ explut_jt51[3] <= 45'b111010100101010010101111000100110010101000011;
+ explut_jt51[4] <= 45'b111001001101010001100111000000110010100001011;
+ explut_jt51[5] <= 45'b110111111011010000011110111101010010011011011;
+ explut_jt51[6] <= 45'b110110100011001111010110111001010010010100100;
+ explut_jt51[7] <= 45'b110101001011001110001110110101110010001110011;
+ explut_jt51[8] <= 45'b110011111011001101000110110001110010001000011;
+ explut_jt51[9] <= 45'b110010100011001011111110101110010010000010011;
+ explut_jt51[10] <= 45'b110001010011001010111010101010010001111011011;
+ explut_jt51[11] <= 45'b101111111011001001110010100110110001110101011;
+ explut_jt51[12] <= 45'b101110101011001000101010100011001101101111011;
+ explut_jt51[13] <= 45'b101101010101000111100110011111010001101001011;
+ explut_jt51[14] <= 45'b101100000011000110100010011011110001100011011;
+ explut_jt51[15] <= 45'b101010110011000101011110011000010001011101011;
+ explut_jt51[16] <= 45'b101001100011000100011010010100101101010111011;
+ explut_jt51[17] <= 45'b101000010011000011010010010001001101010001011;
+ explut_jt51[18] <= 45'b100111000011000010010010001101101101001011011;
+ explut_jt51[19] <= 45'b100101110011000001001110001010001101000101011;
+ explut_jt51[20] <= 45'b100100100011000000001010000110010000111111011;
+ explut_jt51[21] <= 45'b100011010010111111001010000011001100111001011;
+ explut_jt51[22] <= 45'b100010000010111110000101111111101100110011011;
+ explut_jt51[23] <= 45'b100000110010111101000001111100001100101101011;
+ explut_jt51[24] <= 45'b011111101010111100000001111000101100101000010;
+ explut_jt51[25] <= 45'b011110011010111011000001110101001100100010011;
+ explut_jt51[26] <= 45'b011101001010111010000001110001110000011100011;
+ explut_jt51[27] <= 45'b011100000010111001000001101110010000010110011;
+ explut_jt51[28] <= 45'b011010110010111000000001101011001100010001011;
+ explut_jt51[29] <= 45'b011001101010110111000001100111101100001011011;
+ explut_jt51[30] <= 45'b011000100000110110000001100100010000000110010;
+ explut_jt51[31] <= 45'b010111010010110101000001100001001100000000011;
+end
diff --git a/src/sound/turbosound.sv b/src/sound/turbosound.sv
new file mode 100644
index 0000000..876eb99
--- /dev/null
+++ b/src/sound/turbosound.sv
@@ -0,0 +1,137 @@
+//============================================================================
+// Turbosound-FM
+//
+// Copyright (C) 2018 Ilia Sharin
+// Copyright (C) 2018 Sorgelig
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//============================================================================
+
+
+module turbosound
+(
+ input RESET, // Chip RESET (set all Registers to '0', active high)
+
+ input CLK, // Global clock
+ input CE_CPU, // CPU Clock enable
+ input CE_YM, // YM2203 Master Clock enable x2 (due to YM2612 model!)
+ input BDIR, // Bus Direction (0 - read , 1 - write)
+ input BC, // Bus control
+ input [7:0] DI, // Data In
+ output [7:0] DO, // Data Out
+ output [11:0] CHANNEL_L, // Output channel L
+ output [11:0] CHANNEL_R, // Output channel R
+ output ACTIVE
+);
+
+// AY1 selected by default
+reg ay_select = 1;
+reg stat_sel = 1;
+reg fm_ena = 0;
+
+always_ff @(posedge CLK or posedge RESET) begin
+ if (RESET) begin
+ ay_select <= 1;
+ stat_sel <= 1;
+ fm_ena <= 0;
+ end
+ else if (BDIR & BC & &DI[7:3]) begin
+ ay_select <= DI[0];
+ stat_sel <= DI[1];
+ fm_ena <= ~DI[2];
+ end
+end
+
+wire [7:0] psg_ch_a_0;
+wire [7:0] psg_ch_b_0;
+wire [7:0] psg_ch_c_0;
+wire [10:0] opn_0;
+wire [7:0] DO_0;
+
+wire WE_0 = ~ay_select & BDIR;
+wire ay0_playing;
+
+ym2203 ym2203_0
+(
+ .RESET(RESET),
+ .CLK(CLK),
+ .CE_CPU(CE_CPU),
+ .CE_YM(CE_YM),
+
+ .A0(WE_0 ? ~BC : stat_sel),
+ .WE(WE_0),
+ .DI(DI),
+ .DO(DO_0),
+
+ .CHANNEL_A(psg_ch_a_0),
+ .CHANNEL_B(psg_ch_b_0),
+ .CHANNEL_C(psg_ch_c_0),
+ .CHANNEL_FM(opn_0),
+
+ .PSG_ACTIVE(ay0_playing),
+ .FM_ENA(fm_ena)
+);
+
+wire [7:0] psg_ch_a_1;
+wire [7:0] psg_ch_b_1;
+wire [7:0] psg_ch_c_1;
+wire [10:0] opn_1;
+wire [7:0] DO_1;
+
+wire WE_1 = ay_select & BDIR;
+wire ay1_playing;
+
+ym2203 ym2203_1
+(
+ .RESET(RESET),
+ .CLK(CLK),
+ .CE_CPU(CE_CPU),
+ .CE_YM(CE_YM),
+
+ .A0(WE_1 ? ~BC : stat_sel),
+ .WE(WE_1),
+ .DI(DI),
+ .DO(DO_1),
+
+ .CHANNEL_A(psg_ch_a_1),
+ .CHANNEL_B(psg_ch_b_1),
+ .CHANNEL_C(psg_ch_c_1),
+ .CHANNEL_FM(opn_1),
+
+ .PSG_ACTIVE(ay1_playing),
+ .FM_ENA(fm_ena)
+);
+
+assign DO = ay_select ? DO_1 : DO_0;
+assign ACTIVE = ay0_playing | ay1_playing | fm_ena;
+
+// Mix channel signals from both AY/YM chips (extending to 9 bits width to prevent clipping)
+wire [8:0] sum_ch_a = { 1'b0, psg_ch_a_1 } + { 1'b0, psg_ch_a_0 };
+wire [8:0] sum_ch_b = { 1'b0, psg_ch_b_1 } + { 1'b0, psg_ch_b_0 };
+wire [8:0] sum_ch_c = { 1'b0, psg_ch_c_1 } + { 1'b0, psg_ch_c_0 };
+
+// Control output channels (Only AY_1 plays if not in TurboSound mode)
+wire [7:0] psg_a = ~ay0_playing ? psg_ch_a_1 : sum_ch_a[8:1];
+wire [7:0] psg_b = ~ay0_playing ? psg_ch_b_1 : sum_ch_b[8:1];
+wire [7:0] psg_c = ~ay0_playing ? psg_ch_c_1 : sum_ch_c[8:1];
+
+wire signed [11:0] psg_l = {3'b000, psg_a, 1'd0} + {4'b0000, psg_b};
+wire signed [11:0] psg_r = {3'b000, psg_c, 1'd0} + {4'b0000, psg_b};
+wire signed [11:0] opn_s = {{2{opn_0[10]}}, opn_0[10:1]} + {{2{opn_1[10]}}, opn_1[10:1]};
+
+assign CHANNEL_L = fm_ena ? opn_s + psg_l : psg_l;
+assign CHANNEL_R = fm_ena ? opn_s + psg_r : psg_r;
+
+endmodule
diff --git a/src/sound/turbosound.vhd b/src/sound/turbosound.vhd
deleted file mode 100644
index bb500f0..0000000
--- a/src/sound/turbosound.vhd
+++ /dev/null
@@ -1,78 +0,0 @@
--------------------------------------------------------------------[07.09.2013]
--- TurboSound
--------------------------------------------------------------------------------
--- V0.1 15.10.2011 Initial version
-
-library IEEE;
-use IEEE.STD_LOGIC_1164.ALL;
-use IEEE.NUMERIC_STD.ALL;
-
-entity turbosound is
-port(
- RESET : in std_logic;
- CLK : in std_logic;
- ENA : in std_logic;
- A : in std_logic_vector(15 downto 0);
- DI : in std_logic_vector(7 downto 0);
- WR_n : in std_logic;
- IORQ_n : in std_logic;
- M1_n : in std_logic;
- SEL : out std_logic;
- CN0_DO : out std_logic_vector(7 downto 0);
- CN0_A : out std_logic_vector(7 downto 0);
- CN0_B : out std_logic_vector(7 downto 0);
- CN0_C : out std_logic_vector(7 downto 0);
- CN1_DO : out std_logic_vector(7 downto 0);
- CN1_A : out std_logic_vector(7 downto 0);
- CN1_B : out std_logic_vector(7 downto 0);
- CN1_C : out std_logic_vector(7 downto 0));
-end turbosound;
-
-architecture turbosound_arch of turbosound is
- signal bc1 : std_logic;
- signal bdir : std_logic;
- signal ssg : std_logic;
-begin
- bc1 <= '1' when (IORQ_n = '0' and A(15) = '1' and A(1) = '0' and M1_n = '1' and A(14) = '1') else '0';
- bdir <= '1' when (IORQ_n = '0' and A(15) = '1' and A(1) = '0' and M1_n = '1' and WR_n = '0') else '0';
- SEL <= ssg;
-
- process(CLK, RESET)
- begin
- if (RESET = '1') then
- ssg <= '0';
- elsif (CLK'event and CLK = '1') then
- if (DI(7 downto 1) = "1111111" and bdir = '1' and bc1 = '1') then
- ssg <= DI(0);
- end if;
- end if;
- end process;
-
-ssg0_unit: entity work.ay8910(rtl)
- port map(
- RESET => RESET,
- CLK => CLK,
- DI => DI,
- DO => CN0_DO,
- ENA => ENA,
- CS => not ssg,
- BDIR => bdir,
- BC => bc1,
- OUT_A => CN0_A,
- OUT_B => CN0_B,
- OUT_C => CN0_C);
-
-ssg1_unit: entity work.ay8910(rtl)
- port map(
- RESET => RESET,
- CLK => CLK,
- DI => DI,
- DO => CN1_DO,
- ENA => ENA,
- CS => ssg,
- BDIR => bdir,
- BC => bc1,
- OUT_A => CN1_A,
- OUT_B => CN1_B,
- OUT_C => CN1_C);
-end turbosound_arch;
\ No newline at end of file
diff --git a/src/sound/ym2149.sv b/src/sound/ym2149.sv
new file mode 100644
index 0000000..4281952
--- /dev/null
+++ b/src/sound/ym2149.sv
@@ -0,0 +1,354 @@
+//
+// Copyright (c) MikeJ - Jan 2005
+// Copyright (c) 2016-2018 Sorgelig
+//
+// All rights reserved
+//
+// Redistribution and use in source and synthezised forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// Redistributions in synthesized form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// Neither the name of the author nor the names of other contributors may
+// be used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+// BDIR BC MODE
+// 0 0 inactive
+// 0 1 read value
+// 1 0 write value
+// 1 1 set address
+//
+// Registers description
+// R0, R1, R2, R4, R4, R5 - Tone Generator Control
+// R6 - Noise Generator Control
+// R7 - Mixer Control-I/O Enable
+// R10, R11, R12 - Amplitude Control
+// R13, R14, R15 - Envelope Generator Control
+// R13, R14 - Envelope Period Control
+// R15 - Envelope Shape/Cycle Control
+
+module ym2149
+(
+ input CLK, // Global clock
+ input CE, // PSG Clock enable
+ input RESET, // Chip RESET (set all Registers to '0', active hi)
+ input BDIR, // Bus Direction (0 - read , 1 - write)
+ input BC, // Bus control
+ input [7:0] DI, // Data In
+ output [7:0] DO, // Data Out
+ output [7:0] CHANNEL_A, // PSG Output channel A
+ output [7:0] CHANNEL_B, // PSG Output channel B
+ output [7:0] CHANNEL_C, // PSG Output channel C
+
+ input SEL,
+ input MODE,
+
+ output [5:0] ACTIVE,
+
+ input [7:0] IOA_in,
+ output [7:0] IOA_out,
+
+ input [7:0] IOB_in,
+ output [7:0] IOB_out
+);
+
+assign ACTIVE = ~ymreg[7][5:0];
+
+assign IOA_out = ymreg[14];
+assign IOB_out = ymreg[15];
+
+reg ena_div;
+reg ena_div_noise;
+reg [7:0] addr;
+reg [7:0] ymreg[16];
+reg env_ena;
+reg [4:0] env_vol;
+
+wire [7:0] volTableAy[16] =
+ '{8'h00, 8'h03, 8'h04, 8'h06,
+ 8'h0a, 8'h0f, 8'h15, 8'h22,
+ 8'h28, 8'h41, 8'h5b, 8'h72,
+ 8'h90, 8'hb5, 8'hd7, 8'hff
+ };
+
+wire [7:0] volTableYm[32] =
+ '{8'h00, 8'h01, 8'h01, 8'h02,
+ 8'h02, 8'h03, 8'h03, 8'h04,
+ 8'h06, 8'h07, 8'h09, 8'h0a,
+ 8'h0c, 8'h0e, 8'h11, 8'h13,
+ 8'h17, 8'h1b, 8'h20, 8'h25,
+ 8'h2c, 8'h35, 8'h3e, 8'h47,
+ 8'h54, 8'h66, 8'h77, 8'h88,
+ 8'ha1, 8'hc0, 8'he0, 8'hff
+ };
+
+// Read from AY
+assign DO = dout;
+reg [7:0] dout;
+always_comb begin
+ if(addr[7:4]) dout <= 8'hFF;
+ else begin
+ case(addr[3:0])
+ 0: dout = ymreg[0];
+ 1: dout = ymreg[1][3:0];
+ 2: dout = ymreg[2];
+ 3: dout = ymreg[3][3:0];
+ 4: dout = ymreg[4];
+ 5: dout = ymreg[5][3:0];
+ 6: dout = ymreg[6][4:0];
+ 7: dout = ymreg[7];
+ 8: dout = ymreg[8][4:0];
+ 9: dout = ymreg[9][4:0];
+ 10: dout = ymreg[10][4:0];
+ 11: dout = ymreg[11];
+ 12: dout = ymreg[12];
+ 13: dout = ymreg[13][3:0];
+ 14: dout = (ymreg[7][6] ? ymreg[14] : IOA_in);
+ 15: dout = (ymreg[7][7] ? ymreg[15] : IOB_in);
+ endcase
+ end
+end
+
+// p_divider
+always @(posedge CLK) begin
+ reg [3:0] cnt_div;
+ reg noise_div;
+
+ if(CE) begin
+ ena_div <= 0;
+ ena_div_noise <= 0;
+ if(!cnt_div) begin
+ cnt_div <= {SEL, 3'b111};
+ ena_div <= 1;
+
+ noise_div <= (~noise_div);
+ if (noise_div) ena_div_noise <= 1;
+ end else begin
+ cnt_div <= cnt_div - 1'b1;
+ end
+ end
+end
+
+
+reg noise_gen_op;
+
+// p_noise_gen
+always @(posedge CLK) begin
+ reg [16:0] poly17;
+ reg [4:0] noise_gen_cnt;
+
+ if(CE) begin
+ if (ena_div_noise) begin
+ if(ymreg[6][4:0]) begin
+ if (noise_gen_cnt >= ymreg[6][4:0] - 1'd1) begin
+ noise_gen_cnt <= 0;
+ poly17 <= {(poly17[0] ^ poly17[2] ^ !poly17), poly17[16:1]};
+ end else begin
+ noise_gen_cnt <= noise_gen_cnt + 1'd1;
+ end
+ noise_gen_op <= poly17[0];
+ end else begin
+ noise_gen_op <= 0;
+ noise_gen_cnt <= 0;
+ end
+ end
+ end
+end
+
+wire [11:0] tone_gen_freq[1:3];
+assign tone_gen_freq[1] = {ymreg[1][3:0], ymreg[0]};
+assign tone_gen_freq[2] = {ymreg[3][3:0], ymreg[2]};
+assign tone_gen_freq[3] = {ymreg[5][3:0], ymreg[4]};
+
+reg [3:1] tone_gen_op;
+
+//p_tone_gens
+always @(posedge CLK) begin
+ integer i;
+ reg [11:0] tone_gen_cnt[1:3];
+
+ if(CE) begin
+ // looks like real chips count up - we need to get the Exact behaviour ..
+
+ for (i = 1; i <= 3; i = i + 1) begin
+ if(ena_div) begin
+ if (tone_gen_freq[i]) begin
+ if (tone_gen_cnt[i] >= (tone_gen_freq[i] - 1'd1)) begin
+ tone_gen_cnt[i] <= 0;
+ tone_gen_op[i] <= ~tone_gen_op[i];
+ end else begin
+ tone_gen_cnt[i] <= tone_gen_cnt[i] + 1'd1;
+ end
+ end else begin
+ tone_gen_op[i] <= 0;
+ tone_gen_cnt[i] <= 0;
+ end
+ end
+ end
+ end
+end
+
+wire [15:0] env_gen_comp = {ymreg[12], ymreg[11]} ? {ymreg[12], ymreg[11]} - 1'd1 : 16'd0;
+
+//p_envelope_freq
+always @(posedge CLK) begin
+ reg [15:0] env_gen_cnt;
+
+ if(CE) begin
+ env_ena <= 0;
+ if(ena_div) begin
+ if (env_gen_cnt >= env_gen_comp) begin
+ env_gen_cnt <= 0;
+ env_ena <= 1;
+ end else begin
+ env_gen_cnt <= (env_gen_cnt + 1'd1);
+ end
+ end
+ end
+end
+
+wire is_bot = (env_vol == 5'b00000);
+wire is_bot_p1 = (env_vol == 5'b00001);
+wire is_top_m1 = (env_vol == 5'b11110);
+wire is_top = (env_vol == 5'b11111);
+
+always @(posedge CLK) begin
+ reg old_BDIR;
+ reg env_reset;
+ reg env_hold;
+ reg env_inc;
+
+ // envelope shapes
+ // C AtAlH
+ // 0 0 x x \___
+ //
+ // 0 1 x x /___
+ //
+ // 1 0 0 0 \\\\
+ //
+ // 1 0 0 1 \___
+ //
+ // 1 0 1 0 \/\/
+ // ___
+ // 1 0 1 1 \
+ //
+ // 1 1 0 0 ////
+ // ___
+ // 1 1 0 1 /
+ //
+ // 1 1 1 0 /\/\
+ //
+ // 1 1 1 1 /___
+
+ if(RESET) begin
+ ymreg[0] <= '0;
+ ymreg[1] <= '0;
+ ymreg[2] <= '0;
+ ymreg[3] <= '0;
+ ymreg[4] <= '0;
+ ymreg[5] <= '0;
+ ymreg[6] <= '0;
+ ymreg[7] <= '1;
+ ymreg[8] <= '0;
+ ymreg[9] <= '0;
+ ymreg[10] <= '0;
+ ymreg[11] <= '0;
+ ymreg[12] <= '0;
+ ymreg[13] <= '0;
+ ymreg[14] <= '0;
+ ymreg[15] <= '0;
+ addr <= '0;
+ env_vol <= '0;
+ end else begin
+ old_BDIR <= BDIR;
+ if(~old_BDIR & BDIR) begin
+ if(BC) addr <= DI;
+ else if(!addr[7:4])begin
+ ymreg[addr[3:0]] <= DI;
+ env_reset <= (addr == 13);
+ end
+ end
+ end
+
+ if(CE) begin
+ if(env_reset) begin
+ env_reset <= 0;
+ // load initial state
+ if(!ymreg[13][2]) begin // attack
+ env_vol <= 5'b11111;
+ env_inc <= 0; // -1
+ end else begin
+ env_vol <= 5'b00000;
+ env_inc <= 1; // +1
+ end
+ env_hold <= 0;
+ end else begin
+
+ if (env_ena) begin
+ if (!env_hold) begin
+ if (env_inc) env_vol <= (env_vol + 5'b00001);
+ else env_vol <= (env_vol + 5'b11111);
+ end
+
+ // envelope shape control.
+ if(!ymreg[13][3]) begin
+ if(!env_inc) begin // down
+ if(is_bot_p1) env_hold <= 1;
+ end else if (is_top) env_hold <= 1;
+ end else if(ymreg[13][0]) begin // hold = 1
+ if(!env_inc) begin // down
+ if(ymreg[13][1]) begin // alt
+ if(is_bot) env_hold <= 1;
+ end else if(is_bot_p1) env_hold <= 1;
+ end else if(ymreg[13][1]) begin // alt
+ if(is_top) env_hold <= 1;
+ end else if(is_top_m1) env_hold <= 1;
+ end else if(ymreg[13][1]) begin // alternate
+ if(env_inc == 1'b0) begin // down
+ if(is_bot_p1) env_hold <= 1;
+ if(is_bot) begin
+ env_hold <= 0;
+ env_inc <= 1;
+ end
+ end else begin
+ if(is_top_m1) env_hold <= 1;
+ if(is_top) begin
+ env_hold <= 0;
+ env_inc <= 0;
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+wire [4:0] A = ~((ymreg[7][0] | tone_gen_op[1]) & (ymreg[7][3] | noise_gen_op)) ? 5'd0 : ymreg[8][4] ? env_vol[4:0] : { ymreg[8][3:0], ymreg[8][3]};
+wire [4:0] B = ~((ymreg[7][1] | tone_gen_op[2]) & (ymreg[7][4] | noise_gen_op)) ? 5'd0 : ymreg[9][4] ? env_vol[4:0] : { ymreg[9][3:0], ymreg[9][3]};
+wire [4:0] C = ~((ymreg[7][2] | tone_gen_op[3]) & (ymreg[7][5] | noise_gen_op)) ? 5'd0 : ymreg[10][4] ? env_vol[4:0] : {ymreg[10][3:0], ymreg[10][3]};
+
+assign CHANNEL_A = MODE ? volTableAy[A[4:1]] : volTableYm[A];
+assign CHANNEL_B = MODE ? volTableAy[B[4:1]] : volTableYm[B];
+assign CHANNEL_C = MODE ? volTableAy[C[4:1]] : volTableYm[C];
+
+endmodule
diff --git a/src/sound/ym2203.sv b/src/sound/ym2203.sv
new file mode 100644
index 0000000..e9ca0a4
--- /dev/null
+++ b/src/sound/ym2203.sv
@@ -0,0 +1,130 @@
+//============================================================================
+// YM2203 wrapper
+// Copyright (C) 2018 Sorgelig
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//============================================================================
+
+
+module ym2203
+(
+ input RESET,
+ input CLK, // Global clock
+ input CE_CPU, // CPU Clock enable
+ input CE_YM, // YM2203 Master Clock enable x2 (due to YM2612 model!)
+
+ input A0, // 0 - register number/read FM, 1 - data/read PSG
+ input WE, // 0 - read , 1 - write
+ input [7:0] DI, // Data In
+ output [7:0] DO, // Data Out
+
+ output [7:0] CHANNEL_A, // PSG Output channel A
+ output [7:0] CHANNEL_B, // PSG Output channel B
+ output [7:0] CHANNEL_C, // PSG Output channel C
+ output [10:0] CHANNEL_FM,// FM Output channel
+
+ output PSG_ACTIVE,
+ input FM_ENA
+);
+
+reg [7:0] ymreg;
+reg [1:0] pres;
+
+always @(posedge CLK) begin
+
+ if(RESET) pres <= 2;
+ else if(CE_CPU & WE) begin
+ if(FM_ENA) begin
+ if(~A0) ymreg <= DI;
+ else begin
+ case(ymreg)
+ 'h2d: pres[1] <= 1;
+ 'h2e: pres[0] <= 1;
+ 'h2f: pres <= 0;
+ endcase
+ end
+ end
+ end
+end
+
+wire [2:0] opn_tbl[4] = '{1,1,5,2};
+wire [2:0] opn_pres = opn_tbl[pres];
+
+wire [2:0] psg_tbl[4] = '{0,0,3,1};
+wire [2:0] psg_pres = psg_tbl[pres];
+
+reg ce_psg_pre, ce_opn_pre;
+always @(posedge CLK) begin
+ reg [2:0] div_psg, div_opn;
+
+ {ce_opn_pre, ce_psg_pre} <= 0;
+
+ if(RESET) {div_opn, div_psg} <= 0;
+ else if (CE_YM) begin
+ div_opn <= div_opn + 1'd1;
+ if(div_opn >= opn_pres) div_opn <= 0;
+ ce_opn_pre <= !div_opn;
+
+ div_psg <= div_psg + 1'd1;
+ if(div_psg >= psg_pres) div_psg <= 0;
+ ce_psg_pre <= !div_psg;
+ end
+end
+
+reg ce_opn, ce_psg;
+always @(negedge CLK) {ce_opn, ce_psg} <= {ce_opn_pre, ce_psg_pre};
+
+wire [5:0] psg_active;
+wire [7:0] psg_dout;
+ym2149 ym2149
+(
+ .CLK(CLK),
+ .CE(ce_psg),
+ .RESET(RESET),
+ .BDIR(WE),
+ .BC(~A0),
+ .DI(DI),
+ .DO(psg_dout),
+ .CHANNEL_A(CHANNEL_A),
+ .CHANNEL_B(CHANNEL_B),
+ .CHANNEL_C(CHANNEL_C),
+ .ACTIVE(psg_active),
+ .SEL(1'b0),
+ .MODE(1'b0)
+);
+
+wire [7:0] opn_dout;
+wire [11:0] opn_audio;
+jt12 jt12
+(
+ .rst(RESET),
+
+ .cpu_clk(CLK & CE_CPU),
+ .cpu_din(DI),
+ .cpu_dout(opn_dout),
+ .cpu_addr({1'b0,A0}),
+ .cpu_cs_n(~FM_ENA),
+ .cpu_wr_n(~WE),
+
+ .syn_clk(CLK & ce_opn),
+ .cpu_limiter_en(1'b1),
+ .syn_snd_right(opn_audio)
+);
+
+assign DO = A0 ? psg_dout : opn_dout;
+assign PSG_ACTIVE = |psg_active;
+assign CHANNEL_FM = opn_audio[10:0];
+
+endmodule
diff --git a/src/tsconf.v b/src/tsconf.v
index 4484150..7c6d676 100644
--- a/src/tsconf.v
+++ b/src/tsconf.v
@@ -158,7 +158,6 @@ wire dram_rnw;
// Port
reg [7:0] port_xxfe_reg;
reg [7:0] port_xx01_reg;
-reg ena_1_75mhz;
reg [5:0] ena_cnt;
// System
wire reset;
@@ -184,16 +183,6 @@ wire [7:0] covox_a;
wire [7:0] covox_b;
wire [7:0] covox_c;
wire [7:0] covox_d;
-// TurboSound
-wire ssg_sel;
-wire [7:0] ssg_cn0_bus;
-wire [7:0] ssg_cn0_a;
-wire [7:0] ssg_cn0_b;
-wire [7:0] ssg_cn0_c;
-wire [7:0] ssg_cn1_bus;
-wire [7:0] ssg_cn1_a;
-wire [7:0] ssg_cn1_b;
-wire [7:0] ssg_cn1_c;
// clock
wire f0;
wire f1;
@@ -865,28 +854,38 @@ soundrive SE10
.outd(covox_d)
);
-// TurboSound
+reg ce_ym, ce_cpu;
+always @(posedge clk_28mhz) begin
+ reg [1:0] div;
+
+ div <= div + 1'd1;
+ ce_ym <= !div;
+
+ ce_cpu <= zclk;
+ if(ce_cpu) ce_cpu <= 0;
+end
+
+wire ts_enable = cpu_a_bus[0] & cpu_a_bus[15] & ~cpu_a_bus[1];
+wire ts_we = ts_enable & ~cpu_iorq_n & ~cpu_wr_n;
+
+wire [11:0] ts_l, ts_r;
+wire [7:0] ts_do;
+
turbosound SE12
-(
- .reset(reset),
- .clk(clk_28mhz),
- .ena(ena_1_75mhz),
- .a(cpu_a_bus),
- .di(cpu_do_bus),
- .wr_n(cpu_wr_n),
- .iorq_n(cpu_iorq_n),
- .m1_n(cpu_m1_n),
- .sel(ssg_sel),
- .cn0_do(ssg_cn0_bus),
- .cn0_a(ssg_cn0_a),
- .cn0_b(ssg_cn0_b),
- .cn0_c(ssg_cn0_c),
- .cn1_do(ssg_cn1_bus),
- .cn1_a(ssg_cn1_a),
- .cn1_b(ssg_cn1_b),
- .cn1_c(ssg_cn1_c)
-);
-
+(
+ .RESET(reset),
+
+ .CLK(clk_28mhz),
+ .CE_CPU(ce_cpu),
+ .CE_YM(ce_ym),
+ .BDIR(ts_we),
+ .BC(cpu_a_bus[14]),
+ .DI(cpu_do_bus),
+ .DO(ts_do),
+ .CHANNEL_L(ts_l),
+ .CHANNEL_R(ts_r)
+);
+
always @(posedge clk_84mhz) begin
ce_gs <= clk_28mhz;
if(ce_gs) ce_gs <= 0;
@@ -937,7 +936,6 @@ assign RESET_OUT = reset;
always @(negedge clk_28mhz) begin
ena_cnt <= ena_cnt + 1'd1;
- ena_1_75mhz <= ~ena_cnt[3] & ena_cnt[2] & ena_cnt[1] & ena_cnt[0];
ena_0_4375mhz <= ~ena_cnt[5] & ena_cnt[4] & ena_cnt[3] & ena_cnt[2] & ena_cnt[1] & ena_cnt[0];
end
@@ -953,8 +951,7 @@ assign cpu_di_bus = (loader && ~cpu_mreq_n && ~cpu_rd_n && !cpu_a_bus[15:13]) ?
(intack) ? im2vect :
(~cpu_iorq_n && ~cpu_rd_n && port_bff7 && port_eff7_reg[7]) ? mc146818a_do_bus : // MC146818A
(gs_sel && ~cpu_rd_n) ? gs_do_bus : // General Sound
- (~cpu_iorq_n && ~cpu_rd_n && cpu_a_bus == 16'hFFFD && ~ssg_sel) ? ssg_cn0_bus : // TurboSound
- (~cpu_iorq_n && ~cpu_rd_n && cpu_a_bus == 16'hFFFD && ssg_sel) ? ssg_cn1_bus :
+ (~cpu_iorq_n && ~cpu_rd_n && ts_enable) ? ts_do : // TurboSound
(~cpu_iorq_n && ~cpu_rd_n && cpu_a_bus == 16'h0001) ? key_scancode :
(ena_ports) ? dout_ports :
8'b11111111;
@@ -993,7 +990,7 @@ assign port_bff7 = ~cpu_iorq_n && cpu_a_bus == 16'hBFF7 && cpu_m1_n && port_eff7
// SAA1099
assign saa_wr_n = ~cpu_iorq_n && ~cpu_wr_n && cpu_a_bus[7:0] == 8'hFF && ~dos;
-assign SOUND_L = ({3'b000, port_xxfe_reg[4], 12'b000000000000}) + ({3'b000, ssg_cn0_a, 5'b00000}) + ({4'b0000, ssg_cn0_b, 4'b0000}) + ({3'b000, ssg_cn1_a, 5'b00000}) + ({4'b0000, ssg_cn1_b, 4'b0000}) + ({2'b00, covox_a, 6'b000000}) + ({2'b00, covox_b, 6'b000000}) + ({gs_l[14], gs_l}) + ({1'b0, saa_out_l, 7'b0000000});
-assign SOUND_R = ({3'b000, port_xxfe_reg[4], 12'b000000000000}) + ({3'b000, ssg_cn0_c, 5'b00000}) + ({4'b0000, ssg_cn0_b, 4'b0000}) + ({3'b000, ssg_cn1_c, 5'b00000}) + ({4'b0000, ssg_cn1_b, 4'b0000}) + ({2'b00, covox_c, 6'b000000}) + ({2'b00, covox_d, 6'b000000}) + ({gs_r[14], gs_r}) + ({1'b0, saa_out_r, 7'b0000000});
-
+assign SOUND_L = {ts_l, 4'b0000} + {gs_l[14], gs_l} + {2'b00, covox_a, 6'b000000} + {2'b00, covox_b, 6'b000000} + {1'b0, saa_out_l, 7'b0000000} + {3'b000, port_xxfe_reg[4], 12'b000000000000};
+assign SOUND_R = {ts_r, 4'b0000} + {gs_r[14], gs_r} + {2'b00, covox_c, 6'b000000} + {2'b00, covox_d, 6'b000000} + {1'b0, saa_out_r, 7'b0000000} + {3'b000, port_xxfe_reg[4], 12'b000000000000};
+
endmodule