summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author holub <andrei.holub@gmail.com>2021-12-24 14:58:14 -0500
committer GitHub <noreply@github.com>2021-12-25 06:58:14 +1100
commit1e32a43b50742c418fc169535f6add2ba725875a (patch)
tree0bc19545457ae630c96895f0b478d2ca515181af
parent6101c55c968b6f1e25352ce4d37de944643ee300 (diff)
Added preliminary TS-Configuration for ZX Evolution driver. (#8989)
New machines marked as not working ------------------------- NedoPC, TS-Labs ZX Evolution TS-Configuration
-rw-r--r--scripts/src/machine.lua22
-rw-r--r--scripts/target/mame/mess.lua5
-rw-r--r--src/devices/machine/glukrs.cpp60
-rw-r--r--src/devices/machine/glukrs.h35
-rw-r--r--src/devices/machine/tsconfdma.cpp195
-rw-r--r--src/devices/machine/tsconfdma.h58
-rw-r--r--src/mame/drivers/atm.cpp18
-rw-r--r--src/mame/drivers/pentagon.cpp18
-rw-r--r--src/mame/drivers/scorpion.cpp20
-rw-r--r--src/mame/drivers/spec128.cpp8
-rw-r--r--src/mame/drivers/spectrum.cpp6
-rw-r--r--src/mame/drivers/tsconf.cpp296
-rw-r--r--src/mame/includes/spectrum.h5
-rw-r--r--src/mame/includes/tsconf.h191
-rw-r--r--src/mame/machine/tsconf.cpp653
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/video/spectrum.cpp6
17 files changed, 1561 insertions, 38 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index e0c955ce9aa..8900f14a108 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -4881,3 +4881,25 @@ if (MACHINES["NS32082"]~=null) then
MAME_DIR .. "src/devices/machine/ns32082.h",
}
end
+
+---------------------------------------------------
+--
+--@src/devices/machine/tsconfdma.h,MACHINES["TSCONF_DMA"] = true
+---------------------------------------------------
+if (MACHINES["TSCONF_DMA"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/tsconfdma.cpp",
+ MAME_DIR .. "src/devices/machine/tsconfdma.h",
+ }
+end
+
+---------------------------------------------------
+--
+--@src/devices/machine/glukrs.h,MACHINES["GLUKRS"] = true
+---------------------------------------------------
+if (MACHINES["GLUKRS"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/glukrs.cpp",
+ MAME_DIR .. "src/devices/machine/glukrs.h",
+ }
+end
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 2b15464d324..66e11808b98 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -534,6 +534,7 @@ MACHINES["ER2055"] = true
MACHINES["EXORTERM"] = true
MACHINES["F3853"] = true
MACHINES["F4702"] = true
+MACHINES["GLUKRS"] = true
MACHINES["GT913"] = true
MACHINES["HD63450"] = true
MACHINES["HD64610"] = true
@@ -716,6 +717,7 @@ MACHINES["TMS9901"] = true
MACHINES["TMS9902"] = true
MACHINES["TMS9914"] = true
MACHINES["TPI6525"] = true
+MACHINES["TSCONF_DMA"] = true
MACHINES["TTL7400"] = true
MACHINES["TTL7404"] = true
--MACHINES["TSB12LV01A"] = true
@@ -3800,6 +3802,9 @@ files {
MAME_DIR .. "src/mame/video/zx8301.h",
MAME_DIR .. "src/mame/machine/zx8302.cpp",
MAME_DIR .. "src/mame/machine/zx8302.h",
+ MAME_DIR .. "src/mame/drivers/tsconf.cpp",
+ MAME_DIR .. "src/mame/includes/tsconf.h",
+ MAME_DIR .. "src/mame/machine/tsconf.cpp",
}
createMESSProjects(_target, _subtarget, "siemens")
diff --git a/src/devices/machine/glukrs.cpp b/src/devices/machine/glukrs.cpp
new file mode 100644
index 00000000000..e5cbf3f1424
--- /dev/null
+++ b/src/devices/machine/glukrs.cpp
@@ -0,0 +1,60 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/***************************************************************************
+
+ Mr Gluk Reset Service
+
+ Refs:
+ https://zxart.ee/spa/software/prikladnoe-po/electronics/pzu/mr-gluk-reset-service-663/mr-gluk-reset-service-663/action:viewFile/id:250389/fileId:814961/
+
+****************************************************************************/
+
+#include "emu.h"
+#include "glukrs.h"
+
+glukrs_device::glukrs_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, GLUKRS, tag, owner, clock),
+ device_rtc_interface(mconfig, *this),
+ m_nvram(*this, "nvram") {}
+
+void glukrs_device::device_add_mconfig(machine_config &config)
+{
+ NVRAM(config, m_nvram, nvram_device::DEFAULT_ALL_1);
+}
+
+void glukrs_device::device_start()
+{
+ m_nvram->set_base(&m_cmos, sizeof(m_cmos));
+
+ m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(glukrs_device::timer_callback), this));
+ m_timer->adjust(attotime::from_hz(clock() / XTAL(32'768)), 0, attotime::from_hz(clock() / XTAL(32'768)));
+}
+
+u8 glukrs_device::read(offs_t address)
+{
+ return m_cmos[address];
+}
+
+void glukrs_device::write(offs_t address, u8 data)
+{
+ m_cmos[address] = data;
+}
+
+void glukrs_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
+{
+ m_cmos[0x00] = convert_to_bcd(second);
+ m_cmos[0x02] = convert_to_bcd(minute);
+ m_cmos[0x04] = convert_to_bcd(hour);
+ m_cmos[0x06] = convert_to_bcd(day_of_week);
+ m_cmos[0x07] = convert_to_bcd(day);
+ m_cmos[0x08] = convert_to_bcd(month);
+ m_cmos[0x09] = convert_to_bcd(year % 100);
+}
+
+TIMER_CALLBACK_MEMBER(glukrs_device::timer_callback)
+{
+ advance_seconds();
+}
+
+// device type definition
+DEFINE_DEVICE_TYPE(GLUKRS, glukrs_device, "glukrs", "Mr Gluk Reset Service")
diff --git a/src/devices/machine/glukrs.h b/src/devices/machine/glukrs.h
new file mode 100644
index 00000000000..fc088bc779e
--- /dev/null
+++ b/src/devices/machine/glukrs.h
@@ -0,0 +1,35 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+
+#ifndef MAME_MACHINE_GLUKRS_H
+#define MAME_MACHINE_GLUKRS_H
+
+#pragma once
+
+#include "machine/nvram.h"
+#include "dirtc.h"
+
+class glukrs_device : public device_t,
+ public device_rtc_interface
+{
+public:
+ glukrs_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 32'768);
+
+ u8 read(offs_t address);
+ void write(offs_t address, u8 data);
+
+ TIMER_CALLBACK_MEMBER(timer_callback);
+
+protected:
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override;
+
+private:
+ u8 m_cmos[0x100];
+ required_device<nvram_device> m_nvram;
+ emu_timer *m_timer;
+};
+
+DECLARE_DEVICE_TYPE(GLUKRS, glukrs_device)
+#endif // MAME_MACHINE_GLUKRS_H
diff --git a/src/devices/machine/tsconfdma.cpp b/src/devices/machine/tsconfdma.cpp
new file mode 100644
index 00000000000..5ba61e33cd3
--- /dev/null
+++ b/src/devices/machine/tsconfdma.cpp
@@ -0,0 +1,195 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/**********************************************************************
+
+ TS-Conf (ZX-Evolution) DMA Controller
+
+**********************************************************************/
+
+#include "emu.h"
+#include "tsconfdma.h"
+
+tsconfdma_device::tsconfdma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, TSCONF_DMA, tag, owner, clock),
+ m_in_mreq_cb(*this),
+ m_out_mreq_cb(*this),
+ m_in_mspi_cb(*this),
+ m_out_cram_cb(*this)
+{
+}
+
+void tsconfdma_device::device_start()
+{
+ m_in_mreq_cb.resolve_safe(0);
+ m_out_mreq_cb.resolve_safe();
+ m_in_mspi_cb.resolve_safe(0);
+ m_out_cram_cb.resolve_safe();
+
+ save_item(NAME(m_ready));
+ save_item(NAME(m_address_s));
+ save_item(NAME(m_address_d));
+ save_item(NAME(m_block_len));
+ save_item(NAME(m_block_num));
+ save_item(NAME(m_align_s));
+ save_item(NAME(m_align_d));
+ save_item(NAME(m_align));
+}
+
+void tsconfdma_device::device_reset()
+{
+ m_block_num = 0;
+ m_ready = ASSERT_LINE;
+}
+
+int tsconfdma_device::is_ready()
+{
+ return m_ready;
+}
+
+void tsconfdma_device::set_saddr_l(u8 addr_l)
+{
+ m_address_s = (m_address_s & 0xffffff00) | (addr_l & 0xfe);
+}
+
+void tsconfdma_device::set_saddr_h(u8 addr_h)
+{
+ m_address_s = (m_address_s & 0xffffc0ff) | ((addr_h & 0x3f) << 8);
+}
+
+void tsconfdma_device::set_saddr_x(u8 addr_x)
+{
+ m_address_s = (m_address_s & 0x0003fff) | (addr_x << 14);
+}
+
+void tsconfdma_device::set_daddr_l(u8 addr_l)
+{
+ m_address_d = (m_address_d & 0xffffff00) | (addr_l & 0xfe);
+}
+
+void tsconfdma_device::set_daddr_h(u8 addr_h)
+{
+ m_address_d = (m_address_d & 0xffffc0ff) | ((addr_h & 0x3f) << 8);
+}
+
+void tsconfdma_device::set_daddr_x(u8 addr_x)
+{
+ m_address_d = (m_address_d & 0x0003fff) | (addr_x << 14);
+}
+
+void tsconfdma_device::set_block_len(u8 len)
+{
+ // 1..256
+ m_block_len = len;
+}
+
+void tsconfdma_device::set_block_num_l(u8 num_l)
+{
+ m_block_num = (m_block_num & 0xff00) | num_l;
+}
+
+void tsconfdma_device::set_block_num_h(u8 num_h)
+{
+ // 1..1025 * 2
+ m_block_num = (m_block_num & 0x00ff) | ((num_h & 0x03) << 8);
+}
+
+void tsconfdma_device::start_tx(u8 dev, bool s_align, bool d_align, bool align_opt)
+{
+ m_ready = CLEAR_LINE;
+ m_align = align_opt ? 512 : 256;
+
+ // TODO Transfers 2 byte/cycle at 7MHz
+ switch (dev)
+ {
+ case 0b0001: // Mem -> Mem
+ for (u16 block = 0; block <= m_block_num; block++)
+ {
+ auto s_addr = m_address_s;
+ auto d_addr = m_address_d;
+ for (u16 len = 0; len <= m_block_len; len++)
+ {
+ m_out_mreq_cb(d_addr, m_in_mreq_cb(s_addr));
+ s_addr += 2;
+ d_addr += 2;
+ }
+ m_address_s = s_align ? (m_address_s + m_align) : s_addr;
+ m_address_d = d_align ? (m_address_d + m_align) : d_addr;
+ }
+ break;
+
+ case 0b0010: // SPI -> Mem
+ for (u16 block = 0; block <= m_block_num; block++)
+ {
+ auto d_addr = m_address_d;
+ for (u16 len = 0; len <= m_block_len; len++)
+ {
+ m_out_mreq_cb(d_addr, m_in_mspi_cb());
+ d_addr += 2;
+ }
+ m_address_d = d_align ? (m_address_d + m_align) : d_addr;
+ }
+ break;
+
+ case 0b0100: // Fill
+ for (u16 block = 0; block <= m_block_num; block++)
+ {
+ u16 data = m_in_mreq_cb(m_address_s);
+ auto d_addr = m_address_d;
+ for (u16 len = 0; len <= m_block_len; len++)
+ {
+ m_out_mreq_cb(d_addr, data);
+ d_addr += 2;
+ }
+ m_address_d = d_align ? (m_address_d + m_align) : d_addr;
+ }
+ break;
+
+ case 0b1001: // Blt -> Mem
+ for (u16 block = 0; block <= m_block_num; block++)
+ {
+ auto s_addr = m_address_s;
+ auto d_addr = m_address_d;
+ for (u16 len = 0; len <= m_block_len; len++)
+ {
+ u16 d_val = m_in_mreq_cb(d_addr);
+ if (d_val != 0)
+ {
+ m_out_mreq_cb(d_addr, m_in_mreq_cb(s_addr));
+ }
+ s_addr += 2;
+ d_addr += 2;
+ }
+ m_address_s = s_align ? (m_address_s + m_align) : s_addr;
+ m_address_d = d_align ? (m_address_d + m_align) : d_addr;
+ }
+ break;
+
+ case 0b1100: // RAM -> CRAM
+ for (u16 block = 0; block <= m_block_num; block++)
+ {
+ auto s_addr = m_address_s;
+ auto d_addr = m_address_d;
+ for (u16 len = 0; len <= m_block_len; len++)
+ {
+ m_out_cram_cb(d_addr, m_in_mreq_cb(s_addr));
+ s_addr += 2;
+ d_addr += 2;
+ }
+ m_address_s = s_align ? (m_address_s + m_align) : s_addr;
+ m_address_d = d_align ? (m_address_d + m_align) : d_addr;
+ }
+ break;
+
+ //case 0b1101: // RAM -> SFILE
+ // break;
+
+ default:
+ logerror("'tsdma': TX %02X: %06X (%02X:%04X) -> %06X\n", dev, m_address_s, m_block_len, m_block_num, m_address_d);
+ break;
+ }
+
+ m_ready = ASSERT_LINE;
+}
+
+// device type definition
+DEFINE_DEVICE_TYPE(TSCONF_DMA, tsconfdma_device, "tsconfdma", "TS-Conf DMA Controller")
diff --git a/src/devices/machine/tsconfdma.h b/src/devices/machine/tsconfdma.h
new file mode 100644
index 00000000000..dc8c246b83a
--- /dev/null
+++ b/src/devices/machine/tsconfdma.h
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/***************************************************************************
+
+ TS-Conf (ZX-Evolution) DMA
+
+***************************************************************************/
+
+#ifndef MAME_MACHINE_TSCONF_DMA_H
+#define MAME_MACHINE_TSCONF_DMA_H
+
+#pragma once
+
+class tsconfdma_device : public device_t
+{
+public:
+ tsconfdma_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ auto in_mreq_callback() { return m_in_mreq_cb.bind(); }
+ auto out_mreq_callback() { return m_out_mreq_cb.bind(); }
+ auto in_spireq_callback() { return m_in_mspi_cb.bind(); }
+ auto out_cram_callback() { return m_out_cram_cb.bind(); }
+
+ int is_ready();
+
+ void set_saddr_l(uint8_t addr_l);
+ void set_saddr_h(uint8_t addr_h);
+ void set_saddr_x(uint8_t addr_x);
+ void set_daddr_l(uint8_t addr_l);
+ void set_daddr_h(uint8_t addr_h);
+ void set_daddr_x(uint8_t addr_x);
+ void set_block_len(uint8_t len);
+ void set_block_num_l(uint8_t num_l);
+ void set_block_num_h(uint8_t num_h);
+ void start_tx(uint8_t dev, bool s_align, bool d_align, bool blitting_opt);
+
+private:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ devcb_read16 m_in_mreq_cb;
+ devcb_write16 m_out_mreq_cb;
+ devcb_read16 m_in_mspi_cb;
+ devcb_write16 m_out_cram_cb;
+
+ u8 m_ready;
+
+ offs_t m_address_s;
+ offs_t m_address_d;
+ u8 m_block_len;
+ u16 m_block_num;
+ bool m_align_s;
+ bool m_align_d;
+ u16 m_align;
+};
+
+DECLARE_DEVICE_TYPE(TSCONF_DMA, tsconfdma_device)
+#endif // MAME_MACHINE_TSCONF_DMA_H
diff --git a/src/mame/drivers/atm.cpp b/src/mame/drivers/atm.cpp
index 7786ccd339e..1bf5e85ffb0 100644
--- a/src/mame/drivers/atm.cpp
+++ b/src/mame/drivers/atm.cpp
@@ -127,15 +127,15 @@ void atm_state::atm_mem(address_map &map)
void atm_state::atm_io(address_map &map)
{
map.unmap_value_high();
- map(0x001f, 0x001f).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w)).mirror(0xff00);
- map(0x003f, 0x003f).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w)).mirror(0xff00);
- map(0x005f, 0x005f).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w)).mirror(0xff00);
- map(0x007f, 0x007f).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w)).mirror(0xff00);
- map(0x00fe, 0x00fe).rw(FUNC(atm_state::spectrum_port_fe_r), FUNC(atm_state::spectrum_port_fe_w)).select(0xff00);
- map(0x00ff, 0x00ff).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w)).mirror(0xff00);
- map(0x4000, 0x4000).w(FUNC(atm_state::atm_port_7ffd_w)).mirror(0x3ffd);
- map(0x8000, 0x8000).w("ay8912", FUNC(ay8910_device::data_w)).mirror(0x3ffd);
- map(0xc000, 0xc000).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w)).mirror(0x3ffd);
+ map(0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
+ map(0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
+ map(0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
+ map(0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
+ map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(atm_state::spectrum_port_fe_r), FUNC(atm_state::spectrum_port_fe_w));
+ map(0x00ff, 0x00ff).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w));
+ map(0x4000, 0x4000).mirror(0x3ffd).w(FUNC(atm_state::atm_port_7ffd_w));
+ map(0x8000, 0x8000).mirror(0x3ffd).w("ay8912", FUNC(ay8910_device::data_w));
+ map(0xc000, 0xc000).mirror(0x3ffd).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
}
void atm_state::atm_switch(address_map &map)
diff --git a/src/mame/drivers/pentagon.cpp b/src/mame/drivers/pentagon.cpp
index cef525a20ee..61428d4e778 100644
--- a/src/mame/drivers/pentagon.cpp
+++ b/src/mame/drivers/pentagon.cpp
@@ -205,15 +205,15 @@ void pentagon_state::pentagon_mem(address_map &map)
void pentagon_state::pentagon_io(address_map &map)
{
map.unmap_value_high();
- map(0x0000, 0x0000).w(FUNC(pentagon_state::pentagon_port_7ffd_w)).mirror(0x7ffd); // (A15 | A1) == 0
- map(0x001f, 0x001f).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w)).mirror(0xff00);
- map(0x003f, 0x003f).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w)).mirror(0xff00);
- map(0x005f, 0x005f).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w)).mirror(0xff00);
- map(0x007f, 0x007f).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w)).mirror(0xff00);
- map(0x00fe, 0x00fe).rw(FUNC(pentagon_state::spectrum_port_fe_r), FUNC(pentagon_state::spectrum_port_fe_w)).select(0xff00);
- map(0x00ff, 0x00ff).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w)).mirror(0xff00);
- map(0x8000, 0x8000).w("ay8912", FUNC(ay8910_device::data_w)).mirror(0x3ffd);
- map(0xc000, 0xc000).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w)).mirror(0x3ffd);
+ map(0x0000, 0x0000).mirror(0x7ffd).w(FUNC(pentagon_state::pentagon_port_7ffd_w)); // (A15 | A1) == 0
+ map(0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
+ map(0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
+ map(0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
+ map(0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
+ map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(pentagon_state::spectrum_port_fe_r), FUNC(pentagon_state::spectrum_port_fe_w));
+ map(0x00ff, 0x00ff).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w));
+ map(0x8000, 0x8000).mirror(0x3ffd).w("ay8912", FUNC(ay8910_device::data_w));
+ map(0xc000, 0xc000).mirror(0x3ffd).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
}
void pentagon_state::pentagon_switch(address_map &map)
diff --git a/src/mame/drivers/scorpion.cpp b/src/mame/drivers/scorpion.cpp
index e94e03609fc..95174090351 100644
--- a/src/mame/drivers/scorpion.cpp
+++ b/src/mame/drivers/scorpion.cpp
@@ -193,16 +193,16 @@ void scorpion_state::scorpion_mem(address_map &map)
void scorpion_state::scorpion_io(address_map &map)
{
map.unmap_value_high();
- map(0x001f, 0x001f).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w)).mirror(0xff00);
- map(0x003f, 0x003f).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w)).mirror(0xff00);
- map(0x005f, 0x005f).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w)).mirror(0xff00);
- map(0x007f, 0x007f).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w)).mirror(0xff00);
- map(0x00fe, 0x00fe).rw(FUNC(scorpion_state::spectrum_port_fe_r), FUNC(scorpion_state::spectrum_port_fe_w)).select(0xff00);
- map(0x00ff, 0x00ff).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w)).mirror(0xff00);
- map(0x4021, 0x4021).w(FUNC(scorpion_state::scorpion_port_7ffd_w)).mirror(0x3fdc);
- map(0x8021, 0x8021).w("ay8912", FUNC(ay8910_device::data_w)).mirror(0x3fdc);
- map(0xc021, 0xc021).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w)).mirror(0x3fdc);
- map(0x0021, 0x0021).w(FUNC(scorpion_state::scorpion_port_1ffd_w)).mirror(0x3fdc);
+ map(0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
+ map(0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
+ map(0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
+ map(0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
+ map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(scorpion_state::spectrum_port_fe_r), FUNC(scorpion_state::spectrum_port_fe_w));
+ map(0x00ff, 0x00ff).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w));
+ map(0x4021, 0x4021).mirror(0x3fdc).w(FUNC(scorpion_state::scorpion_port_7ffd_w));
+ map(0x8021, 0x8021).mirror(0x3fdc).w("ay8912", FUNC(ay8910_device::data_w));
+ map(0xc021, 0xc021).mirror(0x3fdc).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
+ map(0x0021, 0x0021).mirror(0x3fdc).w(FUNC(scorpion_state::scorpion_port_1ffd_w));
}
void scorpion_state::scorpion_switch(address_map &map)
diff --git a/src/mame/drivers/spec128.cpp b/src/mame/drivers/spec128.cpp
index c9fb9801b32..8a12f04b145 100644
--- a/src/mame/drivers/spec128.cpp
+++ b/src/mame/drivers/spec128.cpp
@@ -249,10 +249,10 @@ uint8_t spectrum_128_state::spectrum_128_ula_r()
void spectrum_128_state::spectrum_128_io(address_map &map)
{
map(0x0000, 0xffff).rw(m_exp, FUNC(spectrum_expansion_slot_device::iorq_r), FUNC(spectrum_expansion_slot_device::iorq_w));
- map(0x0000, 0x0000).rw(FUNC(spectrum_128_state::spectrum_port_fe_r), FUNC(spectrum_128_state::spectrum_port_fe_w)).select(0xfffe);
- map(0x0001, 0x0001).w(FUNC(spectrum_128_state::spectrum_128_port_7ffd_w)).select(0x7ffc); // (A15 | A1) == 0, note: reading from this port does write to it by value from data bus
- map(0x8000, 0x8000).w("ay8912", FUNC(ay8910_device::data_w)).mirror(0x3ffd);
- map(0xc000, 0xc000).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w)).mirror(0x3ffd);
+ map(0x0000, 0x0000).select(0xfffe).rw(FUNC(spectrum_128_state::spectrum_port_fe_r), FUNC(spectrum_128_state::spectrum_port_fe_w));
+ map(0x0001, 0x0001).select(0x7ffc).w(FUNC(spectrum_128_state::spectrum_128_port_7ffd_w)); // (A15 | A1) == 0, note: reading from this port does write to it by value from data bus
+ map(0x8000, 0x8000).mirror(0x3ffd).w("ay8912", FUNC(ay8910_device::data_w));
+ map(0xc000, 0xc000).mirror(0x3ffd).rw("ay8912", FUNC(ay8910_device::data_r), FUNC(ay8910_device::address_w));
map(0x0001, 0x0001).r(FUNC(spectrum_128_state::spectrum_128_ula_r)); // .mirror(0xfffe);
}
diff --git a/src/mame/drivers/spectrum.cpp b/src/mame/drivers/spectrum.cpp
index 7f945db26c1..621e1ddb9c2 100644
--- a/src/mame/drivers/spectrum.cpp
+++ b/src/mame/drivers/spectrum.cpp
@@ -509,14 +509,14 @@ The function decodes the ports appropriately */
void spectrum_state::spectrum_io(address_map &map)
{
map(0x0000, 0xffff).w(m_exp, FUNC(spectrum_expansion_slot_device::iorq_w));
- map(0x00, 0x00).rw(FUNC(spectrum_state::spectrum_port_fe_r), FUNC(spectrum_state::spectrum_port_fe_w)).select(0xfffe);
- map(0x01, 0x01).r(FUNC(spectrum_state::spectrum_port_ula_r)).select(0xfffe);
+ map(0x00, 0x00).select(0xfffe).rw(FUNC(spectrum_state::spectrum_port_fe_r), FUNC(spectrum_state::spectrum_port_fe_w));
+ map(0x01, 0x01).select(0xfffe).r(FUNC(spectrum_state::spectrum_port_ula_r));
}
void spectrum_state::spectrum_clone_io(address_map &map)
{
map(0x0000, 0xffff).rw(m_exp, FUNC(spectrum_expansion_slot_device::iorq_r), FUNC(spectrum_expansion_slot_device::iorq_w));
- map(0x00, 0x00).rw(FUNC(spectrum_state::spectrum_port_fe_r), FUNC(spectrum_state::spectrum_port_fe_w)).select(0xfffe);
+ map(0x00, 0x00).select(0xfffe).rw(FUNC(spectrum_state::spectrum_port_fe_r), FUNC(spectrum_state::spectrum_port_fe_w));
map(0x01, 0x01).r(FUNC(spectrum_state::spectrum_clone_port_ula_r)); // .mirror(0xfffe);
}
diff --git a/src/mame/drivers/tsconf.cpp b/src/mame/drivers/tsconf.cpp
new file mode 100644
index 00000000000..b727b91301d
--- /dev/null
+++ b/src/mame/drivers/tsconf.cpp
@@ -0,0 +1,296 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/***************************************************************************
+
+ TS-Configuration (ZX Evolution) machine driver.
+ Implementation: Revision C / 5-bit VDAC
+
+ Hobby computer ZX Evolution is Spectrum-compatible with extensions.
+
+ Hardware (ZX Evolution):
+ - Z80 3.5 MHz (classic mode)/ 7 MHz (turbo mode without CPU wait circles)/ 14 MHz (mega turbo with CPU wait circles);
+ - 4 Mb RAM, 512Kb ROM;
+ - MiniITX board (172x170mm), 2 ZXBUS slots, power ATX or +5,+12V;
+ - Based on fpga (Altera EP1K50);
+ - Peripheral MCU ATMEGA128;
+ - PS/2 keyboard and mouse support;
+ - Floppy (WDC1793) Beta-disk compatible interface, IDE (one channel, up to 2 devices on master/slave mode), SD(HC) card, RS232;
+ - Sound: AY, Beeper, Covox (PWM);
+ - Real-time clock.
+
+ Features (TS-Configuration):
+ - Resolutions: 360x288, 320x240, 320x200, 256x192
+ - Hardware scrolled graphic planes
+ - 256 and 16 indexed colors per pixel
+ - Programmable color RAM with RGB555 color space and 256 cells
+ - Text mode with loadable font and hardware vertical scroll
+ - Up to 256 graphic screens
+
+ - Up to 85 sprites per line
+ - Sprites sized from 8x8 to 64x64 pixels
+ - Up to 3 sprite planes
+ - Up to 2 tile planes with 8x8 pixels tiles
+ - Up to 16 palettes for sprites per line
+ - Up to 4 palettes for tiles per line for each tile plane
+
+ - DRAM-to-Device, Device-to-DRAM and DRAM-to-DRAM DMA Controller
+
+ Refs:
+ ZxEvo: http://nedopc.com/zxevo/zxevo_eng.php
+ Principal scheme (rev. C) :: http://nedopc.com/zxevo/zxevo_sch_revc.pdf
+ Montage scheme (rev. C) :: http://nedopc.com/zxevo/zxevo_mon_revc.pdf
+ TsConf: https://github.com/tslabs/zx-evo/blob/master/pentevo/docs/TSconf/tsconf_en.md
+ https://github.com/tslabs/zx-evo/raw/master/pentevo/docs/TSconf/TSconf.xls
+ FAQ-RUS: https://forum.tslabs.info/viewtopic.php?f=35&t=157
+ ROM: https://github.com/tslabs/zx-evo/blob/master/pentevo/rom/bin/ts-bios.rom (validated on: 2021-12-14)
+
+ HowTo:
+ # Use ts-bios.rom above. You also need tr-dos roms which simpliest(?) to get from pentagon.
+ # Create SD image "wc.img"
+ # Copy WC files from archive https://github.com/tslabs/zx-evo/blob/master/pentevo/soft/WC/wc.zip
+ # Tech Demos (currently *.spg only): http://prods.tslabs.info/index.php?t=4
+ $ chdman createhd -i wc.img -o wc.chd -c none
+ $ mame tsconf -hard wc.chd
+ # BIOS Setup loads on fresh setup (return to BIOS: RShift+F3)
+ # Change "Reset To: BD boot.$c"
+ # Reset (F3)
+ # Enable keyboard: MAME Setup (Tab) > Keyboard Mode > AT Keyboard: Enabled
+
+ TODO:
+ - Interrupts
+ - Sprites
+ - Sound
+ - ZX-Mode locks
+ - CPU frequency
+ - Timings
+ - Ram cache
+ - VDos
+ - Many more...
+
+ ****************************************************************************/
+
+#include "emu.h"
+#include "includes/tsconf.h"
+
+TILE_GET_INFO_MEMBER(tsconf_state::get_tile_info_txt)
+{
+ u8 *m_row_location = &m_ram->pointer()[(m_regs[V_PAGE] << 14) + (tile_index / tilemap.cols() * 256)];
+ u8 col = tile_index % tilemap.cols();
+ u8 symbol = m_row_location[col];
+ tileinfo.set(1, symbol, 0, 0);
+}
+
+template <u8 Layer>
+TILE_GET_INFO_MEMBER(tsconf_state::get_tile_info_16c)
+{
+ u8 col_offset = (tile_index % tilemap.cols()) << 1;
+ u16 row_offset = (((tile_index / tilemap.cols()) << 1) + Layer) * 64 * 2;
+
+ u8 *tile_info_addr = &m_ram->pointer()[(m_regs[T_MAP_PAGE] << 14) + row_offset + col_offset];
+ u8 hi = tile_info_addr[1];
+
+ u16 tile = ((u16(hi) & 0x0f) << 8) | tile_info_addr[0];
+ tile = tile / tilemap.cols() * 64 * 8 + (tile % tilemap.cols());
+ u8 pal = (BIT(m_regs[PAL_SEL], 4 + Layer * 2, 2) << 2) | BIT(hi, 4, 2);
+ if (BIT(hi, 6, 2))
+ {
+ logerror("FIXME - FLIP Case\n");
+ }
+ tileinfo.set(2 + Layer, tile, pal, 0);
+}
+
+TILE_GET_INFO_MEMBER(tsconf_state::get_sprite_info_16c)
+{
+ tileinfo.set(4, tile_index, 0, 0);
+}
+
+void tsconf_state::tsconf_mem(address_map &map)
+{
+ map(0x0000, 0x3fff).bankr("bank1").w(FUNC(tsconf_state::tsconf_bank_w<0>));
+ map(0x4000, 0x7fff).bankr("bank2").w(FUNC(tsconf_state::tsconf_bank_w<1>));
+ map(0x8000, 0xbfff).bankr("bank3").w(FUNC(tsconf_state::tsconf_bank_w<2>));
+ map(0xc000, 0xffff).bankr("bank4").w(FUNC(tsconf_state::tsconf_bank_w<3>));
+}
+
+void tsconf_state::tsconf_io(address_map &map)
+{
+ map.unmap_value_high();
+ map(0x0000, 0x0000).mirror(0x7ffd).w(FUNC(tsconf_state::tsconf_port_7ffd_w));
+ map(0x001f, 0x001f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::status_r), FUNC(beta_disk_device::command_w));
+ map(0x003f, 0x003f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::track_r), FUNC(beta_disk_device::track_w));
+ map(0x0057, 0x0057).mirror(0xff00).rw(FUNC(tsconf_state::tsconf_port_57_zctr_r), FUNC(tsconf_state::tsconf_port_57_zctr_w)); // spi config
+ map(0x0077, 0x0077).mirror(0xff00).rw(FUNC(tsconf_state::tsconf_port_77_zctr_r), FUNC(tsconf_state::tsconf_port_77_zctr_w)); // spi data
+ map(0x005f, 0x005f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::sector_r), FUNC(beta_disk_device::sector_w));
+ map(0x007f, 0x007f).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::data_r), FUNC(beta_disk_device::data_w));
+ map(0x00fe, 0x00fe).select(0xff00).rw(FUNC(tsconf_state::spectrum_port_fe_r), FUNC(tsconf_state::tsconf_port_fe_w));
+ map(0x00ff, 0x00ff).mirror(0xff00).rw(m_beta, FUNC(beta_disk_device::state_r), FUNC(beta_disk_device::param_w));
+ map(0x00af, 0x00af).select(0xff00).rw(FUNC(tsconf_state::tsconf_port_xxaf_r), FUNC(tsconf_state::tsconf_port_xxaf_w));
+ map(0x8ff7, 0x8ff7).select(0x7000).w(FUNC(tsconf_state::tsconf_port_f7_w)); // 3:bff7 5:dff7 6:eff7
+ map(0xbff7, 0xbff7).r(FUNC(tsconf_state::tsconf_port_f7_r));
+}
+
+void tsconf_state::tsconf_switch(address_map &map)
+{
+ map(0x0000, 0x3fff).r(FUNC(tsconf_state::beta_neutral_r)); // Overlap with next because we want real addresses on the 3e00-3fff range
+ map(0x3d00, 0x3dff).r(FUNC(tsconf_state::beta_enable_r));
+ map(0x4000, 0xffff).r(FUNC(tsconf_state::beta_disable_r));
+}
+
+template <unsigned Bank>
+void tsconf_state::tsconf_bank_w(offs_t offset, u8 data)
+{
+ tsconf_state::ram_bank_write(Bank, offset, data);
+}
+
+static const gfx_layout spectrum_charlayout =
+{
+ 8, 8, /* 8 x 8 characters */
+ 96, /* 96 characters */
+ 1, /* 1 bits per pixel */
+ {0}, /* no bitplanes */
+ /* x offsets */
+ {STEP8(0, 1)},
+ /* y offsets */
+ {STEP8(0, 8)},
+ 8 * 8 /* every char takes 8 bytes */
+};
+
+static const gfx_layout tsconf_charlayout =
+{
+ 8,
+ 8,
+ 256,
+ 1,
+ {0},
+ {STEP8(0, 1)},
+ {STEP8(0, 8)},
+ 8 * 8
+};
+
+static const gfx_layout tsconf_tile_16cpp_layout =
+{
+ 8,
+ 8,
+ 64 * 64 * 8,
+ 4,
+ {STEP4(0, 1)},
+ {STEP8(0, 4)},
+ {STEP8(0, 256 * 8)},
+ // Much more tiles when needed. Because tiles are in RAW formut but we don't know region properties.
+ 8 * 4
+};
+
+static GFXDECODE_START(gfx_tsconf)
+ GFXDECODE_ENTRY("maincpu", 0x1fd00, spectrum_charlayout, 0xf7, 1)
+ GFXDECODE_ENTRY("maincpu", 0, tsconf_charlayout, 0xf7, 1) // TXT
+ GFXDECODE_ENTRY("maincpu", 0, tsconf_tile_16cpp_layout, 0, 255) // T0 16cpp
+ GFXDECODE_ENTRY("maincpu", 0, tsconf_tile_16cpp_layout, 0, 255) // T1 16cpp
+ GFXDECODE_ENTRY("maincpu", 0, tsconf_tile_16cpp_layout, 0, 255) // Sprites 16cpp
+GFXDECODE_END
+
+void tsconf_state::video_start()
+{
+ spectrum_128_state::video_start();
+
+ m_ts_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_txt)), TILEMAP_SCAN_ROWS, 8, 8, 128, 64);
+ m_ts_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_16c<0>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
+ m_ts_tilemap[1]->set_transparent_pen(0);
+ m_ts_tilemap[2] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_tile_info_16c<1>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
+ m_ts_tilemap[2]->set_transparent_pen(0);
+ m_ts_tilemap[3] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(tsconf_state::get_sprite_info_16c)), TILEMAP_SCAN_ROWS, 8, 8, 64, 64);
+ m_ts_tilemap[3]->set_transparent_pen(0);
+}
+
+void tsconf_state::machine_start()
+{
+ save_item(NAME(m_regs));
+ // TODO save'm'all!
+}
+
+void tsconf_state::machine_reset()
+{
+ u8 *messram = m_ram->pointer();
+ m_program = &m_maincpu->space(AS_PROGRAM);
+
+ m_ram_0000 = nullptr;
+
+ m_port_f7_ext = DISABLED;
+
+ m_regs[V_CONFIG] = 0x00;
+ m_regs[V_PAGE] = 0x05;
+ m_regs[G_X_OFFS_L] = 0x00;
+ m_regs[G_X_OFFS_H] &= 0xfe; // xxxxxxx0
+ m_regs[G_Y_OFFS_L] = 0x00;
+ m_regs[G_Y_OFFS_H] &= 0xfe; // xxxxxxx0
+ m_regs[TS_CONFIG] &= 0x03; // 000000xx
+ m_regs[PAL_SEL] = 0x0f;
+ m_regs[PAGE0] = 0x00;
+ m_regs[PAGE1] = 0x05;
+ m_regs[PAGE2] = 0x02;
+ m_regs[PAGE3] = 0x00;
+ m_regs[FMAPS] &= 0xef; // xxx0xxxx
+ m_regs[SYS_CONFIG] = 0x00;
+ m_regs[MEM_CONFIG] = 0x04;
+ m_regs[HS_INT] = 0x01; // 00000001
+ m_regs[VS_INT_L] = 0x00; // 00000001
+ m_regs[VS_INT_H] = 0x00; // 0000xxx0
+ // FDDVirt = 0x00; // 0000xxx0
+ m_regs[INT_MASK] = 0x01; // xxxxx001
+ // CacheConfig = 0x01; // xxxxx001
+
+ if (m_beta->started())
+ m_beta->enable();
+
+ memset(messram, 0, 4096 * 1024);
+
+ m_port_7ffd_data = 0;
+
+ m_zctl_cs = 1;
+ m_zctl_di = 0xff;
+
+ tsconf_update_bank1();
+
+ m_keyboard->write(0xff);
+ while (m_keyboard->read() != 0) { /* invalidate buffer */ }
+}
+
+void tsconf_state::tsconf(machine_config &config)
+{
+ spectrum_128(config);
+ config.device_remove("exp");
+ config.device_remove("palette");
+
+ //m_maincpu->set_clock(XTAL(14'000'000));
+ m_maincpu->set_addrmap(AS_PROGRAM, &tsconf_state::tsconf_mem);
+ m_maincpu->set_addrmap(AS_IO, &tsconf_state::tsconf_io);
+ m_maincpu->set_addrmap(AS_OPCODES, &tsconf_state::tsconf_switch);
+
+ m_ram->set_default_size("4096K");
+
+ GLUKRS(config, m_glukrs);
+
+ TSCONF_DMA(config, m_dma, XTAL(14'000'000) / 2);
+ m_dma->in_mreq_callback().set(FUNC(tsconf_state::ram_read16));
+ m_dma->out_mreq_callback().set(FUNC(tsconf_state::ram_write16));
+ m_dma->in_spireq_callback().set(FUNC(tsconf_state::spi_read16));
+ m_dma->out_cram_callback().set(FUNC(tsconf_state::cram_write16));
+
+ BETA_DISK(config, m_beta, 0);
+ SPI_SDCARD(config, m_sdcard, 0);
+ m_sdcard->spi_miso_callback().set(FUNC(tsconf_state::tsconf_spi_miso_w));
+
+ PALETTE(config, "palette", FUNC(tsconf_state::tsconf_palette), 256);
+ m_screen->set_raw(X1_128_SINCLAIR / 2.5, 448, 0, 360, 320, 0, 288);
+ subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_tsconf);
+ RAM(config, m_cram).set_default_size("512").set_default_value(0);
+
+ AT_KEYB(config, m_keyboard, pc_keyboard_device::KEYBOARD_TYPE::AT, 3);
+}
+
+ROM_START(tsconf)
+ ROM_REGION(0x090000, "maincpu", ROMREGION_ERASEFF) // 16KB ROM
+ ROM_LOAD("ts-bios.rom", 0x010000, 0x10000, CRC(b060b0d9) SHA1(820d3539de115141daff220a3cb733fc880d1bab))
+ROM_END
+
+// YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP( 2011, tsconf, spec128, 0, tsconf, spec_plus, tsconf_state, empty_init, "NedoPC, TS-Labs", "ZX Evolution TS-Configuration", MACHINE_NO_SOUND | MACHINE_WRONG_COLORS | MACHINE_IMPERFECT_TIMING )
diff --git a/src/mame/includes/spectrum.h b/src/mame/includes/spectrum.h
index c9a9d153c29..e25dfc5c5b5 100644
--- a/src/mame/includes/spectrum.h
+++ b/src/mame/includes/spectrum.h
@@ -159,7 +159,7 @@ protected:
uint8_t spectrum_clone_port_ula_r();
void spectrum_palette(palette_device &palette) const;
- uint32_t screen_update_spectrum(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ virtual uint32_t screen_update_spectrum(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
DECLARE_WRITE_LINE_MEMBER(screen_vblank_spectrum);
INTERRUPT_GEN_MEMBER(spec_interrupt);
@@ -210,7 +210,8 @@ protected:
optional_ioport m_io_joy2;
void spectrum_UpdateBorderBitmap();
- void spectrum_UpdateScreenBitmap(bool eof = false);
+ virtual u16 get_border_color();
+ virtual void spectrum_UpdateScreenBitmap(bool eof = false);
inline unsigned char get_display_color(unsigned char color, int invert);
inline void spectrum_plot_pixel(bitmap_ind16 &bitmap, int x, int y, uint32_t color);
diff --git a/src/mame/includes/tsconf.h b/src/mame/includes/tsconf.h
new file mode 100644
index 00000000000..d8700b3f7ae
--- /dev/null
+++ b/src/mame/includes/tsconf.h
@@ -0,0 +1,191 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/*****************************************************************************
+ *
+ * includes/tsconfig.h
+ *
+ ****************************************************************************/
+#ifndef MAME_INCLUDES_TSCONF_H
+#define MAME_INCLUDES_TSCONF_H
+
+#pragma once
+
+#include "machine/beta.h"
+#include "machine/glukrs.h"
+#include "machine/pckeybrd.h"
+#include "machine/spi_sdcard.h"
+#include "machine/tsconfdma.h"
+#include "spectrum.h"
+#include "tilemap.h"
+
+class tsconf_state : public spectrum_128_state
+{
+public:
+ tsconf_state(const machine_config &mconfig, device_type type, const char *tag)
+ : spectrum_128_state(mconfig, type, tag),
+ m_p_rom(*this, "maincpu"),
+ m_bank1(*this, "bank1"), m_bank2(*this, "bank2"), m_bank3(*this, "bank3"), m_bank4(*this, "bank4"),
+ m_keyboard(*this, "pc_keyboard"),
+ m_beta(*this, BETA_DISK_TAG),
+ m_dma(*this, "dma"),
+ m_sdcard(*this, "sdcard"),
+ m_glukrs(*this, "glukrs"),
+ m_palette(*this, "palette"),
+ m_gfxdecode(*this, "gfxdecode"),
+ m_cram(*this, "cram")
+ {
+ }
+
+ void tsconf(machine_config &config);
+
+protected:
+ virtual void video_start() override;
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+private:
+ enum gluk_ext : u8
+ {
+ CONF_VERSION = 0x00,
+ BOOTLOADER_VERSION = 0x01,
+ PS2KEYBOARDS_LOG = 0x02,
+ RDCFG = 0x03,
+ CONFIG = 0x0e,
+ SPIFL = 0x10,
+
+ DISABLED = 0xff
+ };
+
+ enum tsconf_regs : u8
+ {
+ V_CONFIG = 0x00,
+ V_PAGE = 0x01,
+
+ G_X_OFFS_L = 0x02,
+ G_X_OFFS_H = 0x03,
+ G_Y_OFFS_L = 0x04,
+ G_Y_OFFS_H = 0x05,
+
+ TS_CONFIG = 0x06,
+ PAL_SEL = 0x07,
+
+ BORDER = 0x0f,
+ PAGE0 = 0x10,
+ PAGE1 = 0x11,
+ PAGE2 = 0x12,
+ PAGE3 = 0x13,
+
+ FMAPS = 0x15,
+ T_MAP_PAGE = 0x16,
+ T0_G_PAGE = 0x17,
+ T1_G_PAGE = 0x18,
+ SG_PAGE = 0x19,
+
+ DMAS_ADDRESS_L = 0x1a,
+ DMAS_ADDRESS_H = 0x1b,
+ DMAS_ADDRESS_X = 0x1c,
+ DMAD_ADDRESS_L = 0x1d,
+ DMAD_ADDRESS_H = 0x1e,
+ DMAD_ADDRESS_X = 0x1f,
+
+ SYS_CONFIG = 0x20,
+ MEM_CONFIG = 0x21,
+ HS_INT = 0x22,
+ VS_INT_L = 0x23,
+ VS_INT_H = 0x24,
+
+ DMA_WAIT_PORT_DEV = 0x25,
+ DMA_LEN = 0x26,
+ DMA_CTRL = 0x27,
+ DMA_NUM_L = 0x28,
+ INT_MASK = 0x2a,
+ DMA_NUM_H = 0x2c,
+
+ T0_X_OFFSER_L = 0x40,
+ T0_X_OFFSER_H = 0x41,
+ T0_Y_OFFSER_L = 0x42,
+ T0_Y_OFFSER_H = 0x43,
+ T1_X_OFFSER_L = 0x44,
+ T1_X_OFFSER_H = 0x45,
+ T1_Y_OFFSER_L = 0x46,
+ T1_Y_OFFSER_H = 0x47
+ };
+
+ DECLARE_VIDEO_START(tsconf);
+ TILE_GET_INFO_MEMBER(get_tile_info_txt);
+ template <u8 Layer>
+ TILE_GET_INFO_MEMBER(get_tile_info_16c);
+ TILE_GET_INFO_MEMBER(get_sprite_info_16c);
+
+ // Changing this consider to revert 'virtual' in spectrum.h
+ u32 screen_update_spectrum(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) override;
+ void spectrum_UpdateScreenBitmap(bool eof = false) override;
+ void tsconf_palette(palette_device &palette) const;
+ u16 get_border_color() override;
+ void draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void tsconf_update_video_mode();
+ rectangle get_resolution_info();
+
+ void tsconf_port_7ffd_w(u8 data);
+ void tsconf_port_fe_w(offs_t offset, u8 data);
+ u8 tsconf_port_xxaf_r(offs_t reg);
+ void tsconf_port_xxaf_w(offs_t reg, u8 data);
+ u8 tsconf_port_77_zctr_r(offs_t reg);
+ void tsconf_port_77_zctr_w(offs_t reg, u8 data);
+ u8 tsconf_port_57_zctr_r(offs_t reg);
+ void tsconf_port_57_zctr_w(offs_t reg, u8 data);
+ void tsconf_spi_miso_w(u8 data);
+ u8 tsconf_port_f7_r(offs_t offset);
+ void tsconf_port_f7_w(offs_t offset, u8 data);
+
+ void tsconf_update_bank1();
+ u8 beta_neutral_r(offs_t offset);
+ u8 beta_enable_r(offs_t offset);
+ u8 beta_disable_r(offs_t offset);
+
+ void tsconf_io(address_map &map);
+ void tsconf_mem(address_map &map);
+ void tsconf_switch(address_map &map);
+
+ template <unsigned Bank>
+ void tsconf_bank_w(offs_t offset, u8 data);
+ void ram_bank_write(u8 bank, offs_t offset, u8 data);
+ void ram_page_write(u8 page, offs_t offset, u8 data);
+ void cram_write(u16 offset, u8 data);
+ void cram_write16(offs_t offset, u16 data);
+ void ram_write16(offs_t offset, u16 data);
+ u16 ram_read16(offs_t offset);
+ u16 spi_read16();
+
+ u8 m_regs[0x100];
+
+ address_space *m_program;
+ required_region_ptr<u8> m_p_rom;
+ required_memory_bank m_bank1;
+ required_memory_bank m_bank2;
+ required_memory_bank m_bank3;
+ required_memory_bank m_bank4;
+
+ required_device<at_keyboard_device> m_keyboard;
+
+ required_device<beta_disk_device> m_beta;
+ required_device<tsconfdma_device> m_dma;
+ required_device<spi_sdcard_sdhc_device> m_sdcard;
+ u8 m_zctl_di;
+ u8 m_zctl_cs;
+
+ required_device<glukrs_device> m_glukrs;
+ gluk_ext m_port_f7_ext;
+ u8 m_port_f7_gluk_reg;
+
+ optional_device<device_palette_interface> m_palette;
+ required_device<gfxdecode_device> m_gfxdecode;
+ tilemap_t *m_ts_tilemap[4];
+ required_device<ram_device> m_cram;
+};
+
+/*----------- defined in drivers/tsconf.c -----------*/
+
+INPUT_PORTS_EXTERN(tsconf);
+
+#endif // MAME_INCLUDES_TSCONF_H
diff --git a/src/mame/machine/tsconf.cpp b/src/mame/machine/tsconf.cpp
new file mode 100644
index 00000000000..98cf851d4aa
--- /dev/null
+++ b/src/mame/machine/tsconf.cpp
@@ -0,0 +1,653 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+
+#include "emu.h"
+#include "includes/tsconf.h"
+
+#define PAGE4K(_r) ((_r) << 14)
+
+#define W0_RAM (BIT(m_regs[MEM_CONFIG], 3))
+#define NW0_MAP (BIT(m_regs[MEM_CONFIG], 2))
+#define W0_WE (BIT(m_regs[MEM_CONFIG], 1))
+#define ROM128 (BIT(m_regs[MEM_CONFIG], 0))
+
+#define VM static_cast<v_mode>(BIT(m_regs[V_CONFIG], 0, 2))
+
+static constexpr rectangle resolution_info[4] = {
+ rectangle(52, 256 + 51, 48, 192 + 47), // 52|256|52 x 48-192-48
+ rectangle(20, 320 + 19, 44, 200 + 43), // 20|320|20 x 44-200-44
+ rectangle(20, 320 + 19, 24, 240 + 23), // 20|320|20 x 24-240-24
+ rectangle(0, 360 - 1, 0, 288 - 1) // 0|360|0 x 0-288-0
+};
+
+enum v_mode : u8
+{
+ VM_ZX = 0,
+ VM_16C,
+ VM_256C,
+ VM_TXT
+};
+
+// https://github.com/tslabs/zx-evo/blob/master/pentevo/vdac/vdac1/cpld/top.v
+static constexpr u8 pwm_to_rgb[32] = {
+ 0, 10, 21, 31, 42, 53, 63, 74,
+ 85, 95, 106, 117, 127, 138, 149, 159,
+ 170, 181, 191, 202, 213, 223, 234, 245,
+ 255, 255, 255, 255, 255, 255, 255, 255};
+
+static constexpr rgb_t from_pwm(u16 pwm15)
+{
+ return rgb_t(pwm_to_rgb[BIT(pwm15, 10, 5)], pwm_to_rgb[BIT(pwm15, 5, 5)], pwm_to_rgb[BIT(pwm15, 0, 5)]);
+}
+
+rectangle tsconf_state::get_resolution_info()
+{
+ rectangle info = resolution_info[BIT(m_regs[V_CONFIG], 6, 2)];
+ if (VM == VM_TXT)
+ {
+ info.set_origin(0, 0);
+ info.set_width(info.width() << 1);
+ }
+ return info;
+}
+
+void tsconf_state::tsconf_palette(palette_device &palette) const
+{
+ rgb_t colors[256] = {0};
+ palette.set_pen_colors(0, colors);
+}
+
+void tsconf_state::tsconf_update_bank1()
+{
+
+ //W0_WE
+ if (NW0_MAP)
+ {
+ m_ROMSelection = m_regs[PAGE0];
+ }
+ else
+ {
+ /* ROM: 0-SYS, 1-DOS, 2-128, 3-48 */
+ m_ROMSelection = m_beta->started() && m_beta->is_active() ? ROM128 : (0x02 | ROM128);
+ m_ROMSelection |= (m_regs[PAGE0] & 0xfc);
+ }
+
+ uint8_t *rom0;
+ if (W0_RAM)
+ {
+ rom0 = m_ram->pointer() + PAGE4K(m_ROMSelection);
+ m_bank1->set_base(rom0);
+ }
+ else
+ {
+ rom0 = &m_p_rom[0x10000 + PAGE4K(m_ROMSelection & 0x1f)];
+ m_bank1->set_base(rom0);
+ }
+ m_ram_0000 = W0_WE ? rom0 : nullptr;
+}
+
+void tsconf_state::tsconf_update_video_mode()
+{
+ rectangle resolution = resolution_info[3];
+ u8 *messram = m_ram->pointer();
+ switch (VM)
+ {
+ case VM_TXT: // Text Mode
+ resolution = get_resolution_info();
+ m_gfxdecode->gfx(1)->set_source(messram + PAGE4K(m_regs[V_PAGE] ^ 0x01));
+ break;
+ case VM_ZX: // Zx
+ {
+ m_screen_location = messram + ((m_port_7ffd_data & 8) ? PAGE4K(7) : PAGE4K(5));
+ break;
+ }
+ default:
+ break;
+ }
+
+ m_screen->configure(resolution.width(), resolution.height(), resolution, HZ_TO_ATTOSECONDS(50));
+}
+
+uint32_t tsconf_state::screen_update_spectrum(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_screen_bitmap.valid())
+ {
+ // Border
+ if (m_border_bitmap.valid())
+ {
+ if (BIT(m_regs[V_CONFIG], 5) || VM != VM_ZX)
+ {
+ copyscrollbitmap(bitmap, m_border_bitmap, 0, nullptr, 0, nullptr, cliprect);
+ }
+ }
+
+ // Main Graphics
+ rectangle resolution = get_resolution_info();
+ if (!BIT(m_regs[V_CONFIG], 5))
+ {
+ if (VM == VM_ZX)
+ {
+ // Zx palette is stored at 0xf0. Adjust bitmaps colors.
+ for (auto y = cliprect.top(); y <= cliprect.bottom(); y++)
+ {
+ u16 *bm = &m_screen_bitmap.pix(y, 0);
+ for (auto x = cliprect.left(); x <= cliprect.right(); x++)
+ {
+ *bm++ |= 0xf0;
+ }
+ }
+ spectrum_state::screen_update_spectrum(screen, bitmap, cliprect);
+ }
+ else
+ {
+ copyscrollbitmap(bitmap, m_screen_bitmap, 0, nullptr, 0, nullptr, resolution);
+ }
+ }
+ // Tiles & Sprites
+ // TODO layers
+ if (!BIT(m_regs[V_CONFIG], 4))
+ {
+ if (BIT(m_regs[TS_CONFIG], 5))
+ {
+ m_ts_tilemap[1]->draw(screen, bitmap, resolution, 0);
+ }
+ if (BIT(m_regs[TS_CONFIG], 6))
+ {
+ m_ts_tilemap[2]->draw(screen, bitmap, resolution, 0);
+ }
+ if (BIT(m_regs[TS_CONFIG], 7))
+ {
+ draw_sprites(screen, bitmap, resolution);
+ }
+ }
+ }
+ return 0;
+}
+
+void tsconf_state::spectrum_UpdateScreenBitmap(bool eof)
+{
+ if (!BIT(m_regs[V_CONFIG], 5))
+ {
+ if (VM == VM_ZX)
+ {
+ spectrum_state::spectrum_UpdateScreenBitmap(eof);
+ }
+ else if (!eof)
+ {
+ u8 pal_offset = m_regs[PAL_SEL] << 4;
+ rectangle resolution = get_resolution_info();
+ if (VM == VM_TXT)
+ {
+ u8 *messram = m_ram->pointer();
+ u16 y = m_screen->vpos();
+ u8 *font_location = messram + PAGE4K(m_regs[V_PAGE] ^ 0x01);
+ u8 *text_location = messram + PAGE4K(m_regs[V_PAGE]) + (y / 8 * 256); // OFFSETs
+ u16 *bm = &m_screen_bitmap.pix(y, 0);
+ for (auto x = 0; x < resolution.width() / 8; x++)
+ {
+ u8 char_x = *(font_location + (*text_location * 8) + (y % 8));
+ u8 font_color = *(text_location + 128) & 0x0f;
+ u8 bg_color = (*(text_location + 128) & 0xf0) >> 4;
+ for (auto i = 7; i >= 0; i--)
+ {
+ *bm++ = (BIT(char_x, i) ? font_color : bg_color) + pal_offset;
+ }
+ text_location++;
+ }
+ }
+ else
+ {
+ if (m_screen->vpos() >= resolution.top())
+ {
+ u16 y = m_screen->vpos() - resolution.top();
+ u32 offset = ((y + ((m_regs[G_Y_OFFS_H] & 1) << 8) + m_regs[G_Y_OFFS_L]) * 512) +
+ ((m_regs[G_X_OFFS_H] & 1) << 8) + m_regs[G_X_OFFS_L];
+ if (VM == VM_16C)
+ {
+ // FIXME wouldn't work for odd offsets
+ offset >>= 1;
+ }
+ u8 *video_location = &m_ram->pointer()[PAGE4K(m_regs[V_PAGE]) + offset];
+ u16 *bm = &m_screen_bitmap.pix(m_screen->vpos(), resolution.left());
+ for (auto x = resolution.left(); x <= resolution.right(); x++)
+ {
+ u8 pix = *video_location;
+ if (VM == VM_16C)
+ {
+ *bm++ = (pix >> 4) + pal_offset;
+ *bm++ = (pix & 0x0f) + pal_offset;
+ x++;
+ }
+ else
+ {
+ *bm++ = pix;
+ }
+ video_location++;
+ }
+ }
+ }
+ }
+ }
+}
+
+u16 tsconf_state::get_border_color()
+{
+ return m_regs[BORDER];
+}
+
+void tsconf_state::draw_sprites(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ //u8 *messram = m_ram->pointer();
+ //u8 *sprites_location = messram + PAGE4K(m_regs[SG_PAGE]);
+ for (u8 i = 0; i < 85; i++)
+ {
+ logerror("Draw Sprites ... TODO\n");
+ }
+}
+
+void tsconf_state::ram_bank_write(u8 bank, offs_t offset, u8 data)
+{
+ offs_t machine_addr = PAGE4K(bank) + offset;
+ offs_t fmap_addr = BIT(m_regs[FMAPS], 0, 4) << 12;
+ if (BIT(m_regs[FMAPS], 4) && (machine_addr >= fmap_addr) && (machine_addr < (fmap_addr + 512)))
+ {
+ cram_write(machine_addr - fmap_addr, data);
+ }
+ else if (bank > 0 || (W0_WE && W0_RAM))
+ {
+ ram_page_write(m_regs[PAGE0 + bank], offset, data);
+ }
+}
+
+void tsconf_state::ram_page_write(u8 page, offs_t offset, u8 data)
+{
+ u32 ram_addr = PAGE4K(page) + offset;
+ if (ram_addr >= PAGE4K(m_regs[T_MAP_PAGE]) && ram_addr < (PAGE4K(m_regs[T_MAP_PAGE] + 1)))
+ {
+ //TODO invalidate sprites, not entire map
+ m_ts_tilemap[1]->mark_all_dirty();
+ m_ts_tilemap[2]->mark_all_dirty();
+ }
+ else
+ {
+ if (ram_addr >= PAGE4K(m_regs[T0_G_PAGE]) && ram_addr < PAGE4K(m_regs[T0_G_PAGE] + 8))
+ {
+ m_ts_tilemap[1]->mark_all_dirty();
+ }
+ if (ram_addr >= PAGE4K(m_regs[T1_G_PAGE]) && ram_addr < PAGE4K(m_regs[T1_G_PAGE] + 8))
+ {
+ m_ts_tilemap[2]->mark_all_dirty();
+ }
+ if (ram_addr >= PAGE4K(m_regs[SG_PAGE]) && ram_addr < PAGE4K(m_regs[SG_PAGE] + 8))
+ {
+ m_ts_tilemap[3]->mark_all_dirty();
+ }
+ }
+
+ m_ram->write(ram_addr, data);
+}
+
+u16 tsconf_state::ram_read16(offs_t offset)
+{
+ return ((m_ram->read(offset & 0xfffffffe)) << 8) | m_ram->read(offset | 1);
+}
+
+void tsconf_state::ram_write16(offs_t offset, u16 data)
+{
+ m_ram->write(offset & 0xfffffffe, data >> 8);
+ m_ram->write(offset | 1, data & 0xff);
+}
+
+u16 tsconf_state::spi_read16()
+{
+ return (tsconf_port_57_zctr_r(0) << 8) | tsconf_port_57_zctr_r(0);
+}
+
+void tsconf_state::cram_write(u16 offset, u8 data)
+{
+ m_cram->write(offset, data);
+ u8 pen = offset >> 1;
+ rgb_t rgb = from_pwm((m_cram->read(offset | 1) << 8 | m_cram->read(offset & 0xfffe)));
+ m_palette->set_pen_color(pen, rgb);
+};
+
+void tsconf_state::cram_write16(offs_t offset, u16 data)
+{
+ cram_write(offset & 0xfffe, data >> 8);
+ cram_write(offset | 1, data & 0xff);
+};
+
+void tsconf_state::tsconf_port_7ffd_w(u8 data)
+{
+ /* disable paging */
+ if (m_port_7ffd_data & 0x20)
+ return;
+
+ /* store new state */
+ m_port_7ffd_data = data;
+ m_regs[MEM_CONFIG] = (m_regs[MEM_CONFIG] & 0xfe) | BIT(data, 4); // ROM128
+ m_regs[V_PAGE] = BIT(data, 3) ? 7 : 5;
+
+ /* update memory */
+ tsconf_update_bank1();
+ tsconf_update_video_mode();
+}
+
+void tsconf_state::tsconf_port_fe_w(offs_t offset, u8 data)
+{
+ m_regs[BORDER] = (data & 0x07) | 0xf0;
+ spectrum_port_fe_w(offset, data);
+}
+
+u8 tsconf_state::tsconf_port_xxaf_r(offs_t port)
+{
+ u8 nreg = port >> 8;
+ u8 data = 0xff;
+
+ switch (nreg)
+ {
+ case V_CONFIG:
+ data = 0b01000011; // PWR_UP, !FDRVER, 5bit VDAC
+ break;
+ case PAGE2:
+ case PAGE3:
+ data = m_regs[nreg];
+ break;
+
+ case DMA_CTRL: // DMAStatus
+ data = m_dma->is_ready() ? 0x00 : 0x80;
+ break;
+
+ case 0x30: // FRCnt0
+ case 0x31: // FRCnt1
+ case 0x32: // FRCnt2
+ default:
+ logerror("'tsconf': unmapped reg read %02X\n", nreg);
+ break;
+ }
+
+ //LOGWARN("'tsconf': reg read %02X = %02x\n", nreg, data);
+ return data;
+}
+
+void tsconf_state::tsconf_port_xxaf_w(offs_t port, u8 data)
+{
+ u8 nreg = port >> 8;
+ m_regs[nreg] = data;
+
+ switch (nreg)
+ {
+ case V_CONFIG:
+ case V_PAGE:
+ case PAL_SEL:
+ tsconf_update_video_mode();
+ break;
+
+ case T_MAP_PAGE:
+ m_ts_tilemap[1]->mark_all_dirty();
+ m_ts_tilemap[2]->mark_all_dirty();
+ break;
+
+ case T0_G_PAGE:
+ m_gfxdecode->gfx(2)->set_source(m_ram->pointer() + PAGE4K(data));
+ m_ts_tilemap[1]->mark_all_dirty();
+ break;
+
+ case T0_X_OFFSER_L:
+ case T0_X_OFFSER_H:
+ case T0_Y_OFFSER_L:
+ case T0_Y_OFFSER_H:
+ m_ts_tilemap[1]->set_scrollx((m_regs[T0_X_OFFSER_H] << 8) | m_regs[T0_X_OFFSER_L]);
+ m_ts_tilemap[1]->set_scrolly((m_regs[T0_Y_OFFSER_H] << 8) | m_regs[T0_Y_OFFSER_L]);
+ break;
+
+ case T1_G_PAGE:
+ m_gfxdecode->gfx(3)->set_source(m_ram->pointer() + PAGE4K(data));
+ m_ts_tilemap[2]->mark_all_dirty();
+ break;
+
+ case T1_X_OFFSER_L:
+ case T1_X_OFFSER_H:
+ case T1_Y_OFFSER_L:
+ case T1_Y_OFFSER_H:
+ m_ts_tilemap[2]->set_scrollx((m_regs[T1_X_OFFSER_H] << 8) | m_regs[T1_X_OFFSER_L]);
+ m_ts_tilemap[2]->set_scrolly((m_regs[T1_Y_OFFSER_H] << 8) | m_regs[T1_Y_OFFSER_L]);
+ break;
+
+ case SG_PAGE:
+ m_gfxdecode->gfx(4)->set_source(m_ram->pointer() + PAGE4K(data));
+ m_ts_tilemap[3]->mark_all_dirty();
+ break;
+
+ case MEM_CONFIG:
+ m_port_7ffd_data = (m_port_7ffd_data & 0xef) | (ROM128 << 4);
+ tsconf_update_bank1();
+ break;
+
+ case PAGE0:
+ tsconf_update_bank1();
+ break;
+
+ case PAGE1:
+ m_bank2->set_base(m_ram->pointer() + PAGE4K(data));
+ break;
+
+ case PAGE2:
+ m_bank3->set_base(m_ram->pointer() + PAGE4K(data));
+ break;
+
+ case PAGE3:
+ m_bank4->set_base(m_ram->pointer() + PAGE4K(data));
+ break;
+
+ case BORDER:
+ spectrum_UpdateBorderBitmap();
+ break;
+
+ case DMAS_ADDRESS_L:
+ m_dma->set_saddr_l(data);
+ break;
+
+ case DMAS_ADDRESS_H:
+ m_dma->set_saddr_h(data);
+ break;
+
+ case DMAS_ADDRESS_X:
+ m_dma->set_saddr_x(data);
+ break;
+
+ case DMAD_ADDRESS_L:
+ m_dma->set_daddr_l(data);
+ break;
+
+ case DMAD_ADDRESS_H:
+ m_dma->set_daddr_h(data);
+ break;
+
+ case DMAD_ADDRESS_X:
+ m_dma->set_daddr_x(data);
+ break;
+
+ case DMA_LEN:
+ m_dma->set_block_len(data);
+ break;
+
+ case DMA_NUM_L:
+ m_dma->set_block_num_l(data);
+ break;
+
+ case DMA_NUM_H:
+ m_dma->set_block_num_h(data);
+ break;
+
+ case DMA_CTRL:
+ m_dma->start_tx(((BIT(data, 7) << 3) | (data & 0x07)), BIT(data, 5), BIT(data, 4), BIT(data, 3));
+ break;
+
+ case SYS_CONFIG:
+ // 0 - 3.5MHz, 1 - 7MHz, 2 - 14MHz, 3 - reserved
+ switch (data & 0x03)
+ {
+ case 2:
+ m_maincpu->set_clock(X1);
+ break;
+ case 1:
+ m_maincpu->set_clock(X1 / 2);
+ break;
+ case 0:
+ default:
+ m_maincpu->set_clock(X1 / 4);
+ break;
+ }
+ break;
+
+ case FMAPS:
+ case TS_CONFIG:
+ case G_X_OFFS_L:
+ case G_X_OFFS_H:
+ case G_Y_OFFS_L:
+ case G_Y_OFFS_H:
+ case HS_INT:
+ case VS_INT_L:
+ case VS_INT_H:
+ case INT_MASK:
+ break;
+
+ default:
+ logerror("Unsupported reg write: %02X = %02x\n", nreg, data);
+ break;
+ }
+}
+
+u8 tsconf_state::tsconf_port_f7_r(offs_t offset)
+{
+ // BFF7
+ u8 data = 0xff;
+ if (m_port_f7_ext == PS2KEYBOARDS_LOG && m_port_f7_gluk_reg == 0xf0)
+ {
+ data = m_keyboard->read();
+ }
+ else if (m_port_f7_ext != DISABLED)
+ {
+ data = m_glukrs->read(m_port_f7_gluk_reg);
+ }
+
+ return data;
+}
+
+void tsconf_state::tsconf_port_f7_w(offs_t offset, u8 data)
+{
+ auto m_l = offset >> 12;
+ if (m_l == 6) // EF
+ {
+ m_port_f7_ext = (data & 0x80) ? CONF_VERSION : DISABLED;
+ }
+ else if (m_port_f7_ext != DISABLED)
+ {
+ if (m_l == 5) // DF
+ {
+ // 0x0E..0xEF
+ m_port_f7_gluk_reg = data;
+ }
+ else if (m_l == 3) // BF
+ {
+ if (m_port_f7_gluk_reg == 0xf0)
+ {
+ u8 m_fx[0xf] = {0xff};
+ m_port_f7_ext = static_cast<gluk_ext>(data);
+ switch (m_port_f7_ext)
+ {
+ case CONF_VERSION:
+ {
+ strcpy((char *)m_fx, "M.A.M.E.");
+ PAIR16 m_ver;
+ m_ver.w = ((21 << 9) | (12 << 5) | 15);
+ m_fx[0x0c] = m_ver.b.l;
+ m_fx[0x0d] = m_ver.b.h;
+ break;
+ }
+ case BOOTLOADER_VERSION:
+ case PS2KEYBOARDS_LOG:
+ break;
+ default:
+ logerror("Gluk extention not supported %x\n", m_port_f7_gluk_reg);
+ break;
+ }
+ for (u8 i = 0; i < 0xf; i++)
+ {
+ m_glukrs->write(0xf0 + i, m_fx[i]);
+ }
+ }
+ else
+ {
+ m_glukrs->write(m_port_f7_gluk_reg, data);
+ }
+ }
+ }
+}
+
+void tsconf_state::tsconf_port_77_zctr_w(offs_t port, u8 data)
+{
+ m_sdcard->spi_ss_w(BIT(data, 0));
+ m_zctl_cs = BIT(data, 1);
+}
+
+u8 tsconf_state::tsconf_port_77_zctr_r(offs_t port)
+{
+ return 0x02 | !m_sdcard->get_card_present();
+}
+
+void tsconf_state::tsconf_port_57_zctr_w(offs_t port, u8 data)
+{
+ if (!m_zctl_cs)
+ {
+ for (u8 m = 0x80; m; m >>= 1)
+ {
+ m_sdcard->spi_clock_w(CLEAR_LINE); //0-S R
+ m_sdcard->spi_mosi_w(data & m ? 1 : 0);
+ m_sdcard->spi_clock_w(ASSERT_LINE); //1-L W
+ }
+ }
+}
+
+u8 tsconf_state::tsconf_port_57_zctr_r(offs_t port)
+{
+ tsconf_port_57_zctr_w(0, 0xff);
+ return m_zctl_cs ? 0xff : m_zctl_di;
+}
+
+void tsconf_state::tsconf_spi_miso_w(u8 data)
+{
+ m_zctl_di <<= 1;
+ m_zctl_di |= data;
+}
+
+u8 tsconf_state::beta_neutral_r(offs_t offset)
+{
+ return m_program->read_byte(offset);
+}
+
+u8 tsconf_state::beta_enable_r(offs_t offset)
+{
+ if (m_ROMSelection == 3)
+ {
+ if (m_beta->started() /*&& !m_beta->is_active()*/)
+ {
+ m_beta->enable();
+ tsconf_update_bank1();
+ }
+ }
+
+ return m_program->read_byte(offset + 0x3d00);
+}
+
+u8 tsconf_state::beta_disable_r(offs_t offset)
+{
+ if (m_beta->started() && m_beta->is_active())
+ {
+ m_beta->disable();
+ tsconf_update_bank1();
+ }
+
+ return m_program->read_byte(offset + 0x4000);
+}
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 8dab36b88f8..81e061e9c9a 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -44078,6 +44078,9 @@ zx80 // 1980 Sinclair ZX-80
zx81 // 1981 Sinclair ZX-81
zx97 //
+@source:tsconf.cpp
+tsconf // ZX Evolution - TS-Configuration
+
@source:testpat.cpp
tp1983
tp1985
diff --git a/src/mame/video/spectrum.cpp b/src/mame/video/spectrum.cpp
index f16b55e86c1..c465921b587 100644
--- a/src/mame/video/spectrum.cpp
+++ b/src/mame/video/spectrum.cpp
@@ -266,6 +266,10 @@ void spectrum_state::spectrum_UpdateScreenBitmap(bool eof)
}
}
+u16 spectrum_state::get_border_color() {
+ return m_port_fe_data & 0x07;
+}
+
/* The code below is just a per-pixel 'partial update' for the border */
void spectrum_state::spectrum_UpdateBorderBitmap()
@@ -278,7 +282,7 @@ void spectrum_state::spectrum_UpdateBorderBitmap()
if (m_border_bitmap.valid())
{
- uint16_t border = m_port_fe_data & 0x07;
+ uint16_t border = get_border_color();
//printf("update border from %d,%d to %d,%d\n", m_previous_border_x, m_previous_border_y, x, y);