aboutsummaryrefslogtreecommitdiff
path: root/sim/m68hc11/interrupts.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/m68hc11/interrupts.c')
-rw-r--r--sim/m68hc11/interrupts.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/sim/m68hc11/interrupts.c b/sim/m68hc11/interrupts.c
new file mode 100644
index 0000000..f172276
--- /dev/null
+++ b/sim/m68hc11/interrupts.c
@@ -0,0 +1,297 @@
+/* interrupts.c -- 68HC11 Interrupts Emulation
+ Copyright 1999, 2000 Free Software Foundation, Inc.
+ Written by Stephane Carrez (stcarrez@worldnet.fr)
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "sim-main.h"
+
+struct interrupt_def idefs[] = {
+ /* Serial interrupts. */
+ { M6811_INT_SCI, M6811_SCSR, M6811_TDRE, M6811_SCCR2, M6811_TIE },
+ { M6811_INT_SCI, M6811_SCSR, M6811_TC, M6811_SCCR2, M6811_TCIE },
+ { M6811_INT_SCI, M6811_SCSR, M6811_RDRF, M6811_SCCR2, M6811_RIE },
+ { M6811_INT_SCI, M6811_SCSR, M6811_IDLE, M6811_SCCR2, M6811_ILIE },
+
+ /* SPI interrupts. */
+ { M6811_INT_SPI, M6811_SPSR, M6811_SPIF, M6811_SPCR, M6811_SPIE },
+
+ /* Realtime interrupts. */
+ { M6811_INT_TCTN, M6811_TFLG2, M6811_TOF, M6811_TMSK2, M6811_TOI },
+ { M6811_INT_RT, M6811_TFLG2, M6811_RTIF, M6811_TMSK2, M6811_RTII },
+
+ /* Output compare interrupts. */
+ { M6811_INT_OUTCMP1, M6811_TFLG1, M6811_OC1F, M6811_TMSK1, M6811_OC1I },
+ { M6811_INT_OUTCMP2, M6811_TFLG1, M6811_OC2F, M6811_TMSK1, M6811_OC2I },
+ { M6811_INT_OUTCMP3, M6811_TFLG1, M6811_OC3F, M6811_TMSK1, M6811_OC3I },
+ { M6811_INT_OUTCMP4, M6811_TFLG1, M6811_OC4F, M6811_TMSK1, M6811_OC4I },
+ { M6811_INT_OUTCMP5, M6811_TFLG1, M6811_OC5F, M6811_TMSK1, M6811_OC5I },
+
+ /* Input compare interrupts. */
+ { M6811_INT_INCMP1, M6811_TFLG1, M6811_IC1F, M6811_TMSK1, M6811_IC1I },
+ { M6811_INT_INCMP2, M6811_TFLG1, M6811_IC2F, M6811_TMSK1, M6811_IC2I },
+ { M6811_INT_INCMP3, M6811_TFLG1, M6811_IC3F, M6811_TMSK1, M6811_IC3I },
+#if 0
+ { M6811_INT_COPRESET, M6811_CONFIG, M6811_NOCOP, 0, 0 },
+ { M6811_INT_COPFAIL, M6811_CONFIG, M6811_NOCOP, 0, 0 }
+#endif
+};
+
+#define TableSize(X) (sizeof X / sizeof(X[0]))
+#define CYCLES_MAX ((((signed64) 1) << 62) - 1)
+
+/* Initialize the interrupts of the processor. */
+int
+interrupts_initialize (struct _sim_cpu *proc)
+{
+ struct interrupts *interrupts = &proc->cpu_interrupts;
+ int i;
+
+ interrupts->cpu = proc;
+ interrupts->pending_mask = 0;
+ interrupts->vectors_addr = 0xffc0;
+ interrupts->nb_interrupts_raised = 0;
+ interrupts->min_mask_cycles = CYCLES_MAX;
+ interrupts->max_mask_cycles = 0;
+ interrupts->start_mask_cycle = -1;
+ interrupts->xirq_start_mask_cycle = -1;
+ interrupts->xirq_max_mask_cycles = 0;
+ interrupts->xirq_min_mask_cycles = CYCLES_MAX;
+
+ for (i = 0; i < M6811_INT_NUMBER; i++)
+ {
+ interrupts->interrupt_order[i] = i;
+ }
+ return 0;
+}
+
+
+/* Update the mask of pending interrupts. This operation must be called
+ when the state of some 68HC11 IO registers changes. It looks the
+ different registers that indicate a pending interrupt (timer, SCI, SPI,
+ ...) and records the interrupt if it's there and enabled. */
+void
+interrupts_update_pending (struct interrupts *interrupts)
+{
+ int i;
+ uint8 *ioregs;
+
+ ioregs = &interrupts->cpu->ios[0];
+
+ for (i = 0; i < TableSize(idefs); i++)
+ {
+ struct interrupt_def *idef = &idefs[i];
+ uint8 data;
+
+ /* Look if the interrupt is enabled. */
+ if (idef->enable_paddr)
+ {
+ data = ioregs[idef->enable_paddr];
+ if (!(data & idef->enabled_mask))
+ continue;
+ }
+
+ /* Interrupt is enabled, see if it's there. */
+ data = ioregs[idef->int_paddr];
+ if (!(data & idef->int_mask))
+ continue;
+
+ /* Ok, raise it. */
+ interrupts->pending_mask |= (1 << idef->int_number);
+ }
+}
+
+
+/* Finds the current active and non-masked interrupt.
+ Returns the interrupt number (index in the vector table) or -1
+ if no interrupt can be serviced. */
+int
+interrupts_get_current (struct interrupts *interrupts)
+{
+ int i;
+
+ if (interrupts->pending_mask == 0)
+ return -1;
+
+ /* SWI and illegal instructions are simulated by an interrupt.
+ They are not maskable. */
+ if (interrupts->pending_mask & (1 << M6811_INT_SWI))
+ {
+ interrupts->pending_mask &= ~(1 << M6811_INT_SWI);
+ return M6811_INT_SWI;
+ }
+ if (interrupts->pending_mask & (1 << M6811_INT_ILLEGAL))
+ {
+ interrupts->pending_mask &= ~(1 << M6811_INT_ILLEGAL);
+ return M6811_INT_ILLEGAL;
+ }
+
+ /* If there is a non maskable interrupt, go for it (unless we are masked
+ by the X-bit. */
+ if (interrupts->pending_mask & (1 << M6811_INT_XIRQ))
+ {
+ if (cpu_get_ccr_X (interrupts->cpu) == 0)
+ {
+ interrupts->pending_mask &= ~(1 << M6811_INT_XIRQ);
+ return M6811_INT_XIRQ;
+ }
+ return -1;
+ }
+
+ /* Interrupts are masked, do nothing. */
+ if (cpu_get_ccr_I (interrupts->cpu) == 1)
+ {
+ return -1;
+ }
+
+ /* Returns the first interrupt number which is pending.
+ The interrupt priority is specified by the table `interrupt_order'. */
+ for (i = 0; i < M6811_INT_NUMBER; i++)
+ {
+ enum M6811_INT int_number = interrupts->interrupt_order[i];
+
+ if (interrupts->pending_mask & (1 << int_number))
+ {
+ interrupts->pending_mask &= ~(1 << int_number);
+ return int_number;
+ }
+ }
+ return -1;
+}
+
+
+/* Process the current interrupt if there is one. This operation must
+ be called after each instruction to handle the interrupts. If interrupts
+ are masked, it does nothing. */
+int
+interrupts_process (struct interrupts *interrupts)
+{
+ int id;
+ uint8 ccr;
+
+ /* See if interrupts are enabled/disabled and keep track of the
+ number of cycles the interrupts are masked. Such information is
+ then reported by the info command. */
+ ccr = cpu_get_ccr (interrupts->cpu);
+ if (ccr & M6811_I_BIT)
+ {
+ if (interrupts->start_mask_cycle < 0)
+ interrupts->start_mask_cycle = cpu_current_cycle (interrupts->cpu);
+ }
+ else if (interrupts->start_mask_cycle >= 0
+ && (ccr & M6811_I_BIT) == 0)
+ {
+ signed64 t = cpu_current_cycle (interrupts->cpu);
+
+ t -= interrupts->start_mask_cycle;
+ if (t < interrupts->min_mask_cycles)
+ interrupts->min_mask_cycles = t;
+ if (t > interrupts->max_mask_cycles)
+ interrupts->max_mask_cycles = t;
+ interrupts->start_mask_cycle = -1;
+ }
+ if (ccr & M6811_X_BIT)
+ {
+ if (interrupts->xirq_start_mask_cycle < 0)
+ interrupts->xirq_start_mask_cycle
+ = cpu_current_cycle (interrupts->cpu);
+ }
+ else if (interrupts->xirq_start_mask_cycle >= 0
+ && (ccr & M6811_X_BIT) == 0)
+ {
+ signed64 t = cpu_current_cycle (interrupts->cpu);
+
+ t -= interrupts->xirq_start_mask_cycle;
+ if (t < interrupts->xirq_min_mask_cycles)
+ interrupts->xirq_min_mask_cycles = t;
+ if (t > interrupts->xirq_max_mask_cycles)
+ interrupts->xirq_max_mask_cycles = t;
+ interrupts->xirq_start_mask_cycle = -1;
+ }
+
+ id = interrupts_get_current (interrupts);
+ if (id >= 0)
+ {
+ uint16 addr;
+
+ cpu_push_all (interrupts->cpu);
+ addr = memory_read16 (interrupts->cpu,
+ interrupts->vectors_addr + id * 2);
+ cpu_call (interrupts->cpu, addr);
+
+ /* Now, protect from nested interrupts. */
+ if (id == M6811_INT_XIRQ)
+ {
+ cpu_set_ccr_X (interrupts->cpu, 1);
+ }
+ else
+ {
+ cpu_set_ccr_I (interrupts->cpu, 1);
+ }
+
+ interrupts->nb_interrupts_raised++;
+ cpu_add_cycles (interrupts->cpu, 14);
+ return 1;
+ }
+ return 0;
+}
+
+void
+interrupts_raise (struct interrupts *interrupts, enum M6811_INT number)
+{
+ interrupts->pending_mask |= (1 << number);
+ interrupts->nb_interrupts_raised ++;
+}
+
+
+
+void
+interrupts_info (SIM_DESC sd, struct interrupts *interrupts)
+{
+ if (interrupts->start_mask_cycle >= 0)
+ {
+ signed64 t = cpu_current_cycle (interrupts->cpu);
+
+ t -= interrupts->start_mask_cycle;
+ if (t > interrupts->max_mask_cycles)
+ interrupts->max_mask_cycles = t;
+ }
+ if (interrupts->xirq_start_mask_cycle >= 0)
+ {
+ signed64 t = cpu_current_cycle (interrupts->cpu);
+
+ t -= interrupts->xirq_start_mask_cycle;
+ if (t > interrupts->xirq_max_mask_cycles)
+ interrupts->xirq_max_mask_cycles = t;
+ }
+
+ sim_io_printf (sd, "Interrupts Info:\n");
+ sim_io_printf (sd, " Interrupts raised: %lu\n",
+ interrupts->nb_interrupts_raised);
+ sim_io_printf (sd, " Min interrupts masked sequence: %llu cycles\n",
+ interrupts->min_mask_cycles == CYCLES_MAX ?
+ interrupts->max_mask_cycles :
+ interrupts->min_mask_cycles);
+ sim_io_printf (sd, " Max interrupts masked sequence: %llu cycles\n",
+ interrupts->max_mask_cycles);
+ sim_io_printf (sd, " XIRQ Min interrupts masked sequence: %llu cycles\n",
+ interrupts->xirq_min_mask_cycles == CYCLES_MAX ?
+ interrupts->xirq_max_mask_cycles :
+ interrupts->xirq_min_mask_cycles);
+ sim_io_printf (sd, " XIRQ Max interrupts masked sequence: %llu cycles\n",
+ interrupts->xirq_max_mask_cycles);
+}