diff options
Diffstat (limited to 'src/devices/cpu/pdp8')
-rw-r--r-- | src/devices/cpu/pdp8/hd6120.cpp | 1364 | ||||
-rw-r--r-- | src/devices/cpu/pdp8/hd6120.h | 216 | ||||
-rw-r--r-- | src/devices/cpu/pdp8/pdp8.cpp | 40 | ||||
-rw-r--r-- | src/devices/cpu/pdp8/pdp8.h | 23 | ||||
-rw-r--r-- | src/devices/cpu/pdp8/pdp8dasm.cpp | 1024 | ||||
-rw-r--r-- | src/devices/cpu/pdp8/pdp8dasm.h | 38 |
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 ¶ms) +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 ¶ms) +{ + 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 ¶ms) 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 |