summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/cop400/cop400.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/cop400/cop400.cpp')
-rw-r--r--src/devices/cpu/cop400/cop400.cpp306
1 files changed, 197 insertions, 109 deletions
diff --git a/src/devices/cpu/cop400/cop400.cpp b/src/devices/cpu/cop400/cop400.cpp
index 1db85978414..30b1680150a 100644
--- a/src/devices/cpu/cop400/cop400.cpp
+++ b/src/devices/cpu/cop400/cop400.cpp
@@ -50,7 +50,6 @@
- COP410L/COP410C
- save internal RAM when CKO is RAM power supply pin
- COP404L opcode map switching, dual timer, microbus enable
- - improve SIO output timing for interface with 93CXX serial EEPROMs
*/
@@ -85,6 +84,9 @@ DEFINE_DEVICE_TYPE(COP446C, cop446c_cpu_device, "cop446c", "COP446C")
#define LOG_MICROBUS 0
+// step through skipped instructions in debugger
+#define COP_DEBUG_SKIP 0
+
/***************************************************************************
MACROS
@@ -181,9 +183,8 @@ cop400_cpu_device::cop400_cpu_device(const machine_config &mconfig, device_type
, m_d_mask(d_mask)
, m_in_mask(in_mask)
{
- for (int i = 0; i < 256; i++) {
+ for (int i = 0; i < 256; i++)
m_InstLen[i] = 1;
- }
m_InstLen[0x33] = m_InstLen[0x23] = 2;
@@ -192,7 +193,8 @@ cop400_cpu_device::cop400_cpu_device(const machine_config &mconfig, device_type
case COP410_FEATURE:
m_opcode_map = COP410_OPCODE_MAP;
- for (int r = 0; r < 2; r++) {
+ for (int r = 0; r < 2; r++)
+ {
m_InstLen[0x60 + r] = 2; // JMP
m_InstLen[0x68 + r] = 2; // JSR
}
@@ -201,7 +203,8 @@ cop400_cpu_device::cop400_cpu_device(const machine_config &mconfig, device_type
case COP420_FEATURE:
m_opcode_map = COP420_OPCODE_MAP;
- for (int r = 0; r < 4; r++) {
+ for (int r = 0; r < 4; r++)
+ {
m_InstLen[0x60 + r] = 2; // JMP
m_InstLen[0x68 + r] = 2; // JSR
}
@@ -210,7 +213,8 @@ cop400_cpu_device::cop400_cpu_device(const machine_config &mconfig, device_type
case COP444L_FEATURE:
m_opcode_map = COP444L_OPCODE_MAP;
- for (int r = 0; r < 8; r++) {
+ for (int r = 0; r < 8; r++)
+ {
m_InstLen[0x60 + r] = 2; // JMP
m_InstLen[0x68 + r] = 2; // JSR
}
@@ -219,7 +223,8 @@ cop400_cpu_device::cop400_cpu_device(const machine_config &mconfig, device_type
case COP424C_FEATURE:
m_opcode_map = COP424C_OPCODE_MAP;
- for (int r = 0; r < 8; r++) {
+ for (int r = 0; r < 8; r++)
+ {
m_InstLen[0x60 + r] = 2; // JMP
m_InstLen[0x68 + r] = 2; // JSR
}
@@ -369,16 +374,39 @@ void cop400_cpu_device::WRITE_G(uint8_t data)
OUT_G(G);
}
+void cop400_cpu_device::WRITE_EN(uint8_t data)
+{
+ if (EN != data)
+ {
+ EN = data;
+
+ if (BIT(EN, 2))
+ {
+ OUT_L(Q);
+ }
+ else
+ {
+ // tri-state(floating) pins
+ OUT_L(m_read_l_tristate(0, 0xff));
+ }
+
+ sk_update();
+ }
+}
+
/***************************************************************************
OPCODE HANDLERS
***************************************************************************/
-#define INSTRUCTION(mnemonic) void (cop400_cpu_device::mnemonic)(uint8_t opcode)
+#define INSTRUCTION(mnemonic) void (cop400_cpu_device::mnemonic)(uint8_t operand)
#define OP(mnemonic) &cop400_cpu_device::mnemonic
INSTRUCTION(illegal)
{
- logerror("COP400: PC = %03x, Illegal opcode = %02x\n", PC-1, ROM(PC-1));
+ if (m_second_byte)
+ logerror("COP400: PC = %03x, Illegal opcode = %02x %02x\n", m_prevpc, m_opcode, operand);
+ else
+ logerror("COP400: PC = %03x, Illegal opcode = %02x\n", m_prevpc, m_opcode);
}
#include "cop400op.hxx"
@@ -426,9 +454,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP410_OPCODE_23_
OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal)
};
-void cop400_cpu_device::cop410_op23(uint8_t opcode)
+void cop400_cpu_device::cop410_op23(uint8_t operand)
{
- uint8_t opcode23 = fetch();
+ uint8_t opcode23 = operand;
(this->*COP410_OPCODE_23_MAP[opcode23])(opcode23);
}
@@ -472,9 +500,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP410_OPCODE_33_
OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal)
};
-void cop400_cpu_device::cop410_op33(uint8_t opcode)
+void cop400_cpu_device::cop410_op33(uint8_t operand)
{
- uint8_t opcode33 = fetch();
+ uint8_t opcode33 = operand;
(this->*COP410_OPCODE_33_MAP[opcode33])(opcode33);
}
@@ -557,9 +585,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP420_OPCODE_23_
OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal)
};
-void cop400_cpu_device::cop420_op23(uint8_t opcode)
+void cop400_cpu_device::cop420_op23(uint8_t operand)
{
- uint8_t opcode23 = fetch();
+ uint8_t opcode23 = operand;
(this->*COP420_OPCODE_23_MAP[opcode23])(opcode23);
}
@@ -603,9 +631,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP420_OPCODE_33_
OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal) , OP(illegal)
};
-void cop400_cpu_device::cop420_op33(uint8_t opcode)
+void cop400_cpu_device::cop420_op33(uint8_t operand)
{
- uint8_t opcode33 = fetch();
+ uint8_t opcode33 = operand;
(this->*COP420_OPCODE_33_MAP[opcode33])(opcode33);
}
@@ -688,9 +716,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP444L_OPCODE_23
OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad)
};
-void cop400_cpu_device::cop444l_op23(uint8_t opcode)
+void cop400_cpu_device::cop444l_op23(uint8_t operand)
{
- uint8_t opcode23 = fetch();
+ uint8_t opcode23 = operand;
(this->*COP444L_OPCODE_23_MAP[opcode23])(opcode23);
}
@@ -734,9 +762,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP444L_OPCODE_33
OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi)
};
-void cop400_cpu_device::cop444l_op33(uint8_t opcode)
+void cop400_cpu_device::cop444l_op33(uint8_t operand)
{
- uint8_t opcode33 = fetch();
+ uint8_t opcode33 = operand;
(this->*COP444L_OPCODE_33_MAP[opcode33])(opcode33);
}
@@ -819,9 +847,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP424C_OPCODE_23
OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad) , OP(xad)
};
-void cop400_cpu_device::cop424c_op23(uint8_t opcode)
+void cop400_cpu_device::cop424c_op23(uint8_t operand)
{
- uint8_t opcode23 = fetch();
+ uint8_t opcode23 = operand;
(this->*COP424C_OPCODE_23_MAP[opcode23])(opcode23);
}
@@ -865,9 +893,9 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP424C_OPCODE_33
OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi) , OP(lbi)
};
-void cop400_cpu_device::cop424c_op33(uint8_t opcode)
+void cop400_cpu_device::cop424c_op33(uint8_t operand)
{
- uint8_t opcode33 = fetch();
+ uint8_t opcode33 = operand;
(this->*COP424C_OPCODE_33_MAP[opcode33])(opcode33);
}
@@ -911,6 +939,24 @@ const cop400_cpu_device::cop400_opcode_func cop400_cpu_device::COP424C_OPCODE_MA
OP(jp) , OP(jp) , OP(jp) , OP(jp) , OP(jp) , OP(jp) , OP(jp) , OP(jid)
};
+inline bool cop400_cpu_device::is_control_transfer(uint8_t opcode)
+{
+ // JP, JSRP, JID or LQID
+ // (TODO: verify that LQID inhibits interrupts since it transfers control temporarily)
+ if ((opcode & 0x80) == 0x80)
+ return true;
+
+ // JMP or JSR
+ if ((opcode & 0xf0) == 0x60)
+ return true;
+
+ // RET or RETSK
+ if ((opcode & 0xfe) == 0x48)
+ return true;
+
+ return false;
+}
+
/***************************************************************************
TIMER CALLBACKS
***************************************************************************/
@@ -931,10 +977,6 @@ void cop400_cpu_device::serial_tick()
OUT_SO(BIT(EN, 3));
- // serial clock
-
- OUT_SK(SKL);
-
// serial input
m_si <<= 1;
@@ -971,17 +1013,6 @@ void cop400_cpu_device::serial_tick()
OUT_SO(0);
}
- // serial clock
-
- if (SKL)
- {
- OUT_SK(1); // SYNC
- }
- else
- {
- OUT_SK(0);
- }
-
// serial input
SIO = ((SIO << 1) | IN_SI()) & 0x0f;
@@ -992,7 +1023,8 @@ void cop400_cpu_device::counter_tick()
{
T++;
- if (!T) {
+ if (!T)
+ {
m_skt_latch = 1;
m_idle = false;
}
@@ -1000,12 +1032,9 @@ void cop400_cpu_device::counter_tick()
void cop400_cpu_device::inil_tick()
{
- uint8_t in;
- int i;
-
- in = IN_IN();
+ uint8_t in = IN_IN();
- for (i = 0; i < 4; i++)
+ for (int i = 0; i < 4; i++)
{
m_in[i] = (m_in[i] << 1) | BIT(in, i);
@@ -1024,17 +1053,9 @@ void cop400_cpu_device::device_timer(emu_timer &timer, device_timer_id id, int p
{
switch (id)
{
- case TIMER_SERIAL:
- serial_tick();
- break;
-
case TIMER_COUNTER:
counter_tick();
break;
-
- case TIMER_INIL:
- inil_tick();
- break;
}
}
@@ -1059,10 +1080,6 @@ void cop400_cpu_device::device_start()
m_write_sk.resolve_safe();
m_read_cko.resolve_safe(0);
- /* allocate serial timer */
- m_serial_timer = timer_alloc(TIMER_SERIAL);
- m_serial_timer->adjust(attotime::zero, 0, attotime::from_ticks(m_cki, clock()));
-
/* allocate counter timer */
m_counter_timer = nullptr;
if (m_has_counter)
@@ -1071,14 +1088,6 @@ void cop400_cpu_device::device_start()
m_counter_timer->adjust(attotime::zero, 0, attotime::from_ticks(m_cki * 4, clock()));
}
- /* allocate IN latch timer */
- m_inil_timer = nullptr;
- if (m_has_inil)
- {
- m_inil_timer = timer_alloc(TIMER_INIL);
- m_inil_timer->adjust(attotime::zero, 0, attotime::from_ticks(m_cki, clock()));
- }
-
/* register for state saving */
save_item(NAME(m_pc));
save_item(NAME(m_prevpc));
@@ -1102,6 +1111,8 @@ void cop400_cpu_device::device_start()
save_item(NAME(m_in));
save_item(NAME(m_halt));
save_item(NAME(m_idle));
+ save_item(NAME(m_opcode));
+ save_item(NAME(m_second_byte));
// setup debugger state display
offs_t pc_mask = m_program->addrmask();
@@ -1112,18 +1123,20 @@ void cop400_cpu_device::device_start()
state_add(COP400_PC, "PC", m_pc).mask(pc_mask);
state_add(COP400_SA, "SA", m_sa).mask(pc_mask);
state_add(COP400_SB, "SB", m_sb).mask(pc_mask);
- if (!(m_featuremask & COP410_FEATURE)) {
+ if (!(m_featuremask & COP410_FEATURE))
state_add(COP400_SC, "SC", m_sc).mask(pc_mask);
- }
state_add(COP400_B, "B", m_b);
state_add(COP400_A, "A", m_a).mask(0xf);
+ state_add(COP400_M, "M", m_temp_m).mask(0xf).callimport().callexport();
state_add(COP400_G, "G", m_g).mask(0xf);
state_add(COP400_Q, "Q", m_q);
- state_add(COP400_SIO, "SIO", m_sio).mask(0xf);
+ state_add(COP400_SIO, "SIO", m_sio).mask(0xf).formatstr("%4s");
state_add(COP400_EN, "EN", m_en).mask(0xf);
- if (m_featuremask & COP424C_FEATURE) {
+ if (m_has_counter)
state_add(COP400_T, "T", m_t);
- }
+#if COP_DEBUG_SKIP
+ state_add(COP400_SKIP, "SKIP", m_skip).mask(1);
+#endif
m_icountptr = &m_icount;
@@ -1133,12 +1146,14 @@ void cop400_cpu_device::device_start()
m_sc = 0;
m_sio = 0;
m_flags = 0;
+ m_temp_m = 0;
m_il = 0;
m_in[0] = m_in[1] = m_in[2] = m_in[3] = 0;
m_si = 0;
m_skip_lbi = 0;
m_last_skip = false;
m_skip = false;
+ m_opcode = 0x44;
}
@@ -1154,55 +1169,80 @@ void cop400_cpu_device::device_reset()
C = 0;
OUT_D(0);
EN = 0;
+ OUT_L(m_read_l_tristate(0, 0xff));
WRITE_G(0);
SKL = 1;
+ OUT_SK(0);
T = 0;
m_skt_latch = 1;
m_halt = false;
m_idle = false;
+ m_second_byte = false;
}
/***************************************************************************
EXECUTION
***************************************************************************/
-uint8_t cop400_cpu_device::fetch()
+void cop400_cpu_device::skip()
{
- m_icount--;
+ // skip the next instruction (TODO: this flag can become an output)
+ m_skip = true;
+}
- return ROM(PC++);
+void cop400_cpu_device::sk_update()
+{
+ // update SK output after XAS or LEI
+ if (BIT(EN, 0))
+ {
+ // SK = SKL when EN0 = 1 (binary counter mode)
+ OUT_SK(SKL);
+ }
+ else
+ {
+ // SK = clock or 0 when EN0 = 0 (shift register mode)
+ OUT_SK(0);
+ }
}
void cop400_cpu_device::execute_run()
{
do
{
- if (!m_skip) {
+ if (!m_second_byte && (!m_skip || COP_DEBUG_SKIP))
+ {
// debugger hook
m_prevpc = PC;
debugger_instruction_hook(this, PC);
}
// halt logic
- if (m_cko == COP400_CKO_HALT_IO_PORT) {
+ if (m_cko == COP400_CKO_HALT_IO_PORT)
m_halt = IN_CKO();
- }
- if (m_halt || m_idle) {
- m_icount--;
- continue;
- }
+ if (!m_halt && !m_idle)
+ {
+ if (!BIT(EN, 0) && SKL)
+ {
+ // Sync pulse actually has a 50% duty cycle, coinciding with address latch for external program
+ // This implementation, however, is good enough to interface with 93CXX serial EEPROMs
+ OUT_SK(0);
+ OUT_SK(1);
+ }
+
+ // fetch opcode/operand
+ uint8_t operand = ROM(PC++);
+ if (!m_second_byte)
+ m_opcode = operand;
- // fetch opcode
- uint8_t opcode = fetch();
- cop400_opcode_func function = m_opcode_map[opcode];
+ // check for interrupt when all successive transfer of control instructions and successive LBIs have been completed
+ if (!m_second_byte && BIT(EN, 1) && BIT(IL, 1) && !is_control_transfer(m_opcode) && !m_skip_lbi)
+ {
+ // acknowledge interrupt
+ IL &= ~2;
- // check for interrupt
- if (BIT(EN, 1) && BIT(IL, 1)) {
- // all successive transfer of control instructions and successive LBIs have been completed
- if ((function != OP(jp)) && (function != OP(jmp)) && (function != OP(jsr)) && !m_skip_lbi) {
// store skip logic
m_last_skip = m_skip;
m_skip = false;
@@ -1213,31 +1253,53 @@ void cop400_cpu_device::execute_run()
// jump to interrupt service routine
PC = 0x0ff;
- // disable interrupt
- EN &= ~0x02;
+ // disable interrupts
+ EN &= ~2;
}
-
- IL &= ~2;
- }
-
- if (m_skip) {
- // skip instruction
- if (m_InstLen[opcode] == 2) {
- // fetch second byte
- opcode = fetch();
+ else if (!m_second_byte && m_InstLen[m_opcode] > 1)
+ {
+ m_second_byte = true;
+ }
+ else if (m_skip)
+ {
+ // finish skipping and execute next instruction
+ m_skip = false;
+ m_second_byte = false;
+ }
+ else
+ {
+ cop400_opcode_func function = m_opcode_map[m_opcode];
+
+ if (!m_second_byte && (function == OP(jid) || function == OP(lqid)))
+ {
+ // JID and LQID must first transfer control and then fetch the operand
+ // LQID saves the old program counter on the stack; JID doesn't and just jumps again
+ if (function == OP(lqid))
+ PUSH(PC);
+
+ // jump within page to operand at A/M
+ PC = (PC & 0x700) | (A << 4) | RAM_R(B);
+ m_second_byte = true;
+ }
+ else
+ {
+ // execute instruction
+ (this->*(function))(operand);
+
+ // LBI skip logic
+ if (m_skip_lbi > 0)
+ m_skip_lbi--;
+
+ // ready for the next instruction
+ m_second_byte = false;
+ }
}
- m_skip = false;
- continue;
- }
-
- // execute instruction
- (this->*(function))(opcode);
-
- // LBI skip logic
- if (m_skip_lbi > 0) {
- m_skip_lbi--;
+ serial_tick();
+ if (m_has_inil)
+ inil_tick();
}
+ m_icount--;
} while (m_icount > 0);
}
@@ -1256,6 +1318,11 @@ void cop400_cpu_device::state_import(const device_state_entry &entry)
m_c = BIT(m_flags, 1);
m_skl = BIT(m_flags, 0);
break;
+
+ case COP400_M:
+ auto dis = machine().disable_side_effect();
+ RAM_W(B, m_temp_m);
+ break;
}
}
@@ -1266,6 +1333,11 @@ void cop400_cpu_device::state_export(const device_state_entry &entry)
case STATE_GENFLAGS:
m_flags = (m_skt_latch ? 0x04 : 0x00) | (m_c ? 0x02 : 0x00) | (m_skl ? 0x01 : 0x00);
break;
+
+ case COP400_M:
+ auto dis = machine().disable_side_effect();
+ m_temp_m = RAM_R(B);
+ break;
}
}
@@ -1279,6 +1351,22 @@ void cop400_cpu_device::state_string_export(const device_state_entry &entry, std
m_skl ? 'S' : '.',
m_skt_latch ? 'T' : '.');
break;
+ case COP400_SIO:
+ if (BIT(EN, 0))
+ {
+ // display counter in hex
+ str = string_format("%X ", m_sio);
+ }
+ else
+ {
+ // display shift register in binary
+ str = string_format("%d%d%d%d",
+ BIT(m_sio, 3),
+ BIT(m_sio, 2),
+ BIT(m_sio, 1),
+ BIT(m_sio, 0));
+ }
+ break;
}
}