Revert "ctk551: implement sound, promote to working (#8960)" (#8980)

This reverts commit 7ce27dadde.
This commit is contained in:
R. Belmont 2021-12-14 10:59:52 -05:00 committed by GitHub
parent ef2c11c06f
commit 04c0b4fbb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 338 deletions

View File

@ -11,7 +11,7 @@
- Two timers, three 8-bit ports, two 8-bit ADCs
- Keyboard controller w/ key velocity detection
- MIDI UART
- 24-voice DPCM sound
- 24-voice PCM sound (currently not emulated / fully understood)
Earlier and later Casio keyboard models contain "uPD912" and "uPD914" chips,
which are presumably similar.
@ -52,9 +52,9 @@ void gt913_device::map(address_map &map)
map(0xfac0, 0xffbf).ram(); // CTK-551 zeroes out this range at $0418
/* ffc0-ffcb: sound */
map(0xffc0, 0xffc5).rw(m_sound, FUNC(gt913_sound_device::data_r), FUNC(gt913_sound_device::data_w));
map(0xffc6, 0xffc7).w(m_sound, FUNC(gt913_sound_device::command_w));
map(0xffca, 0xffcb).r(m_sound, FUNC(gt913_sound_device::status_r));
map(0xffc0, 0xffc5).rw(m_sound, FUNC(gt913_sound_hle_device::data_r), FUNC(gt913_sound_hle_device::data_w));
map(0xffc6, 0xffc7).w(m_sound, FUNC(gt913_sound_hle_device::command_w));
map(0xffca, 0xffcb).r(m_sound, FUNC(gt913_sound_hle_device::status_r));
/* ffd0-ffd5: key controller */
map(0xffd0, 0xffd1).r(m_kbd, FUNC(gt913_kbd_hle_device::read));
@ -88,14 +88,7 @@ void gt913_device::device_add_mconfig(machine_config &config)
{
GT913_INTC(config, "intc");
/*
generate sound at 104 cycles per sample (= 144.231 kHz sample clock to the DAC)
on keyboard models that include a DSP, this also results in a multiple
of the 36.058 kHz CPU->DSP sync signal shown in some schematics (WK-1200 and others)
*/
GT913_SOUND(config, m_sound, std::round(clock() / 104.0f));
m_sound->set_device_rom_tag(tag());
GT913_SOUND_HLE(config, m_sound, 0);
GT913_KBD_HLE(config, m_kbd, 0);
m_kbd->irq_cb().set([this](int val) { if (val) m_intc->internal_interrupt(5); });
GT913_IO_HLE(config, m_io_hle, "intc", 6, 7);
@ -109,7 +102,7 @@ void gt913_device::device_add_mconfig(machine_config &config)
void gt913_device::uart_rate_w(uint8_t data)
{
m_sci->brr_w(data >> 2);
m_sci->brr_w(data >> 1);
}
void gt913_device::uart_control_w(uint8_t data)

View File

@ -62,7 +62,7 @@ protected:
required_device<gt913_intc_device> m_intc;
/* sound */
required_device<gt913_sound_device> m_sound;
required_device<gt913_sound_hle_device> m_sound;
/* key controller */
required_device<gt913_kbd_hle_device> m_kbd;

View File

@ -107,7 +107,7 @@
5e00 ff00 0 bsr rel8 -
5f00 fff0 0 mov.w imm16 r16l
66000000 ff00ff8f 0 btst imm3 r16ihh
66000000 ff00ff8f 0 btst imm3 abs8
6800 ff80 0 mov.b r16ih r8l
6900 ff88 0 mov.w r16ih r16l

View File

@ -108,9 +108,9 @@ void gt913_io_hle_device::timer_adjust(offs_t num)
logerror("unknown timer %u prescaler %u (pc = %04x)\n", num, m_timer_control[num] & 0x7, m_cpu->pc());
[[fallthrough]];
case 0:
break;
clocks <<= 1; break;
case 2:
clocks <<= 9; break;
clocks <<= 10; break;
}
attotime period = m_cpu->clocks_to_attotime(clocks);

View File

@ -8,13 +8,13 @@
which is then input to either a serial DAC or a HG51B-based DSP,
depending on the model of keyboard.
The sample format, as well as other details such as the linear interpolation,
are covered in these two Japanese patents:
https://patents.google.com/patent/JP3603343B2/en
https://patents.google.com/patent/JPH07199996A/en
Currently, the actual sample format in ROM is unknown.
The serial output is twos-complement 16-bit PCM, but the data in ROM
doesn't seem to be - reading it as such produces sounds that are
somewhat recognizable, but highly distorted.
TODO: Volume envelope rates still need adjusting.
(See comment in gt913_sound_device::command_w regarding command 6007)
For now, all known (and unknown) register writes are just logged
without generating any sound.
***************************************************************************/
@ -26,319 +26,140 @@
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(GT913_SOUND, gt913_sound_device, "gt913_sound_hle", "Casio GT913F sound")
DEFINE_DEVICE_TYPE(GT913_SOUND_HLE, gt913_sound_hle_device, "gt913_sound_hle", "Casio GT913F sound (HLE)")
// expand 2-bit exponent deltas
const u8 gt913_sound_device::exp_2_to_3[4] = { 0, 1, 2, 7 };
// sign-extend 7-bit sample deltas
const s8 gt913_sound_device::sample_7_to_8[128] =
{
0, 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, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49,
-48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33,
-32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17,
-16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
};
gt913_sound_device::gt913_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, GT913_SOUND, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, device_rom_interface(mconfig, *this)
gt913_sound_hle_device::gt913_sound_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, GT913_SOUND_HLE, tag, owner, clock)
{
}
void gt913_sound_device::device_start()
void gt913_sound_hle_device::device_start()
{
m_stream = stream_alloc(0, 2, clock());
save_item(NAME(m_gain));
save_item(NAME(m_data));
save_item(STRUCT_MEMBER(m_voices, m_enable));
save_item(STRUCT_MEMBER(m_voices, m_addr_start));
save_item(STRUCT_MEMBER(m_voices, m_addr_end));
save_item(STRUCT_MEMBER(m_voices, m_addr_loop));
save_item(STRUCT_MEMBER(m_voices, m_addr_current));
save_item(STRUCT_MEMBER(m_voices, m_addr_frac));
save_item(STRUCT_MEMBER(m_voices, m_pitch));
save_item(STRUCT_MEMBER(m_voices, m_sample));
save_item(STRUCT_MEMBER(m_voices, m_sample_next));
save_item(STRUCT_MEMBER(m_voices, m_exp));
save_item(STRUCT_MEMBER(m_voices, m_volume_current));
save_item(STRUCT_MEMBER(m_voices, m_volume_target));
save_item(STRUCT_MEMBER(m_voices, m_volume_rate));
save_item(STRUCT_MEMBER(m_voices, m_volume_end));
save_item(STRUCT_MEMBER(m_voices, m_balance));
save_item(STRUCT_MEMBER(m_voices, m_gain));
save_item(NAME(m_volume_target));
save_item(NAME(m_volume_rate));
}
void gt913_sound_device::device_reset()
void gt913_sound_hle_device::device_reset()
{
m_gain = 0;
std::memset(m_data, 0, sizeof(m_data));
std::memset(m_voices, 0, sizeof(m_voices));
std::memset(m_volume_target, 0, sizeof(m_volume_target));
std::memset(m_volume_rate, 0, sizeof(m_volume_rate));
}
void gt913_sound_device::sound_stream_update(sound_stream& stream, std::vector<read_stream_view> const& inputs, std::vector<write_stream_view>& outputs)
{
for (int i = 0; i < outputs[0].samples(); i++)
{
s64 left = 0, right = 0;
for (auto& voice : m_voices)
{
if (voice.m_enable)
mix_sample(voice, left, right);
}
outputs[0].put_int_clamp(i, (left * m_gain) >> 26, 32678);
outputs[1].put_int_clamp(i, (right * m_gain) >> 26, 32768);
}
}
void gt913_sound_device::rom_bank_updated()
{
m_stream->update();
}
void gt913_sound_device::mix_sample(voice_t& voice, s64& left, s64& right)
{
// update sample position
voice.m_addr_frac += voice.m_pitch;
while (voice.m_addr_frac >= (1 << 25))
{
voice.m_addr_frac -= (1 << 25);
update_sample(voice);
}
// update volume envelope
if (voice.m_volume_target > voice.m_volume_current
&& (voice.m_volume_target - voice.m_volume_current) > voice.m_volume_rate)
{
voice.m_volume_current += voice.m_volume_rate;
}
else if (voice.m_volume_target < voice.m_volume_current
&& (voice.m_volume_current - voice.m_volume_target) > voice.m_volume_rate)
{
voice.m_volume_current -= voice.m_volume_rate;
}
else
{
voice.m_volume_current = voice.m_volume_target;
}
// interpolate, apply envelope + channel gain, and mix into output
const u8 step = (voice.m_addr_frac >> 22) & 7;
const u8 env = (voice.m_volume_current >> 24);
/*
the current envelope level effects amplitude non-linearly, just apply the value twice
(this hardware family is branded as "A² (A-Square) Sound Source" in some of Casio's
promotional materials, possibly for this reason?)
*/
const s64 sample = ((s64)voice.m_sample + (voice.m_sample_next * step / 8)) * voice.m_gain * env * env;
left += sample * voice.m_balance[0];
right += sample * voice.m_balance[1];
}
void gt913_sound_device::update_sample(voice_t& voice)
{
voice.m_sample += voice.m_sample_next;
if (voice.m_addr_current == (voice.m_addr_loop | 1))
{
/*
The last 12 bytes of each sample are a table containing five sample and exponent value pairs
for the data words immediately after the loop point. The first pair corresponds to what the
sample and exponent value will be _after_ processing the first word after the loop,
so once we've reached that point, use those values to reload the current sample and exponent
*/
const u32 addr_loop_data = (voice.m_addr_end + 1) & ~1;
voice.m_sample_next = read_word(addr_loop_data) - voice.m_sample;
voice.m_exp = read_word(addr_loop_data + 10) & 7;
}
else
{
/*
For all other samples, just get the next sample delta value.
For even-numbered samples, also update the exponent/shift value.
*/
const u16 word = read_word(voice.m_addr_current & ~1);
s16 delta = 0;
if (!BIT(voice.m_addr_current, 0))
{
voice.m_exp += exp_2_to_3[word & 3];
voice.m_exp &= 7;
delta = sample_7_to_8[(word >> 2) & 0x7f];
}
else
{
delta = sample_7_to_8[word >> 9];
}
voice.m_sample_next = delta * (1 << voice.m_exp);
}
voice.m_addr_current++;
if (voice.m_addr_current == voice.m_addr_end)
{
voice.m_addr_current = voice.m_addr_loop;
if (voice.m_addr_loop == voice.m_addr_end)
voice.m_enable = false;
}
}
void gt913_sound_device::data_w(offs_t offset, u16 data)
void gt913_sound_hle_device::data_w(offs_t offset, uint16_t data)
{
assert(offset < 3);
m_data[offset] = data;
}
u16 gt913_sound_device::data_r(offs_t offset)
uint16_t gt913_sound_hle_device::data_r(offs_t offset)
{
assert(offset < 3);
return m_data[offset];
}
void gt913_sound_device::command_w(u16 data)
void gt913_sound_hle_device::command_w(uint16_t data)
{
m_stream->update();
const uint8_t voicenum = (data & 0x1f00) >> 8;
const uint16_t voicecmd = data & 0x60ff;
uint8_t voicenum = (data & 0x1f00) >> 8;
uint16_t voicecmd = data & 0x60ff;
if (data == 0x0012)
{
m_gain = m_data[0] & 0x3f;
return;
uint8_t gain = m_data[0] & 0x3f;
if (gain != m_gain)
logerror("gain %u\n", gain);
m_gain = gain;
}
else if (voicenum >= 24)
{
return;
}
auto& voice = m_voices[voicenum];
if (voicecmd == 0x0008)
else if (voicecmd == 0x0008)
{
/*
sample start addresses seem to need to be word-aligned to decode properly
(see: ctk551 "Trumpet" patch, which will have a bad exponent value otherwise)
this apparently doesn't apply to end/loop addresses, though, or else samples
may loop badly or even become noticeably detuned
TODO: is the LSB of start addresses supposed to indicate something else, then?
Set the voice's sample start point as a ROM address.
This is usually word-aligned, but not always
(e.g. ctk551's lowest piano sample is at address 0x5a801)
*/
voice.m_addr_start = (m_data[1] | (m_data[2] << 16)) & 0x1ffffe;
uint32_t samplestart = (m_data[1] | (m_data[2] << 16)) & 0x1fffff;
logerror("voice %u sample start 0x%06x\n", voicenum, samplestart);
}
else if (voicecmd == 0x0000)
{
voice.m_addr_end = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
/*
Set the voice's sample end point as a ROM address.
*/
uint32_t sampleend = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
logerror("voice %u sample end 0x%06x\n", voicenum, sampleend);
}
else if (voicecmd == 0x2000)
{
voice.m_addr_loop = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
/*
Set the voice's sample loop point as a ROM address.
*/
uint32_t sampleloop = (m_data[0] | (m_data[1] << 16)) & 0x1fffff;
logerror("voice %u sample loop 0x%06x\n", voicenum, sampleloop);
}
else if (voicecmd == 0x200a)
{
/* TODO: what does bit 4 of data[2] do? ctk551 sets it unconditionally */
voice.m_exp = m_data[2] & 7;
logerror("voice %u cmd 0x200a (data = %02x)\n", voicenum, m_data[2] & 0xff);
}
else if (voicecmd == 0x200b)
{
bool enable = BIT(m_data[2], 7);
if (enable && !m_voices[voicenum].m_enable)
{
voice.m_addr_current = voice.m_addr_start;
voice.m_addr_frac = 0;
voice.m_sample = 0;
}
voice.m_enable = enable;
voice.m_volume_end &= enable;
/*
Turn this voice on/off.
ctk551 turns output off before assigning a new note or instrument to this voice,
then turns output back on afterward
*/
logerror("voice %u output %s\n", voicenum, BIT(m_data[2], 7) ? "on" : "off");
}
else if (voicecmd == 0x4004)
{
voice.m_balance[0] = (m_data[1] & 0xe0) >> 5;
voice.m_balance[1] = (m_data[1] & 0x1c) >> 2;
/*
Set this voice's panning, in the form of left and right volume levels (3 bits each)
*/
uint8_t left = (m_data[1] & 0xe0) >> 5;
uint8_t right = (m_data[1] & 0x1c) >> 2;
logerror("voice %u left %u right %u\n", voicenum, left, right);
}
else if (voicecmd == 0x4005)
{
/*
for pitch, data[1] apparently contains both the most and least significant of 4 bytes,
with data0 in the middle. strange, but apparently correct (see higher octaves of ctk551 E.Piano2)
Set the current pitch of this voice.
The actual format of the value is unknown, but presumably some kind of fixed point
*/
voice.m_pitch = (m_data[1] << 24) | (m_data[0] << 8) | (m_data[1] >> 8);
uint32_t pitch = (m_data[0] << 8) | (m_data[1] >> 8);
logerror("voice %u pitch 0x%06x\n", voicenum, pitch);
}
else if (voicecmd == 0x6006)
{
/*
per-voice gain used for normalizing samples
currently treated such that the lower 3 bits are fractional
*/
voice.m_gain = m_data[1] & 0xff;
logerror("voice %u cmd 0x6006 (data = %02x)\n", voicenum, m_data[1] & 0xff);
}
else if (voicecmd == 0x6007)
{
logerror("voice %u volume %u rate %u\n", voicenum, (m_data[0] >> 8), m_data[0] & 0xff);
/*
only set a new volume level/rate if we haven't previously indicated the end of an envelope,
unless the new level also has the high bit set. otherwise, a timer irq may try to update the
normal envelope while other code is trying to force a note off
Raise or lower the volume to a specified level at a specified rate.
The actual volume level is probably 7.8 fixed point or something like that, but this command
only sets the most significant bits.
*/
const bool end = BIT(m_data[0], 15);
if (!voice.m_volume_end || end)
{
voice.m_volume_end = end;
voice.m_volume_target = (m_data[0] & 0x7f00) << 16;
/*
In addition to volume levels applying non-linearly, envelope rates
are also non-linear. Unfortunately, with the ctk-551's limited patch set and
lack of editing features, figuring out the correct behavior isn't easy.
This is essentially a rough estimate until a higher-end model (ctk-601 series, etc)
can be dumped and used for more detailed testing.
*/
const u8 x = m_data[0] & 0xff;
if (x >= 127)
voice.m_volume_rate = x << 21;
else if (x >= 63)
voice.m_volume_rate = x << 16;
else if (x >= 47)
voice.m_volume_rate = x << 14;
else if (x >= 31)
voice.m_volume_rate = x << 11;
else if (x >= 23)
voice.m_volume_rate = x << 9;
else if (x >= 15)
voice.m_volume_rate = x << 7;
else
voice.m_volume_rate = x << 5;
}
logerror("voice %u volume %u rate %u\n", voicenum, (m_data[0] >> 8) & 0x7f, m_data[0] & 0xff);
m_volume_target[voicenum] = m_data[0] & 0x7f00;
m_volume_rate[voicenum] = m_data[0] & 0xff;
}
else if (voicecmd == 0x2028)
{
/*
ctk551 issues this command and then reads the voice's current volume from data0
to determine if it's time to start the next part of the volume envelope or not.
For now, just return the "target" volume immediately
(TODO: also figure out what it expects to be returned in data1)
*/
m_data[0] = voice.m_enable ? (voice.m_volume_current >> 16) : 0;
/*
data1 is used to read consecutive output sample and detect zero crossings when
applying volume or expression changes to a MIDI channel
*/
m_data[1] = voice.m_sample;
m_data[0] = m_volume_target[voicenum];
m_data[1] = 0;
}
else
{
@ -346,11 +167,9 @@ void gt913_sound_device::command_w(u16 data)
}
}
u16 gt913_sound_device::status_r()
uint16_t gt913_sound_hle_device::status_r()
{
/*
ctk551 reads the current gain level out of the lower 6 bits and ignores the rest
it's unknown what, if anything, the other bits are supposed to contain
*/
/* ctk551 reads the current gain level out of the lower 6 bits and ignores the rest
it's unknown what, if anything, the other bits are supposed to contain */
return m_gain & 0x3f;
}

