| /* m6811_cpu.c -- 68HC11&68HC12 CPU Emulation |
| Copyright 1999-2024 Free Software Foundation, Inc. |
| Written by Stephane Carrez (stcarrez@nerim.fr) |
| |
| This file is part of GDB, GAS, and the GNU binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it 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 program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include <stdlib.h> |
| |
| #include "sim-main.h" |
| #include "sim-assert.h" |
| #include "sim-module.h" |
| #include "sim-options.h" |
| #include "sim-signal.h" |
| |
| #include "m68hc11-sim.h" |
| |
| enum { |
| OPTION_CPU_RESET = OPTION_START, |
| OPTION_EMUL_OS, |
| OPTION_CPU_CONFIG, |
| OPTION_CPU_BOOTSTRAP, |
| OPTION_CPU_MODE |
| }; |
| |
| static DECLARE_OPTION_HANDLER (cpu_option_handler); |
| |
| static const OPTION cpu_options[] = |
| { |
| { {"cpu-reset", no_argument, NULL, OPTION_CPU_RESET }, |
| '\0', NULL, "Reset the CPU", |
| cpu_option_handler }, |
| |
| { {"emulos", no_argument, NULL, OPTION_EMUL_OS }, |
| '\0', NULL, "Emulate some OS system calls (read, write, ...)", |
| cpu_option_handler }, |
| |
| { {"cpu-config", required_argument, NULL, OPTION_CPU_CONFIG }, |
| '\0', NULL, "Specify the initial CPU configuration register", |
| cpu_option_handler }, |
| |
| { {"bootstrap", no_argument, NULL, OPTION_CPU_BOOTSTRAP }, |
| '\0', NULL, "Start the processing in bootstrap mode", |
| cpu_option_handler }, |
| |
| { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL } |
| }; |
| |
| |
| static SIM_RC |
| cpu_option_handler (SIM_DESC sd, sim_cpu *cpu, |
| int opt, char *arg, int is_command) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| int val; |
| |
| cpu = STATE_CPU (sd, 0); |
| switch (opt) |
| { |
| case OPTION_CPU_RESET: |
| sim_board_reset (sd); |
| break; |
| |
| case OPTION_EMUL_OS: |
| m68hc11_cpu->cpu_emul_syscall = 1; |
| break; |
| |
| case OPTION_CPU_CONFIG: |
| if (sscanf(arg, "0x%x", &val) == 1 |
| || sscanf(arg, "%d", &val) == 1) |
| { |
| m68hc11_cpu->cpu_config = val; |
| m68hc11_cpu->cpu_use_local_config = 1; |
| } |
| else |
| m68hc11_cpu->cpu_use_local_config = 0; |
| break; |
| |
| case OPTION_CPU_BOOTSTRAP: |
| m68hc11_cpu->cpu_start_mode = "bootstrap"; |
| break; |
| |
| case OPTION_CPU_MODE: |
| break; |
| } |
| |
| return SIM_RC_OK; |
| } |
| |
| |
| void |
| cpu_call (sim_cpu *cpu, uint16_t addr) |
| { |
| |
| cpu_set_pc (cpu, addr); |
| } |
| |
| void |
| cpu_return (sim_cpu *cpu) |
| { |
| } |
| |
| /* Set the stack pointer and re-compute the current frame. */ |
| void |
| cpu_set_sp (sim_cpu *cpu, uint16_t val) |
| { |
| M68HC11_SIM_CPU (cpu)->cpu_regs.sp = val; |
| } |
| |
| static uint16_t |
| cpu_get_reg (sim_cpu *cpu, uint8_t reg) |
| { |
| switch (reg) |
| { |
| case 0: |
| return cpu_get_x (cpu); |
| |
| case 1: |
| return cpu_get_y (cpu); |
| |
| case 2: |
| return cpu_get_sp (cpu); |
| |
| case 3: |
| return cpu_get_pc (cpu); |
| |
| default: |
| return 0; |
| } |
| } |
| |
| static uint16_t |
| cpu_get_src_reg (sim_cpu *cpu, uint8_t reg) |
| { |
| switch (reg) |
| { |
| case 0: |
| return cpu_get_a (cpu); |
| |
| case 1: |
| return cpu_get_b (cpu); |
| |
| case 2: |
| return cpu_get_ccr (cpu); |
| |
| case 3: |
| return cpu_get_tmp3 (cpu); |
| |
| case 4: |
| return cpu_get_d (cpu); |
| |
| case 5: |
| return cpu_get_x (cpu); |
| |
| case 6: |
| return cpu_get_y (cpu); |
| |
| case 7: |
| return cpu_get_sp (cpu); |
| |
| default: |
| return 0; |
| } |
| } |
| |
| static void |
| cpu_set_dst_reg (sim_cpu *cpu, uint8_t reg, uint16_t val) |
| { |
| switch (reg) |
| { |
| case 0: |
| cpu_set_a (cpu, val); |
| break; |
| |
| case 1: |
| cpu_set_b (cpu, val); |
| break; |
| |
| case 2: |
| cpu_set_ccr (cpu, val); |
| break; |
| |
| case 3: |
| cpu_set_tmp2 (cpu, val); |
| break; |
| |
| case 4: |
| cpu_set_d (cpu, val); |
| break; |
| |
| case 5: |
| cpu_set_x (cpu, val); |
| break; |
| |
| case 6: |
| cpu_set_y (cpu, val); |
| break; |
| |
| case 7: |
| cpu_set_sp (cpu, val); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static void |
| cpu_set_reg (sim_cpu *cpu, uint8_t reg, uint16_t val) |
| { |
| switch (reg) |
| { |
| case 0: |
| cpu_set_x (cpu, val); |
| break; |
| |
| case 1: |
| cpu_set_y (cpu, val); |
| break; |
| |
| case 2: |
| cpu_set_sp (cpu, val); |
| break; |
| |
| case 3: |
| cpu_set_pc (cpu, val); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| /* Returns the address of a 68HC12 indexed operand. |
| Pre and post modifications are handled on the source register. */ |
| uint16_t |
| cpu_get_indexed_operand_addr (sim_cpu *cpu, int restricted) |
| { |
| uint8_t reg; |
| uint16_t sval; |
| uint16_t addr; |
| uint8_t code; |
| |
| code = cpu_fetch8 (cpu); |
| |
| /* n,r with 5-bit signed constant. */ |
| if ((code & 0x20) == 0) |
| { |
| reg = (code >> 6) & 3; |
| sval = (code & 0x1f); |
| if (code & 0x10) |
| sval |= 0xfff0; |
| |
| addr = cpu_get_reg (cpu, reg); |
| addr += sval; |
| } |
| |
| /* Auto pre/post increment/decrement. */ |
| else if ((code & 0xc0) != 0xc0) |
| { |
| reg = (code >> 6) & 3; |
| sval = (code & 0x0f); |
| if (sval & 0x8) |
| { |
| sval |= 0xfff0; |
| } |
| else |
| { |
| sval = sval + 1; |
| } |
| addr = cpu_get_reg (cpu, reg); |
| cpu_set_reg (cpu, reg, addr + sval); |
| if ((code & 0x10) == 0) |
| { |
| addr += sval; |
| } |
| } |
| |
| /* [n,r] 16-bits offset indexed indirect. */ |
| else if ((code & 0x07) == 3) |
| { |
| if (restricted) |
| { |
| return 0; |
| } |
| reg = (code >> 3) & 0x03; |
| addr = cpu_get_reg (cpu, reg); |
| addr += cpu_fetch16 (cpu); |
| addr = memory_read16 (cpu, addr); |
| cpu_add_cycles (cpu, 1); |
| } |
| else if ((code & 0x4) == 0) |
| { |
| if (restricted) |
| { |
| return 0; |
| } |
| reg = (code >> 3) & 0x03; |
| addr = cpu_get_reg (cpu, reg); |
| if (code & 0x2) |
| { |
| sval = cpu_fetch16 (cpu); |
| cpu_add_cycles (cpu, 1); |
| } |
| else |
| { |
| sval = cpu_fetch8 (cpu); |
| if (code & 0x1) |
| sval |= 0xff00; |
| cpu_add_cycles (cpu, 1); |
| } |
| addr += sval; |
| } |
| else |
| { |
| reg = (code >> 3) & 0x03; |
| addr = cpu_get_reg (cpu, reg); |
| switch (code & 3) |
| { |
| case 0: |
| addr += cpu_get_a (cpu); |
| break; |
| case 1: |
| addr += cpu_get_b (cpu); |
| break; |
| case 2: |
| addr += cpu_get_d (cpu); |
| break; |
| case 3: |
| default: |
| addr += cpu_get_d (cpu); |
| addr = memory_read16 (cpu, addr); |
| cpu_add_cycles (cpu, 1); |
| break; |
| } |
| } |
| |
| return addr; |
| } |
| |
| static uint8_t |
| cpu_get_indexed_operand8 (sim_cpu *cpu, int restricted) |
| { |
| uint16_t addr; |
| |
| addr = cpu_get_indexed_operand_addr (cpu, restricted); |
| return memory_read8 (cpu, addr); |
| } |
| |
| static uint16_t |
| cpu_get_indexed_operand16 (sim_cpu *cpu, int restricted) |
| { |
| uint16_t addr; |
| |
| addr = cpu_get_indexed_operand_addr (cpu, restricted); |
| return memory_read16 (cpu, addr); |
| } |
| |
| void |
| cpu_move8 (sim_cpu *cpu, uint8_t code) |
| { |
| uint8_t src; |
| uint16_t addr; |
| |
| switch (code) |
| { |
| case 0x0b: |
| src = cpu_fetch8 (cpu); |
| addr = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x08: |
| addr = cpu_get_indexed_operand_addr (cpu, 1); |
| src = cpu_fetch8 (cpu); |
| break; |
| |
| case 0x0c: |
| addr = cpu_fetch16 (cpu); |
| src = memory_read8 (cpu, addr); |
| addr = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x09: |
| addr = cpu_get_indexed_operand_addr (cpu, 1); |
| src = memory_read8 (cpu, cpu_fetch16 (cpu)); |
| break; |
| |
| case 0x0d: |
| src = cpu_get_indexed_operand8 (cpu, 1); |
| addr = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x0a: |
| src = cpu_get_indexed_operand8 (cpu, 1); |
| addr = cpu_get_indexed_operand_addr (cpu, 1); |
| break; |
| |
| default: |
| sim_engine_abort (CPU_STATE (cpu), cpu, 0, |
| "Invalid code 0x%0x -- internal error?", code); |
| return; |
| } |
| memory_write8 (cpu, addr, src); |
| } |
| |
| void |
| cpu_move16 (sim_cpu *cpu, uint8_t code) |
| { |
| uint16_t src; |
| uint16_t addr; |
| |
| switch (code) |
| { |
| case 0x03: |
| src = cpu_fetch16 (cpu); |
| addr = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x00: |
| addr = cpu_get_indexed_operand_addr (cpu, 1); |
| src = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x04: |
| addr = cpu_fetch16 (cpu); |
| src = memory_read16 (cpu, addr); |
| addr = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x01: |
| addr = cpu_get_indexed_operand_addr (cpu, 1); |
| src = memory_read16 (cpu, cpu_fetch16 (cpu)); |
| break; |
| |
| case 0x05: |
| src = cpu_get_indexed_operand16 (cpu, 1); |
| addr = cpu_fetch16 (cpu); |
| break; |
| |
| case 0x02: |
| src = cpu_get_indexed_operand16 (cpu, 1); |
| addr = cpu_get_indexed_operand_addr (cpu, 1); |
| break; |
| |
| default: |
| sim_engine_abort (CPU_STATE (cpu), cpu, 0, |
| "Invalid code 0x%0x -- internal error?", code); |
| return; |
| } |
| memory_write16 (cpu, addr, src); |
| } |
| |
| int |
| cpu_initialize (SIM_DESC sd, sim_cpu *cpu) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| sim_add_option_table (sd, 0, cpu_options); |
| |
| memset (&m68hc11_cpu->cpu_regs, 0, sizeof(m68hc11_cpu->cpu_regs)); |
| |
| m68hc11_cpu->cpu_absolute_cycle = 0; |
| m68hc11_cpu->cpu_current_cycle = 0; |
| m68hc11_cpu->cpu_emul_syscall = 1; |
| m68hc11_cpu->cpu_running = 1; |
| m68hc11_cpu->cpu_stop_on_interrupt = 0; |
| m68hc11_cpu->cpu_frequency = 8 * 1000 * 1000; |
| m68hc11_cpu->cpu_use_elf_start = 0; |
| m68hc11_cpu->cpu_elf_start = 0; |
| m68hc11_cpu->cpu_use_local_config = 0; |
| m68hc11_cpu->bank_start = 0; |
| m68hc11_cpu->bank_end = 0; |
| m68hc11_cpu->bank_shift = 0; |
| m68hc11_cpu->cpu_config = M6811_NOSEC | M6811_NOCOP | M6811_ROMON | |
| M6811_EEON; |
| interrupts_initialize (sd, cpu); |
| |
| m68hc11_cpu->cpu_is_initialized = 1; |
| return 0; |
| } |
| |
| |
| /* Reinitialize the processor after a reset. */ |
| int |
| cpu_reset (sim_cpu *cpu) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| |
| /* Initialize the config register. |
| It is only initialized at reset time. */ |
| memset (m68hc11_cpu->ios, 0, sizeof (m68hc11_cpu->ios)); |
| if (m68hc11_cpu->cpu_configured_arch->arch == bfd_arch_m68hc11) |
| m68hc11_cpu->ios[M6811_INIT] = 0x1; |
| else |
| m68hc11_cpu->ios[M6811_INIT] = 0; |
| |
| /* Output compare registers set to 0xFFFF. */ |
| m68hc11_cpu->ios[M6811_TOC1_H] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC1_L] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC2_H] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC2_L] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC3_H] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC4_L] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC5_H] = 0xFF; |
| m68hc11_cpu->ios[M6811_TOC5_L] = 0xFF; |
| |
| /* Setup the processor registers. */ |
| memset (&m68hc11_cpu->cpu_regs, 0, sizeof(m68hc11_cpu->cpu_regs)); |
| m68hc11_cpu->cpu_absolute_cycle = 0; |
| m68hc11_cpu->cpu_current_cycle = 0; |
| m68hc11_cpu->cpu_is_initialized = 0; |
| |
| /* Reset interrupts. */ |
| interrupts_reset (&m68hc11_cpu->cpu_interrupts); |
| |
| /* Reinitialize the CPU operating mode. */ |
| m68hc11_cpu->ios[M6811_HPRIO] = m68hc11_cpu->cpu_mode; |
| return 0; |
| } |
| |
| /* Reinitialize the processor after a reset. */ |
| int |
| cpu_restart (sim_cpu *cpu) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| uint16_t addr; |
| |
| /* Get CPU starting address depending on the CPU mode. */ |
| if (m68hc11_cpu->cpu_use_elf_start == 0) |
| { |
| switch ((m68hc11_cpu->ios[M6811_HPRIO]) & (M6811_SMOD | M6811_MDA)) |
| { |
| /* Single Chip */ |
| default: |
| case 0 : |
| addr = memory_read16 (cpu, 0xFFFE); |
| break; |
| |
| /* Expanded Multiplexed */ |
| case M6811_MDA: |
| addr = memory_read16 (cpu, 0xFFFE); |
| break; |
| |
| /* Special Bootstrap */ |
| case M6811_SMOD: |
| addr = 0; |
| break; |
| |
| /* Factory Test */ |
| case M6811_MDA | M6811_SMOD: |
| addr = memory_read16 (cpu, 0xFFFE); |
| break; |
| } |
| } |
| else |
| { |
| addr = m68hc11_cpu->cpu_elf_start; |
| } |
| |
| /* Setup the processor registers. */ |
| m68hc11_cpu->cpu_insn_pc = addr; |
| m68hc11_cpu->cpu_regs.pc = addr; |
| m68hc11_cpu->cpu_regs.ccr = M6811_X_BIT | M6811_I_BIT | M6811_S_BIT; |
| m68hc11_cpu->cpu_absolute_cycle = 0; |
| m68hc11_cpu->cpu_is_initialized = 1; |
| m68hc11_cpu->cpu_current_cycle = 0; |
| |
| cpu_call (cpu, addr); |
| |
| return 0; |
| } |
| |
| void |
| print_io_reg_desc (SIM_DESC sd, io_reg_desc *desc, int val, int mode) |
| { |
| while (desc->mask) |
| { |
| if (val & desc->mask) |
| sim_io_printf (sd, "%s", |
| mode == 0 ? desc->short_name : desc->long_name); |
| desc++; |
| } |
| } |
| |
| void |
| print_io_byte (SIM_DESC sd, const char *name, io_reg_desc *desc, |
| uint8_t val, uint16_t addr) |
| { |
| sim_io_printf (sd, " %-9.9s @ 0x%04x 0x%02x ", name, addr, val); |
| if (desc) |
| print_io_reg_desc (sd, desc, val, 0); |
| } |
| |
| void |
| print_io_word (SIM_DESC sd, const char *name, io_reg_desc *desc, |
| uint16_t val, uint16_t addr) |
| { |
| sim_io_printf (sd, " %-9.9s @ 0x%04x 0x%04x ", name, addr, val); |
| if (desc) |
| print_io_reg_desc (sd, desc, val, 0); |
| } |
| |
| void |
| cpu_ccr_update_tst8 (sim_cpu *cpu, uint8_t val) |
| { |
| cpu_set_ccr_V (cpu, 0); |
| cpu_set_ccr_N (cpu, val & 0x80 ? 1 : 0); |
| cpu_set_ccr_Z (cpu, val == 0 ? 1 : 0); |
| } |
| |
| |
| uint16_t |
| cpu_fetch_relbranch (sim_cpu *cpu) |
| { |
| uint16_t addr = (uint16_t) cpu_fetch8 (cpu); |
| |
| if (addr & 0x0080) |
| { |
| addr |= 0xFF00; |
| } |
| addr += M68HC11_SIM_CPU (cpu)->cpu_regs.pc; |
| return addr; |
| } |
| |
| uint16_t |
| cpu_fetch_relbranch16 (sim_cpu *cpu) |
| { |
| uint16_t addr = cpu_fetch16 (cpu); |
| |
| addr += M68HC11_SIM_CPU (cpu)->cpu_regs.pc; |
| return addr; |
| } |
| |
| /* Push all the CPU registers (when an interruption occurs). */ |
| void |
| cpu_push_all (sim_cpu *cpu) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| |
| if (m68hc11_cpu->cpu_configured_arch->arch == bfd_arch_m68hc11) |
| { |
| cpu_m68hc11_push_uint16 (cpu, m68hc11_cpu->cpu_regs.pc); |
| cpu_m68hc11_push_uint16 (cpu, m68hc11_cpu->cpu_regs.iy); |
| cpu_m68hc11_push_uint16 (cpu, m68hc11_cpu->cpu_regs.ix); |
| cpu_m68hc11_push_uint16 (cpu, m68hc11_cpu->cpu_regs.d); |
| cpu_m68hc11_push_uint8 (cpu, m68hc11_cpu->cpu_regs.ccr); |
| } |
| else |
| { |
| cpu_m68hc12_push_uint16 (cpu, m68hc11_cpu->cpu_regs.pc); |
| cpu_m68hc12_push_uint16 (cpu, m68hc11_cpu->cpu_regs.iy); |
| cpu_m68hc12_push_uint16 (cpu, m68hc11_cpu->cpu_regs.ix); |
| cpu_m68hc12_push_uint16 (cpu, m68hc11_cpu->cpu_regs.d); |
| cpu_m68hc12_push_uint8 (cpu, m68hc11_cpu->cpu_regs.ccr); |
| } |
| } |
| |
| /* Simulation of the dbcc/ibcc/tbcc 68HC12 conditional branch operations. */ |
| void |
| cpu_dbcc (sim_cpu *cpu) |
| { |
| uint8_t code; |
| uint16_t addr; |
| uint16_t inc; |
| uint16_t reg; |
| |
| code = cpu_fetch8 (cpu); |
| switch (code & 0xc0) |
| { |
| case 0x80: /* ibcc */ |
| inc = 1; |
| break; |
| case 0x40: /* tbcc */ |
| inc = 0; |
| break; |
| case 0: /* dbcc */ |
| inc = -1; |
| break; |
| default: |
| abort (); |
| break; |
| } |
| |
| addr = cpu_fetch8 (cpu); |
| if (code & 0x10) |
| addr |= 0xff00; |
| |
| addr += cpu_get_pc (cpu); |
| reg = cpu_get_src_reg (cpu, code & 0x07); |
| reg += inc; |
| |
| /* Branch according to register value. */ |
| if ((reg != 0 && (code & 0x20)) || (reg == 0 && !(code & 0x20))) |
| { |
| cpu_set_pc (cpu, addr); |
| } |
| cpu_set_dst_reg (cpu, code & 0x07, reg); |
| } |
| |
| void |
| cpu_exg (sim_cpu *cpu, uint8_t code) |
| { |
| uint8_t r1, r2; |
| uint16_t src1; |
| uint16_t src2; |
| |
| r1 = (code >> 4) & 0x07; |
| r2 = code & 0x07; |
| if (code & 0x80) |
| { |
| src1 = cpu_get_src_reg (cpu, r1); |
| src2 = cpu_get_src_reg (cpu, r2); |
| if (r2 == 1 || r2 == 2) |
| src2 |= 0xff00; |
| |
| cpu_set_dst_reg (cpu, r2, src1); |
| cpu_set_dst_reg (cpu, r1, src2); |
| } |
| else |
| { |
| src1 = cpu_get_src_reg (cpu, r1); |
| |
| /* Sign extend the 8-bit registers (A, B, CCR). */ |
| if ((r1 == 0 || r1 == 1 || r1 == 2) && (src1 & 0x80)) |
| src1 |= 0xff00; |
| |
| cpu_set_dst_reg (cpu, r2, src1); |
| } |
| } |
| |
| /* Handle special instructions. */ |
| void |
| cpu_special (sim_cpu *cpu, enum M6811_Special special) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| |
| switch (special) |
| { |
| case M6811_RTI: |
| { |
| uint8_t ccr; |
| |
| ccr = cpu_m68hc11_pop_uint8 (cpu); |
| cpu_set_ccr (cpu, ccr); |
| cpu_set_d (cpu, cpu_m68hc11_pop_uint16 (cpu)); |
| cpu_set_x (cpu, cpu_m68hc11_pop_uint16 (cpu)); |
| cpu_set_y (cpu, cpu_m68hc11_pop_uint16 (cpu)); |
| cpu_set_pc (cpu, cpu_m68hc11_pop_uint16 (cpu)); |
| cpu_return (cpu); |
| break; |
| } |
| |
| case M6812_RTI: |
| { |
| uint8_t ccr; |
| |
| ccr = cpu_m68hc12_pop_uint8 (cpu); |
| cpu_set_ccr (cpu, ccr); |
| cpu_set_d (cpu, cpu_m68hc12_pop_uint16 (cpu)); |
| cpu_set_x (cpu, cpu_m68hc12_pop_uint16 (cpu)); |
| cpu_set_y (cpu, cpu_m68hc12_pop_uint16 (cpu)); |
| cpu_set_pc (cpu, cpu_m68hc12_pop_uint16 (cpu)); |
| cpu_return (cpu); |
| break; |
| } |
| |
| case M6811_WAI: |
| /* In the ELF-start mode, we are in a special mode where |
| the WAI corresponds to an exit. */ |
| if (m68hc11_cpu->cpu_use_elf_start) |
| { |
| cpu_set_pc (cpu, m68hc11_cpu->cpu_insn_pc); |
| sim_engine_halt (CPU_STATE (cpu), cpu, |
| NULL, NULL_CIA, sim_exited, |
| cpu_get_d (cpu)); |
| return; |
| } |
| /* SCz: not correct... */ |
| cpu_push_all (cpu); |
| break; |
| |
| case M6811_SWI: |
| interrupts_raise (&m68hc11_cpu->cpu_interrupts, M6811_INT_SWI); |
| interrupts_process (&m68hc11_cpu->cpu_interrupts); |
| break; |
| |
| case M6811_EMUL_SYSCALL: |
| case M6811_ILLEGAL: |
| if (m68hc11_cpu->cpu_emul_syscall) |
| { |
| uint8_t op = memory_read8 (cpu, |
| cpu_get_pc (cpu) - 1); |
| if (op == 0x41) |
| { |
| cpu_set_pc (cpu, m68hc11_cpu->cpu_insn_pc); |
| sim_engine_halt (CPU_STATE (cpu), cpu, |
| NULL, NULL_CIA, sim_exited, |
| cpu_get_d (cpu)); |
| return; |
| } |
| else |
| { |
| emul_os (op, cpu); |
| } |
| return; |
| } |
| |
| interrupts_raise (&m68hc11_cpu->cpu_interrupts, M6811_INT_ILLEGAL); |
| interrupts_process (&m68hc11_cpu->cpu_interrupts); |
| break; |
| |
| case M6811_TEST: |
| case M6812_BGND: |
| { |
| SIM_DESC sd; |
| |
| sd = CPU_STATE (cpu); |
| |
| /* Breakpoint instruction if we are under gdb. */ |
| if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG) |
| { |
| m68hc11_cpu->cpu_regs.pc --; |
| sim_engine_halt (CPU_STATE (cpu), cpu, |
| 0, cpu_get_pc (cpu), sim_stopped, |
| SIM_SIGTRAP); |
| } |
| /* else this is a nop but not in test factory mode. */ |
| break; |
| } |
| |
| case M6812_IDIVS: |
| { |
| int32_t src1 = (int16_t) cpu_get_d (cpu); |
| int32_t src2 = (int16_t) cpu_get_x (cpu); |
| |
| if (src2 == 0) |
| { |
| cpu_set_ccr_C (cpu, 1); |
| } |
| else |
| { |
| cpu_set_d (cpu, src1 % src2); |
| src1 = src1 / src2; |
| cpu_set_x (cpu, src1); |
| cpu_set_ccr_C (cpu, 0); |
| cpu_set_ccr_Z (cpu, src1 == 0); |
| cpu_set_ccr_N (cpu, src1 & 0x8000); |
| cpu_set_ccr_V (cpu, src1 >= 32768 || src1 < -32768); |
| } |
| } |
| break; |
| |
| case M6812_EDIV: |
| { |
| uint32_t src1 = (uint32_t) cpu_get_x (cpu); |
| uint32_t src2 = (uint32_t) (cpu_get_y (cpu) << 16) |
| | (uint32_t) (cpu_get_d (cpu)); |
| |
| if (src1 == 0) |
| { |
| cpu_set_ccr_C (cpu, 1); |
| } |
| else |
| { |
| cpu_set_ccr_C (cpu, 0); |
| cpu_set_d (cpu, src2 % src1); |
| src2 = src2 / src1; |
| cpu_set_y (cpu, src2); |
| cpu_set_ccr_Z (cpu, src2 == 0); |
| cpu_set_ccr_N (cpu, (src2 & 0x8000) != 0); |
| cpu_set_ccr_V (cpu, (src2 & 0xffff0000) != 0); |
| } |
| } |
| break; |
| |
| case M6812_EDIVS: |
| { |
| int32_t src1 = (int16_t) cpu_get_x (cpu); |
| int32_t src2 = (uint32_t) (cpu_get_y (cpu) << 16) |
| | (uint32_t) (cpu_get_d (cpu)); |
| |
| if (src1 == 0) |
| { |
| cpu_set_ccr_C (cpu, 1); |
| } |
| else |
| { |
| cpu_set_ccr_C (cpu, 0); |
| cpu_set_d (cpu, src2 % src1); |
| src2 = src2 / src1; |
| cpu_set_y (cpu, src2); |
| cpu_set_ccr_Z (cpu, src2 == 0); |
| cpu_set_ccr_N (cpu, (src2 & 0x8000) != 0); |
| cpu_set_ccr_V (cpu, src2 > 32767 || src2 < -32768); |
| } |
| } |
| break; |
| |
| case M6812_EMULS: |
| { |
| int32_t src1, src2; |
| |
| src1 = (int16_t) cpu_get_d (cpu); |
| src2 = (int16_t) cpu_get_y (cpu); |
| src1 = src1 * src2; |
| cpu_set_d (cpu, src1 & 0x0ffff); |
| cpu_set_y (cpu, src1 >> 16); |
| cpu_set_ccr_Z (cpu, src1 == 0); |
| cpu_set_ccr_N (cpu, (src1 & 0x80000000) != 0); |
| cpu_set_ccr_C (cpu, (src1 & 0x00008000) != 0); |
| } |
| break; |
| |
| case M6812_EMACS: |
| { |
| int32_t src1, src2; |
| uint16_t addr; |
| |
| addr = cpu_fetch16 (cpu); |
| src1 = (int16_t) memory_read16 (cpu, cpu_get_x (cpu)); |
| src2 = (int16_t) memory_read16 (cpu, cpu_get_y (cpu)); |
| src1 = src1 * src2; |
| src2 = (((uint32_t) memory_read16 (cpu, addr)) << 16) |
| | (uint32_t) memory_read16 (cpu, addr + 2); |
| |
| memory_write16 (cpu, addr, (src1 + src2) >> 16); |
| memory_write16 (cpu, addr + 2, (src1 + src2)); |
| |
| |
| } |
| break; |
| |
| case M6812_CALL: |
| { |
| uint8_t page; |
| uint16_t addr; |
| |
| addr = cpu_fetch16 (cpu); |
| page = cpu_fetch8 (cpu); |
| |
| cpu_m68hc12_push_uint16 (cpu, cpu_get_pc (cpu)); |
| cpu_m68hc12_push_uint8 (cpu, cpu_get_page (cpu)); |
| |
| cpu_set_page (cpu, page); |
| cpu_set_pc (cpu, addr); |
| } |
| break; |
| |
| case M6812_CALL_INDIRECT: |
| { |
| uint8_t code; |
| uint16_t addr; |
| uint8_t page; |
| |
| code = memory_read8 (cpu, cpu_get_pc (cpu)); |
| /* Indirect addressing call has the page specified in the |
| memory location pointed to by the address. */ |
| if ((code & 0xE3) == 0xE3) |
| { |
| addr = cpu_get_indexed_operand_addr (cpu, 0); |
| page = memory_read8 (cpu, addr + 2); |
| addr = memory_read16 (cpu, addr); |
| } |
| else |
| { |
| /* Otherwise, page is in the opcode. */ |
| addr = cpu_get_indexed_operand16 (cpu, 0); |
| page = cpu_fetch8 (cpu); |
| } |
| cpu_m68hc12_push_uint16 (cpu, cpu_get_pc (cpu)); |
| cpu_m68hc12_push_uint8 (cpu, cpu_get_page (cpu)); |
| cpu_set_page (cpu, page); |
| cpu_set_pc (cpu, addr); |
| } |
| break; |
| |
| case M6812_RTC: |
| { |
| uint8_t page = cpu_m68hc12_pop_uint8 (cpu); |
| uint16_t addr = cpu_m68hc12_pop_uint16 (cpu); |
| |
| cpu_set_page (cpu, page); |
| cpu_set_pc (cpu, addr); |
| } |
| break; |
| |
| case M6812_ETBL: |
| default: |
| sim_engine_halt (CPU_STATE (cpu), cpu, NULL, |
| cpu_get_pc (cpu), sim_stopped, |
| SIM_SIGILL); |
| break; |
| } |
| } |
| |
| |
| void |
| cpu_single_step (sim_cpu *cpu) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| |
| m68hc11_cpu->cpu_current_cycle = 0; |
| m68hc11_cpu->cpu_insn_pc = cpu_get_pc (cpu); |
| |
| /* Handle the pending interrupts. If an interrupt is handled, |
| treat this as an single step. */ |
| if (interrupts_process (&m68hc11_cpu->cpu_interrupts)) |
| { |
| m68hc11_cpu->cpu_absolute_cycle += m68hc11_cpu->cpu_current_cycle; |
| return; |
| } |
| |
| /* printf("PC = 0x%04x\n", cpu_get_pc (cpu));*/ |
| m68hc11_cpu->cpu_interpretor (cpu); |
| m68hc11_cpu->cpu_absolute_cycle += m68hc11_cpu->cpu_current_cycle; |
| } |
| |
| /* VARARGS */ |
| void |
| sim_memory_error (sim_cpu *cpu, SIM_SIGNAL excep, |
| uint16_t addr, const char *message, ...) |
| { |
| char buf[1024]; |
| va_list args; |
| |
| va_start (args, message); |
| vsprintf (buf, message, args); |
| va_end (args); |
| |
| sim_io_printf (CPU_STATE (cpu), "%s\n", buf); |
| cpu_memory_exception (cpu, excep, addr, buf); |
| } |
| |
| |
| void |
| cpu_memory_exception (sim_cpu *cpu, SIM_SIGNAL excep, |
| uint16_t addr, const char *message) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| |
| if (m68hc11_cpu->cpu_running == 0) |
| return; |
| |
| cpu_set_pc (cpu, m68hc11_cpu->cpu_insn_pc); |
| sim_engine_halt (CPU_STATE (cpu), cpu, NULL, |
| cpu_get_pc (cpu), sim_stopped, excep); |
| |
| #if 0 |
| m68hc11_cpu->mem_exception = excep; |
| m68hc11_cpu->fault_addr = addr; |
| m68hc11_cpu->fault_msg = strdup (message); |
| |
| if (m68hc11_cpu->cpu_use_handler) |
| { |
| longjmp (&m68hc11_cpu->cpu_exception_handler, 1); |
| } |
| (* m68hc11_cpu->callback->printf_filtered) |
| (m68hc11_cpu->callback, "Fault at 0x%04x: %s\n", addr, message); |
| #endif |
| } |
| |
| void |
| cpu_info (SIM_DESC sd, sim_cpu *cpu) |
| { |
| struct m68hc11_sim_cpu *m68hc11_cpu = M68HC11_SIM_CPU (cpu); |
| |
| sim_io_printf (sd, "CPU info:\n"); |
| sim_io_printf (sd, " Absolute cycle: %s\n", |
| cycle_to_string (cpu, m68hc11_cpu->cpu_absolute_cycle, |
| PRINT_TIME | PRINT_CYCLE)); |
| |
| sim_io_printf (sd, " Syscall emulation: %s\n", |
| m68hc11_cpu->cpu_emul_syscall ? "yes, via 0xcd <n>" : "no"); |
| sim_io_printf (sd, " Memory errors detection: %s\n", |
| m68hc11_cpu->cpu_check_memory ? "yes" : "no"); |
| sim_io_printf (sd, " Stop on interrupt: %s\n", |
| m68hc11_cpu->cpu_stop_on_interrupt ? "yes" : "no"); |
| } |
| |