diff --git a/scripts/src/cpu.lua b/scripts/src/cpu.lua index 6c4d5b9a5c4d2..f2fd8127e776a 100644 --- a/scripts/src/cpu.lua +++ b/scripts/src/cpu.lua @@ -3989,3 +3989,20 @@ if opt_tool(CPUS, "C33") then table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/c33/c33dasm.cpp") table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/c33/c33dasm.h") end + +-------------------------------------------------- +-- IBM PALM +--@src/devices/cpu/palm/palm.h,CPUS["PALM"] = true +-------------------------------------------------- + +if CPUS["PALM"] then + files { + MAME_DIR .. "src/devices/cpu/palm/palm.cpp", + MAME_DIR .. "src/devices/cpu/palm/palm.h", + } +end + +if opt_tool(CPUS, "PALM") then + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/palm/palmd.cpp") + table.insert(disasm_files , MAME_DIR .. "src/devices/cpu/palm/palmd.h") +end diff --git a/src/devices/cpu/palm/palm.cpp b/src/devices/cpu/palm/palm.cpp new file mode 100644 index 0000000000000..4e7558770d61f --- /dev/null +++ b/src/devices/cpu/palm/palm.cpp @@ -0,0 +1,280 @@ +// 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/interrupts + * - instruction timing + */ + +#include "emu.h" +#include "palm.h" +#include "palmd.h" + +//#define VERBOSE (LOG_GENERAL) +#include "logmacro.h" + + // helpers for IBM bit numbering +template constexpr T IBIT(T x, U n) noexcept +{ + return BIT(x, sizeof(T) * 8 - n - 1); +} + +template constexpr T IBIT(T x, U n, V w) +{ + return BIT(x, sizeof(T) * 8 - n - w, w); +} + +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_ros_config("ros", 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_icount(0) + , m_r{} + , m_il(0) + , m_il_pending(0) + , m_irpt_req(0) +{ +} + +void palm_device::device_start() +{ + set_icountptr(m_icount); + + state_add(STATE_GENPC, "GENPC", m_r[m_il][0]).mask(0xfffe).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_r[m_il][0]).mask(0xfffe); + + state_add(0, "IL", m_il); + for (unsigned i = 0; i < std::size(m_r[m_il]); i++) + state_add(i + 1, util::string_format("R%d", i).c_str(), m_r[m_il][i]); + + save_item(NAME(m_r)); + save_item(NAME(m_il)); + save_item(NAME(m_il_pending)); + save_item(NAME(m_irpt_req)); + + space(AS_ROS).specific(m_ros); + 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); + + m_il = 0; + + // read initial PC from ROS + m_r[m_il][0] = m_ros.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) + { + // immediately switch to highest pending interrupt level + if (m_il != m_il_pending) + { + m_il = m_il_pending; + + // notify the debugger + if (m_il && machine().debug_flags & DEBUG_FLAG_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_ros.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: m_ioc.write_byte(DA, IMM); break; // control + 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 + // 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; + } +} + +void palm_device::execute_set_input(int irqline, int state) +{ + u8 const il_priority[] = { 0, 1, 2, 3, 3, 3, 3, 3 }; + + switch (irqline) + { + case INPUT_LINE_NMI: + // TODO: machine check + break; + + default: + // interrupt lines are active low + if (!state) + m_irpt_req |= 1U << irqline; + else + m_irpt_req &= ~(1U << irqline); + + // update pending interrupt level + m_il_pending = il_priority[m_irpt_req & 7]; + break; + } +} + +device_memory_interface::space_config_vector palm_device::memory_space_config() const +{ + return space_config_vector + { + std::make_pair(AS_ROS, &m_ros_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 palm_device::create_disassembler() +{ + return std::make_unique(); +} + +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; +} diff --git a/src/devices/cpu/palm/palm.h b/src/devices/cpu/palm/palm.h new file mode 100644 index 0000000000000..ff2bc0c6d0812 --- /dev/null +++ b/src/devices/cpu/palm/palm.h @@ -0,0 +1,69 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_CPU_PALM_PALM_H +#define MAME_CPU_PALM_PALM_H + +#pragma once + +class palm_device : public cpu_device +{ +public: + // interrupt lines are numbered 1..3 + static unsigned constexpr IRPT_REQ1 = INPUT_LINE_IRQ0; + static unsigned constexpr IRPT_REQ2 = INPUT_LINE_IRQ1; + static unsigned constexpr IRPT_REQ3 = INPUT_LINE_IRQ2; + + // four address spaces + static unsigned constexpr AS_ROS = AS_PROGRAM; + static unsigned constexpr AS_RWS = AS_DATA; + static unsigned constexpr AS_IOC = AS_IO; + static unsigned constexpr AS_IOD = 4; + + palm_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + +protected: + // device_t implementation + virtual void device_start() override; + virtual void device_reset() override; + + // device_execute_interface implementation + virtual u32 execute_min_cycles() const noexcept override { return 17; } + virtual u32 execute_max_cycles() const noexcept override { return 54; } + virtual u32 execute_input_lines() const noexcept override { return 3; } + virtual void execute_run() override; + virtual void execute_set_input(int inputnum, int state) override; + + // device_memory_interface implementation + virtual space_config_vector memory_space_config() const override; + + // device_disasm_interface implementation + virtual std::unique_ptr create_disassembler() override; + + bool condition(unsigned const modifier, u16 const data, u8 const mask) const; + s16 modifier(unsigned const modifier) const; + +private: + // address spaces + address_space_config const m_ros_config; + address_space_config const m_rws_config; + address_space_config const m_ioc_config; + address_space_config const m_iod_config; + + memory_access<16, 1, 0, ENDIANNESS_BIG>::specific m_ros; + memory_access<16, 1, 0, ENDIANNESS_BIG>::specific m_rws; + memory_access<4, 0, 0, ENDIANNESS_BIG>::specific m_ioc; + memory_access<4, 0, 0, ENDIANNESS_BIG>::specific m_iod; + + // mame state + int m_icount; + + u16 m_r[4][16]; // registers + u8 m_il; // interrupt level + u8 m_il_pending; // pending interrupt level + u8 m_irpt_req; // interrupt line state +}; + +DECLARE_DEVICE_TYPE(PALM, palm_device) + +#endif // MAME_CPU_PALM_PALM_H diff --git a/src/devices/cpu/palm/palmd.cpp b/src/devices/cpu/palm/palmd.cpp new file mode 100644 index 0000000000000..39b6c358971ef --- /dev/null +++ b/src/devices/cpu/palm/palmd.cpp @@ -0,0 +1,179 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#include "emu.h" +#include "palmd.h" + +static const char *const r[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }; +static const char *const jm[] = { "le", "lo", "eq", "no", "all", "allm", "nom", "ham", "hi", "he", "hl", "sb", "sn", "snm", "sm", "hsnm" }; +static const char *const im[] = { "+1", "+2", "+3", "+4", "-1", "-2", "-3", "-4" }; + +// helper for IBM bit numbering +template constexpr T IBIT(T x, U n, V w) +{ + return BIT(x, sizeof(T) * 8 - n - w, w); +} + +// instruction field shorthand +#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) + +offs_t palm_disassembler::disassemble(std::ostream &stream, offs_t pc, data_buffer const &opcodes, data_buffer const ¶ms) +{ + u16 const op = opcodes.r16(pc); + u32 flags = 0; + + pc += 2; + + switch (IBIT(op, 0, 4)) + { + case 0x0: + switch (MOD) + { + case 0x0: + if (IBIT(op, 4, 8)) + // move minus 2 + util::stream_format(stream, "mvm2 %s,%s", Rx, Ry); + else + // synthetic halt (branch to self) + util::stream_format(stream, "halt"); + break; + case 0x1: util::stream_format(stream, "mvm1 %s,%s", Rx, Ry); break; // move minus 1 + case 0x2: util::stream_format(stream, "mvp1 %s,%s", Rx, Ry); break; // move plus 1 + case 0x3: util::stream_format(stream, "mvp2 %s,%s", Rx, Ry); break; // move plus 2 + case 0x4: + if (Rx) + // move register + util::stream_format(stream, "move %s,%s", Rx, Ry); + else if (Ry) + { + // synthetic jump to register + util::stream_format(stream, "jump %s", Ry); + flags |= STEP_COND; + } + else // synthetic no operation + util::stream_format(stream, "noop"); + break; + case 0x5: util::stream_format(stream, "and %s,%s", Rx, Ry); break; // and byte + case 0x6: util::stream_format(stream, "orb %s,%s", Rx, Ry); break; // or byte + case 0x7: util::stream_format(stream, "xor %s,%s", Rx, Ry); break; // xor byte + case 0x8: util::stream_format(stream, "add %s,%s", Rx, Ry); break; // add + case 0x9: util::stream_format(stream, "sub %s,%s", Rx, Ry); break; // subtract + case 0xa: util::stream_format(stream, "adds1 %s,%s", Rx, Ry); break; // add special #1 + case 0xb: util::stream_format(stream, "adds2 %s,%s", Rx, Ry); break; // add special #2 + case 0xc: util::stream_format(stream, "htl %s,%s", Rx, Ry); break; // high to low + case 0xd: util::stream_format(stream, "lth %s,%s", Rx, Ry); break; // low to high + case 0xe: util::stream_format(stream, "getr $%x,%s", DA, Ry); break; // get to register + case 0xf: util::stream_format(stream, "geta $%x,%s", DA, Ry); break; // get to register and add + } + break; + case 0x1: util::stream_format(stream, "ctl $%x,#$%x", DA, IMM); break; // control + case 0x2: util::stream_format(stream, "ldhd %s,$%x", Rx, IMM * 2); break; // load halfword direct + case 0x3: util::stream_format(stream, "sthd %s,$%x", Rx, IMM * 2); break; // store halfword direct + case 0x4: + // put byte + if (MOD < 8) + util::stream_format(stream, "putb $%x,%s,%s", DA, Ry, im[MOD]); + else + util::stream_format(stream, "putb $%x,%s", DA, Ry); + break; + case 0x5: + // store halfword indirect + if (MOD < 8) + util::stream_format(stream, "sthi %s,%s,%s", Rx, Ry, im[MOD]); + else + util::stream_format(stream, "sthi %s,%s", Rx, Ry); + break; + case 0x6: + // load byte indirect + if (MOD < 8) + util::stream_format(stream, "ldbi %s,%s,%s", Rx, Ry, im[MOD]); + else + util::stream_format(stream, "ldbi %s,%s", Rx, Ry); + break; + case 0x7: + // store byte indirect + if (MOD < 8) + util::stream_format(stream, "stbi %s,%s,%s", Rx, Ry, im[MOD]); + else + util::stream_format(stream, "stbi %s,%s", Rx, Ry); + break; + case 0x8: util::stream_format(stream, "emit %s,#$%x", Rx, IMM); break; // emit byte + case 0x9: util::stream_format(stream, "clri %s,#$%x", Rx, IMM); break; // clear immediate + case 0xa: + if (Rx) + // add immediate + util::stream_format(stream, "addi %s,#$%x", Rx, IMM + 1); + else + { + // synthetic jump relative (forward) + util::stream_format(stream, "jump $%x", pc + IMM + 1); + flags |= STEP_COND; + } + break; + case 0xb: util::stream_format(stream, "seti %s,#$%x", Rx, IMM); break; // set immediate + case 0xc: + switch (MOD) + { + case 0x3: case 0x4: case 0xb: case 0xc: + // one-operand jumps + util::stream_format(stream, "j%-4s %s", jm[MOD], Rx); + break; + case 0x0: case 0x1: case 0x2: case 0x5: + case 0x6: case 0x7: case 0x8: case 0x9: + case 0xa: case 0xd: case 0xe: case 0xf: + // two-operand jumps + util::stream_format(stream, "j%-4s %s,%s", jm[MOD], Rx, Ry); + break; + } + flags |= STEP_COND; + break; + case 0xd: + // load halfword indirect + if (MOD < 8) + util::stream_format(stream, "ldhi %s,%s,%s", Rx, Ry, im[MOD]); + else + util::stream_format(stream, "ldhi %s,%s", Rx, Ry); + break; + case 0xe: + if (DA == 0 && MOD >= 0xc) + { + switch (MOD) + { + case 0xc: util::stream_format(stream, "shftr %s", Ry); break; // shift right 1 + case 0xd: util::stream_format(stream, "rotr %s", Ry); break; // shift right and rotate 1 + case 0xe: util::stream_format(stream, "srr3 %s", Ry); break; // shift right and rotate 3 + case 0xf: util::stream_format(stream, "srr4 %s", Ry); break; // shift right and rotate 4 + } + } + else + { + if (MOD < 0x8) + // get byte + util::stream_format(stream, "getb $%x,%s,%s", DA, Ry, im[MOD]); + else if (MOD < 0xc) + // get byte + util::stream_format(stream, "getb $%x,%s", DA, Ry); + else + // get register byte + util::stream_format(stream, "getrb $%x,%s", DA, Ry); + } + break; + case 0xf: + if (Rx) + // subtract immediate + util::stream_format(stream, "subi %s,#$%x", Rx, IMM + 1); + else + { + // synthetic jump relative (backward) + util::stream_format(stream, "jump $%x", pc - IMM - 1); + flags |= STEP_COND; + } + break; + } + + return 2 | flags | SUPPORTED; +} diff --git a/src/devices/cpu/palm/palmd.h b/src/devices/cpu/palm/palmd.h new file mode 100644 index 0000000000000..773fd9a5edc47 --- /dev/null +++ b/src/devices/cpu/palm/palmd.h @@ -0,0 +1,19 @@ +// license:BSD-3-Clause +// copyright-holders:Patrick Mackinlay + +#ifndef MAME_CPU_PALM_PALMD_H +#define MAME_CPU_PALM_PALMD_H + +#pragma once + +class palm_disassembler : public util::disasm_interface +{ +public: + palm_disassembler() = default; + virtual ~palm_disassembler() = default; + + virtual offs_t disassemble(std::ostream &stream, offs_t pc, data_buffer const &opcodes, data_buffer const ¶ms) override; + virtual u32 opcode_alignment() const override { return 2; } +}; + +#endif // MAME_CPU_PALM_PALMD_H diff --git a/src/tools/unidasm.cpp b/src/tools/unidasm.cpp index cd68c78adee3a..a8fd8a976499a 100644 --- a/src/tools/unidasm.cpp +++ b/src/tools/unidasm.cpp @@ -135,6 +135,7 @@ using util::BIT; #include "cpu/ns32000/ns32000d.h" #include "cpu/nuon/nuondasm.h" #include "cpu/pace/pacedasm.h" +#include "cpu/palm/palmd.h" #include "cpu/patinhofeio/patinho_feio_dasm.h" #include "cpu/pdp1/pdp1dasm.h" #include "cpu/pdp8/pdp8dasm.h" @@ -566,6 +567,7 @@ static const dasm_table_entry dasm_table[] = { "p8xc552", le, 0, []() -> util::disasm_interface * { return new p8xc552_disassembler; } }, { "p8xc562", le, 0, []() -> util::disasm_interface * { return new p8xc562_disassembler; } }, { "pace", le, -1, []() -> util::disasm_interface * { return new pace_disassembler; } }, + { "palm", be, 0, []() -> util::disasm_interface * { return new palm_disassembler; } }, { "patinho_feio", le, 0, []() -> util::disasm_interface * { return new patinho_feio_disassembler; } }, { "pdp1", be, -2, []() -> util::disasm_interface * { return new pdp1_disassembler; } }, { "pdp8", be, -1, []() -> util::disasm_interface * { return new pdp8_disassembler; } },