diff --git a/TSConf-lite.qsf b/TSConf-lite.qsf index 56f4d19..a93938b 100644 --- a/TSConf-lite.qsf +++ b/TSConf-lite.qsf @@ -375,6 +375,7 @@ 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 VHDL_FILE src/sound/gs.vhd +set_global_assignment -name SYSTEMVERILOG_FILE src/sound/saa1099.sv set_global_assignment -name VERILOG_FILE src/memory/dma.v set_global_assignment -name VERILOG_FILE src/memory/arbiter.v set_global_assignment -name VERILOG_FILE src/video/video_ts_render.v diff --git a/TSConf.qsf b/TSConf.qsf index 612f56d..2c7aaf7 100644 --- a/TSConf.qsf +++ b/TSConf.qsf @@ -381,6 +381,7 @@ 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 VHDL_FILE src/sound/gs.vhd +set_global_assignment -name SYSTEMVERILOG_FILE src/sound/saa1099.sv set_global_assignment -name VERILOG_FILE src/memory/dma.v set_global_assignment -name VERILOG_FILE src/memory/arbiter.v set_global_assignment -name VERILOG_FILE src/video/video_ts_render.v diff --git a/src/clock.v b/src/clock.v index 56f38e0..d12003e 100644 --- a/src/clock.v +++ b/src/clock.v @@ -20,7 +20,8 @@ module clock ( output reg f0, f1, output reg h0, h1, output reg c0, c1, c2, c3, - output wire ay_clk + output wire ay_clk, + output reg ce_saa ); @@ -45,6 +46,15 @@ module clock ( begin skip_cnt <= skip_cnt[7] ? 8'd73 : skip_cnt - 8'd1; ay_cnt <= ay_cnt + (skip_cnt[7] & ay_mod[0] ? 4'd2 : 4'd1); + end + + always @(posedge clk) begin + reg [2:0] div; + + div <= div + 1'd1; + if(div == 6) div <= 0; + + ce_saa <= (div == 0 || div == 3); end diff --git a/src/sound/saa1099.sv b/src/sound/saa1099.sv new file mode 100644 index 0000000..7212f2f --- /dev/null +++ b/src/sound/saa1099.sv @@ -0,0 +1,403 @@ +//============================================================================ +// +// SAA1099 sound generator +// Copyright (C) 2016 Sorgelig +// +// Based on SAA1099.v code from Miguel Angel Rodriguez Jodar +// Based on SAASound code from Dave Hooper +// +// 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. +// +//============================================================================ +`timescale 1ns / 1ps +`default_nettype none + + +module saa1099 +( + input clk_sys, + input ce, // 8 MHz + input rst_n, + input cs_n, + input a0, // 0=data, 1=address + input wr_n, + input [7:0] din, + output [7:0] out_l, + output [7:0] out_r +); + +reg [7:0] amplit0, amplit1, amplit2, amplit3, amplit4, amplit5; +reg [7:0] freq0, freq1, freq2, freq3, freq4, freq5; +reg [7:0] oct10, oct32, oct54; +reg [7:0] freqenable; +reg [7:0] noiseenable; +reg [7:0] noisegen; +reg [7:0] envelope0, envelope1; +reg [7:0] ctrl; + +reg [4:0] addr; +wire rst = ~rst_n | ctrl[1]; +reg wr; + +always @(posedge clk_sys) begin + reg old_wr; + old_wr <= wr_n; + + wr <= 0; + if(~rst_n) begin + addr <= 0; + {amplit0, amplit1, amplit2, amplit3, amplit4, amplit5} <= 0; + {freq0, freq1, freq2, freq3, freq4, freq5} <= 0; + {oct10, oct32, oct54} <= 0; + {freqenable, noiseenable, noisegen} <= 0; + {envelope0, envelope1} <= 0; + ctrl <= 0; + end + else begin + if(!cs_n & old_wr & !wr_n) begin + wr <= 1; + if(a0) addr <= din[4:0]; + else begin + case (addr) + 'h00: amplit0 <= din; + 'h01: amplit1 <= din; + 'h02: amplit2 <= din; + 'h03: amplit3 <= din; + 'h04: amplit4 <= din; + 'h05: amplit5 <= din; + + 'h08: freq0 <= din; + 'h09: freq1 <= din; + 'h0A: freq2 <= din; + 'h0B: freq3 <= din; + 'h0C: freq4 <= din; + 'h0D: freq5 <= din; + + 'h10: oct10 <= din; + 'h11: oct32 <= din; + 'h12: oct54 <= din; + + 'h14: freqenable <= din; + 'h15: noiseenable<= din; + 'h16: noisegen <= din; + + 'h18: envelope0 <= din; + 'h19: envelope1 <= din; + + 'h1C: ctrl <= din; + endcase + end + end + end +end + +wire [21:0] out0; +saa1099_triplet top +( + .*, + .vol('{amplit0, amplit1, amplit2}), + .env(envelope0), + + .freq('{freq0, freq1, freq2}), + .octave('{oct10[2:0], oct10[6:4], oct32[2:0]}), + .freq_en(freqenable[2:0]), + + .noise_en(noiseenable[2:0]), + .noise_freq(noisegen[1:0]), + + .wr_addr(wr & a0 & (din[4:0] == 'h18)), + .wr_data(wr & !a0 & (addr == 'h18)), + + .out(out0) +); + +wire [21:0] out1; +saa1099_triplet bottom +( + .*, + .vol('{amplit3, amplit4, amplit5}), + .env(envelope1), + + .freq('{freq3, freq4, freq5}), + .octave('{oct32[6:4], oct54[2:0], oct54[6:4]}), + .freq_en(freqenable[5:3]), + + .noise_en(noiseenable[5:3]), + .noise_freq(noisegen[5:4]), + + .wr_addr(wr & a0 & (din[4:0] == 'h19)), + .wr_data(wr & !a0 & (addr == 'h19)), + + .out(out1) +); + +saa1099_output_mixer outmix_l(.*, .en(ctrl[0]), .in0(out0[10:0]), .in1(out1[10:0]), .out(out_l)); +saa1099_output_mixer outmix_r(.*, .en(ctrl[0]), .in0(out0[21:11]), .in1(out1[21:11]), .out(out_r)); + +endmodule + +///////////////////////////////////////////////////////////////////////////////// +module saa1099_triplet +( + input rst, + input clk_sys, + input ce, + + input [7:0] vol[3], + input [7:0] env, + + input [7:0] freq[3], + input [2:0] octave[3], + input [2:0] freq_en, + + input [2:0] noise_en, + input [1:0] noise_freq, + + input wr_addr, + input wr_data, + + output[21:0] out +); + +wire tone0, tone1, tone2, noise; +wire pulse_noise, pulse_envelope; +wire[21:0] out0, out1, out2; + +saa1099_tone freq_gen0(.*, .out(tone0), .octave(octave[0]), .freq(freq[0]), .pulse(pulse_noise)); +saa1099_tone freq_gen1(.*, .out(tone1), .octave(octave[1]), .freq(freq[1]), .pulse(pulse_envelope)); +saa1099_tone freq_gen2(.*, .out(tone2), .octave(octave[2]), .freq(freq[2]), .pulse()); +saa1099_noise noise_gen(.*, .out(noise)); + +saa1099_amp amp0(.*, .mixmode({noise_en[0], freq_en[0]}), .tone(tone0), .envreg(0), .vol(vol[0]), .out(out0)); +saa1099_amp amp1(.*, .mixmode({noise_en[1], freq_en[1]}), .tone(tone1), .envreg(0), .vol(vol[1]), .out(out1)); +saa1099_amp amp2(.*, .mixmode({noise_en[2], freq_en[2]}), .tone(tone2), .envreg(env), .vol(vol[2]), .out(out2)); + +assign out[10:0] = out0[8:0] + out1[8:0] + out2[8:0]; +assign out[21:11] = out0[17:9] + out1[17:9] + out2[17:9]; + +endmodule + +///////////////////////////////////////////////////////////////////////////////// + +module saa1099_tone +( + input rst, + input clk_sys, + input ce, + input [2:0] octave, + input [7:0] freq, + output reg out, + output reg pulse +); + +wire [16:0] fcount = ((17'd511 - freq) << (4'd8 - octave)) - 1'd1; +always @(posedge clk_sys) begin + reg [16:0] count; + + pulse <= 0; + if(rst) begin + count <= fcount; + out <= 0; + end else if(ce) begin + if(!count) begin + count <= fcount; + pulse <= 1; + out <= ~out; + end else begin + count <= count - 1'd1; + end + end +end + +endmodule + +///////////////////////////////////////////////////////////////////////////////// + +module saa1099_noise +( + input rst, + input clk_sys, + input ce, + input pulse_noise, + input [1:0] noise_freq, + output out +); + +reg [16:0] lfsr = 0; +wire [16:0] new_lfsr = {(lfsr[0] ^ lfsr[2] ^ !lfsr), lfsr[16:1]}; +wire [10:0] fcount = (11'd256 << noise_freq) - 1'b1; + +always @(posedge clk_sys) begin + reg [10:0] count; + + if(rst) begin + count <= fcount; + end else + if(noise_freq != 3) begin + if(ce) begin + if(!count) begin + count <= fcount; + lfsr <= new_lfsr; + end else begin + count <= count - 1'd1; + end + end + end else if(pulse_noise) begin + lfsr <= new_lfsr; + end +end + +assign out = lfsr[0]; + +endmodule + +///////////////////////////////////////////////////////////////////////////////// + +module saa1099_amp +( + input rst, + input clk_sys, + input [7:0] envreg, + input [1:0] mixmode, + input tone, + input noise, + input wr_addr, + input wr_data, + input pulse_envelope, + input [7:0] vol, + output reg [17:0] out +); + +wire phases[8] = '{0,0,0,0,1,1,0,0}; +wire [1:0] env[8][2] = '{'{0,0}, '{1,1}, '{2,0}, '{2,0}, '{3,2}, '{3,2}, '{3,0}, '{3,0}}; +wire [3:0] levels[4][16] = +'{ + '{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + '{15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15}, + '{15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, + '{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15} +}; + +reg [2:0] shape; +reg stereo; +wire resolution = envreg[4]; +wire enable = envreg[7]; +reg [3:0] counter; +reg phase; +wire[3:0] mask = {3'b000, resolution}; + +always @(posedge clk_sys) begin + reg clock; + reg new_data; + + if(rst | ~enable) begin + new_data <= 0; + stereo <= envreg[0]; + shape <= envreg[3:1]; + clock <= envreg[5]; + phase <= 0; + counter <= 0; + end + else begin + if(wr_data) new_data <= 1; + if(clock ? wr_addr : pulse_envelope) begin // pulse from internal or external clock? + counter <= counter + resolution + 1'd1; + if((counter | mask) == 15) begin + if(phase >= phases[shape]) begin + if(~shape[0]) counter <= 15; + if(new_data | shape[0]) begin // if we reached one of the designated points (3) or (4) and there is pending data, load it + new_data <= 0; + stereo <= envreg[0]; + shape <= envreg[3:1]; + clock <= envreg[5]; + phase <= 0; + if(new_data) counter <= 0; + end + end else begin + phase <= 1; + end + end + end + end +end + +wire [3:0] env_l = levels[env[shape][phase]][counter] & ~mask; +wire [3:0] env_r = stereo ? (4'd15 & ~mask) - env_l : env_l; // bit 0 of envreg inverts envelope shape + +reg [1:0] outmix; +always_comb begin + case(mixmode) + 0: outmix <= 0; + 1: outmix <= {tone, 1'b0}; + 2: outmix <= {noise, 1'b0}; + 3: outmix <= {tone & ~noise, tone & noise}; + endcase +end + +wire [8:0] vol_mix_l = {vol[3:1], vol[0] & ~enable, 5'b00000} >> outmix[0]; +wire [8:0] vol_mix_r = {vol[7:5], vol[4] & ~enable, 5'b00000} >> outmix[0]; +wire [8:0] env_out_l; +wire [8:0] env_out_r; +saa1099_mul_env mod_l(.vol(vol_mix_l[8:4]), .env(env_l), .out(env_out_l)); +saa1099_mul_env mod_r(.vol(vol_mix_r[8:4]), .env(env_r), .out(env_out_r)); + +always_comb begin + case({enable, outmix}) + 'b100, 'b101: out = {env_out_r, env_out_l}; + 'b001, 'b010: out = {vol_mix_r, vol_mix_l}; + default: out = 0; + endcase +end + +endmodule + +///////////////////////////////////////////////////////////////////////////////// + +module saa1099_mul_env +( + input [4:0] vol, + input [3:0] env, + output [8:0] out +); + +assign out = (env[0] ? vol : 9'd0)+ + (env[1] ? { vol,1'b0} : 9'd0)+ + (env[2] ? { vol,2'b00} : 9'd0)+ + (env[3] ? {vol,3'b000} : 9'd0); + +endmodule + +///////////////////////////////////////////////////////////////////////////////// + +module saa1099_output_mixer +( + input clk_sys, + input ce, + input en, + input [10:0] in0, + input [10:0] in1, + output reg [7:0] out +); + +wire [17:0] o = 18'd91 * ({1'b0,in0} + {1'b0,in1}); + +// Clean the audio. +always @(posedge clk_sys) begin + reg ced; + ced <= ce; + if(ced) out <= ~en ? 8'h00 : o[17:10]; +end + +endmodule diff --git a/src/tsconf.vhd b/src/tsconf.vhd index 1f89e1f..199ade1 100644 --- a/src/tsconf.vhd +++ b/src/tsconf.vhd @@ -353,6 +353,12 @@ signal gs_c : std_logic_vector(13 downto 0); signal gs_d : std_logic_vector(13 downto 0); signal gs_do_bus : std_logic_vector(7 downto 0); +-- SAA1099 +signal saa_wr_n : std_logic; +signal saa_out_l : std_logic_vector(7 downto 0); +signal saa_out_r : std_logic_vector(7 downto 0); +signal ce_saa : std_logic; + ------------------------------------------------------------------------------- -- COMPONENTS TS Lab ------------------------------------------------------------------------------- @@ -368,7 +374,8 @@ port ( c1 : out std_logic; c2 : out std_logic; c3 : out std_logic; - ay_clk : out std_logic); + ay_clk : out std_logic; + ce_saa : out std_logic); end component; component zclock is @@ -801,6 +808,19 @@ port ( dout : out std_logic_vector(7 downto 0)); end component; +component saa1099 +port ( + clk_sys : in std_logic; + ce : in std_logic; + rst_n : in std_logic; + cs_n : in std_logic; + a0 : in std_logic; + wr_n : in std_logic; + din : in std_logic_vector(7 downto 0); + out_l : out std_logic_vector(7 downto 0); + out_r : out std_logic_vector(7 downto 0)); +end component; + ------------------------------------------------------------------------------- begin @@ -817,7 +837,7 @@ port map ( c1 => c1, c2 => c2, c3 => c3, - ay_clk => open); + ce_saa => ce_saa); TS02: zclock port map ( @@ -1388,6 +1408,18 @@ port map ( OUTC => gs_c, OUTD => gs_d); +U16: saa1099 +port map( + clk_sys => clk_28mhz, + ce => ce_saa, + rst_n => not reset, + cs_n => '0', + a0 => cpu_a_bus(8), -- 0=data, 1=address + wr_n => saa_wr_n, + din => cpu_do_bus, + out_l => saa_out_l, + out_r => saa_out_r); + ------------------------------------------------------------------------------- -- Global ------------------------------------------------------------------------------- @@ -1456,7 +1488,10 @@ port_bff7 <= '1' when (cpu_iorq_n = '0' and cpu_a_bus = X"BFF7" and cpu_m1_n = ' -- Z-Controller SD_CS_N <= sdcs_n_TS; -SOUND_L <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_a & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_a & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_a & "000") + ("0000" & covox_b & "000") + ("00" & gs_a) + ("00" & gs_b); -- + ("0000" & saa_out_l & "000"); -SOUND_R <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_c & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_c & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_c & "000") + ("0000" & covox_d & "000") + ("00" & gs_c) + ("00" & gs_d); -- + ("0000" & saa_out_r & "000"); +-- SAA1099 +saa_wr_n <= '0' when (cpu_iorq_n = '0' and cpu_wr_n = '0' and cpu_a_bus(7 downto 0) = "11111111" and dos = '0') else '1'; + +SOUND_L <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_a & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_a & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_a & "000") + ("0000" & covox_b & "000") + ("00" & gs_a) + ("00" & gs_b) + ("0000" & saa_out_l & "000"); +SOUND_R <= ("0000" & port_xxfe_reg(4) & "0000000000") + ("0000" & ssg_cn0_c & "000") + ("0000" & ssg_cn0_b & "000") + ("0000" & ssg_cn1_c & "000") + ("0000" & ssg_cn1_b & "000") + ("0000" & covox_c & "000") + ("0000" & covox_d & "000") + ("00" & gs_c) + ("00" & gs_d) + ("0000" & saa_out_r & "000"); end rtl;