summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Brice Onken <32155223+briceonk@users.noreply.github.com>2022-08-19 07:25:32 -0700
committer GitHub <noreply@github.com>2022-08-20 00:25:32 +1000
commit5ac798b23844ceec2cdab6628e86685049707a45 (patch)
treefe2c5b931785fb444288df7b33cc2f236f64c9c6
parent171607573760c5e361f991aeecb6a7c61456d2b5 (diff)
sony_news/news_r4k.cpp: Implemented Sony NEWS NWS-5000X driver. (#8854)
* sony_news/news_hid.cpp: Added APbus I/O mapping. * bus/nscsi/cd.cpp: Added Sony CD-ROM CDU-541 1.0A recognised by NEWS-OS. * machine/upd765.cpp: Added National Semiconductor PC8477B clone. New machines marked not working -------------------- Sony NET WORK STATION NWS-5000X
-rw-r--r--src/devices/bus/nscsi/cd.cpp6
-rw-r--r--src/devices/bus/nscsi/cd.h7
-rw-r--r--src/devices/machine/upd765.cpp23
-rw-r--r--src/devices/machine/upd765.h13
-rw-r--r--src/emu/xtal.cpp1
-rw-r--r--src/mame/mame.lst3
-rw-r--r--src/mame/mess.flt1
-rw-r--r--src/mame/sony_news/cxd8442q.cpp7
-rw-r--r--src/mame/sony_news/cxd8452aq.cpp2
-rw-r--r--src/mame/sony_news/news_hid.cpp27
-rw-r--r--src/mame/sony_news/news_hid.h1
-rw-r--r--src/mame/sony_news/news_r4k.cpp1257
12 files changed, 1342 insertions, 6 deletions
diff --git a/src/devices/bus/nscsi/cd.cpp b/src/devices/bus/nscsi/cd.cpp
index 750f7be774a..db71c61a1e8 100644
--- a/src/devices/bus/nscsi/cd.cpp
+++ b/src/devices/bus/nscsi/cd.cpp
@@ -8,6 +8,7 @@
DEFINE_DEVICE_TYPE(NSCSI_CDROM, nscsi_cdrom_device, "scsi_cdrom", "SCSI CD-ROM")
DEFINE_DEVICE_TYPE(NSCSI_CDROM_SGI, nscsi_cdrom_sgi_device, "scsi_cdrom_sgi", "SCSI CD-ROM SGI")
+DEFINE_DEVICE_TYPE(NSCSI_CDROM_NEWS, nscsi_cdrom_news_device, "scsi_cdrom_news", "SCSI CD-ROM NEWS")
DEFINE_DEVICE_TYPE(NSCSI_RRD45, nscsi_dec_rrd45_device, "nrrd45", "RRD45 CD-ROM (New)")
DEFINE_DEVICE_TYPE(NSCSI_XM3301, nscsi_toshiba_xm3301_device, "nxm3301", "XM-3301TA CD-ROM (New)")
DEFINE_DEVICE_TYPE(NSCSI_XM5301SUN, nscsi_toshiba_xm5301_sun_device, "nxm5301sun", "XM-5301B Sun 4x CD-ROM (New)")
@@ -26,6 +27,11 @@ nscsi_cdrom_sgi_device::nscsi_cdrom_sgi_device(const machine_config &mconfig, co
{
}
+nscsi_cdrom_news_device::nscsi_cdrom_news_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ nscsi_cdrom_device(mconfig, NSCSI_CDROM_NEWS, tag, owner, "Sony", "CD-ROM CDU-541", "1.0A", 0x00, 0x05)
+{
+}
+
nscsi_dec_rrd45_device::nscsi_dec_rrd45_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
nscsi_cdrom_device(mconfig, NSCSI_RRD45, tag, owner, "DEC ", "RRD45 (C) DEC ", "0436", 0x98, 0x02)
{
diff --git a/src/devices/bus/nscsi/cd.h b/src/devices/bus/nscsi/cd.h
index 1a87e150b13..c1745615474 100644
--- a/src/devices/bus/nscsi/cd.h
+++ b/src/devices/bus/nscsi/cd.h
@@ -68,6 +68,12 @@ protected:
virtual bool scsi_command_done(uint8_t command, uint8_t length) override;
};
+class nscsi_cdrom_news_device : public nscsi_cdrom_device
+{
+public:
+ nscsi_cdrom_news_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
+};
+
class nscsi_dec_rrd45_device : public nscsi_cdrom_device
{
public:
@@ -115,6 +121,7 @@ protected:
DECLARE_DEVICE_TYPE(NSCSI_CDROM, nscsi_cdrom_device)
DECLARE_DEVICE_TYPE(NSCSI_CDROM_SGI, nscsi_cdrom_sgi_device)
+DECLARE_DEVICE_TYPE(NSCSI_CDROM_NEWS, nscsi_cdrom_news_device)
DECLARE_DEVICE_TYPE(NSCSI_RRD45, nscsi_dec_rrd45_device)
DECLARE_DEVICE_TYPE(NSCSI_XM3301, nscsi_toshiba_xm3301_device)
DECLARE_DEVICE_TYPE(NSCSI_XM5301SUN, nscsi_toshiba_xm5301_sun_device)
diff --git a/src/devices/machine/upd765.cpp b/src/devices/machine/upd765.cpp
index 39e0a402fd6..4996093e0bd 100644
--- a/src/devices/machine/upd765.cpp
+++ b/src/devices/machine/upd765.cpp
@@ -53,6 +53,7 @@ DEFINE_DEVICE_TYPE(N82077AA, n82077aa_device, "n82077aa", "Int
DEFINE_DEVICE_TYPE(PC_FDC_SUPERIO, pc_fdc_superio_device, "pc_fdc_superio", "Winbond PC FDC Super I/O")
DEFINE_DEVICE_TYPE(DP8473, dp8473_device, "dp8473", "National Semiconductor DP8473 FDC")
DEFINE_DEVICE_TYPE(PC8477A, pc8477a_device, "pc8477a", "National Semiconductor PC8477A FDC")
+DEFINE_DEVICE_TYPE(PC8477B, pc8477b_device, "pc8477b", "National Semiconductor PC8477B FDC")
DEFINE_DEVICE_TYPE(WD37C65C, wd37c65c_device, "wd37c65c", "Western Digital WD37C65C FDC")
DEFINE_DEVICE_TYPE(MCS3201, mcs3201_device, "mcs3201", "Motorola MCS3201 FDC")
DEFINE_DEVICE_TYPE(TC8566AF, tc8566af_device, "tc8566af", "Toshiba TC8566AF FDC")
@@ -139,6 +140,19 @@ void pc8477a_device::map(address_map &map)
map(0x7, 0x7).rw(FUNC(pc8477a_device::dir_r), FUNC(pc8477a_device::ccr_w));
}
+void pc8477b_device::map(address_map &map)
+{
+ if(mode != mode_t::AT) {
+ map(0x0, 0x0).r(FUNC(pc8477b_device::sra_r));
+ map(0x1, 0x1).r(FUNC(pc8477b_device::srb_r));
+ }
+ map(0x2, 0x2).rw(FUNC(pc8477b_device::dor_r), FUNC(pc8477b_device::dor_w));
+ map(0x3, 0x3).rw(FUNC(pc8477b_device::tdr_r), FUNC(pc8477b_device::tdr_w));
+ map(0x4, 0x4).rw(FUNC(pc8477b_device::msr_r), FUNC(pc8477b_device::dsr_w));
+ map(0x5, 0x5).rw(FUNC(pc8477b_device::fifo_r), FUNC(pc8477b_device::fifo_w));
+ map(0x7, 0x7).rw(FUNC(pc8477b_device::dir_r), FUNC(pc8477b_device::ccr_w));
+}
+
void wd37c65c_device::map(address_map &map)
{
// NOTE: this map only covers registers defined by CS.
@@ -3153,6 +3167,15 @@ pc8477a_device::pc8477a_device(const machine_config &mconfig, const char *tag, d
recalibrate_steps = 85; // TODO: may also be programmed as 255, 3925 or 4095 by (unemulated) mode command
}
+pc8477b_device::pc8477b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ps2_fdc_device(mconfig, PC8477B, tag, owner, clock)
+{
+ ready_polled = true;
+ ready_connected = false;
+ select_connected = true;
+ select_multiplexed = false;
+ recalibrate_steps = 85; // TODO: may also be programmed as 255, 3925 or 4095 by (unemulated) mode command
+}
+
wd37c65c_device::wd37c65c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
upd765_family_device(mconfig, WD37C65C, tag, owner, clock),
m_clock2(0)
diff --git a/src/devices/machine/upd765.h b/src/devices/machine/upd765.h
index f4441e72543..0eba7da4b29 100644
--- a/src/devices/machine/upd765.h
+++ b/src/devices/machine/upd765.h
@@ -526,6 +526,18 @@ public:
virtual void map(address_map &map) override;
};
+class pc8477b_device : public ps2_fdc_device {
+public:
+ pc8477b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock, mode_t mode)
+ : pc8477b_device(mconfig, tag, owner, clock)
+ {
+ set_mode(mode);
+ }
+ pc8477b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual void map(address_map &map) override;
+};
+
class wd37c65c_device : public upd765_family_device {
public:
wd37c65c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
@@ -591,6 +603,7 @@ DECLARE_DEVICE_TYPE(N82077AA, n82077aa_device)
DECLARE_DEVICE_TYPE(PC_FDC_SUPERIO, pc_fdc_superio_device)
DECLARE_DEVICE_TYPE(DP8473, dp8473_device)
DECLARE_DEVICE_TYPE(PC8477A, pc8477a_device)
+DECLARE_DEVICE_TYPE(PC8477B, pc8477b_device)
DECLARE_DEVICE_TYPE(WD37C65C, wd37c65c_device)
DECLARE_DEVICE_TYPE(MCS3201, mcs3201_device)
DECLARE_DEVICE_TYPE(TC8566AF, tc8566af_device)
diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp
index 2449f62bd2e..fea0239d53a 100644
--- a/src/emu/xtal.cpp
+++ b/src/emu/xtal.cpp
@@ -473,6 +473,7 @@ const double XTAL::known_xtals[] = {
72'000'000, /* 72_MHz_XTAL Aristocrat MKV */
72'576'000, /* 72.576_MHz_XTAL Centipede, Millipede, Missile Command, Let's Go Bowling "Multipede" */
73'728'000, /* 73.728_MHz_XTAL Ms. Pac-Man/Galaga 20th Anniversary */
+ 75'000'000, /* 75_MHz_XTAL Sony NEWS NWS-5000X */
77'414'400, /* 77.4144_MHz_XTAL NCD17c */
80'000'000, /* 80_MHz_XTAL ARM710 */
87'183'360, /* 87.18336_MHz_XTAL AT&T 630 MTG */
diff --git a/src/mame/mame.lst b/src/mame/mame.lst
index 40cabec5015..5101dd1e691 100644
--- a/src/mame/mame.lst
+++ b/src/mame/mame.lst
@@ -33542,6 +33542,9 @@ nws1580 // Sony NEWS NWS-1580
@source:sony_news/news_r3k.cpp
nws3260 // Sony NEWS NWS-3260
+@source:sony_news/news_r4k.cpp
+nws5000x // Sony NEWS NWS-5000X
+
@source:skeleton/newton.cpp
newtnotp
newtonmp
diff --git a/src/mame/mess.flt b/src/mame/mess.flt
index 698885321c2..063e1e7e2a1 100644
--- a/src/mame/mess.flt
+++ b/src/mame/mess.flt
@@ -991,6 +991,7 @@ sony/umatic.cpp
sony_news/news_38xx.cpp
sony_news/news_68k.cpp
sony_news/news_r3k.cpp
+sony_news/news_r4k.cpp
sord/m5.cpp
stm/pp.cpp
sun/sun1.cpp
diff --git a/src/mame/sony_news/cxd8442q.cpp b/src/mame/sony_news/cxd8442q.cpp
index f0beb0ae0b5..7047215a807 100644
--- a/src/mame/sony_news/cxd8442q.cpp
+++ b/src/mame/sony_news/cxd8442q.cpp
@@ -261,15 +261,12 @@ bool cxd8442q_device::apfifo_channel::dma_check()
}
// Check DRQ to see if the device is ready to give or receive data
- bool stay_active;
+ bool stay_active = false;
if (drq)
{
stay_active = dma_cycle();
}
- else
- {
- stay_active = true;
- }
+
return stay_active;
}
diff --git a/src/mame/sony_news/cxd8452aq.cpp b/src/mame/sony_news/cxd8452aq.cpp
index 3e93d96d75b..77855d00821 100644
--- a/src/mame/sony_news/cxd8452aq.cpp
+++ b/src/mame/sony_news/cxd8452aq.cpp
@@ -16,7 +16,7 @@
#define LOG_INTERRUPT (1U << 2)
#define LOG_DATA (1U << 3)
-#define SONIC3_DEBUG (LOG_REGISTERS | LOG_INTERRUPT)
+#define SONIC3_DEBUG (LOG_GENERAL | LOG_REGISTERS | LOG_INTERRUPT)
#define SONIC3_TRACE (SONIC3_DEBUG | LOG_DATA)
#include "logmacro.h"
diff --git a/src/mame/sony_news/news_hid.cpp b/src/mame/sony_news/news_hid.cpp
index 0527299dde3..e2c229820ce 100644
--- a/src/mame/sony_news/news_hid.cpp
+++ b/src/mame/sony_news/news_hid.cpp
@@ -60,6 +60,33 @@ void news_hid_hle_device::map_68k(address_map &map)
map(0x7, 0x7).w(FUNC(news_hid_hle_device::reset_w<MOUSE>));
}
+void news_hid_hle_device::map_apbus(address_map &map)
+{
+ // see https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/apbus/kb_ap.c#L44
+ // Note that this isn't actually fully functional yet, but it doesn't matter since
+ // the APbus driver (NWS-5000X/news_r4k) doesn't have the framebuffer emulated yet
+ // so the graphical console can't be used anyways. This is enough to satisfy the
+ // keyboard/mouse init routines in NEWS-OS.
+ // TODO: speed, buzzer, keyboard tx, make it actually work
+ map(0x0, 0x3).r(FUNC(news_hid_hle_device::data_r<KEYBOARD>));
+ map(0x4, 0x7).r(FUNC(news_hid_hle_device::status_r<KEYBOARD>));
+ map(0x8, 0xb).w(FUNC(news_hid_hle_device::init_w<KEYBOARD>));
+ map(0xc, 0xf).w(FUNC(news_hid_hle_device::reset_w<KEYBOARD>));
+ // 10-13 kb speed r
+ map(0x14, 0x17).r(FUNC(news_hid_hle_device::data_r<MOUSE>));
+ map(0x18, 0x1b).r(FUNC(news_hid_hle_device::status_r<MOUSE>));
+ map(0x1c, 0x1f).w(FUNC(news_hid_hle_device::init_w<MOUSE>));
+ map(0x20, 0x23).w(FUNC(news_hid_hle_device::reset_w<MOUSE>));
+ // 24-27 ms speed r
+ // 28-2b kb buzzf
+ // 2c-2f kb buzz
+ // 30-33 kb tx data
+ map(0x34, 0x37).lr32(NAME([](offs_t offset) { return 0x1; })); // kb tx stat (pretend to accept anything, AFAICT)
+ // 38-3b kb tx init
+ // 3c-3f kb tx reset
+ // 40-43 kb tx speed
+}
+
void news_hid_hle_device::device_start()
{
m_irq_out_cb.resolve_all_safe();
diff --git a/src/mame/sony_news/news_hid.h b/src/mame/sony_news/news_hid.h
index 8d8ab1e5b79..f336a7f4f7d 100644
--- a/src/mame/sony_news/news_hid.h
+++ b/src/mame/sony_news/news_hid.h
@@ -24,6 +24,7 @@ public:
void map(address_map &map);
void map_68k(address_map &map);
+ void map_apbus(address_map &map);
protected:
// device_t overrides
diff --git a/src/mame/sony_news/news_r4k.cpp b/src/mame/sony_news/news_r4k.cpp
new file mode 100644
index 00000000000..6466fc44040
--- /dev/null
+++ b/src/mame/sony_news/news_r4k.cpp
@@ -0,0 +1,1257 @@
+// license:BSD-3-Clause
+// copyright-holders:Brice Onken
+// thanks-to:Patrick Mackinlay,Olivier Galibert,Tsubai Masanari
+
+/*
+ * Sony NEWS R4000/4400 APbus-based workstations
+ *
+ * Sources and more information:
+ * - http://ozuma.o.oo7.jp/nws5000x.htm
+ * - https://katsu.watanabe.name/doc/sonynews/
+ * - https://web.archive.org/web/20170202100940/www3.videa.or.jp/NEWS/
+ * - https://github.com/NetBSD/src/tree/trunk/sys/arch/newsmips
+ * - https://web.archive.org/web/19970713173157/http%3A%2F%2Fwww1.sony.co.jp%2FOCMP%2F
+ * - https://github.com/briceonk/news-os
+ *
+ * CPU configuration:
+ * - CPU card has a 75MHz crystal, multiplier (if any) TBD
+ * - PRId = 0x450
+ * - config register = 0x1081E4BF
+ * [31] CM = 0 (MC mode off)
+ * [30:28] EC = 001 (clock frequency divided by 3)
+ * [27:24] EP = 00000 (doubleword every cycle)
+ * [23:22] SB = 10 (16 word scache line size)
+ * [21] SS = 0 (unified scache)
+ * [20] SW = 0 (128-bit data path to scache)
+ * [19:18] EW = 0 (64-bit system port width)
+ * [17] SC = 0 (scache present)
+ * [16] SM = 1 (Dirty Shared state disabled)
+ * [15] BE = 1 (Big endian)
+ * [14] EM = 1 (Parity mode)
+ * [13] EB = 1 (Sub-block ordering)
+ * [12] Reserved (0)
+ * [11:9] IC = 2 (16 KByte icache)
+ * [8:6] DC = 2 (16 KByte dcache)
+ * Per page 90 of the R4400 user guide, the following bits ([5:0]) are mutable by software at runtime.
+ * [5] IB = 1 (32 byte icache line)
+ * [4] DB = 1 (32 byte icache line)
+ * [3] CU = 1 (SC uses cacheable coherent update on write)
+ * [2:0] K0 = 7 (cache coherency algo, 7 is reserved)
+ * - Known R4400 config differences between this driver and the physical platform:
+ * - emulated R4400 sets revision to 40 instead of 50. The user manual warns against using the revision field of PRId
+ * in software, so hopefully that won't cause any deltas in behavior before that can be configured.
+ * - emulated SM (Dirty Shared state) is on by default - however, is SM actually being emulated?
+ * - emulated CU and K0 are all 0 instead of all 1 like on the physical platform. Unlike SM, software can set these.
+ * - In general, the secondary cache isn't emulated, which might influence bits 3:0 of the config register.
+ *
+ * General Emulation Status (major chips only, there are additional smaller chips including CPLDs on the boards)
+ * CPU card:
+ * - MIPS R4400: emulated, with the caveats above
+ * - 10x Motorola MCM67A618FN12 SRAMs (secondary cache): partially emulated
+ * - Tag manipulation is implemented. This seems to be enough for NEWS-OS 4 to work.
+ * Motherboard:
+ * - Sony CXD8490G, CXD8491G, CXD8492G, CXD8489G (unknown ASICs): not emulated
+ * - Main memory: partially emulated (memory controller is unknown and barely emulated - memory configurations other than 64MB don't work at the moment)
+ * I/O board:
+ * - Sony CXD8409Q Parallel Interface: not emulated
+ * - National Semi PC8477B Floppy Controller: emulated (-A differences unemulated but seem to be unneeded)
+ * - Zilog Z8523010VSC ESCC serial interface: emulated (see following)
+ * - Sony CXD8421Q WSC-ESCC1 serial APbus interface controller: skeleton (ESCC connections, probably DMA, APbus interface, etc. handled by this chip)
+ * - 2x Sony CXD8442Q WSC-FIFO APbus FIFO/interface chips: partially emulated (handles APbus connections and DMA for sound, floppy, etc.)
+ * - National Semi DP83932B-VF SONIC Ethernet controller: emulated
+ * - Sony CXD8452AQ WSC-SONIC3 SONIC Ethernet APbus interface controller: mostly(?) emulated
+ * - Sony CXD8418Q WSC-PARK3: not fully emulated, but some of the general platform functions may come from this chip (most likely a gate array based on what the PARK2 was in older gen NEWS systems)
+ * - Sony CXD8403Q DMAC3Q DMA controller: Partially emulated
+ * - 2x HP 1TV3-0302 SPIFI3 SCSI controllers: Partially emulated, only works with the monitor ROM and NEWS-OS 4 right now.
+ * - ST Micro M58T02-150PC1 Timekeeper RAM: emulated
+ * DSC-39 XB Framebuffer/video card:
+ * - Sony CXD8486Q XB: not emulated (most likely APbus interface)
+ * - 16x NEC D482235G5 Dual Port Graphics Buffers: not emulated
+ * - Brooktree Bt468KG220 RAMDAC: not emulated
+ *
+ * Known issues:
+ * - Monitor ROM command `ss -r` doesn't show most register values. Not sure what mechanism the APmonitor uses to get the register dump.
+ * (TLB dump is also broken, but unlike the register dump, the TLB dump is broken on the real NWS-5000X too.
+ * Use the `mp` monitor command instead for a working version that shows the TLB correctly, both on real hardware and in emulation)
+ * - Reboot/halt+boot fails because the ESCC FIFO isn't reset properly. For now, the emulator must be hard reset.
+ * It prints out a message (`esccf0: ai->ai_addr points AProm`) and then tries to allocate and use a different FIFO (which fails)
+ * - The NetBSD installer has some weird issues (selects SCSI ID 0 regardless of the ID, some serial weirdness that NEWS-OS doesn't have, etc)
+ *
+ * TODO (in no particular order):
+ * - More complete floppy support (only supports floppy boot at the moment)
+ * - NetBSD SCSI support (mostly/entirely changes needed in the SPIFI driver)
+ * - Sound (lots of undocumented hardware)
+ * - Parallel I/O
+ * - Framebuffer (and remaining kb/ms support)
+ * - APbus expansion slots
+ * - Triage and fix the known issues mentioned above
+ * - Full save state support
+ */
+
+#include "emu.h"
+
+#include "cxd8442q.h"
+#include "cxd8452aq.h"
+#include "dmac3.h"
+#include "news_hid.h"
+
+#include "bus/nscsi/cd.h"
+#include "bus/nscsi/hd.h"
+#include "bus/rs232/rs232.h"
+#include "cpu/mips/r4000.h"
+#include "imagedev/floppy.h"
+#include "machine/dp83932c.h"
+#include "machine/nscsi_bus.h"
+#include "machine/ram.h"
+#include "machine/spifi3.h"
+#include "machine/timekpr.h"
+#include "machine/upd765.h"
+#include "machine/z80scc.h"
+
+#include "formats/pc_dsk.h"
+
+#define LOG_INTERRUPT (1U << 1)
+#define LOG_ALL_INTERRUPT (1U << 2)
+#define LOG_LED (1U << 3)
+#define LOG_MEMORY (1U << 4)
+#define LOG_APBUS (1U << 5)
+#define LOG_ESCC (1U << 6)
+
+#define NEWS_R4K_INFO (LOG_LED)
+#define NEWS_R4K_DEBUG (NEWS_R4K_INFO | LOG_GENERAL | LOG_INTERRUPT)
+#define NEWS_R4K_TRACE (NEWS_R4K_DEBUG | LOG_ALL_INTERRUPT | LOG_APBUS)
+#define NEWS_R4K_MAX (NEWS_R4K_TRACE | LOG_MEMORY | LOG_ESCC)
+
+#define VERBOSE NEWS_R4K_INFO
+
+#include "logmacro.h"
+
+namespace {
+
+class news_r4k_state : public driver_device
+{
+public:
+ news_r4k_state(machine_config const &mconfig, device_type type, char const *tag) :
+ driver_device(mconfig, type, tag),
+ m_cpu(*this, "cpu"),
+ m_ram(*this, "ram"),
+ m_dma_ram(*this, "dmaram"),
+ m_net_ram(*this, "netram"),
+ m_rtc(*this, "rtc"),
+ m_escc(*this, "escc"),
+ m_serial(*this, "serial%u", 0U),
+ m_fifo0(*this, "apfifo0"),
+ m_fifo1(*this, "apfifo1"),
+ m_sonic3(*this, "sonic3"),
+ m_sonic(*this, "sonic"),
+ m_fdc(*this, "fdc"),
+ m_hid(*this, "hid"),
+ m_dmac(*this, "dmac"),
+ m_scsi0(*this, "scsi0:7:spifi3"),
+ m_scsi1(*this, "scsi1:7:spifi3"),
+ m_scsibus0(*this, "scsi0"),
+ m_scsibus1(*this, "scsi1"),
+ m_dip_switch(*this, "FRONT_PANEL"),
+ m_led(*this, "led%u", 0U)
+ {
+ }
+
+ void nws5000x(machine_config &config);
+
+protected:
+
+ enum irq0_number : uint32_t
+ {
+ DMAC = 0x01,
+ SONIC = 0x02,
+ FDC = 0x10
+ };
+
+ enum irq1_number : uint32_t
+ {
+ KBD = 0x01,
+ ESCC = 0x02,
+ AUDIO0 = 0x04,
+ AUDIO1 = 0x08,
+ PARALLEL = 0x20,
+ FB = 0x80
+ };
+
+ enum irq2_number : uint32_t
+ {
+ TIMER0 = 0x01,
+ TIMER1 = 0x02
+ };
+
+ enum irq4_number : uint32_t
+ {
+ APBUS = 0x01
+ };
+
+ enum escc_channel
+ {
+ CHA,
+ CHB
+ };
+
+ enum APBUS_PTE_MASKS
+ {
+ ENTRY_VALID = 0x80000000,
+ ENTRY_COHERENT = 0x40000000,
+ ENTRY_PAD = 0x3FF00000,
+ ENTRY_PAD_SHIFT = 20,
+ ENTRY_PFNUM = 0xFFFFF
+ };
+
+ // APbus DMA page table entry structure
+ // See https://github.com/NetBSD/src/blob/faa527952534272f8f4737347d9fc8c38e070c9c/sys/arch/newsmips/apbus/dmac3reg.h#L71
+ struct apbus_pte
+ {
+ // Unused
+ uint32_t pad;
+ // Entry is OK to use
+ bool valid;
+ // DMA coherence enabled (don't care for emulation??)
+ bool coherent;
+ // Unused
+ uint32_t pad2;
+ // Page number (upper 20 bits of physical address)
+ uint32_t pfnum;
+ };
+
+ // Interrupts and other platform state
+ bool m_ram_map_shift = false;
+ bool m_int_state[6] = {false, false, false, false, false, false};
+ uint32_t m_inten[6] = {0, 0, 0, 0, 0, 0};
+ uint32_t m_intst[6] = {0, 0, 0, 0, 0, 0};
+ uint32_t escc1_int_status = 0;
+ memory_access<64, 3, 0, ENDIANNESS_BIG>::cache m_main_cache;
+
+ // Freerun timer (1us period)
+ // NetBSD source code corroborates the period (https://github.com/NetBSD/src/blob/229cf3aa2cda57ba5f0c244a75ae83090e59c716/sys/arch/newsmips/newsmips/news5000.c#L259)
+ emu_timer *m_freerun_timer;
+ uint32_t m_freerun_timer_val;
+
+ // Hardware timer TIMER0 (10ms period)
+ // TODO: Is this programmable somehow? 100Hz is what the NetBSD kernel expects.
+ emu_timer *m_timer0_timer;
+
+ // MIPS R4400 CPU
+ required_device<r4400_device> m_cpu;
+
+ // Memory
+ required_device<ram_device> m_ram;
+ required_device<ram_device> m_dma_ram;
+ required_device<ram_device> m_net_ram;
+
+ // ST Micro M48T02 Timekeeper NVRAM + RTC
+ required_device<m48t02_device> m_rtc;
+
+ // Zilog ESCC and Sony CXD8421Q ESCC1 serial controller
+ required_device<z80scc_device> m_escc;
+ required_device_array<rs232_port_device, 2> m_serial;
+
+ // Sony CXD8442Q APbus FIFOs
+ required_device<cxd8442q_device> m_fifo0;
+ required_device<cxd8442q_device> m_fifo1;
+
+ // Sony CXD8452AQ WSC-SONIC3 APbus interface
+ required_device<cxd8452aq_device> m_sonic3;
+
+ // SONIC ethernet controller
+ required_device<dp83932c_device> m_sonic;
+
+ // National Semiconductor PC8477B floppy controller
+ required_device<pc8477b_device> m_fdc;
+
+ // NEWS keyboard and mouse
+ required_device<news_hid_hle_device> m_hid;
+
+ // DMAC3 DMA controller
+ required_device<dmac3_device> m_dmac;
+
+ // HP SPIFI3 SCSI controllers
+ required_device<spifi3_device> m_scsi0;
+ required_device<spifi3_device> m_scsi1;
+ required_device<nscsi_bus_device> m_scsibus0;
+ required_device<nscsi_bus_device> m_scsibus1;
+
+ // Front panel
+ required_ioport m_dip_switch;
+ output_finder<6> m_led;
+
+ // Constants
+ static constexpr uint32_t ICACHE_SIZE = 0x4000;
+ static constexpr uint32_t DCACHE_SIZE = 0x4000;
+ static constexpr uint32_t SCACHE_SIZE = 0x100000;
+ static constexpr int FREERUN_FREQUENCY = 1000000; // Hz
+ static constexpr int TIMER0_FREQUENCY = 100; // Hz
+ static constexpr uint32_t APBUS_DMA_MAP_ADDRESS = 0x14c20000;
+ static constexpr uint32_t APBUS_DMA_MAP_RAM_SIZE = 0x20000; // 128 kibibytes
+ static constexpr const char* MAIN_MEMORY_DEFAULT = "64M";
+ static constexpr int interrupt_map[6] = {0, 1, 2, 3, 4, 5};
+
+ static constexpr std::string_view LED_MAP[6] = {"LED_POWER", "LED_DISK", "LED_FLOPPY", "LED_SEC", "LED_NET", "LED_CD"};
+ std::map<offs_t, std::string_view> m_apbus_cmd_decoder
+ {
+ {0x0c, "INTMSK"}, // Interrupt mask
+ {0x14, "INSTST"}, // Interrupt status
+ {0x1c, "BER_ADDR"}, // Bus error address
+ {0x34, "CFG_CTRL"}, // config control
+ {0x5c, "DER_A"}, // DMA error address
+ {0x6c, "DER_S"}, // DMA error slot
+ {0x84, "DMA"} // unmapped DMA coherency (?)
+ };
+
+ // driver_device overrides
+ virtual void machine_start() override;
+ virtual void machine_reset() override;
+
+ void machine_common(machine_config &config);
+
+ // address maps
+ void cpu_map(address_map &map);
+ void sonic3_map(address_map &map);
+ void cpu_map_main_memory(address_map &map);
+ void cpu_map_debug(address_map &map);
+
+ // Interrupts
+ // See news5000 section of https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/include/adrsmap.h
+ void inten_w(offs_t offset, uint32_t data);
+ uint32_t inten_r(offs_t offset);
+ uint32_t intst_r(offs_t offset);
+ void intclr_w(offs_t offset, uint32_t data);
+ void generic_irq_w(uint32_t irq, uint32_t mask, int state);
+
+ template <irq0_number Number>
+ void irq_w(int state) { generic_irq_w(0, Number, state); }
+ template <irq1_number Number>
+ void irq_w(int state) { generic_irq_w(1, Number, state); }
+ template <irq2_number Number>
+ void irq_w(int state) { generic_irq_w(2, Number, state); }
+ template <irq4_number Number>
+ void irq_w(int state) { generic_irq_w(4, Number, state); }
+ void int_check();
+
+ void led_state_w(offs_t offset, uint32_t data);
+ uint64_t front_panel_r(offs_t offset);
+
+ TIMER_CALLBACK_MEMBER(freerun_clock);
+ uint32_t freerun_r(offs_t offset);
+ void freerun_w(offs_t offset, uint32_t data);
+
+ TIMER_CALLBACK_MEMBER(timer0_clock);
+ void timer0_w(offs_t offset, uint32_t data);
+
+ // APbus control (should be split into a device eventually)
+ uint8_t apbus_cmd_r(offs_t offset);
+ void apbus_cmd_w(offs_t offset, uint32_t data);
+ uint32_t apbus_virt_to_phys(uint32_t v_address);
+
+ // RAM accessors
+ uint8_t ram_r(offs_t offset);
+ void ram_w(offs_t offset, uint8_t data);
+
+ // CXD8421Q glue logic and secondary control for the ESCC
+ // This belongs in a separate device if the other features are ever implemented.
+ uint32_t escc1_int_status_r(offs_t offset);
+ void escc1_int_status_w(offs_t offset, uint32_t data);
+ uint32_t escc_ch_read(escc_channel channel, offs_t offset);
+ void escc_ch_write(escc_channel channel, offs_t offset, uint32_t data);
+ template <escc_channel Channel>
+ uint32_t escc_ch_read(offs_t offset) { return escc_ch_read(Channel, offset); }
+ template <escc_channel Channel>
+ void escc_ch_write(offs_t offset, uint32_t data) { escc_ch_write(Channel, offset, data); }
+};
+
+/*
+ * news_scsi_devices
+ *
+ * Declares the SCSI devices supported by this driver.
+ */
+static void news_scsi_devices(device_slot_interface &device)
+{
+ device.option_add("harddisk", NSCSI_HARDDISK);
+ device.option_add("cdrom", NSCSI_CDROM_NEWS);
+}
+
+/*
+ * machine_common
+ *
+ * Creates an emulated R4400-based NEWS platform.
+ */
+void news_r4k_state::machine_common(machine_config &config)
+{
+ // TODO: clock divisor support (config[30:28] = 0xb001) in r4000.cpp
+ R4400(config, m_cpu, 75_MHz_XTAL, true, SCACHE_SIZE, 0x40);
+ m_cpu->set_addrmap(AS_PROGRAM, &news_r4k_state::cpu_map);
+
+ RAM(config, m_ram);
+ m_ram->set_default_size(MAIN_MEMORY_DEFAULT);
+
+ RAM(config, m_dma_ram);
+ m_dma_ram->set_default_size("128KB");
+
+ RAM(config, m_net_ram);
+ m_net_ram->set_default_size("32KB");
+
+ M48T02(config, m_rtc);
+
+ SCC85230(config, m_escc, 9.8304_MHz_XTAL);
+
+ RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal");
+ m_serial[0]->cts_handler().set(m_escc, FUNC(z80scc_device::ctsa_w));
+ m_serial[0]->dcd_handler().set(m_escc, FUNC(z80scc_device::dcda_w));
+ m_serial[0]->rxd_handler().set(m_escc, FUNC(z80scc_device::rxa_w));
+ m_escc->out_rtsa_callback().set(m_serial[0], FUNC(rs232_port_device::write_rts));
+ m_escc->out_txda_callback().set(m_serial[0], FUNC(rs232_port_device::write_txd));
+ m_escc->out_dtra_callback().set(m_serial[0], FUNC(rs232_port_device::write_dtr));
+
+ RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr);
+ m_serial[1]->cts_handler().set(m_escc, FUNC(z80scc_device::ctsb_w));
+ m_serial[1]->dcd_handler().set(m_escc, FUNC(z80scc_device::dcdb_w));
+ m_serial[1]->rxd_handler().set(m_escc, FUNC(z80scc_device::rxb_w));
+ m_escc->out_rtsb_callback().set(m_serial[1], FUNC(rs232_port_device::write_rts));
+ m_escc->out_txdb_callback().set(m_serial[1], FUNC(rs232_port_device::write_txd));
+ m_escc->out_dtrb_callback().set(m_serial[1], FUNC(rs232_port_device::write_dtr));
+ m_escc->out_int_callback().set(FUNC(news_r4k_state::irq_w<ESCC>));
+
+ CXD8442Q(config, m_fifo0, 0);
+ CXD8442Q(config, m_fifo1, 0);
+
+ // Reverse polarity for ESCC DMA signals
+ m_escc->out_dtra_callback().set(
+ [this] (int status)
+ { m_fifo1->drq_w<cxd8442q_device::fifo_channel_number::CH2>(!status); });
+ m_escc->out_wreqa_callback().set(
+ [this] (int status)
+ { m_fifo1->drq_w<cxd8442q_device::fifo_channel_number::CH3>(!status); });
+ m_fifo1->dma_w_cb<cxd8442q_device::fifo_channel_number::CH2>().set(
+ [this] (uint32_t data)
+ { m_escc->da_w(0, data); });
+ m_fifo1->dma_r_cb<cxd8442q_device::fifo_channel_number::CH3>().set(
+ [this] ()
+ { return m_escc->da_r(0); });
+ m_fifo1->out_int_callback().set(
+ [this] (int status)
+ {
+ // Not sure if this is the actual mapping, or if sending any level 0 interrupt causes
+ // NEWS-OS to scan the ESCC and FIFO registers. It seems to work OK, but should be
+ // revisited if the exact details can be worked out.
+ generic_irq_w(0, 0x4, status);
+ escc1_int_status = status ? 0x8 : 0x0; // guess
+ });
+
+ CXD8452AQ(config, m_sonic3, 0);
+ m_sonic3->set_addrmap(0, &news_r4k_state::sonic3_map);
+ m_sonic3->irq_out().set(FUNC(news_r4k_state::irq_w<irq0_number::SONIC>));
+ m_sonic3->set_bus(m_cpu, 0);
+ m_sonic3->set_apbus_address_translator(FUNC(news_r4k_state::apbus_virt_to_phys));
+
+ // TODO: actual clock frequency - there is a 20MHz crystal nearby on the board, but traces need to be confirmed
+ DP83932C(config, m_sonic, 20'000'000);
+ m_sonic->out_int_cb().set(m_sonic3, FUNC(cxd8452aq_device::irq_w));
+ m_sonic->set_bus(m_sonic3, 1);
+
+ // Use promiscuous mode to force network driver to accept all packets, since SONIC has its own filter (CAM table)
+ // Not sure if needing to use this means something else isn't set up correctly.
+ m_sonic->set_promisc(true);
+
+ // Unlike 68k and R3000 NEWS machines, the keyboard and mouse seem to share an interrupt
+ // See https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/apbus/ms_ap.c#L103
+ // where the mouse interrupt handler is initialized using the Keyboard interrupt.
+ NEWS_HID_HLE(config, m_hid);
+ m_hid->irq_out<news_hid_hle_device::KEYBOARD>().set(FUNC(news_r4k_state::irq_w<KBD>));
+ m_hid->irq_out<news_hid_hle_device::MOUSE>().set(FUNC(news_r4k_state::irq_w<KBD>));
+
+ PC8477B(config, m_fdc, 24_MHz_XTAL, pc8477b_device::mode_t::PS2);
+ FLOPPY_CONNECTOR(config, "fdc:0", "35hd", FLOPPY_35_HD, true, floppy_image_device::default_pc_floppy_formats).enable_sound(false);
+
+ // Note - FDC IRQ might go through the FIFO first. Might need to be updated in the future.
+ m_fdc->intrq_wr_callback().set(FUNC(news_r4k_state::irq_w<irq0_number::FDC>));
+ m_fdc->drq_wr_callback().set(
+ [this] (int status)
+ { m_fifo0->drq_w<cxd8442q_device::fifo_channel_number::CH2>(status); });
+ m_fifo0->dma_r_cb<cxd8442q_device::fifo_channel_number::CH2>().set(
+ [this] ()
+ { return uint32_t(m_fdc->dma_r()); });
+
+ DMAC3(config, m_dmac, 0);
+ m_dmac->set_apbus_address_translator(FUNC(news_r4k_state::apbus_virt_to_phys));
+ m_dmac->set_bus(m_cpu, 0);
+ m_dmac->irq_out().set(FUNC(news_r4k_state::irq_w<DMAC>));
+
+ NSCSI_BUS(config, m_scsibus0);
+ NSCSI_BUS(config, m_scsibus1);
+
+ NSCSI_CONNECTOR(config, "scsi0:0", news_scsi_devices, "harddisk");
+ NSCSI_CONNECTOR(config, "scsi0:1", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi0:2", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi0:3", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi0:4", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi0:5", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi0:6", news_scsi_devices, "cdrom");
+
+ NSCSI_CONNECTOR(config, "scsi1:0", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi1:1", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi1:2", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi1:3", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi1:4", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi1:5", news_scsi_devices, nullptr);
+ NSCSI_CONNECTOR(config, "scsi1:6", news_scsi_devices, nullptr);
+
+ // TODO: Actual SPIFI3 clock frequency
+ NSCSI_CONNECTOR(config, "scsi0:7").option_set("spifi3", SPIFI3)
+ .clock(16'000'000)
+ .machine_config(
+ [this](device_t *device)
+ {
+ spifi3_device &adapter = dynamic_cast<spifi3_device &>(*device);
+ adapter.irq_handler_cb().set(m_dmac, FUNC(dmac3_device::irq_w<dmac3_device::CTRL0>));
+ adapter.drq_handler_cb().set(m_dmac, FUNC(dmac3_device::drq_w<dmac3_device::CTRL0>));
+ });
+ NSCSI_CONNECTOR(config, "scsi1:7").option_set("spifi3", SPIFI3)
+ .clock(16'000'000)
+ .machine_config(
+ [this](device_t *device)
+ {
+ spifi3_device &adapter = dynamic_cast<spifi3_device &>(*device);
+ adapter.irq_handler_cb().set(m_dmac, FUNC(dmac3_device::irq_w<dmac3_device::CTRL1>));
+ adapter.drq_handler_cb().set(m_dmac, FUNC(dmac3_device::drq_w<dmac3_device::CTRL1>));
+ });
+
+ m_dmac->dma_r_cb<dmac3_device::CTRL0>().set(m_scsi0, FUNC(spifi3_device::dma_r));
+ m_dmac->dma_w_cb<dmac3_device::CTRL0>().set(m_scsi0, FUNC(spifi3_device::dma_w));
+ m_dmac->dma_r_cb<dmac3_device::CTRL1>().set(m_scsi1, FUNC(spifi3_device::dma_r));
+ m_dmac->dma_w_cb<dmac3_device::CTRL1>().set(m_scsi1, FUNC(spifi3_device::dma_w));
+}
+
+void news_r4k_state::nws5000x(machine_config &config) { machine_common(config); }
+
+/*
+ * cpu_map
+ *
+ * Assign the address map for the CPU
+ * References:
+ * - https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/include/adrsmap.h
+ * - APmonitor device table (Run APmonitor command `ss -d` for details)
+ */
+void news_r4k_state::cpu_map(address_map &map)
+{
+ // Unmapping to high causes the APmonitor to wait a huge amount of time in between
+ // printing serial characters - what is weird is that manually setting the unmapped
+ // reads to return zero while unmapping everything high doesn't fix this issue, so
+ // there must be a side effect caused by unmapping high somewhere.
+ map.unmap_value_low();
+
+ cpu_map_main_memory(map);
+
+ // NEWS firmware
+ map(0x1fc00000, 0x1fc3ffff).rom().region("mrom", 0);
+ map(0x1f3c0000, 0x1f3c03ff).rom().region("idrom", 0);
+ map(0x1e600000, 0x1e6003ff).rom().region("macrom", 0);
+
+ // Front panel DIP switches
+ map(0x1f3d0000, 0x1f3d0007).r(FUNC(news_r4k_state::front_panel_r));
+
+ // Hardware timers
+ map(0x1f800000, 0x1f800003).w(FUNC(news_r4k_state::timer0_w));
+ map(0x1f840000, 0x1f840003).rw(FUNC(news_r4k_state::freerun_r), FUNC(news_r4k_state::freerun_w));
+
+ // Timekeeper NVRAM and RTC
+ map(0x1f880000, 0x1f881fff).rw(m_rtc, FUNC(m48t02_device::read), FUNC(m48t02_device::write)).umask32(0x000000ff);
+
+ // Interrupt ports
+ map(0x1f4e0000, 0x1f4e0017).w(FUNC(news_r4k_state::intclr_w));
+ map(0x1fa00000, 0x1fa00017).rw(FUNC(news_r4k_state::inten_r), FUNC(news_r4k_state::inten_w));
+ map(0x1fa00020, 0x1fa00037).r(FUNC(news_r4k_state::intst_r));
+
+ // LEDs
+ map(0x1f3f0000, 0x1f3f0017).w(FUNC(news_r4k_state::led_state_w));
+
+ // APbus
+ map(0x14c00000, 0x14c0008f).rw(FUNC(news_r4k_state::apbus_cmd_r), FUNC(news_r4k_state::apbus_cmd_w));
+ map(0x14c20000, 0x14c3ffff).lrw8(NAME([this](offs_t offset)
+ { return m_dma_ram->read(offset); }),
+ NAME([this](offs_t offset, uint8_t data)
+ {
+ LOGMASKED(LOG_MEMORY, "Write to DMA map @ offset 0x%x: 0x%x (%s)\n", offset, data, machine().describe_context());
+ m_dma_ram->write(offset, data);
+ }));
+
+ // ESCC + WSC-ESCC1 (CXD8421Q) serial controller
+ map(0x1e900000, 0x1e93ffff).m(m_fifo1, FUNC(cxd8442q_device::map));
+ map(0x1e940000, 0x1e94000f).rw(FUNC(news_r4k_state::escc_ch_read<CHB>), FUNC(news_r4k_state::escc_ch_write<CHB>));
+ map(0x1e950000, 0x1e95000f).rw(FUNC(news_r4k_state::escc_ch_read<CHA>), FUNC(news_r4k_state::escc_ch_write<CHA>));
+ map(0x1e960000, 0x1e960003).rw(FUNC(news_r4k_state::escc1_int_status_r), FUNC(news_r4k_state::escc1_int_status_w));
+
+ // Interestingly, this is also the RAM that is used before main memory is initialized
+ // It is also the only RAM avaliable if "no memory mode" is set (DIP switch #8)
+ map(0x1e980000, 0x1e99ffff).m(m_fifo1, FUNC(cxd8442q_device::map_fifo_ram));
+
+ // SONIC3 APbus interface and SONIC ethernet controller
+ map(0x1e500000, 0x1e50003f).m(m_sonic3, FUNC(cxd8452aq_device::map));
+ map(0x1e610000, 0x1e6101ff).m(m_sonic, FUNC(dp83932c_device::map)).umask64(0x000000000000ffff);
+ map(0x1e620000, 0x1e627fff).lrw8(NAME([this](offs_t offset)
+ { return m_net_ram->read(offset); }),
+ NAME([this](offs_t offset, uint8_t data)
+ {
+ LOGMASKED(LOG_MEMORY, "Host write to net RAM @ offset 0x%x: 0x%x (%s)\n", offset, data, machine().describe_context());
+ m_net_ram->write(offset, data);
+ }));
+
+ // DMAC3 DMA Controller
+ map(0x1e200000, 0x1e200017).m(m_dmac, FUNC(dmac3_device::map<dmac3_device::CTRL0>));
+ map(0x1e300000, 0x1e300017).m(m_dmac, FUNC(dmac3_device::map<dmac3_device::CTRL1>));
+
+ // SPIFI SCSI controllers
+ map(0x1e280000, 0x1e2803ff).m(m_scsi0, FUNC(spifi3_device::map));
+ map(0x1e380000, 0x1e3803ff).m(m_scsi1, FUNC(spifi3_device::map));
+
+ // HID (kb + ms)
+ map(0x1f900000, 0x1f900047).m(m_hid, FUNC(news_hid_hle_device::map_apbus));
+
+ // FDC controller register mapping
+ // TODO: to be hardware accurate, these shouldn't be umasked.
+ // instead, they should be duplicated across each 32-bit segment to emulate the open address lines
+ // (i.e. status register A and B values of 56 c0 look like 56565656 c0c0c0c0)
+ map(0x1ed60000, 0x1ed6001f).m(m_fdc, FUNC(pc8477b_device::map)).umask32(0x000000ff);
+
+ // The NEWS monitor ROM FDC routines won't run if DRV2 is inactive.
+ // TODO: The 5000X only has a single floppy drive. Why is this needed?
+ map(0x1ed60000, 0x1ed60003).lr8(NAME([this](offs_t offset)
+ { return m_fdc->sra_r() & ~0x40; }))
+ .umask32(0x000000ff);
+
+ // TODO: Floppy aux registers
+ map(0x1ed60200, 0x1ed60207).noprw();
+ map(0x1ed60200, 0x1ed60207).lr8(NAME([](offs_t offset)
+ { return 0x1; }));
+
+ // Map FDCAUX interrupt read (TODO: still need to implement button interrupt mask from 208-20b)
+ map(0x1ed60208, 0x1ed6020f).lr32(NAME([this](offs_t offset)
+ { return (offset == 1) && ((m_intst[0] & irq0_number::FDC) > 0) ? 0x2 : 0x0; }));
+
+ // Map FDC and sound FIFO register file
+ map(0x1ed00000, 0x1ed3ffff).m(m_fifo0, FUNC(cxd8442q_device::map));
+ map(0x1ed80000, 0x1ed9ffff).m(m_fifo0, FUNC(cxd8442q_device::map_fifo_ram));
+
+ // TODO: DSC-39 xb framebuffer/video card (0x14900000)
+ map(0x14900000, 0x149fffff).lr8(NAME([](offs_t offset)
+ { return 0xff; }));
+
+ // TODO: sb sound subsystem (0x1ed00000)
+ // TODO: lp line printer subsystem (0x1ed30000)
+
+ // Assign debug mappings
+ cpu_map_debug(map);
+}
+
+/*
+ * cpu_map_main_memory
+ *
+ * Maps the main memory and supporting items into the provided address map.
+ * TODO: Memory controller logic for non-64MB configurations
+ */
+void news_r4k_state::cpu_map_main_memory(address_map &map)
+{
+ // The mapping mechanism was determined experimentally and only supports the 64MB configuration for now.
+ // This works for the monitor ROM, NEWS-OS kernel, and NetBSD kernel but will not work with other amounts of RAM.
+ map(0x0, 0x7ffffff).rw(FUNC(news_r4k_state::ram_r), FUNC(news_r4k_state::ram_w));
+
+ // Not 100% sure where the below lives on the system. This seems to be related to memory and perhaps APbus init.
+ map(0x14400000, 0x14400003).lr32(NAME([](offs_t offset)
+ { return 0x0; }));
+ map(0x14400004, 0x14400007).lr32(NAME([](offs_t offset)
+ { return 0x3ff17; }));
+ map(0x14400024, 0x14400027).lr32(NAME([](offs_t offset)
+ { return 0x600a4; }));
+ map(0x1440003c, 0x1440003f)
+ .lw32(NAME([this](offs_t offset, uint32_t data)
+ {
+ if (data == 0x10001)
+ {
+ LOGMASKED(LOG_GENERAL, "Enabling RAM map shift!\n");
+ m_ram_map_shift = true;
+ }
+ else
+ {
+ LOGMASKED(LOG_GENERAL, "Disabling RAM map shift!\n");
+ m_ram_map_shift = false;
+ }
+ }));
+}
+
+/*
+ * cpu_map_debug
+ *
+ * Method with temporary address map assignments. Everything in this function can be moved to the main memory
+ * map function once it is understood. This separates the "real" mapping from the workarounds required to get
+ * the monitor ROM to boot due to missing information or incomplete emulation.
+ */
+void news_r4k_state::cpu_map_debug(address_map &map)
+{
+ // APbus WBFLUSH + unknown
+ map(0x1f520000, 0x1f520017).nopw();
+ map(0x1f520000, 0x1f520017).lr8(NAME([](offs_t offset)
+ {
+ uint8_t value = 0x0;
+ if (offset == 7)
+ {
+ value = 0x1;
+ }
+ else if (offset == 11)
+ {
+ value = 0x1;
+ }
+ else if (offset == 15)
+ {
+ value = 0xc8;
+ }
+ else if (offset == 19)
+ {
+ value = 0x32;
+ }
+
+ return value;
+ }));
+
+ // More onboard devices that needs to be mapped for the platform to boot
+ map(0x1fe00000, 0x1fe03fff).ram().mirror(0x1fc000);
+ map(0x1f3e0000, 0x1f3effff).lr8(NAME([](offs_t offset)
+ {
+ uint8_t value = 0x0;
+ if (offset % 4 == 2)
+ {
+ value = 0x6f;
+ }
+ else if (offset % 4 == 3)
+ {
+ value = 0xe0;
+ }
+
+ return value;
+ }));
+}
+
+/*
+ * sonic3_map
+ *
+ * Memory accessor for the SONIC. The SONIC3 ASIC arbitrates memory access for the SONIC. The host platform can
+ * access SONIC's memory directly, but SONIC may not access the bus directly.
+ */
+void news_r4k_state::sonic3_map(address_map &map)
+{
+ map(0x0, 0x7fff).lrw8(NAME([this](offs_t offset)
+ { return m_net_ram->read(offset); }),
+ NAME([this](offs_t offset, uint8_t data)
+ { m_net_ram->write(offset, data); }));
+}
+
+/*
+ * ram_r
+ *
+ * Method that returns the byte at `offset` in main memory.
+ * TODO: Memory controller logic for non-64MB configurations
+ */
+uint8_t news_r4k_state::ram_r(offs_t offset)
+{
+ uint8_t result = 0xff;
+ if ((offset <= 0x1ffffff) || (m_ram_map_shift && offset <= 0x3ffffff))
+ {
+ result = m_ram->read(offset);
+ }
+ else if (!m_ram_map_shift && offset >= 0x7f00000) // upper 32MB before it is mapped into contiguous space
+ {
+ result = m_ram->read(offset - 0x5f00000);
+ }
+ else
+ {
+ LOGMASKED(LOG_GENERAL, "Unmapped RAM read attempted at offset 0x%x\n", offset);
+ }
+ return result;
+}
+
+/*
+ * ram_w
+ *
+ * Method that writes the byte `data` at `offset` in main memory.
+ * TODO: Memory controller logic for non-64MB configurations
+ */
+void news_r4k_state::ram_w(offs_t offset, uint8_t data)
+{
+ if ((offset <= 0x1ffffff) || (m_ram_map_shift && offset <= 0x3ffffff))
+ {
+ m_ram->write(offset, data);
+ }
+ else if (!m_ram_map_shift && offset >= 0x7f00000) // upper 32MB before it is mapped into contiguous space
+ {
+ m_ram->write(offset - 0x5f00000, data);
+ }
+ else
+ {
+ LOGMASKED(LOG_GENERAL, "Unmapped RAM write attempted at offset 0x%x (data: 0x%x)\n", offset, data);
+ }
+}
+
+/*
+ * machine_start
+ *
+ * Initialization method called when the machine first starts.
+ */
+void news_r4k_state::machine_start()
+{
+ // Init front panel LEDs
+ m_led.resolve();
+
+ // Save state support
+ save_item(NAME(m_inten));
+ save_item(NAME(m_intst));
+ save_item(NAME(m_int_state));
+ save_item(NAME(m_freerun_timer_val));
+
+ // Allocate hardware timers
+ m_freerun_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(news_r4k_state::freerun_clock), this));
+ m_timer0_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(news_r4k_state::timer0_clock), this));
+
+ // Cache for main bus access (APbus address translation)
+ m_cpu->space(0).cache(m_main_cache);
+}
+
+/*
+ * freerun_clock
+ *
+ * Method to handle a tick of the freerunning clock.
+ */
+TIMER_CALLBACK_MEMBER(news_r4k_state::freerun_clock)
+{
+ m_freerun_timer_val++;
+}
+
+/*
+ * timer0_clock
+ *
+ * Method to handle a tick of the TIMER0 hardware clock.
+ */
+TIMER_CALLBACK_MEMBER(news_r4k_state::timer0_clock)
+{
+ // Whenever this timer elapses, we need to trigger the timer0 interrupt
+ m_intst[2] |= irq2_number::TIMER0;
+ int_check();
+}
+
+/*
+ * machine_reset
+ *
+ * Method called whenever the machine is reset.
+ */
+void news_r4k_state::machine_reset()
+{
+ m_freerun_timer_val = 0;
+ m_freerun_timer->adjust(attotime::zero, 0, attotime::from_hz(FREERUN_FREQUENCY));
+ m_timer0_timer->adjust(attotime::zero, 0, attotime::from_hz(TIMER0_FREQUENCY));
+}
+
+/*
+ * front_panel_r
+ *
+ * Method to read the state of the front panel DIP switches.
+ */
+uint64_t news_r4k_state::front_panel_r(offs_t offset)
+{
+ ioport_value dipsw = m_dip_switch->read();
+ dipsw |= 0xff00; // Matches physical platform
+ return ((uint64_t)dipsw << 32) | dipsw;
+}
+
+/*
+ * led_state_w
+ *
+ * Sets and logs the status of the front panel LEDs.
+ */
+void news_r4k_state::led_state_w(offs_t offset, uint32_t data)
+{
+ if (m_led[offset] != data)
+ {
+ LOGMASKED(LOG_LED, "%s: %s\n", LED_MAP[offset], data ? "ON" : "OFF");
+ m_led[offset] = data;
+ }
+}
+
+/*
+ * apbus_virt_to_phys
+ *
+ * The APbus has its own virtual->physical addressing scheme. Like the CPU's TLB, the host OS
+ * is responsible for populating the APbus TLB. The `apbus_pte` struct defines what each entry looks like. Note that to avoid
+ * bit-order dependencies and other platform-specific stuff, in this implementation, valid, coherent, pad2, and pfnum are
+ * separate variables, but the actual register is packed like this:
+ *
+ * 0x xxxx xxxx xxxx xxxx
+ * | pad | entry |
+ *
+ * Entry is packed as follows:
+ * 0b x x xxxxxxxxxx xxxxxxxxxxxxxxxxxxxx
+ * |valid|coherent| pad2 | pfnum |
+ *
+ * The APbus requires RAM to hold the TLB. On the NWS-5000X, this is 128KiB starting at physical address 0x14c20000 (and goes to 0x14c3ffff)
+ *
+ * For example, the monitor ROM populates the PTEs as follows in response to a `dl` command.
+ * Addr PTE1 PTE2
+ * 0x14c20000 0000000080103ff5 0000000080103ff6
+ *
+ * It also loads the `address` register of the DMAC3 with 0xd60.
+ *
+ * This will cause the consuming ASIC (in this case, the DMAC3) to start mapping from virtual address 0xd60 to physical address 0x3ff5d60.
+ * If the address register goes beyond 0xFFF, bit 12 will increment. This will increase the page number so the virtual address will be
+ * 0x1000, and will cause the DMAC to use the next PTE (in this case, the next sequential page, 0x3ff6000).
+ *
+ * NetBSD splits the mapping RAM into two sections, one for each DMAC3 controller. If the OS does not keep track, the ASICs
+ * could end up in a configuration that would cause them to overwrite each other's data. NEWS-OS makes even more extensive
+ * use of APbus DMA, including the WSC-SONIC3 for network DMA.
+ *
+ * Another note: NetBSD mentions that the `pad2` section of the register is 10 bits. However, this might not be fully accurate.
+ * On the NWS-5000X, the physical address bus is 36 bits because it has an R4400SC. The 32nd bit is sometimes set, depending
+ * on the virtual address being used (maybe it goes to the memory controller). It doesn't impact the normal operation of the
+ * computer, but does mean that the `pad2` section might only be 6 bits, not 10 bits.
+ */
+uint32_t news_r4k_state::apbus_virt_to_phys(uint32_t v_address)
+{
+ // Convert page number to PTE address and read raw PTE data from the APbus DMA mapping RAM
+ uint32_t apbus_page_address = APBUS_DMA_MAP_ADDRESS + 8 * (v_address >> 12);
+ if (apbus_page_address >= (APBUS_DMA_MAP_ADDRESS + APBUS_DMA_MAP_RAM_SIZE))
+ {
+ fatalerror("%s: APbus address decoder ran past the bounds of the DMA map RAM!", tag());
+ }
+
+ uint64_t raw_pte = m_main_cache.read_qword(apbus_page_address);
+
+ // Marshal raw data to struct
+ apbus_pte pte;
+ pte.valid = raw_pte & ENTRY_VALID;
+ pte.coherent = raw_pte & ENTRY_COHERENT;
+ pte.pad2 = (raw_pte & ENTRY_PAD) >> ENTRY_PAD_SHIFT;
+ pte.pfnum = raw_pte & ENTRY_PFNUM;
+
+ // This might be able to trigger some kind of interrupt - for now, treat as a fatal error.
+ // However, based on the NetBSD source, this would likely trigger INT4.
+ if (!pte.valid)
+ {
+ fatalerror("%s: APbus DMA referenced invalid entry! Raw PTE (v_address = 0x%x, page_address = 0x%x): 0x%x\n", tag(), v_address, apbus_page_address, raw_pte);
+ }
+
+ // Since the entry is valid, use it to calculate the physical address
+ return (pte.pfnum << 12) + (v_address & 0xFFF);
+}
+
+/*
+ * apbus_cmd_r
+ *
+ * Emulates a call to the APbus controller (placeholder)
+ */
+uint8_t news_r4k_state::apbus_cmd_r(offs_t offset)
+{
+ // This function spoofs the APbus being fully initialized
+ // More may have to be added to this method in the future
+ uint8_t value = 0x0;
+ if (offset == 7)
+ {
+ value = 0x1;
+ }
+ else if (offset == 11)
+ {
+ value = 0x1;
+ }
+ else if (offset == 15)
+ {
+ value = 0xc8;
+ }
+ else if (offset == 19)
+ {
+ value = 0x32;
+ }
+
+ LOGMASKED(LOG_APBUS, "APbus read triggered at offset 0x%x, returning 0x%x\n", offset, value);
+ return value;
+}
+
+/*
+ * apbus_cmd_w
+ *
+ * Emulates a call to the APbus controller (placeholder)
+ */
+void news_r4k_state::apbus_cmd_w(offs_t offset, uint32_t data)
+{
+ // Don't take the performance hit unless user cares about logs
+#if (VERBOSE & LOG_APBUS) > 0
+ auto cmd_iter = m_apbus_cmd_decoder.find(offset);
+ if (cmd_iter != m_apbus_cmd_decoder.end())
+ {
+ LOGMASKED(LOG_APBUS, "APbus %s command invoked, data 0x%x\n", cmd_iter->second, data);
+ }
+ else
+ {
+ LOGMASKED(LOG_APBUS, "Unknown APbus command called, offset 0x%x, set to 0x%x\n", offset, data);
+ }
+#endif
+}
+
+/*
+ * freerun_r
+ *
+ * Get the current value of the freerunning clock
+ */
+uint32_t news_r4k_state::freerun_r(offs_t offset)
+{
+ return m_freerun_timer_val;
+}
+
+/*
+ * freerun_w
+ *
+ * Set the current value of the freerunning clock
+ */
+void news_r4k_state::freerun_w(offs_t offset, uint32_t data)
+{
+ LOGMASKED(LOG_INTERRUPT, "freerun_w: Set freerun timer to 0x%x\n", data);
+ m_freerun_timer_val = data;
+}
+
+/*
+ * timer0_w
+ *
+ * Clears the TIMER0 interrupt and starts the timer again.
+ * See https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/newsmips/news5000.c#L114
+ */
+void news_r4k_state::timer0_w(offs_t offset, uint32_t data)
+{
+ m_intst[2] &= ~0x1;
+ int_check();
+}
+
+/*
+ * inten_w
+ *
+ * Update the enable status of the interrupt indicated by `offset`.
+ */
+void news_r4k_state::inten_w(offs_t offset, uint32_t data)
+{
+ LOGMASKED(LOG_INTERRUPT, "inten_w: INTEN%d = 0x%x\n", offset, data);
+ m_inten[offset] = data;
+ int_check();
+}
+
+/*
+ * inten_r
+ *
+ * Get the enable status of the interrupt indicated by `offset`.
+ */
+uint32_t news_r4k_state::inten_r(offs_t offset)
+{
+ LOGMASKED(LOG_ALL_INTERRUPT, "inten_r: INTEN%d = 0x%x\n", offset, m_inten[offset]);
+ return m_inten[offset];
+}
+
+/*
+ * intst_r
+ *
+ * Get the status of the interrupt indicated by `offset`.
+ */
+uint32_t news_r4k_state::intst_r(offs_t offset)
+{
+ LOGMASKED(LOG_ALL_INTERRUPT, "intst_r: INTST%d = 0x%x\n", offset, m_intst[offset]);
+ return m_intst[offset];
+}
+
+/*
+ * generic_irq_w
+ *
+ * Set or clear the provided interrupt using `mask`.
+ */
+void news_r4k_state::generic_irq_w(uint32_t irq, uint32_t mask, int state)
+{
+ LOGMASKED(LOG_INTERRUPT, "generic_irq_w: INTST%d IRQ %d set to %d\n", irq, mask, state);
+ if (state)
+ {
+ m_intst[irq] |= mask;
+ }
+ else
+ {
+ m_intst[irq] &= ~mask;
+ }
+ int_check();
+}
+
+/*
+ * intclr_w
+ *
+ * Clear the provided interrupt.
+ */
+void news_r4k_state::intclr_w(offs_t offset, uint32_t data)
+{
+ // TODO: Haven't found much that uses INTCLR. Might be some nuances to this that will be discovered later.
+ LOGMASKED(LOG_INTERRUPT, "intclr_w: INTCLR%d = 0x%x\n", offset, data);
+ m_intst[offset] &= ~data;
+ int_check();
+}
+
+/*
+ * int_check
+ *
+ * Observes the platform state and updates the R4400's interrupt lines if needed.
+ */
+void news_r4k_state::int_check()
+{
+ // The R4000 series has 6 bits in the interrupt register
+ // These map to the 6 INTST/EN/CLR groups on the NEWS platform
+ // See https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/newsmips/news5000.c
+ // and https://github.com/NetBSD/src/blob/trunk/sys/arch/newsmips/apbus/apbus.c
+ // This has been tested with a few different devices and seems OK so far, but there might be some things missing.
+ for (int i = 0; i < 6; i++)
+ {
+ bool state = m_intst[i] & m_inten[i];
+
+ if (m_int_state[i] != state)
+ {
+ LOGMASKED(LOG_ALL_INTERRUPT, "Setting CPU input line %d to %d\n", interrupt_map[i], state ? 1 : 0);
+ m_int_state[i] = state;
+ m_cpu->set_input_line(interrupt_map[i], state ? 1 : 0);
+ }
+ }
+}
+
+/*
+ * escc_ch_read
+ *
+ * Accesses the read port of the specified ESCC channel.
+ */
+uint32_t news_r4k_state::escc_ch_read(escc_channel channel, offs_t offset)
+{
+ if (offset < 2)
+ {
+ offset |= (channel == CHA ? 0x2 : 0x0);
+ return m_escc->ab_dc_r(offset);
+ }
+ else
+ {
+ // Placeholder for ESCC1 extport and ctl registers
+ LOG("escc1 ch%d r non-passthrough: 0x%x\n", channel, offset);
+ return 0x0;
+ }
+}
+
+/*
+ * escc_ch_read
+ *
+ * Writes `data` to the ESCC specified by channel.
+ */
+void news_r4k_state::escc_ch_write(escc_channel channel, offs_t offset, uint32_t data)
+{
+ if (offset < 2)
+ {
+ offset |= (channel == CHA ? 0x2 : 0x0);
+ m_escc->ab_dc_w(offset, data);
+ }
+ else
+ {
+ // Placeholder for ESCC1 extport and ctl registers
+ LOG("escc1 ch%d w non-passthrough: 0x%x = 0x%x\n", channel, offset, data);
+ return;
+ }
+}
+
+/*
+ * escc1_int_status_r
+ *
+ * Reads the ESCC1 interrupt status register. This is used in conjunction with the ESCC and FIFO chips
+ * to provide asynchronous duplex serial communication.
+ */
+uint32_t news_r4k_state::escc1_int_status_r(offs_t offset)
+{
+ LOGMASKED(LOG_ESCC, "Read ESCC1 int status 0x%x (%s)\n", escc1_int_status, machine().describe_context());
+ return escc1_int_status;
+}
+
+/*
+ * escc1_int_status_w
+ *
+ * Clears the ESCC1 interrupt status register. This is used in conjunction with the ESCC and FIFO chips
+ * to provide asynchronous duplex serial communication.
+ */
+void news_r4k_state::escc1_int_status_w(offs_t offset, uint32_t data)
+{
+ // assume clear for now
+ LOGMASKED(LOG_ESCC, "Clear ESCC1 int status (%s)\n", machine().describe_context());
+ escc1_int_status = 0;
+}
+
+/*
+ * NWS-5000X DIP switches
+ *
+ * 1. Console - switch between serial and bitmap console. Bitmap is not implemented yet.
+ * 2. Bitmap Disable - Enable or disable the internal video card
+ * 3. Abort/Resume Enable - Unknown, NEWS-OS refers to this but doesn't elaborate as to what it does
+ * 4. Clear NVRAM - Upon boot, clear NVRAM contents and restore default values if set
+ * 5. Auto Boot - Upon boot, automatically attempt to boot from the disk specified by the bootdev NVRAM variable
+ * 6. Run Diagnostic Test - Attempt to run diagnostic test after ROM monitor has booted
+ * 7. External APbus Slot Probe Disable - If set, do not attempt to probe the expansion APbus slots
+ * 8. No Memory Mode - If set, do not use the main memory (limits system to 128KiB)
+ *
+ * On real hardware, the default for sw7 is off (probe external APslots).
+ * Since the APbus expansion slots are not emulated yet, setting this avoids a harmless error message during the monitor ROM boot sequence
+ */
+static INPUT_PORTS_START(nws5000)
+ PORT_START("FRONT_PANEL")
+ PORT_DIPNAME(0x01, 0x00, "Console")
+ PORT_DIPLOCATION("FRONT_PANEL:1")
+ PORT_DIPSETTING(0x00, "Serial Terminal")
+ PORT_DIPSETTING(0x01, "Bitmap")
+ PORT_DIPNAME(0x02, 0x00, "Bitmap Disable")
+ PORT_DIPLOCATION("FRONT_PANEL:2")
+ PORT_DIPSETTING(0x00, "Enable Built-in Bitmap")
+ PORT_DIPSETTING(0x02, "Disable Built-in Bitmap")
+ PORT_DIPNAME(0x04, 0x00, "Abort/Resume Enable")
+ PORT_DIPLOCATION("FRONT_PANEL:3")
+ PORT_DIPSETTING(0x00, "Disable Abort/Resume")
+ PORT_DIPSETTING(0x04, "Enable Abort/Resume")
+ PORT_DIPNAME(0x08, 0x00, "Clear NVRAM")
+ PORT_DIPLOCATION("FRONT_PANEL:4")
+ PORT_DIPSETTING(0x00, "Normal Operation (No Clear)")
+ PORT_DIPSETTING(0x08, "Clear NVRAM")
+ PORT_DIPNAME(0x10, 0x00, "Auto Boot")
+ PORT_DIPLOCATION("FRONT_PANEL:5")
+ PORT_DIPSETTING(0x00, "Auto Boot Disable")
+ PORT_DIPSETTING(0x10, "Auto Boot Enable")
+ PORT_DIPNAME(0x20, 0x00, "Run Diagnostic Test")
+ PORT_DIPLOCATION("FRONT_PANEL:6")
+ PORT_DIPSETTING(0x00, "No Diagnostic Test")
+ PORT_DIPSETTING(0x20, "Run Diagnostic Test")
+ PORT_DIPNAME(0x40, 0x40, "External APbus Slot Probe Disable")
+ PORT_DIPLOCATION("FRONT_PANEL:7")
+ PORT_DIPSETTING(0x00, "Enable External Slot Probe")
+ PORT_DIPSETTING(0x40, "Disable External Slot Probe")
+ PORT_DIPNAME(0x80, 0x00, "No Memory Mode")
+ PORT_DIPLOCATION("FRONT_PANEL:8")
+ PORT_DIPSETTING(0x00, "Main Memory Enabled")
+ PORT_DIPSETTING(0x80, "Main Memory Disabled");
+INPUT_PORTS_END
+
+ROM_START(nws5000x)
+
+ ROM_REGION64_BE(0x40000, "mrom", 0)
+ ROM_SYSTEM_BIOS(0, "nws5000x", "APbus System Monitor Release 3.201")
+ ROMX_LOAD("mpu-33__ver3.201__1994_sony.rom", 0x00000, 0x40000, CRC(8a6ca2b7) SHA1(72d52e24a554c56938d69f7d279b2e65e284fd59), ROM_BIOS(0))
+
+ // idrom and macrom dumps include unmapped space that would ideally be umasked, but this is functionally the same.
+ // Additionally, there is machine-specific information contained within each of these, so there are no "golden" full-chip hashes.
+ ROM_REGION64_BE(0x400, "idrom", 0)
+ ROM_LOAD("idrom.rom", 0x000, 0x400, CRC(28ff30c9) SHA1(288fd7d9133ac5e40d90a9b6e24db057cd6b05ad) BAD_DUMP)
+
+ ROM_REGION64_BE(0x400, "macrom", 0)
+ ROM_LOAD("macrom.rom", 0x000, 0x400, CRC(22d384d2) SHA1(b78a2861310929e92f16deab989ac51f9da3131a) BAD_DUMP)
+ROM_END
+
+} // anonymous namespace
+
+// Machine definitions
+// YEAR NAME P CM MACHINE INPUT CLASS INIT COMPANY FULLNAME FLAGS
+COMP(1994, nws5000x, 0, 0, nws5000x, nws5000, news_r4k_state, empty_init, "Sony", "NET WORK STATION NWS-5000X", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_TIMING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND)