summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/sn76477.h
blob: 04e6386b62b8fd781f392f22ad61e7b0e20c2262 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// license:BSD-3-Clause
// copyright-holders:Zsolt Vasvari
/*****************************************************************************

    Texas Instruments SN76477 emulator

    SN76477 pin layout. There is a corresponding interface variable with the
    same name. The only exception is noise_clock which must be programmatically
    set.  The other pins have programmatic equivalents as well.
    The name of the function is SN76477_<pinname>_w.
    All capacitor functions can also specify a fixed voltage on the cap.
    The name of this function is SN76477_<pinname>_voltage_w

                      +-------------------+
          envelope_1  | 1      | |      28|  envelope_2
                      | 2 GND   -       27|  mixer_c
         noise_clock  | 3               26|  mixer_a
     noise_clock_res  | 4               25|  mixer_b
    noise_filter_res  | 5               24|  one_shot_res
    noise_filter_cap  | 6               23|  one_shot_cap
           decay_res  | 7               22|  vco
    attack_decay_cap  | 8               21|  slf_cap
              enable o| 9               20|  slf_res
          attack_res  |10               19|  pitch_voltage
       amplitude_res  |11               18|  vco_res
        feedback_res  |12               17|  vco_cap
                      |13 OUTPUT        16|  vco_voltage
                      |14 Vcc   +5V OUT 15|
                      +-------------------+

    All resistor values in Ohms
    All capacitor values in Farads
    Use RES_K, RES_M and CAP_U, CAP_N, CAP_P macros in rescap.h to convert
    magnitudes, eg. 220k = RES_K(220), 47nF = CAP_N(47)

 *****************************************************************************/

#ifndef MAME_SOUND_SN76477_H
#define MAME_SOUND_SN76477_H

#pragma once

#include "machine/rescap.h"


/*****************************************************************************
 *
 *  Interface definition
 *
 *****************************************************************************/


#define MCFG_SN76477_NOISE_PARAMS(_clock_res, _filter_res, _filter_cap) \
	downcast<sn76477_device &>(*device).set_noise_params(_clock_res, _filter_res, _filter_cap);

#define MCFG_SN76477_DECAY_RES(_decay_res) \
	downcast<sn76477_device &>(*device).set_decay_res(_decay_res);

#define MCFG_SN76477_ATTACK_PARAMS(_decay_cap, _res) \
	downcast<sn76477_device &>(*device).set_attack_params(_decay_cap, _res);

#define MCFG_SN76477_AMP_RES(_amp_res) \
	downcast<sn76477_device &>(*device).set_amp_res(_amp_res);

#define MCFG_SN76477_FEEDBACK_RES(_feedback_res) \
	downcast<sn76477_device &>(*device).set_feedback_res(_feedback_res);

#define MCFG_SN76477_VCO_PARAMS(_volt, _cap, _res) \
	downcast<sn76477_device &>(*device).set_vco_params(_volt, _cap, _res);

#define MCFG_SN76477_PITCH_VOLTAGE(_volt) \
	downcast<sn76477_device &>(*device).set_pitch_voltage(_volt);

#define MCFG_SN76477_SLF_PARAMS(_cap, _res) \
	downcast<sn76477_device &>(*device).set_slf_params(_cap, _res);

#define MCFG_SN76477_ONESHOT_PARAMS(_cap, _res) \
	downcast<sn76477_device &>(*device).set_oneshot_params(_cap, _res);

#define MCFG_SN76477_VCO_MODE(_mode) \
	downcast<sn76477_device &>(*device).set_vco_mode(_mode);

#define MCFG_SN76477_MIXER_PARAMS(_a, _b, _c) \
	downcast<sn76477_device &>(*device).set_mixer_params(_a, _b, _c);

#define MCFG_SN76477_ENVELOPE_PARAMS(_env1, _env2) \
	downcast<sn76477_device &>(*device).set_envelope_params(_env1, _env2);

