pps41: add some i/o opcodes

This commit is contained in:
hap 2021-03-06 18:52:30 +01:00
parent 3f4045e8ea
commit d608ebfa41
7 changed files with 138 additions and 37 deletions

View File

@ -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;
}

View File

@ -43,6 +43,7 @@ protected:
virtual void device_start() override;
// opcode handlers
virtual void op_ibm() override;
virtual void op_ios() override;
};

View File

@ -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

View File

@ -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()

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<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_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;