diff --git a/src/devices/cpu/pps41/mm75.cpp b/src/devices/cpu/pps41/mm75.cpp index a0fe7e2bc40..d1d4a324235 100644 --- a/src/devices/cpu/pps41/mm75.cpp +++ b/src/devices/cpu/pps41/mm75.cpp @@ -10,7 +10,7 @@ #include "mm75.h" -DEFINE_DEVICE_TYPE(MM75, mm75_device, "mm75", "Rockwell MM75") +DEFINE_DEVICE_TYPE(MM75, mm75_device, "mm75", "Rockwell MM75") // stripped-down MM76 (no serial i/o, less pins) // constructor @@ -24,4 +24,5 @@ void mm75_device::device_start() { mm76_device::device_start(); m_d_pins--; + m_d_mask >>= 1; } diff --git a/src/devices/cpu/pps41/mm75.h b/src/devices/cpu/pps41/mm75.h index f2950207dc8..5dd036a1086 100644 --- a/src/devices/cpu/pps41/mm75.h +++ b/src/devices/cpu/pps41/mm75.h @@ -43,6 +43,7 @@ protected: virtual void device_start() override; // opcode handlers + virtual void op_ibm() override; virtual void op_ios() override; }; diff --git a/src/devices/cpu/pps41/mm75op.cpp b/src/devices/cpu/pps41/mm75op.cpp index bea107b9fc4..3f0769ebc8f 100644 --- a/src/devices/cpu/pps41/mm75op.cpp +++ b/src/devices/cpu/pps41/mm75op.cpp @@ -9,6 +9,12 @@ // opcodes (differences with mm76_device) +void mm75_device::op_ibm() +{ + // IBM: does not PI5-8 pins + op_illegal(); +} + void mm75_device::op_ios() { // IOS: does not have serial I/O diff --git a/src/devices/cpu/pps41/mm76.cpp b/src/devices/cpu/pps41/mm76.cpp index 11041e25ad2..38ce96ea67a 100644 --- a/src/devices/cpu/pps41/mm76.cpp +++ b/src/devices/cpu/pps41/mm76.cpp @@ -12,10 +12,10 @@ #include "pps41d.h" -DEFINE_DEVICE_TYPE(MM76, mm76_device, "mm76", "Rockwell MM76") -DEFINE_DEVICE_TYPE(MM76L, mm76l_device, "mm76l", "Rockwell MM76L") -DEFINE_DEVICE_TYPE(MM76E, mm76e_device, "mm76e", "Rockwell MM76E") -DEFINE_DEVICE_TYPE(MM76EL, mm76el_device, "mm76el", "Rockwell MM76EL") +DEFINE_DEVICE_TYPE(MM76, mm76_device, "mm76", "Rockwell MM76") // 640 bytes ROM, 48 bytes RAM +DEFINE_DEVICE_TYPE(MM76L, mm76l_device, "mm76l", "Rockwell MM76L") // low-power +DEFINE_DEVICE_TYPE(MM76E, mm76e_device, "mm76e", "Rockwell MM76E") // ROM extended to 1KB +DEFINE_DEVICE_TYPE(MM76EL, mm76el_device, "mm76el", "Rockwell MM76EL") // low-power // constructor @@ -82,7 +82,6 @@ void mm76_device::device_start() { pps41_base_device::device_start(); m_stack_levels = 1; - m_d_pins = 10; } void mm76_device::device_reset() diff --git a/src/devices/cpu/pps41/mm76op.cpp b/src/devices/cpu/pps41/mm76op.cpp index fced467662c..eb0537cb78e 100644 --- a/src/devices/cpu/pps41/mm76op.cpp +++ b/src/devices/cpu/pps41/mm76op.cpp @@ -89,23 +89,83 @@ void mm76_device::op_eob() void mm76_device::op_sb() { - // Bu != 3: SB x: set memory bit - // Bu == 3: SOS: set output - op_todo(); + // 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) + { + if ((m_ram_addr & 0xf) > m_d_pins) + logerror("SOS invalid pin %d at $%03X\n", m_ram_addr & 0xf, m_prev_pc); + else + { + m_d_output = (m_d_output | (1 << (m_ram_addr & 0xf))) & 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() { - // Bu != 3: RB x: reset memory bit - // Bu == 3: ROS: reset output - op_todo(); + // 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) + { + if ((m_ram_addr & 0xf) > m_d_pins) + logerror("ROS invalid pin %d at $%03X\n", m_ram_addr & 0xf, m_prev_pc); + else + { + m_d_output = m_d_output & ~(1 << (m_ram_addr & 0xf)); + 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() { - // Bu != 3: SKBF x: test memory bit - // Bu == 3: SKISL: test input - op_todo(); + // 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) + { + if ((m_ram_addr & 0xf) > m_d_pins) + logerror("SKISL invalid pin %d at $%03X\n", m_ram_addr & 0xf, m_prev_pc); + else + m_skip = !BIT((m_d_output | m_read_d()) & m_d_mask, m_ram_addr & 0xf); + } + + // Bu != 3: SKBF + if ((m_prev_b & 0x30) != 0x30) + m_skip = m_skip || !BIT(ram_r(), m_op & 3); } @@ -257,7 +317,7 @@ void mm76_device::op_t() cycle(); // jumps from subroutine pages reset page to SR1 - u16 mask = m_prgmask ^ 0x7f; + u16 mask = m_prgmask & ~0x7f; if ((m_pc & mask) == mask) m_pc &= ~0x40; @@ -277,11 +337,11 @@ void mm76_device::op_tm() cycle(); // calls from subroutine pages don't push PC - u16 mask = m_prgmask ^ 0x7f; + u16 mask = m_prgmask & ~0x7f; if ((m_pc & mask) != mask) push_pc(); - m_pc = ((~m_op & 0x3f) | ~0x3f) & m_prgmask; + m_pc = ((m_prgmask & ~0x3f) | (~m_op & 0x3f)); } void mm76_device::op_tml() @@ -324,24 +384,27 @@ void mm76_device::op_skaei() 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 - op_todo(); + 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 - op_todo(); + m_a &= m_read_r() & m_r_output; } void mm76_device::op_oa() { // OA: output from A to channel A - op_todo(); + m_r_output = (m_r_output & ~0xf) | m_a; + m_write_r(m_r_output); } void mm76_device::op_ios() @@ -353,13 +416,13 @@ void mm76_device::op_ios() void mm76_device::op_i1() { // I1: input channel 1 to A - op_todo(); + m_a = m_read_p() & 0xf; } void mm76_device::op_i2c() { // I2C: input channel 2 to A - op_todo(); + m_a = ~m_read_p() >> 4 & 0xf; } void mm76_device::op_int1h() @@ -389,11 +452,15 @@ void mm76_device::op_din0() void mm76_device::op_seg1() { // SEG1: output A+carry through PLA to channel A - op_todo(); + 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 - op_todo(); + 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/pps41base.cpp b/src/devices/cpu/pps41/pps41base.cpp index 4177c4c4841..d0d99dc960d 100644 --- a/src/devices/cpu/pps41/pps41base.cpp +++ b/src/devices/cpu/pps41/pps41base.cpp @@ -32,7 +32,15 @@ References: 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 -- WIP +- documentation discourages long jumps to the subroutine pages, but does not + explain what would happen +- documentation discourages use of some extended opcodes when in subroutine pages, + but again does not explain why +- documentation is conflicting whether or not MM76/MM75 can (re)set interrupt flip- + flops with SOS/ROS opcodes +- add serial i/o +- add pseudo interrupts +- add MM78 */ @@ -49,6 +57,9 @@ pps41_base_device::pps41_base_device(const machine_config &mconfig, device_type m_prgwidth(prgwidth), m_datawidth(datawidth), m_opla(*this, "opla"), + m_read_p(*this), + m_read_d(*this), + m_write_d(*this), m_read_r(*this), m_write_r(*this) { } @@ -71,7 +82,10 @@ void pps41_base_device::device_start() m_datamask = (1 << m_datawidth) - 1; // resolve callbacks - m_read_r.resolve_safe(0); + m_read_p.resolve_safe(0xff); + m_read_d.resolve_safe(0); + m_write_d.resolve_safe(); + m_read_r.resolve_safe(0xff); m_write_r.resolve_safe(); // zerofill @@ -98,8 +112,10 @@ void pps41_base_device::device_start() m_skip = false; m_skip_count = 0; - m_cha = 0; - m_chb = 0; + m_d_pins = 10; + m_d_mask = (1 << m_d_pins) - 1; + m_d_output = 0; + m_r_output = 0; // register for savestates save_item(NAME(m_pc)); @@ -125,8 +141,8 @@ void pps41_base_device::device_start() save_item(NAME(m_skip)); save_item(NAME(m_skip_count)); - save_item(NAME(m_cha)); - save_item(NAME(m_chb)); + save_item(NAME(m_d_output)); + save_item(NAME(m_r_output)); // register state for debugger state_add(STATE_GENPC, "GENPC", m_pc).formatstr("%03X").noshow(); @@ -156,14 +172,14 @@ device_memory_interface::space_config_vector pps41_base_device::memory_space_con void pps41_base_device::device_reset() { - m_op = m_prev_op = 0; + 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_cha = m_chb = 0xf; - m_write_r(0); + m_write_r(m_r_output = 0xff); + m_write_d(m_d_output = 0); } diff --git a/src/devices/cpu/pps41/pps41base.h b/src/devices/cpu/pps41/pps41base.h index 5157e53f417..0834a38fb96 100644 --- a/src/devices/cpu/pps41/pps41base.h +++ b/src/devices/cpu/pps41/pps41base.h @@ -20,6 +20,13 @@ public: // configuration helpers // I/O ports: + // 8-bit P(parallel) input + auto read_p() { return m_read_p.bind(); } + + // 10-bit D(discrete) I/O + auto read_d() { return m_read_d.bind(); } + auto write_d() { return m_write_d.bind(); } + // 8-bit R I/O auto read_r() { return m_read_r.bind(); } auto write_r() { return m_write_r.bind(); } @@ -61,6 +68,9 @@ protected: optional_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_read8 m_read_r; devcb_write8 m_write_r; @@ -73,7 +83,6 @@ protected: u8 m_prev3_op; int m_stack_levels; u16 m_stack[2]; // max 2 - int m_d_pins; u8 m_a; u8 m_b; @@ -90,8 +99,10 @@ protected: bool m_skip; int m_skip_count; - u8 m_cha; - u8 m_chb; + int m_d_pins; + u16 m_d_mask; + u16 m_d_output; + u8 m_r_output; // misc handlers virtual bool op_is_tr(u8 op) = 0;