summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Devin Acker <d@revenant1.net>2025-02-21 16:34:36 -0500
committer GitHub <noreply@github.com>2025-02-22 08:34:36 +1100
commit752c45b42bcb8f13cbfdd76ac2630bd72a779309 (patch)
tree33f01e48b49aad29316b517e979908b87f40299c
parent6d32d33567e21663ea8b9ceaf76ac7024aa9ba8e (diff)
casio/wk1800.cpp, sound/gt155.cpp: Added Casio WK-1800/WK-1600 music keyboards. (#12957)
WK-1800 floppy support is not implemented. New working systems ---------- Casio WK-1800 [Edward d-tech, Devin Acker] New working clones ---------- Casio WK-1600 [Edward d-tech, Devin Acker]
-rw-r--r--scripts/src/sound.lua12
-rw-r--r--src/devices/cpu/h8/h83048.cpp1
-rw-r--r--src/devices/cpu/h8/h8_adc.cpp2
-rw-r--r--src/devices/cpu/h8/h8_dtc.cpp1
-rw-r--r--src/devices/sound/gt155.cpp408
-rw-r--r--src/devices/sound/gt155.h103
-rw-r--r--src/mame/casio/wk1800.cpp637
-rw-r--r--src/mame/mame.lst4
8 files changed, 1166 insertions, 2 deletions
diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua
index 390b9e7a750..3746cc64bd6 100644
--- a/scripts/src/sound.lua
+++ b/scripts/src/sound.lua
@@ -1766,3 +1766,15 @@ if (SOUNDS["UPD65043GFU01"]~=null) then
MAME_DIR .. "src/devices/sound/upd65043gfu01.h",
}
end
+
+---------------------------------------------------
+-- Casio GT155
+--@src/devices/sound/gt155.h,SOUNDS["GT155"] = true
+---------------------------------------------------
+
+if (SOUNDS["GT155"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/sound/gt155.cpp",
+ MAME_DIR .. "src/devices/sound/gt155.h",
+ }
+end
diff --git a/src/devices/cpu/h8/h83048.cpp b/src/devices/cpu/h8/h83048.cpp
index f04b8f6cea7..545acb7f425 100644
--- a/src/devices/cpu/h8/h83048.cpp
+++ b/src/devices/cpu/h8/h83048.cpp
@@ -301,6 +301,7 @@ u8 h83048_device::syscr_r()
void h83048_device::syscr_w(u8 data)
{
m_syscr = data;
+ m_intc->set_nmi_edge(BIT(data, 2));
update_irq_filter();
logerror("syscr = %02x\n", data);
}
diff --git a/src/devices/cpu/h8/h8_adc.cpp b/src/devices/cpu/h8/h8_adc.cpp
index c4d80ba0b4a..05c012017f3 100644
--- a/src/devices/cpu/h8/h8_adc.cpp
+++ b/src/devices/cpu/h8/h8_adc.cpp
@@ -292,7 +292,7 @@ void h8_adc_3337_device::mode_update()
m_trigger = m_adcr & 0x80 ? T_EXT : T_SOFT;
if(m_adcsr & 0x10) {
- m_start_mode = ACTIVE | ROTATE;
+ m_start_mode = ACTIVE | REPEAT | ROTATE;
m_start_channel = m_adcsr & 4;
m_end_channel = m_adcsr & 7;
} else {
diff --git a/src/devices/cpu/h8/h8_dtc.cpp b/src/devices/cpu/h8/h8_dtc.cpp
index 7844c9cec92..3dd276fca11 100644
--- a/src/devices/cpu/h8/h8_dtc.cpp
+++ b/src/devices/cpu/h8/h8_dtc.cpp
@@ -214,7 +214,6 @@ void h8_dtc_device::writeback_done(int vector)
m_intc->internal_interrupt(vector);
} else {
logerror("Software dtc done\n");
- exit(0);
}
}
diff --git a/src/devices/sound/gt155.cpp b/src/devices/sound/gt155.cpp
new file mode 100644
index 00000000000..940bf051d78
--- /dev/null
+++ b/src/devices/sound/gt155.cpp
@@ -0,0 +1,408 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+
+/***************************************************************************
+
+ Casio GT155 (HG51B155FD)
+
+ This is the sound generator and DSP used in various higher-end
+ "A-Squared Sound Source" keyboards and pianos between roughly 1994-2001.
+
+ TODO:
+ - verify per-voice lowpass filter behavior
+ - DSP (architecture/instruction set seems to be the same as the standalone
+ "GD277" DSP used in other contemporary keyboards)
+
+***************************************************************************/
+
+#include "emu.h"
+#include "gt155.h"
+
+#include <algorithm>
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(GT155, gt155_device, "gt155", "Casio GT155")
+
+gt155_device::gt155_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, GT155, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , device_rom_interface(mconfig, *this)
+{
+}
+
+/**************************************************************************/
+void gt155_device::device_start()
+{
+ m_stream = stream_alloc(0, 2, clock() / CLOCKS_PER_SAMPLE); // 16.384 MHz -> 32 kHz, or 24.576 MHz -> 48 kHz
+
+ for (int i = 0; i < 0x800; i++)
+ {
+ const double frac = 1.0 - ((double)i / 0x7ff);
+ m_volume[i] = 0x800 * pow(10, -2.15 * frac) * cos(0.5 * M_PI * frac * frac * frac);
+ }
+
+ save_item(NAME(m_data));
+ save_item(NAME(m_dsp_data));
+ save_item(NAME(m_rom_addr));
+
+ save_item(STRUCT_MEMBER(m_voices, m_enable));
+ save_item(STRUCT_MEMBER(m_voices, m_format));
+
+ save_item(STRUCT_MEMBER(m_voices, m_addr));
+ save_item(STRUCT_MEMBER(m_voices, m_addr_frac));
+ 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_loop_frac));
+
+ save_item(STRUCT_MEMBER(m_voices, m_pitch));
+
+ save_item(STRUCT_MEMBER(m_voices, m_filter_gain));
+ save_item(STRUCT_MEMBER(m_voices, m_filter));
+ save_item(STRUCT_MEMBER(m_voices, m_filter_out));
+ save_item(STRUCT_MEMBER(m_voices, m_filter_unk));
+
+ save_item(STRUCT_MEMBER(m_voices, m_sample_last));
+ save_item(STRUCT_MEMBER(m_voices, m_sample));
+
+ save_item(STRUCT_MEMBER(m_voices, m_env_current));
+ save_item(STRUCT_MEMBER(m_voices, m_env_target));
+ save_item(STRUCT_MEMBER(m_voices, m_env_level));
+ save_item(STRUCT_MEMBER(m_voices, m_env_scale));
+ save_item(STRUCT_MEMBER(m_voices, m_env_rate));
+
+ save_item(STRUCT_MEMBER(m_voices, m_balance));
+ save_item(STRUCT_MEMBER(m_voices, m_dsp_send));
+
+}
+
+/**************************************************************************/
+void gt155_device::device_reset()
+{
+ std::fill(std::begin(m_data), std::end(m_data), 0);
+ std::fill(std::begin(m_dsp_data), std::end(m_dsp_data), 0);
+ std::fill(std::begin(m_voices), std::end(m_voices), voice_t());
+}
+
+/**************************************************************************/
+void gt155_device::device_clock_changed()
+{
+ m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE);
+}
+
+/**************************************************************************/
+void gt155_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);
+ voice.update_envelope();
+ }
+ }
+
+ outputs[0].put_int_clamp(i, left >> 11, 32678);
+ outputs[1].put_int_clamp(i, right >> 11, 32768);
+ }
+}
+
+/**************************************************************************/
+void gt155_device::rom_bank_pre_change()
+{
+ m_stream->update();
+}
+
+/**************************************************************************/
+void gt155_device::mix_sample(voice_t &voice, s64 &left, s64 &right)
+{
+ // update sample position
+ voice.m_addr_frac += voice.m_pitch;
+ if (voice.m_addr_frac >= (1 << 15))
+ update_sample(voice);
+
+ // interpolate, apply envelope + channel gain and lowpass, and mix into output
+ s64 sample = voice.m_sample_last + (s64(voice.m_sample - voice.m_sample_last) * voice.m_addr_frac >> 15);
+
+ // TODO: does this produce accurate filter output?
+ sample = sample * voice.m_filter_gain;
+ sample += s32(voice.m_filter_out) * (voice.m_filter ^ 0xffff);
+ sample >>= 16;
+ voice.m_filter_out = sample;
+
+ const u16 env_level = voice.m_env_current >> (ENV_SHIFT + 4);
+ sample *= m_volume[env_level];
+
+ left += (sample * voice.m_balance[0]) / 0x1f;
+ right += (sample * voice.m_balance[1]) / 0x1f;
+}
+
+/**************************************************************************/
+void gt155_device::voice_t::update_envelope()
+{
+ if (m_env_target > m_env_current
+ && (m_env_target - m_env_current) > m_env_rate)
+ {
+ m_env_current += m_env_rate;
+ }
+ else if (m_env_target < m_env_current
+ && (m_env_current - m_env_target) > m_env_rate)
+ {
+ m_env_current -= m_env_rate;
+ }
+ else
+ {
+ m_env_current = m_env_target;
+ }
+}
+
+/**************************************************************************/
+void gt155_device::update_sample(voice_t &voice)
+{
+ voice.m_sample_last = voice.m_sample;
+
+ while (voice.m_addr_frac >= (1 << 15))
+ {
+ voice.m_addr += (voice.m_addr_frac >> 15) * (voice.m_format ? 2 : 1);
+ voice.m_addr_frac &= 0x7fff;
+
+ if (voice.m_addr >= voice.m_addr_end)
+ {
+ if (voice.m_addr_loop == voice.m_addr_end)
+ {
+ // if this is a one-shot sample, just disable it now
+ voice.m_enable = 0;
+ voice.m_env_current = voice.m_env_target = 0;
+ return;
+ }
+
+ // apply the fractional component of the loop
+ if (voice.m_format)
+ {
+ voice.m_addr -= (voice.m_addr_end - (voice.m_addr_loop & ~1));
+ voice.m_addr_frac += (voice.m_addr_loop_frac >> 1);
+ if (BIT(voice.m_addr_loop, 0))
+ voice.m_addr_frac += 1 << 14;
+ }
+ else
+ {
+ voice.m_addr -= (voice.m_addr_end - voice.m_addr_loop);
+ voice.m_addr_frac += voice.m_addr_loop_frac;
+ }
+ }
+ }
+
+ if (voice.m_format)
+ {
+ voice.m_sample = read_word(voice.m_addr & ~1);
+ }
+ else
+ {
+ // wk1800 apparently expects 8bit samples to only be expanded to 9 bits
+ // (with m_filter_gain then boosted to compensate)
+ voice.m_sample = s16(s8(read_byte(voice.m_addr))) << 1;
+ }
+}
+
+/**************************************************************************/
+void gt155_device::write(offs_t offset, u8 data)
+{
+ offset &= 0xf;
+
+ if (offset < 6)
+ {
+ m_data[offset] = data;
+ }
+ else if (offset == 6)
+ {
+ switch (data)
+ {
+ case 0x00:
+ m_rom_addr = (m_data[0] << 1) | (m_data[1] << 9) | (m_data[2] << 17);
+ break;
+
+ case 0x06:
+ m_dsp_data[m_data[5] & 0x7f] &= 0xffff;
+ m_dsp_data[m_data[5] & 0x7f] |= ((m_data[0] << 16) | (m_data[1] << 24));
+ break;
+
+ case 0x07:
+ m_dsp_data[m_data[5] & 0x7f] &= 0xffff0000;
+ m_dsp_data[m_data[5] & 0x7f] |= (m_data[0] | (m_data[1] << 8));
+ return;
+
+ default:
+ voice_command(data);
+ break;
+ }
+
+ }
+ else
+ {
+ logerror("%s: write offset %u = %02x\n", machine().describe_context(), offset, data);
+ }
+}
+
+/**************************************************************************/
+u8 gt155_device::read(offs_t offset)
+{
+ u8 data = 0;
+ offset &= 0xf;
+
+ if (offset < 0x6)
+ {
+ data = m_data[offset];
+ }
+ else if (offset == 0xe)
+ {
+ data = read_byte(m_rom_addr);
+ }
+ else if (offset == 0xf)
+ {
+ data = read_byte(m_rom_addr + 1);
+ if (!machine().side_effects_disabled())
+ m_rom_addr += 2;
+ }
+ else if (!machine().side_effects_disabled())
+ {
+ logerror("%s: read offset %u\n", machine().describe_context(), offset);
+ }
+
+ return data;
+}
+
+/**************************************************************************/
+u16 gt155_device::reg16(u8 num) const
+{
+ return m_data[num] | (m_data[num+1] << 8);
+}
+
+/**************************************************************************/
+u32 gt155_device::reg24(u8 num) const
+{
+ return m_data[num] | (m_data[num+1] << 8) | (m_data[num+2] << 16);
+}
+
+/**************************************************************************/
+u32 gt155_device::reg32(u8 num) const
+{
+ return m_data[num] | (m_data[num+1] << 8) | (m_data[num+2] << 16) | (m_data[num+3] << 24);
+}
+
+/**************************************************************************/
+void gt155_device::voice_command(u8 data)
+{
+ m_stream->update();
+
+ voice_t &voice = m_voices[m_data[5] & 0x1f];
+ const u16 cmd = data | ((m_data[5] & 0xe0) << 8);
+
+ switch (cmd)
+ {
+ case 0x0001: // sample start address
+ voice.m_addr = reg32(1) >> 7;
+ voice.m_addr_frac = reg16(0) & 0x7fff;
+ break;
+
+ case 0x2001: // sample loop address
+ voice.m_addr_loop = reg32(1) >> 7;
+ voice.m_addr_loop_frac = reg16(0) & 0x7fff;
+ break;
+
+ case 0x0002: // sample envelope step
+ case 0x2002: // sample envelope step (double rate?) used when forcing a voice off
+ voice.m_env_rate = reg16(0) & 0x7fff;
+ if (cmd == 0x2002)
+ voice.m_env_rate <<= 1;
+ voice.m_env_level = reg16(2);
+ voice.m_env_target = u32(voice.m_env_level) * voice.m_env_scale;
+ break;
+
+ case 0x0003: // sample end address and envelope scale
+ voice.m_addr_end = reg24(0) << 1;
+ voice.m_env_scale = m_data[3];
+ voice.m_env_target = u32(voice.m_env_level) * voice.m_env_scale;
+ break;
+
+ case 0x0004:
+ /*
+ * params used by wk1800:
+ * ff ff 00 00 (on boot)
+ * fe 07 0c 00 (before note on)
+ * f5 0d 0a 02 (starting 8-bit sample)
+ * f5 05 0a 0a (starting 16-bit sample)
+ * 00 20 00 00 (before note off)
+ * 14 00 08 02 (after envelope update)
+ *
+ * TODO: is any of this related to how 8bit samples are expanded?
+ * (see comment at bottom of update_sample)
+ */
+ if (!voice.m_enable && !BIT(m_data[0], 1))
+ {
+ voice.m_format = BIT(m_data[3], 3);
+ voice.m_sample_last = voice.m_sample = 0;
+ }
+ voice.m_enable = BIT(~m_data[0], 1);
+ break;
+
+ case 0x0005: // sample pitch
+ voice.m_pitch = reg16(0);
+ break;
+
+ case 0x2005:
+ {
+ // this is actually gain premultiplied by filter level, so the sound HW does one fewer mult when applying the filter.
+ const u16 gain = reg16(0);
+ voice.m_filter_gain = (gain & 0x1fff) << (3 + (gain >> 13));
+ }
+ break;
+
+ case 0x4005:
+ /*
+ filter coefficient (bit 15 inverted) - this is also premultiplied into the value written to m_filter_gain,
+ with this value probably only being used as a coefficient for the previous filter output?
+ */
+ voice.m_filter = reg16(0) ^ 0x8000;
+ break;
+
+ case 0x6005:
+ voice.m_filter_unk = reg16(0);
+ break;
+
+ case 0x000e:
+ voice.m_balance[0] = m_data[0] & 0x1f;
+ voice.m_balance[1] = m_data[1] & 0x1f;
+ break;
+
+ case 0x200e:
+ voice.m_dsp_send[0] = m_data[0] & 0x1f;
+ voice.m_dsp_send[1] = m_data[1] & 0x1f;
+ break;
+
+ case 0x0014: // read envelope ready status
+ if (voice.m_env_current == voice.m_env_target)
+ m_data[2] = 0x00;
+ else
+ m_data[2] = 0x08;
+ break;
+
+ case 0x2018: // read envelope output & current sample output
+ m_data[1] = voice.m_env_current >> ENV_SHIFT;
+ m_data[2] = voice.m_env_current >> (ENV_SHIFT + 8);
+ m_data[3] = voice.m_sample;
+ m_data[4] = voice.m_sample >> 8;
+ break;
+
+ default:
+ logerror("%s: sound cmd %02x%02x, param = %02x %02x %02x %02x %02x\n", machine().describe_context(),
+ m_data[5], data,
+ m_data[0], m_data[1], m_data[2], m_data[3], m_data[4]);
+ break;
+ }
+}
diff --git a/src/devices/sound/gt155.h b/src/devices/sound/gt155.h
new file mode 100644
index 00000000000..fe1d4d96691
--- /dev/null
+++ b/src/devices/sound/gt155.h
@@ -0,0 +1,103 @@
+// license:BSD-3-Clause
+// copyright-holders: Devin Acker
+
+/***************************************************************************
+ Casio GT155 (HG51B155FD)
+***************************************************************************/
+
+#ifndef MAME_SOUND_GT155_H
+#define MAME_SOUND_GT155_H
+
+#pragma once
+
+#include "dirom.h"
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+class gt155_device : public device_t,
+ public device_sound_interface,
+ public device_rom_interface<23>
+{
+public:
+ static constexpr feature_type imperfect_features() { return feature::SOUND; }
+
+ // construction/destruction
+ gt155_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
+
+ void write(offs_t offset, u8 data);
+ u8 read(offs_t offset);
+
+protected:
+ // device_t overrides
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+ virtual void device_clock_changed() 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_pre_change() override;
+
+private:
+ static constexpr unsigned CLOCKS_PER_SAMPLE = 512;
+ static constexpr unsigned ENV_SHIFT = 8;
+
+ struct voice_t
+ {
+ u8 m_enable = 0;
+ u8 m_format = 0;
+
+ u32 m_addr = 0;
+ u32 m_addr_frac = 0;
+ u32 m_addr_end = 0;
+ u32 m_addr_loop = 0;
+ u32 m_addr_loop_frac = 0;
+
+ u32 m_pitch = 0;
+
+ u32 m_filter_gain = 0;
+ u16 m_filter = 0;
+ s16 m_filter_out = 0;
+ u16 m_filter_unk = 0;
+
+ s16 m_sample_last = 0;
+ s16 m_sample = 0;
+
+ u32 m_env_current = 0;
+ u32 m_env_target = 0;
+ u16 m_env_level = 0;
+ u8 m_env_scale = 0;
+ u16 m_env_rate = 0;
+
+ u8 m_balance[2] = {0};
+ u8 m_dsp_send[2] = {0};
+
+ void update_envelope();
+ };
+
+ void mix_sample(voice_t &voice, s64 &left, s64 &right);
+ void update_sample(voice_t &voice);
+
+ u16 reg16(u8 num) const;
+ u32 reg24(u8 num) const;
+ u32 reg32(u8 num) const;
+
+ void voice_command(u8 data);
+
+ sound_stream *m_stream;
+
+ u16 m_volume[0x800];
+
+ u8 m_data[6];
+ u32 m_dsp_data[128];
+ u32 m_rom_addr;
+
+ voice_t m_voices[32];
+};
+
+DECLARE_DEVICE_TYPE(GT155, gt155_device)
+
+#endif // MAME_SOUND_GT155_H
diff --git a/src/mame/casio/wk1800.cpp b/src/mame/casio/wk1800.cpp
new file mode 100644
index 00000000000..a6026921020
--- /dev/null
+++ b/src/mame/casio/wk1800.cpp
@@ -0,0 +1,637 @@
+// license:BSD-3-Clause
+// copyright-holders:Devin Acker
+
+/*
+ Casio WK-1600/1800 series keyboards
+
+ Models on this hardware:
+ - CTK-711EX (1998)
+ 61 keys, 5MB wave ROM
+ - CTK-811EX (1998), CTK-731 (1999)
+ 61 keys, 5MB wave ROM, floppy drive
+ - WK-1600, WK-1630 (2000)
+ 73 keys, 8MB wave ROM
+ - WK-1800 (2000)
+ 73 keys, 8MB wave ROM, floppy drive
+ - AP-60R (1999), AP-65R (2001)
+ 88 keys, 8MB wave ROM, floppy drive
+
+ TODO:
+ - fix floppy controller hookup for wk1800. current issues:
+ - pressing the Disk button with the drive empty starts the drive motor,
+ then the firmware waits forever on some status bit that is never set
+ - pressing the Disk button with a disk inserted results in several 'forced abort'
+ errors from the H8 DMA controller
+ - wk1800 firmware seems to rely on different TS bit behavior from the HD63266
+ compared to a standard uPD765
+ - add software list for style/program disks
+ */
+
+#include "emu.h"
+
+#include "bus/midi/midiinport.h"
+#include "bus/midi/midioutport.h"
+#include "cpu/h8/h83048.h"
+#include "imagedev/floppy.h"
+#include "machine/nvram.h"
+#include "machine/gt913_kbd.h"
+#include "machine/upd765.h"
+#include "sound/gt155.h"
+#include "video/hd44780.h"
+
+#include "emupal.h"
+#include "screen.h"
+#include "softlist_dev.h"
+#include "speaker.h"
+
+#include <algorithm>
+
+namespace {
+
+class wk1800_state : public driver_device
+{
+public:
+ static constexpr feature_type unemulated_features() { return feature::DISK; }
+
+ wk1800_state(machine_config const &mconfig, device_type type, char const *tag)
+ : driver_device(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_gt155(*this, "gt155")
+ , m_lcdc(*this, "lcdc")
+ , m_fdc(*this, "fdc")
+ , m_floppy(*this, "fdc:0")
+ , m_sound_rom(*this, "gt155")
+ , m_inputs(*this, "KC%u", 0U)
+ , m_outputs(*this, "%02x.%d.%d", 0U, 0U, 0U)
+ , m_led(*this, "led%d", 0U)
+ , m_led_power(*this, "led_power")
+ {
+ }
+
+ void wk1600(machine_config &config) ATTR_COLD;
+ void wk1800(machine_config &config) ATTR_COLD;
+
+ TIMER_CALLBACK_MEMBER(nmi_clear) { m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); }
+
+ ioport_value lcd_r() { return m_lcdc->db_r() >> 4; }
+ void lcd_w(int state) { m_lcdc->db_w(state << 4); }
+
+ void fdc_rate_w(int state) { if (m_fdc) m_fdc->rate_w(state); }
+
+ void shift_data_w(int state) { m_shift_data = state; }
+ void led_clk_w(int state);
+ void input_clk_w(int state);
+
+ DECLARE_INPUT_CHANGED_MEMBER(power_w);
+
+ template<int StartBit> ioport_value inputs_r();
+
+ void apo_w(int state);
+
+private:
+ void wk1600_map(address_map &map) ATTR_COLD;
+ void wk1800_map(address_map &map) ATTR_COLD;
+
+ virtual void driver_start() override ATTR_COLD;
+
+ void render_w(int state);
+
+ required_device<h83048_device> m_maincpu;
+ required_device<gt155_device> m_gt155;
+ required_device<hd44780_device> m_lcdc;
+ optional_device<hd63266f_device> m_fdc;
+ optional_device<floppy_connector> m_floppy;
+
+ required_memory_region m_sound_rom;
+
+ emu_timer* m_nmi_timer = nullptr;
+
+ optional_ioport_array<8> m_inputs;
+
+ output_finder<64, 8, 5> m_outputs;
+ output_finder<8> m_led;
+ output_finder<> m_led_power;
+
+ u8 m_sound_regs[16];
+ u32 m_sound_rom_addr;
+ u32 m_dsp_data[128];
+
+ u8 m_led_sel, m_input_sel;
+ u8 m_led_clk, m_input_clk, m_shift_data;
+};
+
+class wk1600_state : public wk1800_state
+{
+public:
+ static constexpr feature_type unemulated_features() { return feature::NONE; }
+
+ using wk1800_state::wk1800_state;
+};
+
+/**************************************************************************/
+void wk1800_state::led_clk_w(int state)
+{
+ if (state && !m_led_clk)
+ {
+ m_led_sel <<= 1;
+ m_led_sel |= (m_shift_data & 1);
+
+ for (int i = 0; i < 8; i++)
+ m_led[i] = BIT(~m_led_sel, i);
+ }
+
+ m_led_clk = state;
+}
+
+/**************************************************************************/
+void wk1800_state::input_clk_w(int state)
+{
+ if (state && !m_input_clk)
+ {
+ m_input_sel <<= 1;
+ m_input_sel |= (m_shift_data & 1);
+ }
+
+ m_input_clk = state;
+}
+
+/**************************************************************************/
+INPUT_CHANGED_MEMBER(wk1800_state::power_w)
+{
+ if (newval)
+ {
+ m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
+ m_nmi_timer->adjust(attotime::never);
+ }
+ else
+ {
+ // give the CPU enough time to switch NMI to active-high so it fires again
+ // otherwise, releasing the power button too quickly may be ignored
+ m_nmi_timer->adjust(attotime::from_msec(100));
+ }
+}
+
+/**************************************************************************/
+template<int StartBit>
+ioport_value wk1800_state::inputs_r()
+{
+ ioport_value result = 0;
+ for (unsigned i = 0U; i < m_inputs.size(); i++)
+ {
+ if (BIT(m_input_sel, i))
+ result |= m_inputs[i].read_safe(0);
+ }
+
+ return result >> StartBit;
+}
+
+/**************************************************************************/
+void wk1800_state::apo_w(int state)
+{
+ logerror("apo_w: %x\n", state);
+ if (!state)
+ m_lcdc->reset();
+
+ m_led_power = state;
+ m_gt155->set_output_gain(ALL_OUTPUTS, state ? 1.0 : 0.0);
+}
+
+/**************************************************************************/
+void wk1800_state::render_w(int state)
+{
+ if (!state)
+ return;
+
+ const u8 *render = m_lcdc->render();
+ for (int x = 0; x < 64; x++)
+ {
+ for (int y = 0; y < 8; y++)
+ {
+ u8 v = *render++;
+ for (int z = 0; z < 5; z++)
+ m_outputs[x][y][z] = (v >> z) & 1;
+ }
+ render += 8;
+ }
+}
+
+
+/**************************************************************************/
+void wk1800_state::wk1600_map(address_map &map)
+{
+ map(0x00000, 0x1ffff).rom();
+ map(0x20000, 0x2ffff).rw(m_gt155, FUNC(gt155_device::read), FUNC(gt155_device::write));
+ map(0x30000, 0x30001).mirror(0x0fff0).r("kbd", FUNC(gt913_kbd_hle_device::read));
+ map(0x30002, 0x30003).mirror(0x0fff0).r("kbd", FUNC(gt913_kbd_hle_device::status_r));
+ map(0x80000, 0x9ffff).mirror(0x60000).ram().share("nvram");
+}
+
+/**************************************************************************/
+void wk1800_state::wk1800_map(address_map &map)
+{
+ map(0x00000, 0x1ffff).rom();
+ map(0x20000, 0x2ffff).rw(m_gt155, FUNC(gt155_device::read), FUNC(gt155_device::write));
+ map(0x30000, 0x30001).mirror(0x0fff0).r("kbd", FUNC(gt913_kbd_hle_device::read));
+ map(0x30002, 0x30003).mirror(0x0fff0).r("kbd", FUNC(gt913_kbd_hle_device::status_r));
+// map(0x40000, 0x40003).mirror(0x1fffc).m(m_fdc, FUNC(hd63266f_device::map));
+// map(0x60000, 0x7ffff).rw(m_fdc, FUNC(hd63266f_device::dma_r), FUNC(hd63266f_device::dma_w));
+ map(0x80000, 0xbffff).mirror(0x40000).ram().share("nvram");
+}
+
+
+/**************************************************************************/
+void wk1800_state::driver_start()
+{
+ m_led.resolve();
+ m_led_power.resolve();
+ m_outputs.resolve();
+
+ m_nmi_timer = timer_alloc(FUNC(wk1800_state::nmi_clear), this);
+
+ std::fill(std::begin(m_sound_regs), std::end(m_sound_regs), 0);
+ std::fill(std::begin(m_dsp_data), std::end(m_dsp_data), 0);
+ m_sound_rom_addr = 0;
+
+ m_led_sel = 0xff;
+ m_input_sel = 0;
+
+ m_led_clk = m_input_clk = m_shift_data = 0;
+
+ save_item(NAME(m_sound_regs));
+ save_item(NAME(m_dsp_data));
+ save_item(NAME(m_sound_rom_addr));
+
+ save_item(NAME(m_led_sel));
+ save_item(NAME(m_input_sel));
+
+ save_item(NAME(m_led_clk));
+ save_item(NAME(m_input_clk));
+ save_item(NAME(m_shift_data));
+}
+
+
+/**************************************************************************/
+void wk1800_state::wk1600(machine_config &config)
+{
+ H83048(config, m_maincpu, 16'000'000).set_mode_a20();
+ m_maincpu->set_addrmap(AS_PROGRAM, &wk1800_state::wk1600_map);
+ m_maincpu->read_adc<0>().set_constant(0);
+ m_maincpu->read_adc<1>().set_ioport("AN1");
+ m_maincpu->read_adc<2>().set_constant(0);
+ m_maincpu->read_adc<3>().set_ioport("AN3");
+ m_maincpu->read_port6().set_ioport("P6");
+ m_maincpu->read_port7().set_ioport("P7");
+ m_maincpu->read_port8().set_ioport("P8");
+ m_maincpu->write_port8().set_ioport("P8");
+ m_maincpu->read_port9().set_ioport("P9");
+ m_maincpu->read_porta().set_ioport("PA");
+ m_maincpu->write_porta().set_ioport("PA");
+ m_maincpu->read_portb().set(FUNC(wk1800_state::lcd_r));
+ m_maincpu->write_portb().set_ioport("PB");
+
+ NVRAM(config, "nvram");
+
+ GT913_KBD_HLE(config, "kbd"); // actually TC190C020AF-001 gate array
+
+ auto &mdin(MIDI_PORT(config, "mdin"));
+ midiin_slot(mdin);
+ mdin.rxd_handler().set(m_maincpu, FUNC(h83048_device::sci_rx_w<0>));
+
+ auto &mdout(MIDI_PORT(config, "mdout"));
+ midiout_slot(mdout);
+ m_maincpu->write_sci_tx<0>().set(mdout, FUNC(midi_port_device::write_txd));
+
+ HD44780(config, m_lcdc, 270'000); // TODO: Wrong device type, should be SED1278F2A; clock not measured, datasheet typical clock used
+ m_lcdc->set_lcd_size(2, 8);
+
+ auto &screen(SCREEN(config, "screen", SCREEN_TYPE_SVG));
+ screen.set_refresh_hz(60);
+ screen.set_size(1755, 450);
+ screen.set_visarea_full();
+ screen.screen_vblank().set(FUNC(wk1800_state::render_w));
+
+ SPEAKER(config, "lspeaker").front_left();
+ SPEAKER(config, "rspeaker").front_right();
+
+ GT155(config, m_gt155, 24.576_MHz_XTAL);
+ m_gt155->add_route(0, "lspeaker", 1.0);
+ m_gt155->add_route(1, "rspeaker", 1.0);
+}
+
+/**************************************************************************/
+[[maybe_unused]] static void wk1800_floppies(device_slot_interface &device)
+{
+ device.option_add("35hd", FLOPPY_35_HD);
+}
+
+/**************************************************************************/
+void wk1800_state::wk1800(machine_config &config)
+{
+ wk1600(config);
+ m_maincpu->set_addrmap(AS_PROGRAM, &wk1800_state::wk1800_map);
+
+#if 0
+ HD63266F(config, m_fdc, 16'000'000);
+ m_fdc->set_ready_line_connected(false);
+ m_fdc->drq_wr_callback().set_inputline(m_maincpu, H8_INPUT_LINE_DREQ0);
+ m_maincpu->tend0().set(m_fdc, FUNC(upd765a_device::tc_line_w));
+
+ FLOPPY_CONNECTOR(config, m_floppy, wk1800_floppies, "35hd", floppy_image_device::default_pc_floppy_formats);
+ m_floppy->enable_sound(true);
+ SOFTWARE_LIST(config, "flop_list").set_compatible("midi_flop");
+#endif
+}
+
+
+INPUT_PORTS_START(wk1600)
+ PORT_START("kbd:FI0")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E1")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F1#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G1#")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A1#")
+
+ PORT_START("kbd:FI1")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B1")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C2#")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D2#")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E2")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F2#")
+
+ PORT_START("kbd:FI2")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G2#")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A2#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B2")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C3#")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3")
+
+ PORT_START("kbd:FI3")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D3#")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E3")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F3#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G3#")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A3#")
+
+ PORT_START("kbd:FI4")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B3")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C4#")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D4#")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E4")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F4#")
+
+ PORT_START("kbd:FI5")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G4#")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A4#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B4")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C5#")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5")
+
+ PORT_START("kbd:FI6")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D5#")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E5")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F5#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G5#")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A5#")
+
+ PORT_START("kbd:FI7")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B5")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C6#")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D6")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D6#")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E6")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F6")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F6#")
+
+ PORT_START("kbd:FI8")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G6")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G6#")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A6")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("A6#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("B6")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C7")
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("C7#")
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D7")
+
+ PORT_START("kbd:FI9")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("D7#")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("E7")
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F7")
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("F7#")
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("G7")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("kbd:FI10")
+ PORT_START("kbd:KI0")
+ PORT_START("kbd:KI1")
+ PORT_START("kbd:KI2")
+
+ PORT_START("kbd:VELOCITY")
+ PORT_BIT( 0xff, 0xff, IPT_POSITIONAL ) PORT_NAME("Key Velocity") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_CENTERDELTA(0) PORT_CODE_DEC(KEYCODE_PGDN) PORT_CODE_INC(KEYCODE_PGUP)
+
+ PORT_START("KC0")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Mode")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Intro")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Mixer Select")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 8 / Chord 3")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration A")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 16 / Track 6")
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Split")
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad - / No") PORT_CODE(KEYCODE_MINUS_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Demo")
+
+ PORT_START("KC1")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Record")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Normal / Fill-In")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 1 / Upper 1")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 9 / Bass")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration B")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Auto Harmonize")
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Layer")
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 2") PORT_CODE(KEYCODE_2_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synth")
+
+ PORT_START("KC2")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Song")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Variation / Fill-In")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 2 / Upper 2")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 10 / Rhythm")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration C")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Enter") PORT_CODE(KEYCODE_ENTER_PAD)
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Rhythm")
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 5") PORT_CODE(KEYCODE_5_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tune")
+
+ PORT_START("KC3")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Pattern")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Synchro / Ending")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 3 / Lower 1")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 11 / Track 1")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration D")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Left") PORT_CODE(KEYCODE_LEFT)
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tone")
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 8") PORT_CODE(KEYCODE_8_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("MIDI")
+
+ PORT_START("KC4")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("DSP")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Start / Stop")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 4 / Lower 2")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 12 / Track 2")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration E")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Up") PORT_CODE(KEYCODE_UP)
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 0") PORT_CODE(KEYCODE_0_PAD)
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad + / Yes") PORT_CODE(KEYCODE_PLUS_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("KC5")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Contrast")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Down")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 5 / Acc. Vol.")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 13 / Track 3")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration Store")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Down") PORT_CODE(KEYCODE_DOWN)
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 1") PORT_CODE(KEYCODE_1_PAD)
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 3") PORT_CODE(KEYCODE_3_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("KC6")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Free Session")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tempo Up")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 6 / Chord 1")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 14 / Track 4")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose Down")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Cursor Right") PORT_CODE(KEYCODE_RIGHT)
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 4") PORT_CODE(KEYCODE_4_PAD)
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 6") PORT_CODE(KEYCODE_6_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("KC7")
+ PORT_BIT( 0x001, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("One Touch Preset")
+ PORT_BIT( 0x002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Registration Bank")
+ PORT_BIT( 0x004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 7 / Chord 2")
+ PORT_BIT( 0x008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Channel 15 / Track 5")
+ PORT_BIT( 0x010, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Transpose Up")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
+ PORT_BIT( 0x040, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 7") PORT_CODE(KEYCODE_7_PAD)
+ PORT_BIT( 0x080, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Keypad 9") PORT_CODE(KEYCODE_9_PAD)
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("SWITCH")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_POWER_ON ) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, FUNC(wk1800_state::power_w), 0)
+
+ PORT_START("AN1")
+ PORT_BIT( 0x3ff, 0x200, IPT_PADDLE ) PORT_NAME("Pitch Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0x3ff) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
+
+ PORT_START("AN3")
+ PORT_BIT( 0x3ff, 0x000, IPT_POSITIONAL_V ) PORT_NAME("Modulation Wheel") PORT_SENSITIVITY(100) PORT_KEYDELTA(10) PORT_MINMAX(0x00, 0x3ff) PORT_PLAYER(2) PORT_CODE_DEC(JOYCODE_Y_DOWN_SWITCH) PORT_CODE_INC(JOYCODE_Y_UP_SWITCH)
+
+ PORT_START("P6")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x06, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1800_state::inputs_r<1>))
+ PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("P7")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1800_state::inputs_r<0>))
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_CONFNAME( 0x04, 0x00, "Power Source" )
+ PORT_CONFSETTING( 0x00, "AC Adapter" )
+ PORT_CONFSETTING( 0x04, "Battery" )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Pedal")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1800_state::inputs_r<4>))
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("P8")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OUTPUT ) // reset sound, LCD, FDC
+ PORT_BIT( 0x0e, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_CUSTOM ) // high = WK-1800, low = WK-1600
+ PORT_BIT( 0xe0, IP_ACTIVE_HIGH, IPT_UNUSED )
+
+ PORT_START("P9")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1800_state::inputs_r<3>))
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x38, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1800_state::inputs_r<5>))
+
+ PORT_START("PA")
+ PORT_BIT( 0x03, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_CUSTOM_MEMBER(FUNC(wk1800_state::inputs_r<8>))
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::input_clk_w))
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::shift_data_w))
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::led_clk_w))
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::apo_w))
+
+ PORT_START("PB")
+ PORT_BIT( 0x0f, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::lcd_w))
+ PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::e_w))
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rw_w))
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED )
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_DEVICE_MEMBER("lcdc", FUNC(hd44780_device::rs_w))
+INPUT_PORTS_END
+
+INPUT_PORTS_START( wk1800 )
+ PORT_INCLUDE(wk1600)
+
+ PORT_MODIFY("KC2")
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Tune / MIDI")
+
+ PORT_MODIFY("KC3")
+ PORT_BIT( 0x100, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Touch Response")
+
+ PORT_MODIFY("KC7")
+ PORT_BIT( 0x020, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_NAME("Disk")
+
+ PORT_MODIFY("P7")
+ PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_CUSTOM ) // TODO: disk HD/DD detect
+
+ PORT_MODIFY("P8")
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_CUSTOM ) // high = WK-1800, low = WK-1600
+
+ PORT_MODIFY("PA")
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OUTPUT ) PORT_WRITE_LINE_MEMBER(FUNC(wk1800_state::fdc_rate_w))
+INPUT_PORTS_END
+
+
+ROM_START(wk1800)
+ ROM_REGION(0x20000, "maincpu", 0) // "Ver.1.61"
+ ROM_LOAD("hd6433048sa89f.lsi9", 0x00000, 0x20000, CRC(bd5bfab3) SHA1(2731b5ab1cb288553bfee9b856264a5d1eb0ef1a))
+
+ ROM_REGION16_LE(0x800000, "gt155", 0) // "Ver.1.60"
+ ROM_LOAD("lhmn5kpn.lsi2", 0x000000, 0x400000, CRC(f75d21f0) SHA1(e08937ce2fa152db85fa96cef53f81351e690666))
+ ROM_LOAD("lhmn5kpp.lsi1", 0x400000, 0x400000, CRC(f6cc5048) SHA1(9f48730a5bd3582f6fe08cb937848907d11aa804))
+
+ ROM_REGION(585110, "screen", 0)
+ ROM_LOAD("wk1800.svg", 0, 585110, CRC(5fab0b26) SHA1(6181b9eb950cd30474efb37b8cd660cba4b0b914))
+ROM_END
+
+#define rom_wk1600 rom_wk1800
+
+} // anonymous namespace
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+SYST( 2000, wk1800, 0, 0, wk1800, wk1800, wk1800_state, empty_init, "Casio", "WK-1800", MACHINE_SUPPORTS_SAVE )
+SYST( 2000, wk1600, wk1800, 0, wk1600, wk1600, wk1600_state, empty_init, "Casio", "WK-1600", MACHINE_SUPPORTS_SAVE )
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 3d139299df1..03cd2de2312 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -16219,6 +16219,10 @@ sk10
@source:casio/sx1000.cpp
sx1010
+@source:casio/wk1800.cpp
+wk1600 // 2000 Casio
+wk1800 // 2000 Casio
+
@source:casio/zoomer.cpp
zoomer