summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/t11/t11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/t11/t11.cpp')
-rw-r--r--src/devices/cpu/t11/t11.cpp409
1 files changed, 325 insertions, 84 deletions
diff --git a/src/devices/cpu/t11/t11.cpp b/src/devices/cpu/t11/t11.cpp
index a771b61b241..f347f655025 100644
--- a/src/devices/cpu/t11/t11.cpp
+++ b/src/devices/cpu/t11/t11.cpp
@@ -14,7 +14,6 @@
#include "emu.h"
#include "t11.h"
#include "t11dasm.h"
-#include "debugger.h"
/*************************************
@@ -37,28 +36,47 @@
DEFINE_DEVICE_TYPE(T11, t11_device, "t11", "DEC T11")
+DEFINE_DEVICE_TYPE(K1801VM1, k1801vm1_device, "k1801vm1", "K1801VM1")
DEFINE_DEVICE_TYPE(K1801VM2, k1801vm2_device, "k1801vm2", "K1801VM2")
+k1801vm1_device::k1801vm1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : t11_device(mconfig, K1801VM1, tag, owner, clock)
+ , z80_daisy_chain_interface(mconfig, *this)
+{
+ c_insn_set = IS_LEIS | IS_MXPS | IS_VM1;
+}
+
k1801vm2_device::k1801vm2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: t11_device(mconfig, K1801VM2, tag, owner, clock)
+ , z80_daisy_chain_interface(mconfig, *this)
{
+ c_insn_set = IS_LEIS | IS_EIS | IS_MXPS | IS_VM2;
}
t11_device::t11_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config("program", ENDIANNESS_LITTLE, 16, 16, 0)
, c_initial_mode(0)
+ , m_cp_state(0)
+ , m_vec_active(false)
+ , m_pf_active(false)
+ , m_berr_active(false)
+ , m_hlt_active(false)
, m_out_reset_func(*this)
+ , m_in_iack_func(*this, 0) // default vector (T-11 User's Guide, p. A-11)
{
m_program_config.m_is_octal = true;
- memset(m_reg, 0x00, sizeof(m_reg));
- memset(&m_psw, 0x00, sizeof(m_psw));
+ for (auto &reg : m_reg)
+ reg.d = 0;
+ m_psw.d = 0;
+ m_ppc.d = 0;
}
t11_device::t11_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: t11_device(mconfig, T11, tag, owner, clock)
{
+ c_insn_set = IS_LEIS | IS_MFPT | IS_MXPS | IS_T11;
}
device_memory_interface::space_config_vector t11_device::memory_space_config() const
@@ -77,8 +95,7 @@ device_memory_interface::space_config_vector t11_device::memory_space_config() c
int t11_device::ROPCODE()
{
- PC &= 0xfffe;
- int val = m_cache->read_word(PC);
+ int val = m_cache.read_word(PC & 0xfffe);
PC += 2;
return val;
}
@@ -86,25 +103,25 @@ int t11_device::ROPCODE()
int t11_device::RBYTE(int addr)
{
- return m_program->read_byte(addr);
+ return m_program.read_byte(addr);
}
void t11_device::WBYTE(int addr, int data)
{
- m_program->write_byte(addr, data);
+ m_program.write_byte(addr, data);
}
int t11_device::RWORD(int addr)
{
- return m_program->read_word(addr & 0xfffe);
+ return m_program.read_word(addr & 0xfffe);
}
void t11_device::WWORD(int addr, int data)
{
- m_program->write_word(addr & 0xfffe, data);
+ m_program.write_word(addr & 0xfffe, data);
}
@@ -148,6 +165,8 @@ int t11_device::POP()
#define GET_V (PSW & VFLAG)
#define GET_Z (PSW & ZFLAG)
#define GET_N (PSW & NFLAG)
+#define GET_T (PSW & (1 << 4))
+#define GET_I (PSW & (1 << 7))
/* clears flags */
#define CLR_C (PSW &= ~CFLAG)
@@ -177,56 +196,210 @@ struct irq_table_entry
static const struct irq_table_entry irq_table[] =
{
- { 0<<5, 0x00 },
- { 4<<5, 0x38 },
- { 4<<5, 0x34 },
- { 4<<5, 0x30 },
- { 5<<5, 0x5c },
- { 5<<5, 0x58 },
- { 5<<5, 0x54 },
- { 5<<5, 0x50 },
- { 6<<5, 0x4c },
- { 6<<5, 0x48 },
- { 6<<5, 0x44 },
- { 6<<5, 0x40 },
- { 7<<5, 0x6c },
- { 7<<5, 0x68 },
- { 7<<5, 0x64 },
- { 7<<5, 0x60 }
+ { 0<<5, 0000 },
+ { 4<<5, 0070 },
+ { 4<<5, 0064 },
+ { 4<<5, 0060 },
+ { 5<<5, 0134 },
+ { 5<<5, 0130 },
+ { 5<<5, 0124 },
+ { 5<<5, 0120 },
+ { 6<<5, 0114 },
+ { 6<<5, 0110 },
+ { 6<<5, 0104 },
+ { 6<<5, 0100 },
+ { 7<<5, 0154 },
+ { 7<<5, 0150 },
+ { 7<<5, 0144 },
+ { 7<<5, 0140 }
};
-void t11_device::t11_check_irqs()
+// PSW7 masks external interrupts (except IRQ1) -- IRQ2, IRQ3, VIRQ
+// PSW11 also masks IRQ1. PSW10 also masks ACLO.
+void k1801vm1_device::t11_check_irqs()
{
- const struct irq_table_entry *irq = &irq_table[m_irq_state & 15];
- int priority = PSW & 0xe0;
+ // 2. bus error on vector fetch; nm, vec 160012
+ // 3. double bus error; nm, vec 160006
+ // 4. bus error; PSW11, PSW10
+ if (m_bus_error)
+ {
+ m_bus_error = false;
+ m_mcir = MCIR_IRQ;
+ m_vsel = T11_TIMEOUT;
+ }
+ // 5. illegal insn; nm
+ else if (m_mcir == MCIR_ILL)
+ {
+ take_interrupt(m_vsel);
+ }
+ // 6. trace trap; WCPU
+ else if (m_trace_trap && m_mcir == MCIR_NONE) // allow trap_to() to execute first
+ {
+ if (GET_T)
+ {
+ m_mcir = MCIR_IRQ;
+ m_vsel = T11_BPT;
+ }
+ else
+ m_trace_trap = false;
+ }
+ else if (GET_T)
+ {
+ m_trace_trap = true;
+ }
+ // 7. power fail (ACLO pin); PSW10
+ else if (m_power_fail)
+ {
+ m_mcir = MCIR_IRQ;
+ m_vsel = T11_PWRFAIL;
+ }
+ // 8. external HALT (nIRQ1 pin); PSW11, PSW10
+ else if (m_hlt_active)
+ {
+ m_hlt_active = 0;
+ m_mcir = MCIR_HALT;
+ m_vsel = VM1_HALT;
+ }
+ // 9. internal timer, vector 0270; PSW7, PSW10
+ // 10. line clock (nIRQ2 pin); PSW7, PSW10
+ else if (BIT(m_cp_state, 2) && !GET_I)
+ {
+ m_mcir = MCIR_IRQ;
+ m_vsel = VM1_EVNT;
+ }
+ // 11. nIRQ3 pin; PSW7, PSW10
+ else if (BIT(m_cp_state, 3) && !GET_I)
+ {
+ m_mcir = MCIR_IRQ;
+ m_vsel = VM1_IRQ3;
+ }
+ // 12. nVIRQ pin; PSW7, PSW10
+ else if (m_vec_active && !GET_I)
+ {
+ device_z80daisy_interface *intf = daisy_get_irq_device();
+ int vec = (intf != nullptr) ? intf->z80daisy_irq_ack() : m_in_iack_func(0);
+ if (vec == -1 || vec == 0)
+ {
+ m_vec_active = 0;
+ return;
+ }
+ m_mcir = MCIR_IRQ;
+ m_vsel = vec;
+ }
- /* compare the priority of the interrupt to the PSW */
- if (irq->priority > priority)
+ switch (m_mcir)
{
- int vector = irq->vector;
- int new_pc, new_psw;
+ case MCIR_SET:
+ if (m_vsel >= 0160000)
+ take_interrupt_halt(m_vsel);
+ else
+ take_interrupt(m_vsel);
+ break;
+
+ case MCIR_IRQ:
+ take_interrupt(m_vsel);
+ break;
+
+ case MCIR_HALT:
+ take_interrupt_halt(m_vsel);
+ break;
+ }
+
+ m_mcir = MCIR_NONE;
+}
+
+void k1801vm1_device::take_interrupt_halt(uint16_t vector)
+{
+ // vectors in HALT mode are word (not doubleworld) aligned
+ assert((vector & 1) == 0);
+
+ // enter HALT mode
+ WWORD(VM1_SEL1, RWORD(VM1_SEL1) | SEL1_HALT);
+
+ // push the old state, set the new one
+ WWORD(VM1_STACK, PC);
+ WWORD(VM1_STACK + 2, PSW);
+ PCD = RWORD(vector);
+ PSW = RWORD(vector + 2);
- /* call the callback; if we don't get -1 back, use the return value as our vector */
- int new_vector = standard_irq_callback(m_irq_state & 15);
- if (new_vector != -1)
- vector = new_vector;
+ // count cycles and clear the WAIT flag
+ m_icount -= 114;
+ m_wait_state = 0;
+}
- /* fetch the new PC and PSW from that vector */
- assert((vector & 3) == 0);
- new_pc = RWORD(vector);
- new_psw = RWORD(vector + 2);
+void t11_device::t11_check_irqs()
+{
+ // HLT is nonmaskable
+ if (m_ext_halt)
+ {
+ m_ext_halt = false;
- /* push the old state, set the new one */
+ // push the old state, set the new one
PUSH(PSW);
PUSH(PC);
- PCD = new_pc;
- PSW = new_psw;
- t11_check_irqs();
+ PCD = m_initial_pc + 4;
+ PSW = 0340;
- /* count cycles and clear the WAIT flag */
+ // count cycles and clear the WAIT flag
m_icount -= 114;
m_wait_state = 0;
+
+ return;
}
+
+ // non-maskable hardware traps
+ if (m_bus_error)
+ {
+ m_bus_error = false;
+ take_interrupt(T11_TIMEOUT);
+ return;
+ }
+ else if (m_power_fail)
+ {
+ m_power_fail = false;
+ take_interrupt(T11_PWRFAIL);
+ return;
+ }
+
+ // compare the priority of the CP interrupt to the PSW
+ const struct irq_table_entry *irq = &irq_table[m_cp_state & 15];
+ if (irq->priority > (PSW & 0340))
+ {
+ // call the callback
+ standard_irq_callback(m_cp_state & 15, PC);
+
+ // T11 encodes the interrupt level on DAL<12:8>
+ uint8_t iaddr = bitswap<4>(~m_cp_state & 15, 0, 1, 2, 3);
+ if (!m_vec_active)
+ iaddr |= 16;
+
+ // vector is input on DAL<7:2>
+ uint8_t vector = m_in_iack_func(iaddr);
+
+ // nonvectored or vectored interrupt depending on VEC
+ if (BIT(iaddr, 4))
+ take_interrupt(irq->vector);
+ else
+ take_interrupt(vector & ~3);
+ }
+}
+
+void t11_device::take_interrupt(uint8_t vector)
+{
+ // fetch the new PC and PSW from that vector
+ assert((vector & 3) == 0);
+ uint16_t new_pc = RWORD(vector);
+ uint16_t new_psw = RWORD(vector + 2);
+
+ // push the old state, set the new one
+ PUSH(PSW);
+ PUSH(PC);
+ PCD = new_pc;
+ PSW = new_psw;
+
+ // count cycles and clear the WAIT flag
+ m_icount -= 114;
+ m_wait_state = 0;
}
@@ -260,9 +433,8 @@ void t11_device::device_start()
};
m_initial_pc = initial_pc[c_initial_mode >> 13];
- m_program = &space(AS_PROGRAM);
- m_cache = m_program->cache<1, 0, ENDIANNESS_LITTLE>();
- m_out_reset_func.resolve_safe();
+ space(AS_PROGRAM).cache(m_cache);
+ space(AS_PROGRAM).specific(m_program);
save_item(NAME(m_ppc.w.l));
save_item(NAME(m_reg[0].w.l));
@@ -276,21 +448,30 @@ void t11_device::device_start()
save_item(NAME(m_psw.w.l));
save_item(NAME(m_initial_pc));
save_item(NAME(m_wait_state));
- save_item(NAME(m_irq_state));
+ save_item(NAME(m_cp_state));
+ save_item(NAME(m_vec_active));
+ save_item(NAME(m_pf_active));
+ save_item(NAME(m_hlt_active));
+ save_item(NAME(m_power_fail));
+ save_item(NAME(m_ext_halt));
+ save_item(NAME(m_trace_trap));
+ save_item(NAME(m_check_irqs));
+ save_item(NAME(m_mcir));
+ save_item(NAME(m_vsel));
// Register debugger state
- state_add( T11_PC, "PC", m_reg[7].w.l).formatstr("%04X");
- state_add( T11_SP, "SP", m_reg[6].w.l).formatstr("%04X");
- state_add( T11_PSW, "PSW", m_psw.b.l).formatstr("%02X");
- state_add( T11_R0, "R0", m_reg[0].w.l).formatstr("%04X");
- state_add( T11_R1, "R1", m_reg[1].w.l).formatstr("%04X");
- state_add( T11_R2, "R2", m_reg[2].w.l).formatstr("%04X");
- state_add( T11_R3, "R3", m_reg[3].w.l).formatstr("%04X");
- state_add( T11_R4, "R4", m_reg[4].w.l).formatstr("%04X");
- state_add( T11_R5, "R5", m_reg[5].w.l).formatstr("%04X");
-
- state_add(STATE_GENPC, "GENPC", m_reg[7].w.l).noshow();
- state_add(STATE_GENPCBASE, "CURPC", m_ppc.w.l).noshow();
+ state_add( T11_PC, "PC", m_reg[7].w.l).formatstr("%06O");
+ state_add( T11_SP, "SP", m_reg[6].w.l).formatstr("%06O");
+ state_add( T11_PSW, "PSW", m_psw.b.l).formatstr("%03O");
+ state_add( T11_R0, "R0", m_reg[0].w.l).formatstr("%06O");
+ state_add( T11_R1, "R1", m_reg[1].w.l).formatstr("%06O");
+ state_add( T11_R2, "R2", m_reg[2].w.l).formatstr("%06O");
+ state_add( T11_R3, "R3", m_reg[3].w.l).formatstr("%06O");
+ state_add( T11_R4, "R4", m_reg[4].w.l).formatstr("%06O");
+ state_add( T11_R5, "R5", m_reg[5].w.l).formatstr("%06O");
+
+ state_add(STATE_GENPC, "GENPC", m_reg[7].w.l).formatstr("%06O").noshow();
+ state_add(STATE_GENPCBASE, "CURPC", m_ppc.w.l).formatstr("%06O").noshow();
state_add(STATE_GENFLAGS, "GENFLAGS", m_psw.b.l).formatstr("%8s").noshow();
set_icountptr(m_icount);
@@ -315,6 +496,25 @@ void t11_device::state_string_export(const device_state_entry &entry, std::strin
}
}
+void k1801vm1_device::state_string_export(const device_state_entry &entry, std::string &str) const
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ str = string_format("%c%c%c%c%c%c%c%c",
+ m_psw.b.l & 0x80 ? 'I':'.',
+ m_psw.b.l & 0x40 ? '?':'.',
+ m_psw.b.l & 0x20 ? '?':'.',
+ m_psw.b.l & 0x10 ? 'T':'.',
+ m_psw.b.l & 0x08 ? 'N':'.',
+ m_psw.b.l & 0x04 ? 'Z':'.',
+ m_psw.b.l & 0x02 ? 'V':'.',
+ m_psw.b.l & 0x01 ? 'C':'.'
+ );
+ break;
+ }
+}
+
void k1801vm2_device::state_string_export(const device_state_entry &entry, std::string &str) const
{
switch (entry.index())
@@ -344,27 +544,32 @@ void k1801vm2_device::state_string_export(const device_state_entry &entry, std::
void t11_device::device_reset()
{
- /* initial SP is 376 octal, or 0xfe */
- SP = 0x00fe;
+ // initial SP is 376 octal, or 0xfe
+ SP = 0376;
- /* initial PC comes from the setup word */
+ // initial PC comes from the setup word
PC = m_initial_pc;
- /* PSW starts off at highest priority */
- PSW = 0xe0;
+ // PSW starts off at highest priority
+ PSW = 0340;
- /* initialize the IRQ state */
- m_irq_state = 0;
-
- /* reset the remaining state */
- REGD(0) = 0;
- REGD(1) = 0;
- REGD(2) = 0;
- REGD(3) = 0;
- REGD(4) = 0;
- REGD(5) = 0;
- m_ppc.d = 0;
m_wait_state = 0;
+ m_power_fail = false;
+ m_bus_error = false;
+ m_ext_halt = false;
+ m_trace_trap = false;
+ m_check_irqs = false;
+}
+
+void k1801vm1_device::device_reset()
+{
+ t11_device::device_reset();
+
+ PC = RWORD(VM1_SEL1) & 0177400;
+ WWORD(VM1_SEL1, RWORD(VM1_SEL1) | SEL1_HALT);
+
+ m_mcir = MCIR_NONE;
+ m_vsel = 0;
}
void k1801vm2_device::device_reset()
@@ -384,11 +589,41 @@ void k1801vm2_device::device_reset()
void t11_device::execute_set_input(int irqline, int state)
{
- /* set the appropriate bit */
- if (state == CLEAR_LINE)
- m_irq_state &= ~(1 << irqline);
- else
- m_irq_state |= 1 << irqline;
+ switch (irqline)
+ {
+ case CP0_LINE:
+ case CP1_LINE:
+ case CP2_LINE:
+ case CP3_LINE:
+ // set the appropriate bit
+ if (state == CLEAR_LINE)
+ m_cp_state &= ~(1 << irqline);
+ else
+ m_cp_state |= 1 << irqline;
+ break;
+
+ case VEC_LINE:
+ m_vec_active = (state != CLEAR_LINE);
+ break;
+
+ case PF_LINE:
+ if (state != CLEAR_LINE && !m_pf_active)
+ m_power_fail = true;
+ m_pf_active = (state != CLEAR_LINE);
+ break;
+
+ case BUS_ERROR:
+ if (state != CLEAR_LINE && !m_berr_active)
+ m_bus_error = true;
+ m_berr_active = (state != CLEAR_LINE);
+ break;
+
+ case HLT_LINE:
+ if (state != CLEAR_LINE && !m_hlt_active)
+ m_ext_halt = true;
+ m_hlt_active = (state != CLEAR_LINE);
+ break;
+ }
}
@@ -405,11 +640,12 @@ void t11_device::execute_run()
if (m_wait_state)
{
+ debugger_wait_hook();
m_icount = 0;
return;
}
- do
+ while (m_icount > 0)
{
uint16_t op;
@@ -420,7 +656,12 @@ void t11_device::execute_run()
op = ROPCODE();
(this->*s_opcode_table[op >> 3])(op);
- } while (m_icount > 0);
+ if (m_check_irqs || m_trace_trap || GET_T)
+ {
+ m_check_irqs = false;
+ t11_check_irqs();
+ }
+ }
}
std::unique_ptr<util::disasm_interface> t11_device::create_disassembler()