// license:BSD-3-Clause // copyright-holders:R. Belmont /* Fujitsu Micro MB9061x Microcontroller Family Emulation by R. Belmont */ #include "emu.h" #include "mb9061x.h" //************************************************************************** // MACROS / CONSTANTS //************************************************************************** //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** DEFINE_DEVICE_TYPE(MB90610A, mb90610_device, "mb90610a", "Fujitsu MB90610A") DEFINE_DEVICE_TYPE(MB90611A, mb90611_device, "mb90611a", "Fujitsu MB90611A") //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // mb9061x_device - constructor //------------------------------------------------- mb9061x_device::mb9061x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor internal_map) : f2mc16_device(mconfig, type, tag, owner, clock), m_program_config("program", ENDIANNESS_LITTLE, 8, 24, 0, internal_map) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- 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); m_timer[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mb9061x_device::timer0_tick), this)); m_timer[0]->adjust(attotime::never); m_timer[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(mb9061x_device::timer1_tick), this)); m_timer[1]->adjust(attotime::never); } device_memory_interface::space_config_vector mb9061x_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config) }; } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void mb9061x_device::device_reset() { f2mc16_device::device_reset(); m_tbtc = 0; memset(m_timer_regs, 0, sizeof(m_timer_regs)); memset(m_event_count, 0, sizeof(m_event_count)); m_event_state[0] = m_event_state[1] = CLEAR_LINE; } 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 } mb90610_device::mb90610_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : mb90610_device(mconfig, MB90610A, tag, owner, clock) { } mb90610_device::mb90610_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : mb9061x_device(mconfig, type, tag, owner, clock, address_map_constructor(FUNC(mb90610_device::mb90610_map), this)) { } /* 16-bit preload timers with event count function */ enum { TCTH_CSL1 = 0x08, // clock source select bit 1 TCTH_CSL0 = 0x04, // clock source select bit 0 TCTH_MOD2 = 0x02, // mode bit 2 TCTH_MOD1 = 0x01, // mode bit 1 TCTL_MOD0 = 0x80, // mode bit 0 TCTL_OUTE = 0x40, // output enable TCTL_OUTL = 0x20, // output level TCTL_RELD = 0x10, // reload TCTL_INTE = 0x08, // IRQ enable TCTL_UF = 0x04, // expire flag TCTL_CNTE = 0x02, // enable counting TCTL_TRG = 0x01 // force a reload and start counting }; TIMER_CALLBACK_MEMBER(mb9061x_device::timer0_tick) { u8 ctl = m_timer_regs[0]; m_timer_regs[0] |= TCTL_UF; if (ctl & TCTL_INTE) { // printf("timer 0 IRQ\n"); intc_trigger_irq(9, 0x1d); } if (ctl & TCTL_RELD) { recalc_timer(0); m_timer[0]->adjust(attotime::from_hz(m_timer_hz[0])); } } TIMER_CALLBACK_MEMBER(mb9061x_device::timer1_tick) { u8 ctl = m_timer_regs[4]; m_timer_regs[4] |= TCTL_UF; if (ctl & TCTL_INTE) { // printf("timer 1 IRQ\n"); intc_trigger_irq(9, 0x1e); } if (ctl & TCTL_RELD) { recalc_timer(0); m_timer[1]->adjust(attotime::from_hz(m_timer_hz[1])); } } u8 mb9061x_device::timer_r(offs_t offset) { //printf("timer_r: offset %d = %02x\n", offset, m_timer_regs[offset]); return m_timer_regs[offset]; } void mb9061x_device::timer_w(offs_t offset, u8 data) { int timer = offset / 4; int reg = offset % 4; //printf("timer_w: %x to %d\n", data, offset); #if 0 switch (reg) { case 0: // control/status, lower printf("%02x to timer %d lower control\n", data, timer); break; case 1: // control/status, upper printf("%02x to timer %d upper control\n", data, timer); break; case 2: // timer/reload, lower printf("%02x to timer %d lower reload\n", data, timer); break; case 3: // timer/reload, upper printf("%02x to timer %d upper reload\n", data, timer); break; } #endif if (reg == 0) { m_timer_regs[offset] &= (TCTL_TRG|TCTL_UF); m_timer_regs[offset] |= data; } else { m_timer_regs[offset] = data; } int rbase = timer << 2; u8 csl = (m_timer_regs[rbase+1] >> 2) & 3; if (reg == 0) { if (data & TCTL_TRG) { //printf("Got TRG\n"); recalc_timer(timer); if ((m_timer_regs[rbase] & TCTL_CNTE) && (csl != 3)) { m_timer[timer]->adjust(attotime::from_hz(m_timer_hz[timer])); } } if ((data & TCTL_CNTE) && (csl != 3)) { //printf("CNTE set, starting timer at %d Hz\n", m_timer_hz[timer]); m_timer[timer]->adjust(attotime::from_hz(m_timer_hz[timer])); } if ((data & TCTL_CNTE) && (csl == 3)) { m_event_count[timer] = m_timer_regs[2] | (m_timer_regs[3]<<8); //printf("CNTE set in event counter mode, reseeding count to %04x\n", m_event_count[timer]); } if (!(data & TCTL_UF)) { intc_clear_irq(9, 0x1d + timer); } } } WRITE_LINE_MEMBER(mb9061x_device::tin0_w) { tin_common(0, 0, state); } WRITE_LINE_MEMBER(mb9061x_device::tin1_w) { tin_common(1, 4, state); } void mb9061x_device::tin_common(int timer, int base, int state) { // emsure event counter mode if ((m_timer_regs[base + 1] & (TCTH_CSL0|TCTH_CSL1)) == (TCTH_CSL0|TCTH_CSL1) && (m_timer_regs[base] & TCTL_CNTE)) { bool bTickIt = false; // rising edge if ((state && !m_event_state[base/4]) && (m_timer_regs[base] & TCTL_MOD0) && !(m_timer_regs[base+1] & TCTH_MOD1)) { bTickIt = true; } // falling edge if ((!state && m_event_state[base/4]) && !(m_timer_regs[base] & TCTL_MOD0) && (m_timer_regs[base+1] & TCTH_MOD1)) { bTickIt = true; } // any edge if ((state != m_event_state[base/4]) && !(m_timer_regs[base] & TCTL_MOD0) && (m_timer_regs[base+1] & TCTH_MOD1)) { bTickIt = true; } if (bTickIt) { m_event_count[timer]--; //printf("Tick timer %d, new value %04x\n", timer, m_event_count[timer]); if (m_event_count[timer] == 0xffff) { u8 ctl = m_timer_regs[base]; m_timer_regs[base] |= TCTL_UF; //printf("Timer %d exp, CL %02x\n", timer, m_timer_regs[base]); if (ctl & TCTL_INTE) { //printf("timer %d IRQ\n", timer); intc_trigger_irq(9, 0x1d + timer); } if (m_timer_regs[timer * 4] & TCTL_RELD) { m_event_count[timer] = m_timer_regs[2] | (m_timer_regs[3]<<8); //printf("timer %d reload to %04x\n", timer, m_event_count[timer]); } } } m_event_state[timer] = state; } } void mb9061x_device::recalc_timer(int tnum) { int rbase = tnum << 2; u32 divider = 1; u8 csl = (m_timer_regs[rbase+1] >> 2) & 3; // check clock select switch (csl) { case 0: divider = 2; break; case 1: divider = 16; break; case 2: divider = 64; break; case 3: // event counter mode return; } u32 tclk = clock() / divider; u32 tval = m_timer_regs[rbase+2] | (m_timer_regs[rbase+3]<<8); //printf("CSL %d, tclk %d, tval %d\n", csl, tclk, tval); //printf("Timer is %d Hz\n", tclk / tval); m_timer_hz[tnum] = tclk / tval; } /* 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 }; u8 mb9061x_device::tbtc_r() { return m_tbtc; } void mb9061x_device::tbtc_w(u8 data) { static const float periods[4] = { 1.024f, 4.096f, 16.384f, 131.072f }; //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); } } u8 mb9061x_device::intc_r(offs_t offset) { return m_intc[offset]; } void mb9061x_device::intc_w(offs_t offset, u8 data) { //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(0x0038, 0x003f).rw(FUNC(mb9061x_device::timer_r), FUNC(mb9061x_device::timer_w)); 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 } mb90611_device::mb90611_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : mb90611_device(mconfig, MB90611A, tag, owner, clock) { } mb90611_device::mb90611_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : mb9061x_device(mconfig, type, tag, owner, clock, address_map_constructor(FUNC(mb90611_device::mb90611_map), this)) { }