1 // license:BSD-3-Clause
2 // copyright-holders:Sandro Ronco
3 /***************************************************************************
4 
5     Alesis HR-16 sound (DM3AG + PCM54) emulation
6 
7     TODO:
8     - volume
9     - panning
10     - output 2
11 
12 ****************************************************************************/
13 
14 #include "emu.h"
15 #include "includes/alesis.h"
16 #include "speaker.h"
17 
18 #define LOG 1
19 
20 // device type definition
21 DEFINE_DEVICE_TYPE(ALESIS_DM3AG, alesis_dm3ag_device, "alesis_dm3ag", "Alesis DM3AG")
22 
23 /***************************************************************************
24     IMPLEMENTATION
25 ***************************************************************************/
26 
27 
28 //-------------------------------------------------
29 //  alesis_dm3ag_device - constructor
30 //-------------------------------------------------
31 
alesis_dm3ag_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)32 alesis_dm3ag_device::alesis_dm3ag_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
33 	: device_t(mconfig, ALESIS_DM3AG, tag, owner, clock)
34 	, m_dac(*this, "dac")
35 	, m_samples(*this, DEVICE_SELF)
36 {
37 }
38 
39 //-------------------------------------------------
40 //  device_add_mconfig
41 //-------------------------------------------------
42 
device_add_mconfig(machine_config & config)43 void alesis_dm3ag_device::device_add_mconfig(machine_config &config)
44 {
45 	SPEAKER(config, "lspeaker1").front_left();
46 	SPEAKER(config, "rspeaker1").front_right();
47 	SPEAKER(config, "lspeaker2").front_left();
48 	SPEAKER(config, "rspeaker2").front_right();
49 	PCM54HP(config, m_dac, 0).add_route(ALL_OUTPUTS, "lspeaker1", 1.0).add_route(ALL_OUTPUTS, "rspeaker1", 1.0); // PCM54HP DAC + R63/R73-75 + Sample and hold
50 }
51 
52 //-------------------------------------------------
53 //  device_start - device-specific startup
54 //-------------------------------------------------
55 
device_start()56 void alesis_dm3ag_device::device_start()
57 {
58 	m_dac_update_timer = timer_alloc(TIMER_DAC_UPDATE);
59 }
60 
61 //-------------------------------------------------
62 //  device_reset - device-specific reset
63 //-------------------------------------------------
64 
device_reset()65 void alesis_dm3ag_device::device_reset()
66 {
67 	m_dac_update_timer->adjust(attotime::from_hz(48000), 0, attotime::from_hz(48000));
68 
69 	m_output_active = false;
70 	m_count = 0;
71 	m_cur_sample = 0;
72 	m_shift = 0;
73 	memset(m_cmd, 0, sizeof(m_cmd));
74 	m_dac->write(0x8000);
75 }
76 
77 //-------------------------------------------------
78 //  device_timer - handler timer events
79 //-------------------------------------------------
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)80 void alesis_dm3ag_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
81 {
82 	if (m_output_active)
83 	{
84 		int16_t sample = m_samples[m_cur_sample++];
85 		int count = 0;
86 
87 		while (sample == -128)
88 		{
89 			count++;
90 
91 			if (count == 1 && m_shift)
92 			{
93 				/*
94 				    The HR-16 seems to use a simple scheme to generate 16-bit samples from its 8-bit sample ROMs.
95 				    When the sound starts the 8-bit sample is sent to the most significant bits of the DAC and every
96 				    time a -1 sample is found the data is shifted one position to right.
97 				*/
98 				m_shift--;
99 
100 				if (LOG)    logerror("DM3AG '%s' shift: %02x\n", tag(), m_shift);
101 			}
102 
103 			// every block ends with three or more -1 samples
104 			if (m_cur_sample == 0xfffff || count >= 3)
105 			{
106 				m_output_active = false;
107 				sample = 0;
108 
109 				if (LOG)    logerror("DM3AG '%s' stop: %d, len: %d\n", tag(), m_cur_sample, m_cur_sample-((m_cmd[0]<<12) | (m_cmd[1]<<4) | ((m_cmd[2]>>4) & 0x0f)));
110 
111 				break;
112 			}
113 
114 			sample = m_samples[m_cur_sample++];
115 		}
116 
117 		m_dac->write(0x8000 - (sample << m_shift));
118 	}
119 }
120 
write(uint8_t data)121 void alesis_dm3ag_device::write(uint8_t data)
122 {
123 	if (LOG)    logerror("DM3AG '%s' write: %02x\n", tag(), data);
124 
125 	m_cmd[m_count++] = data;
126 
127 	if (m_count == 5)
128 	{
129 		/*
130 		    commands are sent in block of 5 bytes (40 bits)
131 
132 		    bit 00-19       sample position in the roms
133 		    bit 20-23       ???
134 		    bit 24-31       volume
135 		    bit 32-34       panning
136 		    bit 35          output selector: 0 = out2, 1 = out1
137 		    bit 36-39       ???
138 		*/
139 
140 		m_cur_sample = (m_cmd[0]<<12) | (m_cmd[1]<<4) | ((m_cmd[2]>>4) & 0x0f);
141 
142 		if (m_cur_sample > 0)
143 		{
144 			m_output_active = true;
145 			m_shift = 8;
146 
147 			if (LOG)
148 			{
149 				bool good_pos = (m_cur_sample<2 || m_samples[m_cur_sample-2] == -128);
150 
151 				logerror("DM3AG '%s' start: %d (%s), vol: %02x out: %d pan: %d\n", tag(), m_cur_sample, good_pos ? "ok": "no", m_cmd[3], m_cmd[4] & 0x10 ? 1 : 2, (m_cmd[4]>>5)&7);
152 			}
153 		}
154 
155 		m_count = 0;
156 	}
157 }
158