summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2021-05-19 18:27:34 -0400
committer AJR <ajrhacker@users.noreply.github.com>2021-05-19 18:27:34 -0400
commitab88bacf35e9db1b4bf9493810f5b8de15469c71 (patch)
tree07add5e8cc2ad05ba4d36dcc7cc00d75773768d9
parent2fa2ef8b0e19d7c376f7779a60a20c95dc9d6ea4 (diff)
decmate2, decmate3: Emulate HD-6120 CPU; add enough I/O to initialize various peripheral ICs and run tests on disk controller
-rw-r--r--scripts/src/cpu.lua2
-rw-r--r--src/devices/cpu/pdp8/hd6120.cpp1356
-rw-r--r--src/devices/cpu/pdp8/hd6120.h217
-rw-r--r--src/mame/drivers/decmate2.cpp656
4 files changed, 2205 insertions, 26 deletions
diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua
index 4ddf33406ed..c89a05b8588 100644
--- a/scripts/src/cpu.lua
+++ b/scripts/src/cpu.lua
@@ -555,6 +555,8 @@ end
if CPUS["PDP8"] then
files {
+ MAME_DIR .. "src/devices/cpu/pdp8/hd6120.cpp",
+ MAME_DIR .. "src/devices/cpu/pdp8/hd6120.h",
MAME_DIR .. "src/devices/cpu/pdp8/pdp8.cpp",
MAME_DIR .. "src/devices/cpu/pdp8/pdp8.h",
}
diff --git a/src/devices/cpu/pdp8/hd6120.cpp b/src/devices/cpu/pdp8/hd6120.cpp
new file mode 100644
index 00000000000..48134d00dd5
--- /dev/null
+++ b/src/devices/cpu/pdp8/hd6120.cpp
@@ -0,0 +1,1356 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ Harris HD-6120 High-Speed CMOS 12 Bit Microprocessor
+
+ The HD-6120 is a second-generation 12-bit microprocessor developed by
+ Harris Semiconductor in conjunction with Digital Equipment Corporation.
+ Like its predecessor, Intersil's IM6100 (which Harris second-sourced),
+ it is a single-chip implementation of DEC's PDP-8 architecture in
+ fully static CMOS with a multiplexed address/data bus. Though HD-6120
+ was originally designed to operate at typical frequencies similar to
+ those of the IM6100 (its maximum oscillator input was later upgraded
+ from 5.1 MHz to 8 MHz), it executes instructions faster largely due to
+ increased parallelism; the number of minor states in each machine
+ cycle is also considerably more variable on HD-6120.
+
+ HD-6120 also differs from IM6100 in its external interface. Whereas
+ IM6100 generates a single narrow LXMAR strobe at the beginning of each
+ memory or I/O cycle, followed by strobes that identify the space but
+ not the direction of transfer, HD-6120 identifies the space by the
+ falling edge of LXMAR, LXPAR or LXDAR and holds it low while generating
+ READ and/or WRITE strobes, as well as MEMSEL for memory accesses.
+ Accesses to the “switch register” (a data word stored outside the CPU
+ itself, as on previous PDP-8s, but is also writable here) are
+ identified by READ and WRITE occurring in the absence of LXMAR, LXPAR
+ or LXDAR, as are the special register transfer operations. (DATAF is
+ asserted for the former, along with DF, and IFETCH for the latter.)
+ OUT primarily provides directional control for bus transceivers such
+ as HD-6432 and HD-6433 (though 74LS245 may be used instead), but its
+ inversion can also be used to provide a wider READ strobe. ACK is used
+ to extend READ and WRITE operations while keeping the clock running;
+ it may be pulled up to Vcc when not used (as is the case on all of the
+ DECmates). LXDAR is also brought low to indicate the data portion of
+ auto-indexing cycles, and is held low briefly after RESET to help
+ distinguish the power-on IOCLR from the pulse generated by the CAF
+ instruction.
+
+ During a minor cycle in which LXMAR, LXPAR or LXDAR goes from high to
+ low, either IF or DF is output on the C0, C1 and EMA2 lines together
+ with the memory or device address on DX0–DX11. The DATAF output used
+ by IM6100 to indicate indirect accesses to data memory may be ignored
+ here, though some systems still use it for banking.
+
+ IOT instructions with codes 6000–6007 and 6200–6277 are reserved for
+ on-chip functions and do not generate LXDAR when executed. These
+ functions include the standard memory extension controls (excluding
+ time-sharing modes and the Intersil-exclusive LIF), plus a set of new
+ IOTs that can be used to move values of AC and PC to and from two
+ independent stacks in memory field 0. SKON, SRQ and GTF are replaced
+ by completely different operations in control panel mode, which can
+ now be accessed by software using HLT or the new PR0–PR3 traps. Panel
+ mode programs may generate indirect accesses to control panel memory
+ rather than main memory by setting the Panel Data Flag (PDF).
+
+ For external I/O transfer cycles, HD-6120 performs a WRITE followed by
+ an optional READ, whereas IM6100 always performed a read first. During
+ completion of the AC write phase, the C0, C1 and SKIP lines are sampled
+ to be interpreted appropriately (HD-6120 does not recognize C2 despite
+ EMA2 being three-stated at this time), and no data is received from the
+ device if C0 is inactive high. (This emulation uses a parallel address
+ space to read these flags and defines 1 as low and 0 as high, as these
+ and most other signals were on older PDP-8 buses.) DF is also output
+ (and DATAF asserted) along with the device address during the LXDAR
+ minor cycle, and some peripheral implementations make use of bits
+ latched from DF as extra control inputs.
+
+ HD-6120, like IM6100, has a special “control panel” mode, with priority
+ over normal interrupts, used to execute supervisory functions from a
+ separate memory space that has the same dimensions as main memory but
+ is inaccessible from programs executing from there. Due to MAME's
+ memory architecture only fully supporting one program space, this
+ emulation translates addresses in panel space into the upper extension
+ of a 64K-word memory space, which does agree with how the DECmate II
+ and DECmate III physically implement it.
+
+ The INTGNT output becomes active low when an interrupt request is
+ accepted and returns high at the end of the first external IOT. This
+ signal is intended for interrupting devices, specifically the HD-6121
+ Input/Output Controller, to respond specially to this first IOT. The
+ INTGNT output is suppressed upon entering panel mode, only to become
+ active again when execution returns to main memory.
+
+ The HD-6120's 12-bit major registers, besides AC, MQ (which one
+ functional diagram more aptly labels the “Accumulator-Adjunct
+ Register”), PC and the two stack pointers, include several which are
+ only implicitly used in execution: a TEMP register that latches ALU
+ outputs, the instruction register IR, and the output latch register OL
+ that holds all addresses and data to be output on the DX bus. HD-6120
+ also internally maintains a group of 3-bit registers whose path
+ connects to TEMP, each of which contains the current memory extension
+ fields, their mirrors or various flags. (This emulation extends the
+ field registers to 4 bits to include the CTRLFF, PDF and PEX flags,
+ which are neither readable nor output directly at any time.)
+ Particular flag registers are enabled on the C0, C1 and EMA2 lines
+ during the final writes of ISZ, DCA and JMS. The most important of
+ these flag registers includes LINK, the interrupt enable flip-flop and
+ the GT flag hitherto provided only on arithmetic extensions of some
+ previous PDP-8 CPUs, though like MQ it conveys no specific purpose
+ here. Another flag register contains the inverse of the active-low
+ INTREQ input, the PWRON flag (set if STRTUP = VSS at RESET time,
+ causing entry into panel mode) and 0 in its LSB.
+
+ Undefined Group 3 OPRs and internal IOTs have no effect on the
+ HD-6120 except that both interrupts and panel requests are blocked
+ until after the next instruction. This was apparently provided to
+ allow a hypothetical extended arithmetic processor or programmer's
+ console to independently decode any number of these instructions and
+ reliably trap them by initiating a register transfer operation during
+ the immediately following IFETCH cycle. This operation, initiated by
+ pulling SKIP low any time an instruction word is being read, causes
+ the CPU to disregard that instruction, strobe out AC, flags and MQ,
+ then stall indefinitely until SKIP is released, at which point it
+ reads AC, LINK, GT, MQ and PC back in.
+
+ As ever with the PDP-8, official documentation numbers MSB as 0 and
+ the LSB of a word as 11. This may cause some confusion, since MAME,
+ like most computers since the PDP-11, numbers bits the opposite way.
+
+ Known issues:
+ * Several instruction timings are likely off by one minor cycle, and
+ their cycle-by-cycle sequencing is even more of an educated guess.
+ Official documentation is very inconsistent and does not offer
+ complete timing information for specific instructions (such as
+ exactly when INTREQ is sampled for SRQ or GTF or the duration of
+ the IOCLR pulse issued by CAF).
+ * Some of the internal IOTs have not been tested thoroughly, the
+ stack operations in particular. Their implementation may need to
+ be adjusted slightly.
+ * The DMAREQ input and DMAGNT output have not been implemented.
+ * The RUN/HLT and ACK inputs have not been implemented, though no
+ system is known to use either of these.
+ * As explained above, auto-indexing cycles are distinguished by the
+ assertion of the DATAF signal. If any system uses this output to
+ modify memory accesses in some way, the device implementation
+ should add whatever hooks and configuration flags are needed.
+ * The register transfer operation and special flag outputs have not
+ been implemented. Harris's datasheet has no more than one vague
+ allusion to the latter feature, and both seem much more likely to
+ have been used for IC testing than in any commercial product.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "hd6120.h"
+#include "pdp8dasm.h"
+
+// device type definition
+DEFINE_DEVICE_TYPE(HD6120, hd6120_device, "hd6120", "Harris HD-6120")
+
+ALLOW_SAVE_TYPE(hd6120_device::minor_state)
+
+hd6120_device::hd6120_device(const machine_config &config, const char *tag, device_t *owner, u32 clock)
+ : cpu_device(config, HD6120, tag, owner, clock)
+ , m_inst_config("instruction", ENDIANNESS_BIG, 16, 16, -1) // 12 data bits
+ , m_data_config("data", ENDIANNESS_BIG, 16, 16, -1) // 12 data bits
+ , m_io_config("I/O", ENDIANNESS_BIG, 16, 9, -1) // 12 data bits
+ , m_devctl_config("device control", ENDIANNESS_BIG, 8, 9, 0) // only 3 bits used
+ , m_lxmar_callback(*this)
+ , m_lxpar_callback(*this)
+ , m_lxdar_callback(*this)
+ , m_rsr_callback(*this)
+ , m_wsr_callback(*this)
+ , m_strtup_callback(*this)
+ , m_intgnt_callback(*this)
+ , m_ioclr_callback(*this)
+ , m_pc(0)
+ , m_ac(0)
+ , m_mq(0)
+ , m_sp{0, 0}
+ , m_temp(0)
+ , m_ir(0)
+ , m_if(0)
+ , m_ib(0)
+ , m_df(0)
+ , m_sf(0)
+ , m_flags(0)
+ , m_pnlflgs(0)
+ , m_fz(false)
+ , m_iiff(false)
+ , m_pwron(false)
+ , m_intgnt(false)
+ , m_state(minor_state::RESET_1)
+ , m_iaddr(0)
+ , m_icount(0)
+ , m_intreq_input(false)
+ , m_cpreq_input(false)
+{
+ m_inst_config.m_is_octal = true;
+ m_data_config.m_is_octal = true;
+ m_io_config.m_is_octal = true;
+ m_devctl_config.m_is_octal = true; // data might not be logically octal, but addresses sure are
+}
+
+std::unique_ptr<util::disasm_interface> hd6120_device::create_disassembler()
+{
+ return std::make_unique<hd6120_disassembler>();
+}
+
+hd6120_device::space_config_vector hd6120_device::memory_space_config() const
+{
+ if (has_configured_map(AS_DATA))
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_inst_config),
+ std::make_pair(AS_DATA, &m_data_config),
+ std::make_pair(AS_IO, &m_io_config),
+ std::make_pair(AS_DEVCTL, &m_devctl_config)
+ };
+ else
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_inst_config),
+ std::make_pair(AS_IO, &m_io_config),
+ std::make_pair(AS_DEVCTL, &m_devctl_config)
+ };
+}
+
+void hd6120_device::device_resolve_objects()
+{
+ // Resolve callbacks
+ m_lxmar_callback.resolve_safe();
+ m_lxpar_callback.resolve_safe();
+ m_lxdar_callback.resolve_safe();
+ m_rsr_callback.resolve();
+ m_wsr_callback.resolve_safe();
+ m_strtup_callback.resolve_safe(1);
+ m_intgnt_callback.resolve_safe();
+ m_ioclr_callback.resolve_safe();
+}
+
+u16 hd6120_device::rotate_step(u16 data)
+{
+ const bool link = BIT(m_flags, 2);
+ switch (BIT(m_ir, 1, 3))
+ {
+ case 0: default:
+ // No rotate
+ return data;
+
+ case 1: case 6:
+ // BSW (twice) or R3L (once)
+ return ((data << 3) & 07770) | ((data >> 9) & 7);
+
+ case 2: case 3:
+ // RAL (once) or RTL (twice)
+ if (BIT(data, 11))
+ m_flags |= 4;
+ else
+ m_flags &= 3;
+ return ((data << 1) & 07776) | (link ? 1 : 0);
+
+ case 4: case 5:
+ // RAR (once) or RTR (twice)
+ if (BIT(data, 0))
+ m_flags |= 4;
+ else
+ m_flags &= 3;
+ return ((data >> 1) & 03777) | (link ? 04000 : 0);
+ }
+}
+
+bool hd6120_device::skip_test() const
+{
+ bool cond = false;
+ if (BIT(m_ir, 6) && m_ac >= 04000) // SMA/SPA
+ cond = true;
+ else if (BIT(m_ir, 5) && m_ac == 0) // SZA/SNA
+ cond = true;
+ else if (BIT(m_ir, 4) && BIT(m_flags, 2)) // SNL/SZL
+ cond = true;
+ if (BIT(m_ir, 3))
+ return !cond;
+ else
+ return cond;
+}
+
+u16 hd6120_device::dataf_map(u16 addr) const
+{
+ if (m_ir >= 04000)
+ return u16(m_iiff ? m_ib : m_fz ? 010 : m_if) << 12 | addr;
+ else
+ return u16(m_df) << 12 | addr;
+}
+
+void hd6120_device::next_instruction()
+{
+ if (m_iiff || m_ib >= 010)
+ m_state = minor_state::IFETCH_1;
+ else if (m_pwron || m_pnlflgs != 0)
+ m_state = minor_state::CPINT_1;
+ else if (BIT(m_flags, 0) && m_intreq_input)
+ m_state = minor_state::INTGNT_1;
+ else
+ m_state = minor_state::IFETCH_1;
+}
+
+void hd6120_device::transfer_pc(u16 addr)
+{
+ bool panel_exit = false;
+ if (m_iiff)
+ {
+ if (BIT(m_if & ~m_ib, 3))
+ {
+ debugger_privilege_hook();
+ panel_exit = true;
+
+ // PDF is nominally reset only upon entering panel mode, but has no function outside it
+ m_df &= 7;
+ }
+ m_if = m_ib;
+ m_iiff = false;
+ m_fz = false;
+ }
+ m_pc = addr;
+ if (m_ib >= 010)
+ m_state = minor_state::IFETCH_1;
+ else if ((panel_exit ? (m_pnlflgs & 6) : m_pnlflgs) != 0)
+ m_state = minor_state::CPINT_1;
+ else
+ {
+ if (m_intgnt && panel_exit)
+ m_intgnt_callback(0);
+ if (BIT(m_flags, 0) && m_intreq_input)
+ m_state = minor_state::INTGNT_1;
+ else
+ m_state = minor_state::IFETCH_1;
+ }
+}
+
+void hd6120_device::debug_set_pc(u16 addr)
+{
+ m_iaddr = addr;
+ m_pc = addr & 07777;
+ m_if = BIT(addr, 12, 4);
+ m_fz = false;
+ if (!m_iiff)
+ {
+ // Fix up IB and DF
+ if (BIT(m_if, 3))
+ m_ib |= 010;
+ else
+ {
+ m_ib &= 7;
+ m_df &= 7;
+ }
+ }
+}
+
+void hd6120_device::debug_update_pc(u16 addr)
+{
+ m_pc = addr;
+ m_iaddr = (m_iaddr & 0170000) | addr;
+}
+
+void hd6120_device::device_start()
+{
+ set_icountptr(m_icount);
+
+ // Bind address spaces
+ space(AS_PROGRAM).cache(m_icache);
+ space(AS_PROGRAM).specific(m_inst);
+ space(has_space(AS_DATA) ? AS_DATA : AS_PROGRAM).specific(m_data);
+ space(AS_IO).specific(m_io);
+ space(AS_DEVCTL).specific(m_devctl);
+
+ // Register debug state
+ using namespace std::placeholders;
+ state_add(HD6120_PC, "PC", m_pc, std::bind(&hd6120_device::debug_update_pc, this, _1)).mask(07777).formatstr("%04O");
+ state_add(STATE_GENPC, "GENPC", m_iaddr, std::bind(&hd6120_device::debug_set_pc, this, _1)).mask(0177777).formatstr("%06O").noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_iaddr, std::bind(&hd6120_device::debug_set_pc, this, _1)).mask(0177777).formatstr("%06O").noshow();
+ state_add(HD6120_IF, "IF", m_if).mask(017).formatstr("%6s");
+ state_add(HD6120_IB, "IB", m_ib).mask(017).formatstr("%7s");
+ state_add(HD6120_DF, "DF", m_df).mask(017).formatstr("%7s");
+ state_add(HD6120_SF, "SF", m_sf).mask(077).formatstr("%02O");
+ state_add(HD6120_IIFF, "IIFF", m_iiff);
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).mask(7).formatstr("%10s").noshow();
+ state_add(HD6120_FLAGS, "FLAGS", m_flags).mask(7);
+ state_add(HD6120_PNLFLGS, "PNLFLGS", m_pnlflgs).mask(7);
+ state_add(HD6120_PWRON, "PWRON", m_pwron);
+ state_add(HD6120_AC, "AC", m_ac).mask(07777).formatstr("%04O");
+ state_add(HD6120_MQ, "MQ", m_mq).mask(07777).formatstr("%04O");
+ state_add(HD6120_SP1, "SP1", m_sp[0]).mask(07777).formatstr("%04O");
+ state_add(HD6120_SP2, "SP2", m_sp[1]).mask(07777).formatstr("%04O");
+
+ // Register save state
+ save_item(NAME(m_pc));
+ save_item(NAME(m_ac));
+ save_item(NAME(m_mq));
+ save_item(NAME(m_sp));
+ save_item(NAME(m_temp));
+ save_item(NAME(m_ir));
+ save_item(NAME(m_if));
+ save_item(NAME(m_ib));
+ save_item(NAME(m_df));
+ save_item(NAME(m_sf));
+ save_item(NAME(m_flags));
+ save_item(NAME(m_pnlflgs));
+ save_item(NAME(m_fz));
+ save_item(NAME(m_iiff));
+ save_item(NAME(m_pwron));
+ save_item(NAME(m_intgnt));
+ save_item(NAME(m_state));
+ save_item(NAME(m_iaddr));
+ save_item(NAME(m_oaddr));
+ save_item(NAME(m_intreq_input));
+ save_item(NAME(m_cpreq_input));
+}
+
+void hd6120_device::device_reset()
+{
+ m_ac = 0;
+ m_flags = 0;
+ m_pnlflgs = 0;
+ m_iiff = false;
+ m_fz = false;
+ m_pwron = !m_strtup_callback();
+ m_if = 0;
+ m_ib = 0;
+ m_df = 0;
+ m_sf = 0;
+ m_intgnt = false;
+ m_intgnt_callback(1);
+ m_ioclr_callback(0);
+}
+
+void hd6120_device::execute_run()
+{
+ do
+ {
+ switch (m_state)
+ {
+ case minor_state::RESET_1:
+ m_state = minor_state::RESET_2;
+ break;
+
+ case minor_state::RESET_2:
+ m_ioclr_callback(1);
+ m_state = minor_state::RESET_3;
+ break;
+
+ case minor_state::RESET_3:
+ m_state = minor_state::RESET_4;
+ break;
+
+ case minor_state::RESET_4:
+ m_temp = 07777;
+ m_state = minor_state::RESET_5;
+ break;
+
+ case minor_state::RESET_5:
+ m_pc = m_temp;
+ m_temp = 0;
+ next_instruction();
+ break;
+
+ case minor_state::IFETCH_1:
+ m_ac = m_temp & 07777;
+ m_iaddr = u16(m_fz ? 010 : m_if) << 12 | m_pc;
+ debugger_instruction_hook(m_iaddr);
+ if (m_iaddr >= 0100000)
+ m_lxpar_callback(IFETCH, m_iaddr & 077777);
+ else
+ m_lxmar_callback(IFETCH, m_iaddr);
+ m_state = minor_state::IFETCH_2;
+ break;
+
+ case minor_state::IFETCH_2:
+ m_state = minor_state::IFETCH_3;
+ break;
+
+ case minor_state::IFETCH_3:
+ m_ir = m_icache.read_word(m_iaddr) & 07777;
+ m_temp = m_pc + 1;
+ if (m_ir >= 07400)
+ {
+ if (BIT(m_ir, 0))
+ m_state = minor_state::OP3_1;
+ else
+ m_state = minor_state::OP2_1;
+ }
+ else if (m_ir >= 07000)
+ m_state = minor_state::OP1_1;
+ else if (m_ir >= 06000)
+ m_state = minor_state::IOT_1;
+ else
+ {
+ m_oaddr = (m_iaddr & (BIT(m_ir, 7) ? 0177600 : 0170000)) | (m_ir & 0177);
+ if (BIT(m_ir, 8))
+ m_state = minor_state::INDIR_1;
+ else if ((m_ir & 07000) == 05000)
+ m_state = minor_state::JMP_1;
+ else
+ m_state = minor_state::EXEC_1;
+ }
+ break;
+
+ case minor_state::INDIR_1:
+ m_pc = m_temp & 07777;
+ if (m_oaddr >= 0100000)
+ m_lxpar_callback(INSTF, m_oaddr & 077777);
+ else
+ m_lxmar_callback(INSTF, m_oaddr);
+ m_state = minor_state::INDIR_2;
+ break;
+
+ case minor_state::INDIR_2:
+ if ((m_oaddr & 07770) == 0010)
+ {
+ // Begin auto-indexing
+ m_state = minor_state::INDIR_3A;
+ }
+ else
+ m_state = minor_state::INDIR_3;
+ break;
+
+ case minor_state::INDIR_3:
+ m_oaddr = dataf_map(m_inst.read_word(m_oaddr) & 07777);
+ m_temp = m_pc;
+ if ((m_ir & 07000) == 05000)
+ m_state = minor_state::JMP_1;
+ else
+ m_state = minor_state::EXEC_1;
+ break;
+
+ case minor_state::INDIR_3A:
+ m_temp = m_inst.read_word(m_oaddr) + 1;
+ m_state = minor_state::INDIR_4;
+ break;
+
+ case minor_state::INDIR_4:
+ m_state = minor_state::INDIR_5;
+ break;
+
+ case minor_state::INDIR_5:
+ m_inst.write_word(m_oaddr, m_temp & 07777);
+ m_oaddr = dataf_map(m_temp & 07777);
+ m_temp = m_pc;
+ if ((m_ir & 07000) == 05000)
+ m_state = minor_state::JMP_1;
+ else
+ m_state = minor_state::EXEC_1;
+ break;
+
+ case minor_state::EXEC_1:
+ {
+ const bool dataf = BIT(m_ir, 8) && m_ir < 04000;
+ if (m_oaddr >= 0100000)
+ m_lxpar_callback(dataf ? DATAF : INSTF, m_oaddr & 077777);
+ else
+ m_lxmar_callback(dataf ? DATAF : INSTF, m_oaddr);
+ m_pc = m_temp & 07777;
+ if (m_ir >= 03000)
+ m_state = minor_state::DEP_2;
+ else
+ m_state = minor_state::EXEC_2;
+ break;
+ }
+
+ case minor_state::EXEC_2:
+ m_state = minor_state::EXEC_3;
+ break;
+
+ case minor_state::EXEC_3:
+ if (BIT(m_ir, 8))
+ m_temp = m_data.read_word(m_oaddr) & 07777;
+ else
+ m_temp = m_inst.read_word(m_oaddr) & 07777;
+ if (m_ir >= 02000)
+ m_state = minor_state::ISZ_4;
+ else if (m_ir >= 01000)
+ m_state = minor_state::TAD_4;
+ else
+ m_state = minor_state::AND_4;
+ break;
+
+ case minor_state::DEP_2:
+ m_state = minor_state::DEP_3;
+ break;
+
+ case minor_state::DEP_3:
+ m_temp = m_oaddr + 1;
+ if (m_ir >= 04000)
+ m_state = minor_state::JMS_4;
+ else
+ m_state = minor_state::DCA_4;
+ break;
+
+ case minor_state::AND_4:
+ m_temp &= m_ac;
+ next_instruction();
+ break;
+
+ case minor_state::TAD_4:
+ m_temp += m_ac;
+ if (m_temp >= 010000)
+ m_flags |= 4;
+ else
+ m_flags &= 3;
+ next_instruction();
+ break;
+
+ case minor_state::ISZ_4:
+ ++m_temp;
+ m_state = minor_state::ISZ_5;
+ break;
+
+ case minor_state::ISZ_5:
+ m_state = minor_state::ISZ_6;
+ break;
+
+ case minor_state::ISZ_6:
+ if (BIT(m_ir, 8))
+ m_data.write_word(m_oaddr, m_temp & 07777);
+ else
+ m_inst.write_word(m_oaddr, m_temp & 07777);
+ if (m_temp < 010000)
+ next_instruction();
+ else
+ m_state = minor_state::ISZ_7;
+ m_temp = m_ac;
+ break;
+
+ case minor_state::ISZ_7:
+ m_temp = m_pc + 1;
+ m_state = minor_state::ISZ_8;
+ break;
+
+ case minor_state::ISZ_8:
+ m_pc = m_temp & 07777;
+ m_temp = m_ac;
+ next_instruction();
+ break;
+
+ case minor_state::DCA_4:
+ if (BIT(m_ir, 8))
+ m_data.write_word(m_oaddr, m_ac);
+ else
+ m_inst.write_word(m_oaddr, m_ac);
+ m_temp = 0;
+ next_instruction();
+ break;
+
+ case minor_state::JMS_4:
+ m_icache.write_word(m_oaddr, m_pc);
+ transfer_pc(m_temp & 07777);
+ m_temp = m_ac;
+ break;
+
+ case minor_state::JMP_1:
+ transfer_pc(m_oaddr & 07777);
+ m_temp = m_ac;
+ break;
+
+ case minor_state::OP1_1:
+ m_pc = m_temp & 07777;
+ m_temp = ((BIT(m_ir, 7) ? 0 : m_ac) ^ (BIT(m_ir, 5) ? 07777 : 0)) + (m_ir & 0001);
+ m_state = minor_state::OP1_2;
+ break;
+
+ case minor_state::OP1_2:
+ m_ac = m_temp & 07777;
+ if (BIT(m_ir, 6))
+ m_flags &= 3;
+ if (BIT(m_ir, 4))
+ m_flags ^= 4;
+ m_state = minor_state::OP1_3;
+ break;
+
+ case minor_state::OP1_3:
+ m_temp = rotate_step(m_ac);
+ if (BIT(m_ir, 1))
+ m_state = minor_state::OP1_4;
+ else
+ next_instruction();
+ break;
+
+ case minor_state::OP1_4:
+ m_state = minor_state::OP1_5;
+ break;
+
+ case minor_state::OP1_5:
+ m_temp = rotate_step(m_temp);
+ next_instruction();
+ break;
+
+ case minor_state::OP2_1:
+ if (skip_test())
+ ++m_temp;
+ if (BIT(m_ir, 2))
+ m_state = minor_state::OSR_2;
+ else
+ m_state = minor_state::OP2_2;
+ break;
+
+ case minor_state::OP2_2:
+ m_pc = m_temp & 07777;
+ m_temp = 0;
+ m_state = minor_state::OP2_3;
+ break;
+
+ case minor_state::OP2_3:
+ if (BIT(m_ir, 1))
+ m_pnlflgs |= 1; // Set HLTFLG
+ m_state = minor_state::OP2_4;
+ break;
+
+ case minor_state::OP2_4:
+ m_temp |= BIT(m_ir, 7) ? 0 : m_ac;
+ next_instruction();
+ break;
+
+ case minor_state::OSR_2:
+ m_pc = m_temp & 07777;
+ m_state = minor_state::OSR_3;
+ break;
+
+ case minor_state::OSR_3:
+ if (m_rsr_callback.isnull())
+ {
+ logerror("%06o: SR read (IR = %04o)\n", m_iaddr, m_ir);
+ m_temp = 0;
+ }
+ else
+ m_temp = m_rsr_callback(m_df & 7);
+ m_state = minor_state::OP2_3;
+ break;
+
+ case minor_state::OP3_1:
+ m_pc = m_temp & 07777;
+ m_temp = BIT(m_ir, 7) ? 0 : m_ac;
+ m_state = minor_state::OP3_2;
+ break;
+
+ case minor_state::OP3_2:
+ m_state = minor_state::OP3_3;
+ break;
+
+ case minor_state::OP3_3:
+ if (BIT(m_ir, 4))
+ {
+ if (BIT(m_ir, 6))
+ std::swap(m_temp, m_mq);
+ else
+ {
+ // MQL always clears AC
+ m_mq = m_temp;
+ m_temp = 0;
+ }
+ }
+ else if (BIT(m_ir, 6))
+ m_temp |= m_mq;
+ if ((m_ir & 0056) != 0)
+ m_state = minor_state::IFETCH_1; // Interrupts conditionally blocked
+ else
+ next_instruction();
+ break;
+
+ case minor_state::IOT_1:
+ m_pc = m_temp & 07777;
+ if (m_ir >= 06010 && (m_ir & 0700) != 0200)
+ m_state = minor_state::EXTIOT_1;
+ else switch (m_ir & 0777)
+ {
+ case 0000:
+ if (m_if >= 010)
+ m_state = minor_state::PRS_1;
+ else
+ m_state = minor_state::SKON_1;
+ break;
+
+ case 0001: case 0002:
+ m_state = minor_state::IEN_1;
+ break;
+
+ case 0003:
+ if (m_if >= 010)
+ m_state = minor_state::PGO_1;
+ else
+ m_state = minor_state::SRQ_1;
+ break;
+
+ case 0004:
+ if (m_if >= 010)
+ m_state = minor_state::PEX_1;
+ else
+ m_state = minor_state::GTF_1;
+ break;
+
+ case 0005:
+ m_state = minor_state::RTF_1;
+ break;
+
+ case 0006:
+ m_state = minor_state::SGT_1;
+ break;
+
+ case 0007:
+ m_state = minor_state::CAF_1;
+ break;
+
+ case 0201: case 0202: case 0203:
+ case 0211: case 0212: case 0213:
+ case 0221: case 0222: case 0223:
+ case 0231: case 0232: case 0233:
+ case 0241: case 0242: case 0243:
+ case 0251: case 0252: case 0253:
+ case 0261: case 0262: case 0263:
+ case 0271: case 0272: case 0273:
+ m_state = minor_state::CFIELD_1;
+ break;
+
+ case 0205: case 0245:
+ m_state = minor_state::PPC_1;
+ break;
+
+ case 0206: case 0216: case 0226: case 0236:
+ m_state = minor_state::PRQ_1;
+ break;
+
+ case 0207: case 0227:
+ m_state = minor_state::RSP_1;
+ break;
+
+ case 0214: case 0224:
+ m_state = minor_state::RFIELD_1;
+ break;
+
+ case 0215: case 0255:
+ m_state = minor_state::PAC_1;
+ break;
+
+ case 0217: case 0237:
+ m_state = minor_state::LSP_1;
+ break;
+
+ case 0225: case 0265:
+ m_state = minor_state::RTN_1;
+ break;
+
+ case 0234:
+ m_state = minor_state::RIB_1;
+ break;
+
+ case 0235: case 0275:
+ m_state = minor_state::POP_1;
+ break;
+
+ case 0244:
+ m_state = minor_state::RMF_1;
+ break;
+
+ case 0246:
+ m_state = minor_state::WSR_1;
+ break;
+
+ case 0256:
+ m_state = minor_state::GCF_1;
+ break;
+
+ case 0266: case 0276:
+ m_state = minor_state::SPD_1;
+ break;
+
+ default:
+ m_state = minor_state::IOT_2;
+ break;
+ }
+ break;
+
+ case minor_state::IOT_2:
+ logerror("%06o: Undefined internal IOT (IR=%04o, AC=%04o)\n", m_iaddr, m_ir, m_ac);
+ m_temp = m_ac;
+ m_state = minor_state::IFETCH_1;
+ break;
+
+ case minor_state::SKON_1:
+ m_temp = m_pc + (m_flags & 1);
+ m_flags &= 6;
+ m_state = minor_state::SKON_2;
+ break;
+
+ case minor_state::SKON_2:
+ m_pc = m_temp;
+ m_state = minor_state::SKON_3;
+ break;
+
+ case minor_state::SKON_3:
+ m_temp = m_ac;
+ next_instruction();
+ break;
+
+ case minor_state::IEN_1:
+ m_flags = (m_flags & 6) | (m_ir & 0001);
+ m_state = minor_state::IEN_2;
+ break;
+
+ case minor_state::IEN_2:
+ m_temp = m_ac;
+ m_state = minor_state::IFETCH_1; // Interrupts are blocked
+ break;
+
+ case minor_state::SRQ_1:
+ m_temp = m_pc + (m_intreq_input ? 1 : 0);
+ m_state = minor_state::SKON_2;
+ break;
+
+ case minor_state::GTF_1:
+ m_state = minor_state::GTF_2;
+ break;
+
+ case minor_state::GTF_2:
+ m_state = minor_state::GTF_3;
+ break;
+
+ case minor_state::GTF_3:
+ m_temp = m_sf;
+ m_state = minor_state::GTF_4;
+ break;
+
+ case minor_state::GTF_4:
+ m_temp |= u16(m_flags & 6) << 9 | 0200; // 1 is loaded into bit 4 instead of IEFF
+ m_state = minor_state::GTF_5;
+ break;
+
+ case minor_state::GTF_5:
+ if (m_intreq_input)
+ m_temp |= 01000;
+ if (m_pwron)
+ m_temp |= 0400;
+ next_instruction();
+ break;
+
+ case minor_state::RTF_1:
+ m_temp = m_ac;
+ m_flags = (m_temp & 06000) >> 9 | (m_temp & 0200) >> 7;
+ m_state = minor_state::RTF_2;
+ break;
+
+ case minor_state::RTF_2:
+ m_df = m_temp & 0007;
+ m_state = minor_state::RTF_3;
+ break;
+
+ case minor_state::RTF_3:
+ m_ib = (m_temp & 0070) >> 3;
+ m_iiff = true;
+ m_state = minor_state::RTF_4;
+ break;
+
+ case minor_state::RTF_4:
+ m_temp = 0;
+ next_instruction();
+ break;
+
+ case minor_state::SGT_1:
+ m_temp = m_pc + (BIT(m_flags, 1) ? 1 : 0);
+ m_state = minor_state::SKON_2;
+ break;
+
+ case minor_state::CAF_1:
+ m_temp = 0;
+ m_state = minor_state::CAF_2;
+ break;
+
+ case minor_state::CAF_2:
+ m_flags = 0; // LINK, GT and IEFF are cleared
+ m_ioclr_callback(0);
+ m_state = minor_state::CAF_3;
+ break;
+
+ case minor_state::CAF_3:
+ m_ioclr_callback(1);
+ next_instruction();
+ break;
+
+ case minor_state::PRS_1:
+ m_state = minor_state::PRS_2;
+ break;
+
+ case minor_state::PRS_2:
+ m_state = minor_state::PRS_3;
+ break;
+
+ case minor_state::PRS_3:
+ m_state = minor_state::PRS_4;
+ break;
+
+ case minor_state::PRS_4:
+ m_temp = u16(m_pnlflgs & 6) << 9 | (m_pnlflgs & 1) << 7;
+ if (m_intreq_input)
+ m_temp |= 01000;
+ if (m_pwron)
+ m_temp |= 0400;
+ m_pnlflgs &= m_temp >= 04000 ? 1 : 5;
+ m_pwron = false;
+ m_state = minor_state::IFETCH_1;
+ break;
+
+ case minor_state::PGO_1:
+ m_pnlflgs &= 6; // Clear HLTFLG
+ m_state = minor_state::SKON_3;
+ break;
+
+ case minor_state::PEX_1:
+ m_state = minor_state::PEX_2;
+ break;
+
+ case minor_state::PEX_2:
+ m_ib &= 7;
+ m_pnlflgs &= 5; // Clear PNLTRP
+ m_pwron = false;
+ m_iiff = true;
+ m_state = minor_state::IFETCH_1;
+ break;
+
+ case minor_state::CFIELD_1:
+ m_state = minor_state::CFIELD_2;
+ break;
+
+ case minor_state::CFIELD_2:
+ m_temp = m_ac;
+ if (BIT(m_ir, 1))
+ {
+ m_ib = (m_ib & 010) | BIT(m_ir, 3, 3);
+ m_iiff = true;
+ }
+ if (BIT(m_ir, 0))
+ m_df = (m_df & 010) | BIT(m_ir, 3, 3);
+ next_instruction();
+ break;
+
+ case minor_state::RFIELD_1:
+ m_state = minor_state::RFIELD_2;
+ break;
+
+ case minor_state::RFIELD_2:
+ m_temp = m_ac | (BIT(m_ir, 3) ? m_df & 7 : m_if & 7) << 3;
+ next_instruction();
+ break;
+
+ case minor_state::RIB_1:
+ m_state = minor_state::RIB_2;
+ break;
+
+ case minor_state::RIB_2:
+ m_temp = m_ac | m_sf;
+ next_instruction();
+ break;
+
+ case minor_state::RMF_1:
+ m_state = minor_state::RMF_2;
+ break;
+
+ case minor_state::RMF_2:
+ m_temp = m_sf;
+ m_state = minor_state::RMF_3;
+ break;
+
+ case minor_state::RMF_3:
+ m_df = (m_df & 010) | (m_temp & 7);
+ m_state = minor_state::RMF_4;
+ break;
+
+ case minor_state::RMF_4:
+ m_ib = (m_ib & 010) | ((m_temp >> 3) & 7);
+ m_iiff = true;
+ m_state = minor_state::SKON_3;
+ break;
+
+ case minor_state::PRQ_1:
+ if (m_if < 010)
+ m_pnlflgs |= 2; // Set PNLTRP
+ m_state = minor_state::SKON_3;
+ break;
+
+ case minor_state::WSR_1:
+ m_state = minor_state::WSR_2;
+ break;
+
+ case minor_state::WSR_2:
+ // WSR may be used from main memory as well as panel memory; DECmates protect it using external circuitry
+ m_wsr_callback(m_df & 7, m_ac);
+ m_state = minor_state::RTF_4;
+ break;
+
+ case minor_state::GCF_1:
+ m_state = minor_state::GCF_2;
+ break;
+
+ case minor_state::GCF_2:
+ m_temp = m_df & 7;
+ m_state = minor_state::GCF_3;
+ break;
+
+ case minor_state::GCF_3:
+ m_temp |= (m_if & 7) << 3;
+ m_state = minor_state::GCF_4;
+ break;
+
+ case minor_state::GCF_4:
+ m_temp |= u16(m_flags & 6) << 9 | (m_flags & 1) << 7;
+ m_state = minor_state::GTF_5;
+ break;
+
+ case minor_state::SPD_1:
+ m_temp = m_ac;
+ if (BIT(m_ir, 3) && m_if >= 010)
+ m_df |= 010;
+ else
+ m_df &= 7;
+ next_instruction();
+ break;
+
+ case minor_state::PPC_1:
+ m_temp = m_pc + 1;
+ m_state = minor_state::PPC_2;
+ break;
+
+ case minor_state::PPC_2:
+ m_oaddr = u16(m_if & 010) << 12 | m_sp[BIT(m_ir, 5)];
+ m_state = minor_state::PPC_3;
+ break;
+
+ case minor_state::PPC_3:
+ if (m_if >= 010)
+ m_lxpar_callback(INSTF, m_sp[BIT(m_ir, 5)]);
+ else
+ m_lxmar_callback(INSTF, m_sp[BIT(m_ir, 5)]);
+ m_state = minor_state::PPC_4;
+ break;
+
+ case minor_state::PPC_4:
+ m_state = minor_state::PPC_5;
+ break;
+
+ case minor_state::PPC_5:
+ m_inst.write_word(m_oaddr, m_temp);
+ m_temp = m_sp[BIT(m_ir, 5)] - 1;
+ m_state = minor_state::PPC_6;
+ break;
+
+ case minor_state::PPC_6:
+ m_sp[BIT(m_ir, 5)] = m_temp & 07777;
+ m_temp = m_ac;
+ next_instruction();
+ break;
+
+ case minor_state::PAC_1:
+ m_oaddr = u16(m_if & 010) << 12 | m_sp[BIT(m_ir, 5)];
+ m_state = minor_state::PAC_2;
+ break;
+
+ case minor_state::PAC_2:
+ if (m_if >= 010)
+ m_lxpar_callback(INSTF, m_sp[BIT(m_ir, 5)]);
+ else
+ m_lxmar_callback(INSTF, m_sp[BIT(m_ir, 5)]);
+ m_state = minor_state::PAC_3;
+ break;
+
+ case minor_state::PAC_3:
+ m_temp = m_ac;
+ m_state = minor_state::PPC_5;
+ break;
+
+ case minor_state::RTN_1:
+ m_temp = m_sp[BIT(m_ir, 5)] + 1;
+ m_state = minor_state::RTN_2;
+ break;
+
+ case minor_state::RTN_2:
+ m_oaddr = u16(m_ib & 010) << 12 | (m_temp & 07777);
+ m_state = minor_state::RTN_3;
+ break;
+
+ case minor_state::RTN_3:
+ m_sp[BIT(m_ir, 5)] = m_temp & 07777;
+ m_state = minor_state::RTN_4;
+ break;
+
+ case minor_state::RTN_4:
+ m_oaddr = u16(m_ib) << 12 | m_inst.read_word(m_sp[BIT(m_ir, 5)]);
+ m_state = minor_state::JMP_1;
+ break;
+
+ case minor_state::POP_1:
+ m_temp = m_sp[BIT(m_ir, 5)] + 1;
+ m_state = minor_state::POP_2;
+ break;
+
+ case minor_state::POP_2:
+ m_oaddr = u16(m_if & 010) << 12 | (m_temp & 07777);
+ m_state = minor_state::POP_3;
+ break;
+
+ case minor_state::POP_3:
+ m_sp[BIT(m_ir, 5)] = m_temp & 07777;
+ m_state = minor_state::POP_4;
+ break;
+
+ case minor_state::POP_4:
+ m_ac = m_inst.read_word(m_sp[BIT(m_ir, 5)]);
+ m_state = minor_state::SKON_3;
+ break;
+
+ case minor_state::RSP_1:
+ m_state = minor_state::RSP_2;
+ break;
+
+ case minor_state::RSP_2:
+ m_temp = m_sp[BIT(m_ir, 4)];
+ next_instruction();
+ break;
+
+ case minor_state::LSP_1:
+ m_sp[BIT(m_ir, 4)] = m_ac;
+ m_state = minor_state::RTF_4;
+ break;
+
+ case minor_state::EXTIOT_1:
+ m_lxdar_callback(DATAF, u16(m_df & 7) << 12 | m_ir);
+ m_state = minor_state::EXTIOT_2;
+ break;
+
+ case minor_state::EXTIOT_2:
+ m_temp = m_pc + 1;
+ m_state = minor_state::EXTIOT_3;
+ break;
+
+ case minor_state::EXTIOT_3:
+ {
+ // C0, C1 and SKIP are sampled at the rising edge of WRITE
+ // The control flags are read first here, since writes can and do change them
+ // (see esp. 6366 on DECmate II, where the change depends on the data written)
+ u8 devctl = m_devctl.read_byte(m_ir & 0777);
+ m_io.write_word(m_ir & 0777, m_ac);
+ if ((devctl & SKIP) != 0)
+ m_pc = m_temp & 07777;
+ m_temp = (devctl & C0) != 0 ? 0 : m_ac;
+ if ((devctl & C1) != 0)
+ m_state = minor_state::EXTIOT_4R;
+ else
+ m_state = minor_state::EXTIOT_4;
+ break;
+ }
+
+ case minor_state::EXTIOT_4:
+ // IOTs take one fewer minor cycle if no read
+ m_state = minor_state::EXTIOT_5;
+ break;
+
+ case minor_state::EXTIOT_4R:
+ m_state = minor_state::EXTIOT_5R;
+ break;
+
+ case minor_state::EXTIOT_5R:
+ m_temp |= m_io.read_word(m_ir & 0777);
+ m_state = minor_state::EXTIOT_5;
+ break;
+
+ case minor_state::EXTIOT_5:
+ if (m_intgnt)
+ {
+ m_intgnt = false;
+ m_intgnt_callback(1);
+ }
+ next_instruction();
+ break;
+
+ case minor_state::INTGNT_1:
+ m_ac = m_temp & 07777;
+ (void)standard_irq_callback(INTREQ_LINE);
+ m_intgnt = true;
+ m_intgnt_callback(0);
+ m_flags &= 6;
+ m_sf = m_if << 3 | m_df; // Save fields
+ m_if = 0;
+ m_ib = 0;
+ m_df = 0;
+ m_oaddr = 0;
+ m_lxmar_callback(INSTF, 0);
+ m_ir = 04000;
+ m_state = minor_state::DEP_2;
+ break;
+
+ case minor_state::CPINT_1:
+ m_ac = m_temp & 07777;
+ debugger_privilege_hook();
+ if (BIT(m_pnlflgs, 2))
+ (void)standard_irq_callback(CPREQ_LINE);
+ if (m_intgnt)
+ m_intgnt_callback(1);
+ m_if |= 010;
+ m_ib |= 010;
+ m_fz = true;
+ m_oaddr = 0100000;
+ m_lxpar_callback(INSTF, 0);
+ m_state = minor_state::CPINT_2;
+ break;
+
+ case minor_state::CPINT_2:
+ m_temp = 07777;
+ m_state = minor_state::JMS_4;
+ break;
+ }
+ } while (--m_icount > 0);
+}
+
+void hd6120_device::execute_set_input(int linenum, int state)
+{
+ switch (linenum)
+ {
+ case INTREQ_LINE:
+ m_intreq_input = state != CLEAR_LINE;
+ break;
+
+ case CPREQ_LINE:
+ // Falling-edge active Schmitt-trigger input
+ if (!m_cpreq_input && state != CLEAR_LINE)
+ m_pnlflgs |= 4; // Set BTSTRP
+ m_cpreq_input = state != CLEAR_LINE;
+ break;
+ }
+}
+
+void hd6120_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = util::string_format("%s %s %s", BIT(m_flags, 2) ? "LINK" : "----", BIT(m_flags, 1) ? "GT" : "--", BIT(m_flags, 0) ? "IE" : "--");
+ break;
+
+ case HD6120_IF:
+ if (BIT(m_if, 3))
+ str = util::string_format("%o (%s)", m_if & 7, m_fz ? "FZ" : "CP");
+ else
+ str = util::string_format("%o ", m_if);
+ break;
+
+ case HD6120_DF:
+ if (BIT(m_df, 3))
+ str = util::string_format("%o (PDF)", m_df & 7);
+ else
+ str = util::string_format("%o ", m_df);
+ break;
+
+ case HD6120_IB:
+ if (BIT(m_ib, 3))
+ str = util::string_format("%o (CP) ", m_ib & 7);
+ else if (BIT(m_if, 3))
+ str = util::string_format("%o (PEX)", m_ib);
+ else
+ str = util::string_format("%o ", m_ib);
+ break;
+ }
+}
diff --git a/src/devices/cpu/pdp8/hd6120.h b/src/devices/cpu/pdp8/hd6120.h
new file mode 100644
index 00000000000..126ce115040
--- /dev/null
+++ b/src/devices/cpu/pdp8/hd6120.h
@@ -0,0 +1,217 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/***************************************************************************
+
+ Harris HD-6120 High-Speed CMOS 12 Bit Microprocessor
+
+***********************************************************************
+ _____ _____
+ _OUT 1 |* \_/ | 40 VCC
+ DMAGNT 2 | | 39 _READ
+ _DMAREQ 3 | | 38 _WRITE
+ _SKIP 4 | | 37 _MEMSEL
+ RUN/_HLT 5 | | 36 _IOCLR
+ _RUN 6 | | 35 _LXDAR
+ _RESET 7 | | 34 _LXMAR
+ ACK 8 | | 33 _LXPAR
+ OSCIN 9 | | 32 _DATAF
+ OSCOUT 10 | HD-6120 | 31 _INTGNT
+ _IFETCH 11 | | 30 _INTREQ
+ DX0 12 | | 29 _CPREQ
+ DX1 13 | | 28 STRTUP
+ DX2 14 | | 27 EMA2
+ DX3 15 | | 26 C1/_C1
+ DX4 16 | | 25 C0/_C0
+ DX5 17 | | 24 DX11
+ DX6 18 | | 23 DX10
+ DX7 19 | | 22 DX9
+ VSS 20 |_____________| 21 DX8
+
+***************************************************************************/
+
+#ifndef MAME_CPU_PDP8_HD6120_H
+#define MAME_CPU_PDP8_HD6120_H
+
+#pragma once
+
+class hd6120_device : public cpu_device
+{
+public:
+ static constexpr int AS_DEVCTL = AS_OPCODES + 1;
+
+ static constexpr offs_t INSTF = 0;
+ static constexpr offs_t IFETCH = 1;
+ static constexpr offs_t DATAF = 2;
+
+ enum {
+ HD6120_PC,
+ HD6120_AC, HD6120_MQ,
+ HD6120_SP1, HD6120_SP2,
+ HD6120_IF, HD6120_IB, HD6120_DF, HD6120_SF, HD6120_IIFF,
+ HD6120_FLAGS, HD6120_PNLFLGS, HD6120_PWRON
+ };
+
+ // input lines
+ enum {
+ INTREQ_LINE = 0,
+ CPREQ_LINE
+ //SKIP_LINE,
+ //DMAREQ_LINE
+ };
+
+ // device control flags
+ enum : u8 {
+ SKIP = 1 << 3,
+ C0 = 1 << 2,
+ C1 = 1 << 1
+ // C2 is ignored on HD-6120
+ };
+
+ // device type constructor
+ hd6120_device(const machine_config &config, const char *tag, device_t *owner, u32 clock);
+
+ // callback configuration
+ auto lxmar_callback() { return m_lxmar_callback.bind(); }
+ auto lxpar_callback() { return m_lxpar_callback.bind(); }
+ auto lxdar_callback() { return m_lxdar_callback.bind(); }
+ auto rsr_callback() { return m_rsr_callback.bind(); }
+ auto wsr_callback() { return m_wsr_callback.bind(); }
+ auto strtup_callback() { return m_strtup_callback.bind(); }
+ auto intgnt_callback() { return m_intgnt_callback.bind(); }
+ auto ioclr_callback() { return m_ioclr_callback.bind(); }
+
+protected:
+ // device-level overrides
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ // device_execute_interface overrides
+ virtual void execute_run() override;
+ virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const noexcept override { return (clocks + 2 - 1) / 2; }
+ virtual uint64_t execute_cycles_to_clocks(uint64_t cycles) const noexcept override { return (cycles * 2); }
+ virtual void execute_set_input(int linenum, int state) override;
+
+ // device_disasm_interface overrides
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ // device_memory_interface overrides
+ virtual space_config_vector memory_space_config() const override;
+
+ // device_state_interface overrides
+ virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
+
+private:
+ enum class minor_state : u8 {
+ RESET_1, RESET_2, RESET_3, RESET_4, RESET_5,
+ IFETCH_1, IFETCH_2, IFETCH_3,
+ INDIR_1, INDIR_2, INDIR_3, INDIR_3A, INDIR_4, INDIR_5,
+ EXEC_1, EXEC_2, EXEC_3,
+ DEP_2, DEP_3,
+ AND_4,
+ TAD_4,
+ ISZ_4, ISZ_5, ISZ_6, ISZ_7, ISZ_8,
+ DCA_4,
+ JMS_4,
+ JMP_1,
+ OP1_1, OP1_2, OP1_3, OP1_4, OP1_5,
+ OP2_1, OP2_2, OP2_3, OP2_4,
+ OSR_2, OSR_3,
+ OP3_1, OP3_2, OP3_3,
+ IOT_1, IOT_2,
+ SKON_1, SKON_2, SKON_3,
+ IEN_1, IEN_2,
+ GTF_1, GTF_2, GTF_3, GTF_4, GTF_5,
+ RTF_1, RTF_2, RTF_3, RTF_4,
+ SRQ_1,
+ SGT_1,
+ CAF_1, CAF_2, CAF_3,
+ PRS_1, PRS_2, PRS_3, PRS_4,
+ PGO_1,
+ PEX_1, PEX_2,
+ CFIELD_1, CFIELD_2,
+ RFIELD_1, RFIELD_2,
+ RIB_1, RIB_2,
+ RMF_1, RMF_2, RMF_3, RMF_4,
+ PRQ_1,
+ WSR_1, WSR_2,
+ GCF_1, GCF_2, GCF_3, GCF_4,
+ SPD_1,
+ PPC_1, PPC_2, PPC_3, PPC_4, PPC_5, PPC_6,
+ PAC_1, PAC_2, PAC_3,
+ RTN_1, RTN_2, RTN_3, RTN_4,
+ POP_1, POP_2, POP_3, POP_4,
+ RSP_1, RSP_2,
+ LSP_1,
+ EXTIOT_1, EXTIOT_2, EXTIOT_3, EXTIOT_4, EXTIOT_4R, EXTIOT_5R, EXTIOT_5,
+ INTGNT_1,
+ CPINT_1, CPINT_2
+ };
+
+ u16 rotate_step(u16 data);
+ bool skip_test() const;
+ u16 dataf_map(u16 addr) const;
+ void next_instruction();
+ void transfer_pc(u16 addr);
+ void debug_set_pc(u16 addr);
+ void debug_update_pc(u16 addr);
+
+ // address spaces
+ address_space_config m_inst_config;
+ address_space_config m_data_config;
+ address_space_config m_io_config;
+ address_space_config m_devctl_config;
+ memory_access<16, 1, -1, ENDIANNESS_BIG>::cache m_icache;
+ memory_access<16, 1, -1, ENDIANNESS_BIG>::specific m_inst;
+ memory_access<16, 1, -1, ENDIANNESS_BIG>::specific m_data;
+ memory_access<9, 1, -1, ENDIANNESS_BIG>::specific m_io;
+ memory_access<9, 0, 0, ENDIANNESS_BIG>::specific m_devctl;
+
+ // callback objects
+ devcb_write16 m_lxmar_callback;
+ devcb_write16 m_lxpar_callback;
+ devcb_write16 m_lxdar_callback;
+ devcb_read16 m_rsr_callback;
+ devcb_write16 m_wsr_callback;
+ //devcb_read16 m_rtin_callback;
+ //devcb_write16 m_rtout_callback;
+ devcb_read_line m_strtup_callback;
+ devcb_write_line m_intgnt_callback;
+ //devcb_write_line m_dmagnt_callback;
+ devcb_write_line m_ioclr_callback;
+
+ // major registers
+ u16 m_pc;
+ u16 m_ac;
+ u16 m_mq;
+ u16 m_sp[2];
+ u16 m_temp;
+ u16 m_ir;
+
+ // field and flag registers
+ u8 m_if;
+ u8 m_ib;
+ u8 m_df;
+ u8 m_sf;
+ u8 m_flags;
+ u8 m_pnlflgs;
+ bool m_fz;
+ bool m_iiff;
+ bool m_pwron;
+ bool m_intgnt;
+
+ // misc. execution state
+ minor_state m_state;
+ u16 m_iaddr;
+ u16 m_oaddr;
+ s32 m_icount;
+
+ // input lines
+ bool m_intreq_input;
+ bool m_cpreq_input;
+};
+
+// device type declaration
+DECLARE_DEVICE_TYPE(HD6120, hd6120_device)
+
+#endif // MAME_CPU_PDP8_HD6120_H
diff --git a/src/mame/drivers/decmate2.cpp b/src/mame/drivers/decmate2.cpp
index a3f3d97a740..40d52bd0357 100644
--- a/src/mame/drivers/decmate2.cpp
+++ b/src/mame/drivers/decmate2.cpp
@@ -2,7 +2,7 @@
// copyright-holders:AJR
/****************************************************************************
- Skeleton driver for DECmate II & III business PCs.
+ Preliminary driver for DECmate II & III business PCs.
The DECmate II & III are the last and in some ways least PDP-8-like
members of DEC's 12-bit computer line. Based on a Harris HD-6120 CMOS
@@ -23,31 +23,34 @@
green or amber) VR201 monitor also physically resembles. (As with the
DECmate's VT100 keyboard, the LK201 may have its PF1 key is painted gold
to highlight its importance to DECmate software.) DECmate II also differs
- from its CMOS-8 predecessors in using standard 8-bit EPROMs rather than
- specialized 12-bit ROMs for its "control panel" program.
+ from its CMOS-8 predecessors in using generic 8-bit EPROMs or mask ROMs
+ rather than specialized 12-bit ROMs for its "control panel" program.
DECmate II had three different types of expansion options: the
aforementioned storage adapter, which also supported a 10 MB hard disk
- (RDC51-CA); a graphics board supporting monochrome or color graphics on
- a second monitor; and an Auxiliary Processor Unit. The basic APU
- (PC27X-AA or -AB) ran CP/M on a Z80 with 64K of its own RAM. The
- upgraded XPU (PC27X-AH or PC27X-AJ) could also run MS-DOS 2.11 using an
- additional 8086 CPU with 256K or 512K of RAM.
+ interface (RDC51-CA) with its own 8051; a graphics board supporting
+ monochrome or color graphics on a second monitor; and an Auxiliary
+ Processor Unit. The basic APU (PC27X-AA or -AB) ran CP/M on a Z80 with
+ 64K of its own RAM. The upgraded XPU (PC27X-AH or PC27X-AJ) could also
+ run MS-DOS 2.11 using an additional 8086 CPU with 256K or 512K of RAM.
DECmate III (PC238) was a repackaging of the DECmate II in a smaller but
- taller box that provides space for only one internal RX33 floppy drive.
+ taller box that provides space for only one internal RX50 floppy drive.
Much of the timing and support logic is compressed into two large PLCC
gate arrays (DC381 and DC382), with a PLL and PAL16R8 to assist the FDC.
- The DECmate III's own APU and graphics options share a single and unique
- expansion connector; the PC23X-CA graphics card supported RGB color
- output on a VR241 monitor. DEC also later released an enhanced version
- called DECmate III Plus (PC24P), which added a 20 MB RD31 hard disk.
+ (One consequence of this reduction is that the printer port is fixed to
+ the same baud rate as the keyboard.) The DECmate III's own APU and
+ graphics options share a single and unique expansion connector; the
+ PC23X-CA graphics card supported RGB color output on a VR241 monitor.
+ DEC also later released an enhanced version called DECmate III Plus
+ (PC24P), which included a 20 MB RD31 hard disk underneath a half-height
+ RX33 floppy drive.
****************************************************************************/
#include "emu.h"
#include "bus/rs232/rs232.h"
-#include "cpu/pdp8/pdp8.h"
+#include "cpu/pdp8/hd6120.h"
#include "cpu/mcs51/mcs51.h"
#include "imagedev/floppy.h"
#include "machine/ay31015.h"
@@ -73,9 +76,24 @@ public:
, m_mpscc(*this, "mpscc")
, m_brg(*this, "brg%u", 0U)
, m_crtc(*this, "crtc")
+ , m_ram(*this, "ram")
, m_cprom(*this, "cprom")
, m_chargen(*this, "chargen")
+ , m_cprom_iview(*this, "cpromi")
+ , m_cprom_dview(*this, "cpromd")
+ , m_cpsel(false)
+ , m_vint(false)
+ , m_cpromsel(false)
+ , m_eadd0(false)
+ , m_com_control_read(false)
+ , m_kbd_rflg(false)
+ , m_kbd_tflg(false)
+ , m_prt_rflg(false)
+ , m_prt_tflg(false)
+ , m_crtc_addr(0)
, m_rxdata(0)
+ , m_rx_control(0)
+ , m_rx_status(0)
{
}
@@ -86,9 +104,67 @@ public:
protected:
virtual void machine_start() override;
+ virtual void machine_reset() override;
private:
u32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
+ DECLARE_WRITE_LINE_MEMBER(vint_w);
+
+ void lxmar_w(offs_t offset, u16 data);
+ void lxpar_w(offs_t offset, u16 data);
+ void lxdar_w(offs_t offset, u16 data);
+ void wsr_w(u16 data);
+ u16 cprom_switch_r(offs_t offset);
+ DECLARE_WRITE_LINE_MEMBER(pc278_ioclr_w);
+ DECLARE_WRITE_LINE_MEMBER(pc238_ioclr_w);
+
+ DECLARE_WRITE_LINE_MEMBER(keyboard_dr_w);
+ DECLARE_WRITE_LINE_MEMBER(keyboard_dr_ff_w);
+ DECLARE_WRITE_LINE_MEMBER(keyboard_tbre_w);
+ DECLARE_WRITE_LINE_MEMBER(keyboard_tbre_ff_w);
+ DECLARE_WRITE_LINE_MEMBER(printer_dr_w);
+ DECLARE_WRITE_LINE_MEMBER(printer_dr_ff_w);
+ DECLARE_WRITE_LINE_MEMBER(printer_tbre_w);
+ DECLARE_WRITE_LINE_MEMBER(printer_tbre_ff_w);
+ u8 kbdrflg_devctl_r();
+ void kbdrflg_set_w(u16 data);
+ void kbdrflg_clear_w(u16 data);
+ u16 kbduart_r();
+ u8 kbdtflg_devctl_r();
+ void kbdtflg_set_w(u16 data);
+ void kbdtflg_clear_w(u16 data);
+ u8 prtrflg_devctl_r();
+ void prtrflg_set_w(u16 data);
+ void prtrflg_clear_w(u16 data);
+ u16 prtuart_r();
+ void prttflg_set_w(u16 data);
+ void prttflg_clear_w(u16 data);
+ u8 prttflg_devctl_r();
+ u8 apten_r();
+
+ u8 comreg_devctl_r();
+ u16 comreg_r();
+ void comreg_w(u16 data);
+ void cominit_w(u16 data);
+
+ u8 vint_devctl_r();
+ void lscreg_w(u16 data);
+ void wrcrtc_w(u16 data);
+ u16 rdcrtc_r();
+ void video_mod_w(u16 data);
+
+ void modem_w(u16 data);
+
+ void sel_w(u16 data);
+ void lcd_w(u16 data);
+ u16 xdr_r();
+ void xdr_w(u16 data);
+ u8 xdr_devctl_r();
+ u8 str_devctl_r();
+ u8 ser_devctl_r();
+ u8 sdn_devctl_r();
+ void intr_w(u16 data);
+ void rxinit_w(u16 data);
u8 rx_ldata_r();
void rx_ldata_w(u8 data);
@@ -100,10 +176,14 @@ private:
void rx_sel_w(u8 data);
u8 rx_rdy_r();
- void mem_map(address_map &map);
+ void inst_map(address_map &map);
+ void data_map(address_map &map);
+ void pc278_io_map(address_map &map);
+ void pc238_io_map(address_map &map);
+ void devctl_map(address_map &map);
void rx_map(address_map &map);
- required_device<cpu_device> m_maincpu;
+ required_device<hd6120_device> m_maincpu;
required_device<i8051_device> m_rxcpu;
required_device<fd1793_device> m_fdc;
optional_device_array<floppy_connector, 4> m_floppy;
@@ -112,10 +192,27 @@ private:
required_device<upd7201_device> m_mpscc;
optional_device_array<com8116_device, 2> m_brg;
required_device<crt9007_device> m_crtc;
+ required_shared_ptr<u16> m_ram;
required_region_ptr<u16> m_cprom;
required_region_ptr<u8> m_chargen;
+ memory_view m_cprom_iview;
+ memory_view m_cprom_dview;
+ bool m_cpsel;
+ bool m_vint;
+ bool m_cpromsel;
+ bool m_eadd0;
+ bool m_com_control_read;
+
+ bool m_kbd_rflg;
+ bool m_kbd_tflg;
+ bool m_prt_rflg;
+ bool m_prt_tflg;
+
+ u8 m_crtc_addr;
u16 m_rxdata; // 74LS298, writable from both 6120 and 8051
+ u8 m_rx_control;
+ u8 m_rx_status;
};
void decmate2_state::init_pc278()
@@ -168,7 +265,26 @@ void decmate2_state::machine_start()
m_mpscc->synca_w(1);
m_mpscc->syncb_w(1);
+ save_item(NAME(m_cpsel));
+ save_item(NAME(m_vint));
+ save_item(NAME(m_cpromsel));
+ save_item(NAME(m_eadd0));
+ save_item(NAME(m_com_control_read));
+ save_item(NAME(m_kbd_rflg));
+ save_item(NAME(m_kbd_tflg));
+ save_item(NAME(m_prt_rflg));
+ save_item(NAME(m_prt_tflg));
+ save_item(NAME(m_crtc_addr));
save_item(NAME(m_rxdata));
+ save_item(NAME(m_rx_control));
+ save_item(NAME(m_rx_status));
+}
+
+void decmate2_state::machine_reset()
+{
+ m_cprom_iview.select(0);
+ m_cprom_dview.select(0);
+ m_cpromsel = false;
}
u32 decmate2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
@@ -177,6 +293,370 @@ u32 decmate2_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, c
return 0;
}
+WRITE_LINE_MEMBER(decmate2_state::vint_w)
+{
+ // TODO: synchronize
+ m_vint = state;
+}
+
+void decmate2_state::lxmar_w(offs_t offset, u16 data)
+{
+ if (offset == hd6120_device::IFETCH)
+ m_cpsel = false;
+}
+
+void decmate2_state::lxpar_w(offs_t offset, u16 data)
+{
+ if (offset == hd6120_device::IFETCH)
+ m_cpsel = true;
+}
+
+void decmate2_state::lxdar_w(offs_t offset, u16 data)
+{
+ m_eadd0 = BIT(data, 12); // latched from EMA2
+}
+
+void decmate2_state::wsr_w(u16 data)
+{
+ if (m_cpsel)
+ {
+ if (BIT(data, 0) != m_cpromsel) // DX11
+ {
+ m_cprom_iview.select(1);
+ m_cpromsel = BIT(data, 0);
+ }
+
+ if (BIT(data, 11)) // DX0
+ m_cprom_dview.disable();
+ else
+ m_cprom_dview.select(0);
+ }
+}
+
+// This is not a precise implementation of the actual RAMDIS control circuit, but is close enough to work just as well
+u16 decmate2_state::cprom_switch_r(offs_t offset)
+{
+ if (m_cpromsel)
+ {
+ if (!machine().side_effects_disabled())
+ m_cprom_iview.disable();
+ return m_cprom[offset & 07777];
+ }
+ else
+ {
+ if (!machine().side_effects_disabled())
+ m_cprom_iview.select(0);
+ return m_ram[offset];
+ }
+}
+
+WRITE_LINE_MEMBER(decmate2_state::pc278_ioclr_w)
+{
+ if (!state)
+ {
+ m_kbd_rflg = false;
+ m_kbd_tflg = false;
+ m_prt_rflg = false;
+ m_prt_tflg = false;
+ rxinit_w(0);
+ cominit_w(0);
+ }
+}
+
+WRITE_LINE_MEMBER(decmate2_state::pc238_ioclr_w)
+{
+ if (!state)
+ {
+ rxinit_w(0);
+ cominit_w(0);
+ }
+}
+
+WRITE_LINE_MEMBER(decmate2_state::keyboard_dr_w)
+{
+ m_kbd_rflg = state;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::keyboard_dr_ff_w)
+{
+ // TODO: edge trigger
+ if (state)
+ m_kbd_rflg = true;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::keyboard_tbre_w)
+{
+ m_kbd_tflg = state;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::keyboard_tbre_ff_w)
+{
+ // TODO: edge trigger
+ if (state)
+ m_kbd_tflg = true;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::printer_dr_w)
+{
+ m_prt_rflg = state;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::printer_dr_ff_w)
+{
+ // TODO: edge trigger
+ if (state)
+ m_prt_rflg = true;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::printer_tbre_w)
+{
+ m_prt_tflg = state;
+}
+
+WRITE_LINE_MEMBER(decmate2_state::printer_tbre_ff_w)
+{
+ // TODO: edge trigger
+ if (state)
+ m_prt_tflg = true;
+}
+
+u8 decmate2_state::kbdrflg_devctl_r()
+{
+ return m_kbd_rflg ? hd6120_device::SKIP : 0;
+}
+
+void decmate2_state::kbdrflg_set_w(u16 data)
+{
+ m_kbd_rflg = true;
+}
+
+void decmate2_state::kbdrflg_clear_w(u16 data)
+{
+ m_kbd_rflg = false;
+}
+
+u16 decmate2_state::kbduart_r()
+{
+ return m_kbduart->receive();
+}
+
+u8 decmate2_state::kbdtflg_devctl_r()
+{
+ return m_kbd_tflg ? hd6120_device::SKIP : 0;
+}
+
+void decmate2_state::kbdtflg_set_w(u16 data)
+{
+ m_kbd_tflg = true;
+}
+
+void decmate2_state::kbdtflg_clear_w(u16 data)
+{
+ m_kbd_tflg = false;
+}
+
+u8 decmate2_state::prtrflg_devctl_r()
+{
+ return m_prt_rflg ? hd6120_device::SKIP : 0;
+}
+
+void decmate2_state::prtrflg_set_w(u16 data)
+{
+ m_prt_rflg = true;
+}
+
+void decmate2_state::prtrflg_clear_w(u16 data)
+{
+ m_prt_rflg = false;
+}
+
+u16 decmate2_state::prtuart_r()
+{
+ return m_prtuart->receive();
+}
+
+u8 decmate2_state::prttflg_devctl_r()
+{
+ return m_prt_tflg ? hd6120_device::SKIP : 0;
+}
+
+void decmate2_state::prttflg_set_w(u16 data)
+{
+ m_prt_tflg = true;
+}
+
+void decmate2_state::prttflg_clear_w(u16 data)
+{
+ m_prt_tflg = false;
+}
+
+u8 decmate2_state::apten_r()
+{
+ // TODO: from pin 4 of printer connector J2
+ return 0;
+}
+
+u8 decmate2_state::comreg_devctl_r()
+{
+ if (m_com_control_read)
+ return hd6120_device::C1;
+ else
+ return 0;
+}
+
+u16 decmate2_state::comreg_r()
+{
+ if (m_eadd0)
+ return m_mpscc->cb_r();
+ else
+ return m_mpscc->ca_r();
+}
+
+void decmate2_state::comreg_w(u16 data)
+{
+ if (!m_com_control_read)
+ {
+ if (m_eadd0)
+ m_mpscc->cb_w(data & 0377);
+ else
+ m_mpscc->ca_w(data & 0377);
+ }
+ m_com_control_read = BIT(data, 11);
+}
+
+void decmate2_state::cominit_w(u16 data)
+{
+ m_mpscc->reset();
+ m_com_control_read = false;
+}
+
+u8 decmate2_state::vint_devctl_r()
+{
+ return m_vint ? hd6120_device::SKIP : 0;
+}
+
+void decmate2_state::lscreg_w(u16 data)
+{
+ m_crtc_addr = data & 077;
+}
+
+void decmate2_state::wrcrtc_w(u16 data)
+{
+ m_crtc->write(m_crtc_addr, data & 0377);
+}
+
+u16 decmate2_state::rdcrtc_r()
+{
+ return m_crtc->read(m_crtc_addr);
+}
+
+void decmate2_state::video_mod_w(u16 data)
+{
+ logerror("%s: Loading %04o into video mod register (loopback %s)\n", machine().describe_context(), data, BIT(data, 4) ? "enabled" : "disabled");
+}
+
+void decmate2_state::modem_w(u16 data)
+{
+ logerror("%s: Writing %04o to modem control register\n", machine().describe_context(), data);
+}
+
+void decmate2_state::sel_w(u16 data)
+{
+ logerror("%s: RX drive pair %c selected\n", machine().describe_context(), BIT(data, 0) ? 'B' : 'A');
+ if (BIT(data, 0))
+ m_rx_status |= 0x80;
+ else
+ m_rx_status &= 0x7f;
+}
+
+void decmate2_state::lcd_w(u16 data)
+{
+ logerror("%s: RX %d-bit command %d, drive %d\n", machine().describe_context(), BIT(data, 6) ? 8 : 12, BIT(data, 1, 3), BIT(data, 4));
+ m_rxdata = data;
+ m_rx_status &= 0xfc;
+ if (!BIT(data, 6))
+ m_rx_status |= 0x01;
+ m_rxcpu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE);
+}
+
+u16 decmate2_state::xdr_r()
+{
+ if (BIT(m_rx_status, 1))
+ {
+ if (!machine().side_effects_disabled())
+ logerror("%s: XDR received %04o from 8051\n", machine().describe_context(), m_rxdata);
+ return m_rxdata;
+ }
+ else
+ return 0;
+}
+
+void decmate2_state::xdr_w(u16 data)
+{
+ if (!BIT(m_rx_status, 1))
+ {
+ logerror("%s: XDR transmit %04o to 8051\n", machine().describe_context(), data);
+ m_rxdata = data;
+ }
+ m_rxcpu->set_input_line(MCS51_INT1_LINE, CLEAR_LINE);
+}
+
+u8 decmate2_state::xdr_devctl_r()
+{
+ if (BIT(m_rx_status, 1))
+ return hd6120_device::C1 | (BIT(m_rx_status, 0) ? hd6120_device::C0 : 0);
+ else
+ return 0;
+}
+
+u8 decmate2_state::str_devctl_r()
+{
+ if (BIT(m_rx_control, 3))
+ {
+ if (!machine().side_effects_disabled())
+ m_rx_control &= 0x07;
+ return hd6120_device::SKIP;
+ }
+ else
+ return 0;
+}
+
+u8 decmate2_state::ser_devctl_r()
+{
+ if (BIT(m_rx_control, 2))
+ {
+ if (!machine().side_effects_disabled())
+ m_rx_control &= 0x0b;
+ return hd6120_device::SKIP;
+ }
+ else
+ return 0;
+}
+
+u8 decmate2_state::sdn_devctl_r()
+{
+ if (BIT(m_rx_control, 0))
+ {
+ if (!machine().side_effects_disabled())
+ m_rx_control &= 0x0e;
+ return hd6120_device::SKIP;
+ }
+ else
+ return 0;
+}
+
+void decmate2_state::intr_w(u16 data)
+{
+ logerror("%s: RX interrupt enable %s\n", machine().describe_context(), BIT(data, 0) ? "set" : "cleared");
+}
+
+void decmate2_state::rxinit_w(u16 data)
+{
+ m_rx_control = 0;
+ m_rx_status = (m_rx_status & 0x02) | 0x01;
+ m_rxcpu->set_input_line(MCS51_INT0_LINE, ASSERT_LINE);
+}
+
u8 decmate2_state::rx_ldata_r()
{
return m_rxdata & 0xff;
@@ -199,18 +679,20 @@ void decmate2_state::rx_hdata_w(u8 data)
void decmate2_state::rx_control_w(u8 data)
{
- // TODO
+ m_rx_control = data & 0x0d;
+ m_rx_status = (m_rx_status & 0xfd) | (data & 0x02);
+ m_rxcpu->set_input_line(MCS51_INT0_LINE, CLEAR_LINE);
+ m_rxcpu->set_input_line(MCS51_INT1_LINE, ASSERT_LINE);
}
u8 decmate2_state::rx_status_r()
{
- // TODO
- return 0;
+ return m_rx_status;
}
u8 decmate2_state::rx_intr_r()
{
- return m_fdc->intrq_r() << 5;
+ return m_fdc->intrq_r() << 5 | 0xdf;
}
void decmate2_state::rx_sel_w(u8 data)
@@ -236,15 +718,25 @@ void decmate2_state::rx_sel_w(u8 data)
u8 decmate2_state::rx_rdy_r()
{
- return m_fdc->drq_r() << 6 /*| m_fdc->ready_r() << 7*/;
+ return m_fdc->drq_r() << 6 | 1 /*m_fdc->ready_r()*/ << 7 | 0x3f;
}
-void decmate2_state::mem_map(address_map &map)
+void decmate2_state::inst_map(address_map &map)
{
- map(00000, 07777).rom().region("cprom", 0);
+ map(0000000, 0177777).ram().share("ram");
+ map(0000000, 0177777).view(m_cprom_iview);
+ m_cprom_iview[0](0000000, 0007777).mirror(0170000).rom().region("cprom", 0);
+ m_cprom_iview[1](0000000, 0177777).r(FUNC(decmate2_state::cprom_switch_r));
}
-// TODO: all I/O devices
+void decmate2_state::data_map(address_map &map)
+{
+ map(0000000, 0177777).ram().share("ram");
+ map(0000000, 0177777).view(m_cprom_dview);
+ m_cprom_dview[0](0000000, 0007777).mirror(0170000).rom().region("cprom", 0);
+}
+
+// TODO: almost all I/O devices
// 03/04/07: Control panel interrupt
// 05: Keyboard input
// 06: Video interrupt
@@ -259,6 +751,89 @@ void decmate2_state::mem_map(address_map &map)
// 36: Modem control and baud rate
// 75: RX Simulator
+void decmate2_state::pc278_io_map(address_map &map)
+{
+ map(0050, 0050).w(FUNC(decmate2_state::kbdtflg_set_w));
+ map(0051, 0051).w(FUNC(decmate2_state::kbdtflg_clear_w));
+ map(0054, 0054).mirror(2).w(m_kbduart, FUNC(ay31015_device::transmit)).umask16(0377);
+ map(0061, 0061).nopw();
+ map(0110, 0110).w(FUNC(decmate2_state::kbdrflg_set_w));
+ map(0111, 0111).w(FUNC(decmate2_state::kbdrflg_clear_w));
+ map(0112, 0112).nopw();
+ map(0114, 0114).mirror(2).r(FUNC(decmate2_state::kbduart_r)).nopw();
+ map(0121, 0121).nopw();
+ map(0122, 0122).w(FUNC(decmate2_state::lscreg_w));
+ map(0124, 0124).w(FUNC(decmate2_state::wrcrtc_w));
+ map(0126, 0126).w(FUNC(decmate2_state::video_mod_w));
+ map(0127, 0127).r(FUNC(decmate2_state::rdcrtc_r)).nopw();
+ map(0320, 0320).w(FUNC(decmate2_state::prtrflg_set_w));
+ map(0321, 0321).w(FUNC(decmate2_state::prtrflg_clear_w));
+ map(0322, 0322).nopw();
+ map(0324, 0324).mirror(2).r(FUNC(decmate2_state::prtuart_r)).nopw();
+ map(0330, 0330).w(FUNC(decmate2_state::prttflg_set_w));
+ map(0331, 0331).w(FUNC(decmate2_state::prttflg_clear_w));
+ map(0334, 0334).mirror(2).w(m_prtuart, FUNC(ay31015_device::transmit)).umask16(0377);
+ map(0362, 0362).w(FUNC(decmate2_state::modem_w));
+ map(0366, 0366).rw(FUNC(decmate2_state::comreg_r), FUNC(decmate2_state::comreg_w));
+ map(0367, 0367).w(FUNC(decmate2_state::cominit_w));
+ map(0750, 0750).w(FUNC(decmate2_state::sel_w));
+ map(0751, 0751).w(FUNC(decmate2_state::lcd_w));
+ map(0752, 0752).rw(FUNC(decmate2_state::xdr_r), FUNC(decmate2_state::xdr_w));
+ map(0753, 0755).nopw();
+ map(0756, 0756).w(FUNC(decmate2_state::intr_w));
+ map(0757, 0757).w(FUNC(decmate2_state::rxinit_w));
+}
+
+void decmate2_state::pc238_io_map(address_map &map)
+{
+ pc278_io_map(map);
+ map(0050, 0051).nopw();
+ map(0110, 0111).nopw();
+ map(0320, 0321).nopw();
+ map(0330, 0331).nopw();
+}
+
+void decmate2_state::devctl_map(address_map &map)
+{
+ map(0050, 0050).lr8(NAME([]() { return 0; }));
+ map(0051, 0051).r(FUNC(decmate2_state::kbdtflg_devctl_r));
+ map(0054, 0054).lr8(NAME([]() { return 0; }));
+ map(0056, 0056).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0061, 0061).r(FUNC(decmate2_state::vint_devctl_r));
+ map(0110, 0110).lr8(NAME([]() { return 0; }));
+ map(0111, 0111).r(FUNC(decmate2_state::kbdrflg_devctl_r));
+ map(0112, 0112).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0114, 0114).lr8(NAME([]() { return hd6120_device::C1; }));
+ map(0116, 0116).lr8(NAME([]() { return hd6120_device::C0 | hd6120_device::C1; }));
+ map(0121, 0121).r(FUNC(decmate2_state::apten_r));
+ map(0122, 0126).lr8(NAME([]() { return 0; }));
+ map(0127, 0127).lr8(NAME([]() { return hd6120_device::C1; }));
+ map(0302, 0302).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0304, 0304).lr8(NAME([]() { return hd6120_device::C1; }));
+ map(0306, 0306).lr8(NAME([]() { return hd6120_device::C0 | hd6120_device::C1; }));
+ map(0314, 0314).lr8(NAME([]() { return 0; }));
+ map(0316, 0316).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0320, 0320).lr8(NAME([]() { return 0; }));
+ map(0321, 0321).r(FUNC(decmate2_state::prtrflg_devctl_r));
+ map(0322, 0322).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0324, 0324).lr8(NAME([]() { return hd6120_device::C1; }));
+ map(0326, 0326).lr8(NAME([]() { return hd6120_device::C0 | hd6120_device::C1; }));
+ map(0330, 0330).lr8(NAME([]() { return 0; }));
+ map(0331, 0331).r(FUNC(decmate2_state::prttflg_devctl_r));
+ map(0334, 0334).lr8(NAME([]() { return 0; }));
+ map(0336, 0336).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0362, 0363).lr8(NAME([]() { return 0; }));
+ map(0366, 0366).r(FUNC(decmate2_state::comreg_devctl_r));
+ map(0367, 0367).lr8(NAME([]() { return 0; }));
+ map(0750, 0750).lr8(NAME([]() { return 0; }));
+ map(0751, 0751).lr8(NAME([]() { return hd6120_device::C0; }));
+ map(0752, 0752).r(FUNC(decmate2_state::xdr_devctl_r));
+ map(0753, 0753).r(FUNC(decmate2_state::str_devctl_r));
+ map(0754, 0754).r(FUNC(decmate2_state::ser_devctl_r));
+ map(0755, 0755).r(FUNC(decmate2_state::sdn_devctl_r));
+ map(0756, 0757).lr8(NAME([]() { return 0; }));
+}
+
void decmate2_state::rx_map(address_map &map)
{
map.global_mask(0x7ff);
@@ -271,6 +846,11 @@ void decmate2_state::rx_map(address_map &map)
}
static INPUT_PORTS_START(decmate2)
+ PORT_START("LAS")
+ PORT_BIT(00010, IP_ACTIVE_LOW, IPT_UNKNOWN) // APU present
+ PORT_BIT(00004, IP_ACTIVE_LOW, IPT_UNKNOWN) // storage adapter present
+ PORT_BIT(00002, IP_ACTIVE_LOW, IPT_UNKNOWN) // graphics controller present
+ PORT_BIT(07761, IP_ACTIVE_LOW, IPT_UNUSED)
INPUT_PORTS_END
static void rx50_floppies(device_slot_interface &device)
@@ -281,8 +861,19 @@ static void rx50_floppies(device_slot_interface &device)
// XTALs: 5.0688 MHz, 15.741 MHz, 22.896 MHz, 16.000 MHz
void decmate2_state::pc278(machine_config &config)
{
- PDP8(config, m_maincpu, 16_MHz_XTAL / 2); // FIXME: HD-6120 with dynamic divider
- m_maincpu->set_addrmap(AS_PROGRAM, &decmate2_state::mem_map);
+ HD6120(config, m_maincpu, 16_MHz_XTAL / 2);
+ // TODO: CPU clock is throttled from 8 MHz to 4 MHz while accessing I/O devices or when CPROM is enabled
+ m_maincpu->set_addrmap(AS_PROGRAM, &decmate2_state::inst_map);
+ m_maincpu->set_addrmap(AS_DATA, &decmate2_state::data_map);
+ m_maincpu->set_addrmap(AS_IO, &decmate2_state::pc278_io_map);
+ m_maincpu->set_addrmap(hd6120_device::AS_DEVCTL, &decmate2_state::devctl_map);
+ m_maincpu->lxmar_callback().set(FUNC(decmate2_state::lxmar_w));
+ m_maincpu->lxpar_callback().set(FUNC(decmate2_state::lxpar_w));
+ m_maincpu->lxdar_callback().set(FUNC(decmate2_state::lxdar_w));
+ m_maincpu->rsr_callback().set_ioport("LAS");
+ m_maincpu->wsr_callback().set(FUNC(decmate2_state::wsr_w));
+ m_maincpu->ioclr_callback().set(FUNC(decmate2_state::pc278_ioclr_w));
+ m_maincpu->strtup_callback().set_constant(0);
I8051(config, m_rxcpu, 16_MHz_XTAL / 2);
m_rxcpu->set_addrmap(AS_IO, &decmate2_state::rx_map);
@@ -300,11 +891,15 @@ void decmate2_state::pc278(machine_config &config)
AY31015(config, m_kbduart); // 6402
m_kbduart->write_so_callback().set("keyboard", FUNC(lk201_device::rx_w));
+ m_kbduart->write_dav_callback().set(FUNC(decmate2_state::keyboard_dr_ff_w));
+ m_kbduart->write_tbmt_callback().set(FUNC(decmate2_state::keyboard_tbre_ff_w));
m_kbduart->set_auto_rdav(true);
AY31015(config, m_prtuart); // 6402
m_prtuart->write_so_callback().set("printer", FUNC(rs232_port_device::write_txd));
m_prtuart->write_fe_callback().set_nop(); // TODO: output on connector pin 9
+ m_prtuart->write_dav_callback().set(FUNC(decmate2_state::printer_dr_ff_w));
+ m_prtuart->write_tbmt_callback().set(FUNC(decmate2_state::printer_tbre_ff_w));
m_prtuart->set_auto_rdav(true);
UPD7201(config, m_mpscc, 16_MHz_XTAL / 8);
@@ -329,6 +924,7 @@ void decmate2_state::pc278(machine_config &config)
CRT9007(config, m_crtc, 15.741_MHz_XTAL / 10);
m_crtc->set_character_width(10); // 9 in 132-column mode
m_crtc->set_screen("screen");
+ m_crtc->int_callback().set(FUNC(decmate2_state::vint_w));
LK201(config, "keyboard").tx_handler().set(m_kbduart, FUNC(ay31015_device::write_si));
@@ -347,6 +943,14 @@ void decmate2_state::pc238(machine_config &config)
config.device_remove("fdc:b0");
config.device_remove("fdc:b1");
+ m_maincpu->set_addrmap(AS_IO, &decmate2_state::pc238_io_map);
+ m_maincpu->ioclr_callback().set(FUNC(decmate2_state::pc238_ioclr_w));
+
+ m_kbduart->write_dav_callback().set(FUNC(decmate2_state::keyboard_dr_w));
+ m_kbduart->write_tbmt_callback().set(FUNC(decmate2_state::keyboard_tbre_w));
+ m_prtuart->write_dav_callback().set(FUNC(decmate2_state::printer_dr_w));
+ m_prtuart->write_tbmt_callback().set(FUNC(decmate2_state::printer_tbre_w));
+
clock_device &pclk(CLOCK(config, "pclk", 16_MHz_XTAL / 208)); // Generated on pin 65 of DC382
pclk.signal_handler().set(m_kbduart, FUNC(ay31015_device::write_rcp));
pclk.signal_handler().append(m_kbduart, FUNC(ay31015_device::write_tcp));