View File

@ -9,78 +9,37 @@
#pragma once
#include "dirom.h"
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> gt913_sound_device
// ======================> gt913_sound_hle_device
class gt913_sound_device : public device_t,
public device_sound_interface,
public device_rom_interface<21, 1, 0, ENDIANNESS_BIG>
class gt913_sound_hle_device : public device_t
{
public:
static constexpr feature_type imperfect_features() { return feature::SOUND; }
// construction/destruction
gt913_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
gt913_sound_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
void data_w(offs_t offset, u16 data);
u16 data_r(offs_t offset);
void command_w(u16 data);
u16 status_r();
void data_w(offs_t offset, uint16_t data);
uint16_t data_r(offs_t offset);
void command_w(uint16_t data);
uint16_t status_r();
protected:
// device_t overrides
virtual void device_start() override;
virtual void device_reset() override;
// device_sound_interface overrides
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
// device_rom_interface overrides
virtual void rom_bank_updated() override;
private:
sound_stream *m_stream;
uint8_t m_gain;
uint16_t m_data[3];
u8 m_gain;
u16 m_data[3];
static const u8 exp_2_to_3[4];
static const s8 sample_7_to_8[128];
struct voice_t
{
bool m_enable;
u32 m_addr_start;
u32 m_addr_end;
u32 m_addr_loop;
u32 m_addr_current;
u32 m_addr_frac, m_pitch;
s16 m_sample, m_sample_next;
u8 m_exp;
u32 m_volume_current, m_volume_target;
u32 m_volume_rate;
bool m_volume_end;
u8 m_balance[2];
u8 m_gain;
};
void mix_sample(voice_t& voice, s64& left, s64& right);
void update_sample(voice_t& voice);
voice_t m_voices[24];
uint16_t m_volume_target[24];
uint8_t m_volume_rate[24];
};
// device type definition
DECLARE_DEVICE_TYPE(GT913_SOUND, gt913_sound_device)
DECLARE_DEVICE_TYPE(GT913_SOUND_HLE, gt913_sound_hle_device)
#endif // MAME_AUDIO_GT913_H