#define MCFG_SN76477_ENABLE(_enable) \
	downcast<sn76477_device &>(*device).set_enable(_enable);

class sn76477_device : public device_t,
						public device_sound_interface
{
public:
	sn76477_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);

	void set_noise_params(double clock_res, double filter_res, double filter_cap)
	{
		m_noise_clock_res = clock_res;
		m_noise_filter_res = filter_res;
		m_noise_filter_cap = filter_cap;
	}
	void set_decay_res(double decay_res) { m_decay_res = decay_res; }
	void set_attack_params(double decay_cap, double res)
	{
		m_attack_decay_cap = decay_cap;
		m_attack_res = res;
	}
	void set_amp_res(double amp_res) { m_amplitude_res = amp_res; }
	void set_feedback_res(double feedback_res) { m_feedback_res = feedback_res; }
	void set_vco_params(double volt, double cap, double res)
	{
		m_vco_voltage = volt;
		m_vco_cap = cap;
		m_vco_res = res;
	}
	void set_pitch_voltage(double volt) { m_pitch_voltage = volt; }
	void set_slf_params(double cap, double res)
	{
		m_slf_cap = cap;
		m_slf_res = res;
	}
	void set_oneshot_params(double cap, double res)
	{
		m_one_shot_cap = cap;
		m_one_shot_res = res;
	}
	void set_vco_mode(uint32_t mode) { m_vco_mode = mode; }
	void set_mixer_params(uint32_t a, uint32_t b, uint32_t c)
	{
		m_mixer_a = a;
		m_mixer_b = b;
		m_mixer_c = c;
	}
	void set_envelope_params(uint32_t env1, uint32_t env2)
	{
		m_envelope_1 = env1;
		m_envelope_2 = env2;
	}
	void set_enable(uint32_t enable) { m_enable = enable; }


	/* these functions take 0 or 1 as a logic input */
	WRITE_LINE_MEMBER( enable_w );      /* active LO, 0 = enabled, 1 = disabled */
	WRITE_LINE_MEMBER( mixer_a_w );
	WRITE_LINE_MEMBER( mixer_b_w );
	WRITE_LINE_MEMBER( mixer_c_w );
	WRITE_LINE_MEMBER( envelope_1_w );
	WRITE_LINE_MEMBER( envelope_2_w );
	WRITE_LINE_MEMBER( vco_w );         /* 0 = external, 1 = controlled by SLF */
	WRITE_LINE_MEMBER( noise_clock_w ); /* noise clock write, if noise_clock_res = 0 */

	/* these functions take a resistor value in Ohms */
	void one_shot_res_w(double data);
	void slf_res_w(double data);
	void vco_res_w(double data);
	void noise_clock_res_w(double data);  /* = 0 if the noise gen is clocked via noise_clock */
	void noise_filter_res_w(double data);
	void decay_res_w(double data);
	void attack_res_w(double data);
	void amplitude_res_w(double data);
	void feedback_res_w(double data);

	/* these functions take a capacitor value in Farads or the voltage on it in Volts */
	static constexpr double EXTERNAL_VOLTAGE_DISCONNECT = -1.0;  /* indicates that the voltage is internally computed,
	                                                           can be used in all the functions that take a
	                                                           voltage on a capacitor */
	void one_shot_cap_w(double data);
	void one_shot_cap_voltage_w(double data);
	void slf_cap_w(double data);
	void slf_cap_voltage_w(double data);
	void vco_cap_w(double data);
	void vco_cap_voltage_w(double data);
	void noise_filter_cap_w(double data);
	void noise_filter_cap_voltage_w(double data);
	void attack_decay_cap_w(double data);
	void attack_decay_cap_voltage_w(double data);

	/* these functions take a voltage value in Volts */
	void vco_voltage_w(double data);
	void pitch_voltage_w(double data);

protected:
	// device-level overrides
	virtual void device_start() override;
	virtual void device_stop() override;

	// sound stream update overrides
	virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;

