summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/cops1/cops1base.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/cops1/cops1base.cpp')
-rw-r--r--src/devices/cpu/cops1/cops1base.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/src/devices/cpu/cops1/cops1base.cpp b/src/devices/cpu/cops1/cops1base.cpp
new file mode 100644
index 00000000000..b0e8dc2b102
--- /dev/null
+++ b/src/devices/cpu/cops1/cops1base.cpp
@@ -0,0 +1,220 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/*
+
+ National Semiconductor COPS(MM57 MCU family) cores
+
+This is the first "COPS" series (Controller Oriented Processor Systems),
+4-bit MCUs with internal RAM and most of them internal ROM too.
+It was only briefly on the market and was quickly superceded by the
+2nd "COPS": the COP400 series.
+
+Short list of MCU types:
+- MM5781+MM5782: 2KB ROM, 160 nibbles RAM
+- MM5799: 1.5KB ROM, 96 nibbles RAM
+- MM57140: 640 bytes ROM(10 bytes inaccessible?), 55 nibbles RAM
+
+Note that not every "MM57" chip is a generic MCU, there are plenty other chips,
+mostly for calculators. For example MM5780 for the Quiz Kid, the decap of that
+looks more like a complex state machine.
+
+References:
+- 1977 National Semiconductor MOS/LSI databook
+
+TODO:
+- documentation says that LB 10 is either 0 or 4, depending on RAM configuration,
+ but on qkracerm it's 5 (also confirmed in patent source code), so I assume
+ LB 10 is fully configurable as mask option
+- MM5799 RAM layout is derived from MCU decap, documentation suggests that the
+ secondary option is literally 6x16 but according to the decap it's 4x16 + 4x8
+
+*/
+
+#include "emu.h"
+#include "cops1base.h"
+
+
+cops1_base_device::cops1_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, int prgwidth, address_map_constructor program, int datawidth, address_map_constructor data) :
+ cpu_device(mconfig, type, tag, owner, clock),
+ m_program_config("program", ENDIANNESS_LITTLE, 8, prgwidth, 0, program),
+ m_data_config("data", ENDIANNESS_LITTLE, 8, datawidth, 0, data),
+ m_prgwidth(prgwidth),
+ m_datawidth(datawidth),
+ m_opla(*this, "opla"),
+ m_read_k(*this, 0),
+ m_read_inb(*this, 0),
+ m_read_f(*this, 0),
+ m_write_f(*this),
+ m_read_do3(*this, 0),
+ m_write_do(*this),
+ m_write_s(*this),
+ m_write_blk(*this),
+ m_read_si(*this, 0),
+ m_write_so(*this)
+{ }
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void cops1_base_device::device_start()
+{
+ m_program = &space(AS_PROGRAM);
+ m_data = &space(AS_DATA);
+ m_prgmask = (1 << m_prgwidth) - 1;
+ m_datamask = (1 << m_datawidth) - 1;
+
+ // zerofill
+ m_pc = 0;
+ m_prev_pc = 0;
+ m_op = 0;
+ m_prev_op = 0;
+ m_arg = 0;
+
+ m_a = 0;
+ m_h = 0;
+ m_b = 0;
+ m_c = 0;
+ m_skip = false;
+ m_sa = 0;
+ m_sb = 0;
+ m_serial = 0;
+ m_f = 0;
+ m_do = 0;
+
+ // register for savestates
+ save_item(NAME(m_pc));
+ save_item(NAME(m_prev_pc));
+ save_item(NAME(m_op));
+ save_item(NAME(m_prev_op));
+ save_item(NAME(m_arg));
+
+ save_item(NAME(m_a));
+ save_item(NAME(m_h));
+ save_item(NAME(m_b));
+ save_item(NAME(m_c));
+ save_item(NAME(m_skip));
+ save_item(NAME(m_sa));
+ save_item(NAME(m_sb));
+ save_item(NAME(m_serial));
+ save_item(NAME(m_f));
+ save_item(NAME(m_do));
+
+ // register state for debugger
+ state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%03X").noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_prev_pc).formatstr("%03X").noshow();
+
+ m_state_count = 0;
+ state_add(++m_state_count, "PC", m_pc).formatstr("%03X"); // 1
+ state_add(++m_state_count, "SA", m_sa).formatstr("%03X"); // 2
+ state_add(++m_state_count, "SB", m_sb).formatstr("%03X"); // 3
+
+ state_add(++m_state_count, "A", m_a).formatstr("%01X"); // 4
+ state_add(++m_state_count, "C", m_c).formatstr("%01X"); // 5
+ state_add(++m_state_count, "H", m_h).formatstr("%01X"); // 6
+ state_add(++m_state_count, "B", m_b).formatstr("%02X"); // 7
+ state_add(++m_state_count, "F", m_f).formatstr("%01X"); // 8
+
+ set_icountptr(m_icount);
+}
+
+device_memory_interface::space_config_vector cops1_base_device::memory_space_config() const
+{
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_DATA, &m_data_config)
+ };
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void cops1_base_device::device_reset()
+{
+ m_op = m_prev_op = 0;
+ m_pc = m_prev_pc = 0;
+ m_skip = false;
+
+ // clear outputs
+ m_write_blk(1);
+ m_write_f(m_f = 0);
+ m_write_do(m_do = 0);
+ m_write_s(0);
+}
+
+
+//-------------------------------------------------
+// device_add_mconfig - add device configuration
+//-------------------------------------------------
+
+void cops1_base_device::device_add_mconfig(machine_config &config)
+{
+ PLA(config, "opla", 4, 7, 15).set_format(pla_device::FMT::BERKELEY);
+}
+
+
+//-------------------------------------------------
+// execute
+//-------------------------------------------------
+
+void cops1_base_device::cycle()
+{
+ m_icount--;
+
+ // shift serial data
+ m_write_so(m_serial & 1);
+ int feed = m_option_axo_si ? 1 : m_read_si();
+ m_serial = (m_serial >> 1 | feed << 3) & 0xf;
+}
+
+void cops1_base_device::increment_pc()
+{
+ // low part is LFSR
+ int feed = ((m_pc & 0x3e) == 0) ? 1 : 0;
+ feed ^= (m_pc >> 1 ^ m_pc) & 1;
+ m_pc = (m_pc & ~0x3f) | (m_pc >> 1 & 0x1f) | (feed << 5);
+}
+
+void cops1_base_device::execute_run()
+{
+ while (m_icount > 0)
+ {
+ // remember previous state
+ m_prev_op = m_op;
+ m_prev_pc = m_pc;
+
+ // BLK goes low for 1 cycle with BTD
+ if (m_prev_op == 0x25)
+ m_write_blk(0);
+
+ // fetch next opcode
+ if (!m_skip)
+ debugger_instruction_hook(m_pc);
+ m_op = m_program->read_byte(m_pc);
+ increment_pc();
+ cycle();
+
+ if (m_op != 0x25)
+ m_write_blk(1);
+
+ // fetch opcode argument
+ if (op_argument())
+ {
+ m_arg = m_program->read_byte(m_pc);
+ increment_pc();
+ cycle();
+ }
+
+ // handle opcode if it's not skipped
+ if (m_skip)
+ {
+ m_skip = false;
+ m_op = 0; // fake nop
+ }
+ else
+ execute_one();
+ }
+}