View File

@ -53,7 +53,6 @@
#include "video/hd44780.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
namespace {
@ -65,7 +64,6 @@ public:
, m_maincpu(*this, "maincpu")
, m_lcdc(*this, "lcdc")
, m_led_touch(*this, "led_touch")
, m_led_power(*this, "led_power")
{
}
@ -92,7 +90,6 @@ private:
required_device<hd44780_device> m_lcdc;
output_finder<> m_led_touch;
output_finder<> m_led_power;
ioport_value m_switch;
};
@ -100,6 +97,7 @@ private:
INPUT_CHANGED_MEMBER(ctk551_state::switch_w)
{
logerror("switch_w: %x\n", param);
if (!oldval && newval)
{
if (m_switch == 0x1 && param != m_switch)
@ -114,11 +112,8 @@ INPUT_CHANGED_MEMBER(ctk551_state::switch_w)
WRITE_LINE_MEMBER(ctk551_state::apo_w)
{
logerror("apo_w: %x\n", state);
/* auto power off - disable the LCD
/* TODO: when 1, this should turn off the LCD, speakers, etc.
the CPU will go to sleep until the power switch triggers a NMI */
if (state)
m_lcdc->reset();
m_led_power = !state;
}
HD44780_PIXEL_UPDATE(ctk551_state::lcd_update)
@ -145,7 +140,6 @@ void ctk551_state::ctk551_io_map(address_map &map)
void ctk551_state::driver_start()
{
m_led_touch.resolve();
m_led_power.resolve();
m_switch = 0x2;
@ -155,11 +149,8 @@ void ctk551_state::driver_start()
void ctk551_state::ctk551(machine_config &config)
{
// CPU
// 30MHz oscillator, divided down internally (otherwise the test mode's OK/NG sounds play at double speed)
GT913(config, m_maincpu, 30'000'000 / 2);
GT913(config, m_maincpu, 30'000'000);
m_maincpu->set_addrmap(AS_IO, &ctk551_state::ctk551_io_map);
m_maincpu->subdevice<gt913_sound_device>("gt_sound")->add_route(0, "lspeaker", 1.0);
m_maincpu->subdevice<gt913_sound_device>("gt_sound")->add_route(1, "rspeaker", 1.0);
// MIDI
auto &mdin(MIDI_PORT(config, "mdin"));
@ -186,9 +177,6 @@ void ctk551_state::ctk551(machine_config &config)
screen.set_palette("palette");
PALETTE(config, "palette", FUNC(ctk551_state::palette_init), 2);
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();
}
INPUT_PORTS_START(ctk551)
@ -368,4 +356,4 @@ ROM_END
} // anonymous namespace
// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
SYST( 1999, ctk551, 0, 0, ctk551, ctk551, ctk551_state, empty_init, "Casio", "CTK-551", MACHINE_IMPERFECT_GRAPHICS | MACHINE_SUPPORTS_SAVE )
SYST( 1999, ctk551, 0, 0, ctk551, ctk551, ctk551_state, empty_init, "Casio", "CTK-551", MACHINE_NO_SOUND | MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE )