diff --git a/src/mame/layout/msx_turbor.lay b/src/mame/layout/msx_turbor.lay
new file mode 100644
index 00000000000..6f66af755e6
--- /dev/null
+++ b/src/mame/layout/msx_turbor.lay
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index fc433641d81..2174e417874 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -32760,8 +32760,6 @@ expert3t //
expertac //
expertdx //
fsa1fx // 1988 MSX2+ Japan
-fsa1gt //
-fsa1st //
fsa1wsx // 1989 MSX2+ Japan
fsa1wx // 1988 MSX2+ Japan
fsa1wxa // 1988 MSX2+ Japan
@@ -32771,6 +32769,10 @@ phc35j // 1989 MSX2+ Japan
phc70fd // 1988 MSX2+ Japan
phc70fd2 // 1988 MSX2+ Japan
+@source:msx/msxtr.cpp
+fsa1gt //
+fsa1st //
+
@source:msx/pengadvb.cpp
pengadvb // (c) 1988 Screen
pengadvb2 // (c) 1988 Comet
diff --git a/src/mame/msx/msx2p.cpp b/src/mame/msx/msx2p.cpp
index 7cf6cf8cbd0..6458918768b 100644
--- a/src/mame/msx/msx2p.cpp
+++ b/src/mame/msx/msx2p.cpp
@@ -53,9 +53,6 @@ public:
void phc35j(machine_config &config);
void hbf1xdj(machine_config &config);
void hbf1xv(machine_config &config);
-
- void fsa1gt(machine_config &config);
- void fsa1st(machine_config &config);
};
/******************************** MSX 2+ **********************************/
@@ -638,94 +635,6 @@ void msx2p_state::hbf1xv(machine_config &config)
msx2plus(SND_YM2149, config, layout_msx_jp_1fdd);
}
-/* MSX Turbo-R - Panasonic FS-A1GT */
-
-ROM_START(fsa1gt)
- ROM_REGION(0x46c000, "maincpu", 0)
- ROM_LOAD("a1gtbios.rom", 0x0000, 0x8000, CRC(937c8dbb) SHA1(242e73d8284a012b275c0a266844ebbc4269d787))
- ROM_LOAD("a1gtext.rom", 0x8000, 0x4000, CRC(70aea0fe) SHA1(018d7a5222f28514908fb1b1513286a6558a6d05))
- ROM_LOAD("a1gtdos.rom", 0xc000, 0x10000, CRC(bb2a0eae) SHA1(4880bf34f1c86fff5456ec2b4cf70d02339e2caa))
- ROM_LOAD("a1gtkdr.rom", 0x1c000, 0x8000, CRC(eaf0d125) SHA1(5b39c1ccd3a213b78e02927f56a9abc72cd8c28d))
- ROM_LOAD("a1gtmus.rom", 0x24000, 0x4000, CRC(f5f93437) SHA1(6aea1aef5ec31c1826c22edf580525f93baad425))
- ROM_LOAD("a1gtopt.rom", 0x28000, 0x4000, CRC(50d11f60) SHA1(b4433a3975c57dd440d6bf12dbd28b2ac1b90ef4))
- ROM_LOAD("a1gtkfn.rom", 0x2c000, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
- ROM_LOAD("a1gtfirm.rom", 0x6c000, 0x400000, CRC(feefeadc) SHA1(e779c338eb91a7dea3ff75f3fde76b8af22c4a3a))
-ROM_END
-
-void msx2p_state::fsa1gt(machine_config &config)
-{
- // AY8910 (in T9769)
- // FDC: tc8566af, 1 3.5" DSDD drive
- // 2 Cartridge slots
- // T9769C + S1990
- // FM built-in
- // Microphone
- // MIDI-in
- // MIDI-out
- // firmware switch
- // pause button
- // ren-sha turbo slider
-
- add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 0, 2, "maincpu");
- add_internal_slot(config, MSX_SLOT_MUSIC, "mus", 0, 2, 1, 1, "maincpu", 0x24000).set_ym2413_tag(m_ym2413);
- add_internal_slot(config, MSX_SLOT_ROM, "opt", 0, 3, 1, 1, "maincpu", 0x28000);
- add_cartridge_slot<1>(config, 1);
- add_cartridge_slot<2>(config, 2);
- add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x20000); // 128KB?? Mapper RAM
- add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "maincpu", 0x8000);
- add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "maincpu", 0x1c000);
- add_internal_disk(config, MSX_SLOT_DISK4_TC8566, "dos", 3, 2, 1, 3, "maincpu", 0xc000);
- add_internal_slot(config, MSX_SLOT_ROM, "firm", 3, 3, 0, 4, "maincpu", 0x6c000);
- MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);
-
- msx_ym2413(config);
-
- turbor(SND_AY8910, config, layout_msx_jp_1fdd);
-}
-
-/* MSX Turbo-R - Panasonic FS-A1ST */
-
-ROM_START(fsa1st)
- ROM_REGION(0x46c000, "maincpu", 0)
- ROM_LOAD("a1stbios.rom", 0x0000, 0x8000, CRC(77b94ae0) SHA1(f078b5ec56884bfb81481d45c7151418770bff5a))
- ROM_LOAD("a1stext.rom", 0x8000, 0x4000, CRC(2c2c77a4) SHA1(373412f9c32762de1c3a7e27fc3d80614e0a0c8e))
- ROM_LOAD("a1stdos.rom", 0xc000, 0x10000, CRC(1fc71407) SHA1(5d2186658adcf4ce0c2d3232384b5712341108e5))
- ROM_LOAD("a1stkdr.rom", 0x1c000, 0x8000, CRC(eaf0d125) SHA1(5b39c1ccd3a213b78e02927f56a9abc72cd8c28d))
- ROM_LOAD("a1stmus.rom", 0x24000, 0x4000, CRC(fd7dec41) SHA1(e002a9b426732e6c2d31e548c40cf7c122348ce3))
- ROM_LOAD("a1stopt.rom", 0x28000, 0x4000, CRC(c6a4a2a1) SHA1(cb06dea7b025745f9d2b87dcf03ded615287ead3))
- ROM_LOAD("a1stkfn.rom", 0x2c000, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
- ROM_LOAD("a1stfirm.rom", 0x6c000, 0x400000, CRC(139ac99c) SHA1(c212b11fda13f83dafed688c54d098e7e47ab225))
-ROM_END
-
-void msx2p_state::fsa1st(machine_config &config)
-{
- // AY8910 (in T9769)
- // FDC: tc8566af, 1 3.5" DSDD drive
- // T9769C + S1990
- // 2 Cartridge slots
- // FM built-in
- // microphone
- // firmware switch
- // pause button
- // ren-sha turbo slider
-
- add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 0, 2, "maincpu");
- add_internal_slot(config, MSX_SLOT_MUSIC, "mus", 0, 2, 1, 1, "maincpu", 0x24000).set_ym2413_tag(m_ym2413);
- add_internal_slot(config, MSX_SLOT_ROM, "opt", 0, 3, 1, 1, "maincpu", 0x28000);
- add_cartridge_slot<1>(config, 1);
- add_cartridge_slot<2>(config, 2);
- add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x20000); // 128KB?? Mapper RAM
- add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "maincpu", 0x8000);
- add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "maincpu", 0x1c000);
- add_internal_disk(config, MSX_SLOT_DISK4_TC8566, "dos", 3, 2, 1, 3, "maincpu", 0xc000);
- add_internal_slot(config, MSX_SLOT_ROM, "firm", 3, 3, 0, 4, "maincpu", 0x6c000);
- MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);
-
- msx_ym2413(config);
-
- turbor(SND_AY8910, config, layout_msx_jp_1fdd);
-}
-
} // anonymous namespace
COMP(19??, expert3i, 0, 0, expert3i, msx2, msx2p_state, empty_init, "Ciel", "Expert 3 IDE (MSX2+, Brazil)", MACHINE_NOT_WORKING) // Some hardware not emulated
@@ -741,8 +650,3 @@ COMP(1989, phc70fd2, 0, 0, phc70fd2, msx2jp, msx2p_state, empty
COMP(1989, phc35j, 0, 0, phc35j, msx2jp, msx2p_state, empty_init, "Sanyo", "PHC-35J / Wavy35 (MSX2+, Japan)", 0)
COMP(1988, hbf1xdj, 0, 0, hbf1xdj, msx2jp, msx2p_state, empty_init, "Sony", "HB-F1XDJ (MSX2+, Japan)", 0)
COMP(1989, hbf1xv, 0, 0, hbf1xv, msx2jp, msx2p_state, empty_init, "Sony", "HB-F1XV (MSX2+, Japan)", 0)
-
-/* MSX Turbo-R */
-/* Temporary placeholders, Turbo-R hardware is not supported yet */
-COMP(1991, fsa1gt, 0, 0, fsa1gt, msx2jp, msx2p_state, empty_init, "Panasonic", "FS-A1GT (MSX Turbo-R, Japan)", MACHINE_NOT_WORKING)
-COMP(1991, fsa1st, 0, 0, fsa1st, msx2jp, msx2p_state, empty_init, "Panasonic", "FS-A1ST (MSX Turbo-R, Japan)", MACHINE_NOT_WORKING)
diff --git a/src/mame/msx/msx_s1990.cpp b/src/mame/msx/msx_s1990.cpp
new file mode 100644
index 00000000000..abc443cdf33
--- /dev/null
+++ b/src/mame/msx/msx_s1990.cpp
@@ -0,0 +1,268 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+#include "emu.h"
+#include "msx_s1990.h"
+
+
+#define LOG_CPU (1U << 1)
+#define LOG_PCM (1U << 2)
+//#define VERBOSE (LOG_CPU | LOG_PCM)
+#include "logmacro.h"
+
+/***************************************************************************
+
+ ASCII S1990 MSX-Engine
+
+TODO:
+- Injection of wait states when accessing VDP when Z80 is active.
+- Injection of wait states when accessing VDP when R800 is active.
+- Injection of wait state to align 7MHz R800 bus with 3.5MHz MSX bus.
+- Unknown what the other internal registers do
+
+Internal registers:
+- 0x00
+- 0x01
+- 0x02
+- 0x03
+- 0x04
+- 0x05 - Read only?
+ Read
+ 7------- Unknown
+ -6------ 1 - Firmware enabled
+ --543210 Unknown
+- 0x06 - R/W - CPU Control
+ Write
+ 76------ Unknown
+ --5----- 0 - R800 active, 1 - Z80 active
+ ---43210 Unknown
+- 0x07
+- 0x08
+- 0x09
+- 0x0a
+- 0x0b
+- 0x0c
+- 0x0d
+- 0x0e - Byte before last byte written - read only?
+- 0x0f - Last byte written - read only?
+
+***************************************************************************/
+
+
+DEFINE_DEVICE_TYPE(MSX_S1990, msx_s1990_device, "msx_s1990", "MSX-Engine S1990")
+
+
+msx_s1990_device::msx_s1990_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, MSX_S1990, tag, owner, clock)
+ , device_memory_interface(mconfig, *this)
+ , m_program_config("program", ENDIANNESS_LITTLE, 8, 16, 0)
+ , m_io_config("io", ENDIANNESS_LITTLE, 8, 16, 0)
+ , m_firmware_switch_cb(*this, 0)
+ , m_pause_led_cb(*this)
+ , m_r800_led_cb(*this)
+ , m_dac_write_cb(*this)
+ , m_sample_hold_cb(*this)
+ , m_select_cb(*this)
+ , m_filter_cb(*this)
+ , m_muting_cb(*this)
+ , m_comp_cb(*this, 0)
+ , m_z80(*this, finder_base::DUMMY_TAG)
+ , m_r800(*this, finder_base::DUMMY_TAG)
+ , m_reg_index(0)
+ , m_last_counter_reset(attotime::zero)
+ , m_last_pcm_write_ticks(0)
+ , m_pcm_control(0)
+ , m_pcm_data(0x80)
+ , m_dac_timer(nullptr)
+ , m_z80_halt_enabled(false)
+{
+}
+
+
+device_memory_interface::space_config_vector msx_s1990_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_IO, &m_io_config)
+ };
+}
+
+
+void msx_s1990_device::device_start()
+{
+ space(AS_PROGRAM).specific(m_data);
+ space(AS_IO).specific(m_io);
+
+ save_item(NAME(m_regs));
+ save_item(NAME(m_reg_index));
+ save_item(NAME(m_last_counter_reset));
+ save_item(NAME(m_last_pcm_write_ticks));
+ save_item(NAME(m_pcm_control));
+ save_item(NAME(m_pcm_data));
+
+ m_dac_timer = timer_alloc(FUNC(msx_s1990_device::dac_write), this);
+}
+
+
+void msx_s1990_device::device_reset()
+{
+ m_regs[6] = 0x20; // Z80 active
+ m_last_counter_reset = attotime::zero;
+ m_last_pcm_write_ticks = 0;
+ m_z80->resume(SUSPEND_REASON_HALT);
+ m_r800->suspend(SUSPEND_REASON_HALT, true);
+}
+
+
+INPUT_CHANGED_MEMBER(msx_s1990_device::pause_callback)
+{
+ if (m_z80_halt_enabled && BIT(m_regs[0x06], 5))
+ m_z80->set_input_line(Z80_INPUT_LINE_WAIT, BIT(newval, 0) ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+void msx_s1990_device::pause_w(u8 data)
+{
+ m_pause_led_cb(BIT(data, 0));
+ m_z80_halt_enabled = BIT(data, 1);
+ m_r800_led_cb(BIT(data, 7));
+}
+
+
+void msx_s1990_device::reg_index_write(u8 data)
+{
+ m_reg_index = data;
+}
+
+
+u8 msx_s1990_device::regs_read()
+{
+ if ((m_reg_index & 0x0f) == 0x05)
+ return (m_firmware_switch_cb()) ? 0x40 : 0x00;
+ return m_regs[m_reg_index & 0x0f];
+}
+
+
+void msx_s1990_device::regs_write(u8 data)
+{
+ if ((m_reg_index & 0x0f) == 0x06)
+ {
+ if (BIT(data, 5))
+ {
+ LOGMASKED(LOG_CPU, "Enable Z80, disable R800\n");
+ m_z80->resume(SUSPEND_REASON_HALT);
+ m_r800->suspend(SUSPEND_REASON_HALT, true);
+ }
+ else
+ {
+ LOGMASKED(LOG_CPU, "Disable Z80, enable R800\n");
+ m_z80->suspend(SUSPEND_REASON_HALT, true);
+ m_r800->resume(SUSPEND_REASON_HALT);
+ }
+ }
+
+ m_regs[m_reg_index & 0x0f] = data;
+}
+
+
+void msx_s1990_device::mem_write(offs_t offset, u8 data)
+{
+ // TODO Clock syncing when in R800 mode
+ m_regs[0x0e] = m_regs[0x0f];
+ m_regs[0x0f] = data;
+ m_data.write_byte(offset, data);
+}
+
+
+u8 msx_s1990_device::mem_read(offs_t offset)
+{
+ // TODO Clock syncing when in R800 mode
+ return m_data.read_byte(offset);
+}
+
+
+void msx_s1990_device::io_write(offs_t offset, u8 data)
+{
+ // TODO Clock syncing/extra wait cycles
+ // TODO Injection of wait states when accessing VDP
+ m_io.write_byte(offset, data);
+}
+
+
+u8 msx_s1990_device::io_read(offs_t offset)
+{
+ // TODO Clock syncing/extra wait cycles
+ return m_io.read_byte(offset);
+}
+
+
+void msx_s1990_device::counter_write(u8 data)
+{
+ // TODO: Round to last counter tick?
+ m_last_counter_reset = machine().time();
+}
+
+
+u8 msx_s1990_device::counter_read(offs_t offset)
+{
+ u64 counter = attotime_to_clocks(machine().time() - m_last_counter_reset) / 28;
+ return (counter >> (8 * BIT(offset, 0))) & 0xff;
+}
+
+
+TIMER_CALLBACK_MEMBER(msx_s1990_device::dac_write)
+{
+ m_dac_write_cb(m_pcm_data);
+}
+
+
+void msx_s1990_device::pmdac(u8 data)
+{
+ m_pcm_data = data;
+ if (!BIT(m_pcm_control, 1))
+ {
+ // Round to last counter tick and schedule next dac write?
+ m_last_pcm_write_ticks = machine().time().as_ticks(PCM_FREQUENCY);
+ m_dac_timer->adjust(attotime::from_ticks(m_last_pcm_write_ticks + 1, PCM_FREQUENCY));
+ }
+}
+
+
+u8 msx_s1990_device::pmcnt()
+{
+ return (machine().time().as_ticks(PCM_FREQUENCY) - m_last_pcm_write_ticks) & 0x03;
+}
+
+
+void msx_s1990_device::pmcntl(u8 data)
+{
+ // 7------- 0
+ // -6------ 0
+ // --5----- 0
+ // ---4---- SMPL
+ // ----3--- SEL
+ // -----2-- FILT
+ // ------1- MUTE
+ // -------0 ADDA ??
+
+ LOGMASKED(LOG_PCM, "pmcntl: %02x\n", data);
+ m_pcm_control = data & 0x1f;
+ m_sample_hold_cb(BIT(m_pcm_control, 4));
+ m_select_cb(BIT(m_pcm_control, 3));
+ m_filter_cb(BIT(m_pcm_control, 2));
+ m_muting_cb(BIT(m_pcm_control, 1));
+}
+
+
+u8 msx_s1990_device::pmstat()
+{
+ // 7------- COMP
+ // -6------ 0
+ // --5----- 0
+ // ---4---- SMPL
+ // ----3--- SEL
+ // -----2-- FILT
+ // ------1- MUTE
+ // -------0 BUFF ??
+
+ return (m_pcm_control & 0x1f) | (m_comp_cb() ? 0x80 : 0x00);
+}
diff --git a/src/mame/msx/msx_s1990.h b/src/mame/msx/msx_s1990.h
new file mode 100644
index 00000000000..3a8c7587d35
--- /dev/null
+++ b/src/mame/msx/msx_s1990.h
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+#ifndef MAME_MSX_MSX_S1990_H
+#define MAME_MSX_MSX_S1990_H
+
+#pragma once
+
+#include "cpu/z80/r800.h"
+#include "cpu/z80/z80.h"
+
+DECLARE_DEVICE_TYPE(MSX_S1990, msx_s1990_device)
+
+class msx_s1990_device : public device_t,
+ public device_memory_interface
+{
+public:
+ msx_s1990_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static constexpr feature_type imperfect_features() { return feature::TIMING; }
+
+ // configuration
+ template void set_z80_tag(T &&tag) { m_z80.set_tag(std::forward(tag)); }
+ template void set_r800_tag(T &&tag) { m_r800.set_tag(std::forward(tag)); }
+ auto firmware_switch_callback() { return m_firmware_switch_cb.bind(); }
+ auto pause_led_callback() { return m_pause_led_cb.bind(); }
+ auto r800_led_callback() { return m_r800_led_cb.bind(); }
+ auto dac_write_callback() { return m_dac_write_cb.bind(); }
+ auto sample_hold_callback() { return m_sample_hold_cb.bind(); }
+ auto select_callback() { return m_select_cb.bind(); }
+ auto filter_callback() { return m_filter_cb.bind(); }
+ auto muting_callback() { return m_muting_cb.bind(); }
+ auto comp_callback() { return m_comp_cb.bind(); }
+
+ void pause_w(u8 data);
+
+ void reg_index_write(u8 data);
+ u8 regs_read();
+ void regs_write(u8 data);
+
+ void mem_write(offs_t offset, u8 data);
+ u8 mem_read(offs_t offset);
+ void io_write(offs_t offset, u8 data);
+ u8 io_read(offs_t offset);
+
+ void counter_write(u8 data);
+ u8 counter_read(offs_t offset);
+
+ void pmdac(u8 data);
+ u8 pmcnt();
+ void pmcntl(u8 data);
+ u8 pmstat();
+
+ DECLARE_INPUT_CHANGED_MEMBER(pause_callback);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_memory_interface implementation
+ virtual space_config_vector memory_space_config() const override;
+ virtual u32 translate_memory_address(u16 address) { return address; }
+
+private:
+ TIMER_CALLBACK_MEMBER(dac_write);
+
+ static constexpr u32 PCM_FREQUENCY = 15750;
+ const address_space_config m_program_config;
+ const address_space_config m_io_config;
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_data;
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_io;
+ devcb_read_line m_firmware_switch_cb;
+ devcb_write_line m_pause_led_cb;
+ devcb_write_line m_r800_led_cb;
+ devcb_write8 m_dac_write_cb;
+ devcb_write_line m_sample_hold_cb;
+ devcb_write_line m_select_cb;
+ devcb_write_line m_filter_cb;
+ devcb_write_line m_muting_cb;
+ devcb_read_line m_comp_cb;
+ required_device m_z80;
+ required_device m_r800;
+
+ u8 m_regs[16];
+ u8 m_reg_index;
+ attotime m_last_counter_reset;
+ u64 m_last_pcm_write_ticks;
+ u8 m_pcm_control;
+ u8 m_pcm_data;
+ emu_timer *m_dac_timer;
+ bool m_z80_halt_enabled;
+};
+
+#endif // MAME_MSX_MSX_S1990_H
diff --git a/src/mame/msx/msxtr.cpp b/src/mame/msx/msxtr.cpp
new file mode 100644
index 00000000000..eb684081dbb
--- /dev/null
+++ b/src/mame/msx/msxtr.cpp
@@ -0,0 +1,463 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+
+#include "emu.h"
+#include "msx.h"
+#include "msx_keyboard.h"
+#include "msx_s1990.h"
+#include "msx_systemflags.h"
+#include "bus/midi/midi.h"
+#include "bus/msx/slot/disk.h"
+#include "bus/msx/slot/music.h"
+#include "bus/msx/slot/panasonic08r.h"
+#include "bus/msx/slot/ram_mm.h"
+#include "bus/msx/slot/rom.h"
+#include "machine/i8251.h"
+#include "machine/pit8253.h"
+#include "softlist_dev.h"
+
+#include "msx_turbor.lh"
+
+using namespace msx_keyboard;
+
+
+/***************************************************************************
+
+ MSX Turbo-R machine drivers
+
+The R800 and Z80 see exactly the same memory layouts, only one of the two CPUs
+is active at any one time. The S1990 controls which CPU is active and can inject
+wait states depending on which cpu is active.
+
+TODO:
+- sfg extensions may not work due to irq callbacks being registered with the Z80 only.
+- v9958 commands seem to execute too fast. Several software items hang because of this.
+- verify midi interface operation on fsa1gt.
+- Implement DAC/PCM related hardware/filter and hook it up to the S1990.
+- Microphone input.
+
+***************************************************************************/
+
+namespace {
+
+class msxtr_state : public msx2p_base_state
+{
+public:
+ msxtr_state(const machine_config &mconfig, device_type type, const char *tag)
+ : msx2p_base_state(mconfig, type, tag, 21.477272_MHz_XTAL, 6)
+ , m_r800(*this, "r800")
+ , m_s1990(*this, "s1990")
+ , m_pause_switch(*this, "PAUSE")
+ , m_firmware_switch(*this, "FIRMWARE")
+ , m_pause_led(*this, "pause_led")
+ , m_r800_led(*this, "r800_led")
+ , m_pcmdac(*this, "pcmdac")
+ {
+ }
+
+ void fsa1st(machine_config &config);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ void turbor(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout);
+ void turbor_add_softlists(machine_config &config);
+ void s1990_mem_map(address_map &map);
+ void s1990_io_map(address_map &map);
+ void cpu_mem_map(address_map &map);
+ void cpu_io_map(address_map &map);
+
+ virtual void setup_slot_spaces(msx_internal_slot_interface &device) override;
+ virtual address_space& get_io_space() override;
+
+ void pause_led_w(int state);
+ void r800_led_w(int state);
+ void pcm_dac_w(u8 data);
+ void pcm_sample_hold_w(int state);
+ void pcm_select_w(int state);
+ void pcm_filter_w(int state);
+ void muting_w(int state);
+ int pcm_comp_r();
+
+ required_device m_r800;
+ required_device m_s1990;
+ required_ioport m_pause_switch;
+ required_ioport m_firmware_switch;
+ output_finder<> m_pause_led;
+ output_finder<> m_r800_led;
+ required_device m_pcmdac;
+ u8 m_pcm_last_sample;
+ u8 m_pcm_held_sample;
+ u8 m_pcm_sample_hold;
+};
+
+class fsa1gt_state : public msxtr_state
+{
+public:
+ fsa1gt_state(const machine_config &mconfig, device_type type, const char *tag)
+ : msxtr_state(mconfig, type, tag)
+ , m_i8251(*this, "i8251")
+ , m_i8254(*this, "i8254")
+ , m_midiin(*this, "midiin_port")
+ , m_midiout(*this, "midiout_port")
+ , m_dtr(false)
+ , m_rts(false)
+ , m_rxrdy(false)
+ , m_timer2_ff(false)
+ {
+ }
+
+ void fsa1gt(machine_config &config);
+
+private:
+ required_device m_i8251;
+ required_device m_i8254;
+ required_device m_midiin;
+ required_device m_midiout;
+ bool m_dtr;
+ bool m_rts;
+ bool m_rxrdy;
+ bool m_timer2_ff;
+
+ void s1990_io_map(address_map &map);
+ void dtr_w(int state);
+ void rts_w(int state);
+ void rxrdy_w(int state);
+ void timer0_w(int state);
+ void timer2_w(int state);
+ void update_midi_int_state();
+ void clear_timer2_ff(u8 data);
+};
+
+
+address_space& msxtr_state::get_io_space()
+{
+ return m_s1990->space(AS_IO);
+}
+
+void msxtr_state::setup_slot_spaces(msx_internal_slot_interface &device)
+{
+ device.set_memory_space(m_s1990, AS_PROGRAM);
+ device.set_io_space(m_s1990, AS_IO);
+ // TODO: This is used for signalling irq vectors. But that should go to the currently active cpu not just the z80.
+ device.set_maincpu(m_maincpu);
+}
+
+void msxtr_state::machine_start()
+{
+ msx2p_base_state::machine_start();
+ m_pause_led.resolve();
+ m_r800_led.resolve();
+
+ save_item(NAME(m_pcm_last_sample));
+ save_item(NAME(m_pcm_held_sample));
+ save_item(NAME(m_pcm_sample_hold));
+}
+
+void msxtr_state::machine_reset()
+{
+ msx2p_base_state::machine_reset();
+ m_pause_led = 0;
+ m_r800_led = 0;
+}
+
+void msxtr_state::s1990_mem_map(address_map &map)
+{
+ memory_map(map);
+}
+
+void msxtr_state::s1990_io_map(address_map &map)
+{
+ msx2plus_io_map(map);
+ map(0xa4, 0xa4).rw(m_s1990, FUNC(msx_s1990_device::pmcnt), FUNC(msx_s1990_device::pmdac));
+ map(0xa5, 0xa5).rw(m_s1990, FUNC(msx_s1990_device::pmstat), FUNC(msx_s1990_device::pmcntl));
+ map(0xa7, 0xa7).portr(m_pause_switch.finder_tag());
+ map(0xa7, 0xa7).w(m_s1990, FUNC(msx_s1990_device::pause_w));
+ map(0xe4, 0xe4).w(m_s1990, FUNC(msx_s1990_device::reg_index_write));
+ map(0xe5, 0xe5).rw(m_s1990, FUNC(msx_s1990_device::regs_read), FUNC(msx_s1990_device::regs_write));
+ map(0xe6, 0xe6).w(m_s1990, FUNC(msx_s1990_device::counter_write));
+ map(0xe6, 0xe7).r(m_s1990, FUNC(msx_s1990_device::counter_read));
+}
+
+void msxtr_state::cpu_mem_map(address_map &map)
+{
+ map(0x0000, 0xffff).rw(m_s1990, FUNC(msx_s1990_device::mem_read), FUNC(msx_s1990_device::mem_write));
+}
+
+void msxtr_state::cpu_io_map(address_map &map)
+{
+ map.global_mask(0xff);
+ map(0x00, 0xff).rw(m_s1990, FUNC(msx_s1990_device::io_read), FUNC(msx_s1990_device::io_write));
+}
+
+void msxtr_state::pause_led_w(int state)
+{
+ m_pause_led = state;
+}
+
+void msxtr_state::r800_led_w(int state)
+{
+ m_r800_led = state;
+}
+
+void msxtr_state::turbor(ay8910_type ay8910_type, machine_config &config, const internal_layout &layout)
+{
+ msx2plus_base(ay8910_type, config, layout);
+
+ m_maincpu->set_addrmap(AS_PROGRAM, &msxtr_state::cpu_mem_map);
+ m_maincpu->set_addrmap(AS_IO, &msxtr_state::cpu_io_map);
+
+ R800(config, m_r800, 28.636363_MHz_XTAL);
+ m_r800->set_addrmap(AS_PROGRAM, &msxtr_state::cpu_mem_map);
+ m_r800->set_addrmap(AS_IO, &msxtr_state::cpu_io_map);
+
+ MSX_S1990(config, m_s1990, 28.636363_MHz_XTAL / 4);
+ m_s1990->set_addrmap(AS_PROGRAM, &msxtr_state::s1990_mem_map);
+ m_s1990->set_addrmap(AS_IO, &msxtr_state::s1990_io_map);
+ m_s1990->set_z80_tag(m_maincpu);
+ m_s1990->set_r800_tag(m_r800);
+ m_s1990->pause_led_callback().set(FUNC(msxtr_state::pause_led_w));
+ m_s1990->r800_led_callback().set(FUNC(msxtr_state::r800_led_w));
+ m_s1990->firmware_switch_callback().set_ioport(m_firmware_switch);
+ m_s1990->dac_write_callback().set(FUNC(msxtr_state::pcm_dac_w));
+ m_s1990->sample_hold_callback().set(FUNC(msxtr_state::pcm_sample_hold_w));
+ m_s1990->select_callback().set(FUNC(msxtr_state::pcm_select_w));
+ m_s1990->filter_callback().set(FUNC(msxtr_state::pcm_filter_w));
+ m_s1990->muting_callback().set(FUNC(msxtr_state::muting_w));
+ m_s1990->comp_callback().set(FUNC(msxtr_state::pcm_comp_r));
+
+ // TODO: Do IRQ requests go to both cpus (only to be ignored by the inactive cpu) or only to the currently active cpu?
+ m_mainirq->output_handler().append_inputline(m_r800, INPUT_LINE_IRQ0);
+
+ DAC_8BIT_R2R(config, m_pcmdac).add_route(ALL_OUTPUTS, m_speaker, 1.0); // Unknown DAC type
+
+ // Software lists
+ turbor_add_softlists(config);
+}
+
+void msxtr_state::pcm_dac_w(u8 data)
+{
+ m_pcmdac->write(data);
+ m_pcm_last_sample = data;
+}
+
+void msxtr_state::pcm_sample_hold_w(int state)
+{
+ if (!m_pcm_sample_hold && state)
+ m_pcm_held_sample = m_pcm_last_sample;
+ m_pcm_sample_hold = state;
+}
+
+void msxtr_state::pcm_select_w(int state)
+{
+ // TODO pcm select
+}
+
+void msxtr_state::pcm_filter_w(int state)
+{
+ // TODO enable pcm filter
+}
+
+int msxtr_state::pcm_comp_r()
+{
+ // TODO pcm output compare
+ return 0;
+}
+
+void msxtr_state::muting_w(int state)
+{
+ // TODO mute all sound
+}
+
+void msxtr_state::turbor_add_softlists(machine_config &config)
+{
+ if (m_hw_def.has_cartslot())
+ {
+ SOFTWARE_LIST(config, "cart_list").set_original("msxr_cart");
+ SOFTWARE_LIST(config, "msx2p_cart_list").set_compatible("msx2p_cart");
+ SOFTWARE_LIST(config, "msx2_cart_list").set_compatible("msx2_cart");
+ SOFTWARE_LIST(config, "msx1_cart_list").set_compatible("msx1_cart");
+ }
+
+ if (m_hw_def.has_fdc())
+ {
+ SOFTWARE_LIST(config, "flop_list").set_original("msxr_flop");
+ SOFTWARE_LIST(config, "msx2p_flop_list").set_compatible("msx2p_flop");
+ SOFTWARE_LIST(config, "msx2_flop_list").set_compatible("msx2_flop");
+ SOFTWARE_LIST(config, "msx1_flop_list").set_compatible("msx1_flop");
+ }
+}
+
+/* MSX Turbo-R - Panasonic FS-A1GT */
+
+ROM_START(fsa1gt)
+ ROM_REGION(0x400000, "firmware", 0)
+ // This should be 2 1MB roms at locations IC20 and IC23.
+ ROM_LOAD("a1gtfirm.rom", 0, 0x400000, CRC(feefeadc) SHA1(e779c338eb91a7dea3ff75f3fde76b8af22c4a3a) BAD_DUMP)
+
+ ROM_REGION(0x40000, "kanji", 0)
+ ROM_LOAD("a1gtkfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
+ROM_END
+
+void fsa1gt_state::s1990_io_map(address_map &map)
+{
+ msxtr_state::s1990_io_map(map);
+ map(0xe8, 0xe9).rw(m_i8251, FUNC(i8251_device::read), FUNC(i8251_device::write));
+ map(0xea, 0xea).w(FUNC(fsa1gt_state::clear_timer2_ff));
+ map(0xec, 0xef).rw(m_i8254, FUNC(pit8254_device::read), FUNC(pit8254_device::write));
+}
+
+void fsa1gt_state::fsa1gt(machine_config &config)
+{
+ // AY8910 (in T9769)
+ // FDC: tc8566af, 1 3.5" DSDD drive
+ // 2 Cartridge slots
+ // T9769C + S1990
+ // FM built-in
+ // Microphone
+ // MIDI-in
+ // MIDI-out
+ // firmware switch
+ // pause button
+ // ren-sha turbo slider
+
+ add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 0, 2, "firmware", 0x050000);
+ add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 0, 2, 1, 1, "firmware", 0x07c000).set_ym2413_tag(m_ym2413);
+ add_internal_slot(config, MSX_SLOT_ROM, "opt", 0, 3, 1, 1, "firmware", 0x048000);
+ add_cartridge_slot<1>(config, 1);
+ add_cartridge_slot<2>(config, 2);
+ add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x80000); // 512KB Mapper RAM
+ add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "firmware", 0x070000);
+ add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "firmware", 0x074000);
+ add_internal_disk(config, MSX_SLOT_DISK4_TC8566, "dos", 3, 2, 1, 3, "firmware", 0x060000);
+ add_internal_slot(config, MSX_SLOT_PANASONIC08R, "firmware", 3, 3, 0, 4, "firmware").set_sram_size(0x8000).set_mm_tag("ram_mm");
+
+ MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);
+
+ msx_ym2413(config);
+
+ m_hw_def.has_cassette(false);
+ turbor(SND_AY8910, config, layout_msx_turbor);
+ m_s1990->set_addrmap(AS_IO, &fsa1gt_state::s1990_io_map);
+
+ I8251(config, m_i8251, 16_MHz_XTAL / 4); // Not sure about this
+ m_i8251->txd_handler().set(m_midiout, FUNC(midi_port_device::write_txd));
+ m_i8251->dtr_handler().set(*this, FUNC(fsa1gt_state::dtr_w));
+ m_i8251->rts_handler().set(*this, FUNC(fsa1gt_state::rts_w));
+ m_i8251->rxrdy_handler().set(*this, FUNC(fsa1gt_state::rxrdy_w));
+
+ PIT8254(config, m_i8254);
+ m_i8254->set_clk<0>(16_MHz_XTAL / 4);
+ m_i8254->set_clk<2>(16_MHz_XTAL/ 4);
+ m_i8254->out_handler<0>().set(*this, FUNC(fsa1gt_state::timer0_w));
+ m_i8254->out_handler<2>().set(*this, FUNC(fsa1gt_state::timer2_w));
+
+ MIDI_PORT(config, m_midiin, midiin_slot, "midiin").rxd_handler().set(m_i8251, FUNC(i8251_device::rx_w));
+ MIDI_PORT(config, m_midiout, midiout_slot, "midiout");
+}
+
+void fsa1gt_state::rts_w(int state)
+{
+ m_rts = state;
+ update_midi_int_state();
+}
+
+void fsa1gt_state::rxrdy_w(int state)
+{
+ m_rxrdy = state;
+ update_midi_int_state();
+}
+
+void fsa1gt_state::dtr_w(int state)
+{
+ m_dtr = state;
+ update_midi_int_state();
+}
+
+void fsa1gt_state::timer0_w(int state)
+{
+ m_i8251->tx_clock_w(state);
+ m_i8251->rx_clock_w(state);
+}
+
+void fsa1gt_state::timer2_w(int state)
+{
+ m_timer2_ff = m_timer2_ff | state;
+ m_i8254->write_clk1(state);
+ update_midi_int_state();
+}
+
+void fsa1gt_state::clear_timer2_ff(u8 data)
+{
+ m_timer2_ff = false;
+ update_midi_int_state();
+}
+
+void fsa1gt_state::update_midi_int_state()
+{
+ m_i8251->write_dsr(m_timer2_ff && m_dtr);
+ m_mainirq->in_w<3>(!((m_timer2_ff && m_dtr) || (m_rts && m_rxrdy)));
+}
+
+/* MSX Turbo-R - Panasonic FS-A1ST */
+
+ROM_START(fsa1st)
+ ROM_REGION(0x400000, "firmware", 0)
+ // This should be either 3 512KB roms or 1 1MB and 1 512KB rom (at IC12 and IC18?). Both variants are known to exist.
+ ROM_LOAD("a1stfirm.rom", 0, 0x400000, CRC(139ac99c) SHA1(c212b11fda13f83dafed688c54d098e7e47ab225) BAD_DUMP)
+
+ ROM_REGION(0x40000, "kanji", 0)
+ ROM_LOAD("a1stkfn.rom", 0, 0x40000, CRC(1f6406fb) SHA1(5aff2d9b6efc723bc395b0f96f0adfa83cc54a49))
+ROM_END
+
+void msxtr_state::fsa1st(machine_config &config)
+{
+ // AY8910 (in T9769)
+ // FDC: tc8566af, 1 3.5" DSDD drive
+ // T9769C + S1990
+ // 2 Cartridge slots
+ // FM built-in
+ // microphone
+ // firmware switch
+ // pause button
+ // ren-sha turbo slider
+
+ add_internal_slot(config, MSX_SLOT_ROM, "bios", 0, 0, 0, 2, "firmware", 0x050000);
+ add_internal_slot(config, MSX_SLOT_MUSIC, "msxmusic", 0, 2, 1, 1, "firmware", 0x07c000).set_ym2413_tag(m_ym2413);
+ add_internal_slot(config, MSX_SLOT_ROM, "opt", 0, 3, 1, 1, "firmware", 0x048000);
+ add_cartridge_slot<1>(config, 1);
+ add_cartridge_slot<2>(config, 2);
+ add_internal_slot(config, MSX_SLOT_RAM_MM, "ram_mm", 3, 0, 0, 4).set_total_size(0x40000); // 256KB Mapper RAM
+ add_internal_slot(config, MSX_SLOT_ROM, "ext", 3, 1, 0, 1, "firmware", 0x070000);
+ add_internal_slot(config, MSX_SLOT_ROM, "kdr", 3, 1, 1, 2, "firmware", 0x074000);
+ add_internal_disk(config, MSX_SLOT_DISK4_TC8566, "dos", 3, 2, 1, 3, "firmware", 0x060000);
+ add_internal_slot(config, MSX_SLOT_PANASONIC08R, "firmware", 3, 3, 0, 4, "firmware").set_sram_size(0x4000).set_mm_tag("ram_mm");
+
+ MSX_SYSTEMFLAGS(config, "sysflags", m_maincpu, 0x00);
+
+ msx_ym2413(config);
+
+ m_hw_def.has_cassette(false);
+ turbor(SND_AY8910, config, layout_msx_turbor);
+}
+
+INPUT_PORTS_START(msxtr)
+ PORT_INCLUDE(msx2jp)
+
+ PORT_START("PAUSE")
+ PORT_CONFNAME(0x01, 0x00, "Pause") PORT_CHANGED_MEMBER("s1990", msx_s1990_device, pause_callback, 0)
+ PORT_CONFSETTING(0x00, "off")
+ PORT_CONFSETTING(0x01, "on")
+
+ PORT_START("FIRMWARE")
+ PORT_CONFNAME(0x01, 0x01, "Firmware")
+ PORT_CONFSETTING(0x01, "enabled")
+ PORT_CONFSETTING(0x00, "disabled")
+INPUT_PORTS_END
+
+} // anonymous namespace
+
+/* MSX Turbo-R */
+COMP(1991, fsa1gt, 0, 0, fsa1gt, msxtr, fsa1gt_state, empty_init, "Panasonic", "FS-A1GT (MSX Turbo-R, Japan)", MACHINE_NOT_WORKING)
+COMP(1991, fsa1st, 0, 0, fsa1st, msxtr, msxtr_state, empty_init, "Panasonic", "FS-A1ST (MSX Turbo-R, Japan)", MACHINE_NOT_WORKING)