swp30: Make the stream synchronous, add dummy-ish internal register read, more logging

This commit is contained in:
Olivier Galibert 2021-03-22 22:51:20 +01:00
parent 22823416c1
commit b59544c13c
3 changed files with 238 additions and 124 deletions

View File

@ -6,6 +6,8 @@
#include "emu.h" #include "emu.h"
#include "swp30.h" #include "swp30.h"
static int scount = 0;
/* /*
The SWP30 is the combination of a rompler called AWM2 (Advanced Wave The SWP30 is the combination of a rompler called AWM2 (Advanced Wave
Memory 2) and an effects DSP called MEG (Multiple Effects Memory 2) and an effects DSP called MEG (Multiple Effects
@ -52,7 +54,7 @@
25 bits address and 32 bits data wide. It applies four filters to 25 bits address and 32 bits data wide. It applies four filters to
the sample data, two of fixed type (low pass then highpass) and two the sample data, two of fixed type (low pass then highpass) and two
free 3-point FIR filters (used for yet another lowpass and free 3-point FIR filters (used for yet another lowpass and
highpass). Envelopes are handled automatically, and the final highpass). Envelopes are handled semi-automatically, and the final
panned result is sent to the mixer. panned result is sent to the mixer.
@ -163,9 +165,9 @@ void swp30_device::device_add_mconfig(machine_config &config)
void swp30_device::device_start() void swp30_device::device_start()
{ {
m_stream = stream_alloc(0, 2, 44100); m_stream = stream_alloc(0, 2, 44100, STREAM_SYNCHRONOUS);
// Attenuantion for panning is 4.4 floating point. That means 0 // Attenuation for panning is 4.4 floating point. That means 0
// to -96.3dB. Since it's a nice range, we assume it's the same // to -96.3dB. Since it's a nice range, we assume it's the same
// for other attenuation values. Computed value is 1.16 // for other attenuation values. Computed value is 1.16
// format, to avoid overflow // format, to avoid overflow
@ -226,6 +228,8 @@ void swp30_device::device_start()
save_item(NAME(m_eq_filter)); save_item(NAME(m_eq_filter));
save_item(NAME(m_routing)); save_item(NAME(m_routing));
save_item(NAME(m_internal_adr));
save_item(NAME(m_program_address)); save_item(NAME(m_program_address));
} }
@ -263,7 +267,7 @@ void swp30_device::device_reset()
void swp30_device::rom_bank_updated() void swp30_device::rom_bank_updated()
{ {
m_stream->update(); // Nothing to do, stream is synchronous
} }
void swp30_device::map(address_map &map) void swp30_device::map(address_map &map)
@ -306,7 +310,10 @@ void swp30_device::map(address_map &map)
// Control registers // Control registers
// These appear as channel slots 0x0e and 0x0f // These appear as channel slots 0x0e and 0x0f
// 00-0b missing // 00-01 missing
rctrl(map, 0x02).rw(FUNC(swp30_device::internal_adr_r), FUNC(swp30_device::internal_adr_w));
rctrl(map, 0x03).r (FUNC(swp30_device::internal_r));
// 04-0b missing
rctrl(map, 0x0c).rw(FUNC(swp30_device::keyon_mask_r<3>), FUNC(swp30_device::keyon_mask_w<3>)); rctrl(map, 0x0c).rw(FUNC(swp30_device::keyon_mask_r<3>), FUNC(swp30_device::keyon_mask_w<3>));
rctrl(map, 0x0d).rw(FUNC(swp30_device::keyon_mask_r<2>), FUNC(swp30_device::keyon_mask_w<2>)); rctrl(map, 0x0d).rw(FUNC(swp30_device::keyon_mask_r<2>), FUNC(swp30_device::keyon_mask_w<2>));
rctrl(map, 0x0e).rw(FUNC(swp30_device::keyon_mask_r<1>), FUNC(swp30_device::keyon_mask_w<1>)); rctrl(map, 0x0e).rw(FUNC(swp30_device::keyon_mask_r<1>), FUNC(swp30_device::keyon_mask_w<1>));
@ -428,7 +435,7 @@ void swp30_device::lpf_cutoff_w(offs_t offset, u16 data)
{ {
m_stream->update(); m_stream->update();
u8 chan = offset >> 6; u8 chan = offset >> 6;
if(0 && m_lpf_cutoff[chan] != data) if(1 || m_lpf_cutoff[chan] != data)
logerror("chan %02x lpf cutoff %04x\n", chan, data); logerror("chan %02x lpf cutoff %04x\n", chan, data);
m_lpf_cutoff[chan] = data; m_lpf_cutoff[chan] = data;
} }
@ -442,7 +449,7 @@ void swp30_device::lpf_cutoff_inc_w(offs_t offset, u16 data)
{ {
m_stream->update(); m_stream->update();
u8 chan = offset >> 6; u8 chan = offset >> 6;
if(0 && m_lpf_cutoff_inc[chan] != data) if(1 || m_lpf_cutoff_inc[chan] != data)
logerror("chan %02x lpf cutoff increment %04x\n", chan, data); logerror("chan %02x lpf cutoff increment %04x\n", chan, data);
m_lpf_cutoff_inc[chan] = data; m_lpf_cutoff_inc[chan] = data;
} }
@ -456,7 +463,7 @@ void swp30_device::hpf_cutoff_w(offs_t offset, u16 data)
{ {
m_stream->update(); m_stream->update();
u8 chan = offset >> 6; u8 chan = offset >> 6;
if(0 && m_hpf_cutoff[chan] != data) if(1 || m_hpf_cutoff[chan] != data)
logerror("chan %02x hpf cutoff %04x\n", chan, data); logerror("chan %02x hpf cutoff %04x\n", chan, data);
m_hpf_cutoff[chan] = data; m_hpf_cutoff[chan] = data;
} }
@ -470,7 +477,7 @@ void swp30_device::lpf_reso_w(offs_t offset, u16 data)
{ {
m_stream->update(); m_stream->update();
u8 chan = offset >> 6; u8 chan = offset >> 6;
if(0 && m_lpf_reso[chan] != data) if(1 || m_lpf_reso[chan] != data)
logerror("chan %02x lpf resonance %04x\n", chan, data); logerror("chan %02x lpf resonance %04x\n", chan, data);
m_lpf_reso[chan] = data; m_lpf_reso[chan] = data;
} }
@ -583,7 +590,7 @@ template<int sel> void swp30_device::envelope_w(offs_t offset, u16 data)
u8 chan = offset >> 6; u8 chan = offset >> 6;
bool ch = m_envelope[chan][sel] != data; bool ch = m_envelope[chan][sel] != data;
m_envelope[chan][sel] = data; m_envelope[chan][sel] = data;
if(0 && ch) if(1 && ch)
logerror("snd chan %02x envelopes %04x %04x %04x\n", chan, m_envelope[chan][0], m_envelope[chan][1], m_envelope[chan][2]); logerror("snd chan %02x envelopes %04x %04x %04x\n", chan, m_envelope[chan][0], m_envelope[chan][1], m_envelope[chan][2]);
} }
@ -660,6 +667,52 @@ void swp30_device::address_l_w(offs_t offset, u16 data)
logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff); logerror("snd chan %02x format %s flags %02x address %06x\n", chan, formats[m_address[chan] >> 30], (m_address[chan] >> 24) & 0x3f, m_address[chan] & 0xffffff);
} }
u16 swp30_device::internal_adr_r()
{
return m_internal_adr;
}
void swp30_device::internal_adr_w(u16 data)
{
m_internal_adr = data;
}
u16 swp30_device::internal_r()
{
u8 chan = m_internal_adr & 0x3f;
switch(m_internal_adr >> 8) {
case 0:
// used at d312
// r0 = [b15, b14, 0,0,0,0,0,0, !b13, !b12, !b11, !b10, !b9, !b8, 1, 1]
// voice[45] = (voice[4b] * r0l) >> 7 (unsigned)
// if(r0h == 0) voice[43] = 0xff
// else voice[43] = voice[51] - r0l; // 51 = inverse velocity
// if(r0l >= 0x80) ???
logerror("read %02x.0\n", chan);
// 3f -> active stays 2 until release where 1
// 7f, bf, ff -> active = 0 immediatly
return 0x0000 | 0x0000;
case 4:
// used at 44c4
// tests & 0x4000 only
logerror("read %02x.4\n", chan);
return 0x0000;
case 6:
// used at 3e7c
// tests & 0x8000 only, keyoff?
logerror("read %02x.6\n", chan);
return 0x0000;
}
logerror("%s internal_r port %x channel %02x sample %d\n", machine().time().to_string(), m_internal_adr >> 8, m_internal_adr & 0x1f, scount);
machine().debug_break();
return 0;
}
// MEG registers forwarding // MEG registers forwarding
@ -751,8 +804,8 @@ void swp30_device::snd_w(offs_t offset, u16 data)
preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan); preg = util::string_format("lf%02x", (slot-0x3e) + 2*chan);
else else
preg = util::string_format("%02x.%02x", chan, slot); preg = util::string_format("%02x.%02x", chan, slot);
if((slot >= 0xa && slot <= 0xd) || (slot >= 0x2c && slot <= 0x2f)) // if((slot >= 0xa && slot <= 0xd) || (slot >= 0x2c && slot <= 0x2f))
machine().debug_break(); // machine().debug_break();
logerror("snd_w [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, data); logerror("snd_w [%04x %04x] %-5s, %04x\n", offset, offset*2, preg, data);
} }
@ -763,117 +816,117 @@ void swp30_device::snd_w(offs_t offset, u16 data)
void swp30_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) void swp30_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{ {
// Loop first on the samples and not on the channels otherwise if(outputs[0].samples() != 1)
// effects will be annoying to implement. fatalerror("Sync stream not sync?\n");
for(int sample = 0; sample < outputs[0].samples(); sample++) { scount++;
// Accumulate on 64 bits, shift/clamp at the end
s64 acc_left = 0, acc_right = 0;
// Loop on channels // Accumulate on 64 bits, shift/clamp at the end
for(int channel = 0; channel < 64; channel++) s64 acc_left = 0, acc_right = 0;
if(m_active_mask & (u64(1) << channel)) {
// First, read the sample
// - Find the base sample index and base address // Loop on channels
s32 spos = m_sample_pos[channel] >> 8; for(int channel = 0; channel < 64; channel++)
offs_t base_address = (m_address[channel] & 0x1ffffff) << 2; if(m_active_mask & (u64(1) << channel)) {
// - Read/decompress the sample // First, read the sample
s16 samp = 0;
switch(m_address[channel] >> 30) {
case 0: { // 16-bits linear
offs_t adr = base_address + (spos << 1);
samp = read_word(adr);
break;
}
case 1: { // 12-bits linear // - Find the base sample index and base address
offs_t adr = base_address + (spos >> 2)*6; s32 spos = m_sample_pos[channel] >> 8;
switch(spos & 3) { offs_t base_address = (m_address[channel] & 0x1ffffff) << 2;
case 0: { // .abc .... .... // - Read/decompress the sample
u16 w0 = read_word(adr); s16 samp = 0;
samp = (w0 & 0x0fff) << 4; switch(m_address[channel] >> 30) {
break; case 0: { // 16-bits linear
} offs_t adr = base_address + (spos << 1);
case 1: { // C... ..AB .... samp = read_word(adr);
u16 w0 = read_word(adr); break;
u16 w1 = read_word(adr+2);
samp = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
break;
}
case 2: { // .... bc.. ...a
u16 w0 = read_word(adr+2);
u16 w1 = read_word(adr+4);
samp = ((w0 & 0xff00) >> 4) | ((w1 & 0x000f) << 12);
break;
}
case 3: { // .... .... ABC.
u16 w1 = read_word(adr+4);
samp = w1 & 0xfff0;
break;
}
}
break;
}
case 2: // 8-bits linear
samp = read_byte(base_address + spos) << 8;
break;
case 3: // 8-bits logarithmic
samp = m_sample_log8[read_byte(base_address + spos)];
break;
}
//logerror("sample %02x %06x [%d] %+5d %04x %04x %04x\n", channel, base_address >> 2, m_address[channel] >> 30, spos, samp & 0xffff, m_volume[channel], m_pan[channel]);
// Second, step the sample pos, loop/deactivate as needed
m_sample_pos[channel] += m_sample_increment[m_freq[channel] & 0x3fff];
s32 loop_size = (m_post_size[channel] << 8) | ((m_address[channel] >> 22) & 0xf8);
if(m_sample_pos[channel] >= loop_size) {
// We reached the loop point, stop if loop size is zero,
// otherwise loop
if(!loop_size)
m_active_mask &= ~((u64(1) << channel));
else
do
m_sample_pos[channel] -= loop_size;
while(m_sample_pos[channel] >= loop_size);
}
// Third, filter the sample
// - missing lpf_cutoff, lpf_reso, hpf_cutoff
// - eq lowpass
s32 samp1 = (samp * m_eq_filter[channel][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0]) >> 13;
m_sample_history[channel][0][1] = m_sample_history[channel][0][0];
m_sample_history[channel][0][0] = samp;
// - eq highpass
s32 samp2 = (samp1 * m_eq_filter[channel][5] + m_sample_history[channel][1][0] * m_eq_filter[channel][4] + m_sample_history[channel][1][1] * m_eq_filter[channel][3]) >> 13;
m_sample_history[channel][1][1] = m_sample_history[channel][1][0];
m_sample_history[channel][1][0] = samp1;
// - anything else?
// Fourth, volume (disabled) and pan, clamp the attenuation at -96dB
s32 sampl = samp2 * m_linear_attenuation[std::min(0xff, (m_volume[channel] & 0x00) + (m_pan[channel] >> 8))];
s32 sampr = samp2 * m_linear_attenuation[std::min(0xff, (m_volume[channel] & 0x00) + (m_pan[channel] & 0xff))];
// Fifth, add to the accumulators
acc_left += sampl;
acc_right += sampr;
// Missing: reverb, chorus, effects in general
} }
// Samples are 16 bits, there are up to 64 of them, and the accumulators are fixed-point signed 48.16 case 1: { // 12-bits linear
// Global EQ is missing (it's done in the MEG) offs_t adr = base_address + (spos >> 2)*6;
switch(spos & 3) {
case 0: { // .abc .... ....
u16 w0 = read_word(adr);
samp = (w0 & 0x0fff) << 4;
break;
}
case 1: { // C... ..AB ....
u16 w0 = read_word(adr);
u16 w1 = read_word(adr+2);
samp = ((w0 & 0xf000) >> 8) | ((w1 & 0x00ff) << 8);
break;
}
case 2: { // .... bc.. ...a
u16 w0 = read_word(adr+2);
u16 w1 = read_word(adr+4);
samp = ((w0 & 0xff00) >> 4) | ((w1 & 0x000f) << 12);
break;
}
case 3: { // .... .... ABC.
u16 w1 = read_word(adr+4);
samp = w1 & 0xfff0;
break;
}
}
break;
}
acc_left >>= (16+6); case 2: // 8-bits linear
outputs[0].put_int_clamp(sample, acc_left, 32768); samp = read_byte(base_address + spos) << 8;
break;
acc_right >>= (16+6); case 3: // 8-bits logarithmic
outputs[1].put_int_clamp(sample, acc_right, 32768); samp = m_sample_log8[read_byte(base_address + spos)];
} break;
}
//logerror("sample %02x %06x [%d] %+5d %04x %04x %04x\n", channel, base_address >> 2, m_address[channel] >> 30, spos, samp & 0xffff, m_volume[channel], m_pan[channel]);
// Second, step the sample pos, loop/deactivate as needed
m_sample_pos[channel] += m_sample_increment[m_freq[channel] & 0x3fff];
s32 loop_size = (m_post_size[channel] << 8) | ((m_address[channel] >> 22) & 0xf8);
if(m_sample_pos[channel] >= loop_size) {
// We reached the loop point, stop if loop size is zero,
// otherwise loop
if(!loop_size)
m_active_mask &= ~((u64(1) << channel));
else
do
m_sample_pos[channel] -= loop_size;
while(m_sample_pos[channel] >= loop_size);
}
// Third, filter the sample
// - missing lpf_cutoff, lpf_reso, hpf_cutoff
// - eq lowpass
s32 samp1 = (samp * m_eq_filter[channel][2] + m_sample_history[channel][0][0] * m_eq_filter[channel][1] + m_sample_history[channel][0][1] * m_eq_filter[channel][0]) >> 13;
m_sample_history[channel][0][1] = m_sample_history[channel][0][0];
m_sample_history[channel][0][0] = samp;
// - eq highpass
s32 samp2 = (samp1 * m_eq_filter[channel][5] + m_sample_history[channel][1][0] * m_eq_filter[channel][4] + m_sample_history[channel][1][1] * m_eq_filter[channel][3]) >> 13;
m_sample_history[channel][1][1] = m_sample_history[channel][1][0];
m_sample_history[channel][1][0] = samp1;
// - anything else?
// Fourth, volume (disabled) and pan, clamp the attenuation at -96dB
s32 sampl = samp2 * m_linear_attenuation[std::min(0xff, (m_volume[channel] & 0x00) + (m_pan[channel] >> 8))];
s32 sampr = samp2 * m_linear_attenuation[std::min(0xff, (m_volume[channel] & 0x00) + (m_pan[channel] & 0xff))];
// Fifth, add to the accumulators
acc_left += sampl;
acc_right += sampr;
// Missing: reverb, chorus, effects in general
}
// Samples are 16 bits, there are up to 64 of them, and the accumulators are fixed-point signed 48.16
// Global EQ is missing (it's done in the MEG)
acc_left >>= (16+6);
outputs[0].put_int_clamp(0, acc_left, 32768);
acc_right >>= (16+6);
outputs[1].put_int_clamp(0, acc_right, 32768);
} }

View File

@ -50,6 +50,8 @@ private:
u16 m_routing[0x40][3]; u16 m_routing[0x40][3];
u16 m_map[8]; u16 m_map[8];
u16 m_internal_adr;
u16 m_program_address; u16 m_program_address;
// AWM2 per-channel registers // AWM2 per-channel registers
@ -87,6 +89,9 @@ private:
void dry_rev_w(offs_t offset, u16 data); void dry_rev_w(offs_t offset, u16 data);
u16 cho_var_r(offs_t offset); u16 cho_var_r(offs_t offset);
void cho_var_w(offs_t offset, u16 data); void cho_var_w(offs_t offset, u16 data);
u16 internal_adr_r();
void internal_adr_w(u16 data);
u16 internal_r();
template<int sel> u16 routing_r(offs_t offset); template<int sel> u16 routing_r(offs_t offset);
template<int sel> void routing_w(offs_t offset, u16 data); template<int sel> void routing_w(offs_t offset, u16 data);

View File

@ -176,6 +176,7 @@ public:
void regs_int_read_tap(offs_t address, u16 data, u16 mem_mask); void regs_int_read_tap(offs_t address, u16 data, u16 mem_mask);
void regs_int_write_tap(offs_t address, u16 data, u16 mem_mask); void regs_int_write_tap(offs_t address, u16 data, u16 mem_mask);
void voice_write_tap(offs_t address, u16 data, u16 mem_mask); void voice_write_tap(offs_t address, u16 data, u16 mem_mask);
void voice_read_tap(offs_t address, u16 data, u16 mem_mask);
void chan_write_tap(offs_t address, u16 data, u16 mem_mask); void chan_write_tap(offs_t address, u16 data, u16 mem_mask);
void prg_write_tap(offs_t address, u16 data, u16 mem_mask); void prg_write_tap(offs_t address, u16 data, u16 mem_mask);
@ -188,10 +189,14 @@ public:
m_maincpu->space(0).install_write_tap(0x20cb10, 0x20cb10 + 0x122*0x22 - 1, "chan debug", [this](offs_t offset, u16 &data, u16 mem_mask) { m_maincpu->space(0).install_write_tap(0x20cb10, 0x20cb10 + 0x122*0x22 - 1, "chan debug", [this](offs_t offset, u16 &data, u16 mem_mask) {
chan_write_tap(offset, data, mem_mask); chan_write_tap(offset, data, mem_mask);
}); });
if(0) if(1)
m_maincpu->space(0).install_write_tap(0x20f03e, 0x20f03e + 0x92*0x40 - 1, "voice debug", [this](offs_t offset, u16 &data, u16 mem_mask) { m_maincpu->space(0).install_write_tap(0x20f03e, 0x20f03e + 0x92*0x40 - 1, "voice debug", [this](offs_t offset, u16 &data, u16 mem_mask) {
voice_write_tap(offset, data, mem_mask); voice_write_tap(offset, data, mem_mask);
}); });
if(1)
m_maincpu->space(0).install_read_tap(0x20f03e, 0x20f03e + 0x92*0x40 - 1, "voice debug", [this](offs_t offset, u16 &data, u16 mem_mask) {
voice_read_tap(offset, data, mem_mask);
});
if(0) if(0)
m_maincpu->space(0).install_readwrite_tap(0x214ca2+0x20, 0x214ca2+0x320-1, "regs fp", m_maincpu->space(0).install_readwrite_tap(0x214ca2+0x20, 0x214ca2+0x320-1, "regs fp",
[this](offs_t offset, u16 &data, u16 mem_mask) { [this](offs_t offset, u16 &data, u16 mem_mask) {
@ -411,20 +416,71 @@ void mu100_state::regs_int_write_tap(offs_t address, u16 data, u16 mem_mask)
logerror("regs_int_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc); logerror("regs_int_w %03x, %04x @ %04x (%06x)\n", reg, data, mem_mask, pc);
} }
struct xmap {
int slot;
const char *name;
};
static xmap vmap[] = {
{ 0x00, "instrumenthi" },
{ 0x02, "instrumentlo" },
{ 0x04, "midi_channelhi" },
{ 0x06, "midi_channello" },
{ 0x0c, "lpf_cutoff" },
{ 0x42, "delay_time" },
{ 0x48, "active" },
{ 0x4a, "velocity" },
{ 0x51, "inverse_velocity" },
{ -1, "" },
};
void mu100_state::voice_write_tap(offs_t address, u16 data, u16 mem_mask) void mu100_state::voice_write_tap(offs_t address, u16 data, u16 mem_mask)
{ {
offs_t pc = m_maincpu->pc(); offs_t pc = m_maincpu->pc();
offs_t off = address - 0x20f03e; offs_t off = address - 0x20f03e;
int voice = off / 0x92; int voice = off / 0x92;
int slot = off % 0x92; int slot = off % 0x92;
if(mem_mask == 0xff00)
data >>= 8;
else if(mem_mask == 0x00ff)
slot++;
std::string slotname = util::string_format("%02x", slot);
for(int i=0; vmap[i].slot != -1; i++)
if(vmap[i].slot == slot)
slotname = vmap[i].name;
if(mem_mask == 0xffff) { if(mem_mask == 0xffff) {
logerror("voice_w %02x:%02x, %04x (%06x)\n", voice, slot, data, pc); logerror("voice_w %02x:%s, %04x (%06x)\n", voice, slotname, data, pc);
} else { } else {
if(mem_mask == 0xff00) logerror("voice_w %02x:%s, %02x (%06x)\n", voice, slotname, data, pc);
data >>= 8; }
else }
slot++;
logerror("voice_w %02x:%02x, %02x (%06x)\n", voice, slot, data, pc); void mu100_state::voice_read_tap(offs_t address, u16 data, u16 mem_mask)
{
offs_t pc = m_maincpu->pc();
offs_t off = address - 0x20f03e;
int voice = off / 0x92;
int slot = off % 0x92;
logerror("off %x voice %x slot %x mask %04x\n", off, voice, slot, mem_mask);
data &= mem_mask;
if(mem_mask == 0xff00)
data >>= 8;
else if(mem_mask == 0x00ff)
slot++;
std::string slotname = util::string_format("%02x", slot);
for(int i=0; vmap[i].slot != -1; i++)
if(vmap[i].slot == slot)
slotname = vmap[i].name;
if(mem_mask == 0xffff) {
logerror("voice_r %02x:%s, %04x (%06x)\n", voice, slotname, data, pc);
} else {
logerror("voice_r %02x:%s, %02x (%06x)\n", voice, slotname, data, pc);
} }
} }