diff options
Diffstat (limited to 'src/devices/cpu/pps41')
-rw-r--r-- | src/devices/cpu/pps41/mm75.cpp | 27 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm75.h | 56 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm75op.cpp | 40 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm76.cpp | 182 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm76.h | 165 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm76op.cpp | 473 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm78.cpp | 217 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm78.h | 128 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm78la.cpp | 113 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm78la.h | 98 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm78laop.cpp | 73 | ||||
-rw-r--r-- | src/devices/cpu/pps41/mm78op.cpp | 202 | ||||
-rw-r--r-- | src/devices/cpu/pps41/pps41base.cpp | 351 | ||||
-rw-r--r-- | src/devices/cpu/pps41/pps41base.h | 154 | ||||
-rw-r--r-- | src/devices/cpu/pps41/pps41d.cpp | 210 | ||||
-rw-r--r-- | src/devices/cpu/pps41/pps41d.h | 66 |
16 files changed, 2555 insertions, 0 deletions
diff --git a/src/devices/cpu/pps41/mm75.cpp b/src/devices/cpu/pps41/mm75.cpp new file mode 100644 index 00000000000..83c5ae06368 --- /dev/null +++ b/src/devices/cpu/pps41/mm75.cpp @@ -0,0 +1,27 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM75 MCU + +*/ + +#include "emu.h" +#include "mm75.h" + + +DEFINE_DEVICE_TYPE(MM75, mm75_device, "mm75", "Rockwell MM75 A7500") // stripped-down MM76 (no serial i/o, less pins) + + +// constructor +mm75_device::mm75_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm76_device(mconfig, MM75, tag, owner, clock, 10, address_map_constructor(FUNC(mm75_device::program_0_6k), this), 6, address_map_constructor(FUNC(mm75_device::data_48x4), this)) +{ } + + +// initialize +void mm75_device::device_start() +{ + mm76_device::device_start(); + set_d_pins(9); +} diff --git a/src/devices/cpu/pps41/mm75.h b/src/devices/cpu/pps41/mm75.h new file mode 100644 index 00000000000..92785d0d5ae --- /dev/null +++ b/src/devices/cpu/pps41/mm75.h @@ -0,0 +1,56 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM75 MCU + +*/ + +#ifndef MAME_CPU_PPS41_MM75_H +#define MAME_CPU_PPS41_MM75_H + +#pragma once + +#include "mm76.h" + +// pinout reference + +/* + ____ ____ + RIO8 1 |* \_/ | 28 RIO7 + RIO1 2 | | 27 RIO6 + RIO2 3 | | 26 RIO5 + RIO3 4 | | 25 INT0 + RIO4 5 | | 24 PO + DIO0 6 | | 23 PI4 + DIO1 7 | MM75 | 22 PI3 + DIO2 8 | | 21 PI2 + DIO3 9 | | 20 PI1 + DIO4 10 | | 19 TEST + DIO5 11 | | 18 Vdd + DIO6 12 | | 17 VC + DIO7 13 | | 16 A + Vss 14 |___________| 15 DIO8 + +*/ + +class mm75_device : public mm76_device +{ +public: + mm75_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + virtual void device_start() override ATTR_COLD; + + // opcode handlers + virtual void op_ios() override; + virtual void op_i2c() override; + + virtual void op_ibm() override; + virtual void op_int1h() override; +}; + + +DECLARE_DEVICE_TYPE(MM75, mm75_device) + +#endif // MAME_CPU_PPS41_MM75_H diff --git a/src/devices/cpu/pps41/mm75op.cpp b/src/devices/cpu/pps41/mm75op.cpp new file mode 100644 index 00000000000..445655ef31b --- /dev/null +++ b/src/devices/cpu/pps41/mm75op.cpp @@ -0,0 +1,40 @@ +// license:BSD-3-Clause +// copyright-holders:hap + +// MM75 opcode handlers + +#include "emu.h" +#include "mm75.h" + + +// unsupported opcodes + +void mm75_device::op_ios() +{ + // IOS: does not have serial I/O + op_illegal(); +} + +void mm75_device::op_i2c() +{ + // I2C: does not have PI5-8 pins + m_a = 0; + op_illegal(); +} + + +// changed opcodes + +void mm75_device::op_ibm() +{ + // IBM: INT1 is shared with RIO8 + mm76_device::op_ibm(); + m_a &= ~(m_int_line[1] << 3); +} + +void mm75_device::op_int1h() +{ + // INT1H: INT1 is shared with RIO8 + int state = (m_read_r() & m_r_output) >> 7 & m_int_line[1]; + m_skip = bool(state); +} diff --git a/src/devices/cpu/pps41/mm76.cpp b/src/devices/cpu/pps41/mm76.cpp new file mode 100644 index 00000000000..d1eb8bac0ca --- /dev/null +++ b/src/devices/cpu/pps41/mm76.cpp @@ -0,0 +1,182 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM76 MCU + +*/ + +#include "emu.h" +#include "mm76.h" + +#include "pps41d.h" + + +DEFINE_DEVICE_TYPE(MM76, mm76_device, "mm76", "Rockwell MM76 A7600") // 640 bytes ROM, 48 nibbles RAM +DEFINE_DEVICE_TYPE(MM76L, mm76l_device, "mm76l", "Rockwell MM76L B7600") // low-power +DEFINE_DEVICE_TYPE(MM76E, mm76e_device, "mm76e", "Rockwell MM76E A8600") // ROM extended to 1KB +DEFINE_DEVICE_TYPE(MM76EL, mm76el_device, "mm76el", "Rockwell MM76EL B8600") // low-power + + +// constructor +mm76_device::mm76_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm76_device(mconfig, MM76, tag, owner, clock, 10, address_map_constructor(FUNC(mm76_device::program_0_6k), this), 6, address_map_constructor(FUNC(mm76_device::data_48x4), this)) +{ } + +mm76_device::mm76_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) : + pps41_base_device(mconfig, type, tag, owner, clock, prgwidth, program, datawidth, data) +{ } + +mm76l_device::mm76l_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm76_device(mconfig, MM76L, tag, owner, clock, 10, address_map_constructor(FUNC(mm76l_device::program_0_6k), this), 6, address_map_constructor(FUNC(mm76l_device::data_48x4), this)) +{ } + +mm76e_device::mm76e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm76e_device(mconfig, MM76E, tag, owner, clock, 10, address_map_constructor(FUNC(mm76e_device::program_1k), this), 6, address_map_constructor(FUNC(mm76e_device::data_48x4), this)) +{ } + +mm76e_device::mm76e_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) : + mm76_device(mconfig, type, tag, owner, clock, prgwidth, program, datawidth, data) +{ } + +mm76el_device::mm76el_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm76e_device(mconfig, MM76EL, tag, owner, clock, 10, address_map_constructor(FUNC(mm76el_device::program_1k), this), 6, address_map_constructor(FUNC(mm76el_device::data_48x4), this)) +{ } + + +// internal memory maps +void mm76_device::program_0_6k(address_map &map) +{ + map(0x000, 0x17f).mirror(0x200).rom(); + map(0x180, 0x1ff).rom(); + map(0x380, 0x3ff).rom(); +} + +void mm76e_device::program_1k(address_map &map) +{ + map(0x000, 0x3ff).rom(); +} + +void mm76_device::data_48x4(address_map &map) +{ + map(0x00, 0x2f).ram(); + map(0x30, 0x3f).lr8(NAME([]() { return 0xf; })).nopw(); +} + + +// machine config +void mm76_device::device_add_mconfig(machine_config &config) +{ + PLA(config, "opla", 5, 8, 17).set_format(pla_device::FMT::BERKELEY); +} + + +// disasm +std::unique_ptr<util::disasm_interface> mm76_device::create_disassembler() +{ + return std::make_unique<mm76_disassembler>(); +} + + +// initialize +void mm76_device::device_start() +{ + pps41_base_device::device_start(); +} + +void mm76_device::device_reset() +{ + pps41_base_device::device_reset(); +} + + +//------------------------------------------------- +// execute +//------------------------------------------------- + +void mm76_device::execute_one() +{ + if (op_is_tr(m_prev_op)) + { + // 2-byte opcodes + switch (m_op & 0xf0) + { + case 0x20: op_skbei(); break; + case 0x60: op_skaei(); break; + + case 0x80: case 0x90: case 0xa0: case 0xb0: op_tml(); break; + case 0xc0: case 0xd0: case 0xe0: case 0xf0: op_tl(); break; + + default: op_illegal(); break; + } + } + else + { + // standard opcodes + switch (m_op & 0xf0) + { + case 0x20: op_lb(); break; + case 0x30: op_tr(); break; + case 0x60: op_aisk(); break; + case 0x70: op_lai(); break; + + case 0x80: case 0x90: case 0xa0: case 0xb0: op_tm(); break; + case 0xc0: case 0xd0: case 0xe0: case 0xf0: op_t(); break; + + default: + switch (m_op & 0xfc) + { + case 0x08: op_skbf(); break; + case 0x10: op_sb(); break; + case 0x14: op_rb(); break; + case 0x1c: op_eob(); break; + case 0x50: op_l(); break; + case 0x54: op_xnsk(); break; + case 0x58: op_x(); break; + case 0x5c: op_xdsk(); break; + + default: + switch (m_op) + { + case 0x00: op_nop(); break; + case 0x01: op_sknc(); break; + case 0x02: op_rt(); break; + case 0x03: op_rtsk(); break; + case 0x04: op_int0l(); break; + case 0x05: op_int1h(); break; + case 0x06: op_din1(); break; + case 0x07: op_din0(); break; + case 0x0c: op_sc(); break; + case 0x0d: op_rc(); break; + case 0x0e: op_seg1(); break; + case 0x0f: op_seg2(); break; + + case 0x18: op_oa(); break; + case 0x19: op_ob(); break; + case 0x1a: op_iam(); break; + case 0x1b: op_ibm(); break; + + case 0x40: op_ac(); break; + case 0x41: op_acsk(); break; + case 0x42: op_a(); break; + case 0x43: op_ask(); break; + case 0x44: op_lba(); break; + case 0x45: op_com(); break; + case 0x46: op_xab(); break; + case 0x47: op_skmea(); break; + case 0x4a: op_i1(); break; + case 0x4b: op_i2c(); break; + case 0x4c: op_lsa(); break; + case 0x4d: op_ios(); break; + case 0x4e: op_xas(); break; + + default: op_illegal(); break; + } + break; // 0xff + + } + break; // 0xfc + + } // 0xf0 + } +} diff --git a/src/devices/cpu/pps41/mm76.h b/src/devices/cpu/pps41/mm76.h new file mode 100644 index 00000000000..b984ad9c713 --- /dev/null +++ b/src/devices/cpu/pps41/mm76.h @@ -0,0 +1,165 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM76 MCU + +*/ + +#ifndef MAME_CPU_PPS41_MM76_H +#define MAME_CPU_PPS41_MM76_H + +#pragma once + +#include "pps41base.h" + +// pinout reference + +/* + ____ ____ ____ ____ + A 1 |* \_/ | 42 BP BP 1 |* \_/ | 40 A + EXCLK 2 | | 41 DIO9 VC 2 | | 39 DIO9 + CLKIN 3 | | 40 DIO8 XTLIN 3 | | 38 DIO8 + VC 4 | | 39 N/C XTLOUT 4 | | 37 DIO7 + Vdd 5 | | 38 DIO7 Vdd 5 | | 36 DIO6 + Vss 6 | | 37 DIO6 PI2 6 | | 35 DIO5 + TEST 7 | | 36 DIO5 TEST 7 | | 34 DIO4 + PI2 8 | | 35 DIO4 PI6 8 | | 33 DIO3 + PI6 9 | | 34 DIO3 PI1 9 | | 32 DIO2 + PI1 10 | MM76 | 33 DIO2 PI5 10 | MM76L | 31 DIO1 + PI5 11 | MM76E | 32 DIO1 PI7 11 | MM76EL | 30 DIO0 + PI7 12 | | 31 DIO0 PI3 12 | | 29 CLOCK + PI3 13 | | 30 CLOCK PI8 13 | | 28 DATAO + PI8 14 | | 29 DATAO PI4 14 | | 27 DATAI + PI4 15 | | 28 DATAI PO 15 | | 26 RIO4 + Vdd 16 | | 27 RIO4 INT0 16 | | 25 RIO3 + PO 17 | | 26 RIO3 INT1 17 | | 24 RIO2 + INT0 18 | | 25 RIO2 RIO5 18 | | 23 RIO1 + INT1 19 | | 24 RIO1 RIO6 19 | | 22 RIO8 + RIO5 20 | | 23 RIO8 Vss 20 |___________| 21 RIO7 + RIO6 21 |___________| 22 RIO7 + +*/ + +class mm76_device : public pps41_base_device +{ +public: + mm76_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + mm76_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); + + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + // device_disasm_interface overrides + virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; + + // device_execute_interface overrides + virtual void execute_one() override; + + void data_48x4(address_map &map) ATTR_COLD; + void program_0_6k(address_map &map) ATTR_COLD; + + // opcode helpers + u8 ram_r(); + void ram_w(u8 data); + void pop_pc(); + void push_pc(); + void op_illegal(); + void op_todo(); + + virtual bool op_is_tr(u8 op) override { return (op & 0xf0) == 0x30; } + virtual bool op_is_eob(u8 op) { return (op & 0xfc) == 0x1c; } + virtual bool op_is_lb(u8 op) { return (op & 0xf0) == 0x20; } + virtual bool op_is_lai(u8 op) { return (op & 0xf0) == 0x70; } + + // opcode handlers + virtual void op_xab(); + virtual void op_lba(); + virtual void op_lb(); + virtual void op_eob(); + + virtual void op_sb(); + virtual void op_rb(); + virtual void op_skbf(); + + virtual void op_xas(); + virtual void op_lsa(); + + virtual void op_l(); + virtual void op_x(); + virtual void op_xdsk(); + virtual void op_xnsk(); + + virtual void op_a(); + virtual void op_ac(); + virtual void op_acsk(); + virtual void op_ask(); + virtual void op_com(); + virtual void op_rc(); + virtual void op_sc(); + virtual void op_sknc(); + virtual void op_lai(); + virtual void op_aisk(); + + virtual void op_rt(); + virtual void op_rtsk(); + virtual void op_t(); + virtual void op_tl(); + virtual void op_tm(); + virtual void op_tml(); + virtual void op_tr(); + virtual void op_nop(); + + virtual void op_skmea(); + virtual void op_skbei(); + virtual void op_skaei(); + + virtual void op_ibm(); + virtual void op_ob(); + virtual void op_iam(); + virtual void op_oa(); + virtual void op_ios(); + virtual void op_i1(); + virtual void op_i2c(); + virtual void op_int1h(); + virtual void op_din1(); + virtual void op_int0l(); + virtual void op_din0(); + virtual void op_seg1(); + virtual void op_seg2(); +}; + +class mm76l_device : public mm76_device +{ +public: + mm76l_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +class mm76e_device : public mm76_device +{ +public: + mm76e_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + mm76e_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); + + void program_1k(address_map &map) ATTR_COLD; +}; + +class mm76el_device : public mm76e_device +{ +public: + mm76el_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + + +DECLARE_DEVICE_TYPE(MM76, mm76_device) +DECLARE_DEVICE_TYPE(MM76L, mm76l_device) +DECLARE_DEVICE_TYPE(MM76E, mm76e_device) +DECLARE_DEVICE_TYPE(MM76EL, mm76el_device) + +#endif // MAME_CPU_PPS41_MM76_H diff --git a/src/devices/cpu/pps41/mm76op.cpp b/src/devices/cpu/pps41/mm76op.cpp new file mode 100644 index 00000000000..6cef3dae06d --- /dev/null +++ b/src/devices/cpu/pps41/mm76op.cpp @@ -0,0 +1,473 @@ +// license:BSD-3-Clause +// copyright-holders:hap + +// MM76/shared opcode handlers + +#include "emu.h" +#include "mm76.h" + + +// internal helpers + +u8 mm76_device::ram_r() +{ + return m_data->read_byte(m_ram_addr & m_datamask) & 0xf; +} + +void mm76_device::ram_w(u8 data) +{ + m_data->write_byte(m_ram_addr & m_datamask, data & 0xf); +} + +void mm76_device::pop_pc() +{ + m_pc = m_stack[0] & m_prgmask; + for (int i = 0; i < m_stack_levels-1; i++) + m_stack[i] = m_stack[i+1]; +} + +void mm76_device::push_pc() +{ + for (int i = m_stack_levels-1; i >= 1; i--) + m_stack[i] = m_stack[i-1]; + m_stack[0] = m_pc; +} + +void mm76_device::op_illegal() +{ + logerror("unknown opcode $%02X at $%03X\n", m_op, m_prev_pc); +} + +void mm76_device::op_todo() +{ + logerror("unimplemented opcode $%02X at $%03X\n", m_op, m_prev_pc); +} + + +// opcodes + +// RAM addressing instructions + +void mm76_device::op_xab() +{ + // XAB: exchange A with Bl + u8 a = m_a; + m_a = m_b & 0xf; + m_b = (m_b & ~0xf) | a; + m_ram_delay = true; +} + +void mm76_device::op_lba() +{ + // LBA: load Bl from A + m_b = (m_b & ~0xf) | m_a; + m_ram_delay = true; +} + +void mm76_device::op_lb() +{ + // LB x: load B from x (successive LB/EOB are ignored) + if (!(op_is_lb(m_prev_op) && !op_is_tr(m_prev2_op)) && !op_is_eob(m_prev_op)) + m_b = m_op & 0xf; +} + +void mm76_device::op_eob() +{ + // EOB x: XOR Bu with x (successive LB/EOB are ignored, except after first executed LB) + bool first_lb = (op_is_lb(m_prev_op) && !op_is_tr(m_prev2_op)) && !(op_is_lb(m_prev2_op) && !op_is_tr(m_prev3_op)) && !op_is_eob(m_prev2_op); + if ((!(op_is_lb(m_prev_op) && !op_is_tr(m_prev2_op)) && !op_is_eob(m_prev_op)) || first_lb) + m_b ^= m_op << 4 & m_datamask; +} + + +// bit manipulation instructions + +void mm76_device::op_sb() +{ + // SB x: set memory bit / SOS: set output + + // Bu rising: opcode is invalid + if ((m_prev2_b & 0x30) != 0x30 && (m_prev_b & 0x30) == 0x30) + { + logerror("SB/SOS invalid access at $%03X\n", m_prev_pc); + return; + } + + // Bu falling or Bu == 3: SOS + if (((m_prev2_b & 0x30) == 0x30 && (m_prev_b & 0x30) != 0x30) || (m_prev_b & 0x30) == 0x30) + { + u8 bl = m_ram_addr & 0xf; + if (bl > m_d_pins) + logerror("SOS invalid pin %d at $%03X\n", bl, m_prev_pc); + else + { + m_d_output = (m_d_output | (1 << bl)) & m_d_mask; + m_write_d(m_d_output); + } + } + + // Bu != 3: SB + if ((m_prev_b & 0x30) != 0x30) + ram_w(ram_r() | (1 << (m_op & 3))); +} + +void mm76_device::op_rb() +{ + // RB x: reset memory bit / ROS: reset output + + // Bu rising: opcode is invalid + if ((m_prev2_b & 0x30) != 0x30 && (m_prev_b & 0x30) == 0x30) + { + logerror("RB/ROS invalid access at $%03X\n", m_prev_pc); + return; + } + + // Bu falling or Bu == 3: ROS + if (((m_prev2_b & 0x30) == 0x30 && (m_prev_b & 0x30) != 0x30) || (m_prev_b & 0x30) == 0x30) + { + u8 bl = m_ram_addr & 0xf; + if (bl > m_d_pins) + logerror("ROS invalid pin %d at $%03X\n", bl, m_prev_pc); + else + { + m_d_output = m_d_output & ~(1 << bl); + m_write_d(m_d_output); + } + } + + // Bu != 3: RB + if ((m_prev_b & 0x30) != 0x30) + ram_w(ram_r() & ~(1 << (m_op & 3))); +} + +void mm76_device::op_skbf() +{ + // SKBF x: test memory bit / SKISL: test input + + // Bu rising: opcode is invalid + if ((m_prev2_b & 0x30) != 0x30 && (m_prev_b & 0x30) == 0x30) + { + logerror("SKBF/SKISL invalid access at $%03X\n", m_prev_pc); + return; + } + + // Bu falling or Bu == 3: SKISL + if (((m_prev2_b & 0x30) == 0x30 && (m_prev_b & 0x30) != 0x30) || (m_prev_b & 0x30) == 0x30) + { + u8 bl = m_ram_addr & 0xf; + if (bl > m_d_pins) + logerror("SKISL invalid pin %d at $%03X\n", bl, m_prev_pc); + else + m_skip = !BIT((m_d_output | m_read_d()) & m_d_mask, bl); + } + + // Bu != 3: SKBF + if ((m_prev_b & 0x30) != 0x30) + m_skip = m_skip || !BIT(ram_r(), m_op & 3); +} + + +// register to register instructions + +void mm76_device::op_xas() +{ + // XAS: exchange A with S + u8 a = m_a; + m_a = m_s; + m_s = a; + m_write_sdo(BIT(m_s, 3)); +} + +void mm76_device::op_lsa() +{ + // LSA: load S from A + m_s = m_a; + m_write_sdo(BIT(m_s, 3)); +} + + +// register memory instructions + +void mm76_device::op_l() +{ + // L x: load A from memory, XOR Bu with x + m_a = ram_r(); + m_b ^= m_op << 4 & 0x30; +} + +void mm76_device::op_x() +{ + // X x: exchange A with memory, XOR Bu with x + u8 a = m_a; + m_a = ram_r(); + ram_w(a); + m_b ^= m_op << 4 & 0x30; +} + +void mm76_device::op_xdsk() +{ + // XDSK x: X x + decrement Bl + op_x(); + m_b = (m_b & ~0xf) | ((m_b - 1) & 0xf); + m_skip = (m_b & 0xf) == 0xf; + m_ram_delay = true; +} + +void mm76_device::op_xnsk() +{ + // XNSK x: X x + increment Bl + op_x(); + m_b = (m_b & ~0xf) | ((m_b + 1) & 0xf); + m_skip = (m_b & 0xf) == 0; + m_ram_delay = true; +} + + +// arithmetic instructions + +void mm76_device::op_a() +{ + // A: add memory to A + m_a = (m_a + ram_r()) & 0xf; +} + +void mm76_device::op_ac() +{ + // AC: add memory and carry to A + m_a += ram_r() + m_c_in; + m_c = m_a >> 4 & 1; + m_a &= 0xf; + m_c_delay = true; +} + +void mm76_device::op_acsk() +{ + // ACSK: AC + skip on no overflow + op_ac(); + m_skip = !m_c; +} + +void mm76_device::op_ask() +{ + // ASK: A + skip on no overflow + u8 a = m_a; + op_a(); + m_skip = m_a >= a; +} + +void mm76_device::op_com() +{ + // COM: complement A + m_a ^= 0xf; +} + +void mm76_device::op_rc() +{ + // RC: reset carry + m_c = 0; +} + +void mm76_device::op_sc() +{ + // SC: set carry + m_c = 1; +} + +void mm76_device::op_sknc() +{ + // SKNC: skip on no carry + m_skip = !m_c_in; +} + +void mm76_device::op_lai() +{ + // LAI x: load A from x (successive LAI are ignored) + if (!(op_is_lai(m_prev_op) && !op_is_tr(m_prev2_op))) + m_a = m_op & 0xf; +} + +void mm76_device::op_aisk() +{ + // AISK x: add x to A, skip on no overflow + m_a += m_op & 0xf; + m_skip = !(m_a & 0x10); + m_a &= 0xf; +} + + +// ROM addressing instructions + +void mm76_device::op_rt() +{ + // RT: return from subroutine + cycle(); + pop_pc(); +} + +void mm76_device::op_rtsk() +{ + // RTSK: RT + skip next instruction + op_rt(); + m_skip = true; +} + +void mm76_device::op_t() +{ + // T x: transfer on-page + cycle(); + + // jumps from subroutine pages reset page to SR1 + u16 mask = m_prgmask & ~0x7f; + if ((m_pc & mask) == mask) + m_pc &= ~0x40; + + m_pc = (m_pc & ~0x3f) | (~m_op & 0x3f); +} + +void mm76_device::op_tl() +{ + // TL x: transfer long off-page + cycle(); + m_pc = (~m_prev_op & 0xf) << 6 | (~m_op & 0x3f); +} + +void mm76_device::op_tm() +{ + // TM x: transfer and mark to SR0 + cycle(); + + // calls from subroutine pages don't push PC + u16 mask = m_prgmask & ~0x7f; + if ((m_pc & mask) != mask) + push_pc(); + + m_pc = ((m_prgmask & ~0x3f) | (~m_op & 0x3f)); +} + +void mm76_device::op_tml() +{ + // TML x: transfer and mark long + cycle(); + push_pc(); + m_pc = (~m_prev_op & 0xf) << 6 | (~m_op & 0x3f); +} + +void mm76_device::op_tr() +{ + // TR x: prefix for extended opcode +} + +void mm76_device::op_nop() +{ + // NOP: no operation +} + + +// logical comparison instructions + +void mm76_device::op_skmea() +{ + // SKMEA: skip on memory equals A + m_skip = m_a == ram_r(); +} + +void mm76_device::op_skbei() +{ + // SKBEI x: skip on Bl equals x + m_skip = (m_b & 0xf) == (m_op & 0xf); +} + +void mm76_device::op_skaei() +{ + // SKAEI x: skip on A equals X + m_skip = m_a == (~m_op & 0xf); +} + + +// input/output instructions + +void mm76_device::op_ibm() +{ + // IBM: input channel B to A + m_a &= (m_read_r() & m_r_output) >> 4; +} + +void mm76_device::op_ob() +{ + // OB: output from A to channel B + m_r_output = (m_r_output & 0xf) | m_a << 4; + m_write_r(m_r_output); +} + +void mm76_device::op_iam() +{ + // IAM: input channel A to A + m_a &= m_read_r() & m_r_output; +} + +void mm76_device::op_oa() +{ + // OA: output from A to channel A + m_r_output = (m_r_output & ~0xf) | m_a; + m_write_r(m_r_output); +} + +void mm76_device::op_ios() +{ + // IOS: start serial I/O (2 cycles per shift, 1st shift at IOS+3) + m_sclock_count = 9; +} + +void mm76_device::op_i1() +{ + // I1: input channel 1 to A + m_a = m_read_p() & 0xf; +} + +void mm76_device::op_i2c() +{ + // I2C: input channel 2 to A + m_a = ~m_read_p() >> 4 & 0xf; +} + +void mm76_device::op_int1h() +{ + // INT1H: skip on INT1 high + m_skip = bool(m_int_line[1]); +} + +void mm76_device::op_din1() +{ + // DIN1: test INT1 flip-flop + m_skip = !m_int_ff[1]; + m_int_ff[1] = 1; +} + +void mm76_device::op_int0l() +{ + // INT0L: skip on INT0 low + m_skip = !m_int_line[0]; +} + +void mm76_device::op_din0() +{ + // DIN0: test INT0 flip-flop + m_skip = !m_int_ff[0]; + m_int_ff[0] = 1; +} + +void mm76_device::op_seg1() +{ + // SEG1: output A+carry through PLA to channel A + u8 out = bitswap<8>(m_opla->read((m_c_in << 4 | (ram_r() & ~m_a)) ^ 0x1f), 7,5,3,1,0,2,4,6); + m_r_output = (m_r_output & ~0xf) | (out & 0xf); + m_write_r(m_r_output); +} + +void mm76_device::op_seg2() +{ + // SEG2: output A+carry through PLA to channel B + u8 out = bitswap<8>(m_opla->read((m_c_in << 4 | (ram_r() & ~m_a)) ^ 0x1f), 7,5,3,1,0,2,4,6); + m_r_output = (m_r_output & 0xf) | (out & 0xf0); + m_write_r(m_r_output); +} diff --git a/src/devices/cpu/pps41/mm78.cpp b/src/devices/cpu/pps41/mm78.cpp new file mode 100644 index 00000000000..dd875551fc8 --- /dev/null +++ b/src/devices/cpu/pps41/mm78.cpp @@ -0,0 +1,217 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM77/MM78 MCU + +*/ + +#include "emu.h" +#include "mm78.h" + +#include "pps41d.h" + + +DEFINE_DEVICE_TYPE(MM78, mm78_device, "mm78", "Rockwell MM78 A7800") // 2KB ROM, 128 nibbles RAM +DEFINE_DEVICE_TYPE(MM78L, mm78l_device, "mm78l", "Rockwell MM78L B7800") // low-power +DEFINE_DEVICE_TYPE(MM77, mm77_device, "mm77", "Rockwell MM77 A7700") // 1.3KB ROM, 96 nibbles RAM +DEFINE_DEVICE_TYPE(MM77L, mm77l_device, "mm77l", "Rockwell MM77L B7700") // 1.5KB ROM, low-power + + +// constructor +mm78_device::mm78_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm78_device(mconfig, MM78, tag, owner, clock, 11, address_map_constructor(FUNC(mm78_device::program_2k), this), 7, address_map_constructor(FUNC(mm78_device::data_128x4), this)) +{ } + +mm78_device::mm78_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) : + mm76_device(mconfig, type, tag, owner, clock, prgwidth, program, datawidth, data) +{ } + +mm78l_device::mm78l_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm78_device(mconfig, MM78L, tag, owner, clock, 11, address_map_constructor(FUNC(mm78l_device::program_2k), this), 7, address_map_constructor(FUNC(mm78l_device::data_128x4), this)) +{ } + +mm77_device::mm77_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm77_device(mconfig, MM77, tag, owner, clock, 11, address_map_constructor(FUNC(mm77_device::program_1_3k), this), 7, address_map_constructor(FUNC(mm77_device::data_96x4), this)) +{ } + +mm77_device::mm77_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) : + mm78_device(mconfig, type, tag, owner, clock, prgwidth, program, datawidth, data) +{ } + +mm77l_device::mm77l_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm77_device(mconfig, MM77L, tag, owner, clock, 11, address_map_constructor(FUNC(mm77l_device::program_1_5k), this), 7, address_map_constructor(FUNC(mm77l_device::data_96x4), this)) +{ } + + +// internal memory maps +void mm78_device::program_2k(address_map &map) +{ + map(0x000, 0x7ff).rom(); +} + +void mm78_device::program_1_3k(address_map &map) +{ + map(0x040, 0x1ff).rom(); + map(0x240, 0x3ff).rom(); + map(0x640, 0x7ff).rom(); +} + +void mm78_device::program_1_5k(address_map &map) +{ + map(0x000, 0x3ff).rom(); + map(0x400, 0x5ff).mirror(0x200).rom(); +} + +void mm78_device::data_128x4(address_map &map) +{ + map(0x00, 0x7f).ram(); +} + +void mm78_device::data_96x4(address_map &map) +{ + map(0x00, 0x3f).ram(); + map(0x40, 0x47).mirror(0x18).ram(); // not to 0x50 + map(0x50, 0x57).ram(); + map(0x60, 0x67).mirror(0x18).ram(); // not to 0x70 + map(0x70, 0x77).ram(); +} + + +// disasm +std::unique_ptr<util::disasm_interface> mm78_device::create_disassembler() +{ + return std::make_unique<mm78_disassembler>(); +} + + +// initialize +void mm78_device::device_start() +{ + mm76_device::device_start(); + m_stack_levels = 2; + + state_add(++m_state_count, "X", m_x).formatstr("%01X"); // 6 +} + +void mm78_device::device_reset() +{ + mm76_device::device_reset(); +} + + +//------------------------------------------------- +// execute +//------------------------------------------------- + +void mm78_device::execute_one() +{ + if (op_is_tr(m_prev_op) && op_is_tr(m_prev2_op)) + { + // 3-byte opcodes + switch (m_op & 0xf0) + { + case 0x80: case 0x90: case 0xa0: case 0xb0: op_tmlb(); break; + case 0xc0: case 0xd0: case 0xe0: case 0xf0: op_tlb(); break; + + default: op_illegal(); break; + } + } + else if (op_is_tr(m_prev_op)) + { + // 2-byte opcodes + switch (m_op & 0xf0) + { + case 0x30: op_tr(); break; + case 0x40: op_skbei(); break; + case 0x60: + if (m_op != 0x60) + op_skaei(); + else + op_illegal(); + break; + + case 0x80: case 0x90: case 0xa0: case 0xb0: op_tml(); break; + case 0xc0: case 0xd0: case 0xe0: case 0xf0: op_tl(); break; + + default: op_illegal(); break; + } + } + else + { + // standard opcodes + switch (m_op & 0xf0) + { + case 0x10: op_lb(); break; + case 0x30: op_tr(); break; + case 0x40: op_lai(); break; + case 0x60: + if (m_op != 0x60) + op_aisk(); + else + op_i1sk(); + break; + + case 0x80: case 0x90: case 0xa0: case 0xb0: op_tm(); break; + case 0xc0: case 0xd0: case 0xe0: case 0xf0: op_t(); break; + + default: + switch (m_op & 0xfc) + { + case 0x08: case 0x0c: op_eob(); break; + case 0x20: op_sb(); break; + case 0x24: op_rb(); break; + case 0x28: op_skbf(); break; + case 0x50: op_l(); break; + case 0x54: op_xnsk(); break; + case 0x58: op_xdsk(); break; + case 0x5c: op_x(); break; + + default: + switch (m_op) + { + case 0x00: op_nop(); break; + case 0x01: op_skisl(); break; + case 0x02: op_sknc(); break; + case 0x03: op_int0h(); break; + case 0x04: op_int1l(); break; + case 0x05: op_rc(); break; + case 0x06: op_sc(); break; + case 0x07: op_sag(); break; + + case 0x2c: break; // TAB + case 0x2d: op_ios(); break; + case 0x2e: op_rtsk(); break; + case 0x2f: op_rt(); break; + + case 0x70: op_sos(); break; + case 0x71: op_ros(); break; + case 0x72: op_ix(); break; + case 0x73: op_ox(); break; + case 0x74: op_xas(); break; + case 0x75: op_lxa(); break; + case 0x76: op_lba(); break; + case 0x77: op_com(); break; + case 0x78: op_i2c(); break; + case 0x79: op_xax(); break; + case 0x7a: op_xab(); break; + case 0x7b: op_ioa(); break; + case 0x7c: op_ac(); break; + case 0x7d: op_acsk(); break; + case 0x7e: op_a(); break; + case 0x7f: op_skmea(); break; + + default: op_illegal(); break; // won't happen + } + break; // 0xff + + } + break; // 0xfc + + } // 0xf0 + } + + // TAB is delayed by 1 opcode (including a single TR) + if (m_prev_op == 0x2c) + op_tab(); +} diff --git a/src/devices/cpu/pps41/mm78.h b/src/devices/cpu/pps41/mm78.h new file mode 100644 index 00000000000..7676cd30226 --- /dev/null +++ b/src/devices/cpu/pps41/mm78.h @@ -0,0 +1,128 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM77/MM78 MCU + +*/ + +#ifndef MAME_CPU_PPS41_MM78_H +#define MAME_CPU_PPS41_MM78_H + +#pragma once + +#include "mm76.h" + +// pinout reference + +/* + ____ ____ ____ ____ + BP 1 |* \_/ | 42 DIO9 BP 1 |* \_/ | 40 DIO9 + A 2 | | 41 DIO8 A 2 | | 39 DIO8 + CLKIN 3 | | 40 DIO7 XTLOUT 3 | | 38 DIO7 + EXCLK 4 | | 39 DIO6 XTLIN 4 | | 37 DIO6 + VC 5 | | 38 DIO5 VC 5 | | 36 DIO5 + Vdd 6 | | 37 DIO4 Vdd 6 | | 35 DIO4 + Vss 7 | | 36 DIO3 Vss 7 | | 34 DIO3 + N/C 8 | | 35 DIO2 TEST 8 | | 33 DIO2 + TEST 9 | | 34 DIO1 PI4 9 | | 32 DIO1 + PI4 10 | MM77 | 33 DIO0 PI8 10 | MM77L | 31 DIO0 + PI8 11 | MM78 | 32 INT1 PI3 11 | MM78L | 30 INT1 + PI3 12 | | 31 INT0 PI7 12 | | 29 INT0 + PI7 13 | | 30 DATAI PI6 13 | | 28 DATAI + PI6 14 | | 29 DATAO PI2 14 | | 27 DATAO + PI2 15 | | 28 CLOCK PI5 15 | | 26 CLOCK + PI5 16 | | 27 RIO4 PI1 16 | | 25 RIO4 + Vdd 17 | | 26 RIO3 PO 17 | | 24 RIO3 + PI1 18 | | 25 RIO2 RIO5 18 | | 23 RIO2 + PO 19 | | 24 RIO1 RIO6 19 | | 22 RIO1 + RIO5 20 | | 23 RIO8 RIO7 20 |___________| 21 RIO8 + RIO6 21 |___________| 22 RIO7 + + MM78 also exists as 40-pin DIP and can have the same pinout as MM78L +*/ + +class mm78_device : public mm76_device +{ +public: + mm78_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + mm78_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); + + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override { ; } + + // device_disasm_interface overrides + virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; + + // device_execute_interface overrides + virtual void execute_one() override; + + void data_96x4(address_map &map) ATTR_COLD; + void data_128x4(address_map &map) ATTR_COLD; + void program_1_3k(address_map &map) ATTR_COLD; + void program_1_5k(address_map &map) ATTR_COLD; + void program_2k(address_map &map) ATTR_COLD; + + // opcode helpers + virtual bool op_is_eob(u8 op) override { return (op & 0xf8) == 0x08; } + virtual bool op_is_lb(u8 op) override { return (op & 0xf0) == 0x10; } + virtual bool op_is_lai(u8 op) override { return (op & 0xf0) == 0x40; } + + // opcode handlers + virtual void op_lba() override; + virtual void op_acsk() override; + virtual void op_aisk() override; + virtual void op_sb() override; + virtual void op_rb() override; + virtual void op_skbf() override; + + virtual void op_sos(); + virtual void op_ros(); + virtual void op_skisl(); + + virtual void op_sag(); + virtual void op_lxa(); + virtual void op_xax(); + virtual void op_tlb(); + virtual void op_tmlb(); + virtual void op_tab(); + virtual void op_ix(); + virtual void op_ox(); + virtual void op_ioa(); + virtual void op_i1sk(); + virtual void op_int0h(); + virtual void op_int1l(); +}; + +class mm78l_device : public mm78_device +{ +public: + mm78l_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + +class mm77_device : public mm78_device +{ +public: + mm77_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + mm77_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); +}; + +class mm77l_device : public mm77_device +{ +public: + mm77l_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); +}; + + +DECLARE_DEVICE_TYPE(MM78, mm78_device) +DECLARE_DEVICE_TYPE(MM78L, mm78l_device) +DECLARE_DEVICE_TYPE(MM77, mm77_device) +DECLARE_DEVICE_TYPE(MM77L, mm77l_device) + +#endif // MAME_CPU_PPS41_MM78_H diff --git a/src/devices/cpu/pps41/mm78la.cpp b/src/devices/cpu/pps41/mm78la.cpp new file mode 100644 index 00000000000..0e03e77935d --- /dev/null +++ b/src/devices/cpu/pps41/mm78la.cpp @@ -0,0 +1,113 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM77LA/MM78LA MCU + +*/ + +#include "emu.h" +#include "mm78la.h" + + +DEFINE_DEVICE_TYPE(MM78LA, mm78la_device, "mm78la", "Rockwell MM78LA B9000") // MM78L + output PLA and tone generator, no serial i/o +DEFINE_DEVICE_TYPE(MM77LA, mm77la_device, "mm77la", "Rockwell MM77LA B8000") // MM77L + " + + +// constructor +mm78la_device::mm78la_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm78la_device(mconfig, MM78LA, tag, owner, clock, 11, address_map_constructor(FUNC(mm78la_device::program_2k), this), 7, address_map_constructor(FUNC(mm78la_device::data_128x4), this)) +{ } + +mm78la_device::mm78la_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) : + mm78_device(mconfig, type, tag, owner, clock, prgwidth, program, datawidth, data) +{ } + +mm77la_device::mm77la_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + mm78la_device(mconfig, MM77LA, tag, owner, clock, 11, address_map_constructor(FUNC(mm77la_device::program_1_5k), this), 7, address_map_constructor(FUNC(mm77la_device::data_96x4), this)) +{ } + + +// machine config +void mm78la_device::device_add_mconfig(machine_config &config) +{ + PLA(config, "opla", 4, 2*14, 16).set_format(pla_device::FMT::BERKELEY); +} + +void mm77la_device::device_add_mconfig(machine_config &config) +{ + PLA(config, "opla", 4, 10, 16).set_format(pla_device::FMT::BERKELEY); +} + + +// initialize +void mm78la_device::device_start() +{ + mm78_device::device_start(); + + set_d_pins(12); + set_r_pins(14); + + // zerofill + m_tone_on = 0; + m_tone_freq = 0; + m_tone_count = 1; + m_spk_output = 2; + m_ios_state = 0; + + // register for savestates + save_item(NAME(m_tone_on)); + save_item(NAME(m_tone_freq)); + save_item(NAME(m_tone_count)); + save_item(NAME(m_spk_output)); + save_item(NAME(m_ios_state)); +} + +void mm78la_device::device_reset() +{ + m_r_mask = 0; + mm78_device::device_reset(); + + // reset speaker + m_ios_state = 0; + m_tone_on = false; + m_write_spk(m_spk_output); +} + +void mm77la_device::device_start() +{ + mm78la_device::device_start(); + set_r_pins(10); +} + +void mm77la_device::device_reset() +{ + mm78la_device::device_reset(); +} + + +// speaker +void mm78la_device::cycle() +{ + mm78_device::cycle(); + + m_tone_count++; + + // toggle when counter matches + if (m_tone_on && m_tone_count == m_tone_freq) + { + toggle_speaker(); + reset_tone_count(); + } +} + +void mm78la_device::reset_tone_count() +{ + m_tone_count = 1; +} + +void mm78la_device::toggle_speaker() +{ + m_spk_output ^= 3; + m_write_spk(m_spk_output); +} diff --git a/src/devices/cpu/pps41/mm78la.h b/src/devices/cpu/pps41/mm78la.h new file mode 100644 index 00000000000..cdbcb1ac275 --- /dev/null +++ b/src/devices/cpu/pps41/mm78la.h @@ -0,0 +1,98 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell MM77LA/MM78LA MCU + +*/ + +#ifndef MAME_CPU_PPS41_MM78LA_H +#define MAME_CPU_PPS41_MM78LA_H + +#pragma once + +#include "mm78.h" + +// pinout reference + +/* + ____ ____ ____ ____ + BP 1 |* \_/ | 42 DIO9 DIO9 1 |* \_/ | 40 DIO8 + A 2 | | 41 DIO8 BP 2 | | 39 DIO7 + Vdd 3 | | 40 DIO7 A 3 | | 38 DIO6 + VC 4 | | 39 DIO6 VC 4 | | 37 DIO5 + TEST 5 | | 38 DIO5 Vdd 5 | | 36 DIO4 + Vss 6 | | 37 DIO4 Vss 6 | | 35 DIO3 + PI4 7 | | 36 DIO3 TEST 7 | | 34 DIO2 + PI8 8 | | 35 DIO2 PI4 8 | | 33 DIO1 + PI3 9 | | 34 DIO1 PI8 9 | | 32 DIO0 + PI7 10 | MM95 | 33 DIO0 PI3 10 | B8000 | 31 DIO10 + PI6 11 | MM78LA | 32 Vdd SPK PI7 11 | | 30 DIO11 + PI2 12 | | 31 SPK R2 PI6 12 | | 29 SPK R2 + PI5 13 | | 30 SPK R1 PI2 13 | | 28 Vdd SPK + PI1 14 | | 29 RO01 PI5 14 | | 27 SPK R1 + PO 15 | | 28 RO02 PI1 15 | | 26 RO01 + RO14 16 | | 27 RO03 PO 16 | | 25 RO02 + RO13 17 | | 26 RO04 RO10 17 | | 24 RO03 + RO12 18 | | 25 RO05 RO09 18 | | 23 RO04 + RO11 19 | | 24 RO06 RO08 19 | | 22 RO05 + RO10 20 | | 23 RO07 RO07 20 |___________| 21 RO06 + RO09 21 |___________| 22 RO08 + + MM78LA = aka MM95, speaker pins can be used for 2 more DIO when speaker is not used. + For B8000 (I call it MM77LA), no documentation is known, pinout might not be accurate. +*/ + +class mm78la_device : public mm78_device +{ +public: + mm78la_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + mm78la_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); + + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + // opcode handlers + virtual void op_ioa() override; + virtual void op_ox() override; + virtual void op_ix() override; + virtual void op_ios() override; + virtual void op_int0h() override; + virtual void op_int1l() override; + + // speaker + bool m_tone_on; + u8 m_tone_freq; + u8 m_tone_count; + u8 m_spk_output; + u8 m_ios_state; + + virtual void cycle() override; + void reset_tone_count(); + void toggle_speaker(); +}; + +class mm77la_device : public mm78la_device +{ +public: + mm77la_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + +protected: + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + // opcode handlers + virtual void op_ix() override; +}; + + +DECLARE_DEVICE_TYPE(MM78LA, mm78la_device) +DECLARE_DEVICE_TYPE(MM77LA, mm77la_device) + +#endif // MAME_CPU_PPS41_MM78LA_H diff --git a/src/devices/cpu/pps41/mm78laop.cpp b/src/devices/cpu/pps41/mm78laop.cpp new file mode 100644 index 00000000000..88b59a12e0c --- /dev/null +++ b/src/devices/cpu/pps41/mm78laop.cpp @@ -0,0 +1,73 @@ +// license:BSD-3-Clause +// copyright-holders:hap + +// MM77LA/MM78LA opcode handlers + +#include "emu.h" +#include "mm78la.h" + + +// changed opcodes + +void mm78la_device::op_ioa() +{ + // IOA: output A+carry to lower half of RO pins + u16 mask = (1 << (m_r_pins / 2)) - 1; + m_r_output = (m_r_output & ~mask) | (m_c_in << 4 | m_a); + m_write_r(m_r_output); +} + +void mm78la_device::op_ox() +{ + // OX: output A+carry to upper half of RO pins + u16 mask = (1 << (m_r_pins / 2)) - 1; + m_r_output = (m_r_output & mask) | (m_c_in << 4 | m_a) << (m_r_pins / 2); + m_write_r(m_r_output); +} + +void mm78la_device::op_ix() +{ + // IX: output A+carry to RO pins through PLA (MM78LA) + u32 out = ~m_opla->read(m_a) & 0x0fff'ffff; + out = bitswap<28>(out, 26,22,18,14,10,6,2,1,5,9,13,17,21,25, 27,23,19,15,11,7,3,0,4,8,12,16,20,24); + m_r_output = out >> (m_c_in ? 0 : 14) & 0x3fff; + m_write_r(m_r_output); +} + +void mm77la_device::op_ix() +{ + // IX: output A to RO pins through PLA (MM77LA) + u16 out = ~m_opla->read(m_a) & 0x3ff; + m_r_output = bitswap<10>(out, 9,7,5,3,1,0,2,4,6,8); + m_write_r(m_r_output); +} + +void mm78la_device::op_ios() +{ + // IOS: set tone frequency + m_tone_freq = m_tone_freq >> 4 | (m_a << 4); + + // state 1->2: turn on + if (m_ios_state == 1) + { + m_tone_on = true; + reset_tone_count(); + } + else + m_tone_on = false; + + // increment state machine + m_ios_state = (m_ios_state + 1) % 3; +} + +void mm78la_device::op_int0h() +{ + // INT0H: toggle speaker + toggle_speaker(); +} + +void mm78la_device::op_int1l() +{ + // INT1L: ? + op_todo(); +} diff --git a/src/devices/cpu/pps41/mm78op.cpp b/src/devices/cpu/pps41/mm78op.cpp new file mode 100644 index 00000000000..ddf7221a682 --- /dev/null +++ b/src/devices/cpu/pps41/mm78op.cpp @@ -0,0 +1,202 @@ +// license:BSD-3-Clause +// copyright-holders:hap + +// MM77/MM78 opcode handlers + +#include "emu.h" +#include "mm78.h" + + +// changed opcodes + +void mm78_device::op_lba() +{ + // LBA: no RAM delay + mm76_device::op_lba(); + m_ram_delay = false; +} + +void mm78_device::op_acsk() +{ + // ACSK: skip logic is inverted + mm76_device::op_acsk(); + m_skip = !m_skip; +} + +void mm78_device::op_aisk() +{ + // AISK x: don't skip if x=6 (aka DC opcode) + mm76_device::op_aisk(); + if ((m_op & 0xf) == 6) + m_skip = false; +} + +void mm78_device::op_sb() +{ + // SB x: SB/SOS opcodes are separated + ram_w(ram_r() | (1 << (m_op & 3))); +} + +void mm78_device::op_rb() +{ + // RB x: RB/ROS opcodes are separated + ram_w(ram_r() & ~(1 << (m_op & 3))); +} + +void mm78_device::op_skbf() +{ + // SKBF x: SKBF/SKISL opcodes are separated + m_skip = !BIT(ram_r(), m_op & 3); +} + +void mm78_device::op_sos() +{ + // SOS: SB/SOS opcodes are separated + + // B7 must be low + if (m_ram_addr & 0x40) + { + logerror("SOS invalid access at $%03X\n", m_prev_pc); + return; + } + + u8 bl = m_ram_addr & 0xf; + if (bl < m_d_pins) + { + m_d_output = (m_d_output | (1 << bl)) & m_d_mask; + m_write_d(m_d_output); + } + else if (bl < 12) + m_int_ff[~bl & 1] = 1; + else + logerror("SOS invalid pin %d at $%03X\n", bl, m_prev_pc); +} + +void mm78_device::op_ros() +{ + // ROS: RB/ROS opcodes are separated + + // B7 must be low + if (m_ram_addr & 0x40) + { + logerror("ROS invalid access at $%03X\n", m_prev_pc); + return; + } + + u8 bl = m_ram_addr & 0xf; + if (bl < m_d_pins) + { + m_d_output = m_d_output & ~(1 << bl); + m_write_d(m_d_output); + } + else if (bl < 12) + m_int_ff[~bl & 1] = 0; + else + logerror("ROS invalid pin %d at $%03X\n", bl, m_prev_pc); +} + +void mm78_device::op_skisl() +{ + // SKISL: SKBF/SKISL opcodes are separated + + // B7 must be low + if (m_ram_addr & 0x40) + { + logerror("SKISL invalid access at $%03X\n", m_prev_pc); + return; + } + + u8 bl = m_ram_addr & 0xf; + if (bl < m_d_pins) + m_skip = !BIT((m_d_output | m_read_d()) & m_d_mask, bl); + else if (bl < 12) + m_skip = !m_int_ff[~bl & 1]; + else + logerror("SKISL invalid pin %d at $%03X\n", bl, m_prev_pc); +} + + +// new opcodes + +void mm78_device::op_sag() +{ + // SAG: set Bu to 3 for the next cycle + m_sag = true; +} + +void mm78_device::op_lxa() +{ + // LXA: load X from A + m_x = m_a; +} + +void mm78_device::op_xax() +{ + // XAX: exchange A with X + u8 a = m_a; + m_a = m_x; + m_x = a; +} + +void mm78_device::op_tlb() +{ + // TLB x: transfer long banked + op_tl(); + m_pc |= 0x400; +} + +void mm78_device::op_tmlb() +{ + // TMLB x: transfer and mark long banked + op_tml(); + m_pc |= 0x400; +} + +void mm78_device::op_tab() +{ + // TAB: table look up transfer + m_skip_count = m_a + 1; + m_a = 0xf; +} + +void mm78_device::op_ix() +{ + // IX: input to X from channel X(aka B) + m_x = (m_read_r() & m_r_output) >> 4 & 0xf; +} + +void mm78_device::op_ox() +{ + // OX: output from X to channel X(aka B) + m_r_output = (m_r_output & 0xf) | m_x << 4; + m_write_r(m_r_output); +} + +void mm78_device::op_ioa() +{ + // IOA: exchange A with channel A + u8 a = m_read_r() & m_r_output & 0xf; + m_r_output = (m_r_output & ~0xf) | m_a; + m_write_r(m_r_output); + m_a = a; +} + +void mm78_device::op_i1sk() +{ + // I1SK: add channel 1 to A, skip on no overflow + m_a += m_read_p() & 0xf; + m_skip = !(m_a & 0x10); + m_a &= 0xf; +} + +void mm78_device::op_int0h() +{ + // INT0H: skip on INT0 high + m_skip = bool(m_int_line[0]); +} + +void mm78_device::op_int1l() +{ + // INT1L: skip on INT1 low + m_skip = !m_int_line[1]; +} diff --git a/src/devices/cpu/pps41/pps41base.cpp b/src/devices/cpu/pps41/pps41base.cpp new file mode 100644 index 00000000000..dc37d66bf5f --- /dev/null +++ b/src/devices/cpu/pps41/pps41base.cpp @@ -0,0 +1,351 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell PPS-4/1 MCU cores + +This is the single-chip evolution of Rockwell's older PPS-4 CPU. It is similar, +but a lot of things were simplified, the ALU instructions are less diverse. +It also has a lot in common with Rockwell's previous MCU series (A/B5000). + +Part numbers: +- A75xx = MM75 - 28 pin dip +- A76xx = MM76 - 42 pin spider +- A77xx = MM77 - 42 pin spider +- A78xx = MM78 - 42 pin spider +- A79xx = MM76C - 52 pin spider - high-speed counter +- A??xx = MM76D - 52 pin spider - 12-bit ADC +- A86xx = MM76E - 42 pin spider - extended ROM +- B76xx = MM76L - 40 pin dip +- B77xx = MM77L - 40 pin dip +- B80xx = MM77LA? - 40 pin dip +- B78xx = MM78L - 40 pin dip +- B86xx = MM76EL - 40 pin dip +- B90xx = MM78LA - 42 pin spider + +"spider" = 2 rows of pins on each side, just like standard PPS-4 CPUs. +"L" main difference is low-power + +Internal clock is 4-phase (4 subcycles per 1-byte opcode), and when running +from an external oscillator, it is divided by 2 first. It also has an internal +oscillator which can be enabled with a resistor wired to VC. + +References: +- Series MM76 Product Description +- Series MM77 Product Description +- MM76 Microcomputer Programming Manual +- MM77 Microcomputer Programming Manual + +TODO: +- add extended opcodes to disasm? it's easy to add there, but the emulation goes + through prefixes 1 cycle at the time which means the live disasm gets messy +- documentation discourages long jumps to the subroutine pages, but does not + explain what would happen. Scrabble Sensor does it, so it's probably ok. +- documentation discourages use of some extended opcodes when in subroutine pages, + but again does not explain why +- allowed opcode after TAB should be limited +- add MCU mask options, there's one for inverting interrupts +- does MM78LA support interrupts? the sparse documentation available says it does +- MM78LA mnemonics for changed opcodes is unknown +- no known documentation exists for MM77LA, mcu name is guessed (maybe it was + designed in collaboration with Mattel, and later evolved into MM78LA) + +*/ + +#include "emu.h" +#include "pps41base.h" + + +pps41_base_device::pps41_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_p(*this, 0xff), + m_read_d(*this, 0), + m_write_d(*this), + m_read_r(*this, 0xff), + m_write_r(*this), + m_read_sdi(*this, 1), + m_write_sdo(*this), + m_write_ssc(*this), + m_write_spk(*this) +{ } + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void pps41_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; + + // init RAM with 0xf + for (int i = 0; i <= m_datamask; i++) + m_data->write_byte(i, 0xf); + + // zerofill + m_pc = 0; + m_prev_pc = 0; + m_op = 0; + m_prev_op = 0; + m_prev2_op = 0; + m_prev3_op = 0; + memset(m_stack, 0, sizeof(m_stack)); + m_stack_levels = 1; + + m_a = 0; + m_b = 0; + m_prev_b = 0; + m_prev2_b = 0; + m_ram_addr = 0; + m_ram_delay = false; + m_sag = false; + m_c = 0; + m_prev_c = 0; + m_c_in = 0; + m_c_delay = false; + m_x = 0; + m_skip = false; + m_skip_count = 0; + + m_s = 0; + m_sclock_in = 0; + m_sclock_count = 0; + + set_d_pins(10); + m_d_output = 0; + set_r_pins(8); + m_r_output = 0; + m_int_line[0] = m_int_line[1] = 1; // GND = 1 + m_int_ff[0] = m_int_ff[1] = 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_prev2_op)); + save_item(NAME(m_prev3_op)); + save_item(NAME(m_stack)); + + save_item(NAME(m_a)); + save_item(NAME(m_b)); + save_item(NAME(m_prev_b)); + save_item(NAME(m_prev2_b)); + save_item(NAME(m_ram_addr)); + save_item(NAME(m_ram_delay)); + save_item(NAME(m_sag)); + save_item(NAME(m_c)); + save_item(NAME(m_prev_c)); + save_item(NAME(m_c_in)); + save_item(NAME(m_c_delay)); + save_item(NAME(m_x)); + save_item(NAME(m_skip)); + save_item(NAME(m_skip_count)); + + save_item(NAME(m_s)); + save_item(NAME(m_sclock_in)); + save_item(NAME(m_sclock_count)); + + save_item(NAME(m_d_output)); + save_item(NAME(m_r_output)); + save_item(NAME(m_int_line)); + save_item(NAME(m_int_ff)); + + // 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, "A", m_a).formatstr("%01X"); // 2 + state_add(++m_state_count, "C", m_c_in).formatstr("%01X"); // 3 + state_add(++m_state_count, "B", m_b).formatstr("%02X"); // 4 + state_add(++m_state_count, "S", m_s).formatstr("%01X"); // 5 + + set_icountptr(m_icount); +} + +device_memory_interface::space_config_vector pps41_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 pps41_base_device::device_reset() +{ + m_op = m_prev_op = m_prev2_op = 0; + m_pc = m_prgmask >> 1 & ~0x3f; + m_skip = false; + m_skip_count = 0; + + // clear outputs + m_write_r(m_r_output = m_r_mask); + m_write_d(m_d_output = 0); + + m_s = 0; + m_sclock_count = 0; + m_write_sdo(0); + m_write_ssc(0); +} + + +//------------------------------------------------- +// interrupt handling +//------------------------------------------------- + +void pps41_base_device::execute_set_input(int line, int state) +{ + state = (state) ? 1 : 0; + + switch (line) + { + case PPS41_INPUT_LINE_INT0: + // reset flip-flop on rising edge + if (state && !m_int_line[0]) + m_int_ff[0] = 0; + m_int_line[0] = state; + break; + + case PPS41_INPUT_LINE_INT1: + // reset flip-flop on falling edge + if (!state && m_int_line[1]) + m_int_ff[1] = 0; + m_int_line[1] = state; + break; + + default: + break; + } +} + + +//------------------------------------------------- +// serial i/o +//------------------------------------------------- + +void pps41_base_device::ssc_w(int state) +{ + state = (state) ? 1 : 0; + + // serial shift on falling edge + if (!state && m_sclock_in) + serial_shift(m_read_sdi()); + m_sclock_in = state; +} + +void pps41_base_device::serial_shift(int state) +{ + state = (state) ? 1 : 0; + m_s = (m_s << 1 | state) & 0xf; + m_write_sdo(BIT(m_s, 3)); +} + +void pps41_base_device::serial_clock() +{ + // internal serial clock cycle + int i = m_read_sdi(); + m_sclock_count--; + m_write_ssc(m_sclock_count & 1); + + if (~m_sclock_count & 1 && m_sclock_count < 8) + serial_shift(i); +} + + +//------------------------------------------------- +// execute +//------------------------------------------------- + +void pps41_base_device::cycle() +{ + m_icount--; + + // clock serial i/o + if (m_sclock_count) + serial_clock(); +} + +void pps41_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 pps41_base_device::execute_run() +{ + while (m_icount > 0) + { + // remember previous state + m_prev3_op = m_prev2_op; + m_prev2_op = m_prev_op; + m_prev_op = m_op; + m_prev_pc = m_pc; + + m_prev2_b = m_prev_b; + m_prev_b = m_b; + m_prev_c = m_c; + + // fetch next opcode + if (!m_skip && !m_skip_count) + debugger_instruction_hook(m_pc); + m_op = m_program->read_byte(m_pc); + increment_pc(); + cycle(); + + // handle opcode if it's not skipped + if (m_skip) + { + // still skip through prefix(es) + m_skip = op_is_tr(m_op); + m_op = 0; // fake nop + } + else if (m_skip_count) + { + m_skip_count--; + + // restore opcode state + m_op = m_prev_op; + m_prev_op = m_prev2_op; + m_prev2_op = m_prev3_op; + } + else + execute_one(); + + // some opcodes delay RAM address(Bl part) adjustment for 1 cycle + m_ram_addr = m_b; + + if (m_ram_delay) + { + m_ram_addr = (m_ram_addr & ~0xf) | (m_prev_b & 0xf); + m_ram_delay = false; + } + + // SAG sets RAM address(Bu part) to 3 for the next cycle + if (m_sag) + { + m_ram_addr = (m_ram_addr & 0xf) | 0x30; + m_sag = false; + } + + // and some opcodes delay carry adjustment for 1 cycle + m_c_in = m_c_delay ? m_prev_c : m_c; + m_c_delay = false; + } +} diff --git a/src/devices/cpu/pps41/pps41base.h b/src/devices/cpu/pps41/pps41base.h new file mode 100644 index 00000000000..e97a8d39f33 --- /dev/null +++ b/src/devices/cpu/pps41/pps41base.h @@ -0,0 +1,154 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell PPS-4/1 MCU cores + + Don't include this file, include the specific device header instead, + for example mm76.h + +*/ + +#ifndef MAME_CPU_PPS41_PPS41BASE_H +#define MAME_CPU_PPS41_PPS41BASE_H + +#pragma once + +#include "machine/pla.h" + +enum +{ + PPS41_INPUT_LINE_INT0 = 0, + PPS41_INPUT_LINE_INT1 +}; + + +class pps41_base_device : public cpu_device +{ +public: + // configuration helpers + // I/O ports: + + // 8-bit P(parallel) input + auto read_p() { return m_read_p.bind(); } + + // 10/12-bit D(discrete) I/O + auto read_d() { return m_read_d.bind(); } + auto write_d() { return m_write_d.bind(); } + + // 8/10/14-bit R I/O + auto read_r() { return m_read_r.bind(); } + auto write_r() { return m_write_r.bind(); } + + // serial data/clock + auto read_sdi() { return m_read_sdi.bind(); } + auto write_sdo() { return m_write_sdo.bind(); } + auto write_ssc() { return m_write_ssc.bind(); } + + // speaker output + auto write_spk() { return m_write_spk.bind(); } + + // I/O access + u16 d_output_r() { return m_d_output; } + u16 r_output_r() { return m_r_output; } + int sdo_r() { return BIT(m_s, 3); } + void ssc_w(int state); + +protected: + // construction/destruction + pps41_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); + + // device-level overrides + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + + // device_execute_interface overrides + virtual u64 execute_clocks_to_cycles(u64 clocks) const noexcept override { return (clocks + 4 - 1) / 4; } + virtual u64 execute_cycles_to_clocks(u64 cycles) const noexcept override { return (cycles * 4); } + virtual u32 execute_min_cycles() const noexcept override { return 1; } + virtual u32 execute_max_cycles() const noexcept override { return 2; } + virtual void execute_set_input(int line, int state) override; + virtual void execute_run() override; + virtual void execute_one() = 0; + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + + address_space_config m_program_config; + address_space_config m_data_config; + address_space *m_program; + address_space *m_data; + + int m_icount; + int m_state_count; + + // fixed settings or mask options + int m_prgwidth; // ROM/RAM address size + int m_datawidth; // " + u16 m_prgmask; // " + u16 m_datamask; // " + + optional_device<pla_device> m_opla; // segment output PLA + + // i/o handlers + devcb_read8 m_read_p; + devcb_read16 m_read_d; + devcb_write16 m_write_d; + devcb_read16 m_read_r; + devcb_write16 m_write_r; + devcb_read_line m_read_sdi; + devcb_write_line m_write_sdo; + devcb_write_line m_write_ssc; + devcb_write8 m_write_spk; + + // internal state, regs + u16 m_pc; + u16 m_prev_pc; + u8 m_op; + u8 m_prev_op; + u8 m_prev2_op; + u8 m_prev3_op; + int m_stack_levels; + u16 m_stack[2]; // max 2 + + u8 m_a; + u8 m_b; + u8 m_prev_b; + u8 m_prev2_b; + u8 m_ram_addr; + bool m_ram_delay; + bool m_sag; + int m_c; + int m_prev_c; + int m_c_in; + bool m_c_delay; + u8 m_x; + bool m_skip; + int m_skip_count; + + u8 m_s; + int m_sclock_in; + int m_sclock_count; + + int m_d_pins; + u16 m_d_mask; + u16 m_d_output; + int m_r_pins; + u16 m_r_mask; + u16 m_r_output; + int m_int_line[2]; + int m_int_ff[2]; + + // misc handlers + void set_d_pins(u8 p) { m_d_pins = p; m_d_mask = (1 << p) - 1; } + void set_r_pins(u8 p) { m_r_pins = p; m_r_mask = (1 << p) - 1; } + virtual bool op_is_tr(u8 op) = 0; + + void serial_shift(int state); + void serial_clock(); + virtual void cycle(); + void increment_pc(); +}; + + +#endif // MAME_CPU_PPS41_PPS41BASE_H diff --git a/src/devices/cpu/pps41/pps41d.cpp b/src/devices/cpu/pps41/pps41d.cpp new file mode 100644 index 00000000000..70e1401f87a --- /dev/null +++ b/src/devices/cpu/pps41/pps41d.cpp @@ -0,0 +1,210 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell PPS-4/1 disassembler + +*/ + +#include "emu.h" +#include "pps41d.h" + +// constructor + +pps41_common_disassembler::pps41_common_disassembler() +{ + // init lfsr pc lut + for (u32 i = 0, pc = 0; i < 0x40; i++) + { + m_l2r[i] = pc; + m_r2l[pc] = i; + pc = increment_pc(pc); + } +} + +offs_t pps41_common_disassembler::increment_pc(offs_t pc) +{ + int feed = ((pc & 0x3e) == 0) ? 1 : 0; + feed ^= (pc >> 1 ^ pc) & 1; + return (pc & ~0x3f) | (pc >> 1 & 0x1f) | (feed << 5); +} + + +// common lookup tables + +enum pps41_common_disassembler::e_mnemonics : unsigned +{ + // MM76/shared + mILL /* 0! */, + mXAB, mLBA, mLB, mEOB2, + mSB, mRB, mSKBF, + mXAS, mLSA, + mL, mX, mXDSK, mXNSK, + mA, mAC, mACSK, mASK, mCOM, mRC, mSC, mSKNC, mLAI, mAISK, + mRT, mRTSK, mT, mNOP, mTL, mTM, mTML, mTR, + mSKMEA, mSKBEI, mSKAEI, + mSOS, mROS, mSKISL, mIBM, mOB, mIAM, mOA, mIOS, mI1, mI2C, mINT1H, mDIN1, mINT0L, mDIN0, mSEG1, mSEG2, + + // MM78 differences + mINT0H, mINT1L, mSAG, mEOB3, mTAB, + mI1SK, mIX, mOX, mLXA, mXAX, mIOA, + mTLB, mTMLB +}; + +const char *const pps41_common_disassembler::s_name[] = +{ + // MM76 + "?", + "XAB", "LBA", "LB", "EOB", + "SB", "RB", "SKBF", + "XAS", "LSA", + "L", "X", "XDSK", "XNSK", + "A", "AC", "ACSK", "ASK", "COM", "RC", "SC", "SKNC", "LAI", "AISK", + "RT", "RTSK", "T", "NOP", "TL", "TM", "TML", "TR", + "SKMEA", "SKBEI", "SKAEI", + "SOS", "ROS", "SKISL", "IBM", "OB", "IAM", "OA", "IOS", "I1", "I2C", "INT1H", "DIN1", "INT0L", "DIN0", "SEG1", "SEG2", + + // MM78 + "INT0H", "INT1L", "SAG", "EOB", "TAB", + "I1SK", "IX", "OX", "LXA", "XAX", "IOA", + "TLB", "TMLB" +}; + +// number of bits per opcode parameter +// note: d4 means hex, d5 means inverted, d6 means extended, d7 means double extended +const u8 pps41_common_disassembler::s_bits[] = +{ + // MM76 + 0, + 0, 0, 4, 2, + 2, 2, 2, + 0, 0, + 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, + 0, 0, 0x36, 0, 0x76, 0x36, 0x76, 0x34, + 0, 0x40, 0x60, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + // MM78 + 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0xf6, 0xf6 +}; + +const u32 pps41_common_disassembler::s_flags[] = +{ + // MM76 + 0, + 0, 0, 0, 0, + 0, 0, STEP_COND, + 0, 0, + 0, 0, STEP_COND, STEP_COND, + 0, 0, STEP_COND, STEP_COND, 0, 0, 0, STEP_COND, 0, STEP_COND, + STEP_OUT, STEP_OUT, 0, 0, 0, STEP_OVER, STEP_OVER, 0, + STEP_COND, STEP_COND, STEP_COND, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, STEP_COND, STEP_COND, STEP_COND, STEP_COND, 0, 0, + + // MM78 + STEP_COND, STEP_COND, 0, 0, 0, + STEP_COND, 0, 0, 0, 0, 0, + 0, STEP_OVER +}; + + +// common disasm + +offs_t pps41_common_disassembler::common_disasm(const u8 *lut_opmap, std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + // get raw opcode + u8 op = opcodes.r8(pc); + u8 instr = lut_opmap[op]; + + // get parameter + u8 bits = s_bits[instr]; + u16 mask = (1 << (bits & 0xf)) - 1; + bits &= 0xf0; + + u16 param = op & mask; + if (bits & 0x20) + param ^= mask; + + // memory bit opcodes are 1,2,3,4 + if (instr == mSB || instr == mRB || instr == mSKBF) + param++; + + // disassemble it + util::stream_format(stream, "%-8s", s_name[instr]); + if (mask > 0) + { + if (bits & 0x10) + util::stream_format(stream, (mask & ~0xf) ? "$%02X" : "$%X", param); + else + util::stream_format(stream, "%d", param); + } + + return 1 | s_flags[instr] | SUPPORTED; +} + + +// MM76 disasm + +const u8 mm76_disassembler::mm76_opmap[0x100] = +{ +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + mNOP, mSKNC, mRT, mRTSK, mINT0L,mINT1H,mDIN1, mDIN0, mSKBF, mSKBF, mSKBF, mSKBF, mSC, mRC, mSEG1, mSEG2, // 0 + mSB, mSB, mSB, mSB, mRB, mRB, mRB, mRB, mOA, mOB, mIAM, mIBM, mEOB2, mEOB2, mEOB2, mEOB2, // 1 + mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, // 2 + mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, // 3 + + mAC, mACSK, mA, mASK, mLBA, mCOM, mXAB, mSKMEA,mILL, mILL, mI1, mI2C, mLSA, mIOS, mXAS, mILL, // 4 + mL, mL, mL, mL, mXNSK, mXNSK, mXNSK, mXNSK, mX, mX, mX, mX, mXDSK, mXDSK, mXDSK, mXDSK, // 5 + mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, // 6 + mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, // 7 + + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // 8 + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // 9 + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // A + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // B + + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, // C + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, // D + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, // E + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT // F +}; + +offs_t mm76_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + return common_disasm(mm76_opmap, stream, pc, opcodes, params); +} + + +// MM78 disasm + +const u8 mm78_disassembler::mm78_opmap[0x100] = +{ +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + mNOP, mSKISL,mSKNC, mINT0H,mINT1L,mRC, mSC, mSAG, mEOB3, mEOB3, mEOB3, mEOB3, mEOB3, mEOB3, mEOB3, mEOB3, // 0 + mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, mLB, // 1 + mSB, mSB, mSB, mSB, mRB, mRB, mRB, mRB, mSKBF, mSKBF, mSKBF, mSKBF, mTAB, mIOS, mRTSK, mRT, // 2 + mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, mTR, // 3 + + mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, mLAI, // 4 + mL, mL, mL, mL, mXNSK, mXNSK, mXNSK, mXNSK, mXDSK, mXDSK, mXDSK, mXDSK, mX, mX, mX, mX, // 5 + mI1SK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, mAISK, // 6 + mSOS, mROS, mIX, mOX, mXAS, mLXA, mLBA, mCOM, mI2C, mXAX, mXAB, mIOA, mAC, mACSK, mA, mSKMEA,// 7 + + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // 8 + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // 9 + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // A + mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, mTM, // B + + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, // C + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, // D + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, // E + mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT, mT // F +}; + +offs_t mm78_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + return common_disasm(mm78_opmap, stream, pc, opcodes, params); +} diff --git a/src/devices/cpu/pps41/pps41d.h b/src/devices/cpu/pps41/pps41d.h new file mode 100644 index 00000000000..5d61b5ebf7b --- /dev/null +++ b/src/devices/cpu/pps41/pps41d.h @@ -0,0 +1,66 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Rockwell PPS-4/1 disassembler + +*/ + +#ifndef MAME_CPU_PPS41_PPS41D_H +#define MAME_CPU_PPS41_PPS41D_H + +#pragma once + + +class pps41_common_disassembler : public util::disasm_interface +{ +public: + pps41_common_disassembler(); + virtual ~pps41_common_disassembler() = default; + + virtual u32 opcode_alignment() const override { return 1; } + virtual u32 interface_flags() const override { return NONLINEAR_PC | PAGED; } + virtual u32 page_address_bits() const override { return 6; } + virtual offs_t pc_linear_to_real(offs_t pc) const override { return (pc & ~0x3f) | m_l2r[pc & 0x3f]; } + virtual offs_t pc_real_to_linear(offs_t pc) const override { return (pc & ~0x3f) | m_r2l[pc & 0x3f]; } + +protected: + enum e_mnemonics : unsigned; + static const char *const s_name[]; + static const u8 s_bits[]; + static const u32 s_flags[]; + + u8 m_l2r[0x40]; + u8 m_r2l[0x40]; + + offs_t increment_pc(offs_t pc); + offs_t common_disasm(const u8 *lut_opmap, std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms); +}; + +class mm76_disassembler : public pps41_common_disassembler +{ +public: + mm76_disassembler() = default; + virtual ~mm76_disassembler() = default; + + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + static const u8 mm76_opmap[0x100]; + +}; + +class mm78_disassembler : public pps41_common_disassembler +{ +public: + mm78_disassembler() = default; + virtual ~mm78_disassembler() = default; + + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + static const u8 mm78_opmap[0x100]; + +}; + +#endif // MAME_CPU_PPS41_PPS41D_H |