summaryrefslogtreecommitdiffstats
path: root/src/devices/sound/tms5110.h
blob: 066590302da00d11ad7ba39d3ef06577bdb6e38d (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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo, Jarek Burczynski, Aaron Giles, Jonathan Gevaryahu, Couriersud
#ifndef MAME_SOUND_TMS5110_H
#define MAME_SOUND_TMS5110_H

#pragma once


/* HACK: if defined, uses impossibly perfect 'straight line' interpolation */
#undef TMS5110_PERFECT_INTERPOLATION_HACK

/* clock rate = 80 * output sample rate,     */
/* usually 640000 for 8000 Hz sample rate or */
/* usually 800000 for 10000 Hz sample rate.  */

#define MCFG_TMS5110_M0_CB(_devcb) \
	devcb = &downcast<tms5110_device &>(*device).set_m0_callback(DEVCB_##_devcb);

#define MCFG_TMS5110_M1_CB(_devcb) \
	devcb = &downcast<tms5110_device &>(*device).set_m1_callback(DEVCB_##_devcb);

#define MCFG_TMS5110_ADDR_CB(_devcb) \
	devcb = &downcast<tms5110_device &>(*device).set_addr_callback(DEVCB_##_devcb);

#define MCFG_TMS5110_DATA_CB(_devcb) \
	devcb = &downcast<tms5110_device &>(*device).set_data_callback(DEVCB_##_devcb);

#define MCFG_TMS5110_ROMCLK_CB(_devcb) \
	devcb = &downcast<tms5110_device &>(*device).set_romclk_callback(DEVCB_##_devcb);


class tms5110_device : public device_t, public device_sound_interface
{
public:
	/* TMS5110 commands */
													/* CTL8  CTL4  CTL2  CTL1  |   PDC's  */
													/* (MSB)             (LSB) | required */
	static constexpr uint8_t CMD_RESET        = 0;  /*    0     0     0     x  |     1    */
	static constexpr uint8_t CMD_LOAD_ADDRESS = 2;  /*    0     0     1     x  |     2    */
	static constexpr uint8_t CMD_OUTPUT       = 4;  /*    0     1     0     x  |     3    */
	static constexpr uint8_t CMD_SPKSLOW      = 6;  /*    0     1     1     x  |     1    */
	/* Note: TMS5110_CMD_SPKSLOW is undocumented on the datasheets, it only appears
	   on the patents. It might not actually work properly on some of the real
	   chips as manufactured. Acts the same as CMD_SPEAK, but makes the
	   interpolator take two A cycles wherever it would normally only take one,
	   effectively making speech of any given word take 1.5x as long as normal. */
	static constexpr uint8_t CMD_READ_BIT    =  8;  /*    1     0     0     x  |     1    */
	static constexpr uint8_t CMD_SPEAK       = 10;  /*    1     0     1     x  |     1    */
	static constexpr uint8_t CMD_READ_BRANCH = 12;  /*    1     1     0     x  |     1    */
	static constexpr uint8_t CMD_TEST_TALK   = 14;  /*    1     1     1     x  |     3    */

	tms5110_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	template <class Object> devcb_base &set_m0_callback(Object &&cb) { return m_m0_cb.set_callback(std::forward<Object>(cb)); }
	template <class Object> devcb_base &set_m1_callback(Object &&cb) { return m_m1_cb.set_callback(std::forward<Object>(cb)); }
	template <class Object> devcb_base &set_addr_callback(Object &&cb) { return m_addr_cb.set_callback(std::forward<Object>(cb)); }
	template <class Object> devcb_base &set_data_callback(Object &&cb) { return m_data_cb.set_callback(std::forward<Object>(cb)); }
	template <class Object> devcb_base &set_romclk_callback(Object &&cb) { return m_romclk_cb.set_callback(std::forward<Object>(cb)); }

	DECLARE_WRITE8_MEMBER( ctl_w );
	DECLARE_READ8_MEMBER( ctl_r );
	DECLARE_WRITE_LINE_MEMBER( pdc_w );

	// this is only used by cvs.c
	// it is not related at all to the speech generation and conflicts with the new ROM controller interface.
	DECLARE_READ_LINE_MEMBER( romclk_hack_r );

protected:
	tms5110_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, int variant);

	// device-level overrides
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_clock_changed() override;

	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;

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

	uint8_t TALK_STATUS() const { return m_SPEN | m_TALKD; }

	uint8_t m_SPEN;             /* set on speak command, cleared on stop command or reset command */
	uint8_t m_TALK;             /* set on SPEN & RESETL4(pc12->pc0 transition), cleared on stop command or reset command */
	uint8_t m_TALKD;            /* TALK(TCON) value, latched every RESETL4 */
	sound_stream *m_stream;

private:
	void new_int_write(uint8_t rc, uint8_t m0, uint8_t m1, uint8_t addr);
	void new_int_write_addr(uint8_t addr);
	uint8_t new_int_read();
	void register_for_save_states();
	int extract_bits(int count);
	void perform_dummy_read();
	int32_t lattice_filter();
	void process(int16_t *buffer, unsigned int size);
	void PDC_set(int data);
	void parse_frame();

	uint8_t &OLD_FRAME_SILENCE_FLAG() { return m_OLDE; } // 1 if E=0, 0 otherwise.
	uint8_t &OLD_FRAME_UNVOICED_FLAG() { return m_OLDP; } // 1 if P=0 (unvoiced), 0 if voiced

	bool NEW_FRAME_STOP_FLAG() const { return m_new_frame_energy_idx == 0x0F; } // 1 if this is a stop (Energy = 0x0F) frame
	bool NEW_FRAME_SILENCE_FLAG() const { return m_new_frame_energy_idx == 0; } // ditto as above
	bool NEW_FRAME_UNVOICED_FLAG() const { return m_new_frame_pitch_idx == 0; } // ditto as above

	// internal state
	/* table */
	optional_region_ptr<uint8_t> m_table;

	/* coefficient tables */
	const int m_variant;                /* Variant of the 5110 - see tms5110.h */

	/* coefficient tables */
	const struct tms5100_coeffs *m_coeff;

	/* these contain global status bits */
	uint8_t m_PDC;
	uint8_t m_CTL_pins;
	uint8_t m_state;

	/* Rom interface */
	uint32_t m_address;
	uint8_t  m_next_is_address;
	uint8_t  m_schedule_dummy_read;
	uint8_t  m_addr_bit;
	/* read byte */
	uint8_t  m_CTL_buffer;

	/* callbacks */
	devcb_write_line   m_m0_cb;      // the M0 line
	devcb_write_line   m_m1_cb;      // the M1 line
	devcb_write8       m_addr_cb;    // Write to ADD1,2,4,8 - 4 address bits
	devcb_read_line    m_data_cb;    // Read one bit from ADD8/Data - voice data
	// On a real chip rom_clk is running all the time
	// Here, we only use it to properly emulate the protocol.
	// Do not rely on it to be a timed signal.
	devcb_write_line   m_romclk_cb;  // rom clock - Only used to drive the data lines

	/* these contain data describing the current and previous voice frames */
	uint8_t m_OLDE;
	uint8_t m_OLDP;

	uint8_t m_new_frame_energy_idx;
	uint8_t m_new_frame_pitch_idx;
	uint8_t m_new_frame_k_idx[10];


	/* these are all used to contain the current state of the sound generation */
#ifndef TMS5110_PERFECT_INTERPOLATION_HACK
	int16_t m_current_energy;
	int16_t m_current_pitch;
	int16_t m_current_k[10];
#else
	uint8_t m_old_frame_energy_idx;
	uint8_t m_old_frame_pitch_idx;
	uint8_t m_old_frame_k_idx[10];
	uint8_t m_old_zpar;
	uint8_t m_old_uv_zpar;

	int32_t m_current_energy;
	int32_t m_current_pitch;
	int32_t m_current_k[10];
#endif

	uint16_t m_previous_energy; /* needed for lattice filter to match patent */

	uint8_t m_subcycle;         /* contains the current subcycle for a given PC: 0 is A' (only used on SPKSLOW mode on 51xx), 1 is A, 2 is B */
	uint8_t m_subc_reload;      /* contains 1 for normal speech, 0 when SPKSLOW is active */
	uint8_t m_PC;               /* current parameter counter (what param is being interpolated), ranges from 0 to 12 */
	/* NOTE: the interpolation period counts 1,2,3,4,5,6,7,0 for divide by 8,8,8,4,4,2,2,1 */
	uint8_t m_IP;               /* the current interpolation period */
	uint8_t m_inhibit;          /* If 1, interpolation is inhibited until the DIV1 period */
	uint8_t m_uv_zpar;          /* If 1, zero k5 thru k10 coefficients */
	uint8_t m_zpar;             /* If 1, zero ALL parameters. */
	uint8_t m_pitch_zero;       /* circuit 412; pitch is forced to zero under certain circumstances */
	uint16_t m_pitch_count;     /* pitch counter; provides chirp rom address */

	int32_t m_u[11];
	int32_t m_x[10];

	uint16_t m_RNG;             /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 TODO: no it isn't */
	int16_t m_excitation_data;

	/* The TMS51xx has two different ways of providing output data: the
	   analog speaker pins (which were usually used) and the Digital I/O pin.
	   The internal DAC used to feed the analog pins is only 8 bits, and has the
	   funny clipping/clamping logic, while the digital pin gives full 10 bit
	   resolution of the output data.
	   TODO: add a way to set/reset this other than the FORCE_DIGITAL define
	 */
	uint8_t m_digital_select;

	int32_t m_speech_rom_bitnum;

	uint8_t m_romclk_hack_timer_started;
	uint8_t m_romclk_hack_state;

	emu_timer *m_romclk_hack_timer;
};


class tms5100_device : public tms5110_device
{
public:
	tms5100_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class tmc0281_device : public tms5110_device
{
public:
	tmc0281_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class tms5100a_device : public tms5110_device
{
public:
	tms5100a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class tmc0281d_device : public tms5110_device
{
public:
	tmc0281d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class cd2801_device : public tms5110_device
{
public:
	cd2801_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class cd2802_device : public tms5110_device
{
public:
	cd2802_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class tms5110a_device : public tms5110_device
{
public:
	tms5110a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};


class m58817_device : public tms5110_device
{
public:
	m58817_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	DECLARE_READ8_MEMBER( status_r );
};


DECLARE_DEVICE_TYPE(TMS5110,  tms5110_device)
DECLARE_DEVICE_TYPE(TMS5100,  tms5100_device)
DECLARE_DEVICE_TYPE(TMC0281,  tmc0281_device)
DECLARE_DEVICE_TYPE(TMS5100A, tms5100a_device)
DECLARE_DEVICE_TYPE(TMC0281D, tmc0281d_device)
DECLARE_DEVICE_TYPE(CD2801,   cd2801_device)
DECLARE_DEVICE_TYPE(CD2802,   cd2802_device)
DECLARE_DEVICE_TYPE(TMS5110A, tms5110a_device)
DECLARE_DEVICE_TYPE(M58817,   m58817_device)



/* PROM controlled TMS5110 interface */

class tmsprom_device : public device_t
{
public:
	tmsprom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	void set_region(const char *region) { m_prom.set_tag(region); }
	void set_rom_size(uint32_t rom_size) { m_rom_size = rom_size; }
	void set_pdc_bit(uint8_t pdc_bit) { m_pdc_bit = pdc_bit; }
	void set_ctl1_bit(uint8_t ctl1_bit) { m_ctl1_bit = ctl1_bit; }
	void set_ctl2_bit(uint8_t ctl2_bit) { m_ctl2_bit = ctl2_bit; }
	void set_ctl4_bit(uint8_t ctl4_bit) { m_ctl4_bit = ctl4_bit; }
	void set_ctl8_bit(uint8_t ctl8_bit) { m_ctl8_bit = ctl8_bit; }
	void set_reset_bit(uint8_t reset_bit) { m_reset_bit = reset_bit; }
	void set_stop_bit(uint8_t stop_bit) { m_stop_bit = stop_bit; }
	template<class Object> devcb_base &set_pdc_callback(Object object) { return m_pdc_cb.set_callback(std::forward<Object>(object)); }
	template<class Object> devcb_base &set_ctl_callback(Object object) { return m_ctl_cb.set_callback(std::forward<Object>(object)); }

	DECLARE_WRITE_LINE_MEMBER( m0_w );
	DECLARE_READ_LINE_MEMBER( data_r );

	/* offset is rom # */
	DECLARE_WRITE8_MEMBER( rom_csq_w );
	DECLARE_WRITE8_MEMBER( bit_w );
	DECLARE_WRITE_LINE_MEMBER( enable_w );

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

	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;

private:
	void register_for_save_states();
	void update_prom_cnt();

	// internal state
	/* Rom interface */
	uint32_t m_address;
	/* ctl lines */
	uint8_t  m_m0;
	uint8_t  m_enable;
	uint32_t  m_base_address;
	uint8_t  m_bit;

	int m_prom_cnt;

	required_region_ptr<uint8_t> m_rom;
	required_region_ptr<uint8_t> m_prom;
	uint32_t m_rom_size;                /* individual rom_size */
	uint8_t m_pdc_bit;                  /* bit # of pdc line */
	/* virtual bit 8: constant 0, virtual bit 9:constant 1 */
	uint8_t m_ctl1_bit;                 /* bit # of ctl1 line */
	uint8_t m_ctl2_bit;                 /* bit # of ctl2 line */
	uint8_t m_ctl4_bit;                 /* bit # of ctl4 line */
	uint8_t m_ctl8_bit;                 /* bit # of ctl8 line */
	uint8_t m_reset_bit;                /* bit # of rom reset */
	uint8_t m_stop_bit;                 /* bit # of stop */
	devcb_write_line m_pdc_cb;      /* tms pdc func */
	devcb_write8 m_ctl_cb;          /* tms ctl func */

	emu_timer *m_romclk_timer;
};

DECLARE_DEVICE_TYPE(TMSPROM, tmsprom_device)

#define MCFG_TMSPROM_REGION(_region) \
	downcast<tmsprom_device &>(*device).set_region(_region);

#define MCFG_TMSPROM_ROM_SIZE(_size) \
	downcast<tmsprom_device &>(*device).set_rom_size(_size);

#define MCFG_TMSPROM_PDC_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_pdc_bit(_bit);

#define MCFG_TMSPROM_CTL1_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_ctl1_bit(_bit);

#define MCFG_TMSPROM_CTL2_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_ctl2_bit(_bit);

#define MCFG_TMSPROM_CTL4_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_ctl4_bit(_bit);

#define MCFG_TMSPROM_CTL8_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_ctl8_bit(_bit);

#define MCFG_TMSPROM_RESET_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_reset_bit(_bit);

#define MCFG_TMSPROM_STOP_BIT(_bit) \
	downcast<tmsprom_device &>(*device).set_stop_bit(_bit);

#define MCFG_TMSPROM_PDC_CB(_devcb) \
	devcb = &downcast<tmsprom_device &>(*device).set_pdc_callback(DEVCB_##_devcb);

#define MCFG_TMSPROM_CTL_CB(_devcb) \
	devcb = &downcast<tmsprom_device &>(*device).set_ctl_callback(DEVCB_##_devcb);

#endif // MAME_SOUND_TMS5110_H