summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/pdp8
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/pdp8')
-rw-r--r--src/devices/cpu/pdp8/hd6120.cpp1364
-rw-r--r--src/devices/cpu/pdp8/hd6120.h216
-rw-r--r--src/devices/cpu/pdp8/pdp8.cpp40
-rw-r--r--src/devices/cpu/pdp8/pdp8.h23
-rw-r--r--src/devices/cpu/pdp8/pdp8dasm.cpp1024
-rw-r--r--src/devices/cpu/pdp8/pdp8dasm.h38
6 files changed, 2516 insertions, 189 deletions
diff --git a/src/devices/cpu/pdp8/hd6120.cpp b/src/devices/cpu/pdp8/hd6120.cpp
new file mode 100644
index 00000000000..0faf3602d9c
--- /dev/null
+++ b/src/devices/cpu/pdp8/hd6120.cpp
@@ -0,0 +1,1364 @@
+// 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 maintains a group of 3-bit internal registers whose data
+ path connects to TEMP. These are used to hold the current memory
+ extension fields, their mirrors and 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.) These 3-bit registers may be enabled on the C0, C1 and EMA2
+ lines at particular times, and the GTF, GCF, PRS, RDF, RIF and RIB
+ internal IOTs read various combinations of them into AC. They include:
+
+ MSB LSB Output conditions
+ -----------------------------------------
+ IF0 IF1 IF2 IFETCH, direct operands (except if FZ)
+ IB0 IB1 IB2 None (until transferred to IF)
+ ISF0 ISF1 ISF2 None
+ DF0 DF1 DF2 Indirect operand addressing, IOTs, etc.
+ DSF0 DSF1 DSF2 None
+ LINK GT IEFF DCA AC writes
+ INTREQ* PWRON 0 ISZ result writes
+ BTSTRAP PNLTRP HLTFLG JMS PC writes
+
+ The GT flag, like MQ, is not used for any specific purpose on the
+ HD-6120, unlike the arithmetic extensions of previous PDP-8 CPUs which
+ originally implemented them. The INTREQ flag is 1 when the input pin
+ is sampled active low and 0 when it is inactive. The PWRON flag is set
+ if STRTUP is sampled as VSS at RESET time; it causes the CPU to trap
+ into panel mode before executing its first instruction.
+
+ 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("io", ENDIANNESS_BIG, 16, 9, -1) // 12 data bits
+ , m_devctl_config("devctl", 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, 0)
+ , m_wsr_callback(*this)
+ , m_strtup_callback(*this, 1)
+ , 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)
+ };
+}
+
+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; // LINK is complemented upon carry out
+ 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); // CLA and/or CMA
+ if (BIT(m_ir, 6))
+ m_flags &= 3; // CLL
+ if (BIT(m_ir, 4))
+ m_flags ^= 4; // CML
+ m_state = minor_state::OP1_2;
+ break;
+
+ case minor_state::OP1_2:
+ if (BIT(m_ir, 0))
+ {
+ ++m_temp; // IAC
+ if (m_temp == 010000)
+ {
+ m_flags ^= 4; // LINK is complemented upon carry out
+ m_temp = 0;
+ }
+ }
+ m_state = minor_state::OP1_3;
+ break;
+
+ case minor_state::OP1_3:
+ m_temp = rotate_step(m_temp);
+ 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.isunset())
+ {
+ 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_temp = m_ac;
+ 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_if < 010)
+ {
+ 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_if << 12 | m_pc);
+ 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, m_if << 12 | m_pc);
+ 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..f3a3d655ba7
--- /dev/null
+++ b/src/devices/cpu/pdp8/hd6120.h
@@ -0,0 +1,216 @@
+// 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_t implementation
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ // device_execute_interface implementation
+ 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 implementation
+ virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;
+
+ // device_memory_interface implementation
+ virtual space_config_vector memory_space_config() const override;
+
+ // device_state_interface implementation
+ 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/devices/cpu/pdp8/pdp8.cpp b/src/devices/cpu/pdp8/pdp8.cpp
index 39f909b03e4..7e30766bf90 100644
--- a/src/devices/cpu/pdp8/pdp8.cpp
+++ b/src/devices/cpu/pdp8/pdp8.cpp
@@ -7,7 +7,6 @@
*/
#include "emu.h"
-#include "debugger.h"
#include "pdp8.h"
#include "pdp8dasm.h"
@@ -51,7 +50,7 @@ DEFINE_DEVICE_TYPE(PDP8, pdp8_device, "pdp8_cpu", "DEC PDP8")
pdp8_device::pdp8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, PDP8, tag, owner, clock),
- m_program_config("program", ENDIANNESS_BIG, 12, 12),
+ m_program_config("program", ENDIANNESS_BIG, 16, 12, -1),
m_pc(0),
m_ac(0),
m_mb(0),
@@ -63,6 +62,7 @@ pdp8_device::pdp8_device(const machine_config &mconfig, const char *tag, device_
m_icount(0)
{
// Allocate & setup
+ m_program_config.m_is_octal = true;
}
@@ -115,18 +115,15 @@ void pdp8_device::device_reset()
//-------------------------------------------------
-// memory_space_config - return the configuration
-// of the specified address space, or nullptr if
-// the space doesn't exist
+// memory_space_config - return a vector of
+// address space configurations for this device
//-------------------------------------------------
-const address_space_config *pdp8_device::memory_space_config(int spacenum) const
+device_memory_interface::space_config_vector pdp8_device::memory_space_config() const
{
- if (spacenum == AS_PROGRAM)
- {
- return &m_program_config;
- }
- return nullptr;
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ };
}
@@ -140,7 +137,7 @@ void pdp8_device::state_string_export(const device_state_entry &entry, std::stri
switch (entry.index())
{
case STATE_GENFLAGS:
- strprintf(str, "%c", m_halt ? 'H' : '.');
+ str = util::string_format("%c", m_halt ? 'H' : '.');
break;
}
}
@@ -151,7 +148,7 @@ void pdp8_device::state_string_export(const device_state_entry &entry, std::stri
// helper function
//-------------------------------------------------
-std::unique_ptr<util::disasm_interface> pdp8_cpu_device::create_disassembler()
+std::unique_ptr<util::disasm_interface> pdp8_device::create_disassembler()
{
return std::make_unique<pdp8_disassembler>();
}
@@ -166,7 +163,7 @@ std::unique_ptr<util::disasm_interface> pdp8_cpu_device::create_disassembler()
// cycles it takes for one instruction to execute
//-------------------------------------------------
-uint32_t pdp8_device::execute_min_cycles() const
+uint32_t pdp8_device::execute_min_cycles() const noexcept
{
return 1; // TODO
}
@@ -177,24 +174,13 @@ uint32_t pdp8_device::execute_min_cycles() const
// cycles it takes for one instruction to execute
//-------------------------------------------------
-uint32_t pdp8_device::execute_max_cycles() const
+uint32_t pdp8_device::execute_max_cycles() const noexcept
{
return 3; // TODO
}
//-------------------------------------------------
-// execute_input_lines - return the number of
-// input/interrupt lines
-//-------------------------------------------------
-
-uint32_t pdp8_device::execute_input_lines() const
-{
- return 0; // TODO
-}
-
-
-//-------------------------------------------------
// execute_set_input - set the state of an input
// line during execution
//-------------------------------------------------
@@ -218,7 +204,7 @@ void pdp8_device::execute_run()
debugger_instruction_hook(m_pc);
- uint16_t op = m_program->read_word(m_pc);
+ uint16_t op [[maybe_unused]] = m_program->read_word(m_pc);
--m_icount;
}
diff --git a/src/devices/cpu/pdp8/pdp8.h b/src/devices/cpu/pdp8/pdp8.h
index 0c59faa3124..f8352f5fda5 100644
--- a/src/devices/cpu/pdp8/pdp8.h
+++ b/src/devices/cpu/pdp8/pdp8.h
@@ -6,10 +6,10 @@
Written by Ryan Holtz
*/
-#pragma once
+#ifndef MAME_CPU_PDP8_PDP8_H
+#define MAME_CPU_PDP8_PDP8_H
-#ifndef __PDP8_H__
-#define __PDP8_H__
+#pragma once
//**************************************************************************
// TYPE DEFINITIONS
@@ -25,14 +25,13 @@ public:
pdp8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
- virtual void device_start() override;
- virtual void device_reset() override;
- virtual void device_stop() override;
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+ virtual void device_stop() override ATTR_COLD;
// device_execute_interface overrides
- virtual uint32_t execute_min_cycles() const override;
- virtual uint32_t execute_max_cycles() const override;
- virtual uint32_t execute_input_lines() const override;
+ virtual uint32_t execute_min_cycles() const noexcept override;
+ virtual uint32_t execute_max_cycles() const noexcept override;
virtual void execute_run() override;
virtual void execute_set_input(int inputnum, int state) override;
@@ -46,7 +45,7 @@ public:
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
// address spaces
- const address_space_config m_program_config;
+ address_space_config m_program_config;
enum state
{
@@ -106,6 +105,4 @@ enum
PDP8_HALT
};
-CPU_DISASSEMBLE( pdp8 );
-
-#endif /* __PDP8_H__ */
+#endif // MAME_CPU_PDP8_PDP8_H
diff --git a/src/devices/cpu/pdp8/pdp8dasm.cpp b/src/devices/cpu/pdp8/pdp8dasm.cpp
index 389cf11fda7..4230eb9f20a 100644
--- a/src/devices/cpu/pdp8/pdp8dasm.cpp
+++ b/src/devices/cpu/pdp8/pdp8dasm.cpp
@@ -1,168 +1,912 @@
// license:BSD-3-Clause
-// copyright-holders:Ryan Holtz
-/*
- First-gen DEC PDP-8 disassembler
+// copyright-holders:AJR
+/***************************************************************************
- Written by Ryan Holtz
-*/
+ PDP-8 family disassembler
+
+ PDP-8 peripherals were traditionally classified by their device codes,
+ which correspond to the second and third octal digits (bits 3:8) of
+ the IOT instruction word. Older PDP-8 buses had the CPU decode the
+ least significant three bits (9:11) into separate strobes issued one
+ after another, which allows some IOTs with codes 6XX1, 6XX2 and 6XX4
+ to be usefully combined with each other. Though the notion of device
+ codes persisted in the Omnibus specification and CMOS-based systems,
+ the I/O transfer protocols used on the PDP-8/E and later CPUs assert
+ the lowest three bits in parallel with the device code, leaving
+ decoding of all nine bits up to the peripherals. This allowed later
+ options to define eight distinct functions for each of their assigned
+ device codes. (The peripherals are also responsible for determining,
+ in real time, whether their IOTs perform reads, writes and/or
+ conditional skips; for the PDP-8/E, PDP-8/A and IM6100 CPUs, they may
+ also request relative or absolute jumps.)
+
+ Most IOTs were traditionally implemented on peripheral options outside
+ the CPU, which originally supported only ION and IOF by itself. (The
+ PDP-8/E CPU added several more which became standard.) It would have
+ been convenient if DEC had assigned every common device type a
+ distinct and unchanging set of IOTs. Unfortunately, not only do
+ several types of options conflict with each other (a few of these
+ could also have their standard device codes modified by jumpers,
+ particularly storage and communications options which could have
+ several used in one system), many Omnibus options use IOTs largely if
+ not entirely different from their functional predecessors built for
+ the older positive and negative buses. DEC reshuffled IOTs less often
+ after the PDP-8/E, but real time clock and parallel I/O facilities
+ were incompatibly reimplemented on the PDP-8/A and CMOS-8s.
+
+ Due to this inconsistency, the "pdp8" disassembler implemented here
+ only specifically identifies IOTs for a few of the most commonly
+ used I/O devices with long and consistent implementation histories
+ (some of which date back all the way to the PDP-5, though the Omnibus
+ interfaces, being functional supersets, are the reference versions):
+ * TTY interface: keyboard (03) + teleprinter (04)
+ * High-speed paper tape reader (01) and punch (02)
+ * Memory extension and time-sharing modes (20–27)
+ * Power fail and automatic restart (10)
+
+ The Group 3 OPR instructions belonging to the Extended Arithmetic
+ Element are also recognized here, though several are unique to the
+ KE8-E version provided for the PDP-8/E, and almost no EAE support was
+ provided for later PDP-8 CPUs except for transfers between AC and
+ MQ. (GT was reintroduced as a general-purpose flag on the HD-6120.)
+ PDP-8/L omits this group entirely, not even recognizing CLA.
+
+ Group 1 OPR combinations are decoded using the standard three-letter
+ shorthand mnemonics. Even shorter macros for these were commonly
+ defined to identify those useful for loading certain constants into
+ AC; the resulting constant values are presented here as comments.
+
+ Certain combinations of IAC, CMA, CML and rotate OPRs are illegal on
+ the PDP-5, the original PDP-8 and the PDP-8/S; of these models, the
+ "Classic-8", which disallows IAC with any rotation, is the only one
+ many programs even tried to remain compatible with. For more obvious
+ reasons, RAL RAR and RTL RTR were officially undefined before HD-6120
+ reassigned the former as R3L, though they had some predictable and
+ even useful results which were highly model-dependent.
+
+ The "hd6120" disassembler, besides adding the HD-6120 exclusive R3L
+ operation, specifically recognizes only its internal IOTs, which now
+ include the memory extension IOTs plus a large number of completely
+ original operations using device codes 20 through 27 (which are
+ strictly internal here and unavailable to external peripherals). The
+ different IOTs for panel mode are used when the disassembly address
+ points above the normal 32K words. HD-6120 based systems tended to
+ reimplement external IOTs using the HD-6121 Input/Output Controller,
+ sometimes partially emulating them with panel-mode programs, and the
+ resulting interfaces offered notoriously poor compatibility with
+ preexisting code.
+
+***************************************************************************/
#include "emu.h"
#include "pdp8dasm.h"
+pdp8_disassembler::pdp8_disassembler(bool has_r3l)
+ : util::disasm_interface()
+ , m_has_r3l(has_r3l)
+{
+}
+
+hd6120_disassembler::hd6120_disassembler()
+ : pdp8_disassembler(true)
+{
+}
+
u32 pdp8_disassembler::opcode_alignment() const
{
- return 2;
+ return 1;
+}
+
+u32 hd6120_disassembler::interface_flags() const
+{
+ return PAGED2LEVEL;
+}
+
+u32 hd6120_disassembler::page_address_bits() const
+{
+ // 5 bits for page + 7 bits for relative address
+ return 12;
+}
+
+u32 hd6120_disassembler::page2_address_bits() const
+{
+ // 3 bits for current field
+ return 3;
+}
+
+void pdp8_disassembler::dasm_memory_reference(std::ostream &stream, u16 inst, offs_t pc)
+{
+ // Bit 4 (DEC numbering) determines whether references are paged
+ const u16 addr = (BIT(inst, 7) ? (pc & 07600) : 0) | (inst & 0177);
+
+ // Bit 3 (DEC numbering) determines indirect addressing
+ if (BIT(inst, 8))
+ {
+ util::stream_format(stream, " I %04o", addr);
+ if ((addr & 07770) == 0010)
+ {
+ stream << " /AUTO-INDEX";
+ if (BIT(inst, 7))
+ stream << " OR CURRENT PAGE"; // IM6100 does no auto-indexing in this case
+ }
+ else if (BIT(inst, 7) && (pc & 07600) == 0)
+ stream << " /CURRENT PAGE";
+ }
+ else
+ {
+ util::stream_format(stream, " %04o", addr);
+ if (BIT(inst, 7) && (pc & 07600) == 0)
+ stream << " /CURRENT PAGE";
+ }
}
-offs_t pdp8_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
+offs_t pdp8_disassembler::dasm_iot(std::ostream &stream, u16 dev, offs_t pc)
{
- uint16_t op = opcodes.r16(pc);
- uint8_t opcode = (op >> 011) & 07;
- uint16_t current_page = pc & 07600;
- uint16_t zero_addr = op & 0177;
- uint16_t current_addr = current_page | zero_addr;
- bool indirect = (op & 0400) ? true : false;
- bool zero_page = (op & 0200) ? false : true;
-
- switch (opcode)
+ switch (dev)
+ {
+ case 000:
+ stream << "SKON"; // PDP-8/E and up
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 001:
+ stream << "ION";
+ break;
+
+ case 002:
+ stream << "IOF";
+ break;
+
+ case 003:
+ stream << "SRQ"; // PDP-8/E and up
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 004:
+ stream << "GTF"; // PDP-8/E and up (used earlier by a PDP-5/Classic-8 ADC option)
+ break;
+
+ case 005:
+ stream << "RTF"; // PDP-8/E and up
+ break;
+
+ case 006:
+ stream << "SGT"; // KE8-E
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 007:
+ stream << "CAF"; // PDP-8/E and up
+ break;
+
+ case 010:
+ stream << "RPE"; // Paper tape reader (PC8-E)
+ break;
+
+ case 011:
+ stream << "RSF"; // Paper tape reader
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 012: case 016:
+ stream << "RRB"; // Paper tape reader
+ if (BIT(dev, 2))
+ stream << " RFC";
+ break;
+
+ case 014:
+ stream << "RFC"; // Paper tape reader
+ break;
+
+ case 020:
+ stream << "PCE"; // Paper tape punch (PC8-E)
+ break;
+
+ case 021:
+ stream << "PSF"; // Paper tape punch
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 022:
+ stream << "PCF"; // Paper tape punch
+ break;
+
+ case 024:
+ stream << "PPC"; // Paper tape punch
+ break;
+
+ case 026:
+ stream << "PLS"; // Paper tape punch
+ break;
+
+ case 030:
+ stream << "KCF"; // TTY/keyboard (KL8-E and up)
+ break;
+
+ case 031:
+ stream << "KSF"; // TTY/keyboard
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 032:
+ stream << "KCC"; // TTY/keyboard
+ break;
+
+ case 034:
+ stream << "KRS"; // TTY/keyboard
+ break;
+
+ case 035:
+ stream << "KIE"; // TTY/keyboard (KL8-E and up)
+ break;
+
+ case 036:
+ stream << "KRB"; // TTY/keyboard
+ break;
+
+ case 040:
+ stream << "TFL"; // Teleprinter (KL8-E and up)
+ break;
+
+ case 041:
+ stream << "TSF"; // Teleprinter
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 042:
+ stream << "TCF"; // Teleprinter
+ break;
+
+ case 044:
+ stream << "TPC"; // Teleprinter
+ break;
+
+ case 045:
+ stream << "TSK"; // Teleprinter (KL8-E and up; SPI is a later name for this IOT)
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 046:
+ stream << "TLS"; // Teleprinter
+ break;
+
+ case 0102:
+ stream << "SPL"; // Automatic restart
+ return 1 | SUPPORTED | STEP_COND;
+
+ 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: // Memory extension
+ if (BIT(dev, 0))
+ stream << "CDF ";
+ if (BIT(dev, 1))
+ stream << "CIF ";
+ util::stream_format(stream, "%o", dev & 070);
+ break;
+
+ case 0204:
+ stream << "CINT"; // Time-sharing extension
+ break;
+
+ case 0214:
+ stream << "RDF"; // Memory extension
+ break;
+
+ case 0224:
+ stream << "RIF"; // Memory extension
+ break;
+
+ case 0234:
+ stream << "RIB"; // Memory extension
+ break;
+
+ case 0244:
+ stream << "RMF"; // Memory extension
+ break;
+
+ case 0254:
+ stream << "SINT"; // Time-sharing extension (LIF on some Intersil peripherals)
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 0264:
+ stream << "CUF"; // Time-sharing extension
+ break;
+
+ case 0274:
+ stream << "SUF"; // Time-sharing extension
+ break;
+
+ default:
+ util::stream_format(stream, "IOT %03o", dev);
+ return 1 | SUPPORTED | STEP_COND; // any IOT can theoretically cause a skip
+ }
+
+ return 1 | SUPPORTED;
+}
+
+
+offs_t hd6120_disassembler::dasm_iot(std::ostream &stream, u16 dev, offs_t pc)
+{
+ switch (dev)
+ {
+ case 000:
+ if (BIT(pc, 15))
+ stream << "PRS";
+ else
+ {
+ stream << "SKON";
+ return 1 | SUPPORTED | STEP_COND;
+ }
+ break;
+
+ case 001:
+ stream << "ION";
+ break;
+
+ case 002:
+ stream << "IOF";
+ break;
+
+ case 003:
+ if (BIT(pc, 15))
+ stream << "PGO";
+ else
+ {
+ stream << "SRQ";
+ return 1 | SUPPORTED | STEP_COND;
+ }
+ break;
+
+ case 004:
+ if (BIT(pc, 15))
+ stream << "PEX";
+ else
+ stream << "GTF";
+ break;
+
+ case 005:
+ stream << "RTF";
+ break;
+
+ case 006:
+ stream << "SGT";
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 007:
+ stream << "CAF";
+ 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:
+ if (BIT(dev, 0))
+ stream << "CDF ";
+ if (BIT(dev, 1))
+ stream << "CIF ";
+ util::stream_format(stream, "%o", dev & 070);
+ break;
+
+ case 0205: case 0215: case 0245: case 0255:
+ util::stream_format(stream, "P%cC%d", BIT(dev, 3) ? 'A' : 'P', BIT(dev, 5) ? 2 : 1);
+ break;
+
+ case 0206: case 0216: case 0226: case 0236:
+ if (BIT(pc, 15))
+ util::stream_format(stream, "IOT %03o", dev); // NOPs if already in panel mode
+ else
+ util::stream_format(stream, "PR%d", (dev & 030) >> 3);
+ return 1 | SUPPORTED | STEP_OVER;
+
+ case 0207: case 0217: case 0227: case 0237:
+ util::stream_format(stream, "%cSP%d", BIT(dev, 3) ? 'L' : 'R', BIT(dev, 4) ? 2 : 1);
+ break;
+
+ case 0214:
+ stream << "RDF";
+ break;
+
+ case 0224:
+ stream << "RIF";
+ break;
+
+ case 0225: case 0265:
+ util::stream_format(stream, "RTN%d", BIT(dev, 5) ? 2 : 1);
+ return 1 | SUPPORTED | STEP_OUT;
+
+ case 0235: case 0275:
+ util::stream_format(stream, "POP%d", BIT(dev, 5) ? 2 : 1);
+ break;
+
+ case 0234:
+ stream << "RIB";
+ break;
+
+ case 0244:
+ stream << "RMF";
+ break;
+
+ case 0246:
+ stream << "WSR";
+ break;
+
+ case 0256:
+ stream << "GCF";
+ break;
+
+ case 0266: case 0276:
+ if (BIT(pc, 15))
+ util::stream_format(stream, "%cPD", BIT(dev, 3) ? 'S' : 'C');
+ else
+ util::stream_format(stream, "IOT %03o", dev); // Panel mode only
+ break;
+
+ default:
+ util::stream_format(stream, "IOT %03o", dev);
+ return 1 | SUPPORTED | STEP_COND;
+ }
+
+ return 1 | SUPPORTED;
+}
+
+// Sequence 1: CLA, CLL
+// Sequence 2: CMA, CML
+// Sequence 3: IAC
+// Sequence 4: RAL, RTL, RAR, RTR, BSW, R3L
+void pdp8_disassembler::dasm_opr_group1(std::ostream &stream, u16 opr)
+{
+ if (opr == 000)
+ stream << "NOP";
+ else if ((opr & 0357) == 0204)
+ {
+ if (BIT(opr, 4))
+ stream << "CML ";
+ stream << "GLK"; // Get link (CLA RAL)
+ }
+ else
{
- case 0:
- util::stream_format(stream, "AND %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr);
+ int count = 0;
+ switch (opr & 0240)
+ {
+ case 000:
break;
- case 1:
- util::stream_format(stream, "TAD %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr);
+
+ case 040:
+ if (BIT(opr, 0))
+ stream << "CIA"; // Complement and increment (CMA IAC); may not be combined with rotate operations on Classic-8
+ else
+ stream << "CMA";
+ ++count;
break;
- case 2:
- util::stream_format(stream, "ISZ %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr);
+
+ case 0200:
+ stream << "CLA";
+ ++count;
+ break;
+
+ case 0240:
+ stream << "STA"; // Set accumulator (CLA CMA)
+ ++count;
break;
- case 3:
- util::stream_format(stream, "DCA %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr);
+ }
+ switch (opr & 0120)
+ {
+ case 000:
break;
- case 4:
- util::stream_format(stream, "JMS %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr);
+
+ case 020:
+ if (count != 0)
+ stream << " ";
+ stream << "CML";
+ ++count;
break;
- case 5:
- util::stream_format(stream, "JMP %c %05o", indirect ? 'I' : ' ', zero_page ? zero_addr : current_addr);
+
+ case 0100:
+ if (count != 0)
+ stream << " ";
+ stream << "CLL";
+ ++count;
break;
- case 6:
- util::stream_format(stream, "IOT %03o %01o", (op >> 03) & 077, op & 07);
+
+ case 0120:
+ if (count != 0)
+ stream << " ";
+ stream << "STL"; // Set link (CLL CML)
+ ++count;
break;
- case 7:
+ }
+ if (BIT(opr, 0) && (opr & 0240) != 040)
+ {
+ if (count != 0)
+ stream << " ";
+ stream << "IAC"; // May not be combined with rotate operations on Classic-8
+ ++count;
+ }
+
+ // Rotate operations
+ if (m_has_r3l && (opr & 016) == 014)
+ {
+ if (count != 0)
+ stream << " ";
+ stream << "R3L";
+ ++count;
+ }
+ else if ((opr & 016) == 002)
{
- bool group2 = ((op & 0401) == 0400);
- if (!group2)
+ if (count != 0)
+ stream << " ";
+ stream << "BSW"; // PDP-8/E and up
+ ++count;
+ }
+ else
+ {
+ if (BIT(opr, 2))
{
- if (!(op & 0377))
- {
- util::stream_format(stream, "NOP ");
- }
- else
- {
- if (op & 0200)
- {
- util::stream_format(stream, "CLA ");
- }
- if (op & 0100)
- {
- util::stream_format(stream, "CLL ");
- }
- if (op & 040)
- {
- util::stream_format(stream, "CMA ");
- }
- if (op & 020)
- {
- util::stream_format(stream, "CML ");
- }
- if (op & 01)
- {
- util::stream_format(stream, "IAC ");
- }
- if (op & 010)
- {
- if (op & 02)
- {
- util::stream_format(stream, "RTR ");
- }
- else
- {
- util::stream_format(stream, "RAR ");
- }
- }
- if (op & 04)
- {
- if (op & 02)
- {
- util::stream_format(stream, "RTL ");
- }
- else
- {
- util::stream_format(stream, "RAL ");
- }
- }
- }
+ if (count != 0)
+ stream << " ";
+ util::stream_format(stream, "R%cL", BIT(opr, 1) ? 'T' : 'A');
+ ++count;
}
- else
+ if (BIT(opr, 3))
+ {
+ if (count != 0)
+ stream << " ";
+ util::stream_format(stream, "R%cR", BIT(opr, 1) ? 'T' : 'A');
+ ++count;
+ }
+ }
+
+ u16 nl;
+ switch (opr & 0314)
+ {
+ case 0200: case 0300:
+ nl = (opr & 041) == 040 ? 07777 : (opr & 041) == 001 ? 1 : 0;
+ if ((opr & 042) == 002)
+ nl <<= 6;
+ util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl);
+ break;
+
+ case 0214: case 0314:
+ if (m_has_r3l && !BIT(opr, 1))
{
- if (!(op & 0377))
- {
- util::stream_format(stream, "NOP ");
- }
- else
- {
- if (op & 010)
- {
- if (!(op & 0160))
- {
- util::stream_format(stream, "SKP ");
- }
- else
- {
- if (op & 0100)
- {
- util::stream_format(stream, "SPA ");
- }
- if (op & 040)
- {
- util::stream_format(stream, "SNA ");
- }
- if (op & 020)
- {
- util::stream_format(stream, "SZL ");
- }
- }
- }
- else
- {
- if (op & 0100)
- {
- util::stream_format(stream, "SMA ");
- }
- if (op & 040)
- {
- util::stream_format(stream, "SZA ");
- }
- if (op & 020)
- {
- util::stream_format(stream, "SNL ");
- }
- }
- if (op & 0200)
- {
- util::stream_format(stream, "CLA ");
- }
- if (op & 04)
- {
- util::stream_format(stream, "OSR ");
- }
- if (op & 02)
- {
- util::stream_format(stream, "HLT ");
- }
- }
+ nl = (opr & 041) == 040 ? 07777 : (opr & 041) == 001 ? 010 : 0;
+ util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl);
}
+ break;
+
+ case 0304:
+ nl = ((opr & 041) == 001 ? 2 : 0) | (BIT(opr, 4) != BIT(opr, 5) ? 1 : 0);
+ if (BIT(opr, 1))
+ nl <<= 1;
+ if ((opr & 041) == 040)
+ nl ^= 07777;
+ util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl);
+ break;
+
+ case 0310:
+ nl = BIT(opr, 4) != BIT(opr, 5) ? 04000 : 0;
+ if (BIT(opr, 1))
+ {
+ nl >>= 1;
+ if ((opr & 041) == 001)
+ nl |= 04000;
+ }
+ if ((opr & 041) == 040)
+ nl ^= 07777;
+ util::stream_format(stream, "%*s/NL%04o", 17 - std::min(count, 4) * 4, "", nl);
+ break;
+ }
+ }
+}
+
+// Sequence 1a: SKP, SPA, SNA, SZL (skip if all conditions hold)
+// Sequence 1b: SMA, SZA, SZL (skip if any condition holds)
+// Sequence 2: CLA
+// Sequence 3: OSR, HLT
+offs_t pdp8_disassembler::dasm_opr_group2(std::ostream &stream, u16 opr)
+{
+ if ((opr & 0170) != 0)
+ {
+ // Skip instructions
+ if ((opr & 0170) == 010)
+ stream << "SKP";
+ else
+ {
+ if (BIT(opr, 6))
+ {
+ stream << (BIT(opr, 3) ? "SPA" : "SMA");
+ if ((opr & 060) != 0)
+ stream << " ";
+ }
+ if (BIT(opr, 5))
+ {
+ stream << (BIT(opr, 3) ? "SNA" : "SZA");
+ if (BIT(opr, 4))
+ stream << " ";
+ }
+ if (BIT(opr, 4))
+ stream << (BIT(opr, 3) ? "SZL" : "SNL");
}
+ if ((opr & 0206) != 0)
+ stream << " ";
+ }
+ if (BIT(opr, 2))
+ {
+ if (BIT(opr, 7))
+ stream << "LSR"; // Load switch register (CLA OSR)
+ else
+ stream << "OSR";
+ if (BIT(opr, 1))
+ stream << " HLT";
+ }
+ else if (BIT(opr, 1))
+ {
+ if (BIT(opr, 7))
+ stream << "CLA ";
+ stream << "HLT";
+ }
+ else
+ {
+ if (BIT(opr, 7))
+ stream << "CLA";
+ else if (opr == 0400)
+ stream << "NOP";
+ if ((opr & 0170) == 0)
+ stream << "!400";
+ }
+
+ // HACK: HLT is a software trap on HD-6120
+ if (m_has_r3l && BIT(opr, 1))
+ return 1 | SUPPORTED | STEP_OVER;
+ else if ((opr & 0160) != 0)
+ return 1 | SUPPORTED | STEP_COND;
+ else
+ return 1 | SUPPORTED;
+}
+
+// Sequence 1: CLA
+// Sequence 2: MQL, MQA (also SCA on older EAEs)
+// Sequence 3: MUY, DIV, NMI, SHL, ASR, LSR, etc.
+offs_t pdp8_disassembler::dasm_opr_group3(std::ostream &stream, u16 opr, offs_t pc, const pdp8_disassembler::data_buffer &opcodes)
+{
+ switch (opr & 0320)
+ {
+ case 000:
+ if (opr == 0401)
+ stream << "NOP";
+ break;
+
+ case 020:
+ if (opr == 0431)
+ {
+ stream << "SWAB"; // KE8-E Mode A (change to Mode B)
+ return 1 | SUPPORTED;
+ }
+ else
+ stream << "MQL"; // Extended Arithmetic Element or internal for PDP-8/E and up
+ break;
+
+ case 0100:
+ stream << "MQA"; // Extended Arithmetic Element or internal for PDP-8/E and up
+ break;
+
+ case 0120: case 0320:
+ if (BIT(opr, 7))
+ stream << "CLA ";
+ if (opr == 0573)
+ {
+ stream << "DPIC"; // KE8-E Mode B
+ return 1 | SUPPORTED;
+ }
+ else if (opr == 0575)
+ {
+ stream << "DCM"; // KE8-E Mode B
+ return 1 | SUPPORTED;
+ }
+ else
+ stream << "SWP"; // PDP-8/E and up
+ break;
+
+ case 0200:
+ stream << "CLA"; // NOP on PDP-8/L
+ break;
+
+ case 0220:
+ if (opr == 0663)
+ {
+ stream << "DLD"; // KE8-E Mode B (CAM DAD)
+ return 1 | SUPPORTED;
+ }
+ else
+ stream << "CAM"; // Extended Arithmetic Element or internal for PDP-8/E and up (CLA MQA)
+ break;
+
+ case 0300:
+ stream << "ACL"; // Extended Arithmetic Element or internal for PDP-8/E and up (CLA MQL)
+ break;
+ }
+ switch (opr & 056)
+ {
+ case 000:
+ if ((opr & 0120) == 0)
+ stream << "!201";
+ break;
+
+ case 002:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "ACS"; // KE8-E Mode B
+ break;
+
+ case 004:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ util::stream_format(stream, "MUY; %04o", opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element
+ return 2 | SUPPORTED;
+
+ case 006:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ util::stream_format(stream, "DVI; %04o", opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element
+ return 2 | SUPPORTED;
+
+ case 010:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "NMI"; // Extended Arithmetic Element
+ break;
+
+ case 012:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ util::stream_format(stream, "SHL; %04o", opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element
+ return 2 | SUPPORTED;
+
+ case 014: case 016:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ util::stream_format(stream, "%cSR; %04o", BIT(opr, 1) ? 'L' : 'A', opcodes.r16(pc + 1) & 07777); // Extended Arithmetic Element
+ return 2 | SUPPORTED;
+
+ case 040:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "SCA"; // Extended Arithmetic Element
+ break;
+
+ case 042:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "DAD"; // KE8-E Mode B
+ break;
+
+ case 044:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "DST"; // KE8-E Mode B
+ break;
+
+ case 046:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "SWBA"; // KE8-E Mode B (change to mode A)
+ break;
+
+ case 050:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "DPSZ"; // KE8-E Mode B
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 056:
+ if ((opr & 0320) != 0)
+ stream << " ";
+ stream << "SAM"; // KE8-E Mode B
+ break;
+
+ default:
+ if ((opr & 0320) != 0)
+ util::stream_format(stream, "!%02o", opr & 056);
+ else
+ util::stream_format(stream, "OPR %03o", opr & 0777);
+ break;
+ }
+ return 1 | SUPPORTED;
+}
+
+offs_t hd6120_disassembler::dasm_opr_group3(std::ostream &stream, u16 opr, offs_t pc, const hd6120_disassembler::data_buffer &opcodes)
+{
+ switch (opr & 0320)
+ {
+ case 000:
+ stream << "NOP";
+ break;
+
+ case 020:
+ stream << "MQL";
+ break;
+
+ case 0100:
+ stream << "MQA";
+ break;
+
+ case 0120: case 0320:
+ if (BIT(opr, 7))
+ stream << "CLA ";
+ stream << "SWP";
+ break;
+
+ case 0200:
+ stream << "CLA";
+ break;
+
+ case 0220:
+ stream << "CAM";
+ break;
+
+ case 0300:
+ stream << "ACL";
+ break;
+ }
+ if ((opr & 0120) == 0)
+ util::stream_format(stream, "!%03o", opr & 0457);
+ else if ((opr & 056) != 0)
+ util::stream_format(stream, "!%02o", opr & 056); // Inhibit interrupts for next instruction
+ return 1 | SUPPORTED;
+}
+
+offs_t pdp8_disassembler::disassemble(std::ostream &stream, offs_t pc, const pdp8_disassembler::data_buffer &opcodes, const pdp8_disassembler::data_buffer &params)
+{
+ const u16 inst = opcodes.r16(pc);
+
+ // Bits 0:2 (DEC numbering) specify instruction code
+ switch (BIT(inst, 9, 3))
+ {
+ case 0:
+ stream << "AND";
+ dasm_memory_reference(stream, inst, pc);
+ break;
+
+ case 1:
+ stream << "TAD";
+ dasm_memory_reference(stream, inst, pc);
+ break;
+
+ case 2:
+ stream << "ISZ";
+ dasm_memory_reference(stream, inst, pc);
+ return 1 | SUPPORTED | STEP_COND;
+
+ case 3:
+ stream << "DCA";
+ dasm_memory_reference(stream, inst, pc);
+ break;
+
+ case 4:
+ stream << "JMS";
+ dasm_memory_reference(stream, inst, pc);
+ return 1 | SUPPORTED | STEP_OVER;
+
+ case 5:
+ stream << "JMP";
+ dasm_memory_reference(stream, inst, pc);
+ if (BIT(inst, 8))
+ return 1 | SUPPORTED | STEP_OUT;
+ break;
+
+ case 6:
+ return dasm_iot(stream, inst & 0777, pc);
+
+ case 7:
+ if (!BIT(inst, 8))
+ dasm_opr_group1(stream, inst & 0777);
+ else if (!BIT(inst, 0))
+ return dasm_opr_group2(stream, inst & 0777);
+ else
+ return dasm_opr_group3(stream, inst & 0777, pc, opcodes);
+ break;
}
- return 2 | SUPPORTED;
+ return 1 | SUPPORTED;
}
diff --git a/src/devices/cpu/pdp8/pdp8dasm.h b/src/devices/cpu/pdp8/pdp8dasm.h
index df6c92e9ddb..fd32ae9cd2e 100644
--- a/src/devices/cpu/pdp8/pdp8dasm.h
+++ b/src/devices/cpu/pdp8/pdp8dasm.h
@@ -1,10 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Ryan Holtz
-/*
- First-gen DEC PDP-8 disassembler
-
- Written by Ryan Holtz
-*/
+// copyright-holders:AJR
#ifndef MAME_CPU_PDP8_PDP8DASM_H
#define MAME_CPU_PDP8_PDP8DASM_H
@@ -14,11 +9,36 @@
class pdp8_disassembler : public util::disasm_interface
{
public:
- pdp8_disassembler() = default;
- virtual ~pdp8_disassembler() = default;
+ pdp8_disassembler(bool has_r3l = false);
+protected:
+ // util::disasm_interface overrides
virtual u32 opcode_alignment() const override;
virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
+
+ void dasm_memory_reference(std::ostream &stream, u16 inst, offs_t pc);
+ virtual offs_t dasm_iot(std::ostream &stream, u16 dev, offs_t pc);
+ void dasm_opr_group1(std::ostream &stream, u16 inst);
+ offs_t dasm_opr_group2(std::ostream &stream, u16 inst);
+ virtual offs_t dasm_opr_group3(std::ostream &stream, u16 inst, offs_t pc, const data_buffer &opcodes);
+
+private:
+ const bool m_has_r3l;
+};
+
+class hd6120_disassembler : public pdp8_disassembler
+{
+public:
+ hd6120_disassembler();
+
+protected:
+ // util::disasm_interface overrides
+ virtual u32 interface_flags() const override;
+ virtual u32 page_address_bits() const override;
+ virtual u32 page2_address_bits() const override;
+
+ virtual offs_t dasm_iot(std::ostream &stream, u16 dev, offs_t pc) override;
+ virtual offs_t dasm_opr_group3(std::ostream &stream, u16 inst, offs_t pc, const data_buffer &opcodes) override;
};
-#endif
+#endif // MAME_CPU_PDP8_PDP8DASM_H