summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/spectrum/mface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/spectrum/mface.cpp')
-rw-r--r--src/devices/bus/spectrum/mface.cpp673
1 files changed, 589 insertions, 84 deletions
diff --git a/src/devices/bus/spectrum/mface.cpp b/src/devices/bus/spectrum/mface.cpp
index 148e06871cc..1c79316bcc9 100644
--- a/src/devices/bus/spectrum/mface.cpp
+++ b/src/devices/bus/spectrum/mface.cpp
@@ -17,16 +17,26 @@
© Romantic Robot UK Ltd 1985
+ Compatibility (real hardware)
+ -------------
+ mface1 models work with 48K, 128K/+2 (only in 48K mode), don't work with +2A/+3
+ mface128 models work with 48K/128K/+2, don't work with +2A/+3
+ mface3 works only with +2A/+3
+ mprint works with 48K/128K/+2, doesn't work with +2A/+3
+ incompatibility is due mainly to expansion slot pinout changes on last Amstrad built models.
+
+
Multiface One
-------------
Many versions exist, a very good source of info is: https://x128.speccy.cz/multiface/multiface.htm
Summary:
- Earliest version has 2KB of RAM, composite video output, and no toolkit (pokes only).
- Next version has 8KB of RAM, composite video output, and basic toolkit (including pokes).
+ Earliest version has 2KB of RAM, composite video output, and no pokes or toolkit.
+ Next version has 8KB of RAM, composite video output, and basic toolkit (just pokes).
Latest and most common version dropped the composite video output, added an enable/disable switch, and full-featured toolkit.
A special (and rare) version supports the Kempston Disc interface but drops Beta support. (not sold in stores, available only on request).
- At some point during "early" revisions, the page out port changed from 0x5f to 0x1f.
+ At some point the page out port changed from 0x5f to 0x1f (rev 2.0 pcb?)
+ Three pcb revs are known: 1.2, 2.0, 2.1.
Various clone/hacked rom versions are known to exist as well.
Roms:
@@ -34,12 +44,16 @@
The two hex digits are the rom checksum.
With the MF menu on-screen, press Symbol Shift + A (STOP) to see checksum, space to return.
+ The "joystick disable" jumper is required for use with Beta disk which would otherwise not work due to port clash.
+ Real world mf1/beta combo only works spectrum->mf1->beta, with jumper open, the user manual suggests Beta users may like to fit a switch instead.
+ Some confusion exactly how this works, as an original-looking schematic shows the jumper simply setting data bits 6,7 hi-z, with the actual joystick still operational.
+ This isn't enough to stop the clash so perhaps schematic is for some non-Beta-supporting version or has an error.
+ Assume for now the entire joystick is disabled as that's the only way Beta can work.
+ Beta support seems limited to Beta v3/plus (TR-DOS 3/4.xx) models, beta128 (TR-DOS 5.xx) doesn't work.
+ BIOS rom "mu21e7" has issues with Betaplus, doesn't work with betav3,beta128,betacbi, perhaps it's for some other interface entirely...?
+
The enable/disable switch became necessary on later versions as games had started including checks to detect presence of the interface.
eg. Renegade ("The Hit Squad" re-release) whilst loading, reads from 0x9f specifically to cause the MF (if present) to page in and crash the machine.
- Todo: confirm exact operation of disable switch.
-
- As mentioned in the user instructions, there is a "joystick disable" jumper inside the unit which must be cut to allow Beta disk compatibility.
- The joystick is not actually disabled but rather just data bus bits D6 + D7 are held hi-z for any reads of kempston range 0b000xxxxx.
rom maps to 0x0000
ram maps to 0x2000
@@ -55,27 +69,51 @@
Multiface 128
-------------
128K/+2 support (also works with 48K)
- DISCiPLE/Plus D disc support
+ Software enable/disable (in menu, press 'o' to toggle on/off)
+ Some new 128k-exclusive menu features when running in 128k mode (mode is auto-detected)
No joystick port
- Software enable/disable
- "Hypertape" recording (?)
+ No Beta support
+ Pass-through connector was optional
+ Original hardware version isn't compatible with DISCiPLE/+D (port clash), has "hypertape" high-speed tape recording
+ Second hardware version added DISCiPLE/+D compatibility and support as save device (uses different ports) but lost hypertape support
- Todo ...
+ supported devices rom
+ -----+----------------------------------------+-------------
+ v1 Tape, Hypertape, Microdrive, Opus 87.1, 87.12
+ v2 Tape, Microdrive, Opus, DISCiPLE/+D 87.2
+
+ I/O R/W v1 v2
+ ---------+-----+-----------+-----------
+ page in R 0x9f 0xbf
+ page out R 0x1f 0x3f
+ nmi reset W 0x9f 0xbf
+ hide W 0x1f 0x3f
Multiface 3
-----------
- +2A/+2B/+3/3B support (doesn't work with 48K/128K/+2)
+ +2A/+2B/+3/+3B support (doesn't work with 48K/128K/+2)
+3 disk support
-
- Todo ...
+ Pass-through connector was optional
+ No support for older external disk interfaces (Beta, Opus, +D etc.)
+ Pcb/logic is quite different to other models, has a PAL16L8 and 74LS670 4x4 register file.
+ A schematic created from the Hard Mirco clone pcb is available, used for various modern "re-creation" projects,
+ official version assumed to be very similar if not identical.
+ Hard Mirco clone has double size ROM (16K), but contains two identical 8K halves of data. Pcb does have ability to select rom half via software,
+ so perhaps an unused multi-bios feature? (unknown if unique to clone pcb or present on official pcb also).
Multiprint
----------
- Version ? multiface with Centronics printer interface
+ Printer interface with some mface features
+ No backup/snapshot feature
+ Has all other usual mface features: cheat/poke, 8KB RAM, magic button etc.
+ Centronics parallel printer port
+ Kempston E and ZX Lprint compatible (LPRINT, LLIST, COPY etc.)
+ Unique "REM" command set (commands "hidden" in rem comments)
+ Pass-through connector was optional
- Todo ...
+ Todo: find other print command hooks, only magic button triggered menu works for now...
*********************************************************************/
@@ -88,7 +126,11 @@
DEVICE DEFINITIONS
***************************************************************************/
+DEFINE_DEVICE_TYPE(SPECTRUM_MFACE1V1, spectrum_mface1v1_device, "spectrum_mface1v1", "Multiface One v1")
+DEFINE_DEVICE_TYPE(SPECTRUM_MFACE1V2, spectrum_mface1v2_device, "spectrum_mface1v2", "Multiface One v2")
+DEFINE_DEVICE_TYPE(SPECTRUM_MFACE1V3, spectrum_mface1v3_device, "spectrum_mface1v3", "Multiface One v3")
DEFINE_DEVICE_TYPE(SPECTRUM_MFACE1, spectrum_mface1_device, "spectrum_mface1", "Multiface One")
+DEFINE_DEVICE_TYPE(SPECTRUM_MFACE128V1, spectrum_mface128v1_device, "spectrum_mface128v1", "Multiface 128 v1")
DEFINE_DEVICE_TYPE(SPECTRUM_MFACE128, spectrum_mface128_device, "spectrum_mface128", "Multiface 128")
DEFINE_DEVICE_TYPE(SPECTRUM_MFACE3, spectrum_mface3_device, "spectrum_mface3", "Multiface 3")
DEFINE_DEVICE_TYPE(SPECTRUM_MPRINT, spectrum_mprint_device, "spectrum_mprint", "MultiPrint")
@@ -100,17 +142,17 @@ DEFINE_DEVICE_TYPE(SPECTRUM_MPRINT, spectrum_mprint_device, "spectrum_mprint", "
INPUT_PORTS_START( mface )
PORT_START("BUTTON")
- PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Red Button") PORT_CODE(KEYCODE_BACKSPACE) PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_mface_base_device, magic_button, 0)
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_BUTTON1) PORT_NAME("Red Button") PORT_CODE(KEYCODE_PLUS_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, spectrum_mface_base_device, magic_button, 0)
INPUT_PORTS_END
-INPUT_PORTS_START( mface1 )
+INPUT_PORTS_START( mface1v2 )
PORT_INCLUDE( mface )
PORT_START("CONFIG")
- PORT_CONFNAME(0x01, 0x00, "Joystick Enable Jumper")
+ PORT_CONFNAME(0x02, 0x00, "Joystick Enable Jumper")
PORT_CONFSETTING(0x00, "Closed")
- PORT_CONFSETTING(0x01, "Open")
- PORT_BIT(0xfe, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_CONFSETTING(0x02, "Open")
+ PORT_BIT(0xfd, IP_ACTIVE_LOW, IPT_UNUSED)
PORT_START("JOY")
PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT) PORT_8WAY
@@ -120,6 +162,16 @@ INPUT_PORTS_START( mface1 )
PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON1)
INPUT_PORTS_END
+INPUT_PORTS_START( mface1 )
+ PORT_INCLUDE( mface1v2 )
+
+ PORT_MODIFY("CONFIG")
+ PORT_CONFNAME(0x01, 0x00, "Interface Enable Switch")
+ PORT_CONFSETTING(0x00, "Enabled")
+ PORT_CONFSETTING(0x01, "Disabled")
+ PORT_BIT(0xfc, IP_ACTIVE_LOW, IPT_UNUSED)
+INPUT_PORTS_END
+
//-------------------------------------------------
// input_ports - device-specific input ports
//-------------------------------------------------
@@ -129,6 +181,11 @@ ioport_constructor spectrum_mface_base_device::device_input_ports() const
return INPUT_PORTS_NAME(mface);
}
+ioport_constructor spectrum_mface1v2_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(mface1v2);
+}
+
ioport_constructor spectrum_mface1_device::device_input_ports() const
{
return INPUT_PORTS_NAME(mface1);
@@ -138,50 +195,75 @@ ioport_constructor spectrum_mface1_device::device_input_ports() const
// ROM( mface )
//-------------------------------------------------
-ROM_START(mface1)
+ROM_START(mface1v1) // pcb rev 1.2
ROM_REGION(0x2000, "rom", 0)
- ROM_DEFAULT_BIOS("mu21e7")
- ROM_SYSTEM_BIOS(0, "mu20fe", "MU 2.0 FE") // pokes only (no toolkit)
- ROMX_LOAD("mf1_20_fe.rom", 0x0000, 0x2000, CRC(fa1b8b0d) SHA1(20cd508b0143166558a7238c7a9ccfbe37b90b0d), ROM_BIOS(0))
- ROM_SYSTEM_BIOS(1, "mu2167", "MU 2.1 67") // Kempston Disc support, no Beta support
- ROMX_LOAD("mf1_21_67.rom", 0x0000, 0x2000, CRC(d720ec1b) SHA1(91a40d8f503ef825df3e2ed712897dbf4ca3671d), ROM_BIOS(1))
- ROM_SYSTEM_BIOS(2, "mu21e4", "MU 2.1 E4") // the most common version
- ROMX_LOAD("mf1_21_e4.rom", 0x0000, 0x2000, CRC(4b31a971) SHA1(ba28754a3cc31a4ca579829ed4310c313409cf5d), ROM_BIOS(2))
- ROM_SYSTEM_BIOS(3, "mu21e7", "MU 2.1 E7") // last known version?
- ROMX_LOAD("mf1_21_e7.rom", 0x0000, 0x2000, CRC(670f0ec2) SHA1(50fba2d628f3a2e9219f72980e4efd62fc9ec1f8), ROM_BIOS(3))
- ROM_SYSTEM_BIOS(4, "bc96", "Brazilian clone 96") // Brazilian clone, unknown pcb version
- ROMX_LOAD("mf1_bc_96.rom", 0x0000, 0x2000, CRC(e6fe4507) SHA1(a7e03e7fee3aa05ce1501072aab4534ea7bae257), ROM_BIOS(4))
+ ROM_DEFAULT_BIOS("mu12cb")
+ ROM_SYSTEM_BIOS(0, "mu12cb", "MU12 CB") // 2KB RAM, no pokes or toolkit, page out port 0x5f
+ ROMX_LOAD("mf1_12_cb.rom", 0x0000, 0x2000, CRC(c88fbf9f) SHA1(c3018d1b495b8bc0a135038db0987de7091c9d4c), ROM_BIOS(0))
ROM_END
-/* Todo ...
-
- ROM_SYSTEM_BIOS(?, "mu12cb", "MU12 CB") // Very early version, 2KB RAM, page out port 0x5F
- ROMX_LOAD("mf1_12_cb.rom", 0x0000, 0x2000, CRC(c88fbf9f) SHA1(c3018d1b495b8bc0a135038db0987de7091c9d4c), ROM_BIOS(?))
+ROM_START(mface1v2) // unknown pcb rev (probably <2.0)
+ ROM_REGION(0x2000, "rom", 0)
+ ROM_DEFAULT_BIOS("muxx23")
+ ROM_SYSTEM_BIOS(0, "muxx23", "MU x.x 23") // 8KB RAM, pokes only (no toolkit), page out port 0x5f
+ ROMX_LOAD("mf1_xx_23.rom", 0x0000, 0x2000, CRC(d4ae8953) SHA1(b442eb634a72fb63f1ccbbd0021a7a581152888d), ROM_BIOS(0))
+ROM_END
- ROM_SYSTEM_BIOS(?, "mu2023", "MU 2.0 23") // pokes only (no toolkit), page out port 0x5F
- ROMX_LOAD("mf1_20_23.rom", 0x0000, 0x2000, CRC(d4ae8953) SHA1(b442eb634a72fb63f1ccbbd0021a7a581152888d), ROM_BIOS(?))
+ROM_START(mface1v3) // pcb rev 2.0
+ ROM_REGION(0x2000, "rom", 0)
+ ROM_DEFAULT_BIOS("mu20fe")
+ ROM_SYSTEM_BIOS(0, "mu20fe", "MU 2.0 FE") // pokes only (no toolkit)
+ ROMX_LOAD("mf1_20_fe.rom", 0x0000, 0x2000, CRC(fa1b8b0d) SHA1(20cd508b0143166558a7238c7a9ccfbe37b90b0d), ROM_BIOS(0))
+ // ROM_SYSTEM_BIOS(1, "mu2090", "MU 2.0, 90") // alt, unknown if earlier or later, no dump
+ // ROMX_LOAD("mf1_20_90.rom", 0x0000, 0x2000, CRC(2eaf8e41) SHA1(?), ROM_BIOS(1))
+ROM_END
- ROM_SYSTEM_BIOS(?, "mu2090", "MU 2.0, 90") // pokes only or full toolkit? NO DUMP?
- ROMX_LOAD("mf1_20_90.rom", 0x0000, 0x2000, CRC(2eaf8e41) SHA1(?), ROM_BIOS(?))
+ROM_START(mface1) // pcb rev 2.1
+ ROM_REGION(0x2000, "rom", 0)
+ ROM_DEFAULT_BIOS("mu21e4")
+ ROM_SYSTEM_BIOS(0, "mu21e4", "MU 2.1 E4") // most common version
+ ROMX_LOAD("mf1_21_e4.rom", 0x0000, 0x2000, CRC(4b31a971) SHA1(ba28754a3cc31a4ca579829ed4310c313409cf5d), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "mu21e7", "MU 2.1 E7") // alt, unknown if earlier or later, glitchy with Betaplus
+ ROMX_LOAD("mf1_21_e7.rom", 0x0000, 0x2000, CRC(670f0ec2) SHA1(50fba2d628f3a2e9219f72980e4efd62fc9ec1f8), ROM_BIOS(1))
+ ROM_SYSTEM_BIOS(2, "mu2167", "MU 2.1 67") // Kempston Disc support, no Beta support
+ ROMX_LOAD("mf1_21_67.rom", 0x0000, 0x2000, CRC(d720ec1b) SHA1(91a40d8f503ef825df3e2ed712897dbf4ca3671d), ROM_BIOS(2))
+ ROM_SYSTEM_BIOS(3, "bc96", "Brazilian clone 96") // Brazilian clone, unknown pcb version
+ ROMX_LOAD("mf1_bc_96.rom", 0x0000, 0x2000, CRC(e6fe4507) SHA1(a7e03e7fee3aa05ce1501072aab4534ea7bae257), ROM_BIOS(3))
+ // ROM_SYSTEM_BIOS(4, "bc93", "Brazilian clone 93") // another Brazilian clone, unknown pcb version, no dump
+ // ROMX_LOAD("mf1_bc_93.rom", 0x0000, 0x2000, CRC(8c17113b) SHA1(?), ROM_BIOS(4))
+ROM_END
- ROM_SYSTEM_BIOS(?, "bc93", "Brazilian clone 93") // NO DUMP?
- ROMX_LOAD("mf1_bc_93.rom", 0x0000, 0x2000, CRC(8c17113b) SHA1(?), ROM_BIOS(?))
-*/
+ROM_START(mface128v1)
+ ROM_REGION(0x2000, "rom", 0)
+ ROM_DEFAULT_BIOS("v350f")
+ ROM_SYSTEM_BIOS(0, "v340d", "87.1 V34 0D")
+ ROMX_LOAD("mf128_34_0d.rom", 0x0000, 0x2000, CRC(8d8cfd39) SHA1(2104962bb6097e58fcab63969bbaca424a872bb5), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "v350f", "87.12 V35 0F")
+ ROMX_LOAD("mf128_35_0f.rom", 0x0000, 0x2000, CRC(cfefd560) SHA1(6cd6fd2c0fbb40a989a568db9d08ba8eed49cbbd), ROM_BIOS(1))
+ROM_END
-ROM_START(mface128)
+ROM_START(mface128) // DISCiPLE/+D compatible version
ROM_REGION(0x2000, "rom", 0)
ROM_DEFAULT_BIOS("v363c")
- ROM_SYSTEM_BIOS(0, "v363c", "87.2 V36 3C")
- ROMX_LOAD("mf128_36_3c.rom", 0x0000, 0x2000, CRC(78ec8cfd) SHA1(8df204ab490b87c389971ce0c7fb5f9cbd281f14), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(0, "vxx1d", "87.2 Vxx 1D") // unknown PCB version
+ ROMX_LOAD("mf128_xx_1d.rom", 0x0000, 0x2000, CRC(f473991e) SHA1(f03f4ecbcf4a654f4775d16bda0d4cc47f884379), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "v363c", "87.2 V36 3C")
+ ROMX_LOAD("mf128_36_3c.rom", 0x0000, 0x2000, CRC(78ec8cfd) SHA1(8df204ab490b87c389971ce0c7fb5f9cbd281f14), ROM_BIOS(1))
ROM_END
ROM_START(mface3)
ROM_REGION(0x2000, "rom", 0)
- ROM_DEFAULT_BIOS("v50fe")
- ROM_SYSTEM_BIOS(0, "v5013", "V50 13")
- ROMX_LOAD("mf3_50_13.rom", 0x0000, 0x2000, CRC(2d594640) SHA1(5d74d2e2e5a537639da92ff120f8a6d86f474495), ROM_BIOS(0))
- ROM_SYSTEM_BIOS(1, "v50fe", "V50 FE")
- ROMX_LOAD("mf3_50_fe.rom", 0x0000, 0x2000, CRC(b5c00f28) SHA1(983699a07665186f498f5827f9b35c442c2178ba), ROM_BIOS(1))
+ ROM_DEFAULT_BIOS("v5013")
+ ROM_SYSTEM_BIOS(0, "v50fe", "3.9 V50 FE") // doesn't have "lock 48K" option in save menu
+ ROMX_LOAD("mf3_50_fe.rom", 0x0000, 0x2000, CRC(b5c00f28) SHA1(983699a07665186f498f5827f9b35c442c2178ba), ROM_BIOS(0))
+ ROM_SYSTEM_BIOS(1, "v5013", "3.c V50 13")
+ ROMX_LOAD("mf3_50_13.rom", 0x0000, 0x2000, CRC(2d594640) SHA1(5d74d2e2e5a537639da92ff120f8a6d86f474495), ROM_BIOS(1))
+ ROM_SYSTEM_BIOS(2, "vxx9a", "Hard Micro Multiface 3 clone Vxx 9A")
+ ROMX_LOAD("mf3_hm_9a.rom", 0x0000, 0x2000, CRC(2ce53095) SHA1(5fa286f1552f26575a14ab32125d59c26ce95978), ROM_BIOS(2))
+ ROM_IGNORE(0x2000) // ROM is 16K, but contains two identical 8K halves
+
+ ROM_REGION( 0x200, "plds", 0 ) // pal from Hard Micro clone, probably the same for all
+ ROM_LOAD( "mf3_pal16l8a.icx", 0x000, 0x104, CRC(710186a1) SHA1(6573c2b8f55f66ef71ce3992f654080141e09739) )
ROM_END
ROM_START(mprint)
@@ -203,6 +285,39 @@ void spectrum_mface_base_device::device_add_mconfig(machine_config &config)
SPECTRUM_EXPANSION_SLOT(config, m_exp, spectrum_expansion_devices, nullptr);
m_exp->irq_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::irq_w));
m_exp->nmi_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::nmi_w));
+ m_exp->fb_r_handler().set(DEVICE_SELF_OWNER, FUNC(spectrum_expansion_slot_device::fb_r));
+}
+
+void spectrum_mface3_device::device_add_mconfig(machine_config &config)
+{
+ spectrum_mface_base_device::device_add_mconfig(config);
+
+ /* passthru (+3 compatible devices only) */
+ SPECTRUM_EXPANSION_SLOT(config.replace(), m_exp, specpls3_expansion_devices, nullptr);
+}
+
+void spectrum_mprint_device::device_add_mconfig(machine_config &config)
+{
+ spectrum_mface_base_device::device_add_mconfig(config);
+
+ /* printer port */
+ CENTRONICS(config, m_centronics, centronics_devices, "printer");
+ m_centronics->busy_handler().set(FUNC(spectrum_mprint_device::busy_w));
+}
+
+const tiny_rom_entry *spectrum_mface1v1_device::device_rom_region() const
+{
+ return ROM_NAME(mface1v1);
+}
+
+const tiny_rom_entry *spectrum_mface1v2_device::device_rom_region() const
+{
+ return ROM_NAME(mface1v2);
+}
+
+const tiny_rom_entry *spectrum_mface1v3_device::device_rom_region() const
+{
+ return ROM_NAME(mface1v3);
}
const tiny_rom_entry *spectrum_mface1_device::device_rom_region() const
@@ -210,6 +325,11 @@ const tiny_rom_entry *spectrum_mface1_device::device_rom_region() const
return ROM_NAME(mface1);
}
+const tiny_rom_entry *spectrum_mface128v1_device::device_rom_region() const
+{
+ return ROM_NAME(mface128v1);
+}
+
const tiny_rom_entry *spectrum_mface128_device::device_rom_region() const
{
return ROM_NAME(mface128);
@@ -242,25 +362,66 @@ spectrum_mface_base_device::spectrum_mface_base_device(const machine_config &mco
{
}
-spectrum_mface1_device::spectrum_mface1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : spectrum_mface_base_device(mconfig, SPECTRUM_MFACE1, tag, owner, clock)
+spectrum_mface1v2_device::spectrum_mface1v2_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface_base_device(mconfig, type, tag, owner, clock)
, m_joy(*this, "JOY")
, m_hwconfig(*this, "CONFIG")
{
}
+spectrum_mface1v2_device::spectrum_mface1v2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface1v2_device(mconfig, SPECTRUM_MFACE1V2, tag, owner, clock)
+{
+}
+
+spectrum_mface1v1_device::spectrum_mface1v1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface1v2_device(mconfig, SPECTRUM_MFACE1V1, tag, owner, clock)
+{
+}
+
+spectrum_mface1v3_device::spectrum_mface1v3_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface1v2_device(mconfig, type, tag, owner, clock)
+{
+}
+
+spectrum_mface1v3_device::spectrum_mface1v3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface1v3_device(mconfig, SPECTRUM_MFACE1V3, tag, owner, clock)
+{
+}
+
+spectrum_mface1_device::spectrum_mface1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface1v3_device(mconfig, SPECTRUM_MFACE1, tag, owner, clock)
+{
+}
+
+spectrum_mface128_base_device::spectrum_mface128_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface_base_device(mconfig, type, tag, owner, clock)
+{
+}
+
+spectrum_mface128v1_device::spectrum_mface128v1_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface128_base_device(mconfig, type, tag, owner, clock)
+{
+}
+
+spectrum_mface128v1_device::spectrum_mface128v1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : spectrum_mface128v1_device(mconfig, SPECTRUM_MFACE128V1, tag, owner, clock)
+{
+}
+
spectrum_mface128_device::spectrum_mface128_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : spectrum_mface_base_device(mconfig, SPECTRUM_MFACE128, tag, owner, clock)
+ : spectrum_mface128v1_device(mconfig, SPECTRUM_MFACE128, tag, owner, clock)
{
}
spectrum_mface3_device::spectrum_mface3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : spectrum_mface_base_device(mconfig, SPECTRUM_MFACE3, tag, owner, clock)
+ : spectrum_mface128_base_device(mconfig, SPECTRUM_MFACE3, tag, owner, clock)
{
}
spectrum_mprint_device::spectrum_mprint_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : spectrum_mface_base_device(mconfig, SPECTRUM_MPRINT, tag, owner, clock)
+ : spectrum_mface128_base_device(mconfig, SPECTRUM_MPRINT, tag, owner, clock)
+ , m_centronics(*this, "centronics")
{
}
@@ -271,6 +432,48 @@ spectrum_mprint_device::spectrum_mprint_device(const machine_config &mconfig, co
void spectrum_mface_base_device::device_start()
{
save_item(NAME(m_romcs));
+ save_item(NAME(m_nmi_pending));
+}
+
+void spectrum_mface1v2_device::device_start()
+{
+ m_ram = std::make_unique<uint8_t[]>(8 * 1024);
+ save_pointer(NAME(m_ram), 8 * 1024);
+ spectrum_mface_base_device::device_start();
+}
+
+void spectrum_mface1v1_device::device_start()
+{
+ m_ram = std::make_unique<uint8_t[]>(2 * 1024);
+ save_pointer(NAME(m_ram), 2 * 1024);
+ spectrum_mface_base_device::device_start();
+}
+
+void spectrum_mface128_base_device::device_start()
+{
+ m_ram = std::make_unique<uint8_t[]>(8 * 1024);
+ save_pointer(NAME(m_ram), 8 * 1024);
+ save_item(NAME(m_hidden));
+ spectrum_mface_base_device::device_start();
+}
+
+void spectrum_mface128v1_device::device_start()
+{
+ spectrum_mface128_base_device::device_start();
+ save_item(NAME(m_d3_ff));
+}
+
+void spectrum_mface3_device::device_start()
+{
+ spectrum_mface128_base_device::device_start();
+ save_item(NAME(m_disable));
+}
+
+void spectrum_mprint_device::device_start()
+{
+ spectrum_mface128_base_device::device_start();
+ m_busy = 0;
+ save_item(NAME(m_busy));
}
//-------------------------------------------------
@@ -280,6 +483,25 @@ void spectrum_mface_base_device::device_start()
void spectrum_mface_base_device::device_reset()
{
m_romcs = 0;
+ m_nmi_pending = 0;
+}
+
+void spectrum_mface128_base_device::device_reset()
+{
+ spectrum_mface_base_device::device_reset();
+ m_hidden = 1;
+}
+
+void spectrum_mface3_device::device_reset()
+{
+ spectrum_mface128_base_device::device_reset();
+ m_disable = 0;
+}
+
+void spectrum_mprint_device::device_reset()
+{
+ spectrum_mface128_base_device::device_reset();
+ m_centronics->write_strobe(1);
}
@@ -287,9 +509,9 @@ void spectrum_mface_base_device::device_reset()
// IMPLEMENTATION
//**************************************************************************
-READ_LINE_MEMBER(spectrum_mface_base_device::romcs)
+bool spectrum_mface_base_device::romcs()
{
- return m_romcs | m_exp->romcs();
+ return m_romcs || m_exp->romcs();
}
void spectrum_mface_base_device::pre_opcode_fetch(offs_t offset)
@@ -298,22 +520,73 @@ void spectrum_mface_base_device::pre_opcode_fetch(offs_t offset)
if (!machine().side_effects_disabled())
{
- if (offset == 0x0066)
+ if (offset == 0x0066 && m_nmi_pending)
m_romcs = 1;
}
}
-uint8_t spectrum_mface1_device::iorq_r(offs_t offset)
+void spectrum_mface128_device::pre_opcode_fetch(offs_t offset)
+{
+ // pass-thru pin a15 is controlled by some extra logic on v2 pcb
+ m_exp->pre_opcode_fetch((m_nmi_pending || m_romcs) ? offset | 0x8000 : offset);
+
+ if (!machine().side_effects_disabled())
+ {
+ if (offset == 0x0066 && m_nmi_pending)
+ m_romcs = 1;
+ }
+}
+
+void spectrum_mprint_device::pre_opcode_fetch(offs_t offset)
+{
+ m_exp->pre_opcode_fetch(offset);
+
+ if (!machine().side_effects_disabled())
+ {
+ if (offset == 0x0066 && m_nmi_pending)
+ m_romcs = 1;
+
+ // todo: must be some other hooks here...
+ // at least LPRINT, LLIST and COPY should be caught
+ // also unique REM xx commands
+ // menu should pop up for LPRINT and REM MP
+ }
+}
+
+uint8_t spectrum_mface1v2_device::iorq_r(offs_t offset)
+{
+ uint8_t data = m_exp->iorq_r(offset);
+
+ if (!machine().side_effects_disabled())
+ {
+ switch (offset & 0xff) // real decodes tbc...
+ {
+ case 0x1f:
+ if (!(m_hwconfig->read() & 0x02))
+ data = m_joy->read() & 0x1f;
+ break;
+ case 0x5f:
+ m_romcs = 0;
+ break;
+ case 0x9f:
+ m_romcs = 1;
+ break;
+ }
+ }
+ return data;
+}
+
+uint8_t spectrum_mface1v3_device::iorq_r(offs_t offset)
{
uint8_t data = m_exp->iorq_r(offset);
if (!machine().side_effects_disabled())
{
- switch (offset & 0xff)
+ switch (offset & 0xff) // real decodes tbc...
{
case 0x1f:
m_romcs = 0;
- if (!(m_hwconfig->read() & 0x01)) // joystick disable jumper
+ if (!(m_hwconfig->read() & 0x02))
data = m_joy->read() & 0x1f;
break;
case 0x9f:
@@ -324,20 +597,71 @@ uint8_t spectrum_mface1_device::iorq_r(offs_t offset)
return data;
}
-uint8_t spectrum_mface128_device::iorq_r(offs_t offset)
+uint8_t spectrum_mface1_device::iorq_r(offs_t offset)
{
uint8_t data = m_exp->iorq_r(offset);
+ if (m_hwconfig->read() & 0x01) // disable switch
+ return data;
+
if (!machine().side_effects_disabled())
{
- switch (offset & 0xff)
+ switch (offset & 0xf2) // -001 --1-
{
- case 0xbf:
+ case 0x12: // 0001 --1- uses 0x1f
+ m_romcs = 0;
+ if (!(m_hwconfig->read() & 0x02))
+ data = m_joy->read() & 0x1f;
+ break;
+ case 0x92: // 1001 --1- uses 0x9f
m_romcs = 1;
break;
- case 0x3f:
+ }
+ }
+ return data;
+}
+
+uint8_t spectrum_mface128v1_device::iorq_r(offs_t offset)
+{
+ uint8_t data = m_exp->iorq_r(offset);
+
+ if (!machine().side_effects_disabled())
+ {
+ switch (offset & 0xf0) // -001 ----
+ {
+ case 0x10: // 0001 ---- uses 0x1f
m_romcs = 0;
break;
+ case 0x90: // 1001 ---- uses 0x9f
+ if (!m_hidden)
+ {
+ m_romcs = 1;
+ data = (data & 0x7f) | (m_d3_ff << 7);
+ }
+ break;
+ }
+ }
+ return data;
+}
+
+uint8_t spectrum_mface128_device::iorq_r(offs_t offset)
+{
+ uint8_t data = m_exp->iorq_r(offset);
+
+ if (!machine().side_effects_disabled())
+ {
+ switch (offset & 0xf4) // -011 -1--
+ {
+ case 0x34: // 0011 -1-- uses 0x3f
+ m_romcs = 0;
+ break;
+ case 0xb4: // 1011 -1-- uses 0xbf
+ if (!m_hidden)
+ {
+ m_romcs = 1;
+ data = (data & 0x7f) | (m_d3_ff << 7);
+ }
+ break;
}
}
return data;
@@ -349,16 +673,26 @@ uint8_t spectrum_mface3_device::iorq_r(offs_t offset)
if (!machine().side_effects_disabled())
{
- switch (offset & 0xff)
+ switch (offset & 0xff) // fully decoded by PAL16L8
{
case 0x3f:
- m_romcs = 1;
+ if (!m_hidden)
+ m_romcs = 1;
break;
case 0xbf:
m_romcs = 0;
break;
}
}
+
+ // mface3 monitors writes to the +3 memory ctrl registers (ports 1ffd, 7ffd)
+ // keeps copies of the low nibbles in a 74LS670 4x4 register file
+ if ((offset & 0x7f) == 0x3f && !m_hidden)
+ {
+ data = 0xf0; // get stored copy of 1ffd, 7ffd
+ data |= m_reg_file[(offset >> 13) & 3] & 0xf;
+ }
+
return data;
}
@@ -368,25 +702,141 @@ uint8_t spectrum_mprint_device::iorq_r(offs_t offset)
if (!machine().side_effects_disabled())
{
- switch (offset & 0xff)
+ switch (offset & 0xff) // real decodes tbc...
{
- case 0xbb:
- m_romcs = 1;
- break;
case 0xbf:
m_romcs = 0;
break;
+ case 0xbb:
+ if (!m_hidden)
+ m_romcs = 1;
+ data &= ~0x40;
+ data |= m_busy << 6;
+ break;
}
}
return data;
}
+void spectrum_mface1v2_device::iorq_w(offs_t offset, uint8_t data)
+{
+ if ((offset & 0xff) == 0x5f) // real decodes tbc...
+ nmi(CLEAR_LINE);
+
+ m_exp->iorq_w(offset, data);
+}
+
+void spectrum_mface1v3_device::iorq_w(offs_t offset, uint8_t data)
+{
+ if ((offset & 0xff) == 0x1f) // real decodes tbc...
+ nmi(CLEAR_LINE);
+
+ m_exp->iorq_w(offset, data);
+}
+
void spectrum_mface1_device::iorq_w(offs_t offset, uint8_t data)
{
- switch (offset & 0xff)
+ if ((offset & 0xf2) == 0x12) // 0001 --1- uses 0x1f
+ nmi(CLEAR_LINE);
+
+ m_exp->iorq_w(offset, data);
+}
+
+void spectrum_mface128v1_device::iorq_w(offs_t offset, uint8_t data)
+{
+ switch (offset & 0xf0) // -001 ----
{
- case 0x1f:
- m_slot->nmi_w(CLEAR_LINE);
+ case 0x10: // 0001 ---- uses 0x1f
+ m_hidden = 1;
+ [[fallthrough]];
+ case 0x90: // 1001 ---- uses 0x9f
+ nmi(CLEAR_LINE);
+ break;
+ }
+
+ // mface128 monitors writes to the 128K memory ctrl register (port 7ffd)
+ // keeps a copy of D3 (screen select bit) in a D flip-flop
+ if ((offset & 0x8002) == 0) // 0--- ---- ---- --0- uses 7ffd
+ m_d3_ff = (data >> 3) & 1;
+
+ m_exp->iorq_w(offset, data);
+}
+
+void spectrum_mface128_device::iorq_w(offs_t offset, uint8_t data)
+{
+ switch (offset & 0xf4) // -011 -1--
+ {
+ case 0x34: // 0011 -1-- uses 0x3f
+ m_hidden = 1;
+ [[fallthrough]];
+ case 0xb4: // 1011 -1-- uses 0xbf
+ nmi(CLEAR_LINE);
+ break;
+ }
+
+ // mface128 monitors writes to the 128K memory ctrl register (port 7ffd)
+ // keeps a copy of D3 (screen select bit) in a D flip-flop
+ if ((offset & 0x8002) == 0) // 0--- ---- ---- --0- uses 7ffd
+ m_d3_ff = (data >> 3) & 1;
+
+ m_exp->iorq_w(offset, data);
+}
+
+void spectrum_mface3_device::iorq_w(offs_t offset, uint8_t data)
+{
+ switch (offset & 0xff) // fully decoded by PAL16L8
+ {
+ case 0x3f:
+ m_hidden = 1; // also Hard Micro clone: D0 is 16KB rom msb (unused)
+ [[fallthrough]];
+ case 0xbf:
+ nmi(CLEAR_LINE);
+ break;
+ }
+
+ // mface3 monitors writes to the +3 memory ctrl registers (ports 1ffd, 7ffd)
+ // keeps copies of the low nibbles in a 74LS670 4x4 register file
+ if ((offset & 0x90fd) == 0x10fd)
+ m_reg_file[(offset >> 13) & 3] = data & 0x0f;
+
+ // also disables itself if special paging mode bit is set (CP/M)
+ if ((offset & 0xf0fd) == 0x10fd)
+ m_disable = data & 1;
+
+ m_exp->iorq_w(offset, data);
+}
+
+void spectrum_mprint_device::iorq_w(offs_t offset, uint8_t data)
+{
+ // write_strobe is fired by wr seq: 0xbf, 0xbb, 0xbf
+ switch (offset & 0xff) // real decodes tbc...
+ {
+ case 0xbf:
+ m_centronics->write_data0(BIT(data, 0));
+ m_centronics->write_data1(BIT(data, 1));
+ m_centronics->write_data2(BIT(data, 2));
+ m_centronics->write_data3(BIT(data, 3));
+ m_centronics->write_data4(BIT(data, 4));
+ m_centronics->write_data5(BIT(data, 5));
+ m_centronics->write_data6(BIT(data, 6));
+ m_centronics->write_data7(BIT(data, 7));
+ m_centronics->write_strobe(1);
+ break;
+ case 0xbb:
+ m_centronics->write_strobe(0);
+ break;
+ }
+
+ switch (offset & 0x5ff)
+ {
+ case 0x4bf:
+ nmi(CLEAR_LINE);
+ break;
+ case 0x4fb: // not essential yet, for other hooks?
+ m_hidden = 0;
+ break;
+ case 0x5fb:
+ m_hidden = 1;
break;
}
@@ -416,6 +866,29 @@ uint8_t spectrum_mface_base_device::mreq_r(offs_t offset)
return data;
}
+uint8_t spectrum_mface1v1_device::mreq_r(offs_t offset)
+{
+ uint8_t data = 0xff;
+
+ if (m_romcs)
+ {
+ switch (offset & 0xe000)
+ {
+ case 0x0000:
+ data = m_rom->base()[offset & 0x1fff];
+ break;
+ case 0x2000:
+ data = m_ram[offset & 0x7ff];
+ break;
+ }
+ }
+
+ if (m_exp->romcs())
+ data &= m_exp->mreq_r(offset);
+
+ return data;
+}
+
void spectrum_mface_base_device::mreq_w(offs_t offset, uint8_t data)
{
if (m_romcs)
@@ -432,26 +905,58 @@ void spectrum_mface_base_device::mreq_w(offs_t offset, uint8_t data)
m_exp->mreq_w(offset, data);
}
-INPUT_CHANGED_MEMBER(spectrum_mface_base_device::magic_button)
+void spectrum_mface1v1_device::mreq_w(offs_t offset, uint8_t data)
{
- if (newval && !oldval)
+ if (m_romcs)
{
- m_slot->nmi_w(ASSERT_LINE);
+ switch (offset & 0xe000)
+ {
+ case 0x2000:
+ m_ram[offset & 0x7ff] = data;
+ break;
+ }
}
- else
+
+ if (m_exp->romcs())
+ m_exp->mreq_w(offset, data);
+}
+
+INPUT_CHANGED_MEMBER(spectrum_mface1v2_device::magic_button)
+{
+ if (!newval)
{
- m_slot->nmi_w(CLEAR_LINE);
+ nmi(ASSERT_LINE);
}
}
INPUT_CHANGED_MEMBER(spectrum_mface1_device::magic_button)
{
- if (newval && !oldval) // key released
+ if (!newval)
{
+ if (!(m_hwconfig->read() & 0x01))
+ {
+ nmi(ASSERT_LINE);
+ }
+ }
+}
+INPUT_CHANGED_MEMBER(spectrum_mface128_base_device::magic_button)
+{
+ if (!newval)
+ {
+ m_hidden = 0;
+ nmi(ASSERT_LINE);
}
- else // key pressed
+}
+
+INPUT_CHANGED_MEMBER(spectrum_mface3_device::magic_button)
+{
+ if (!newval)
{
- m_slot->nmi_w(ASSERT_LINE);
+ if (!m_disable)
+ {
+ m_hidden = 0;
+ nmi(ASSERT_LINE);
+ }
}
}