14e98e3e1Schristos /* interrupts.c -- 68HC11 Interrupts Emulation 2*9b2a9e00Schristos Copyright 1999-2024 Free Software Foundation, Inc. 34e98e3e1Schristos Written by Stephane Carrez (stcarrez@nerim.fr) 44e98e3e1Schristos 54e98e3e1Schristos This file is part of GDB, GAS, and the GNU binutils. 64e98e3e1Schristos 74e98e3e1Schristos This program is free software; you can redistribute it and/or modify 84e98e3e1Schristos it under the terms of the GNU General Public License as published by 94e98e3e1Schristos the Free Software Foundation; either version 3 of the License, or 104e98e3e1Schristos (at your option) any later version. 114e98e3e1Schristos 124e98e3e1Schristos This program is distributed in the hope that it will be useful, 134e98e3e1Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 144e98e3e1Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 154e98e3e1Schristos GNU General Public License for more details. 164e98e3e1Schristos 174e98e3e1Schristos You should have received a copy of the GNU General Public License 184e98e3e1Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 194e98e3e1Schristos 204b169a6bSchristos /* This must come before any other includes. */ 214b169a6bSchristos #include "defs.h" 224b169a6bSchristos 23*9b2a9e00Schristos #include "bfd.h" 24*9b2a9e00Schristos 254e98e3e1Schristos #include "sim-main.h" 264e98e3e1Schristos #include "sim-options.h" 274b169a6bSchristos #include "sim-signal.h" 284e98e3e1Schristos 29*9b2a9e00Schristos #include "m68hc11-sim.h" 30*9b2a9e00Schristos 314e98e3e1Schristos static const char *interrupt_names[] = { 324e98e3e1Schristos "R1", 334e98e3e1Schristos "R2", 344e98e3e1Schristos "R3", 354e98e3e1Schristos "R4", 364e98e3e1Schristos "R5", 374e98e3e1Schristos "R6", 384e98e3e1Schristos "R7", 394e98e3e1Schristos "R8", 404e98e3e1Schristos "R9", 414e98e3e1Schristos "R10", 424e98e3e1Schristos "R11", 434e98e3e1Schristos 444e98e3e1Schristos "SCI", 454e98e3e1Schristos "SPI", 464e98e3e1Schristos "AINPUT", 474e98e3e1Schristos "AOVERFLOW", 484e98e3e1Schristos "TOVERFLOW", 494e98e3e1Schristos "OUT5", 504e98e3e1Schristos "OUT4", 514e98e3e1Schristos "OUT3", 524e98e3e1Schristos "OUT2", 534e98e3e1Schristos "OUT1", 544e98e3e1Schristos "INC3", 554e98e3e1Schristos "INC2", 564e98e3e1Schristos "INC1", 574e98e3e1Schristos "RT", 584e98e3e1Schristos "IRQ", 594e98e3e1Schristos "XIRQ", 604e98e3e1Schristos "SWI", 614e98e3e1Schristos "ILL", 624e98e3e1Schristos "COPRESET", 634e98e3e1Schristos "COPFAIL", 644e98e3e1Schristos "RESET" 654e98e3e1Schristos }; 664e98e3e1Schristos 674e98e3e1Schristos struct interrupt_def idefs[] = { 684e98e3e1Schristos /* Serial interrupts. */ 694e98e3e1Schristos { M6811_INT_SCI, M6811_SCSR, M6811_TDRE, M6811_SCCR2, M6811_TIE }, 704e98e3e1Schristos { M6811_INT_SCI, M6811_SCSR, M6811_TC, M6811_SCCR2, M6811_TCIE }, 714e98e3e1Schristos { M6811_INT_SCI, M6811_SCSR, M6811_RDRF, M6811_SCCR2, M6811_RIE }, 724e98e3e1Schristos { M6811_INT_SCI, M6811_SCSR, M6811_IDLE, M6811_SCCR2, M6811_ILIE }, 734e98e3e1Schristos 744e98e3e1Schristos /* SPI interrupts. */ 754e98e3e1Schristos { M6811_INT_SPI, M6811_SPSR, M6811_SPIF, M6811_SPCR, M6811_SPIE }, 764e98e3e1Schristos 774e98e3e1Schristos /* Realtime interrupts. */ 784e98e3e1Schristos { M6811_INT_TCTN, M6811_TFLG2, M6811_TOF, M6811_TMSK2, M6811_TOI }, 794e98e3e1Schristos { M6811_INT_RT, M6811_TFLG2, M6811_RTIF, M6811_TMSK2, M6811_RTII }, 804e98e3e1Schristos 814e98e3e1Schristos /* Output compare interrupts. */ 824e98e3e1Schristos { M6811_INT_OUTCMP1, M6811_TFLG1, M6811_OC1F, M6811_TMSK1, M6811_OC1I }, 834e98e3e1Schristos { M6811_INT_OUTCMP2, M6811_TFLG1, M6811_OC2F, M6811_TMSK1, M6811_OC2I }, 844e98e3e1Schristos { M6811_INT_OUTCMP3, M6811_TFLG1, M6811_OC3F, M6811_TMSK1, M6811_OC3I }, 854e98e3e1Schristos { M6811_INT_OUTCMP4, M6811_TFLG1, M6811_OC4F, M6811_TMSK1, M6811_OC4I }, 864e98e3e1Schristos { M6811_INT_OUTCMP5, M6811_TFLG1, M6811_OC5F, M6811_TMSK1, M6811_OC5I }, 874e98e3e1Schristos 884e98e3e1Schristos /* Input compare interrupts. */ 894e98e3e1Schristos { M6811_INT_INCMP1, M6811_TFLG1, M6811_IC1F, M6811_TMSK1, M6811_IC1I }, 904e98e3e1Schristos { M6811_INT_INCMP2, M6811_TFLG1, M6811_IC2F, M6811_TMSK1, M6811_IC2I }, 914e98e3e1Schristos { M6811_INT_INCMP3, M6811_TFLG1, M6811_IC3F, M6811_TMSK1, M6811_IC3I }, 924e98e3e1Schristos 934e98e3e1Schristos /* Pulse accumulator. */ 944e98e3e1Schristos { M6811_INT_AINPUT, M6811_TFLG2, M6811_PAIF, M6811_TMSK2, M6811_PAII }, 954e98e3e1Schristos { M6811_INT_AOVERFLOW,M6811_TFLG2, M6811_PAOVF, M6811_TMSK2, M6811_PAOVI}, 964e98e3e1Schristos #if 0 974e98e3e1Schristos { M6811_INT_COPRESET, M6811_CONFIG, M6811_NOCOP, 0, 0 }, 984e98e3e1Schristos { M6811_INT_COPFAIL, M6811_CONFIG, M6811_NOCOP, 0, 0 } 994e98e3e1Schristos #endif 1004e98e3e1Schristos }; 1014e98e3e1Schristos 1024b169a6bSchristos #define CYCLES_MAX ((((int64_t) 1) << 62) - 1) 1034e98e3e1Schristos 1044e98e3e1Schristos enum 1054e98e3e1Schristos { 1064e98e3e1Schristos OPTION_INTERRUPT_INFO = OPTION_START, 1074e98e3e1Schristos OPTION_INTERRUPT_CATCH, 1084e98e3e1Schristos OPTION_INTERRUPT_CLEAR 1094e98e3e1Schristos }; 1104e98e3e1Schristos 1114e98e3e1Schristos static DECLARE_OPTION_HANDLER (interrupt_option_handler); 1124e98e3e1Schristos 1134e98e3e1Schristos static const OPTION interrupt_options[] = 1144e98e3e1Schristos { 1154e98e3e1Schristos { {"interrupt-info", no_argument, NULL, OPTION_INTERRUPT_INFO }, 1164e98e3e1Schristos '\0', NULL, "Print information about interrupts", 1174e98e3e1Schristos interrupt_option_handler }, 1184e98e3e1Schristos { {"interrupt-catch", required_argument, NULL, OPTION_INTERRUPT_CATCH }, 1194e98e3e1Schristos '\0', "NAME[,MODE]", 1204e98e3e1Schristos "Catch interrupts when they are raised or taken\n" 1214e98e3e1Schristos "NAME Name of the interrupt\n" 1224e98e3e1Schristos "MODE Optional mode (`taken' or `raised')", 1234e98e3e1Schristos interrupt_option_handler }, 1244e98e3e1Schristos { {"interrupt-clear", required_argument, NULL, OPTION_INTERRUPT_CLEAR }, 1254e98e3e1Schristos '\0', "NAME", "No longer catch the interrupt", 1264e98e3e1Schristos interrupt_option_handler }, 1274e98e3e1Schristos 1284e98e3e1Schristos { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } 1294e98e3e1Schristos }; 1304e98e3e1Schristos 1314e98e3e1Schristos /* Initialize the interrupts module. */ 1324e98e3e1Schristos void 133796c32c9Schristos interrupts_initialize (SIM_DESC sd, sim_cpu *cpu) 1344e98e3e1Schristos { 135*9b2a9e00Schristos struct interrupts *interrupts = &M68HC11_SIM_CPU (cpu)->cpu_interrupts; 1364e98e3e1Schristos 137796c32c9Schristos interrupts->cpu = cpu; 1384e98e3e1Schristos 1394e98e3e1Schristos sim_add_option_table (sd, 0, interrupt_options); 1404e98e3e1Schristos } 1414e98e3e1Schristos 1424e98e3e1Schristos /* Initialize the interrupts of the processor. */ 1434e98e3e1Schristos void 1444e98e3e1Schristos interrupts_reset (struct interrupts *interrupts) 1454e98e3e1Schristos { 146*9b2a9e00Schristos sim_cpu *cpu = interrupts->cpu; 147*9b2a9e00Schristos struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); 1484e98e3e1Schristos int i; 1494e98e3e1Schristos 1504e98e3e1Schristos interrupts->pending_mask = 0; 151*9b2a9e00Schristos if (m68hc11_cpu->cpu_mode & M6811_SMOD) 1524e98e3e1Schristos interrupts->vectors_addr = 0xbfc0; 1534e98e3e1Schristos else 1544e98e3e1Schristos interrupts->vectors_addr = 0xffc0; 1554e98e3e1Schristos interrupts->nb_interrupts_raised = 0; 1564e98e3e1Schristos interrupts->min_mask_cycles = CYCLES_MAX; 1574e98e3e1Schristos interrupts->max_mask_cycles = 0; 1584e98e3e1Schristos interrupts->last_mask_cycles = 0; 1594e98e3e1Schristos interrupts->start_mask_cycle = -1; 1604e98e3e1Schristos interrupts->xirq_start_mask_cycle = -1; 1614e98e3e1Schristos interrupts->xirq_max_mask_cycles = 0; 1624e98e3e1Schristos interrupts->xirq_min_mask_cycles = CYCLES_MAX; 1634e98e3e1Schristos interrupts->xirq_last_mask_cycles = 0; 1644e98e3e1Schristos 1654e98e3e1Schristos for (i = 0; i < M6811_INT_NUMBER; i++) 1664e98e3e1Schristos { 1674e98e3e1Schristos interrupts->interrupt_order[i] = i; 1684e98e3e1Schristos } 1694e98e3e1Schristos 1704e98e3e1Schristos /* Clear the interrupt history table. */ 1714e98e3e1Schristos interrupts->history_index = 0; 1724e98e3e1Schristos memset (interrupts->interrupts_history, 0, 1734e98e3e1Schristos sizeof (interrupts->interrupts_history)); 1744e98e3e1Schristos 1754e98e3e1Schristos memset (interrupts->interrupts, 0, 1764e98e3e1Schristos sizeof (interrupts->interrupts)); 1774e98e3e1Schristos 1784e98e3e1Schristos /* In bootstrap mode, initialize the vector table to point 1794e98e3e1Schristos to the RAM location. */ 180*9b2a9e00Schristos if (m68hc11_cpu->cpu_mode == M6811_SMOD) 1814e98e3e1Schristos { 1824e98e3e1Schristos bfd_vma addr = interrupts->vectors_addr; 1834b169a6bSchristos uint16_t vector = 0x0100 - 3 * (M6811_INT_NUMBER - 1); 1844e98e3e1Schristos for (i = 0; i < M6811_INT_NUMBER; i++) 1854e98e3e1Schristos { 186*9b2a9e00Schristos memory_write16 (cpu, addr, vector); 1874e98e3e1Schristos addr += 2; 1884e98e3e1Schristos vector += 3; 1894e98e3e1Schristos } 1904e98e3e1Schristos } 1914e98e3e1Schristos } 1924e98e3e1Schristos 1934e98e3e1Schristos static int 1944e98e3e1Schristos find_interrupt (const char *name) 1954e98e3e1Schristos { 1964e98e3e1Schristos int i; 1974e98e3e1Schristos 1984e98e3e1Schristos if (name) 1994e98e3e1Schristos for (i = 0; i < M6811_INT_NUMBER; i++) 2004e98e3e1Schristos if (strcasecmp (name, interrupt_names[i]) == 0) 2014e98e3e1Schristos return i; 2024e98e3e1Schristos 2034e98e3e1Schristos return -1; 2044e98e3e1Schristos } 2054e98e3e1Schristos 2064e98e3e1Schristos static SIM_RC 2074e98e3e1Schristos interrupt_option_handler (SIM_DESC sd, sim_cpu *cpu, 2084e98e3e1Schristos int opt, char *arg, int is_command) 2094e98e3e1Schristos { 2104e98e3e1Schristos char *p; 2114e98e3e1Schristos int mode; 2124e98e3e1Schristos int id; 2134e98e3e1Schristos struct interrupts *interrupts; 2144e98e3e1Schristos 2154e98e3e1Schristos if (cpu == 0) 2164e98e3e1Schristos cpu = STATE_CPU (sd, 0); 2174e98e3e1Schristos 218*9b2a9e00Schristos interrupts = &M68HC11_SIM_CPU (cpu)->cpu_interrupts; 2194e98e3e1Schristos switch (opt) 2204e98e3e1Schristos { 2214e98e3e1Schristos case OPTION_INTERRUPT_INFO: 2224e98e3e1Schristos for (id = 0; id < M6811_INT_NUMBER; id++) 2234e98e3e1Schristos { 2244e98e3e1Schristos sim_io_eprintf (sd, "%-10.10s ", interrupt_names[id]); 2254e98e3e1Schristos switch (interrupts->interrupts[id].stop_mode) 2264e98e3e1Schristos { 2274e98e3e1Schristos case SIM_STOP_WHEN_RAISED: 2284e98e3e1Schristos sim_io_eprintf (sd, "catch raised "); 2294e98e3e1Schristos break; 2304e98e3e1Schristos 2314e98e3e1Schristos case SIM_STOP_WHEN_TAKEN: 2324e98e3e1Schristos sim_io_eprintf (sd, "catch taken "); 2334e98e3e1Schristos break; 2344e98e3e1Schristos 2354e98e3e1Schristos case SIM_STOP_WHEN_RAISED | SIM_STOP_WHEN_TAKEN: 2364e98e3e1Schristos sim_io_eprintf (sd, "catch all "); 2374e98e3e1Schristos break; 2384e98e3e1Schristos 2394e98e3e1Schristos default: 2404e98e3e1Schristos sim_io_eprintf (sd, " "); 2414e98e3e1Schristos break; 2424e98e3e1Schristos } 2434e98e3e1Schristos sim_io_eprintf (sd, "%ld\n", 2444e98e3e1Schristos interrupts->interrupts[id].raised_count); 2454e98e3e1Schristos } 2464e98e3e1Schristos break; 2474e98e3e1Schristos 2484e98e3e1Schristos case OPTION_INTERRUPT_CATCH: 2494e98e3e1Schristos p = strchr (arg, ','); 2504e98e3e1Schristos if (p) 2514e98e3e1Schristos *p++ = 0; 2524e98e3e1Schristos 2534e98e3e1Schristos mode = SIM_STOP_WHEN_RAISED; 2544e98e3e1Schristos id = find_interrupt (arg); 2554e98e3e1Schristos if (id < 0) 2564e98e3e1Schristos sim_io_eprintf (sd, "Interrupt name not recognized: %s\n", arg); 2574e98e3e1Schristos 2584e98e3e1Schristos if (p && strcasecmp (p, "raised") == 0) 2594e98e3e1Schristos mode = SIM_STOP_WHEN_RAISED; 2604e98e3e1Schristos else if (p && strcasecmp (p, "taken") == 0) 2614e98e3e1Schristos mode = SIM_STOP_WHEN_TAKEN; 2624e98e3e1Schristos else if (p && strcasecmp (p, "all") == 0) 2634e98e3e1Schristos mode = SIM_STOP_WHEN_RAISED | SIM_STOP_WHEN_TAKEN; 2644e98e3e1Schristos else if (p) 2654e98e3e1Schristos { 2664e98e3e1Schristos sim_io_eprintf (sd, "Invalid argument: %s\n", p); 2674e98e3e1Schristos break; 2684e98e3e1Schristos } 2694e98e3e1Schristos if (id >= 0) 2704e98e3e1Schristos interrupts->interrupts[id].stop_mode = mode; 2714e98e3e1Schristos break; 2724e98e3e1Schristos 2734e98e3e1Schristos case OPTION_INTERRUPT_CLEAR: 2744e98e3e1Schristos mode = SIM_STOP_WHEN_RAISED; 2754e98e3e1Schristos id = find_interrupt (arg); 2764e98e3e1Schristos if (id < 0) 2774e98e3e1Schristos sim_io_eprintf (sd, "Interrupt name not recognized: %s\n", arg); 2784e98e3e1Schristos else 2794e98e3e1Schristos interrupts->interrupts[id].stop_mode = 0; 2804e98e3e1Schristos break; 2814e98e3e1Schristos } 2824e98e3e1Schristos 2834e98e3e1Schristos return SIM_RC_OK; 2844e98e3e1Schristos } 2854e98e3e1Schristos 2864e98e3e1Schristos /* Update the mask of pending interrupts. This operation must be called 2874e98e3e1Schristos when the state of some 68HC11 IO register changes. It looks the 2884e98e3e1Schristos different registers that indicate a pending interrupt (timer, SCI, SPI, 2894e98e3e1Schristos ...) and records the interrupt if it's there and enabled. */ 2904e98e3e1Schristos void 2914e98e3e1Schristos interrupts_update_pending (struct interrupts *interrupts) 2924e98e3e1Schristos { 2934e98e3e1Schristos int i; 2944b169a6bSchristos uint8_t *ioregs; 2954e98e3e1Schristos unsigned long clear_mask; 2964e98e3e1Schristos unsigned long set_mask; 2974e98e3e1Schristos 2984e98e3e1Schristos clear_mask = 0; 2994e98e3e1Schristos set_mask = 0; 300*9b2a9e00Schristos ioregs = &M68HC11_SIM_CPU (interrupts->cpu)->ios[0]; 3014e98e3e1Schristos 302796c32c9Schristos for (i = 0; i < ARRAY_SIZE (idefs); i++) 3034e98e3e1Schristos { 3044e98e3e1Schristos struct interrupt_def *idef = &idefs[i]; 3054b169a6bSchristos uint8_t data; 3064e98e3e1Schristos 3074e98e3e1Schristos /* Look if the interrupt is enabled. */ 3084e98e3e1Schristos if (idef->enable_paddr) 3094e98e3e1Schristos { 3104e98e3e1Schristos data = ioregs[idef->enable_paddr]; 3114e98e3e1Schristos if (!(data & idef->enabled_mask)) 3124e98e3e1Schristos { 3134e98e3e1Schristos /* Disable it. */ 3144e98e3e1Schristos clear_mask |= (1 << idef->int_number); 3154e98e3e1Schristos continue; 3164e98e3e1Schristos } 3174e98e3e1Schristos } 3184e98e3e1Schristos 3194e98e3e1Schristos /* Interrupt is enabled, see if it's there. */ 3204e98e3e1Schristos data = ioregs[idef->int_paddr]; 3214e98e3e1Schristos if (!(data & idef->int_mask)) 3224e98e3e1Schristos { 3234e98e3e1Schristos /* Disable it. */ 3244e98e3e1Schristos clear_mask |= (1 << idef->int_number); 3254e98e3e1Schristos continue; 3264e98e3e1Schristos } 3274e98e3e1Schristos 3284e98e3e1Schristos /* Ok, raise it. */ 3294e98e3e1Schristos set_mask |= (1 << idef->int_number); 3304e98e3e1Schristos } 3314e98e3e1Schristos 3324e98e3e1Schristos /* Some interrupts are shared (M6811_INT_SCI) so clear 3334e98e3e1Schristos the interrupts before setting the new ones. */ 3344e98e3e1Schristos interrupts->pending_mask &= ~clear_mask; 3354e98e3e1Schristos interrupts->pending_mask |= set_mask; 3364e98e3e1Schristos 3374e98e3e1Schristos /* Keep track of when the interrupt is raised by the device. 3384e98e3e1Schristos Also implements the breakpoint-on-interrupt. */ 3394e98e3e1Schristos if (set_mask) 3404e98e3e1Schristos { 3414b169a6bSchristos int64_t cycle = cpu_current_cycle (interrupts->cpu); 3424e98e3e1Schristos int must_stop = 0; 3434e98e3e1Schristos 3444e98e3e1Schristos for (i = 0; i < M6811_INT_NUMBER; i++) 3454e98e3e1Schristos { 3464e98e3e1Schristos if (!(set_mask & (1 << i))) 3474e98e3e1Schristos continue; 3484e98e3e1Schristos 3494e98e3e1Schristos interrupts->interrupts[i].cpu_cycle = cycle; 3504e98e3e1Schristos if (interrupts->interrupts[i].stop_mode & SIM_STOP_WHEN_RAISED) 3514e98e3e1Schristos { 3524e98e3e1Schristos must_stop = 1; 3534e98e3e1Schristos sim_io_printf (CPU_STATE (interrupts->cpu), 3544e98e3e1Schristos "Interrupt %s raised\n", 3554e98e3e1Schristos interrupt_names[i]); 3564e98e3e1Schristos } 3574e98e3e1Schristos } 3584e98e3e1Schristos if (must_stop) 3594e98e3e1Schristos sim_engine_halt (CPU_STATE (interrupts->cpu), 3604e98e3e1Schristos interrupts->cpu, 3614e98e3e1Schristos 0, cpu_get_pc (interrupts->cpu), 3624e98e3e1Schristos sim_stopped, 3634e98e3e1Schristos SIM_SIGTRAP); 3644e98e3e1Schristos } 3654e98e3e1Schristos } 3664e98e3e1Schristos 3674e98e3e1Schristos 3684e98e3e1Schristos /* Finds the current active and non-masked interrupt. 3694e98e3e1Schristos Returns the interrupt number (index in the vector table) or -1 3704e98e3e1Schristos if no interrupt can be serviced. */ 3714e98e3e1Schristos int 3724e98e3e1Schristos interrupts_get_current (struct interrupts *interrupts) 3734e98e3e1Schristos { 3744e98e3e1Schristos int i; 3754e98e3e1Schristos 3764e98e3e1Schristos if (interrupts->pending_mask == 0) 3774e98e3e1Schristos return -1; 3784e98e3e1Schristos 3794e98e3e1Schristos /* SWI and illegal instructions are simulated by an interrupt. 3804e98e3e1Schristos They are not maskable. */ 3814e98e3e1Schristos if (interrupts->pending_mask & (1 << M6811_INT_SWI)) 3824e98e3e1Schristos { 3834e98e3e1Schristos interrupts->pending_mask &= ~(1 << M6811_INT_SWI); 3844e98e3e1Schristos return M6811_INT_SWI; 3854e98e3e1Schristos } 3864e98e3e1Schristos if (interrupts->pending_mask & (1 << M6811_INT_ILLEGAL)) 3874e98e3e1Schristos { 3884e98e3e1Schristos interrupts->pending_mask &= ~(1 << M6811_INT_ILLEGAL); 3894e98e3e1Schristos return M6811_INT_ILLEGAL; 3904e98e3e1Schristos } 3914e98e3e1Schristos 3924e98e3e1Schristos /* If there is a non maskable interrupt, go for it (unless we are masked 3934e98e3e1Schristos by the X-bit. */ 3944e98e3e1Schristos if (interrupts->pending_mask & (1 << M6811_INT_XIRQ)) 3954e98e3e1Schristos { 3964e98e3e1Schristos if (cpu_get_ccr_X (interrupts->cpu) == 0) 3974e98e3e1Schristos { 3984e98e3e1Schristos interrupts->pending_mask &= ~(1 << M6811_INT_XIRQ); 3994e98e3e1Schristos return M6811_INT_XIRQ; 4004e98e3e1Schristos } 4014e98e3e1Schristos return -1; 4024e98e3e1Schristos } 4034e98e3e1Schristos 4044e98e3e1Schristos /* Interrupts are masked, do nothing. */ 4054e98e3e1Schristos if (cpu_get_ccr_I (interrupts->cpu) == 1) 4064e98e3e1Schristos { 4074e98e3e1Schristos return -1; 4084e98e3e1Schristos } 4094e98e3e1Schristos 4104e98e3e1Schristos /* Returns the first interrupt number which is pending. 4114e98e3e1Schristos The interrupt priority is specified by the table `interrupt_order'. 4124e98e3e1Schristos For these interrupts, the pending mask is cleared when the program 4134e98e3e1Schristos performs some actions on the corresponding device. If the device 4144e98e3e1Schristos is not reset, the interrupt remains and will be re-raised when 4154e98e3e1Schristos we return from the interrupt (see 68HC11 pink book). */ 4164e98e3e1Schristos for (i = 0; i < M6811_INT_NUMBER; i++) 4174e98e3e1Schristos { 4184e98e3e1Schristos enum M6811_INT int_number = interrupts->interrupt_order[i]; 4194e98e3e1Schristos 4204e98e3e1Schristos if (interrupts->pending_mask & (1 << int_number)) 4214e98e3e1Schristos { 4224e98e3e1Schristos return int_number; 4234e98e3e1Schristos } 4244e98e3e1Schristos } 4254e98e3e1Schristos return -1; 4264e98e3e1Schristos } 4274e98e3e1Schristos 4284e98e3e1Schristos 4294e98e3e1Schristos /* Process the current interrupt if there is one. This operation must 4304e98e3e1Schristos be called after each instruction to handle the interrupts. If interrupts 4314e98e3e1Schristos are masked, it does nothing. */ 4324e98e3e1Schristos int 4334e98e3e1Schristos interrupts_process (struct interrupts *interrupts) 4344e98e3e1Schristos { 4354e98e3e1Schristos int id; 4364b169a6bSchristos uint8_t ccr; 4374e98e3e1Schristos 4384e98e3e1Schristos /* See if interrupts are enabled/disabled and keep track of the 4394e98e3e1Schristos number of cycles the interrupts are masked. Such information is 4404e98e3e1Schristos then reported by the info command. */ 4414e98e3e1Schristos ccr = cpu_get_ccr (interrupts->cpu); 4424e98e3e1Schristos if (ccr & M6811_I_BIT) 4434e98e3e1Schristos { 4444e98e3e1Schristos if (interrupts->start_mask_cycle < 0) 4454e98e3e1Schristos interrupts->start_mask_cycle = cpu_current_cycle (interrupts->cpu); 4464e98e3e1Schristos } 4474e98e3e1Schristos else if (interrupts->start_mask_cycle >= 0 4484e98e3e1Schristos && (ccr & M6811_I_BIT) == 0) 4494e98e3e1Schristos { 4504b169a6bSchristos int64_t t = cpu_current_cycle (interrupts->cpu); 4514e98e3e1Schristos 4524e98e3e1Schristos t -= interrupts->start_mask_cycle; 4534e98e3e1Schristos if (t < interrupts->min_mask_cycles) 4544e98e3e1Schristos interrupts->min_mask_cycles = t; 4554e98e3e1Schristos if (t > interrupts->max_mask_cycles) 4564e98e3e1Schristos interrupts->max_mask_cycles = t; 4574e98e3e1Schristos interrupts->start_mask_cycle = -1; 4584e98e3e1Schristos interrupts->last_mask_cycles = t; 4594e98e3e1Schristos } 4604e98e3e1Schristos if (ccr & M6811_X_BIT) 4614e98e3e1Schristos { 4624e98e3e1Schristos if (interrupts->xirq_start_mask_cycle < 0) 4634e98e3e1Schristos interrupts->xirq_start_mask_cycle 4644e98e3e1Schristos = cpu_current_cycle (interrupts->cpu); 4654e98e3e1Schristos } 4664e98e3e1Schristos else if (interrupts->xirq_start_mask_cycle >= 0 4674e98e3e1Schristos && (ccr & M6811_X_BIT) == 0) 4684e98e3e1Schristos { 4694b169a6bSchristos int64_t t = cpu_current_cycle (interrupts->cpu); 4704e98e3e1Schristos 4714e98e3e1Schristos t -= interrupts->xirq_start_mask_cycle; 4724e98e3e1Schristos if (t < interrupts->xirq_min_mask_cycles) 4734e98e3e1Schristos interrupts->xirq_min_mask_cycles = t; 4744e98e3e1Schristos if (t > interrupts->xirq_max_mask_cycles) 4754e98e3e1Schristos interrupts->xirq_max_mask_cycles = t; 4764e98e3e1Schristos interrupts->xirq_start_mask_cycle = -1; 4774e98e3e1Schristos interrupts->xirq_last_mask_cycles = t; 4784e98e3e1Schristos } 4794e98e3e1Schristos 4804e98e3e1Schristos id = interrupts_get_current (interrupts); 4814e98e3e1Schristos if (id >= 0) 4824e98e3e1Schristos { 4834b169a6bSchristos uint16_t addr; 4844e98e3e1Schristos struct interrupt_history *h; 4854e98e3e1Schristos 4864e98e3e1Schristos /* Implement the breakpoint-on-interrupt. */ 4874e98e3e1Schristos if (interrupts->interrupts[id].stop_mode & SIM_STOP_WHEN_TAKEN) 4884e98e3e1Schristos { 4894e98e3e1Schristos sim_io_printf (CPU_STATE (interrupts->cpu), 4904e98e3e1Schristos "Interrupt %s will be handled\n", 4914e98e3e1Schristos interrupt_names[id]); 4924e98e3e1Schristos sim_engine_halt (CPU_STATE (interrupts->cpu), 4934e98e3e1Schristos interrupts->cpu, 4944e98e3e1Schristos 0, cpu_get_pc (interrupts->cpu), 4954e98e3e1Schristos sim_stopped, 4964e98e3e1Schristos SIM_SIGTRAP); 4974e98e3e1Schristos } 4984e98e3e1Schristos 4994e98e3e1Schristos cpu_push_all (interrupts->cpu); 5004e98e3e1Schristos addr = memory_read16 (interrupts->cpu, 5014e98e3e1Schristos interrupts->vectors_addr + id * 2); 5024e98e3e1Schristos cpu_call (interrupts->cpu, addr); 5034e98e3e1Schristos 5044e98e3e1Schristos /* Now, protect from nested interrupts. */ 5054e98e3e1Schristos if (id == M6811_INT_XIRQ) 5064e98e3e1Schristos { 5074e98e3e1Schristos cpu_set_ccr_X (interrupts->cpu, 1); 5084e98e3e1Schristos } 5094e98e3e1Schristos else 5104e98e3e1Schristos { 5114e98e3e1Schristos cpu_set_ccr_I (interrupts->cpu, 1); 5124e98e3e1Schristos } 5134e98e3e1Schristos 5144e98e3e1Schristos /* Update the interrupt history table. */ 5154e98e3e1Schristos h = &interrupts->interrupts_history[interrupts->history_index]; 5164e98e3e1Schristos h->type = id; 5174e98e3e1Schristos h->taken_cycle = cpu_current_cycle (interrupts->cpu); 5184e98e3e1Schristos h->raised_cycle = interrupts->interrupts[id].cpu_cycle; 5194e98e3e1Schristos 5204e98e3e1Schristos if (interrupts->history_index >= MAX_INT_HISTORY-1) 5214e98e3e1Schristos interrupts->history_index = 0; 5224e98e3e1Schristos else 5234e98e3e1Schristos interrupts->history_index++; 5244e98e3e1Schristos 5254e98e3e1Schristos interrupts->nb_interrupts_raised++; 5264e98e3e1Schristos cpu_add_cycles (interrupts->cpu, 14); 5274e98e3e1Schristos return 1; 5284e98e3e1Schristos } 5294e98e3e1Schristos return 0; 5304e98e3e1Schristos } 5314e98e3e1Schristos 5324e98e3e1Schristos void 5334e98e3e1Schristos interrupts_raise (struct interrupts *interrupts, enum M6811_INT number) 5344e98e3e1Schristos { 5354e98e3e1Schristos interrupts->pending_mask |= (1 << number); 5364e98e3e1Schristos interrupts->nb_interrupts_raised ++; 5374e98e3e1Schristos } 5384e98e3e1Schristos 5394e98e3e1Schristos void 5404e98e3e1Schristos interrupts_info (SIM_DESC sd, struct interrupts *interrupts) 5414e98e3e1Schristos { 5424b169a6bSchristos int64_t t, prev_interrupt; 5434e98e3e1Schristos int i; 5444e98e3e1Schristos 5454e98e3e1Schristos sim_io_printf (sd, "Interrupts Info:\n"); 5464e98e3e1Schristos sim_io_printf (sd, " Interrupts raised: %lu\n", 5474e98e3e1Schristos interrupts->nb_interrupts_raised); 5484e98e3e1Schristos 5494e98e3e1Schristos if (interrupts->start_mask_cycle >= 0) 5504e98e3e1Schristos { 5514e98e3e1Schristos t = cpu_current_cycle (interrupts->cpu); 5524e98e3e1Schristos 5534e98e3e1Schristos t -= interrupts->start_mask_cycle; 5544e98e3e1Schristos if (t > interrupts->max_mask_cycles) 5554e98e3e1Schristos interrupts->max_mask_cycles = t; 5564e98e3e1Schristos 5574e98e3e1Schristos sim_io_printf (sd, " Current interrupts masked sequence: %s\n", 5584e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 5594e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 5604e98e3e1Schristos } 5614e98e3e1Schristos t = interrupts->min_mask_cycles == CYCLES_MAX ? 5624e98e3e1Schristos interrupts->max_mask_cycles : 5634e98e3e1Schristos interrupts->min_mask_cycles; 5644e98e3e1Schristos sim_io_printf (sd, " Shortest interrupts masked sequence: %s\n", 5654e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 5664e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 5674e98e3e1Schristos 5684e98e3e1Schristos t = interrupts->max_mask_cycles; 5694e98e3e1Schristos sim_io_printf (sd, " Longest interrupts masked sequence: %s\n", 5704e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 5714e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 5724e98e3e1Schristos 5734e98e3e1Schristos t = interrupts->last_mask_cycles; 5744e98e3e1Schristos sim_io_printf (sd, " Last interrupts masked sequence: %s\n", 5754e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 5764e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 5774e98e3e1Schristos 5784e98e3e1Schristos if (interrupts->xirq_start_mask_cycle >= 0) 5794e98e3e1Schristos { 5804e98e3e1Schristos t = cpu_current_cycle (interrupts->cpu); 5814e98e3e1Schristos 5824e98e3e1Schristos t -= interrupts->xirq_start_mask_cycle; 5834e98e3e1Schristos if (t > interrupts->xirq_max_mask_cycles) 5844e98e3e1Schristos interrupts->xirq_max_mask_cycles = t; 5854e98e3e1Schristos 5864e98e3e1Schristos sim_io_printf (sd, " XIRQ Current interrupts masked sequence: %s\n", 5874e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 5884e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 5894e98e3e1Schristos } 5904e98e3e1Schristos 5914e98e3e1Schristos t = interrupts->xirq_min_mask_cycles == CYCLES_MAX ? 5924e98e3e1Schristos interrupts->xirq_max_mask_cycles : 5934e98e3e1Schristos interrupts->xirq_min_mask_cycles; 5944e98e3e1Schristos sim_io_printf (sd, " XIRQ Min interrupts masked sequence: %s\n", 5954e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 5964e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 5974e98e3e1Schristos 5984e98e3e1Schristos t = interrupts->xirq_max_mask_cycles; 5994e98e3e1Schristos sim_io_printf (sd, " XIRQ Max interrupts masked sequence: %s\n", 6004e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 6014e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 6024e98e3e1Schristos 6034e98e3e1Schristos t = interrupts->xirq_last_mask_cycles; 6044e98e3e1Schristos sim_io_printf (sd, " XIRQ Last interrupts masked sequence: %s\n", 6054e98e3e1Schristos cycle_to_string (interrupts->cpu, t, 6064e98e3e1Schristos PRINT_TIME | PRINT_CYCLE)); 6074e98e3e1Schristos 6084e98e3e1Schristos if (interrupts->pending_mask) 6094e98e3e1Schristos { 6104e98e3e1Schristos sim_io_printf (sd, " Pending interrupts : "); 6114e98e3e1Schristos for (i = 0; i < M6811_INT_NUMBER; i++) 6124e98e3e1Schristos { 6134e98e3e1Schristos enum M6811_INT int_number = interrupts->interrupt_order[i]; 6144e98e3e1Schristos 6154e98e3e1Schristos if (interrupts->pending_mask & (1 << int_number)) 6164e98e3e1Schristos { 6174e98e3e1Schristos sim_io_printf (sd, "%s ", interrupt_names[int_number]); 6184e98e3e1Schristos } 6194e98e3e1Schristos } 6204e98e3e1Schristos sim_io_printf (sd, "\n"); 6214e98e3e1Schristos } 6224e98e3e1Schristos 6234e98e3e1Schristos prev_interrupt = 0; 6244e98e3e1Schristos sim_io_printf (sd, "N Interrupt Cycle Taken Latency" 6254e98e3e1Schristos " Delta between interrupts\n"); 6264e98e3e1Schristos for (i = 0; i < MAX_INT_HISTORY; i++) 6274e98e3e1Schristos { 6284e98e3e1Schristos int which; 6294e98e3e1Schristos struct interrupt_history *h; 6304b169a6bSchristos int64_t dt; 6314e98e3e1Schristos 6324e98e3e1Schristos which = interrupts->history_index - i - 1; 6334e98e3e1Schristos if (which < 0) 6344e98e3e1Schristos which += MAX_INT_HISTORY; 6354e98e3e1Schristos h = &interrupts->interrupts_history[which]; 6364e98e3e1Schristos if (h->taken_cycle == 0) 6374e98e3e1Schristos break; 6384e98e3e1Schristos 6394e98e3e1Schristos dt = h->taken_cycle - h->raised_cycle; 6404e98e3e1Schristos sim_io_printf (sd, "%2d %-9.9s %15.15s ", i, 6414e98e3e1Schristos interrupt_names[h->type], 6424e98e3e1Schristos cycle_to_string (interrupts->cpu, h->taken_cycle, 0)); 6434e98e3e1Schristos sim_io_printf (sd, "%15.15s", 6444e98e3e1Schristos cycle_to_string (interrupts->cpu, dt, 0)); 6454e98e3e1Schristos if (prev_interrupt) 6464e98e3e1Schristos { 6474e98e3e1Schristos dt = prev_interrupt - h->taken_cycle; 6484e98e3e1Schristos sim_io_printf (sd, " %s", 6494e98e3e1Schristos cycle_to_string (interrupts->cpu, dt, PRINT_TIME)); 6504e98e3e1Schristos } 6514e98e3e1Schristos sim_io_printf (sd, "\n"); 6524e98e3e1Schristos prev_interrupt = h->taken_cycle; 6534e98e3e1Schristos } 6544e98e3e1Schristos } 655