mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-30 13:33:53 +08:00
213516ef31
This commit is the result of running the gdb/copyright.py script, which automated the update of the copyright year range for all source files managed by the GDB project to be updated to include year 2023.
1094 lines
25 KiB
C
1094 lines
25 KiB
C
/* m6811_cpu.c -- 68HC11&68HC12 CPU Emulation
|
|
Copyright 1999-2023 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");
|
|
}
|
|
|