1 // license:BSD-3-Clause
2 // copyright-holders:Sandro Ronco
3 /***************************************************************************
4 
5     Videotone TVC 32/64 sound emulation
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "tvc.h"
11 
12 // device type definition
13 DEFINE_DEVICE_TYPE(TVC_SOUND, tvc_sound_device, "tvc_sound", "TVC 64 Custom Sound")
14 
15 //-------------------------------------------------
16 //  tvc_sound_device - constructor
17 //-------------------------------------------------
18 
tvc_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)19 tvc_sound_device::tvc_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
20 	device_t(mconfig, TVC_SOUND, tag, owner, clock),
21 	device_sound_interface(mconfig, *this),
22 	m_write_sndint(*this)
23 {
24 }
25 
26 //-------------------------------------------------
27 //  device_start - device-specific startup
28 //-------------------------------------------------
device_start()29 void tvc_sound_device::device_start()
30 {
31 	// resolve callbacks
32 	m_write_sndint.resolve_safe();
33 
34 	m_stream = stream_alloc(0, 1, machine().sample_rate());
35 	m_sndint_timer = timer_alloc(TIMER_SNDINT);
36 }
37 
38 //-------------------------------------------------
39 //  device_reset - device-specific reset
40 //-------------------------------------------------
device_reset()41 void tvc_sound_device::device_reset()
42 {
43 	m_enabled = 0;
44 	m_freq    = 0;
45 	m_incr    = 0;
46 	m_signal  = 1;
47 	m_sndint_timer->reset();
48 }
49 
50 
51 //-------------------------------------------------
52 //  device_timer - handler timer events
53 //-------------------------------------------------
54 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)55 void tvc_sound_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
56 {
57 	m_write_sndint(1);
58 }
59 
60 //-------------------------------------------------
61 //  sound_stream_update - handle update requests for
62 //  our sound stream
63 //-------------------------------------------------
64 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)65 void tvc_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
66 {
67 	auto &output = outputs[0];
68 	int rate = output.sample_rate() / 2;
69 	if (m_enabled && m_freq)
70 	{
71 		for (int sampindex = 0; sampindex < output.samples(); sampindex++)
72 		{
73 			output.put_int(sampindex, m_signal * m_volume, 32768 / 0x0800);
74 			m_incr -= m_freq;
75 			while(m_incr < 0)
76 			{
77 				m_incr += rate;
78 				m_signal = -m_signal;
79 			}
80 		}
81 	}
82 	else
83 	{
84 		// fill output with 0 if the sound is disabled
85 		output.fill(0);
86 	}
87 }
88 
89 
90 
91 //-------------------------------------------------
92 //  ports write
93 //-------------------------------------------------
94 
write(offs_t offset,uint8_t data)95 void tvc_sound_device::write(offs_t offset, uint8_t data)
96 {
97 	m_stream->update();
98 
99 	m_ports[offset] = data;
100 
101 	switch(offset)
102 	{
103 		case 1:
104 			m_enabled = BIT(data, 4);
105 			// fall through
106 
107 		case 0:
108 		{
109 			uint16_t pitch = (m_ports[0] | (m_ports[1]<<8)) & 0x0fff;
110 			m_freq = (pitch == 0x0fff) ? 0 : (int)(195312.5 / (4096 - pitch));
111 
112 			if ((m_ports[1] & 0x20) && m_freq != 0)
113 				m_sndint_timer->adjust(attotime::from_hz(m_freq), 0, attotime::from_hz(m_freq));
114 			else
115 				m_sndint_timer->reset();
116 
117 			break;
118 		}
119 
120 		case 2:
121 			m_volume = (data>>2) & 0x0f;
122 			break;
123 	}
124 }
125 
126 
127 //-------------------------------------------------
128 //  tvc_sound_device::reset_divider
129 //-------------------------------------------------
130 
reset_divider()131 void tvc_sound_device::reset_divider()
132 {
133 	m_stream->update();
134 
135 	m_incr = 0;
136 	m_signal = 1;
137 
138 	if (m_ports[1] & 0x20 && m_freq != 0)
139 		m_sndint_timer->adjust(attotime::from_hz(m_freq), 0, attotime::from_hz(m_freq));
140 	else
141 		m_sndint_timer->reset();
142 }
143