summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author holub <andrei.holub@gmail.com>2023-05-11 14:02:16 -0400
committer GitHub <noreply@github.com>2023-05-12 04:02:16 +1000
commit430be796c67b7efa08ca65a7b71e4205ff91eaaa (patch)
tree716fa3c8caa22dd6574314b35994d86d630db887 /src
parentdb15fba4422269327217d554e2d06a21d3f873b4 (diff)
sinclair/sprinter.cpp: Added Sprinter Sp2000 enhanced Spectrum clone. (#11018)
cpu/z80: Added support for variants with address translation and implemented Z84C015 chip selects. New working clones ------------------- Peters Plus, Ivan Mak Sprinter Sp2000
Diffstat (limited to 'src')
-rw-r--r--src/devices/bus/rs232/hlemouse.cpp2
-rw-r--r--src/devices/cpu/z80/tmpz84c011.h2
-rw-r--r--src/devices/cpu/z80/tmpz84c015.cpp12
-rw-r--r--src/devices/cpu/z80/tmpz84c015.h9
-rw-r--r--src/devices/cpu/z80/z80.cpp12
-rw-r--r--src/devices/cpu/z80/z80.h1
-rw-r--r--src/devices/cpu/z80/z84c015.cpp111
-rw-r--r--src/devices/cpu/z80/z84c015.h63
-rw-r--r--src/emu/emumem.h1
-rwxr-xr-xsrc/mame/mame.lst3
-rw-r--r--src/mame/sinclair/sprinter.cpp1759
-rw-r--r--src/osd/modules/debugger/debuggdbstub.cpp1
12 files changed, 1961 insertions, 15 deletions
diff --git a/src/devices/bus/rs232/hlemouse.cpp b/src/devices/bus/rs232/hlemouse.cpp
index 7b25b1e327b..49d7a720606 100644
--- a/src/devices/bus/rs232/hlemouse.cpp
+++ b/src/devices/bus/rs232/hlemouse.cpp
@@ -53,7 +53,7 @@
10000lmr xxxxxxxx yyyyyyyy xxxxxxxx yyyyyyyy
- The Mouse systems rotatable protcol allows the host to infer
+ The Mouse systems rotatable protocol allows the host to infer
rotation around the third axis at the cost of halving the maximum
sustained movement speed. The M-1 mouse has two sensors spaced 100
counts apart horizontally. If DIP switch 2 is on, the X and Y delta
diff --git a/src/devices/cpu/z80/tmpz84c011.h b/src/devices/cpu/z80/tmpz84c011.h
index 89b40a9f6aa..7e7455d077e 100644
--- a/src/devices/cpu/z80/tmpz84c011.h
+++ b/src/devices/cpu/z80/tmpz84c011.h
@@ -30,7 +30,7 @@
class tmpz84c011_device : public z80_device
{
public:
- tmpz84c011_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t);
+ tmpz84c011_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// configuration helpers
auto zc0_callback() { return m_zc0_cb.bind(); }
diff --git a/src/devices/cpu/z80/tmpz84c015.cpp b/src/devices/cpu/z80/tmpz84c015.cpp
index 3df10f19c6c..55d21908b77 100644
--- a/src/devices/cpu/z80/tmpz84c015.cpp
+++ b/src/devices/cpu/z80/tmpz84c015.cpp
@@ -17,7 +17,7 @@
DEFINE_DEVICE_TYPE(TMPZ84C015, tmpz84c015_device, "tmpz84c015", "Toshiba TMPZ84C015")
-void tmpz84c015_device::internal_io_map(address_map &map)
+void tmpz84c015_device::internal_io_map(address_map &map) const
{
map(0x10, 0x13).mirror(0xff00).rw(m_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
map(0x18, 0x1b).mirror(0xff00).rw(m_sio, FUNC(z80sio_device::ba_cd_r), FUNC(z80sio_device::ba_cd_w));
@@ -27,10 +27,14 @@ void tmpz84c015_device::internal_io_map(address_map &map)
map(0xf4, 0xf4).mirror(0xff00).w(FUNC(tmpz84c015_device::irq_priority_w));
}
-
tmpz84c015_device::tmpz84c015_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- z80_device(mconfig, TMPZ84C015, tag, owner, clock),
- m_io_space_config( "io", ENDIANNESS_LITTLE, 8, 16, 0, address_map_constructor(FUNC(tmpz84c015_device::internal_io_map), this)),
+ tmpz84c015_device(mconfig, TMPZ84C015, tag, owner, clock, address_map_constructor(FUNC(tmpz84c015_device::internal_io_map), this))
+{
+}
+
+tmpz84c015_device::tmpz84c015_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor io_map) :
+ z80_device(mconfig, type, tag, owner, clock),
+ m_io_space_config( "io", ENDIANNESS_LITTLE, 8, 16, 0, io_map),
m_ctc(*this, "tmpz84c015_ctc"),
m_sio(*this, "tmpz84c015_sio"),
m_pio(*this, "tmpz84c015_pio"),
diff --git a/src/devices/cpu/z80/tmpz84c015.h b/src/devices/cpu/z80/tmpz84c015.h
index 99b6f095714..603d714925d 100644
--- a/src/devices/cpu/z80/tmpz84c015.h
+++ b/src/devices/cpu/z80/tmpz84c015.h
@@ -25,9 +25,11 @@
class tmpz84c015_device : public z80_device
{
public:
- tmpz84c015_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t);
+ tmpz84c015_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// configuration helpers
+ template <int Channel> void set_clk_trg(u32 clock) { subdevice<z80ctc_device>(m_ctc.finder_tag())->set_clk<Channel>(clock); }
+ template <int Channel> void set_clk_trg(const XTAL &xtal) { subdevice<z80ctc_device>(m_ctc.finder_tag())->set_clk<Channel>(xtal); }
// SIO callbacks
auto out_txda_callback() { return m_out_txda_cb.bind(); }
@@ -111,6 +113,8 @@ public:
/////////////////////////////////////////////////////////
protected:
+ tmpz84c015_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor io_map);
+
// device-level overrides
virtual void device_add_mconfig(machine_config &config) override;
virtual void device_start() override;
@@ -119,6 +123,7 @@ protected:
const address_space_config m_io_space_config;
+ void internal_io_map(address_map &map) const;
virtual space_config_vector memory_space_config() const override;
private:
@@ -170,8 +175,6 @@ private:
void irq_priority_w(uint8_t data);
- void internal_io_map(address_map &map);
-
DECLARE_WRITE_LINE_MEMBER( out_txda_cb_trampoline_w ) { m_out_txda_cb(state); }
DECLARE_WRITE_LINE_MEMBER( out_dtra_cb_trampoline_w ) { m_out_dtra_cb(state); }
DECLARE_WRITE_LINE_MEMBER( out_rtsa_cb_trampoline_w ) { m_out_rtsa_cb(state); }
diff --git a/src/devices/cpu/z80/z80.cpp b/src/devices/cpu/z80/z80.cpp
index 63ddff5d772..6941ac6c4e6 100644
--- a/src/devices/cpu/z80/z80.cpp
+++ b/src/devices/cpu/z80/z80.cpp
@@ -463,7 +463,7 @@ inline void z80_device::out(uint16_t port, uint8_t value)
***************************************************************/
uint8_t z80_device::rm(uint16_t addr)
{
- u8 res = m_data.read_byte(addr);
+ u8 res = m_data.read_byte(translate_memory_address(addr));
T(MTM);
return res;
}
@@ -491,7 +491,7 @@ void z80_device::wm(uint16_t addr, uint8_t value)
{
// As we don't count changes between read and write, simply adjust to the end of requested.
if(m_icount_executing != MTM) T(m_icount_executing - MTM);
- m_data.write_byte(addr, value);
+ m_data.write_byte(translate_memory_address(addr), value);
T(MTM);
}
@@ -528,7 +528,7 @@ uint8_t z80_device::rop()
{
// Use leftovers from previous instruction. Mainly to support recursive EXEC(.., rop())
if(m_icount_executing) T(m_icount_executing);
- uint8_t res = m_opcodes.read_byte(PCD);
+ uint8_t res = m_opcodes.read_byte(translate_memory_address(PCD));
T(execute_min_cycles());
m_refresh_cb((m_i << 8) | (m_r2 & 0x80) | (m_r & 0x7f), 0x00, 0xff);
T(execute_min_cycles());
@@ -546,7 +546,7 @@ uint8_t z80_device::rop()
***************************************************************/
uint8_t z80_device::arg()
{
- u8 res = m_args.read_byte(PCD);
+ u8 res = m_args.read_byte(translate_memory_address(PCD));
T(MTM);
PC++;
@@ -2048,7 +2048,7 @@ OP(xycb,ff) { A = set(7, rm_reg(m_ea)); wm(m_ea, A); } /* SET 7,A=(XY+o) */
OP(illegal,1) {
logerror("Z80 ill. opcode $%02x $%02x ($%04x)\n",
- m_opcodes.read_byte((PCD-1)&0xffff), m_opcodes.read_byte(PCD), PCD-1);
+ m_opcodes.read_byte(translate_memory_address((PCD-1)&0xffff)), m_opcodes.read_byte(translate_memory_address(PCD)), PCD-1);
}
/**********************************************************
@@ -2636,7 +2636,7 @@ OP(fd,ff) { illegal_1(); op_ff(); } /* DB FD
OP(illegal,2)
{
logerror("Z80 ill. opcode $ed $%02x\n",
- m_opcodes.read_byte((PCD-1)&0xffff));
+ m_opcodes.read_byte(translate_memory_address((PCD-1)&0xffff)));
}
/**********************************************************
diff --git a/src/devices/cpu/z80/z80.h b/src/devices/cpu/z80/z80.h
index 64951171860..3b91032f710 100644
--- a/src/devices/cpu/z80/z80.h
+++ b/src/devices/cpu/z80/z80.h
@@ -60,6 +60,7 @@ protected:
// device_memory_interface overrides
virtual space_config_vector memory_space_config() const override;
+ virtual u32 translate_memory_address(u16 address) { return address; }
// device_state_interface overrides
virtual void state_import(const device_state_entry &entry) override;
diff --git a/src/devices/cpu/z80/z84c015.cpp b/src/devices/cpu/z80/z84c015.cpp
new file mode 100644
index 00000000000..66807a920a6
--- /dev/null
+++ b/src/devices/cpu/z80/z84c015.cpp
@@ -0,0 +1,111 @@
+// license:BSD-3-Clause
+/***************************************************************************
+
+ Zilog Z84C015, MPUZ80/Z8400/84C00 Family
+ Z80 CPU, SIO, CTC, CGC, PIO, WDT
+
+***************************************************************************/
+
+#include "emu.h"
+#include "z84c015.h"
+
+DEFINE_DEVICE_TYPE(Z84C015, z84c015_device, "z84c015", "Zilog Z84C015")
+
+void z84c015_device::internal_io_map(address_map &map) const
+{
+ tmpz84c015_device::internal_io_map(map);
+ map(0xee, 0xee).mirror(0xff00).rw(FUNC(z84c015_device::scrp_r), FUNC(z84c015_device::scrp_w));
+ map(0xef, 0xef).mirror(0xff00).rw(FUNC(z84c015_device::scdp_r), FUNC(z84c015_device::scdp_w));
+}
+
+z84c015_device::z84c015_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : tmpz84c015_device(mconfig, Z84C015, tag, owner, clock, address_map_constructor(FUNC(z84c015_device::internal_io_map), this))
+ , m_program_space_config("program", ENDIANNESS_LITTLE, 8, 18, 0, 16, 0)
+ , m_opcodes_space_config("opcodes", ENDIANNESS_LITTLE, 8, 18, 0, 16, 0)
+{
+}
+
+device_memory_interface::space_config_vector z84c015_device::memory_space_config() const
+{
+ auto r = z80_device::memory_space_config();
+ for (auto it = r.begin(); it != r.end(); ++it)
+ {
+ if ((*it).first == AS_IO)
+ (*it).second = &m_io_space_config;
+ else if ((*it).first == AS_OPCODES)
+ (*it).second = &m_opcodes_space_config;
+ else if ((*it).first == AS_PROGRAM)
+ (*it).second = &m_program_space_config;
+ }
+
+ return r;
+}
+
+bool z84c015_device::memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space)
+{
+ if (spacenum == AS_PROGRAM || spacenum == AS_OPCODES)
+ address = translate_memory_address(address);
+
+ target_space = &space(spacenum);
+ return true;
+}
+
+u32 z84c015_device::translate_memory_address(u16 addr)
+{
+ const u8 csbr = csbr_r();
+ const u8 at = BIT(addr, 12, 4);
+ return ((BIT(m_mcr, 0) && ((csbr & 0x0f) >= at)) // cs0
+ ? 0x10000
+ : (BIT(m_mcr, 1) && ((csbr >> 4) >= at) && (at > (csbr & 0x0f))) // cs1
+ ? 0x20000 : 0) | addr;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void z84c015_device::device_start()
+{
+ tmpz84c015_device::device_start();
+
+ // register for save states
+ save_item(NAME(m_scrp));
+ save_item(NAME(m_wcr));
+ save_item(NAME(m_mwbr));
+ save_item(NAME(m_csbr));
+ save_item(NAME(m_mcr));
+
+ scrp_w(0);
+ m_wcr = 0x00; // 0xff, then 0x00 on 16th M1
+ m_mwbr = 0xf0;
+ m_csbr = 0xff; // Must be `|= 0x0f` but keep ff for reproducible startup
+ m_mcr = 0x01;
+
+ state_add_divider(-1);
+ state_add(Z84_WCR, "WCR", m_wcr);
+ state_add(Z84_MWBR, "MWBR", m_mwbr);
+ state_add(Z84_CSBR, "CSBR", m_csbr);
+ state_add(Z84_MCR, "MCR", m_mcr);
+}
+
+u8 z84c015_device::scdp_r()
+{
+ if (m_scrp < 0x04)
+ {
+ const u8 regs[4] = { m_wcr, m_mwbr, m_csbr, m_mcr };
+ return regs[m_scrp];
+ }
+ else
+ return 0xff;
+}
+
+void z84c015_device::scdp_w(u8 data)
+{
+ if (m_scrp == 0x00)
+ m_wcr = data;
+ else if (m_scrp == 0x01)
+ m_mwbr = data;
+ else if (m_scrp == 0x02)
+ m_csbr = data;
+ else if (m_scrp == 0x03)
+ m_mcr = data;
+}
diff --git a/src/devices/cpu/z80/z84c015.h b/src/devices/cpu/z80/z84c015.h
new file mode 100644
index 00000000000..0e5c71cb11c
--- /dev/null
+++ b/src/devices/cpu/z80/z84c015.h
@@ -0,0 +1,63 @@
+// license:BSD-3-Clause
+/***************************************************************************
+
+ Zilog Z84C015, MPUZ80/Z8400/84C00 Family
+ Z80 CPU, SIO, CTC, CGC, PIO, WDT
+
+***************************************************************************/
+
+#ifndef MAME_CPU_Z80_Z84C015_H
+#define MAME_CPU_Z80_Z84C015_H
+
+#pragma once
+
+#include "tmpz84c015.h"
+
+
+enum
+{
+ Z84_WCR = Z80_WZ + 1, Z84_MWBR, Z84_CSBR, Z84_MCR
+};
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class z84c015_device : public tmpz84c015_device
+{
+public:
+ z84c015_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ u8 csbr_r() { return m_csbr; }
+
+protected:
+ // device-level overrides
+ virtual void device_start() override;
+
+ const address_space_config m_program_space_config;
+ const address_space_config m_opcodes_space_config;
+
+ void internal_io_map(address_map &map) const;
+ virtual space_config_vector memory_space_config() const override;
+ virtual bool memory_translate(int spacenum, int intention, offs_t &address, address_space *&target_space) override;
+ virtual u32 translate_memory_address(u16 address) override;
+
+private:
+ // system control registers
+ u8 m_scrp;
+ u8 m_wcr;
+ u8 m_mwbr;
+ u8 m_csbr;
+ u8 m_mcr;
+
+ u8 scrp_r() { return m_scrp; };
+ void scrp_w(u8 data) { m_scrp = data; };
+ u8 scdp_r();
+ void scdp_w(u8 data);
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(Z84C015, z84c015_device)
+
+
+#endif // MAME_CPU_Z80_Z84C015_H
diff --git a/src/emu/emumem.h b/src/emu/emumem.h
index c17892f0a48..1b9b6363622 100644
--- a/src/emu/emumem.h
+++ b/src/emu/emumem.h
@@ -2394,6 +2394,7 @@ public:
virtual void remove_passthrough(std::unordered_set<handler_entry *> &handlers) = 0;
u64 unmap() const { return m_unmap; }
+ void unmap_value_high() { m_unmap = ~0; }
std::shared_ptr<emu::detail::memory_passthrough_handler_impl> make_mph(memory_passthrough_handler *mph);
std::shared_ptr<emu::detail::memory_passthrough_handler_impl> get_default_mpl() { return m_default_mpl; }
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 5da155ca5d6..77d673f468e 100755
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -40773,6 +40773,9 @@ tk90x // 1985 TK90x Color Computer
tk95 // 1986 TK95 Color Computer
zvezda // 1990 Zvezda
+@source:sinclair/sprinter.cpp
+sprinter // 2000 Sprinter
+
@source:sinclair/timex.cpp
tc2048 // 198? TC2048
ts2068 // 1983 TS2068
diff --git a/src/mame/sinclair/sprinter.cpp b/src/mame/sinclair/sprinter.cpp
new file mode 100644
index 00000000000..3688eedbf85
--- /dev/null
+++ b/src/mame/sinclair/sprinter.cpp
@@ -0,0 +1,1759 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrei I. Holub
+/*******************************************************************************************
+
+Sprinter Sp2000 (Peters Plus Ltd)
+
+Hardware:
+- CPU Z84C15 (21MHz/3.5MHz)
+- RAM 4Mb (64Mb)
+- Fast RAM 64Kb
+- ROM 256Kb
+- Video RAM 256Kb (512Kb)
+- FDD controller WD1793
+- Support FDD: 3,5" disk (1.44Mb/720Kb) / 5,25" disk (720Kb)
+- CMOS DALLAS
+- HDD controller IDE/AT
+- Keyboard controler 101key/AT
+- Mouse controller MS-Mouse
+- Slots ISA-8
+- Audio out AY-3-8910 (in PLD), Stereo 8 bit (16 bit)
+- Video out TV, CGA analog monitor, RGB
+- Graphic mode 320x256x256, 640x256x16, Spectrum standard screen
+- Text mode 80x32x16
+
+Refs:
+ https://web.archive.org/web/20030208004427/http://www.petersplus.com/sprinter/
+
+TODO:
+- ISA memory slots
+- fully untied from Spectrum parent
+- better rendering (currently not fully discovered) in Game Configuration
+- ? detect loading Configuration by checksum, not by presents in fastram
+
+*******************************************************************************************/
+
+#include "emu.h"
+
+#include "beta_m.h"
+#include "spec128.h"
+
+#include "bus/ata/atapicdr.h"
+#include "bus/ata/ataintf.h"
+#include "bus/ata/idehd.h"
+#include "bus/isa/isa.h"
+#include "bus/isa/isa_cards.h"
+#include "bus/pc_kbd/keyboards.h"
+#include "bus/pc_kbd/pc_kbdc.h"
+#include "bus/rs232/hlemouse.h"
+#include "bus/rs232/rs232.h"
+#include "bus/spectrum/zxbus.h"
+#include "cpu/z80/z84c015.h"
+#include "machine/ds128x.h"
+#include "sound/ay8910.h"
+#include "sound/dac.h"
+
+#include "speaker.h"
+#include "tilemap.h"
+
+#include <algorithm>
+
+
+#define LOG_IO (1U << 1)
+#define LOG_MEM (1U << 2)
+#define LOG_ACCEL (1U << 3)
+#define LOG_WARN (1U << 4)
+
+#define VERBOSE ( /*LOG_GENERAL | LOG_IO | LOG_MEM | LOG_ACCEL |*/ LOG_WARN )
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMEM(...) LOGMASKED(LOG_MEM, __VA_ARGS__)
+#define LOGACCEL(...) LOGMASKED(LOG_ACCEL, __VA_ARGS__)
+#define LOGWARN(...) LOGMASKED(LOG_WARN, __VA_ARGS__)
+
+namespace {
+
+#define X_SP 42_MHz_XTAL // TODO X1 after spectrumless
+
+#define SPRINT_WIDTH 896
+#define SPRINT_BORDER_RIGHT 48
+#define SPRINT_SCREEN_XSIZE 640
+#define SPRINT_BORDER_LEFT 48
+#define SPRINT_XVIS (SPRINT_BORDER_RIGHT + SPRINT_SCREEN_XSIZE + SPRINT_BORDER_LEFT)
+
+#define SPRINT_HEIGHT 320
+#define SPRINT_BORDER_TOP 16
+#define SPRINT_SCREEN_YSIZE 256
+#define SPRINT_BORDER_BOTTOM 16
+#define SPRINT_YVIS (SPRINT_BORDER_TOP + SPRINT_SCREEN_YSIZE + SPRINT_BORDER_BOTTOM)
+
+#define ACC_ENA BIT(m_all_mode, 0)
+#define CBL_MODE BIT(m_cbl_xx, 7)
+#define CBL_STEREO BIT(m_cbl_xx, 6)
+#define CBL_MODE16 BIT(m_cbl_xx, 5)
+#define CBL_INT_ENA BIT(m_cbl_xx, 4)
+#define RAM1 m_ram_pages[0xe9 - 0xc0]
+#define RAM2 m_ram_pages[0xea - 0xc0]
+
+class sprinter_state : public spectrum_128_state
+{
+public:
+ sprinter_state(const machine_config &mconfig, device_type type, const char *tag)
+ : spectrum_128_state(mconfig, type, tag)
+ , m_maincpu(*this, "maincpu")
+ , m_isa(*this, "isa%u", 0U)
+ , m_rtc(*this, "rtc")
+ , m_ata(*this, "ata%u", 1U)
+ , m_beta(*this, BETA_DISK_TAG)
+ , m_ay8910(*this, "ay8912")
+ , m_ldac(*this, "ldac")
+ , m_rdac(*this, "rdac")
+ , m_kbd(*this, "kbd")
+ , m_io_line(*this, "IO_LINE%u", 0U)
+ , m_io_mouse(*this, "mouse_input%u", 1U)
+ , m_io_turbo(*this, "TURBO")
+ , m_palette(*this, "palette")
+ , m_gfxdecode(*this, "gfxdecode")
+ , m_vram(*this, "vram", 0x40000, ENDIANNESS_LITTLE)
+ , m_fastram(*this, "fastram", 0x10000, ENDIANNESS_LITTLE)
+ , m_bank0_fastram(*this, "bank0_fastram")
+ , m_bank_view0(*this, "bank_view0")
+ , m_bank_view3(*this, "bank_view3")
+ { }
+
+ void sprinter(machine_config &config);
+
+ INPUT_CHANGED_MEMBER(turbo_changed);
+
+protected:
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+ virtual void video_start() override;
+
+ void map_io(address_map &map);
+ void map_mem(address_map &map);
+ void map_fetch(address_map &map);
+ u8 m1_r(offs_t offset);
+
+ void init_taps();
+
+ void update_memory();
+ void update_cpu();
+
+ TIMER_CALLBACK_MEMBER(irq_on) override;
+ TIMER_CALLBACK_MEMBER(irq_off) override;
+ TIMER_CALLBACK_MEMBER(cbl_tick);
+
+ u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void screen_update_graph(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void screen_update_game(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void draw_tile(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect);
+ void draw_symbol(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect, bool flash);
+ u8* as_mode(u8 a, u8 b);
+ std::pair<u8, u8> lookback_scroll(u8 a, u8 b);
+
+ required_device<z84c015_device> m_maincpu;
+ required_device_array<isa8_device, 2> m_isa;
+
+private:
+
+ static constexpr u16 BANK_RAM_MASK = 1 << 8;
+ static constexpr u16 BANK_FASTRAM_MASK = 1 << 9;
+ static constexpr u16 BANK_ISA_MASK = 1 << 10;
+ static constexpr u16 BANK_WRDISBL_MASK = 1 << 12;
+ enum accel_state : u8
+ {
+ OFF = 0, // ld b,b
+ FILL, // ld c,c
+ SET_BUFFER, // ld d,d
+ FILL_VERT, // ld e,e
+ DOUBLE, // ld h,h
+ COPY, // ld l,l
+ OFF_HALT, // halt
+ COPY_VERT, // ld a,a
+
+ MODE_AND = 0xa6, // and (hl)
+ MODE_XOR = 0xae, // xor (hl)
+ MODE_OR = 0xb6, // or (hl)
+ MODE_NOP = 0xbe // cp (hl)
+ };
+
+ u8 dcp_r(offs_t offset);
+ void dcp_w(offs_t offset, u8 data);
+
+ u8 bootstrap_r(offs_t offset);
+ void bootstrap_w(offs_t offset, u8 data);
+ u8 ram_r(offs_t offset);
+ void ram_w(offs_t offset, u8 data);
+ void vram_w(offs_t offset, u8 data);
+ void update_int(bool recalculate);
+ u8 isa_r(offs_t offset);
+ void isa_w(offs_t offset, u8 data);
+
+ void accel_control_r(u8 data);
+ void accel_r_tap(u16 offset, u8 &data);
+ void accel_w_tap(u16 offset, u8 &data);
+ u8 &accel_buffer(u8 idx);
+ void update_accel_buffer(u8 idx, u8 data);
+
+ u8 kbd_fe_r(offs_t offset);
+ void on_kbd_data(int state);
+ void do_cpu_wait(bool is_io = false);
+
+ required_device<ds12885_device> m_rtc;
+ required_device_array<ata_interface_device, 2> m_ata;
+ required_device<beta_disk_device> m_beta;
+ required_device<ay8910_device> m_ay8910;
+ required_device<dac_word_interface> m_ldac;
+ required_device<dac_word_interface> m_rdac;
+ required_device<pc_kbdc_device> m_kbd;
+ required_ioport_array<8> m_io_line;
+ required_ioport_array<3> m_io_mouse;
+ required_ioport m_io_turbo;
+ required_device<device_palette_interface> m_palette;
+ required_device<gfxdecode_device> m_gfxdecode;
+ tilemap_t *m_tilemap;
+ memory_region *m_rom;
+ memory_share_creator<u8> m_vram;
+ memory_share_creator<u8> m_fastram;
+ memory_bank_creator m_bank0_fastram;
+ memory_view m_bank_view0;
+ memory_view m_bank_view3;
+ memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_program;
+
+ TILE_GET_INFO_MEMBER(get_tile_info);
+
+ u8 *m_dcp_location;
+ u8 m_ram_pages[0x40] = {}; // 0xc0 - 0xff
+ u16 m_pages[4] = {}; // internal state for faster calculations
+
+ bool m_z80_m1;
+ std::list<std::pair<u16, u16>> m_ints;
+
+ u8 m_conf;
+ bool m_conf_loading;
+ bool m_starting;
+ bool m_dos; // 0-on, 1-off
+ bool m_cash_on;
+
+ u8 m_cnf;
+ bool m_rom_sys;
+ bool m_ram_sys;
+ bool m_sys_pg;
+ bool m_turbo;
+ bool m_turbo_hard;
+ bool m_arom16;
+ u8 m_rom_rg;
+ u8 m_pn;
+ u8 m_sc;
+ u8 m_all_mode;
+ u8 m_port_y;
+ u8 m_rgmod;
+ u8 m_pg3;
+ u8 m_isa_addr_ext;
+ std::pair<s8, s8> m_hold;
+ u8 m_kbd_data_cnt;
+
+ bool m_ata_selected; // 0-primary, 1-secondary
+ u8 m_ata_data_latch;
+
+ // Accelerator
+ bool m_skip_write;
+ u8 m_prf_d;
+ u8 m_acc_cnt;
+ u8 m_accel_buffer[256] = {};
+ bool m_alt_acc;
+ u16 m_aagr;
+ u8 m_xcnt;
+ u8 m_xagr;
+ accel_state m_acc_dir;
+ accel_state m_fn_acc;
+
+ // Covox Blaster
+ u8 m_cbl_xx;
+ u16 m_cbl_data[256] = {};
+ u8 m_cbl_cnt;
+ u8 m_cbl_wa;
+ bool m_cbl_wae;
+ emu_timer *m_cbl_timer = nullptr;
+};
+
+void sprinter_state::update_memory()
+{
+ const bool pre_rom = m_rom_sys || m_cash_on;
+ const bool pre_cash = !m_cash_on;
+ if (!pre_rom && pre_cash)
+ {
+ m_pages[0] = (m_rom_rg & 0x0f) ^ (!m_sys_pg << 3);
+ m_bank_rom[0]->set_entry(m_pages[0]);
+ m_bank_view0.select(1);
+ }
+ else if (pre_rom && !pre_cash)
+ {
+ m_pages[0] = BANK_FASTRAM_MASK | (m_rom_rg & 3);
+ m_bank0_fastram->set_entry(m_pages[0] & 0xff);
+ m_bank_view0.select(2);
+ }
+ else
+ {
+ const bool cash_on = 0;
+ const bool nmi_ena = 1;
+ const bool sc0 = BIT(m_sc, 0);
+ const bool sc_lc = !(sc0 && m_ram_sys) && !cash_on;
+ const u8 spr_ = BIT(m_sc, 1) ? 0 : ((m_dos << 1) | (BIT(m_pn, 4) || !m_dos));
+ const u8 pg0 = 0x20 // 0xe0
+ | ((sc0 || !m_ram_sys || cash_on || !nmi_ena) << 3)
+ | (((m_arom16 && !(sc0 && m_ram_sys)) || (cash_on && nmi_ena)) << 2)
+ | (((BIT(spr_, 1) && sc_lc) || !m_ram_sys || !nmi_ena) << 1)
+ | ((BIT(spr_, 0) && sc_lc) || !m_ram_sys || !nmi_ena);
+
+ m_pages[0] = BANK_RAM_MASK | m_ram_pages[pg0];
+ m_bank_ram[0]->set_entry(m_pages[0] & 0xff);
+ if (sc0 && m_ram_sys)
+ m_bank_view0.disable();
+ else
+ {
+ m_pages[0] |= BANK_WRDISBL_MASK;
+ m_bank_view0.select(0);
+ }
+ }
+
+ m_pages[1] = BANK_RAM_MASK | RAM1;
+ m_bank_ram[1]->set_entry(RAM1);
+ m_pages[2] = BANK_RAM_MASK | RAM2;
+ m_bank_ram[2]->set_entry(RAM2);
+
+ // 0xd0, 0xf0
+ m_pg3 = (BIT(~m_pn, 7) << 5) | 0x10 | (((BIT(m_sc, 4) && BIT(~m_cnf, 7)) || (BIT(m_cnf, 7) && BIT(m_pn, 6))) << 3) | (m_pn & 0x07);
+ m_pages[3] = m_starting ? 0x40 : m_ram_pages[m_pg3];
+ m_bank_ram[3]->set_entry(m_pages[3] & 0xff);
+ if (BIT(m_sc, 4) && ((m_pages[3] & 0xf9) == 0xd0))
+ {
+ m_pages[3] |= BANK_ISA_MASK;
+ m_bank_view3.select(0);
+ }
+ else
+ {
+ m_bank_ram[3]->set_entry(m_pages[3]);
+ m_bank_view3.disable();
+ m_pages[3] |= BANK_RAM_MASK;
+ }
+
+ LOGMEM("MEM: %x %x %x %x\n", m_pages[0], m_pages[1], m_pages[2], m_pages[3]);
+}
+
+void sprinter_state::update_cpu()
+{
+ m_maincpu->set_clock_scale((m_turbo && m_turbo_hard) ? 6 : 1); // 1 - 21MHz, 0 - 3.5MHz
+}
+
+u32 sprinter_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ if (m_conf)
+ screen_update_game(screen, bitmap, cliprect);
+ else
+ screen_update_graph(screen, bitmap, cliprect);
+
+ return 0;
+}
+
+void sprinter_state::screen_update_graph(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ const bool flash = BIT(screen.frame_number(), 4);
+ for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom();)
+ {
+ const u16 b8 = (SPRINT_HEIGHT + vpos - SPRINT_BORDER_TOP - m_hold.second) % SPRINT_HEIGHT;
+ for (u16 hpos = cliprect.left(); hpos <= cliprect.right();)
+ {
+ const u16 a16 = (SPRINT_WIDTH + hpos - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH;
+ u8* mode = as_mode(a16 >> 4, b8 >> 3);
+ const rectangle tile {hpos, std::min(cliprect.right(), hpos + 15 - (a16 & 15)), vpos, std::min(cliprect.bottom(), vpos + 7 - (b8 & 7))};
+ if(BIT(mode[0], 4))
+ draw_symbol(mode, bitmap, tile, flash);
+ else
+ draw_tile(mode, bitmap, tile);
+
+ hpos += tile.width();
+ }
+ vpos += 8 - (b8 & 7);
+ }
+}
+
+void sprinter_state::draw_tile(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ u16 *pix = &(bitmap.pix(cliprect.top(), cliprect.left()));
+ const u16 pal = BIT(mode[0], 6, 2) << 8;
+ u16 x = (BIT(mode[0], 0, 4) << 6) | (BIT(mode[1], 0, 3) << 3);
+ u8 y = BIT(mode[1], 3, 5) << 3;
+ const bool lowres = BIT(mode[2], 2);
+ if (lowres)
+ {
+ x += 4 * BIT(mode[2], 0);
+ y += 4 * BIT(mode[2], 1);
+ }
+ for (auto dy = cliprect.top(); dy <= cliprect.bottom(); dy++)
+ {
+ for (auto dx = cliprect.left(); dx <= cliprect.right(); dx++)
+ {
+ const u8 color = m_vram[(y + ((dy & 7) >> lowres)) * 1024 + x + ((dx & 15) >> (1 + lowres))];
+ *pix++ = pal + (BIT(mode[0], 5) ? color : ((dx & 1) ? (color & 0x0f) : (color >> 4)));
+ }
+ pix += SPRINT_WIDTH - cliprect.width();
+ }
+}
+
+void sprinter_state::draw_symbol(u8* mode, bitmap_ind16 &bitmap, const rectangle &cliprect, bool flash)
+{
+ rectangle partrect = cliprect;
+ do
+ {
+ const bool is_partial = cliprect.right() != partrect.right();
+ if (is_partial)
+ {
+ partrect.setx(partrect.right() + 1, cliprect.right());
+ mode += 1024;
+ }
+ else if (!BIT(mode[0], 5))
+ {
+ if (BIT(cliprect.left() - m_hold.first, 3))
+ mode += 1024;
+ else
+ partrect.setx(partrect.left(), std::min(cliprect.right(), cliprect.left() + 7 - ((cliprect.left() - m_hold.first) & 7)));
+ }
+
+ u16 *pix = &(bitmap.pix(partrect.top(), partrect.left()));
+ u8 attr = 0;
+ u16 pal = 0x400;
+ if (~mode[0] & 0xfc) // !blank
+ {
+ attr = m_vram[(mode[2] << 10) | (BIT(mode[0], 0, 4) << 6) | (BIT(m_pn, 3) << 5) | 0b11000 | BIT(mode[0], 6, 2)];
+ if ((BIT(mode[0], 5, 3) == 7) && ((mode[0] & 0x0c) != 0x0c)) // border
+ pal = 0x400 | ((m_port_fe_data & 0x07) << 3) | (m_port_fe_data & 0x07);
+ }
+
+ for (auto dy = partrect.top(); dy <= partrect.bottom(); dy++)
+ {
+ const u8 symb = (~mode[0] & 0xfc) ? m_vram[(mode[1] << 10) | (BIT(mode[0], 0, 4) << 6) | (BIT(m_pn, 3) << 5) | (BIT(mode[0], 6, 2) << 3) | ((dy - m_hold.second) & 7)] : 0;
+ for (auto dx = partrect.left(); dx <= partrect.right(); dx++)
+ {
+ if (BIT(mode[0], 5, 3) != 7)
+ {
+ const u8 bit = 1 << (7 - (((dx - m_hold.first) >> BIT(mode[0], 5)) & 7));
+ pal = attr + 0x400 + 0x100 * bool(symb & bit) + 0x200 * flash;
+ }
+ *pix++ = pal;
+ }
+ pix += SPRINT_WIDTH - partrect.width();
+ }
+ } while (cliprect.right() != partrect.right());
+}
+
+ // Game Config - used in Thunder in the Deep
+void sprinter_state::screen_update_game(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
+{
+ for (u16 vpos = cliprect.top(); vpos <= cliprect.bottom(); vpos++)
+ {
+ const u8 b = ((SPRINT_HEIGHT + vpos - SPRINT_BORDER_TOP - m_hold.second) % SPRINT_HEIGHT) >> 3;
+ const u8 a = ((SPRINT_WIDTH + cliprect.left() - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH) >> 4;
+ std::pair<u8, u8> scroll = lookback_scroll(a, b);
+
+ u8* mode = nullptr;
+ u16 pal, x;
+ u8 y;
+ u16 *pix = &(bitmap.pix(vpos, cliprect.left()));
+
+ for (u16 hpos = cliprect.left(); hpos <= cliprect.right(); hpos++)
+ {
+ u16 a16 = (SPRINT_WIDTH + hpos + (scroll.first << 1) - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH;
+ if ((mode != nullptr) && BIT(mode[0], 2) && (((a16 - (scroll.first << 1)) & 15) == 0))
+ {
+ scroll = {mode[3] & 0x0f, mode[3] >> 4};
+ a16 = (SPRINT_WIDTH + hpos + (scroll.first << 1) - SPRINT_BORDER_LEFT - m_hold.first) % SPRINT_WIDTH;
+ }
+ const u16 b8 = (SPRINT_HEIGHT + vpos + scroll.second - SPRINT_BORDER_TOP - m_hold.second) % SPRINT_HEIGHT;
+
+ if (mode == nullptr)
+ {
+ mode = as_mode(a16 >> 4, b8 >> 3);
+ pal = BIT(mode[0], 6, 2) << 8;
+ x = ((BIT(mode[0], 0, 2) << 8) | mode[1]);
+ y = mode[2];
+ }
+
+ if ((BIT(mode[0], 5, 3) == 7))
+ *pix++ = 0x400 | (((mode[0] & 0x0c) == 0x0c) ? 0 : ((m_port_fe_data & 0x07) << 3) | (m_port_fe_data & 0x07));
+ else
+ *pix++ = pal + m_vram[(y + (b8 & 7)) * 1024 + x + ((a16 & 15) >> 1)];
+
+ if ((a16 & 15) == 15)
+ {
+ if (BIT(mode[0], 2))
+ scroll = {mode[3] & 0x0f, mode[3] >> 4};
+ mode = nullptr;
+ }
+ }
+ }
+}
+
+u8* sprinter_state::as_mode(u8 a, u8 b)
+{
+ const u32 line1 = (1 + a * 2 + 0x80 * (m_rgmod & 1)) * 1024;
+ const u16 la = 0x300 + b * 4;
+
+ return m_vram + line1 + la;
+}
+
+std::pair<u8, u8> sprinter_state::lookback_scroll(u8 a, u8 b)
+{
+ for (auto v = b; b; b--, a = 55)
+ {
+ for (auto h = a; a; a--)
+ {
+ const u8* mode = as_mode(h, v);
+ if (BIT(mode[0], 2) && (h != a) && (v != b))
+ return { mode[3] & 0x0f, mode[3] >> 4 };
+ }
+ }
+
+ return { 0, 0 };
+}
+
+u8 sprinter_state::dcp_r(offs_t offset)
+{
+ if (m_starting) m_starting = 0;
+
+ if (!machine().side_effects_disabled())
+ {
+ do_cpu_wait(true);
+ if (((offset & 0x7f) == 0x7b))
+ {
+ m_cash_on = BIT(offset, 7);
+ update_memory();
+ }
+ }
+
+ const u16 dcp_offset = (BIT(m_cnf, 3, 2) << 12) | (0 << 11) | (m_dos << 10) | (1 << 9) | (BIT(offset, 14, 2) << 7) | (BIT(offset, 13) << 4) | (BIT(offset, 7) << 3) | (offset & 0x67);
+ const u8 dcpp = m_dcp_location[dcp_offset];
+ u8 data = 0xff;
+ switch (dcpp)
+ {
+ case 0x00: // no port
+ data = 0xff;
+ break;
+
+ case 0x10:
+ data = m_beta->status_r();
+ break;
+ case 0x11:
+ data = m_beta->track_r();
+ break;
+ case 0x12:
+ data = m_beta->sector_r();
+ break;
+ case 0x13:
+ data = m_beta->data_r();
+ break;
+ case 0x15:
+ data = m_beta->state_r();
+ break;
+
+ case 0x1c:
+ data = m_rtc->read(1);
+ break;
+
+ case 0x20:
+ if (!machine().side_effects_disabled() && BIT(~offset, 8))
+ {
+ const u16 tmp = m_ata[m_ata_selected]->cs0_r(0);
+ m_ata_data_latch = tmp >> 8;
+ data = tmp;
+ }
+ else
+ data = m_ata_data_latch;
+ break;
+ case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ if (BIT(~offset, 8))
+ data = m_ata[m_ata_selected]->cs0_r(dcpp & 0x07);
+ break;
+ case 0x28:
+ if (BIT(~offset, 8))
+ data = m_ata[m_ata_selected]->cs1_r(6);
+ break;
+ case 0x29:
+ if (BIT(~offset, 8))
+ data = m_ata[m_ata_selected]->cs1_r(7);
+ break;
+
+ case 0x40:
+ data = kbd_fe_r(offset);
+ break;
+
+ case 0x52: // AY8910
+ data = m_ay8910->data_r();
+ break;
+
+ case 0x58: // Kempston Mouse
+ switch (offset >> 8)
+ {
+ case 0xfa:
+ data = m_io_mouse[2]->read();
+ break;
+ case 0xfb:
+ data = m_io_mouse[0]->read();
+ break;
+ case 0xff:
+ data = m_io_mouse[1]->read();
+ break;
+ default:
+ data = 0xff;
+ }
+ break;
+
+ case 0x89:
+ data = m_cbl_xx;
+ break;
+
+ case 0xc0: case 0xc1: case 0xc2: case 0xc3: case 0xc4: case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ data = m_ram_pages[dcpp - 0xc0];
+ break;
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ data = m_ram_pages[m_pg3];
+ break;
+
+ default:
+ LOGIO("IOr: %02X / %X > %x\n", dcpp, offset, data);
+ break;
+ }
+
+ return data;
+}
+
+void sprinter_state::dcp_w(offs_t offset, u8 data)
+{
+ if (m_starting)
+ return;
+
+ do_cpu_wait(true);
+
+ if ((offset & 0xbf) == 0x3c)
+ {
+ m_rom_sys = BIT(~offset, 6);
+ if (BIT(~data, 1))
+ m_sys_pg = BIT(m_rom_rg, 4) || BIT(data, 0);
+ update_memory();
+ }
+ if (!m_rom_sys && ((offset & 0xff) == 0x5c))
+ {
+ m_rom_rg = data;
+ m_sys_pg |= BIT(m_rom_rg, 4);
+ update_memory();
+ }
+
+ const u16 dcp_offset = (BIT(m_cnf, 3, 2) << 12) | (0 << 11) | (m_dos << 10) | (0 << 9) | (BIT(offset, 14, 2) << 7) | (BIT(offset, 13) << 4) | (BIT(offset, 7) << 3) | (offset & 0x67);
+ const u8 dcpp = m_dcp_location[dcp_offset];
+ if ((dcpp >= 0xc0) && (dcpp < 0xf0))
+ m_ram_pages[dcpp - 0xc0] = data;
+ switch (dcpp)
+ {
+ case 0x10:
+ m_beta->command_w(data);
+ break;
+ case 0x11:
+ m_beta->track_w(data);
+ break;
+ case 0x12:
+ m_beta->sector_w(data);
+ break;
+ case 0x13:
+ m_beta->data_w(data);
+ break;
+ case 0x14:
+ m_beta->param_w(data);
+ break;
+ case 0x16:
+ if (data & 2)
+ m_beta->disable();
+ else
+ m_beta->enable();
+ break;
+
+ case 0x1b:
+ if (data & 0x80)
+ ; // RESET
+ if (data & 0x40)
+ ; // AEN
+ m_isa_addr_ext = data & 0x3f;
+ break;
+
+ case 0x1d:
+ case 0x1e:
+ m_rtc->write(~dcpp & 1, data);
+ break;
+
+ case 0x20:
+ if (BIT(offset, 8))
+ m_ata[m_ata_selected]->cs0_w(0, (data << 8) | m_ata_data_latch);
+ else
+ m_ata_data_latch = data;
+ break;
+ case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ if (BIT(offset, 8))
+ m_ata[m_ata_selected]->cs0_w(dcpp & 0x07, data);
+ break;
+ case 0x28:
+ if (BIT(offset, 8))
+ m_ata[m_ata_selected]->cs1_w(6, data);
+ break;
+ case 0x2a: // HDD1 - secondary
+ m_ata_selected = 1;
+ break;
+ case 0x2b: // HDD2 - primary
+ m_ata_selected = 0;
+ break;
+ case 0x2e:
+ if (m_conf)
+ machine().schedule_hard_reset();
+ else
+ {
+ m_conf_loading = 1;
+ machine().schedule_soft_reset();
+ }
+ break;
+
+ case 0x88:
+ if (CBL_MODE)
+ m_cbl_data[m_cbl_wa++] = data << 8;
+ else
+ {
+ m_ldac->write(data << 8);
+ m_rdac->write(data << 8);
+ }
+ break;
+
+ case 0x89:
+ {
+ m_cbl_xx = data;
+ m_cbl_cnt = 0;
+ m_cbl_wa = 0;
+ m_cbl_wae = CBL_MODE16;
+ const u8 divs[16] = {13, 9, 0, 0, 0, 0, 0, 0, 27, 19, 13, 9, 6, 4, 3, 1};
+ const attotime rate = (CBL_MODE && divs[m_cbl_xx & 15]) ? attotime::from_ticks(divs[m_cbl_xx & 15] + 1, X_SP / 192) : attotime::never;
+ m_cbl_timer->adjust(rate, 0, rate);
+ break;
+ }
+
+ case 0x8f: // rom & fastram page
+ m_rom_rg = data;
+ m_sys_pg = BIT(m_rom_rg, 4) || BIT(data, 0);
+ update_memory();
+ break;
+
+ case 0x90: // AY8910
+ m_ay8910->address_w(data);
+ break;
+ case 0x91:
+ m_ay8910->data_w(data);
+ break;
+
+ case 0xc0: // 1FFD
+ case 0xc8:
+ m_sc = data;
+ if (BIT(m_cnf, 6)) m_sc = 0; // CNF_SC_RESET
+ update_memory();
+ break;
+ case 0xc1: // 7FFD
+ case 0xc9:
+ m_pn = data;
+ if (BIT(m_cnf, 5)) m_pn &= 0xc0; // CNF_PN[5..0]_RESET
+ if (BIT(~m_cnf, 7)) m_pn &= 0x1f; // CNF_PN[7..6]_RESET
+ update_memory();
+ break;
+ case 0xc2:
+ m_screen->update_now();
+ spectrum_ula_w(offset, data);
+ break;
+ case 0xc3:
+ m_all_mode = data;
+ break;
+ case 0xcb:
+ m_hold = {(7 - (data & 0x0f)) * 2, 7 - (data >> 4)};
+ break;
+ case 0xc4:
+ case 0xcc:
+ m_port_y = data;
+ break;
+ case 0xc5:
+ case 0xcd:
+ m_screen->update_now();
+ m_rgmod = data;
+ update_int(true);
+ break;
+
+ case 0xc6: // CNF/SYS
+ case 0xce:
+ m_ram_sys = BIT(~offset, 6);
+ if (BIT(data, 1))
+ {
+ m_turbo = BIT(data, 0);
+ update_cpu();
+ }
+ else
+ m_arom16 = BIT(data, 0);
+
+ if (BIT(data, 2))
+ {
+ m_cnf = data;
+ if (BIT(m_cnf, 5)) m_pn &= 0xc0; // CNF_PN[5..0]_RESET
+ if (BIT(m_cnf, 6)) m_sc = 0; // CNF_SC_RESET
+ if (BIT(~m_cnf, 7)) m_pn &= 0x1f; // CNF_PN[7..6]_RESET
+ }
+
+ update_memory();
+ break;
+
+ case 0xc7:
+ case 0xcf:
+ m_alt_acc = 1;
+ m_aagr = (offset & 0x300) | data;
+ m_xcnt = BIT(offset, 10, 6);
+ m_xagr = 0;
+ break;
+
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ case 0xd8: case 0xd9: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ update_memory();
+ break;
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ m_ram_pages[m_pg3] = data;
+ update_memory();
+ break;
+
+ // no port
+ case 0x00:
+ break;
+
+ default:
+ LOGIO("IOw: %02X / %X < %x\n", dcpp, offset, data);
+ break;
+ }
+}
+
+void sprinter_state::accel_control_r(u8 data)
+{
+ const bool is_prefix = (data == 0xcb) || (data == 0xdd) || (data == 0xed) || (data == 0xfd);
+ if (!is_prefix && !m_prf_d) // neither prefix nor prefixed
+ {
+ if ((((data & 0x1b) == 0x00) || ((data & 0x1b) == 0x09) || ((data & 0x1b) == 0x12) || ((data & 0x1b) == 0x1b))
+ && (((data & 0xe4) == 0x40) || ((data & 0xe4) == 0x64)))
+ {
+ m_acc_dir = ((data & 7) == OFF_HALT) ? OFF : static_cast<accel_state>(data & 7);
+ m_fn_acc = MODE_NOP;
+ }
+ else {
+ const accel_state state_candidate = static_cast<accel_state>(data);
+ switch(state_candidate)
+ {
+ case MODE_AND:
+ case MODE_XOR:
+ case MODE_OR:
+ m_fn_acc = state_candidate;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ m_prf_d = is_prefix;
+}
+
+void sprinter_state::accel_r_tap(u16 offset, u8 &data)
+{
+ const std::string_view m{(m_fn_acc == MODE_AND) ? "&" : (m_fn_acc == MODE_OR) ? "|" : (m_fn_acc == MODE_XOR) ? "^" : ""};
+ if (m_acc_dir == SET_BUFFER)
+ {
+ m_acc_cnt = data;
+ LOGACCEL("Accel buffer: %d\n", m_acc_cnt);
+ }
+ else if (m_pages[offset >> 14] & BANK_RAM_MASK) // block ops RAM only
+ {
+ const u16 acc_cnt = m_acc_cnt ? m_acc_cnt : 256;
+ if (m_acc_dir == COPY)
+ {
+ LOGACCEL("Accel rCOPY: %s%02x\n", m, offset);
+ for (auto i = 0; i < acc_cnt; i++)
+ {
+ if (i && !machine().side_effects_disabled())
+ do_cpu_wait();
+ const u16 addr = offset + i;
+ data = (m_pages[addr >> 14] & BANK_RAM_MASK) ? ram_r(addr) : 0xff;
+ update_accel_buffer(i, data);
+ }
+ }
+ else if (m_acc_dir == COPY_VERT)
+ {
+ LOGACCEL("Accel rCOPY_GR: %s%02x (%x)\n", m, offset, m_port_y);
+ for (auto i = 0; i < acc_cnt; i++)
+ {
+ if (i && !machine().side_effects_disabled())
+ do_cpu_wait();
+ data = ram_r(offset);
+ update_accel_buffer(i, data);
+ m_port_y++;
+ }
+ }
+ else if (m_acc_dir == FILL_VERT)
+ m_port_y += acc_cnt;
+ }
+}
+
+void sprinter_state::accel_w_tap(u16 offset, u8 &data)
+{
+ if (m_acc_dir == SET_BUFFER)
+ {
+ m_acc_cnt = data;
+ LOGACCEL("Accel buffer: %d\n", m_acc_cnt);
+ }
+ else if (m_pages[BIT(offset, 14, 2)] & BANK_RAM_MASK) // block ops RAM only
+ {
+ const u16 acc_cnt = m_acc_cnt ? m_acc_cnt : 256;
+ if (m_acc_dir == FILL)
+ {
+ LOGACCEL("Accel wFILL: %02x\n", offset);
+ for (auto i = 0; i < acc_cnt; i++)
+ {
+ const u16 addr = offset + i;
+ if ((m_pages[addr >> 14] & BANK_RAM_MASK) && (~m_pages[addr >> 14] & BANK_WRDISBL_MASK))
+ ram_w(addr, data);
+ }
+ }
+ else if (m_acc_dir == FILL_VERT)
+ {
+ LOGACCEL("Accel wFILL_VERT: %02x (%x)\n", offset, m_port_y);
+ for (auto i = 0; i < acc_cnt; i++)
+ {
+ ram_w(offset, data);
+ m_port_y++;
+ }
+ }
+ else if (m_acc_dir == DOUBLE)
+ {
+ ram_w(offset, data);
+ ram_w(offset ^ 1, data);
+ }
+ else if (m_acc_dir == COPY)
+ {
+ LOGACCEL("Accel wCOPY: %02x\n", offset);
+ for (auto i = 0; i < acc_cnt; i++)
+ {
+ const u16 addr = offset + i;
+ if ((m_pages[addr >> 14] & BANK_RAM_MASK) && (~m_pages[addr >> 14] & BANK_WRDISBL_MASK))
+ {
+ data = accel_buffer(i);
+ ram_w(addr, data);
+ }
+ }
+ }
+ else if (m_acc_dir == COPY_VERT)
+ {
+ LOGACCEL("Accel wCOPY_VERT: %02x (%x)\n", offset, m_port_y);
+ for (auto i = 0; i < acc_cnt; i++)
+ {
+ data = accel_buffer(i);
+ ram_w(offset, data);
+ m_port_y++;
+ }
+ }
+ else
+ return;
+
+ m_skip_write = true;
+ }
+}
+
+u8 &sprinter_state::accel_buffer(u8 idx)
+{
+ u8 ram_adr = m_acc_cnt - idx;
+ if (m_alt_acc)
+ {
+ ram_adr = m_xcnt;
+ const u16 xcnt_agr = ((m_xcnt << 8) | m_xagr) + m_aagr;
+ m_xcnt = xcnt_agr >> 8;
+ m_xagr = xcnt_agr & 0xff;
+ }
+
+ return m_accel_buffer[ram_adr];
+}
+
+void sprinter_state::update_accel_buffer(u8 idx, u8 data)
+{
+ switch (m_fn_acc)
+ {
+ case MODE_AND:
+ accel_buffer(idx) &= data;
+ break;
+ case MODE_OR:
+ accel_buffer(idx) |= data;
+ break;
+ case MODE_XOR:
+ accel_buffer(idx) ^= data;
+ break;
+ case MODE_NOP:
+ accel_buffer(idx) = data;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+u8 sprinter_state::bootstrap_r(offs_t offset)
+{
+ const u16 addr = offset & 0xffff;
+ return m_conf_loading
+ ? m_fastram[addr]
+ : m_program.read_byte(0x10000 | addr);
+}
+
+void sprinter_state::bootstrap_w(offs_t offset, u8 data)
+{
+ if (m_conf_loading)
+ {
+ m_conf_loading = 0;
+ m_conf = !(m_maincpu->csbr_r() & 0x0f); // cs0 disabled => loader reads config from fastram (which is Game Config)
+ m_ram_pages[0x2e] = m_conf ? 0x41 : 0x00;
+ machine().schedule_soft_reset();
+ }
+ else
+ m_program.write_byte(0x10000 | u16(offset), data);
+}
+
+u8 sprinter_state::ram_r(offs_t offset)
+{
+ const u8 bank = BIT(offset, 14, 2);
+ return ((m_pages[bank] & 0xf0) == 0x50)
+ ? m_ram->pointer()[(0x50 << 14) + m_port_y * 1024 + (offset & 0x3ff)]
+ : reinterpret_cast<u8 *>(m_bank_ram[bank]->base())[offset & 0x3fff];
+}
+
+void sprinter_state::ram_w(offs_t offset, u8 data)
+{
+ if (m_skip_write)
+ {
+ m_skip_write = false;
+ return;
+ }
+ do_cpu_wait();
+
+ const u8 bank = BIT(offset, 14, 2);
+ const u8 page = m_pages[bank] & 0xff;
+ if ((bank == 3) && (m_sc == 0x10) && (m_pages[3] == (BANK_RAM_MASK | 0xa0)))
+ machine().schedule_soft_reset();
+
+ if ((page & 0xf0) == 0x50)
+ {
+ const u32 vaddr = m_port_y * 1024 + (offset & 0x3ff);
+ if (BIT(~page, 2))
+ m_ram->pointer()[(0x50 << 14) + vaddr] = data;
+ if (!(BIT(page, 3) && (data == 0xff)))
+ vram_w(vaddr, data);
+ }
+ else
+ {
+ if (~m_all_mode & 1)
+ {
+ const bool sp_scr = BIT(offset, 14) && (BIT(~offset, 15) || ((m_pg3 & 0x3d) == 0x35)); // B"1101X1"
+ const bool zx_screen = sp_scr && !(BIT(offset, 13) && BIT(~m_port_y, 7)) && BIT(~m_port_y, 6);
+ if (zx_screen)
+ {
+ const bool zxa15 = BIT(offset, 15) ? BIT(m_pg3, 1) : 0; // SP_SA
+ const u8 zxs = m_port_y & 0x3f;
+ const u32 vxa = (BIT(offset, 0, 8) << 10) | (BIT(zxs, 1, 4) << 6) | ((BIT(zxs, 0) ^ zxa15 ^ BIT(offset, 13)) << 5) | BIT(offset, 8, 5);
+ vram_w(vxa, data);
+ }
+ }
+ else if ((m_acc_dir != OFF) && (page == 0xfd))
+ {
+ if (!CBL_MODE16)
+ m_cbl_data[m_cbl_wa++] = (data << 8);
+ else
+ {
+ if (m_cbl_wae)
+ m_cbl_data[m_cbl_wa] = data;
+ else
+ {
+ m_cbl_data[m_cbl_wa] |= ((data ^ 0x80) << 8);
+ m_cbl_wa++;
+ }
+ m_cbl_wae = !m_cbl_wae;
+ }
+ }
+
+ reinterpret_cast<u8 *>(m_bank_ram[bank]->base())[offset & 0x3fff] = data;
+ }
+}
+
+void sprinter_state::vram_w(offs_t offset, u8 data)
+{
+ m_screen->update_now();
+ const u16 laddr = offset & 0x3ff;
+ const bool is_int_updated = (laddr >= 0x300) && (laddr < 0x3a0) && ((offset & 0x403) == 0x400) && (((m_vram[offset] & 0xfc) == 0xfc) || ((data & 0xfc) == 0xfc)) && (m_vram[offset] ^ data);
+ m_vram[offset] = data;
+
+ const u8 col = (offset >> 3) & 0x7f;
+ const u8 row = offset >> 13;
+ m_tilemap->mark_tile_dirty(row * 128 + col);
+ m_gfxdecode->gfx(1)->mark_dirty(row * 128 * 8 + col);
+
+ if (is_int_updated)
+ update_int(true);
+ else if (laddr >= 0x3e0)
+ {
+ const u16 pen = BIT(offset, 2, 3) * 256 + (offset >> 10);
+ const u32 p_red = offset & ~0x3;
+ m_palette->set_pen_color(pen, rgb_t(m_vram[p_red], m_vram[p_red + 1], m_vram[p_red + 2]));
+ }
+}
+
+u8 sprinter_state::isa_r(offs_t offset)
+{
+ const u8 ctrl = m_ram_pages[m_pg3];
+ if ((ctrl & 0xf9) == 0xd0)
+ return BIT(ctrl, 2) // D:2 0-mem, 1-io
+ ? m_isa[BIT(ctrl, 1)]->io_r((m_isa_addr_ext << 14) | offset)
+ : m_isa[BIT(ctrl, 1)]->mem_r((m_isa_addr_ext << 14) | offset);
+
+ return 0xff;
+}
+
+void sprinter_state::isa_w(offs_t offset, u8 data)
+{
+ const u8 ctrl = m_ram_pages[m_pg3];
+ if ((ctrl & 0xf9) == 0xd0)
+ {
+ if (BIT(ctrl, 2))
+ m_isa[BIT(ctrl, 1)]->io_w((m_isa_addr_ext << 14) | offset, data);
+ else
+ m_isa[BIT(ctrl, 1)]->mem_w((m_isa_addr_ext << 14) | offset, data);
+ }
+}
+
+void sprinter_state::update_int(bool recalculate)
+{
+ if (recalculate || m_ints.empty())
+ {
+ m_ints.clear();
+ for (auto a = 0; a <= 55; a++)
+ {
+ const u8* line1 = as_mode(a, 0);
+ for (auto b = 0; b <= 39; b++)
+ {
+ if ((*line1 & 0xfd) == 0xfd)
+ m_ints.push_back({b * 8 + 23, a * 16 + 112});
+ line1 += 4;
+ }
+ }
+ }
+
+ if (!m_ints.empty())
+ {
+ attotime min = attotime::never;
+ for (std::list<std::pair<u16, u16>>::iterator it=m_ints.begin(); it != m_ints.end(); ++it)
+ min = std::min(min, m_screen->time_until_pos((*it).first, (*it).second));
+ m_irq_on_timer->adjust(min);
+ }
+}
+
+u8 sprinter_state::m1_r(offs_t offset)
+{
+ m_z80_m1 = 1;
+ u8 data = m_program.read_byte(offset);
+ m_z80_m1 = 0;
+
+ if (!machine().side_effects_disabled() && ACC_ENA)
+ accel_control_r(data);
+
+ return data;
+}
+
+void sprinter_state::map_fetch(address_map &map)
+{
+ // Overlap with previous because we want real addresses on the 3e00-3fff range
+ map(0x0000, 0x3fff).mirror(0x10000).lr8(NAME([this](offs_t offset)
+ {
+ return m1_r(offset + 0x10000);
+ }));
+ map(0x3d00, 0x3dff).mirror(0x10000).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled() && m_dos && BIT(m_pn, 4))
+ {
+ m_dos = 0;
+ update_memory();
+ }
+ return m1_r(offset + 0x13d00);
+ }));
+ map(0x4000, 0xffff).mirror(0x10000).lr8(NAME([this](offs_t offset)
+ {
+ if (!machine().side_effects_disabled() && !m_dos)
+ {
+ m_dos = 1;
+ update_memory();
+ }
+ return m1_r(offset + 0x14000);
+ }));
+}
+
+void sprinter_state::map_mem(address_map &map)
+{
+ map(0x00000, 0x3ffff).rw(FUNC(sprinter_state::bootstrap_r), FUNC(sprinter_state::bootstrap_w)); // bootstrap
+ map(0x10000, 0x1ffff).rw(FUNC(sprinter_state::ram_r), FUNC(sprinter_state::ram_w));
+
+ map(0x10000, 0x13fff).view(m_bank_view0);
+ m_bank_view0[0](0x10000, 0x13fff).nopw(); // RAM RO
+ m_bank_view0[1](0x10000, 0x13fff).nopw().bankr(m_bank_rom[0]);
+ m_bank_view0[2](0x10000, 0x13fff).bankrw(m_bank0_fastram);
+
+ map(0x1c000, 0x1ffff).view(m_bank_view3);
+ m_bank_view3[0](0x1c000, 0x1ffff).rw(FUNC(sprinter_state::isa_r), FUNC(sprinter_state::isa_w)); // ISA
+}
+
+void sprinter_state::map_io(address_map &map)
+{
+ map.unmap_value_high();
+ map(0x0000, 0xffff).rw(FUNC(sprinter_state::dcp_r), FUNC(sprinter_state::dcp_w));
+}
+
+void sprinter_state::init_taps()
+{
+ address_space &prg = m_maincpu->space(AS_PROGRAM);
+ prg.install_read_tap(0x10000, 0x1ffff, "accel_read", [this](offs_t offset, u8 &data, u8 mem_mask)
+ {
+ if (!machine().side_effects_disabled())
+ {
+ if (!(m_pages[BIT(offset, 14, 2)] & (BANK_FASTRAM_MASK | BANK_ISA_MASK))) // ROM+RAM
+ do_cpu_wait();
+ if(!m_z80_m1 && ACC_ENA && (m_acc_dir != OFF))
+ accel_r_tap(offset, data);
+ }
+ });
+ prg.install_write_tap(0x10000, 0x1ffff, "accel_write", [this](offs_t offset, u8 &data, u8 mem_mask)
+ {
+ if (!(m_pages[BIT(offset, 14, 2)] & 0xff00)) // ROM only, RAM(w) applies waits manually
+ do_cpu_wait();
+ if (!m_z80_m1 && ACC_ENA && (m_acc_dir != OFF))
+ accel_w_tap(offset, data);
+ });
+}
+
+void sprinter_state::machine_start()
+{
+ m_isa[0]->space(isa8_device::AS_ISA_IO).unmap_value_high();
+ m_isa[1]->space(isa8_device::AS_ISA_IO).unmap_value_high();
+
+ spectrum_128_state::machine_start();
+
+ save_item(NAME(m_ram_pages));
+ save_item(NAME(m_pages));
+ save_item(NAME(m_z80_m1));
+ save_item(NAME(m_conf));
+ save_item(NAME(m_conf_loading));
+ save_item(NAME(m_starting));
+ save_item(NAME(m_dos));
+ save_item(NAME(m_cash_on));
+ save_item(NAME(m_cnf));
+ save_item(NAME(m_rom_sys));
+ save_item(NAME(m_ram_sys));
+ save_item(NAME(m_sys_pg));
+ save_item(NAME(m_turbo));
+ save_item(NAME(m_turbo_hard));
+ save_item(NAME(m_arom16));
+ save_item(NAME(m_rom_rg));
+ save_item(NAME(m_pn));
+ save_item(NAME(m_sc));
+ save_item(NAME(m_all_mode));
+ save_item(NAME(m_port_y));
+ save_item(NAME(m_rgmod));
+ save_item(NAME(m_pg3));
+ save_item(NAME(m_isa_addr_ext));
+ //save_item(NAME(m_hold));
+ save_item(NAME(m_kbd_data_cnt));
+ save_item(NAME(m_ata_selected));
+ save_item(NAME(m_ata_data_latch));
+ save_item(NAME(m_skip_write));
+ save_item(NAME(m_prf_d));
+ save_item(NAME(m_acc_cnt));
+ save_item(NAME(m_accel_buffer));
+ save_item(NAME(m_alt_acc));
+ save_item(NAME(m_aagr));
+ save_item(NAME(m_xcnt));
+ save_item(NAME(m_xagr));
+ //save_item(NAME(m_acc_dir));
+ //save_item(NAME(m_fn_acc));
+ save_item(NAME(m_cbl_xx));
+ save_item(NAME(m_cbl_data));
+ save_item(NAME(m_cbl_cnt));
+ save_item(NAME(m_cbl_wa));
+ save_item(NAME(m_cbl_wae));
+
+ m_beta->enable();
+
+ // reconfigure ROMs
+ m_rom = memregion("maincpu");
+ m_bank_rom[0]->configure_entries(0, m_rom->bytes() / 0x4000, m_rom->base(), 0x4000);
+ m_bank0_fastram->configure_entries(0, m_fastram.bytes() / 0x4000, m_fastram.target(), 0x4000);
+ for (auto i = 0; i < 4; i++)
+ m_bank_ram[i]->configure_entries(0, m_ram->size() / 0x4000, m_ram->pointer(), 0x4000);
+
+ m_dcp_location = m_ram->pointer() + (0x40 << 14);
+ m_maincpu->space(AS_PROGRAM).specific(m_program);
+
+ const u8 port_default[0x40] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx - SYS PORTS COPIES
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, // Dx - RAM PAGES
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x05, 0x02, 0x41, 0xff, 0x00, 0x00, 0x41, // Ex - ROM PAGES
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // Fx - RAM PAGES
+ };
+ std::copy(std::begin(port_default), std::end(port_default), std::begin(m_ram_pages));
+
+ m_all_mode = 0x00; // c3
+ m_port_y = 0x00; // c4
+ m_rgmod = 0x00; // c5
+ m_hold = {0, 0}; // cb
+ m_conf_loading = 1;
+ m_conf = 0;
+}
+
+void sprinter_state::machine_reset()
+{
+ m_cbl_timer->adjust(attotime::never);
+
+ spectrum_128_state::machine_reset();
+
+ m_starting = 1;
+ m_dos = 1; // off
+ m_rom_sys = 0;
+ m_ram_sys = 0;
+ m_sys_pg = 0;
+ m_arom16 = 0;
+ m_cnf = 0x00;
+ m_pn = 0x00;
+ m_sc = 0x00;
+ m_rom_rg = 0x00;
+ m_cash_on = 0;
+ m_isa_addr_ext = 0;
+
+ m_skip_write = false;
+ m_prf_d = false;
+ m_acc_dir = OFF;
+ m_alt_acc = 0;
+
+ m_cbl_xx = 0;
+ m_cbl_wa = 0;
+
+ m_ata_selected = 0;
+
+ m_kbd_data_cnt = 0;
+ m_turbo_hard = 1;
+
+ if (m_conf_loading)
+ {
+ m_bank_rom[0]->set_entry(0x0c);
+ m_bank_view0.select(1);
+ m_bank_view3.disable();
+ }
+ else
+ update_memory();
+}
+
+static const gfx_layout sprinter_charlayout =
+{
+ 8, 8, // 8 x 8 characters
+ 256, // 256 characters
+ 1, // 1 bits per pixel
+ { 0 }, // no bitplanes
+ { STEP8(0, 1) }, // x offsets
+ { STEP8(0, 8) }, // y offsets
+ 1024 * 8 // every char takes 8 bytes
+};
+
+static const gfx_layout sprinter_tiles =
+{
+ 8, 8,
+ 128 * 32 * 8,
+ 8,
+ { STEP8(0, 1) },
+ { STEP8(0, 8) },
+ { STEP8(0, 1024 * 8) },
+ 8 * 8
+};
+
+static GFXDECODE_START( gfx_sprinter )
+ GFXDECODE_RAM( "vram", 0x2c0, sprinter_charlayout, 0x70f, 1 )
+ GFXDECODE_RAM( "vram", 0, sprinter_tiles, 0x100, 256 )
+GFXDECODE_END
+
+TILE_GET_INFO_MEMBER(sprinter_state::get_tile_info)
+{
+ const u8 col = tile_index % 128;
+ const u8 row = tile_index / 128;
+ tileinfo.set(1, row * 128 * 8 + col, 0, 0);
+}
+
+void sprinter_state::video_start()
+{
+ spectrum_state::video_start();
+
+ m_tilemap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(sprinter_state::get_tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 128, 32);
+
+ m_contention_pattern = {};
+ init_taps();
+
+ m_cbl_timer = timer_alloc(FUNC(sprinter_state::cbl_tick), this);
+}
+
+static void sprinter_ata_devices(device_slot_interface &device)
+{
+ device.option_add("hdd", IDE_HARDDISK);
+ device.option_add("cdrom", ATAPI_CDROM);
+}
+
+u8 sprinter_state::kbd_fe_r(offs_t offset)
+{
+ u8 data = 0xff;
+
+ u8 oi = offset >> 8;
+ u8 shifts = 0xff;
+ for (u8 i = 0; i < 8; i++, oi >>= 1)
+ {
+ const u8 line_data = m_io_line[i]->read();
+ shifts &= line_data;
+ if ((oi & 1) == 0)
+ data &= line_data;
+ }
+
+ if (((offset & 0x0100) == 0) && BIT(~shifts, 6))
+ data &= ~0x01; // CS
+
+ if (((offset & 0x8000) == 0) && BIT(~shifts, 7))
+ data &= ~0x02; // SS
+
+ data |= 0xe0;
+ data ^= 0x40;
+
+ /* cassette input from wav */
+ if (m_cassette->input() > 0.0038 )
+ data &= ~0x40;
+
+ if (CBL_MODE)
+ {
+ data &= ~0xa0;
+ data |= (m_screen->vpos() >= (SPRINT_BORDER_TOP + SPRINT_SCREEN_YSIZE)) << 5;
+ data |= (CBL_INT_ENA ? (m_cbl_cnt ^ m_cbl_wa) : m_cbl_cnt) & 0x80;
+ }
+
+ return data;
+}
+
+void sprinter_state::on_kbd_data(int state)
+{
+ if (state && ((m_all_mode & 0x09) == 0x09))
+ {
+ m_kbd_data_cnt++;
+ m_kbd_data_cnt %= 11;
+ if (!m_kbd_data_cnt)
+ irq_on(0);
+ }
+}
+
+void sprinter_state::do_cpu_wait(bool is_io)
+{
+ if ((m_turbo && m_turbo_hard))
+ {
+ u8 count = is_io ? 4 : 3;
+ const u8 over = m_maincpu->total_cycles() % count;
+ count = count + (over ? (count - over) : 0);
+ m_maincpu->adjust_icount(-count);
+ }
+}
+
+TIMER_CALLBACK_MEMBER(sprinter_state::irq_on)
+{
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+ m_irq_off_timer->adjust(attotime::from_ticks(32, m_maincpu->unscaled_clock()));
+ update_int(false);
+}
+
+TIMER_CALLBACK_MEMBER(sprinter_state::irq_off)
+{
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+}
+
+TIMER_CALLBACK_MEMBER(sprinter_state::cbl_tick)
+{
+ const u16 left = m_cbl_data[m_cbl_cnt++];
+ m_ldac->write(left);
+ m_rdac->write(CBL_STEREO ? m_cbl_data[m_cbl_cnt++] : left);
+
+ if (CBL_INT_ENA && !(m_cbl_cnt & 0x7f))
+ {
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
+ m_irq_off_timer->adjust(attotime::never);
+ }
+}
+
+INPUT_CHANGED_MEMBER(sprinter_state::turbo_changed)
+{
+ m_turbo_hard = !m_turbo_hard;
+ update_cpu();
+
+ popmessage("Turbo %s\n", (m_turbo && m_turbo_hard) ? "ON" : "OFF");
+}
+
+INPUT_PORTS_START( sprinter )
+ /* PORT_NAME = KEY Mode CAPS Mode SYMBOL Mode EXT Mode EXT+Shift Mode BASIC Mode */
+ PORT_START("IO_LINE0") /* 0xFEFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CAPS SHIFT") PORT_CODE(KEYCODE_LSHIFT) PORT_CODE(KEYCODE_RSHIFT) PORT_CHAR(UCHAR_SHIFT_1) PORT_CHAR(UCHAR_SHIFT_2)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("z Z : LN BEEP COPY") PORT_CODE(KEYCODE_Z) PORT_CHAR('z') PORT_CHAR('Z') PORT_CHAR(':')
+ PORT_CODE(KEYCODE_BACKSLASH)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("x X \xC2\xA3 EXP INK CLEAR") PORT_CODE(KEYCODE_X) PORT_CHAR('x') PORT_CHAR('X') PORT_CHAR(0xA3)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("c C ? LPRINT PAPER CONT") PORT_CODE(KEYCODE_C) PORT_CHAR('c') PORT_CHAR('C') PORT_CHAR('?')
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("v V / LLIST FLASH CLS") PORT_CODE(KEYCODE_V) PORT_CHAR('v') PORT_CHAR('V') PORT_CHAR('/')
+ PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line0")
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line0") PORT_CODE(KEYCODE_BACKSLASH) PORT_CODE(KEYCODE_SLASH) PORT_CODE(KEYCODE_SLASH_PAD)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE1") /* 0xFDFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("a A STOP READ ~ NEW") PORT_CODE(KEYCODE_A) PORT_CHAR('a') PORT_CHAR('A')// PORT_CHAR('~')
+ PORT_CODE(KEYCODE_TILDE)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("s S NOT RESTORE | SAVE") PORT_CODE(KEYCODE_S) PORT_CHAR('s') PORT_CHAR('S')// PORT_CHAR('|')
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("d D STEP DATA \\ DIM") PORT_CODE(KEYCODE_D) PORT_CHAR('d') PORT_CHAR('D')// PORT_CHAR('\\')
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("f F TO SGN { FOR") PORT_CODE(KEYCODE_F) PORT_CHAR('f') PORT_CHAR('F')// PORT_CHAR('{')
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("g G THEN ABS } GOTO") PORT_CODE(KEYCODE_G) PORT_CHAR('g') PORT_CHAR('G')// PORT_CHAR('}')
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line1")
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line1") PORT_CODE(KEYCODE_TILDE)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE2") /* 0xFBFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("q Q <= SIN ASN PLOT") PORT_CODE(KEYCODE_Q) PORT_CHAR('q') PORT_CHAR('Q')
+ PORT_CODE(KEYCODE_HOME)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("w W <> COS ACS DRAW") PORT_CODE(KEYCODE_W) PORT_CHAR('w') PORT_CHAR('W')
+ PORT_CODE(KEYCODE_INSERT)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("e E >= TAN ATN REM") PORT_CODE(KEYCODE_E) PORT_CHAR('e') PORT_CHAR('E')
+ PORT_CODE(KEYCODE_END)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("r R < INT VERIFY RUN") PORT_CODE(KEYCODE_R) PORT_CHAR('r') PORT_CHAR('R') PORT_CHAR('<')
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("t T > RND MERGE RAND") PORT_CODE(KEYCODE_T) PORT_CHAR('t') PORT_CHAR('T') PORT_CHAR('>')
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line2")
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line2") PORT_CODE(KEYCODE_HOME) PORT_CODE(KEYCODE_INSERT) PORT_CODE(KEYCODE_END)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE3") /* 0xF7FE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("1 EDIT ! BLUE DEF FN") PORT_CODE(KEYCODE_1) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CHAR('1') PORT_CHAR('!')
+ PORT_CODE(KEYCODE_F1)
+ PORT_CODE(KEYCODE_TAB)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("2 CAPS LOCK @ RED FN") PORT_CODE(KEYCODE_2) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CHAR('2') PORT_CHAR('@')
+ PORT_CODE(KEYCODE_F2)
+ PORT_CODE(KEYCODE_CAPSLOCK)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("3 TRUE VID # MAGENTA LINE") PORT_CODE(KEYCODE_3) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CHAR('3') PORT_CHAR('#')
+ PORT_CODE(KEYCODE_F3)
+ PORT_CODE(KEYCODE_PGUP)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("4 INV VID $ GREEN OPEN#") PORT_CODE(KEYCODE_4) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CHAR('4') PORT_CHAR('$')
+ PORT_CODE(KEYCODE_F4)
+ PORT_CODE(KEYCODE_PGDN)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("5 Left % CYAN CLOSE#") PORT_CODE(KEYCODE_5) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CHAR('5') PORT_CHAR('%')
+ PORT_CODE(KEYCODE_F5)
+ PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line3") PORT_CODE(KEYCODE_TAB) PORT_CODE(KEYCODE_CAPSLOCK) PORT_CODE(KEYCODE_PGUP) PORT_CODE(KEYCODE_PGDN) PORT_CODE(KEYCODE_LEFT) PORT_CODE(KEYCODE_4_PAD)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line3")
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE4") /* 0xEFFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("0 DEL _ BLACK FORMAT") PORT_CODE(KEYCODE_0) PORT_CHAR(UCHAR_MAMEKEY(F10)) PORT_CHAR('0') PORT_CHAR('_')
+ PORT_CODE(KEYCODE_F10)
+ PORT_CODE(KEYCODE_BACKSPACE)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("9 GRAPH ) POINT") PORT_CODE(KEYCODE_9) PORT_CHAR(UCHAR_MAMEKEY(F9)) PORT_CHAR('9') PORT_CHAR(')')
+ PORT_CODE(KEYCODE_F9)
+ PORT_CODE(KEYCODE_DEL)
+ PORT_CODE(KEYCODE_CLOSEBRACE)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("8 Right ( CAT") PORT_CODE(KEYCODE_8) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CHAR('8') PORT_CHAR('(')
+ PORT_CODE(KEYCODE_F8)
+ PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD)
+ PORT_CODE(KEYCODE_OPENBRACE)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("7 Up ' WHITE ERASE") PORT_CODE(KEYCODE_7) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CHAR('7') PORT_CHAR('\'')
+ PORT_CODE(KEYCODE_F7)
+ PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("6 Down & YELLOW MOVE") PORT_CODE(KEYCODE_6) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CHAR('6') PORT_CHAR('&')
+ PORT_CODE(KEYCODE_F6)
+ PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line4") PORT_CODE(KEYCODE_BACKSPACE) PORT_CODE(KEYCODE_DEL) PORT_CODE(KEYCODE_RIGHT) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_UP) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_DOWN) PORT_CODE(KEYCODE_2_PAD)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line4") PORT_CODE(KEYCODE_CLOSEBRACE) PORT_CODE(KEYCODE_OPENBRACE)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE5") /* 0xDFFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("p P \" TAB (c) PRINT") PORT_CODE(KEYCODE_P) PORT_CHAR('p') PORT_CHAR('P') PORT_CHAR('"')
+ PORT_CODE(KEYCODE_QUOTE)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("o O ; PEEK OUT POKE") PORT_CODE(KEYCODE_O) PORT_CHAR('o') PORT_CHAR('O') PORT_CHAR(';')
+ PORT_CODE(KEYCODE_COLON)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("i I AT CODE IN INPUT") PORT_CODE(KEYCODE_I) PORT_CHAR('i') PORT_CHAR('I')
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("u U OR CHR$ ] IF") PORT_CODE(KEYCODE_U) PORT_CHAR('u') PORT_CHAR('U')// PORT_CHAR(']')
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("y Y AND STR$ [ RETURN") PORT_CODE(KEYCODE_Y) PORT_CHAR('y') PORT_CHAR('Y')// PORT_CHAR('[')
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line5")
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line5") PORT_CODE(KEYCODE_QUOTE) PORT_CODE(KEYCODE_COLON)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE6") /* 0xBFFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("ENTER") PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CHAR(13)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("l L = USR ATTR LET") PORT_CODE(KEYCODE_L) PORT_CHAR('l') PORT_CHAR('L') PORT_CHAR('=')
+ PORT_CODE(KEYCODE_EQUALS)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("k K + LEN SCREEN$ LIST") PORT_CODE(KEYCODE_K) PORT_CHAR('k') PORT_CHAR('K') PORT_CHAR('+')
+ PORT_CODE(KEYCODE_PLUS_PAD)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("j J - VAL VAL$ LOAD") PORT_CODE(KEYCODE_J) PORT_CHAR('j') PORT_CHAR('J') PORT_CHAR('-')
+ PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("h H ^ SQR CIRCLE GOSUB") PORT_CODE(KEYCODE_H) PORT_CHAR('h') PORT_CHAR('H') PORT_CHAR('^')
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line6")
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line6") PORT_CODE(KEYCODE_EQUALS) PORT_CODE(KEYCODE_MINUS) PORT_CODE(KEYCODE_MINUS_PAD) PORT_CODE(KEYCODE_PLUS_PAD)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("IO_LINE7") /* 0x7FFE */
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SPACE") PORT_CODE(KEYCODE_SPACE) PORT_CHAR(' ')
+ PORT_CODE(KEYCODE_ESC)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SYMBOL SHIFT") PORT_CODE(KEYCODE_RCONTROL) PORT_CODE(KEYCODE_LCONTROL) PORT_CHAR(UCHAR_MAMEKEY(LCONTROL)) PORT_CHAR(UCHAR_MAMEKEY(RCONTROL))
+ PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("m M . PI INVERSE PAUSE") PORT_CODE(KEYCODE_M) PORT_CHAR('m') PORT_CHAR('M') PORT_CHAR('.')
+ PORT_CODE(KEYCODE_STOP)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("n N , INKEY$ OVER NEXT") PORT_CODE(KEYCODE_N) PORT_CHAR('n') PORT_CHAR('N') PORT_CHAR(',')
+ PORT_CODE(KEYCODE_COMMA)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("b B * BIN BRIGHT BORDER") PORT_CODE(KEYCODE_B) PORT_CHAR('b') PORT_CHAR('B') PORT_CHAR('*')
+ PORT_CODE(KEYCODE_ASTERISK)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("CS Line7") PORT_CODE(KEYCODE_ESC) PORT_CODE(KEYCODE_LALT) PORT_CODE(KEYCODE_RALT)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SS Line7") PORT_CODE(KEYCODE_STOP) PORT_CODE(KEYCODE_COMMA) PORT_CODE(KEYCODE_ASTERISK)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+
+
+ PORT_START("mouse_input1")
+ PORT_BIT(0xff, 0, IPT_MOUSE_X) PORT_SENSITIVITY(30)
+
+ PORT_START("mouse_input2")
+ PORT_BIT(0xff, 0, IPT_MOUSE_Y) PORT_INVERT PORT_SENSITIVITY(30)
+
+ PORT_START("mouse_input3")
+ PORT_BIT(0xf8, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON4) PORT_NAME("Left mouse button") PORT_CODE(MOUSECODE_BUTTON1)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_BUTTON5) PORT_NAME("Right mouse button") PORT_CODE(MOUSECODE_BUTTON2)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_BUTTON6) PORT_NAME("Middle mouse button") PORT_CODE(MOUSECODE_BUTTON3)
+
+
+ PORT_START("NMI")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("NMI") PORT_CODE(KEYCODE_F11)
+
+ PORT_START("TURBO")
+ PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD) PORT_NAME("Turbo") PORT_CODE(KEYCODE_F12) PORT_TOGGLE PORT_CHANGED_MEMBER(DEVICE_SELF, sprinter_state, turbo_changed, 0)
+ PORT_BIT(0xfe, IP_ACTIVE_HIGH, IPT_UNUSED)
+INPUT_PORTS_END
+
+void sprinter_state::sprinter(machine_config &config)
+{
+ spectrum_128(config);
+ config.device_remove("palette");
+
+ m_ram->set_default_size("64M");
+
+ Z84C015(config.replace(), m_maincpu, X_SP / 12); // 3.5MHz default
+ m_maincpu->set_m1_map(&sprinter_state::map_fetch);
+ m_maincpu->set_memory_map(&sprinter_state::map_mem);
+ m_maincpu->set_io_map(&sprinter_state::map_io);
+ m_maincpu->nomreq_cb().set_nop();
+ m_maincpu->set_irq_acknowledge_callback(NAME([](device_t &, int){ return 0xff; }));
+ m_maincpu->irqack_cb().set(FUNC(sprinter_state::irq_off));
+
+ ISA8(config, m_isa[0], 0);
+ m_isa[0]->set_custom_spaces();
+ zxbus_device &zxbus(ZXBUS(config, "zxbus", 0));
+ zxbus.set_iospace(m_isa[0], isa8_device::AS_ISA_IO);
+ ZXBUS_SLOT(config, "zxbus2isa", 0, "zxbus", zxbus_cards, nullptr);
+
+ ISA8(config, m_isa[1], 0);
+ m_isa[1]->set_custom_spaces();
+ ISA8_SLOT(config, "isa8", 0, m_isa[1], pc_isa8_cards, nullptr, false);
+
+ m_screen->set_raw(X_SP / 3, SPRINT_WIDTH, SPRINT_HEIGHT, { 0, SPRINT_XVIS - 1, 0, SPRINT_YVIS - 1 });
+ m_screen->set_screen_update(FUNC(sprinter_state::screen_update));
+
+ PALETTE(config, "palette", palette_device::BLACK).set_entries(256 * 8);
+
+ PC_KBDC(config, m_kbd, pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL);
+ m_kbd->out_data_cb().set(m_maincpu, FUNC(z84c015_device::rxa_w)); // KBD_DATR
+ m_kbd->out_clock_cb().set(m_maincpu, FUNC(z84c015_device::rxca_w)); // KBD_CLKR
+ m_kbd->out_clock_cb().append(m_maincpu, FUNC(z84c015_device::txca_w));
+ m_kbd->out_clock_cb().append(FUNC(sprinter_state::on_kbd_data));
+
+ m_maincpu->set_clk_trg<0>(X_SP / 48);
+ m_maincpu->set_clk_trg<1>(X_SP / 48);
+ m_maincpu->set_clk_trg<2>(X_SP / 48);
+
+ rs232_port_device &m_rs232(RS232_PORT(config, "rs232", default_rs232_devices, "microsoft_mouse"));
+ m_rs232.option_add("microsoft_mouse", MSFT_HLE_SERIAL_MOUSE);
+ m_rs232.option_add("logitech_mouse", LOGITECH_HLE_SERIAL_MOUSE);
+ m_rs232.option_add("wheel_mouse", WHEEL_HLE_SERIAL_MOUSE);
+ m_rs232.rxd_handler().set(m_maincpu, FUNC(z84c015_device::rxb_w)); // MOUSE_D
+ m_maincpu->out_txdb_callback().set("rs232", FUNC(rs232_port_device::write_txd)); // TXDB
+ m_maincpu->zc_callback<0>().set(m_maincpu, FUNC(z84c015_device::rxcb_w)); // CLK_COM1
+ m_maincpu->zc_callback<0>().append(m_maincpu, FUNC(z84c015_device::txcb_w));
+ m_maincpu->zc_callback<2>().set(m_maincpu, FUNC(z84c015_device::trg3));
+
+ DS12885(config, m_rtc, XTAL(32'768)); // should be DS12887A
+ ATA_INTERFACE(config, m_ata[0]).options(sprinter_ata_devices, "hdd", "hdd", false);
+ ATA_INTERFACE(config, m_ata[1]).options(sprinter_ata_devices, "hdd", "hdd", false);
+
+ BETA_DISK(config, m_beta, 0);
+
+ SPEAKER(config, "lspeaker").front_left();
+ SPEAKER(config, "rspeaker").front_right();
+
+ ay8910_device &ay8910(AY8910(config.replace(), "ay8912", X_SP / 24));
+ ay8910.add_route(0, "lspeaker", 0.50);
+ ay8910.add_route(1, "lspeaker", 0.25);
+ ay8910.add_route(1, "rspeaker", 0.25);
+ ay8910.add_route(2, "rspeaker", 0.50);
+
+ DAC_16BIT_R2R(config, m_ldac, 0).add_route(ALL_OUTPUTS, "lspeaker", 0.5);
+ DAC_16BIT_R2R(config, m_rdac, 0).add_route(ALL_OUTPUTS, "rspeaker", 0.5);
+
+ subdevice<gfxdecode_device>("gfxdecode")->set_info(gfx_sprinter);
+}
+
+
+ROM_START( sprinter )
+ ROM_REGION(0x040000, "maincpu", ROMREGION_ERASEFF)
+ ROM_DEFAULT_BIOS("v3.04.253")
+
+ ROM_SYSTEM_BIOS(0, "v2.13.251", "BIOS v2.13, SETUP v251") // 11.10.2002
+ ROMX_LOAD( "sp2k-2.13.251.rom", 0x000000, 0x40000, CRC(6495575f) SHA1(a9ca06b27e7c5b2b5b9ff8fc2d19ee24ed64c258), ROM_BIOS(0))
+
+ ROM_SYSTEM_BIOS(1, "v2.17.252", "BIOS v2.17, SETUP v252") // 03.03.2002
+ ROMX_LOAD( "sp2k-2.17.252.rom", 0x000000, 0x40000, CRC(3c7f1025) SHA1(d5c3d10b3b67f9ef87d3ce8a52ae3c33b95b9171), ROM_BIOS(1))
+
+ ROM_SYSTEM_BIOS(2, "v3.00.253", "BIOS v3.00, SETUP v253") // 04.10.2002
+ ROMX_LOAD( "sp2k-3.00.253.rom", 0x000000, 0x40000, CRC(193de3da) SHA1(428dcb1253a88e7b5aedcd68b5bf6d2487592e10), ROM_BIOS(2))
+
+ ROM_SYSTEM_BIOS(3, "v3.03.253", "BIOS v3.03, SETUP v253") // 02.05.2003
+ ROMX_LOAD( "sp2k-3.03.253.rom", 0x000000, 0x40000, CRC(fe26f578) SHA1(ef6d0fe4ec1bae7bda572a4fb3b9497a8910b885), ROM_BIOS(3))
+
+ ROM_SYSTEM_BIOS(4, "v3.04.253", "BIOS v3.04, SETUP v253") // 06.16.2003
+ ROMX_LOAD( "sp2k-3.04.253.rom", 0x000000, 0x40000, CRC(1729cb5c) SHA1(fb4c9f80651aa87526f141839fb4d6cb86b654c7), ROM_BIOS(4))
+ROM_END
+
+} // Anonymous namespace
+
+
+/* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS */
+// 1996 - Sp97 Prototype
+COMP( 2000, sprinter, spec128, 0, sprinter, sprinter, sprinter_state, empty_init, "Peters Plus, Ivan Mak", "Sprinter Sp2000", 0)
diff --git a/src/osd/modules/debugger/debuggdbstub.cpp b/src/osd/modules/debugger/debuggdbstub.cpp
index cbaeccb4bfa..409b6fa3bbb 100644
--- a/src/osd/modules/debugger/debuggdbstub.cpp
+++ b/src/osd/modules/debugger/debuggdbstub.cpp
@@ -477,6 +477,7 @@ static const std::map<std::string, const gdb_register_map &> gdb_register_maps =
{ "m68020pmmu", gdb_register_map_m68020pmmu },
{ "m68000", gdb_register_map_m68000 },
{ "z80", gdb_register_map_z80 },
+ { "z84c015", gdb_register_map_z80 },
{ "m6502", gdb_register_map_m6502 },
{ "rp2a03", gdb_register_map_m6502 },
{ "m6809", gdb_register_map_m6809 },