summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/palm/palm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/palm/palm.cpp')
-rw-r--r--src/devices/cpu/palm/palm.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/src/devices/cpu/palm/palm.cpp b/src/devices/cpu/palm/palm.cpp
new file mode 100644
index 00000000000..071c0d9a6fc
--- /dev/null
+++ b/src/devices/cpu/palm/palm.cpp
@@ -0,0 +1,330 @@
+// license:BSD-3-Clause
+// copyright-holders:Patrick Mackinlay
+
+/*
+ * IBM PALM (apparently "Put All Logic in Microcode") single-card microprocessor.
+ *
+ * Sources:
+ * - IBM 5100 Maintenance Information Manual, SY31-0405-3, Fourth Edition (October 1979), International Business Machines Corporation
+ *
+ * TODO:
+ * - machine check
+ * - instruction timing
+ */
+
+#include "emu.h"
+#include "palm.h"
+#include "palmd.h"
+
+//#define VERBOSE (LOG_GENERAL)
+#include "logmacro.h"
+
+ // helpers for IBM bit numbering
+template <typename T, typename U> constexpr T IBIT(T x, U n) noexcept
+{
+ return BIT(x, sizeof(T) * 8 - n - 1);
+}
+
+template <typename T, typename U, typename V> constexpr T IBIT(T x, U n, V w)
+{
+ return BIT(x, sizeof(T) * 8 - n - w, w);
+}
+
+static u8 constexpr il_priority[] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+DEFINE_DEVICE_TYPE(PALM, palm_device, "palm", "IBM PALM")
+
+palm_device::palm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : cpu_device(mconfig, PALM, tag, owner, clock)
+ , m_pgm_config("pgm", ENDIANNESS_BIG, 16, 16)
+ , m_rws_config("rws", ENDIANNESS_BIG, 16, 16)
+ , m_ioc_config("ioc", ENDIANNESS_BIG, 8, 4)
+ , m_iod_config("iod", ENDIANNESS_BIG, 8, 4)
+ , m_getb_bus(*this)
+ , m_program_level(*this)
+ , m_select_ros(*this)
+ , m_icount(0)
+ , m_r{}
+{
+}
+
+enum ff_mask : u8
+{
+ FF_IPL = 0x80, // initial program load
+ FF_MSS = 0x40, // microprogram storage switch
+
+ FF_IE = 0x08, // interrupt enable
+ FF_IR3 = 0x04, // interrupt request 3
+ FF_IR2 = 0x02, // interrupt request 2
+ FF_IR1 = 0x01, // interrupt request 1
+
+ FF_IR = 0x07, // interrupt request
+};
+
+void palm_device::device_start()
+{
+ set_icountptr(m_icount);
+
+ state_add(STATE_GENPC, "GENPC", m_pc).noshow();
+ state_add<u16>(STATE_GENPCBASE, "CURPC", [this]() { return m_r[m_il][0] & 0xfffeU; });
+
+ state_add(0, "IL", m_il);
+ for (unsigned i = 0; i < std::size(m_r[m_il]); i++)
+ state_add<u16>(i + 1, util::string_format("R%d", i).c_str(), [this, i]() { return m_r[m_il][i]; }, [this, i](u16 data) { m_r[m_il][i] = data; });
+
+ save_item(NAME(m_pc));
+ save_item(NAME(m_r));
+ save_item(NAME(m_il));
+ save_item(NAME(m_ff));
+
+ space(AS_PGM).specific(m_pgm);
+ space(AS_RWS).specific(m_rws);
+ space(AS_IOC).specific(m_ioc);
+ space(AS_IOD).specific(m_iod);
+}
+
+void palm_device::device_reset()
+{
+ space(AS_RWS).install_ram(0, sizeof(m_r) - 1, m_r);
+
+ // select instruction source
+ m_ff = FF_IPL | FF_MSS;
+ m_select_ros((m_ff & FF_MSS) && !(m_ff & FF_IPL));
+
+ // read initial PC from program memory
+ m_il = 0;
+ m_program_level(0);
+ m_pc = m_r[m_il][0] = m_pgm.read_word(0);
+}
+
+#define Rx r[IBIT(op, 4, 4)]
+#define Ry r[IBIT(op, 8, 4)]
+#define DA IBIT(op, 4, 4)
+#define IMM IBIT(op, 8, 8)
+#define MOD IBIT(op, 12, 4)
+
+void palm_device::execute_run()
+{
+ while (m_icount > 0)
+ {
+ // handle pending interrupts
+ u8 const il = il_priority[m_ff & FF_IR];
+ if ((m_ff & FF_IE) && m_il != il)
+ {
+ m_il = il;
+ m_program_level(m_il > 0);
+
+ // notify the debugger
+ if (m_il && debugger_enabled())
+ debug()->interrupt_hook(m_il - 1, m_r[m_il][0] & ~1);
+ }
+
+ // select active register bank
+ u16 (&r)[16] = m_r[m_il];
+
+ // notify the debugger
+ debugger_instruction_hook(r[0] & ~1);
+
+ // fetch instruction
+ u16 const op = m_pgm.read_word(r[0] & ~1);
+
+ // increment instruction address register
+ r[0] += 2;
+
+ switch (IBIT(op, 0, 4))
+ {
+ case 0x0:
+ switch (IBIT(op, 12, 4))
+ {
+ case 0x0: Rx = Ry - 2; break; // move minus 2
+ case 0x1: Rx = Ry - 1; break; // move minus 1
+ case 0x2: Rx = Ry + 1; break; // move plus 1
+ case 0x3: Rx = Ry + 2; break; // move plus 2
+ case 0x4: Rx = Ry; break; // move register
+ case 0x5: Rx &= 0xff00U | u8(Ry); break; // and byte
+ case 0x6: Rx |= u8(Ry); break; // or byte
+ case 0x7: Rx ^= u8(Ry); break; // xor byte
+ case 0x8: Rx += u8(Ry); break; // add
+ case 0x9: Rx -= u8(Ry); break; // subtract
+ case 0xa: Rx = u8(Ry) + (Rx >> 8); break; // add special #1
+ case 0xb: Rx = u8(Ry) + (Rx >> 8) - 0x100; break; // add special #2
+ case 0xc: Rx = (Rx & 0xff00U) | (Ry >> 8); break; // high to low
+ case 0xd: Rx = (Rx & 0x00ffU) | (Ry << 8); break; // low to high
+ case 0xe: Ry = (Ry & 0xff00U) | m_iod.read_byte(DA); break; // get to register
+ case 0xf: Ry += (7 - count_leading_ones_32(u32(m_iod.read_byte(DA)) << 24)) * 2; break; // get to register and add
+ }
+ break;
+ case 0x1:
+ // control
+ if (DA == 0)
+ control(IMM);
+
+ m_ioc.write_byte(DA, IMM);
+ break;
+ case 0x2: Rx = m_rws.read_word(IMM * 2); break; // load halfword direct
+ case 0x3: m_rws.write_word(IMM * 2, Rx); break; // store halfword direct
+ case 0x4:
+ // put byte
+ m_iod.write_byte(DA, m_rws.read_byte(Ry));
+ Ry += modifier(MOD);
+ break;
+ case 0x5:
+ // store halfword indirect
+ m_rws.write_word(Ry, Rx);
+ Ry += modifier(MOD);
+ break;
+ case 0x6:
+ // load byte indirect
+ Rx = m_rws.read_byte(Ry);
+ Ry += modifier(MOD);
+ break;
+ case 0x7:
+ // store byte indirect
+ m_rws.write_byte(Ry, Rx);
+ Ry += modifier(MOD);
+ break;
+ case 0x8: Rx = (Rx & 0xff00U) | IMM; break; // emit byte
+ case 0x9: Rx &= ~IMM; break; // clear immediate
+ case 0xa: Rx += IMM + 1; break; // add immediate
+ case 0xb: Rx |= IMM; break; // set immediate
+ case 0xc:
+ // jump (skip next on condition)
+ if (condition(MOD, Rx, Ry))
+ r[0] += 2;
+ else
+ r[1] = r[0] + 2;
+ break;
+ case 0xd:
+ // load halfword indirect
+ Rx = m_rws.read_word(Ry);
+ Ry += modifier(MOD);
+ break;
+ case 0xe:
+ if (DA == 0 && MOD >= 0xc)
+ {
+ switch (MOD)
+ {
+ case 0xc: Ry = (Ry & 0xff00U) | u8(Ry >> 1); break; // shift right 1
+ case 0xd: Ry = (Ry & 0xff00U) | u8(Ry << 7) | u8(Ry) >> 1; break; // shift right and rotate 1
+ case 0xe: Ry = (Ry & 0xff00U) | u8(Ry << 5) | u8(Ry) >> 3; break; // shift right and rotate 3
+ case 0xf: Ry = (Ry & 0xff00U) | u8(Ry << 4) | u8(Ry) >> 4; break; // shift right and rotate 4
+ }
+ }
+ else
+ {
+ if (MOD < 0xc)
+ {
+ // get byte
+ m_rws.write_byte(Ry, m_iod.read_byte(DA));
+ Ry += modifier(MOD);
+ }
+ else
+ {
+ m_getb_bus(DA, Ry);
+
+ // get register byte
+ Ry = (Ry & 0xff00U) | m_ioc.read_byte(DA);
+ }
+ }
+ break;
+ case 0xf: Rx -= IMM + 1; break; // subtract immediate
+ }
+
+ // TODO: average instruction time quoted as 1.75µs (~27 machine cycles)
+ m_icount -= 27;
+ m_pc = r[0];
+ }
+}
+
+void palm_device::execute_set_input(int irqline, int state)
+{
+ switch (irqline)
+ {
+ case INPUT_LINE_NMI:
+ // TODO: machine check
+ break;
+
+ default:
+ // interrupt lines are active low
+ if (!state)
+ m_ff |= 1U << irqline;
+ else
+ m_ff &= ~(1U << irqline);
+ break;
+ }
+}
+
+device_memory_interface::space_config_vector palm_device::memory_space_config() const
+{
+ return space_config_vector
+ {
+ std::make_pair(AS_PGM, &m_pgm_config),
+ std::make_pair(AS_RWS, &m_rws_config),
+ std::make_pair(AS_IOC, &m_ioc_config),
+ std::make_pair(AS_IOD, &m_iod_config),
+ };
+}
+
+std::unique_ptr<util::disasm_interface> palm_device::create_disassembler()
+{
+ return std::make_unique<palm_disassembler>();
+}
+
+bool palm_device::condition(unsigned const modifier, u16 const data, u8 const mask) const
+{
+ switch (modifier)
+ {
+ case 0x0: return u8(data) <= mask; // le: low or equal
+ case 0x1: return u8(data) < mask; // lo: low
+ case 0x2: return u8(data) == mask; // eq: equal
+ case 0x3: return u8(data) == 0x00; // no: no ones
+ case 0x4: return u8(data) == 0xff; // all: all ones
+ case 0x5: return u8(data & mask) == mask; // allm: all masked
+ case 0x6: return u8(data & mask) == 0x00; // nom: no ones masked
+ case 0x7: return u8((data >> 8) & mask) == mask; // ham: high all masked
+ case 0x8: return u8(data) > mask; // hi: high
+ case 0x9: return u8(data) >= mask; // he: high or equal
+ case 0xa: return u8(data) != mask; // hl: high or low
+ case 0xb: return u8(data) != 0x00; // sb: some bits
+ case 0xc: return u8(data) != 0xff; // sn: some not ones
+ case 0xd: return u8(data & mask) != mask; // snm: some not masked
+ case 0xe: return u8(data & mask) != 0x00; // sm: some masked
+ case 0xf: return u8((data >> 8) & mask) != mask; // hsnm: high some bit not masked
+ default:
+ // can't happen
+ abort();
+ }
+}
+
+s16 palm_device::modifier(unsigned const modifier) const
+{
+ if (modifier < 4)
+ return (modifier & 3) + 1;
+ else if (modifier < 8)
+ return -((modifier & 3) + 1);
+ else
+ return 0;
+}
+
+void palm_device::control(u8 data)
+{
+ LOG("control 0x%02x (%s)\n", data, machine().describe_context());
+
+ // 0: reset controller errors
+
+ // 1: 0=disable interrupts
+ // 2: 0=enable interrupts
+ if (!IBIT(data, 1))
+ m_ff &= ~FF_IE;
+ else if (!IBIT(data, 2))
+ m_ff |= FF_IE;
+
+ // 5: state transition
+ if (!IBIT(data, 5))
+ {
+ m_ff &= ~FF_IPL;
+ m_ff ^= FF_MSS;
+
+ m_select_ros((m_ff & FF_MSS) && !(m_ff & FF_IPL));
+ }
+}