diff options
Diffstat (limited to 'src/devices/machine/tmp68301.c')
-rw-r--r-- | src/devices/machine/tmp68301.c | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/src/devices/machine/tmp68301.c b/src/devices/machine/tmp68301.c new file mode 100644 index 00000000000..955a10c463b --- /dev/null +++ b/src/devices/machine/tmp68301.c @@ -0,0 +1,337 @@ +// license:BSD-3-Clause +// copyright-holders:Luca Elia +/*************************************************************************** + + TMP68301 basic emulation + Interrupt Handling + + The Toshiba TMP68301 is a 68HC000 + serial I/O, parallel I/O, + 3 timers, address decoder, wait generator, interrupt controller, + all integrated in a single chip. + + TODO: + - Interrupt generation: handle pending / in-service mechanisms + - Parallel port: handle timing latency + - Serial port: not done at all + - (and many other things) + +***************************************************************************/ + +#include "emu.h" +#include "machine/tmp68301.h" + +const device_type TMP68301 = &device_creator<tmp68301_device>; + +static ADDRESS_MAP_START( tmp68301_regs, AS_0, 16, tmp68301_device ) +// AM_RANGE(0x000,0x3ff) AM_RAM + AM_RANGE(0x094,0x095) AM_READWRITE(imr_r,imr_w) + AM_RANGE(0x098,0x099) AM_READWRITE(iisr_r,iisr_w) + + /* Parallel Port */ + AM_RANGE(0x100,0x101) AM_READWRITE(pdir_r,pdir_w) + AM_RANGE(0x10a,0x10b) AM_READWRITE(pdr_r,pdr_w) + + /* Serial Port */ + AM_RANGE(0x18e,0x18f) AM_READWRITE(scr_r,scr_w) +ADDRESS_MAP_END + +// IRQ Mask register, 0x94 +READ16_MEMBER(tmp68301_device::imr_r) +{ + return m_imr; +} + +WRITE16_MEMBER(tmp68301_device::imr_w) +{ + COMBINE_DATA(&m_imr); +} + +// IRQ In-Service Register +READ16_MEMBER(tmp68301_device::iisr_r) +{ + return m_iisr; +} + +WRITE16_MEMBER(tmp68301_device::iisr_w) +{ + COMBINE_DATA(&m_iisr); +} + +// Serial Control Register (TODO: 8-bit wide) +READ16_MEMBER(tmp68301_device::scr_r) +{ + return m_scr; +} + +WRITE16_MEMBER(tmp68301_device::scr_w) +{ + /* + *--- ---- CKSE + --*- ---- RES + ---- ---* INTM + */ + + COMBINE_DATA(&m_scr); + m_scr &= 0xa1; +} + +/* Parallel direction: 1 = output, 0 = input */ +READ16_MEMBER(tmp68301_device::pdir_r) +{ + return m_pdir; +} + +WRITE16_MEMBER(tmp68301_device::pdir_w) +{ + COMBINE_DATA(&m_pdir); +} + +READ16_MEMBER(tmp68301_device::pdr_r) +{ + return (m_in_parallel_cb(0) & ~m_pdir) | (m_pdr & m_pdir); +} + +WRITE16_MEMBER(tmp68301_device::pdr_w) +{ + UINT16 old = m_pdr; + COMBINE_DATA(&m_pdr); + m_pdr = (old & ~m_pdir) | (m_pdr & m_pdir); + m_out_parallel_cb(0, m_pdr, mem_mask); +} + + +tmp68301_device::tmp68301_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, TMP68301, "TMP68301", tag, owner, clock, "tmp68301", __FILE__), + device_memory_interface(mconfig, *this), + m_in_parallel_cb(*this), + m_out_parallel_cb(*this), + m_imr(0), + m_iisr(0), + m_scr(0), + m_pdir(0), + m_pdr(0), + m_space_config("regs", ENDIANNESS_LITTLE, 16, 10, 0, NULL, *ADDRESS_MAP_NAME(tmp68301_regs)) +{ + memset(m_regs, 0, sizeof(m_regs)); + memset(m_IE, 0, sizeof(m_IE)); + memset(m_irq_vector, 0, sizeof(m_irq_vector)); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void tmp68301_device::device_start() +{ + int i; + for (i = 0; i < 3; i++) + m_tmp68301_timer[i] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(tmp68301_device::timer_callback), this)); + + m_in_parallel_cb.resolve_safe(0); + m_out_parallel_cb.resolve_safe(); + + save_item(NAME(m_regs)); + save_item(NAME(m_IE)); + save_item(NAME(m_irq_vector)); + save_item(NAME(m_imr)); + save_item(NAME(m_iisr)); + save_item(NAME(m_scr)); + save_item(NAME(m_pdir)); + save_item(NAME(m_pdr)); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void tmp68301_device::device_reset() +{ + int i; + + for (i = 0; i < 3; i++) + m_IE[i] = 0; + + m_imr = 0x7f7; // mask all irqs +} + +//------------------------------------------------- +// memory_space_config - return a description of +// any address spaces owned by this device +//------------------------------------------------- + +const address_space_config *tmp68301_device::memory_space_config(address_spacenum spacenum) const +{ + return (spacenum == AS_0) ? &m_space_config : NULL; +} + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +//------------------------------------------------- +// read_byte - read a byte at the given address +//------------------------------------------------- + +inline UINT16 tmp68301_device::read_word(offs_t address) +{ + return space(AS_0).read_word(address << 1); +} + +//------------------------------------------------- +// write_byte - write a byte at the given address +//------------------------------------------------- + +inline void tmp68301_device::write_word(offs_t address, UINT16 data) +{ + space(AS_0).write_word(address << 1, data); +} + +IRQ_CALLBACK_MEMBER(tmp68301_device::irq_callback) +{ + int vector = m_irq_vector[irqline]; +// logerror("%s: irq callback returns %04X for level %x\n",machine.describe_context(),vector,int_level); + return vector; +} + +TIMER_CALLBACK_MEMBER( tmp68301_device::timer_callback ) +{ + int i = param; + UINT16 TCR = m_regs[(0x200 + i * 0x20)/2]; + UINT16 ICR = m_regs[0x8e/2+i]; // Interrupt Controller Register (ICR7..9) + UINT16 IVNR = m_regs[0x9a/2]; // Interrupt Vector Number Register (IVNR) + +// logerror("s: callback timer %04X, j = %d\n",machine.describe_context(),i,tcount); + + if ( (TCR & 0x0004) && // INT + !(m_imr & (0x100<<i)) + ) + { + int level = ICR & 0x0007; + + // Interrupt Vector Number Register (IVNR) + m_irq_vector[level] = IVNR & 0x00e0; + m_irq_vector[level] += 4+i; + + machine().firstcpu->set_input_line(level,HOLD_LINE); + } + + if (TCR & 0x0080) // N/1 + { + // Repeat + update_timer(i); + } + else + { + // One Shot + } +} + +void tmp68301_device::update_timer( int i ) +{ + UINT16 TCR = m_regs[(0x200 + i * 0x20)/2]; + UINT16 MAX1 = m_regs[(0x204 + i * 0x20)/2]; + UINT16 MAX2 = m_regs[(0x206 + i * 0x20)/2]; + + int max = 0; + attotime duration = attotime::zero; + + m_tmp68301_timer[i]->adjust(attotime::never,i); + + // timers 1&2 only + switch( (TCR & 0x0030)>>4 ) // MR2..1 + { + case 1: + max = MAX1; + break; + case 2: + max = MAX2; + break; + } + + switch ( (TCR & 0xc000)>>14 ) // CK2..1 + { + case 0: // System clock (CLK) + if (max) + { + int scale = (TCR & 0x3c00)>>10; // P4..1 + if (scale > 8) scale = 8; + duration = attotime::from_hz(machine().firstcpu->unscaled_clock()) * ((1 << scale) * max); + } + break; + } + +// logerror("%s: TMP68301 Timer %d, duration %lf, max %04X\n",machine().describe_context(),i,duration,max); + + if (!(TCR & 0x0002)) // CS + { + if (duration != attotime::zero) + m_tmp68301_timer[i]->adjust(duration,i); + else + logerror("%s: TMP68301 error, timer %d duration is 0\n",machine().describe_context(),i); + } +} + +/* Update the IRQ state based on all possible causes */ +void tmp68301_device::update_irq_state() +{ + int i; + + /* Take care of external interrupts */ + + UINT16 IVNR = m_regs[0x9a/2]; // Interrupt Vector Number Register (IVNR) + + for (i = 0; i < 3; i++) + { + if ( (m_IE[i]) && + !(m_imr & (1<<i)) + ) + { + UINT16 ICR = m_regs[0x80/2+i]; // Interrupt Controller Register (ICR0..2) + + // Interrupt Controller Register (ICR0..2) + int level = ICR & 0x0007; + + // Interrupt Vector Number Register (IVNR) + m_irq_vector[level] = IVNR & 0x00e0; + m_irq_vector[level] += i; + + m_IE[i] = 0; // Interrupts are edge triggerred + + machine().firstcpu->set_input_line(level,HOLD_LINE); + } + } +} + +READ16_MEMBER( tmp68301_device::regs_r ) +{ + return read_word(offset); +} + +WRITE16_MEMBER( tmp68301_device::regs_w ) +{ + COMBINE_DATA(&m_regs[offset]); + + write_word(offset,m_regs[offset]); + + if (!ACCESSING_BITS_0_7) return; + +// logerror("CPU #0 PC %06X: TMP68301 Reg %04X<-%04X & %04X\n",space.device().safe_pc(),offset*2,data,mem_mask^0xffff); + + switch( offset * 2 ) + { + // Timers + case 0x200: + case 0x220: + case 0x240: + { + int i = ((offset*2) >> 5) & 3; + + update_timer( i ); + } + break; + } +} + +void tmp68301_device::external_interrupt_0() { m_IE[0] = 1; update_irq_state(); } +void tmp68301_device::external_interrupt_1() { m_IE[1] = 1; update_irq_state(); } +void tmp68301_device::external_interrupt_2() { m_IE[2] = 1; update_irq_state(); } |