From 5864e92aa5d81797214e1125d558d181b74dbe29 Mon Sep 17 00:00:00 2001 From: arbee Date: Wed, 24 Jul 2019 22:54:45 -0400 Subject: f2mc16: First pass with working IRQs. [R. Belmont] --- src/devices/cpu/f2mc16/f2mc16.cpp | 146 +++++++++++++++++++++++++++++++--- src/devices/cpu/f2mc16/f2mc16.h | 23 ++++++ src/devices/cpu/f2mc16/f2mc16dasm.cpp | 2 +- src/devices/cpu/f2mc16/mb9061x.cpp | 103 ++++++++++++++++++++++++ src/devices/cpu/f2mc16/mb9061x.h | 21 +++++ 5 files changed, 285 insertions(+), 10 deletions(-) diff --git a/src/devices/cpu/f2mc16/f2mc16.cpp b/src/devices/cpu/f2mc16/f2mc16.cpp index fb4dbd10fff..4707c38aaf2 100644 --- a/src/devices/cpu/f2mc16/f2mc16.cpp +++ b/src/devices/cpu/f2mc16/f2mc16.cpp @@ -105,6 +105,12 @@ void f2mc16_device::device_reset() m_dpr = 0x01; m_dtb = 0; + for (int &entry : m_vector_level) + { + entry = 7; + } + m_outstanding_irqs = 0; + m_pc = read_16_vector(0xffffdc); m_pcb = read_8_vector(0xffffde); @@ -205,12 +211,23 @@ void f2mc16_device::execute_run() { while (m_icount > 0) { - u8 opcode = read_8((m_pcb<<16) | m_pc); + if (m_outstanding_irqs) + { + int cpulevel = m_ps >> 13; - debugger_instruction_hook((m_pcb<<16) | m_pc); + for (int irq = 0; irq < 256; irq++) + { + if (m_vector_level[irq] < cpulevel) + { + take_irq(irq, m_vector_level[irq]); + break; + } + } + } - // m_icount--; + u8 opcode = read_8((m_pcb<<16) | m_pc); + debugger_instruction_hook((m_pcb<<16) | m_pc); switch (opcode) { case 0x00: // NOP @@ -322,8 +339,39 @@ void f2mc16_device::execute_run() m_icount -= 2; break; + // ASRW A case 0x0e: - // stream << "ASRW A"; + m_tmp16 = m_acc & 0xffff; + + // T is set if either carry or T are set beforehand + if ((m_ps & F_C) || (m_ps & F_T)) + { + m_ps |= F_T; + } + // C becomes the previous LSB + m_ps &= ~F_C; + if (m_tmp16 & 1) + { + m_ps |= F_C; + } + + if (m_tmp16 & 0x8000) + { + m_tmp16 >>= 1; + m_tmp16 |= 0x8000; + } + else + { + m_tmp16 >>= 1; + m_tmp16 &= ~0x8000; + } + setNZ_16(m_tmp16); + + m_acc &= 0xffff0000; + m_acc |= m_tmp16; + + m_pc++; + m_icount -= 2; break; // LSRW A @@ -633,8 +681,13 @@ void f2mc16_device::execute_run() m_icount -= 2; break; + // NOT A case 0x37: - // stream << "NOT A"; + m_acc ^= 0xff; + setNZ_8(m_acc & 0xff); + m_ps &= ~F_V; + m_pc++; + m_icount -= 2; break; // ADDW A, #imm16 @@ -697,8 +750,13 @@ void f2mc16_device::execute_run() m_icount -= 2; break; + // NOTW A case 0x3f: - // stream << "NOTW A"; + m_acc ^= 0xffff; + setNZ_16(m_acc & 0xffff); + m_ps &= ~F_V; + m_pc++; + m_icount -= 2; break; // MOV A, dir @@ -1004,8 +1062,44 @@ void f2mc16_device::execute_run() // stream << "RET"; break; + // RETI case 0x6b: - // stream << "RETI"; + // there's an IRQ chaining facility, let's do it + if (m_outstanding_irqs) + { + int cpulevel = m_ps >> 13; + + for (int irq = 0; irq < 256; irq++) + { + if (m_vector_level[irq] < cpulevel) + { + m_ps = read_16((m_ssb << 16) | m_ssp); + m_ps |= F_S; + m_ps &= ~0x7000; + m_ps |= (m_vector_level[irq] & 7) << 13; + + u32 uVecAddr = 0xfffffc - (irq * 4); + m_pc = read_16(uVecAddr); + m_pcb = read_8(uVecAddr + 2); + break; + } + } + } + else + { + m_ps = pull_16_ssp(); + m_pc = pull_16_ssp(); + m_tmp16 = pull_16_ssp(); + m_pcb = m_tmp16 & 0xff; + m_dtb = m_tmp16 >> 8; + m_tmp16 = pull_16_ssp(); + m_adb = m_tmp16 & 0xff; + m_dpr = m_tmp16 >> 8; + m_acc = 0; + m_acc = pull_16_ssp(); + m_acc |= (pull_16_ssp() << 16); + m_icount -= 17; + } break; case 0x6c: // bit operation instructions @@ -2111,10 +2205,9 @@ void f2mc16_device::opcodes_ea76(u8 operand) m_icount -= 10; break; - // ORW A, @RWx + // ORW A, RWx case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7: m_tmp16 = read_rwX(operand & 7); - m_tmp16 = read_16(getRWbank(operand & 7, m_tmp16)); m_acc |= m_tmp16; setNZ_16(m_acc & 0xffff); m_ps &= ~F_V; @@ -2169,3 +2262,38 @@ void f2mc16_device::opcodes_ea78(u8 operand) void f2mc16_device::execute_set_input(int inputnum, int state) { } + +void f2mc16_device::set_irq(int vector, int level) +{ + m_outstanding_irqs++; + m_vector_level[vector] = level; + //printf("set_irq: vec %d level %d\n", vector, level); +} + +void f2mc16_device::clear_irq(int vector) +{ + m_outstanding_irqs--; + m_vector_level[vector] = 7; + //printf("clear_irq: vec %d\n", vector); +} + +// note: this function must not use m_tmp16 unless you change RETI +void f2mc16_device::take_irq(int vector, int level) +{ + //printf("take_irq: vector %d, level %d, old PC = %02x%04x\n", vector, level, m_pcb, m_pc); + push_16_ssp(m_acc>>16); + push_16_ssp(m_acc & 0xffff); + push_16_ssp((m_dpr<<8) | m_adb); + push_16_ssp((m_dtb<<8) | m_pcb); + push_16_ssp(m_pc); + push_16_ssp(m_ps); + + m_ps |= F_S; + m_ps &= ~0x7000; + m_ps |= (level & 7) << 13; + + u32 uVecAddr = 0xfffffc - (vector * 4); + m_pc = read_16(uVecAddr); + m_pcb = read_8(uVecAddr + 2); + //printf("New PC = %02x%04x\n", m_pcb, m_pc); +} diff --git a/src/devices/cpu/f2mc16/f2mc16.h b/src/devices/cpu/f2mc16/f2mc16.h index edc0dda4148..e4c57953ea4 100644 --- a/src/devices/cpu/f2mc16/f2mc16.h +++ b/src/devices/cpu/f2mc16/f2mc16.h @@ -14,6 +14,8 @@ class f2mc16_device : public cpu_device { public: + friend class mb9061x_device; + enum { F2MC16_PC, F2MC16_PS, F2MC16_USP, F2MC16_SSP, F2MC16_ACC, @@ -223,6 +225,12 @@ private: } } + inline void push_16_ssp(u16 val) + { + m_ssp-=2; + write_16((m_ssb << 16) | m_ssp, val); + } + inline void push_32(u32 val) { if (m_ps & F_S) @@ -271,6 +279,14 @@ private: return rv; } + inline u16 pull_16_ssp() + { + u16 rv = read_16((m_ssb << 16) | m_ssp); + m_ssp += 2; + + return rv; + } + inline u32 pull_32() { u32 rv = 0; @@ -431,6 +447,13 @@ private: void opcodes_ea76(u8 operand); void opcodes_ea77(u8 operand); void opcodes_ea78(u8 operand); + + void set_irq(int vector, int level); + void clear_irq(int vector); + void take_irq(int vector, int level); + + int m_vector_level[256]; + int m_outstanding_irqs; }; DECLARE_DEVICE_TYPE(F2MC16, f2mc16_device) diff --git a/src/devices/cpu/f2mc16/f2mc16dasm.cpp b/src/devices/cpu/f2mc16/f2mc16dasm.cpp index 12799dec0f5..95b287b625e 100644 --- a/src/devices/cpu/f2mc16/f2mc16dasm.cpp +++ b/src/devices/cpu/f2mc16/f2mc16dasm.cpp @@ -1192,7 +1192,7 @@ offs_t f2mc16_disassembler::disassemble(std::ostream &stream, offs_t pc, const d break; case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7: - util::stream_format(stream, "ORW A, @RW%d", (operand & 0x1)); + util::stream_format(stream, "ORW A, RW%d", (operand & 0x1)); bytes = 2; break; diff --git a/src/devices/cpu/f2mc16/mb9061x.cpp b/src/devices/cpu/f2mc16/mb9061x.cpp index 7576f8287ea..aea77406f79 100644 --- a/src/devices/cpu/f2mc16/mb9061x.cpp +++ b/src/devices/cpu/f2mc16/mb9061x.cpp @@ -39,6 +39,9 @@ mb9061x_device::mb9061x_device(const machine_config &mconfig, device_type type, void mb9061x_device::device_start() { f2mc16_device::device_start(); + + m_tbtc_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mb9061x_device::tbtc_tick), this)); + m_tbtc_timer->adjust(attotime::never); } @@ -65,6 +68,8 @@ void mb9061x_device::execute_set_input(int inputnum, int state) /* MB90610 - "Evaluation device" with extra RAM */ void mb90610_device::mb90610_map(address_map &map) { + map(0x00a9, 0x00a9).rw(FUNC(mb9061x_device::tbtc_r), FUNC(mb9061x_device::tbtc_w)); + map(0x00b0, 0x00bf).rw(FUNC(mb9061x_device::intc_r), FUNC(mb9061x_device::intc_w)); map(0x0100, 0x10ff).ram(); // 4K of internal RAM from 0x100 to 0x1100 } @@ -78,9 +83,107 @@ mb90610_device::mb90610_device(const machine_config &mconfig, device_type type, { } +/* TBTC: timebase counter */ +enum +{ + TBTC_TEST = 0x80, // test, must always write 1 + TBTC_TBIE = 0x10, // interrupt enable + TBTC_TBOF = 0x08, // expire flag + TBTC_TBR = 0x04, // write 0 to restart + TBTC_TBC1 = 0x02, // rate select bit 1 + TBTC_TBC0 = 0x01 // rate select bit 0 +}; + +READ8_MEMBER(mb9061x_device::tbtc_r) +{ + return m_tbtc; +} + +WRITE8_MEMBER(mb9061x_device::tbtc_w) +{ + static const float periods[4] = { 1.024, 4.096, 16.384, 131.072 }; + + //printf("%02x to TBTC\n", data); + if ((!(data & TBTC_TBR)) || ((data & (TBTC_TBC1|TBTC_TBC0)) != (m_tbtc & (TBTC_TBC1|TBTC_TBC0)))) + { + m_tbtc_timer->adjust(attotime(0, ATTOSECONDS_IN_MSEC(periods[data & (TBTC_TBC1|TBTC_TBC0)]))); + + if (!(data & TBTC_TBR)) + { + intc_clear_irq(11, 0x22); + } + } + + if (!(data & TBTC_TBOF) && (m_tbtc & TBTC_TBOF)) + { + intc_clear_irq(11, 0x22); + } + + m_tbtc = data; +} + +/* INTC: Interrupt controller */ +TIMER_CALLBACK_MEMBER(mb9061x_device::tbtc_tick) +{ + //printf("TBTC tick\n"); + m_tbtc_timer->adjust(attotime::never); + m_tbtc |= TBTC_TBOF; + if (m_tbtc & TBTC_TBIE) + { + intc_trigger_irq(11, 0x22); + } +} + +READ8_MEMBER(mb9061x_device::intc_r) +{ + return m_intc[offset]; +} + +WRITE8_MEMBER(mb9061x_device::intc_w) +{ + //printf("INTC ICR %d to %02x\n", offset, data); + m_intc[offset] = data; +} + +void mb9061x_device::intc_trigger_irq(int icr, int vector) +{ + int level = m_intc[icr] & 7; + + //printf("trigger_irq: ICR %d, level %d\n", icr, level); + + // Extended Intelligent I/O Service? + if (m_intc[icr] & 8) + { + fatalerror("MB9061X: ICR %d (vector %x) calls for EI2OS, not implemented\n", icr, vector); + } + else + { + if (level != 7) + { + set_irq(vector, level); + } + } +} + +void mb9061x_device::intc_clear_irq(int icr, int vector) +{ + int level = m_intc[icr] & 7; + + // Make sure this isn't the Extended Intelligent I/O Service + if (!(m_intc[icr] & 8)) + { + if (level != 7) + { + clear_irq(vector); + } + } +} + /* MB90611 - Production version of this series */ void mb90611_device::mb90611_map(address_map &map) { + map(0x00a9, 0x00a9).rw(FUNC(mb9061x_device::tbtc_r), FUNC(mb9061x_device::tbtc_w)); + map(0x00b0, 0x00bf).rw(FUNC(mb9061x_device::intc_r), FUNC(mb9061x_device::intc_w)); map(0x0100, 0x04ff).ram(); // 1K of internal RAM from 0x100 to 0x500 } diff --git a/src/devices/cpu/f2mc16/mb9061x.h b/src/devices/cpu/f2mc16/mb9061x.h index 7a463577402..e79d0202601 100644 --- a/src/devices/cpu/f2mc16/mb9061x.h +++ b/src/devices/cpu/f2mc16/mb9061x.h @@ -25,6 +25,12 @@ class mb9061x_device : public f2mc16_device public: const address_space_config m_program_config; + // interrupts handled by the interrupt controller + enum + { + ICR0 = 0, ICR1, ICR2, ICR3, ICR4, ICR5, ICR6, ICR7, ICR8, ICR9, ICR10, ICR11, ICR12, ICR13, ICR14, ICR15 + }; + protected: // construction/destruction mb9061x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor internal_map); @@ -36,6 +42,21 @@ protected: virtual space_config_vector memory_space_config() const override; private: + // TBC + TIMER_CALLBACK_MEMBER(tbtc_tick); + READ8_MEMBER(tbtc_r); + WRITE8_MEMBER(tbtc_w); + + // INTC + READ8_MEMBER(intc_r); + WRITE8_MEMBER(intc_w); + void intc_trigger_irq(int icr, int vector); + void intc_clear_irq(int icr, int vector); + + u8 m_tbtc; + emu_timer *m_tbtc_timer; + + u8 m_intc[0x10]; }; class mb90610_device : public mb9061x_device -- cgit v1.2.3