private:
	/* chip's external interface */
	uint32_t m_enable;
	uint32_t m_envelope_mode;
	uint32_t m_vco_mode;
	uint32_t m_mixer_mode;

	double m_one_shot_res;
	double m_one_shot_cap;
	uint32_t m_one_shot_cap_voltage_ext;

	double m_slf_res;
	double m_slf_cap;
	uint32_t m_slf_cap_voltage_ext;

	double m_vco_voltage;
	double m_vco_res;
	double m_vco_cap;
	uint32_t m_vco_cap_voltage_ext;

	double m_noise_clock_res;
	uint32_t m_noise_clock_ext;
	uint32_t m_noise_clock;
	double m_noise_filter_res;
	double m_noise_filter_cap;
	uint32_t m_noise_filter_cap_voltage_ext;

	double m_attack_res;
	double m_decay_res;
	double m_attack_decay_cap;
	uint32_t m_attack_decay_cap_voltage_ext;

	double m_amplitude_res;
	double m_feedback_res;
	double m_pitch_voltage;

	// internal state
	double m_one_shot_cap_voltage;        /* voltage on the one-shot cap */
	uint32_t m_one_shot_running_ff;         /* 1 = one-shot running, 0 = stopped */

	double m_slf_cap_voltage;             /* voltage on the SLF cap */
	uint32_t m_slf_out_ff;                  /* output of the SLF */

	double m_vco_cap_voltage;             /* voltage on the VCO cap */
	uint32_t m_vco_out_ff;                  /* output of the VCO */
	uint32_t m_vco_alt_pos_edge_ff;         /* keeps track of the # of positive edges for VCO Alt envelope */

	double m_noise_filter_cap_voltage;    /* voltage on the noise filter cap */
	uint32_t m_real_noise_bit_ff;           /* the current noise bit before filtering */
	uint32_t m_filtered_noise_bit_ff;       /* the noise bit after filtering */
	uint32_t m_noise_gen_count;             /* noise freq emulation */

	double m_attack_decay_cap_voltage;    /* voltage on the attack/decay cap */

	uint32_t m_rng;                         /* current value of the random number generator */

	// configured by the drivers and used to setup m_mixer_mode & m_envelope_mode at start
	uint32_t m_mixer_a;
	uint32_t m_mixer_b;
	uint32_t m_mixer_c;
	uint32_t m_envelope_1;
	uint32_t m_envelope_2;

	/* others */
	sound_stream *m_channel;              /* returned by stream_create() */
	int m_our_sample_rate;                    /* from machine.sample_rate() */

	wav_file *m_file;                     /* handle of the wave file to produce */

	double compute_one_shot_cap_charging_rate();
	double compute_one_shot_cap_discharging_rate();
	double compute_slf_cap_charging_rate();
	double compute_slf_cap_discharging_rate();
	double compute_vco_cap_charging_discharging_rate();
	double compute_vco_duty_cycle();
	uint32_t compute_noise_gen_freq();
	double compute_noise_filter_cap_charging_rate();
	double compute_noise_filter_cap_discharging_rate();
	double compute_attack_decay_cap_charging_rate();
	double compute_attack_decay_cap_discharging_rate();
	double compute_center_to_peak_voltage_out();

	void log_enable_line();
	void log_mixer_mode();
	void log_envelope_mode();
	void log_vco_mode();
	void log_one_shot_time();
	void log_slf_freq();
	void log_vco_pitch_voltage();
	void log_vco_duty_cycle();
	void log_vco_freq();
	void log_vco_ext_voltage();
	void log_noise_gen_freq();
	void log_noise_filter_freq();
	void log_attack_time();
	void log_decay_time();
	void log_voltage_out();
	void log_complete_state();

	void open_wav_file();
	void close_wav_file();
	void add_wav_data(int16_t data_l, int16_t data_r);

	void intialize_noise();
	inline uint32_t generate_next_real_noise_bit();

	void state_save_register();
};

DECLARE_DEVICE_TYPE(SN76477, sn76477_device)

#endif // MAME_SOUND_SN76477_H