summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/tmp68301.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/tmp68301.c')
-rw-r--r--trunk/src/emu/machine/tmp68301.c189
1 files changed, 189 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/tmp68301.c b/trunk/src/emu/machine/tmp68301.c
new file mode 100644
index 00000000000..369cf9949e5
--- /dev/null
+++ b/trunk/src/emu/machine/tmp68301.c
@@ -0,0 +1,189 @@
+/***************************************************************************
+
+ 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.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "machine/tmp68301.h"
+
+static UINT16 tmp68301_regs[0x400];
+
+static UINT8 tmp68301_IE[3]; // 3 External Interrupt Lines
+static emu_timer *tmp68301_timer[3]; // 3 Timers
+
+static int tmp68301_irq_vector[8];
+
+static void tmp68301_update_timer( running_machine &machine, int i );
+
+static IRQ_CALLBACK(tmp68301_irq_callback)
+{
+ int vector = tmp68301_irq_vector[irqline];
+// logerror("%s: irq callback returns %04X for level %x\n",machine.describe_context(),vector,int_level);
+ return vector;
+}
+
+static TIMER_CALLBACK( tmp68301_timer_callback )
+{
+ int i = param;
+ UINT16 TCR = tmp68301_regs[(0x200 + i * 0x20)/2];
+ UINT16 IMR = tmp68301_regs[0x94/2]; // Interrupt Mask Register (IMR)
+ UINT16 ICR = tmp68301_regs[0x8e/2+i]; // Interrupt Controller Register (ICR7..9)
+ UINT16 IVNR = tmp68301_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
+ !(IMR & (0x100<<i))
+ )
+ {
+ int level = ICR & 0x0007;
+
+ // Interrupt Vector Number Register (IVNR)
+ tmp68301_irq_vector[level] = IVNR & 0x00e0;
+ tmp68301_irq_vector[level] += 4+i;
+
+ device_set_input_line(machine.firstcpu,level,HOLD_LINE);
+ }
+
+ if (TCR & 0x0080) // N/1
+ {
+ // Repeat
+ tmp68301_update_timer(machine, i);
+ }
+ else
+ {
+ // One Shot
+ }
+}
+
+static void tmp68301_update_timer( running_machine &machine, int i )
+{
+ UINT16 TCR = tmp68301_regs[(0x200 + i * 0x20)/2];
+ UINT16 MAX1 = tmp68301_regs[(0x204 + i * 0x20)/2];
+ UINT16 MAX2 = tmp68301_regs[(0x206 + i * 0x20)/2];
+
+ int max = 0;
+ attotime duration = attotime::zero;
+
+ 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)
+ tmp68301_timer[i]->adjust(duration,i);
+ else
+ logerror("%s: TMP68301 error, timer %d duration is 0\n",machine.describe_context(),i);
+ }
+}
+
+MACHINE_START( tmp68301 )
+{
+ int i;
+ for (i = 0; i < 3; i++)
+ tmp68301_timer[i] = machine.scheduler().timer_alloc(FUNC(tmp68301_timer_callback));
+}
+
+MACHINE_RESET( tmp68301 )
+{
+ int i;
+
+ for (i = 0; i < 3; i++)
+ tmp68301_IE[i] = 0;
+
+ device_set_irq_callback(machine.firstcpu, tmp68301_irq_callback);
+}
+
+/* Update the IRQ state based on all possible causes */
+static void update_irq_state(running_machine &machine)
+{
+ int i;
+
+ /* Take care of external interrupts */
+
+ UINT16 IMR = tmp68301_regs[0x94/2]; // Interrupt Mask Register (IMR)
+ UINT16 IVNR = tmp68301_regs[0x9a/2]; // Interrupt Vector Number Register (IVNR)
+
+ for (i = 0; i < 3; i++)
+ {
+ if ( (tmp68301_IE[i]) &&
+ !(IMR & (1<<i))
+ )
+ {
+ UINT16 ICR = tmp68301_regs[0x80/2+i]; // Interrupt Controller Register (ICR0..2)
+
+ // Interrupt Controller Register (ICR0..2)
+ int level = ICR & 0x0007;
+
+ // Interrupt Vector Number Register (IVNR)
+ tmp68301_irq_vector[level] = IVNR & 0x00e0;
+ tmp68301_irq_vector[level] += i;
+
+ tmp68301_IE[i] = 0; // Interrupts are edge triggerred
+
+ device_set_input_line(machine.firstcpu,level,HOLD_LINE);
+ }
+ }
+}
+
+READ16_HANDLER( tmp68301_regs_r )
+{
+ return tmp68301_regs[offset];
+}
+
+WRITE16_HANDLER( tmp68301_regs_w )
+{
+ COMBINE_DATA(&tmp68301_regs[offset]);
+
+ if (!ACCESSING_BITS_0_7) return;
+
+// logerror("CPU #0 PC %06X: TMP68301 Reg %04X<-%04X & %04X\n",cpu_get_pc(&space->device()),offset*2,data,mem_mask^0xffff);
+
+ switch( offset * 2 )
+ {
+ // Timers
+ case 0x200:
+ case 0x220:
+ case 0x240:
+ {
+ int i = ((offset*2) >> 5) & 3;
+
+ tmp68301_update_timer( space->machine(), i );
+ }
+ break;
+ }
+}
+
+void tmp68301_external_interrupt_0(running_machine &machine) { tmp68301_IE[0] = 1; update_irq_state(machine); }
+void tmp68301_external_interrupt_1(running_machine &machine) { tmp68301_IE[1] = 1; update_irq_state(machine); }
+void tmp68301_external_interrupt_2(running_machine &machine) { tmp68301_IE[2] = 1; update_irq_state(machine); }
+