New simulator.

This commit is contained in:
Andrew Cagney 2000-07-27 11:23:39 +00:00
parent 3c765a5497
commit e0709f5044
16 changed files with 7449 additions and 0 deletions

178
sim/m68hc11/ChangeLog Normal file
View File

@ -0,0 +1,178 @@
2000-06-25 Stephane Carrez <Stephane.Carrez@worldnet.fr>
* Makefile.in (SIM_RUN_OBJS): Define to use nrun.c
* dv-m68hc11.c (m68hc11cpu_finish): Register detach address callback.
(dv_m6811_detach_address_callback): New function to detach a
device from an address space.
* dv-m68hc11eepr.c (m68hc11eepr_port_event): Initialize
config register according to --cpu-config option.
* sim-main.h (_sim_cpu): Add cpu_config member.
* interp.c (sim_open): Delete specific simulator options.
* m68hc11_sim.c (cpu_option_handler): New options
--emulos and -cpu-config <val> to configure the simulator.
(cpu_initialize): Initialize cpu_config member.
2000-06-24 Stephane Carrez <Stephane.Carrez@worldnet.fr>
* emulos.c: Fix indentation and comments.
* gencode.c: Likewise.
* dv-m68hc11tim.c (m68hc11tim_timer_event): Handle COMPARE_EVENT.
(m68hc11tim_io_write_buffer): Write compare registers and
setup compare event.
* interp.c: Remove unused global variables.
* interrupts.c (idefs): New compare interrupts.
Fix indentation and comments.
* interrupts.h: Likewise.
2000-06-18 Stephane Carrez <Stephane.Carrez@worldnet.fr>
* dv-m68hc11sio.c: Fix indentation and comments.
Remove INT_PORT.
* dv-m68hc11.c: Fix indentation and comments.
(m68hc11cpu_port_event): Move initialization of M6811_HPRIO from here.
* m68hc11_sim.c (cpu_reset): To here.
* dv-m68hc11eepr.c: Fix indentation and comments.
2000-06-17 Stephane Carrez <Stephane.Carrez@worldnet.fr>
* dv-nvram.c: New file, rename from dv-pram.c.
* dv-pram.c: Delete file.
* sim-main.h: Incorporate m68hc11_sim.h.
* m68hc11_sim.h: Delete file.
* configure.in: Rename pram into nvram.
* interp.c (sim_open): Likewise in creation of device tree.
2000-05-31 Stephane Carrez <Stephane.Carrez@worldnet.fr>
* interp.c (sim_open): Create the SPI device.
* dv-m68hc11spi.c: New file for SPI device simulation.
* configure.in (hw_extra_devices): Add SPI device.
2000-05-28 Stephane Carrez <Stephane.Carrez@worldnet.fr>
* interrupts.c (interrupts_initialize): Clear XIRQ accounting.
(interrupts_process): Separate IRQ and XIRQ accounting.
(interrupts_info): Report XIRQ accounting.
* interrupts.h (struct interrupts): Added accounting for XIRQ.
2000-04-16 Stephane Carrez <stcarrez@worldnet.fr>
* dv-pram.c (attach_pram_regs): Fix the 'save-modified' mode.
* m68hc11_sim.h (_sim_cpu): Allow configuration of cpu mode.
* dv-m68hc11.c (attach_m68hc11_regs): Get the cpu MODA,MODB
configuration from the 'mode' device tree property.
(m68hc11cpu_port_event): Reset M6811_HPRIO to the cpu MODA, MODB
configuration.
2000-02-24 Stephane Carrez <stcarrez@worldnet.fr>
* sim-main.h: Remove WITH_TARGET_* defines.
* Makefile.in (SIM_EXTRA_CFLAGS): Specify the WITH_TARGET_* flags.
2000-02-08 Stephane Carrez <stcarrez@worldnet.fr>
* dv-m68hc11sio.c (m68hc11sio_port_event): Setup the SCI to
1200 baud when cpu is in bootstrap mode.
* dv-m68hc11tim.c (m68hc11tim_io_write_buffer): Be able to
write in the TCTN timer register.
* dv-m68hc11sio.c (m68hc11sio_io_write_buffer): Divide cpu clock
by 4 to obtain the E clock frequency.
(sccr2_desc): Use M6811_TIE for TIE bit.
(m68hc11sio_info): Fix baud rate report.
* dv-m68hc11tim.c (to_realtime): Likewise.
* interp.c (sim_open): When building device tree, only provide
devices that do not exist yet.
* emulos.c: Fix compilation pb under Windows.
* dv-m68hc11.c (attach_m68hc11_regs): Get the clock frequency
from the 'clock' property.
2000-01-02 Stephane Carrez <stcarrez@worldnet.fr>
* m68hc11_sim.h (*_REGNUM): Define.
(_sim_cpu): New member cpu_page0_reg table.
* interp.c (sim_create_inferior): Fill the cpu_page0_reg table with
addresses of soft registers in .page0.
(sim_fetch_register, sim_store_register): Use cpu_page0_reg table
to get/set soft registers.
1999-12-31 Stephane Carrez <stcarrez@worldnet.fr>
* dv-m68hc11.c (m68hc11cpu_io_write_buffer): Clear byte to avoid
returning random values.
1999-12-17 Stephane Carrez <stcarrez@worldnet.fr>
* gencode.c: Fix "subb N,x" that used a instead of b.
1999-09-09 Stephane Carrez <stcarrez@worldnet.fr>
* gencode.c: Fixed sbc8 and adc8 when there was a initial carry.
1999-09-01 Stephane Carrez <stcarrez@worldnet.fr>
* sim-main.h (SIM_HANDLES_LMA): Define to enable loading using lma.
1999-08-14 Stephane Carrez <stcarrez@worldnet.fr>
* dv-m68hc11.c (attach_m68hc11_regs): Save the size of the
register region in the m68hc11cpu struct.
(m68hc11cpu_io_write): When the IO mapping addres changes,
detach the register region and re-attach it at the new address.
(m68hc11cpu_io_read_buffer): Renamed base_address into
attach_address.
(m68hc11cpu_io_write_buffer): Likewise. Pass the hw pointer
to m68hc11cpu_io_write.
1999-08-13 Stephane Carrez <stcarrez@worldnet.fr>
* gencode.c: For sbc8, check the carry and increment the source
before trying to set the carry for the result.
1999-05-24 John S. Kallal <kallal@voicenet.com>
* interp.c (sim_get_info): Don't crash if the command line is 0.
Define prototype for sim_get_info() and init_system().
(sim_info): Correct call to sim_get_info().
1999-05-16 Stephane Carrez <stcarrez@worldnet.fr>
* configure.in: Recognize m6811-*-*.
* configure: Regenerate.
* m68hc11_sim.h (cpu_ccr_update_add8, cpu_ccr_update_add16,
cpu_ccr_update_sub8, cpu_ccr_update_sub16):
Correct the computation of carry of 8 and 16-bits add and subtract.
* gencode.c: Use cpu_ccr_update_sub8 for subtraction (carry and
overflow set in a different manner than add).
1999-05-14 Stephane Carrez <stcarrez@worldnet.fr>
* dv-m68hc11.c (dv_m6811_attach_address_callback): Removed a
trace message.
* interp.c (sim_open, sim_create_inferior): Initialize the
cpu_elf_start from the ELF header.
* m68hc11_sim.c (cpu_initialize): Clear the new data members.
(cpu_restart): Use cpu_elf_start as the starting address when
the flag is set.
(cpu_special): When cpu_use_elf_start is set, the WAI instruction
exits the simulator (exit status is in D).
* m68hc11_sim.h (_sim_cpu): Added members cpu_use_elf_start and
cpu_elf_star to start execution at address specified in ELF file.
1999-05-02 Stephane Carrez <stcarrez@worldnet.fr>
* Makefile.in, config.in, configure, configure.in: New files.
* gencode.c: New file, generation of 68HC11 interpreter.
* m68hc11_sim.h, m68hc11_sim.c: New files, specific operations
for interpreter.
* interrupts.c, interrupts.h: New files, management of interrupts.
* interp.c, sim-main.h,
* dv-m68hc11.c, dv-m68hc11eepr.c, dv-m68hc11sio.c,
dv-m68hc11tim.c, dv-pram.c: New files representing devices for
68HC11 (dv-pram.c is generic and could probably migrate to common).
* emulos.c: New file, basic emulation of some os.

174
sim/m68hc11/config.in Normal file
View File

@ -0,0 +1,174 @@
/* config.in. Generated automatically from configure.in by autoheader. */
/* Define if using alloca.c. */
#undef C_ALLOCA
/* Define to empty if the keyword does not work. */
#undef const
/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
This function is required for alloca.c support on those systems. */
#undef CRAY_STACKSEG_END
/* Define if you have alloca, as a function or macro. */
#undef HAVE_ALLOCA
/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
#undef HAVE_ALLOCA_H
/* Define if you have a working `mmap' system call. */
#undef HAVE_MMAP
/* Define as __inline if that's what the C compiler calls it. */
#undef inline
/* Define to `long' if <sys/types.h> doesn't define. */
#undef off_t
/* Define if you need to in order for stat and other things to work. */
#undef _POSIX_SOURCE
/* Define as the return type of signal handlers (int or void). */
#undef RETSIGTYPE
/* Define to `unsigned' if <sys/types.h> doesn't define. */
#undef size_t
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
automatically deduced at run-time.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown
*/
#undef STACK_DIRECTION
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if your processor stores words with the most significant
byte first (like Motorola and SPARC, unlike Intel and VAX). */
#undef WORDS_BIGENDIAN
/* Define to 1 if NLS is requested. */
#undef ENABLE_NLS
/* Define as 1 if you have gettext and don't want to use GNU gettext. */
#undef HAVE_GETTEXT
/* Define as 1 if you have the stpcpy function. */
#undef HAVE_STPCPY
/* Define if your locale.h file contains LC_MESSAGES. */
#undef HAVE_LC_MESSAGES
/* Define if you have the __argz_count function. */
#undef HAVE___ARGZ_COUNT
/* Define if you have the __argz_next function. */
#undef HAVE___ARGZ_NEXT
/* Define if you have the __argz_stringify function. */
#undef HAVE___ARGZ_STRINGIFY
/* Define if you have the __setfpucw function. */
#undef HAVE___SETFPUCW
/* Define if you have the aint function. */
#undef HAVE_AINT
/* Define if you have the anint function. */
#undef HAVE_ANINT
/* Define if you have the dcgettext function. */
#undef HAVE_DCGETTEXT
/* Define if you have the getcwd function. */
#undef HAVE_GETCWD
/* Define if you have the getpagesize function. */
#undef HAVE_GETPAGESIZE
/* Define if you have the getrusage function. */
#undef HAVE_GETRUSAGE
/* Define if you have the munmap function. */
#undef HAVE_MUNMAP
/* Define if you have the putenv function. */
#undef HAVE_PUTENV
/* Define if you have the setenv function. */
#undef HAVE_SETENV
/* Define if you have the setlocale function. */
#undef HAVE_SETLOCALE
/* Define if you have the sigaction function. */
#undef HAVE_SIGACTION
/* Define if you have the sqrt function. */
#undef HAVE_SQRT
/* Define if you have the stpcpy function. */
#undef HAVE_STPCPY
/* Define if you have the strcasecmp function. */
#undef HAVE_STRCASECMP
/* Define if you have the strchr function. */
#undef HAVE_STRCHR
/* Define if you have the time function. */
#undef HAVE_TIME
/* Define if you have the <argz.h> header file. */
#undef HAVE_ARGZ_H
/* Define if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define if you have the <fpu_control.h> header file. */
#undef HAVE_FPU_CONTROL_H
/* Define if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define if you have the <nl_types.h> header file. */
#undef HAVE_NL_TYPES_H
/* Define if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H
/* Define if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define if you have the <time.h> header file. */
#undef HAVE_TIME_H
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define if you have the <values.h> header file. */
#undef HAVE_VALUES_H
/* Define if you have the m library (-lm). */
#undef HAVE_LIBM

35
sim/m68hc11/configure.in Normal file
View File

@ -0,0 +1,35 @@
dnl Process this file with autoconf to produce a configure script.
sinclude(../common/aclocal.m4)
AC_PREREQ(2.12.1)dnl
AC_INIT(Makefile.in)
SIM_AC_COMMON
dnl Options available in this module
SIM_AC_OPTION_INLINE()
SIM_AC_OPTION_ALIGNMENT(NONSTRICT_ALIGNMENT)
SIM_AC_OPTION_HOSTENDIAN
SIM_AC_OPTION_WARNINGS
#
# Add simulated hardware devices
#
hw_enabled=no
case "${target}" in
m68hc11-*-*|m6811-*-*)
hw_enabled=yes
hw_extra_devices="m68hc11 m68hc11sio m68hc11eepr m68hc11tim m68hc11spi nvram"
m68hc11_extra_objs="dv-sockser.o"
SIM_SUBTARGET="$SIM_SUBTARGET -DTARGET_M68HC11=1"
;;
*)
m68hc11_extra_objs=""
;;
esac
SIM_AC_OPTION_HARDWARE($hw_enabled,$hw_devices,$hw_extra_devices)
AC_CHECK_HEADERS(string.h strings.h stdlib.h stdlib.h fcntl.h)
AC_SUBST(m68hc11_extra_objs)
SIM_AC_OUTPUT

582
sim/m68hc11/dv-m68hc11.c Normal file
View File

@ -0,0 +1,582 @@
/* dv-m68hc11.c -- CPU 68HC11 as a device.
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
/* DEVICE
m68hc11cpu - m68hc11 cpu virtual device
DESCRIPTION
Implements the external m68hc11 functionality. This includes the
delivery of of interrupts generated from other devices and the
handling of device specific registers.
PROPERTIES
reg <base> <size>
Register base (should be 0x1000 0x03f).
clock <hz>
Frequency of the quartz used by the processor.
mode [single | expanded | bootstrap | test]
Cpu operating mode (the MODA and MODB external pins).
PORTS
reset (input)
Reset the cpu and generates a cpu-reset event (used to reset
other devices).
nmi (input)
Deliver a non-maskable interrupt to the processor.
cpu-reset (output)
Event generated after the CPU performs a reset.
BUGS
When delivering an interrupt, this code assumes that there is only
one processor (number 0).
*/
struct m68hc11cpu {
/* Pending interrupts for delivery by event handler. */
int pending_reset;
int pending_nmi;
int pending_level;
struct hw_event *event;
unsigned_word attach_address;
int attach_size;
int attach_space;
};
/* input port ID's */
enum {
RESET_PORT,
NMI_PORT,
IRQ_PORT,
CPU_RESET_PORT
};
static const struct hw_port_descriptor m68hc11cpu_ports[] = {
/* Interrupt inputs. */
{ "reset", RESET_PORT, 0, input_port, },
{ "nmi", NMI_PORT, 0, input_port, },
{ "irq", IRQ_PORT, 0, input_port, },
/* Events generated for connection to other devices. */
{ "cpu-reset", CPU_RESET_PORT, 0, output_port, },
{ NULL, },
};
static hw_io_read_buffer_method m68hc11cpu_io_read_buffer;
static hw_io_write_buffer_method m68hc11cpu_io_write_buffer;
static hw_ioctl_method m68hc11_ioctl;
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc. */
static hw_port_event_method m68hc11cpu_port_event;
static void
dv_m6811_attach_address_callback (struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client)
{
HW_TRACE ((me, "attach - level=%d, space=%d, addr=0x%lx, sz=%ld, client=%s",
level, space, (unsigned long) addr, (unsigned long) nr_bytes,
hw_path (client)));
if (space != io_map)
{
sim_core_attach (hw_system (me),
NULL, /*cpu*/
level,
access_read_write_exec,
space, addr,
nr_bytes,
0, /* modulo */
client,
NULL);
}
else
{
/*printf("Attach from sub device: %d\n", (long) addr);*/
sim_core_attach (hw_system (me),
NULL, /*cpu*/
level,
access_io,
space, addr,
nr_bytes,
0, /* modulo */
client,
NULL);
}
}
static void
dv_m6811_detach_address_callback (struct hw *me,
int level,
int space,
address_word addr,
address_word nr_bytes,
struct hw *client)
{
sim_core_detach (hw_system (me), NULL, /*cpu*/
level, space, addr);
}
static void
attach_m68hc11_regs (struct hw *me,
struct m68hc11cpu *controller)
{
SIM_DESC sd;
sim_cpu *cpu;
reg_property_spec reg;
const char *cpu_mode;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, &reg))
hw_abort (me, "\"reg\" property must contain one addr/size entry");
hw_unit_address_to_attach_address (hw_parent (me),
&reg.address,
&controller->attach_space,
&controller->attach_address,
me);
hw_unit_size_to_attach_size (hw_parent (me),
&reg.size,
&controller->attach_size, me);
hw_attach_address (hw_parent (me), 0,
controller->attach_space,
controller->attach_address,
controller->attach_size,
me);
/* Get cpu frequency. */
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
if (hw_find_property (me, "clock") != NULL)
{
cpu->cpu_frequency = hw_find_integer_property (me, "clock");
}
else
{
cpu->cpu_frequency = 8*1000*1000;
}
cpu_mode = "expanded";
if (hw_find_property (me, "mode") != NULL)
cpu_mode = hw_find_string_property (me, "mode");
if (strcmp (cpu_mode, "test") == 0)
cpu->cpu_mode = M6811_MDA | M6811_SMOD;
else if (strcmp (cpu_mode, "bootstrap") == 0)
cpu->cpu_mode = M6811_SMOD;
else if (strcmp (cpu_mode, "single") == 0)
cpu->cpu_mode = 0;
else
cpu->cpu_mode = M6811_MDA;
}
static void
m68hc11cpu_finish (struct hw *me)
{
struct m68hc11cpu *controller;
controller = HW_ZALLOC (me, struct m68hc11cpu);
me->overlap_mode_hw = 1;
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11cpu_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11cpu_io_write_buffer);
set_hw_ports (me, m68hc11cpu_ports);
set_hw_port_event (me, m68hc11cpu_port_event);
set_hw_attach_address (me, dv_m6811_attach_address_callback);
set_hw_detach_address (me, dv_m6811_detach_address_callback);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11_ioctl);
#else
me->to_ioctl = m68hc11_ioctl;
#endif
/* Initialize the pending interrupt flags. */
controller->pending_level = 0;
controller->pending_reset = 0;
controller->pending_nmi = 0;
controller->event = NULL;
attach_m68hc11_regs (me, controller);
}
/* An event arrives on an interrupt port. */
static void
deliver_m68hc11cpu_interrupt (struct hw *me, void *data)
{
}
static void
m68hc11cpu_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
struct m68hc11cpu *controller = hw_data (me);
SIM_DESC sd;
sim_cpu* cpu;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
HW_TRACE ((me, "port-in reset"));
/* The reset is made in 3 steps:
- First, cleanup the current sim_cpu struct.
- Reset the devices.
- Restart the cpu for the reset (get the CPU mode from the
CONFIG register that gets initialized by EEPROM device). */
cpu_reset (cpu);
hw_port_event (me, CPU_RESET_PORT, 1);
cpu_restart (cpu);
break;
case NMI_PORT:
controller->pending_nmi = 1;
HW_TRACE ((me, "port-in nmi"));
break;
case IRQ_PORT:
/* level == 0 means that the interrupt was cleared. */
if(level == 0)
controller->pending_level = -1; /* signal end of interrupt */
else
controller->pending_level = level;
HW_TRACE ((me, "port-in level=%d", level));
break;
default:
hw_abort (me, "bad switch");
break;
}
/* Schedule an event to be delivered immediately after current
instruction. */
if(controller->event != NULL)
hw_event_queue_deschedule(me, controller->event);
controller->event =
hw_event_queue_schedule (me, 0, deliver_m68hc11cpu_interrupt, NULL);
}
io_reg_desc config_desc[] = {
{ M6811_NOSEC, "NOSEC ", "Security Mode Disable" },
{ M6811_NOCOP, "NOCOP ", "COP System Disable" },
{ M6811_ROMON, "ROMON ", "Enable On-chip Rom" },
{ M6811_EEON, "EEON ", "Enable On-chip EEprom" },
{ 0, 0, 0 }
};
io_reg_desc hprio_desc[] = {
{ M6811_RBOOT, "RBOOT ", "Read Bootstrap ROM" },
{ M6811_SMOD, "SMOD ", "Special Mode" },
{ M6811_MDA, "MDA ", "Mode Select A" },
{ M6811_IRV, "IRV ", "Internal Read Visibility" },
{ 0, 0, 0 }
};
io_reg_desc option_desc[] = {
{ M6811_ADPU, "ADPU ", "A/D Powerup" },
{ M6811_CSEL, "CSEL ", "A/D/EE Charge pump clock source select" },
{ M6811_IRQE, "IRQE ", "IRQ Edge/Level sensitive" },
{ M6811_DLY, "DLY ", "Stop exit turn on delay" },
{ M6811_CME, "CME ", "Clock Monitor Enable" },
{ M6811_CR1, "CR1 ", "COP timer rate select (CR1)" },
{ M6811_CR0, "CR0 ", "COP timer rate select (CR0)" },
{ 0, 0, 0 }
};
static void
m68hc11_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11sio *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
base = cpu_get_io_base (cpu);
sim_io_printf (sd, "M68HC11:\n");
val = cpu->ios[M6811_HPRIO];
print_io_byte (sd, "HPRIO ", hprio_desc, val, base + M6811_HPRIO);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_CONFIG];
print_io_byte (sd, "CONFIG", config_desc, val, base + M6811_CONFIG);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_OPTION];
print_io_byte (sd, "OPTION", option_desc, val, base + M6811_OPTION);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_INIT];
print_io_byte (sd, "INIT ", 0, val, base + M6811_INIT);
sim_io_printf (sd, "Ram = 0x%04x IO = 0x%04x\n",
(((uint16) (val & 0xF0)) << 8),
(((uint16) (val & 0x0F)) << 12));
cpu_info (sd, cpu);
interrupts_info (sd, &cpu->cpu_interrupts);
}
static int
m68hc11_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11_info (me);
return 0;
}
/* generic read/write */
static unsigned
m68hc11cpu_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11cpu *controller = hw_data (me);
sim_cpu *cpu;
unsigned byte = 0;
int result;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
/* Handle reads for the sub-devices. */
base -= controller->attach_address;
result = sim_core_read_buffer (sd, cpu,
io_map, dest, base, nr_bytes);
if (result > 0)
return result;
while (nr_bytes)
{
if (base >= 0x3F)
break;
memcpy (dest, &cpu->ios[base], 1);
dest++;
base++;
byte++;
nr_bytes--;
}
return byte;
}
static void
m68hc11cpu_io_write (struct hw *me, sim_cpu *cpu,
unsigned_word addr, uint8 val)
{
switch (addr)
{
case M6811_PORTA:
break;
case M6811_PIOC:
break;
case M6811_PORTC:
break;
case M6811_PORTB:
break;
case M6811_PORTCL:
break;
case M6811_DDRC:
break;
case M6811_PORTD:
break;
case M6811_DDRD:
break;
case M6811_TMSK2:
break;
/* Change the RAM and I/O mapping. */
case M6811_INIT:
{
uint8 old_bank = cpu->ios[M6811_INIT];
cpu->ios[M6811_INIT] = val;
/* Update IO mapping. Detach from the old address
and attach to the new one. */
if ((old_bank & 0xF0) != (val & 0xF0))
{
struct m68hc11cpu *controller = hw_data (me);
hw_detach_address (hw_parent (me), 0,
controller->attach_space,
controller->attach_address,
controller->attach_size,
me);
controller->attach_address = (val & 0x0F0) << 12;
hw_attach_address (hw_parent (me), 0,
controller->attach_space,
controller->attach_address,
controller->attach_size,
me);
}
if ((old_bank & 0x0F) != (val & 0x0F))
{
;
}
return;
}
/* Writing the config is similar to programing the eeprom.
The config register value is the last byte of the EEPROM.
This last byte is not mapped in memory (that's why we have
to add '1' to 'end_addr'). */
case M6811_CONFIG:
{
return;
}
/* COP reset. */
case M6811_COPRST:
if (val == 0xAA && cpu->ios[addr] == 0x55)
{
val = 0;
/* COP reset here. */
}
break;
default:
break;
}
cpu->ios[addr] = val;
}
static unsigned
m68hc11cpu_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11cpu *controller = hw_data (me);
unsigned byte;
sim_cpu *cpu;
int result;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
base -= controller->attach_address;
result = sim_core_write_buffer (sd, cpu,
io_map, source, base, nr_bytes);
if (result > 0)
return result;
byte = 0;
while (nr_bytes)
{
uint8 val;
if (base >= 0x3F)
break;
val = *((uint8*) source);
m68hc11cpu_io_write (me, cpu, base, val);
source++;
base++;
byte++;
nr_bytes--;
}
return byte;
}
const struct hw_descriptor dv_m68hc11_descriptor[] = {
{ "m68hc11", m68hc11cpu_finish, },
{ NULL },
};

View File

@ -0,0 +1,620 @@
/* dv-m68hc11eepr.c -- Simulation of the 68HC11 Internal EEPROM.
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
#include "sim-assert.h"
#include "sim-events.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* DEVICE
m68hc11eepr - m68hc11 EEPROM
DESCRIPTION
Implements the 68HC11 eeprom device described in the m68hc11
user guide (Chapter 4 in the pink book).
PROPERTIES
reg <base> <length>
Base of eeprom and its length.
file <path>
Path of the EEPROM file. The default is 'm6811.eeprom'.
PORTS
None
*/
/* static functions */
/* port ID's */
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11eepr_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
/* The timer/counter register internal state. Note that we store
state using the control register images, in host endian order. */
struct m68hc11eepr
{
address_word base_address; /* control register base */
int attach_space;
unsigned size;
/* Current state of the eeprom programing:
- eeprom_wmode indicates whether the EEPROM address and byte have
been latched.
- eeprom_waddr indicates the EEPROM address that was latched
and eeprom_wbyte is the byte that was latched.
- eeprom_wcycle indicates the CPU absolute cycle type when
the high voltage was applied (successfully) on the EEPROM.
These data members are setup only when we detect good EEPROM programing
conditions (see Motorola EEPROM Programming and PPROG register usage).
When the high voltage is switched off, we look at the CPU absolute
cycle time to see if the EEPROM command must succeeds or not.
The EEPROM content is updated and saved only at that time.
(EEPROM command is: byte zero bits program, byte erase, row erase
and bulk erase).
The CONFIG register is programmed in the same way. It is physically
located at the end of the EEPROM (eeprom size + 1). It is not mapped
in memory but it's saved in the EEPROM file. */
unsigned long eeprom_wcycle;
uint16 eeprom_waddr;
uint8 eeprom_wbyte;
uint8 eeprom_wmode;
uint8* eeprom;
/* Minimum time in CPU cycles for programming the EEPROM. */
unsigned long eeprom_min_cycles;
char* file_name;
};
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc. */
static hw_io_read_buffer_method m68hc11eepr_io_read_buffer;
static hw_io_write_buffer_method m68hc11eepr_io_write_buffer;
static hw_ioctl_method m68hc11eepr_ioctl;
/* Read or write the memory bank content from/to a file.
Returns 0 if the operation succeeded and -1 if it failed. */
static int
m6811eepr_memory_rw (struct m68hc11eepr *controller, int mode)
{
const char *name = controller->file_name;
int fd;
size_t size;
size = controller->size;
fd = open (name, mode, 0644);
if (fd < 0)
{
if (mode == O_RDONLY)
{
memset (controller->eeprom, 0xFF, size);
/* Default value for CONFIG register (0xFF should be ok):
controller->eeprom[size - 1] = M6811_NOSEC | M6811_NOCOP
| M6811_ROMON | M6811_EEON; */
return 0;
}
return -1;
}
if (mode == O_RDONLY)
{
if (read (fd, controller->eeprom, size) != size)
{
close (fd);
return -1;
}
}
else
{
if (write (fd, controller->eeprom, size) != size)
{
close (fd);
return -1;
}
}
close (fd);
return 0;
}
static void
attach_m68hc11eepr_regs (struct hw *me,
struct m68hc11eepr *controller)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, &reg))
hw_abort (me, "\"reg\" property must contain one addr/size entry");
hw_unit_address_to_attach_address (hw_parent (me),
&reg.address,
&attach_space,
&attach_address,
me);
hw_unit_size_to_attach_size (hw_parent (me),
&reg.size,
&attach_size, me);
/* Attach the two IO registers that control the EEPROM.
The EEPROM is only attached at reset time because it may
be enabled/disabled by the EEON bit in the CONFIG register. */
hw_attach_address (hw_parent (me), 0, io_map, M6811_PPROG, 1, me);
hw_attach_address (hw_parent (me), 0, io_map, M6811_CONFIG, 1, me);
if (hw_find_property (me, "file") == NULL)
controller->file_name = "m6811.eeprom";
else
controller->file_name = hw_find_string_property (me, "file");
controller->attach_space = attach_space;
controller->base_address = attach_address;
controller->eeprom = (char*) malloc (attach_size + 1);
controller->eeprom_min_cycles = 10000;
controller->size = attach_size + 1;
m6811eepr_memory_rw (controller, O_RDONLY);
}
/* An event arrives on an interrupt port. */
static void
m68hc11eepr_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11eepr *controller;
sim_cpu *cpu;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "EEPROM reset"));
/* Re-read the EEPROM from the file. This gives the chance
to users to erase this file before doing a reset and have
a fresh EEPROM taken into account. */
m6811eepr_memory_rw (controller, O_RDONLY);
/* Reset the state of EEPROM programmer. The CONFIG register
is also initialized from the EEPROM/file content. */
cpu->ios[M6811_PPROG] = 0;
if (cpu->cpu_use_local_config)
cpu->ios[M6811_CONFIG] = cpu->cpu_config;
else
cpu->ios[M6811_CONFIG] = controller->eeprom[controller->size-1];
controller->eeprom_wmode = 0;
controller->eeprom_waddr = 0;
controller->eeprom_wbyte = 0;
/* Attach or detach to the bus depending on the EEPROM enable bit.
The EEPROM CONFIG register is still enabled and can be programmed
for a next configuration (taken into account only after a reset,
see Motorola spec). */
if (cpu->ios[M6811_CONFIG] & M6811_EEON)
{
hw_attach_address (hw_parent (me), 0,
controller->attach_space,
controller->base_address,
controller->size - 1,
me);
}
else
{
hw_detach_address (hw_parent (me), 0,
controller->attach_space,
controller->base_address,
controller->size - 1,
me);
}
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
static void
m68hc11eepr_finish (struct hw *me)
{
struct m68hc11eepr *controller;
controller = HW_ZALLOC (me, struct m68hc11eepr);
me->overlap_mode_hw = 1;
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11eepr_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11eepr_io_write_buffer);
set_hw_ports (me, m68hc11eepr_ports);
set_hw_port_event (me, m68hc11eepr_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11eepr_ioctl);
#else
me->to_ioctl = m68hc11eepr_ioctl;
#endif
attach_m68hc11eepr_regs (me, controller);
}
static io_reg_desc pprog_desc[] = {
{ M6811_BYTE, "BYTE ", "Byte Program Mode" },
{ M6811_ROW, "ROW ", "Row Program Mode" },
{ M6811_ERASE, "ERASE ", "Erase Mode" },
{ M6811_EELAT, "EELAT ", "EEProm Latch Control" },
{ M6811_EEPGM, "EEPGM ", "EEProm Programming Voltable Enable" },
{ 0, 0, 0 }
};
extern io_reg_desc config_desc[];
/* Describe the state of the EEPROM device. */
static void
m68hc11eepr_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11eepr *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
base = cpu_get_io_base (cpu);
sim_io_printf (sd, "M68HC11 EEprom:\n");
val = cpu->ios[M6811_PPROG];
print_io_byte (sd, "PPROG ", pprog_desc, val, base + M6811_PPROG);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_CONFIG];
print_io_byte (sd, "CONFIG ", config_desc, val, base + M6811_CONFIG);
sim_io_printf (sd, "\n");
val = controller->eeprom[controller->size - 1];
print_io_byte (sd, "(*NEXT*) ", config_desc, val, base + M6811_CONFIG);
sim_io_printf (sd, "\n");
/* Describe internal state of EEPROM. */
if (controller->eeprom_wmode)
{
if (controller->eeprom_waddr == controller->size - 1)
sim_io_printf (sd, " Programming CONFIG register ");
else
sim_io_printf (sd, " Programming: 0x%04x ",
controller->eeprom_waddr);
sim_io_printf (sd, "with 0x%02x\n",
controller->eeprom_wbyte);
}
sim_io_printf (sd, " EEProm file: %s\n",
controller->file_name);
}
static int
m68hc11eepr_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11eepr_info (me);
return 0;
}
/* generic read/write */
static unsigned
m68hc11eepr_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11eepr *controller;
sim_cpu *cpu;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
controller = hw_data (me);
cpu = STATE_CPU (sd, 0);
if (space == io_map)
{
unsigned cnt = 0;
while (nr_bytes != 0)
{
switch (base)
{
case M6811_PPROG:
case M6811_CONFIG:
*((uint8*) dest) = cpu->ios[base];
break;
default:
hw_abort (me, "reading wrong register 0x%04x", base);
}
dest = (uint8*) (dest) + 1;
base++;
nr_bytes--;
cnt++;
}
return cnt;
}
/* In theory, we can't read the EEPROM when it's being programmed. */
if ((cpu->ios[M6811_PPROG] & M6811_EELAT) != 0
&& cpu_is_running (cpu))
{
sim_memory_error (cpu, SIM_SIGBUS, base,
"EEprom not configured for reading");
}
base = base - controller->base_address;
memcpy (dest, &controller->eeprom[base], nr_bytes);
return nr_bytes;
}
static unsigned
m68hc11eepr_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11eepr *controller;
sim_cpu *cpu;
uint8 val;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
controller = hw_data (me);
cpu = STATE_CPU (sd, 0);
/* Programming several bytes at a time is not possible. */
if (space != io_map && nr_bytes != 1)
{
sim_memory_error (cpu, SIM_SIGBUS, base,
"EEprom write error (only 1 byte can be programmed)");
return 0;
}
if (nr_bytes != 1)
hw_abort (me, "Cannot write more than 1 byte to EEPROM device at a time");
val = *((const uint8*) source);
/* Write to the EEPROM control register. */
if (space == io_map && base == M6811_PPROG)
{
uint8 wrong_bits;
uint16 addr;
addr = base + cpu_get_io_base (cpu);
/* Setting EELAT and EEPGM at the same time is an error.
Clearing them both is ok. */
wrong_bits = (cpu->ios[M6811_PPROG] ^ val) & val;
wrong_bits &= (M6811_EELAT | M6811_EEPGM);
if (wrong_bits == (M6811_EEPGM|M6811_EELAT))
{
sim_memory_error (cpu, SIM_SIGBUS, addr,
"Wrong eeprom programing value");
return 0;
}
if ((val & M6811_EELAT) == 0)
{
val = 0;
}
if ((val & M6811_EEPGM) && !(cpu->ios[M6811_PPROG] & M6811_EELAT))
{
sim_memory_error (cpu, SIM_SIGBUS, addr,
"EEProm high voltage applied after EELAT");
}
if ((val & M6811_EEPGM) && controller->eeprom_wmode == 0)
{
sim_memory_error (cpu, SIM_SIGSEGV, addr,
"EEProm high voltage applied without address");
}
if (val & M6811_EEPGM)
{
controller->eeprom_wcycle = cpu_current_cycle (cpu);
}
else if (cpu->ios[M6811_PPROG] & M6811_PPROG)
{
int i;
unsigned long t = cpu_current_cycle (cpu);
t -= controller->eeprom_wcycle;
if (t < controller->eeprom_min_cycles)
{
sim_memory_error (cpu, SIM_SIGILL, addr,
"EEprom programmed only for %lu cycles",
t);
}
/* Program the byte by clearing some bits. */
if (!(cpu->ios[M6811_PPROG] & M6811_ERASE))
{
controller->eeprom[controller->eeprom_waddr]
&= controller->eeprom_wbyte;
}
/* Erase a byte, row or the complete eeprom. Erased value is 0xFF.
Ignore row or complete eeprom erase when we are programming the
CONFIG register (last EEPROM byte). */
else if ((cpu->ios[M6811_PPROG] & M6811_BYTE)
|| controller->eeprom_waddr == controller->size - 1)
{
controller->eeprom[controller->eeprom_waddr] = 0xff;
}
else if (cpu->ios[M6811_BYTE] & M6811_ROW)
{
size_t max_size;
/* Size of EEPROM (-1 because the last byte is the
CONFIG register. */
max_size = controller->size;
controller->eeprom_waddr &= 0xFFF0;
for (i = 0; i < 16
&& controller->eeprom_waddr < max_size; i++)
{
controller->eeprom[controller->eeprom_waddr] = 0xff;
controller->eeprom_waddr ++;
}
}
else
{
size_t max_size;
max_size = controller->size;
for (i = 0; i < max_size; i++)
{
controller->eeprom[i] = 0xff;
}
}
/* Save the eeprom in a file. We have to save after each
change because the simulator can be stopped or crash... */
if (m6811eepr_memory_rw (controller, O_WRONLY | O_CREAT) != 0)
{
sim_memory_error (cpu, SIM_SIGABRT, addr,
"EEPROM programing failed: errno=%d", errno);
}
controller->eeprom_wmode = 0;
}
cpu->ios[M6811_PPROG] = val;
return 1;
}
/* The CONFIG IO register is mapped at end of EEPROM.
It's not visible. */
if (space == io_map && base == M6811_CONFIG)
{
base = controller->size - 1;
}
else
{
base = base - controller->base_address;
}
/* Writing the memory is allowed for the Debugger or simulator
(cpu not running). */
if (cpu_is_running (cpu))
{
if ((cpu->ios[M6811_PPROG] & M6811_EELAT) == 0)
{
sim_memory_error (cpu, SIM_SIGSEGV, base,
"EEprom not configured for writing");
return 0;
}
if (controller->eeprom_wmode != 0)
{
sim_memory_error (cpu, SIM_SIGSEGV, base,
"EEprom write error");
return 0;
}
controller->eeprom_wmode = 1;
controller->eeprom_waddr = base;
controller->eeprom_wbyte = val;
}
else
{
controller->eeprom[base] = val;
m6811eepr_memory_rw (controller, O_WRONLY);
}
return 1;
}
const struct hw_descriptor dv_m68hc11eepr_descriptor[] = {
{ "m68hc11eepr", m68hc11eepr_finish, },
{ NULL },
};

664
sim/m68hc11/dv-m68hc11sio.c Normal file
View File

@ -0,0 +1,664 @@
/* dv-m68hc11sio.c -- Simulation of the 68HC11 serial device.
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
This file is part of the program GDB, the GNU debugger.
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
#include "dv-sockser.h"
#include "sim-assert.h"
/* DEVICE
m68hc11sio - m68hc11 serial I/O
DESCRIPTION
Implements the m68hc11 serial I/O controller described in the m68hc11
user guide. The serial I/O controller is directly connected to the CPU
interrupt. The simulator implements:
- baud rate emulation
- 8-bits transfers
PROPERTIES
backend {tcp | stdio}
Use dv-sockser TCP-port backend or stdio for backend. Default: stdio.
PORTS
reset (input)
Reset port. This port is only used to simulate a reset of the serial
I/O controller. It should be connected to the RESET output of the cpu.
*/
/* port ID's */
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11sio_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
/* Serial Controller information. */
struct m68hc11sio
{
enum {sio_tcp, sio_stdio} backend; /* backend */
/* Number of cpu cycles to send a bit on the wire. */
unsigned long baud_cycle;
/* Length in bits of characters sent, this includes the
start/stop and parity bits. Together with baud_cycle, this
is used to find the number of cpu cycles to send/receive a data. */
unsigned int data_length;
/* Information about next character to be transmited. */
unsigned char tx_has_char;
unsigned char tx_char;
unsigned char rx_char;
unsigned char rx_clear_scsr;
/* Periodic I/O polling. */
struct hw_event* tx_poll_event;
struct hw_event* rx_poll_event;
};
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc. */
static hw_io_read_buffer_method m68hc11sio_io_read_buffer;
static hw_io_write_buffer_method m68hc11sio_io_write_buffer;
static hw_port_event_method m68hc11sio_port_event;
static hw_ioctl_method m68hc11sio_ioctl;
#define M6811_SCI_FIRST_REG (M6811_BAUD)
#define M6811_SCI_LAST_REG (M6811_SCDR)
static void
attach_m68hc11sio_regs (struct hw *me,
struct m68hc11sio *controller)
{
hw_attach_address (hw_parent (me), 0, io_map,
M6811_SCI_FIRST_REG,
M6811_SCI_LAST_REG - M6811_SCI_FIRST_REG + 1,
me);
if (hw_find_property(me, "backend") != NULL)
{
const char *value = hw_find_string_property(me, "backend");
if(! strcmp(value, "tcp"))
controller->backend = sio_tcp;
else if(! strcmp(value, "stdio"))
controller->backend = sio_stdio;
else
hw_abort (me, "illegal value for backend parameter `%s':"
"use tcp or stdio", value);
}
}
static void
m68hc11sio_finish (struct hw *me)
{
struct m68hc11sio *controller;
controller = HW_ZALLOC (me, struct m68hc11sio);
me->overlap_mode_hw = 1;
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11sio_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11sio_io_write_buffer);
set_hw_ports (me, m68hc11sio_ports);
set_hw_port_event (me, m68hc11sio_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11sio_ioctl);
#else
me->to_ioctl = m68hc11sio_ioctl;
#endif
/* Preset defaults. */
controller->backend = sio_stdio;
/* Attach ourself to our parent bus. */
attach_m68hc11sio_regs (me, controller);
/* Initialize to reset state. */
controller->tx_poll_event = NULL;
controller->rx_poll_event = NULL;
controller->tx_char = 0;
controller->tx_has_char = 0;
controller->rx_clear_scsr = 0;
controller->rx_char = 0;
}
/* An event arrives on an interrupt port. */
static void
m68hc11sio_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
unsigned8 val;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "SCI reset"));
/* Reset the state of SCI registers. */
val = 0;
m68hc11sio_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_BAUD, 1);
m68hc11sio_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_SCCR1, 1);
m68hc11sio_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_SCCR2, 1);
cpu->ios[M6811_SCSR] = M6811_TC | M6811_TDRE;
controller->rx_char = 0;
controller->tx_char = 0;
controller->tx_has_char = 0;
controller->rx_clear_scsr = 0;
if (controller->rx_poll_event)
{
hw_event_queue_deschedule (me, controller->rx_poll_event);
controller->rx_poll_event = 0;
}
if (controller->tx_poll_event)
{
hw_event_queue_deschedule (me, controller->tx_poll_event);
controller->tx_poll_event = 0;
}
/* In bootstrap mode, initialize the SCI to 1200 bauds to
simulate some initial setup by the internal rom. */
if (((cpu->ios[M6811_HPRIO]) & (M6811_SMOD | M6811_MDA)) == M6811_SMOD)
{
unsigned char val = 0x33;
m68hc11sio_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_BAUD, 1);
val = 0x12;
m68hc11sio_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_SCCR2, 1);
}
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
void
m68hc11sio_rx_poll (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
char cc;
int cnt;
int check_interrupt = 0;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (controller->backend)
{
case sio_tcp:
cnt = dv_sockser_read (sd);
if (cnt != -1)
{
cc = (char) cnt;
cnt = 1;
}
break;
case sio_stdio:
cnt = sim_io_poll_read (sd, 0 /* stdin */, &cc, 1);
break;
default:
cnt = 0;
break;
}
if (cnt == 1)
{
/* Raise the overrun flag if the previous character was not read. */
if (cpu->ios[M6811_SCSR] & M6811_RDRF)
cpu->ios[M6811_SCSR] |= M6811_OR;
cpu->ios[M6811_SCSR] |= M6811_RDRF;
controller->rx_char = cc;
controller->rx_clear_scsr = 0;
check_interrupt = 1;
}
else
{
/* handle idle line detect here. */
;
}
if (controller->rx_poll_event)
{
hw_event_queue_deschedule (me, controller->rx_poll_event);
controller->rx_poll_event = 0;
}
if (cpu->ios[M6811_SCCR2] & M6811_RE)
{
unsigned long clock_cycle;
/* Compute CPU clock cycles to wait for the next character. */
clock_cycle = controller->data_length * controller->baud_cycle;
controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
m68hc11sio_rx_poll,
NULL);
}
if (check_interrupt)
interrupts_update_pending (&cpu->cpu_interrupts);
}
void
m68hc11sio_tx_poll (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
int check_interrupt = 0;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
cpu->ios[M6811_SCSR] |= M6811_TDRE;
cpu->ios[M6811_SCSR] |= M6811_TC;
/* Transmitter is enabled and we have something to sent. */
if ((cpu->ios[M6811_SCCR2] & M6811_TE) && controller->tx_has_char)
{
cpu->ios[M6811_SCSR] &= ~M6811_TDRE;
cpu->ios[M6811_SCSR] &= ~M6811_TC;
controller->tx_has_char = 0;
check_interrupt = 1;
switch (controller->backend)
{
case sio_tcp:
dv_sockser_write (sd, controller->tx_char);
break;
case sio_stdio:
sim_io_write_stdout (sd, &controller->tx_char, 1);
sim_io_flush_stdout (sd);
break;
default:
break;
}
}
if (controller->tx_poll_event)
{
hw_event_queue_deschedule (me, controller->tx_poll_event);
controller->tx_poll_event = 0;
}
if ((cpu->ios[M6811_SCCR2] & M6811_TE)
&& ((cpu->ios[M6811_SCSR] & M6811_TC) == 0))
{
unsigned long clock_cycle;
/* Compute CPU clock cycles to wait for the next character. */
clock_cycle = controller->data_length * controller->baud_cycle;
controller->tx_poll_event = hw_event_queue_schedule (me, clock_cycle,
m68hc11sio_tx_poll,
NULL);
}
if (check_interrupt)
interrupts_update_pending (&cpu->cpu_interrupts);
}
/* Descriptions of the SIO I/O ports. These descriptions are only used to
give information of the SIO device under GDB. */
io_reg_desc sccr2_desc[] = {
{ M6811_TIE, "TIE ", "Transmit Interrupt Enable" },
{ M6811_TCIE, "TCIE ", "Transmit Complete Interrupt Enable" },
{ M6811_RIE, "RIE ", "Receive Interrupt Enable" },
{ M6811_ILIE, "ILIE ", "Idle Line Interrupt Enable" },
{ M6811_TE, "TE ", "Transmit Enable" },
{ M6811_RE, "RE ", "Receive Enable" },
{ M6811_RWU, "RWU ", "Receiver Wake Up" },
{ M6811_SBK, "SBRK ", "Send Break" },
{ 0, 0, 0 }
};
io_reg_desc sccr1_desc[] = {
{ M6811_R8, "R8 ", "Receive Data bit 8" },
{ M6811_T8, "T8 ", "Transmit Data bit 8" },
{ M6811_M, "M ", "SCI Character length (0=8-bits, 1=9-bits)" },
{ M6811_WAKE, "WAKE ", "Wake up method select (0=idle, 1=addr mark" },
{ 0, 0, 0 }
};
io_reg_desc scsr_desc[] = {
{ M6811_TDRE, "TDRE ", "Transmit Data Register Empty" },
{ M6811_TC, "TC ", "Transmit Complete" },
{ M6811_RDRF, "RDRF ", "Receive Data Register Full" },
{ M6811_IDLE, "IDLE ", "Idle Line Detect" },
{ M6811_OR, "OR ", "Overrun Error" },
{ M6811_NF, "NF ", "Noise Flag" },
{ M6811_FE, "FE ", "Framing Error" },
{ 0, 0, 0 }
};
io_reg_desc baud_desc[] = {
{ M6811_TCLR, "TCLR ", "Clear baud rate (test mode)" },
{ M6811_SCP1, "SCP1 ", "SCI baud rate prescaler select (SCP1)" },
{ M6811_SCP0, "SCP0 ", "SCI baud rate prescaler select (SCP0)" },
{ M6811_RCKB, "RCKB ", "Baur Rate Clock Check (test mode)" },
{ M6811_SCR2, "SCR2 ", "SCI Baud rate select (SCR2)" },
{ M6811_SCR1, "SCR1 ", "SCI Baud rate select (SCR1)" },
{ M6811_SCR0, "SCR0 ", "SCI Baud rate select (SCR0)" },
{ 0, 0, 0 }
};
static void
m68hc11sio_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11sio *controller;
uint8 val;
long clock_cycle;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
sim_io_printf (sd, "M68HC11 SIO:\n");
base = cpu_get_io_base (cpu);
val = cpu->ios[M6811_BAUD];
print_io_byte (sd, "BAUD ", baud_desc, val, base + M6811_BAUD);
sim_io_printf (sd, " (%ld baud)\n",
(cpu->cpu_frequency / 4) / controller->baud_cycle);
val = cpu->ios[M6811_SCCR1];
print_io_byte (sd, "SCCR1", sccr1_desc, val, base + M6811_SCCR1);
sim_io_printf (sd, " (%d bits) (%dN1)\n",
controller->data_length, controller->data_length - 2);
val = cpu->ios[M6811_SCCR2];
print_io_byte (sd, "SCCR2", sccr2_desc, val, base + M6811_SCCR2);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_SCSR];
print_io_byte (sd, "SCSR ", scsr_desc, val, base + M6811_SCSR);
sim_io_printf (sd, "\n");
clock_cycle = controller->data_length * controller->baud_cycle;
if (controller->tx_poll_event)
{
signed64 t;
int n;
t = hw_event_remain_time (me, controller->tx_poll_event);
n = (clock_cycle - t) / controller->baud_cycle;
n = controller->data_length - n;
sim_io_printf (sd, " Transmit finished in %ld cycles (%d bit%s)\n",
(long) t, n, (n > 1 ? "s" : ""));
}
if (controller->rx_poll_event)
{
signed64 t;
t = hw_event_remain_time (me, controller->rx_poll_event);
sim_io_printf (sd, " Receive finished in %ld cycles\n",
(long) t);
}
}
static int
m68hc11sio_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11sio_info (me);
return 0;
}
/* generic read/write */
static unsigned
m68hc11sio_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
switch (base)
{
case M6811_SCSR:
controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
& (M6811_RDRF | M6811_IDLE | M6811_OR | M6811_NF | M6811_FE);
case M6811_BAUD:
case M6811_SCCR1:
case M6811_SCCR2:
val = cpu->ios[base];
break;
case M6811_SCDR:
if (controller->rx_clear_scsr)
{
cpu->ios[M6811_SCSR] &= ~controller->rx_clear_scsr;
}
val = controller->rx_char;
break;
default:
return 0;
}
*((unsigned8*) dest) = val;
return 1;
}
static unsigned
m68hc11sio_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11sio *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
val = *((const unsigned8*) source);
switch (base)
{
case M6811_BAUD:
{
long divisor;
long baud;
cpu->ios[M6811_BAUD] = val;
switch (val & (M6811_SCP1|M6811_SCP0))
{
case M6811_BAUD_DIV_1:
divisor = 1 * 16;
break;
case M6811_BAUD_DIV_3:
divisor = 3 * 16;
break;
case M6811_BAUD_DIV_4:
divisor = 4 * 16;
break;
default:
case M6811_BAUD_DIV_13:
divisor = 13 * 16;
break;
}
val &= (M6811_SCR2|M6811_SCR1|M6811_SCR0);
divisor *= (1 << val);
baud = (cpu->cpu_frequency / 4) / divisor;
HW_TRACE ((me, "divide rate %ld, baud rate %ld",
divisor, baud));
controller->baud_cycle = divisor;
}
break;
case M6811_SCCR1:
{
if (val & M6811_M)
controller->data_length = 11;
else
controller->data_length = 10;
cpu->ios[M6811_SCCR1] = val;
}
break;
case M6811_SCCR2:
if ((val & M6811_RE) == 0)
{
val &= ~(M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF);
val |= (cpu->ios[M6811_SCCR2]
& (M6811_RDRF|M6811_IDLE|M6811_OR|M6811_NF|M6811_NF));
cpu->ios[M6811_SCCR2] = val;
break;
}
/* Activate reception. */
if (controller->rx_poll_event == 0)
{
long clock_cycle;
/* Compute CPU clock cycles to wait for the next character. */
clock_cycle = controller->data_length * controller->baud_cycle;
controller->rx_poll_event = hw_event_queue_schedule (me, clock_cycle,
m68hc11sio_rx_poll,
NULL);
}
cpu->ios[M6811_SCCR2] = val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
/* No effect. */
case M6811_SCSR:
return 1;
case M6811_SCDR:
if (!(cpu->ios[M6811_SCSR] & M6811_TDRE))
{
return 0;
}
controller->tx_char = val;
controller->tx_has_char = 1;
if ((cpu->ios[M6811_SCCR2] & M6811_TE)
&& controller->tx_poll_event == 0)
{
m68hc11sio_tx_poll (me, NULL);
}
return 1;
default:
return 0;
}
return nr_bytes;
}
const struct hw_descriptor dv_m68hc11sio_descriptor[] = {
{ "m68hc11sio", m68hc11sio_finish, },
{ NULL },
};

508
sim/m68hc11/dv-m68hc11spi.c Normal file
View File

@ -0,0 +1,508 @@
/* dv-m68hc11spi.c -- Simulation of the 68HC11 SPI
Copyright (C) 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
This file is part of the program GDB, the GNU debugger.
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
#include "dv-sockser.h"
#include "sim-assert.h"
/* DEVICE
m68hc11spi - m68hc11 SPI interface
DESCRIPTION
Implements the m68hc11 Synchronous Serial Peripheral Interface
described in the m68hc11 user guide (Chapter 8 in pink book).
The SPI I/O controller is directly connected to the CPU
interrupt. The simulator implements:
- SPI clock emulation
- Data transfer
- Write collision detection
PROPERTIES
None
PORTS
reset (input)
Reset port. This port is only used to simulate a reset of the SPI
I/O controller. It should be connected to the RESET output of the cpu.
*/
/* port ID's */
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11spi_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
/* SPI */
struct m68hc11spi
{
/* Information about next character to be transmited. */
unsigned char tx_char;
int tx_bit;
unsigned char mode;
unsigned char rx_char;
unsigned char rx_clear_scsr;
unsigned char clk_pin;
/* SPI clock rate (twice the real clock). */
unsigned int clock;
/* Periodic SPI event. */
struct hw_event* spi_event;
};
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc */
static hw_io_read_buffer_method m68hc11spi_io_read_buffer;
static hw_io_write_buffer_method m68hc11spi_io_write_buffer;
static hw_port_event_method m68hc11spi_port_event;
static hw_ioctl_method m68hc11spi_ioctl;
#define M6811_SPI_FIRST_REG (M6811_SPCR)
#define M6811_SPI_LAST_REG (M6811_SPDR)
static void
attach_m68hc11spi_regs (struct hw *me,
struct m68hc11spi *controller)
{
hw_attach_address (hw_parent (me), 0, io_map,
M6811_SPI_FIRST_REG,
M6811_SPI_LAST_REG - M6811_SPI_FIRST_REG + 1,
me);
}
static void
m68hc11spi_finish (struct hw *me)
{
struct m68hc11spi *controller;
controller = HW_ZALLOC (me, struct m68hc11spi);
me->overlap_mode_hw = 1;
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11spi_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11spi_io_write_buffer);
set_hw_ports (me, m68hc11spi_ports);
set_hw_port_event (me, m68hc11spi_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11spi_ioctl);
#else
me->to_ioctl = m68hc11spi_ioctl;
#endif
/* Attach ourself to our parent bus. */
attach_m68hc11spi_regs (me, controller);
/* Initialize to reset state. */
controller->spi_event = NULL;
controller->rx_clear_scsr = 0;
}
/* An event arrives on an interrupt port */
static void
m68hc11spi_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11spi *controller;
sim_cpu* cpu;
unsigned8 val;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "SPI reset"));
/* Reset the state of SPI registers. */
controller->rx_clear_scsr = 0;
if (controller->spi_event)
{
hw_event_queue_deschedule (me, controller->spi_event);
controller->spi_event = 0;
}
val = 0;
m68hc11spi_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_SPCR, 1);
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
static void
set_bit_port (struct hw *me, sim_cpu *cpu, int port, int mask, int value)
{
/* TODO: Post an event to inform other devices that pin 'port' changes.
This has only a sense if we provide some device that is logically
connected to these pin ports (SCLK and MOSI) and that handles
the SPI protocol. */
if (value)
cpu->ios[port] |= mask;
else
cpu->ios[port] &= ~mask;
}
/* When a character is sent/received by the SPI, the PD2..PD5 line
are driven by the following signals:
B7 B6
-----+---------+--------+---/-+-------
MOSI | | | | | |
MISO +---------+--------+---/-+
____ ___
CLK _______/ \____/ \__ CPOL=0, CPHA=0
_______ ____ __
\____/ \___/ CPOL=1, CPHA=0
____ ____ __
__/ \____/ \___/ CPOL=0, CPHA=1
__ ____ ___
\____/ \____/ \__ CPOL=1, CPHA=1
SS ___ ____
\__________________________//___/
MISO = PD2
MOSI = PD3
SCK = PD4
SS = PD5
*/
#define SPI_START_BIT 0
#define SPI_MIDDLE_BIT 1
void
m68hc11spi_clock (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11spi* controller;
sim_cpu *cpu;
int check_interrupt = 0;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
/* Cleanup current event. */
if (controller->spi_event)
{
hw_event_queue_deschedule (me, controller->spi_event);
controller->spi_event = 0;
}
/* Change a bit of data at each two SPI event. */
if (controller->mode == SPI_START_BIT)
{
/* Reflect the bit value on bit 2 of port D. */
set_bit_port (me, cpu, M6811_PORTD, (1 << 2),
(controller->tx_char & (1 << controller->tx_bit)));
controller->tx_bit--;
controller->mode = SPI_MIDDLE_BIT;
}
else
{
controller->mode = SPI_START_BIT;
}
/* Change the SPI clock at each event on bit 4 of port D. */
controller->clk_pin = ~controller->clk_pin;
set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
/* Transmit is now complete for this byte. */
if (controller->mode == SPI_START_BIT && controller->tx_bit < 0)
{
controller->rx_clear_scsr = 0;
cpu->ios[M6811_SPSR] |= M6811_SPIF;
if (cpu->ios[M6811_SPCR] & M6811_SPIE)
check_interrupt = 1;
}
else
{
controller->spi_event = hw_event_queue_schedule (me, controller->clock,
m68hc11spi_clock,
NULL);
}
if (check_interrupt)
interrupts_update_pending (&cpu->cpu_interrupts);
}
/* Flags of the SPCR register. */
io_reg_desc spcr_desc[] = {
{ M6811_SPIE, "SPIE ", "Serial Peripheral Interrupt Enable" },
{ M6811_SPE, "SPE ", "Serial Peripheral System Enable" },
{ M6811_DWOM, "DWOM ", "Port D Wire-OR mode option" },
{ M6811_MSTR, "MSTR ", "Master Mode Select" },
{ M6811_CPOL, "CPOL ", "Clock Polarity" },
{ M6811_CPHA, "CPHA ", "Clock Phase" },
{ M6811_SPR1, "SPR1 ", "SPI Clock Rate Select" },
{ M6811_SPR0, "SPR0 ", "SPI Clock Rate Select" },
{ 0, 0, 0 }
};
/* Flags of the SPSR register. */
io_reg_desc spsr_desc[] = {
{ M6811_SPIF, "SPIF ", "SPI Transfer Complete flag" },
{ M6811_WCOL, "WCOL ", "Write Collision" },
{ M6811_MODF, "MODF ", "Mode Fault" },
{ 0, 0, 0 }
};
static void
m68hc11spi_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11spi *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
sim_io_printf (sd, "M68HC11 SPI:\n");
base = cpu_get_io_base (cpu);
val = cpu->ios[M6811_SPCR];
print_io_byte (sd, "SPCR", spcr_desc, val, base + M6811_SPCR);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_SPSR];
print_io_byte (sd, "SPSR", spsr_desc, val, base + M6811_SPSR);
sim_io_printf (sd, "\n");
if (controller->spi_event)
{
signed64 t;
t = hw_event_remain_time (me, controller->spi_event);
sim_io_printf (sd, " SPI operation finished in %ld cycles\n",
(long) t);
}
}
static int
m68hc11spi_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11spi_info (me);
return 0;
}
/* generic read/write */
static unsigned
m68hc11spi_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11spi *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
switch (base)
{
case M6811_SPSR:
controller->rx_clear_scsr = cpu->ios[M6811_SCSR]
& (M6811_SPIF | M6811_WCOL | M6811_MODF);
case M6811_SPCR:
val = cpu->ios[base];
break;
case M6811_SPDR:
if (controller->rx_clear_scsr)
{
cpu->ios[M6811_SPSR] &= ~controller->rx_clear_scsr;
controller->rx_clear_scsr = 0;
}
val = controller->rx_char;
break;
default:
return 0;
}
*((unsigned8*) dest) = val;
return 1;
}
static unsigned
m68hc11spi_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11spi *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
val = *((const unsigned8*) source);
switch (base)
{
case M6811_SPCR:
cpu->ios[M6811_SPCR] = val;
/* The SPI clock rate is 2, 4, 16, 32 of the internal CPU clock.
We have to drive the clock pin and need a 2x faster clock. */
switch (val & (M6811_SPR1 | M6811_SPR0))
{
case 0:
controller->clock = 1;
break;
case 1:
controller->clock = 2;
break;
case 2:
controller->clock = 8;
break;
default:
controller->clock = 16;
break;
}
/* Set the clock pin. */
if ((val & M6811_CPOL)
&& (controller->spi_event == 0
|| ((val & M6811_CPHA) && controller->mode == 1)))
controller->clk_pin = 1;
else
controller->clk_pin = 0;
set_bit_port (me, cpu, M6811_PORTD, (1 << 4), controller->clk_pin);
break;
/* Can't write to SPSR. */
case M6811_SPSR:
break;
case M6811_SPDR:
if (!(cpu->ios[M6811_SPCR] & M6811_SPE))
{
return 0;
}
/* If transfer is taking place, a write to SPDR
generates a collision. */
if (controller->spi_event)
{
cpu->ios[M6811_SPSR] |= M6811_WCOL;
break;
}
/* Refuse the write if there was no read of SPSR. */
/* ???? TBD. */
/* Prepare to send a byte. */
controller->tx_char = val;
controller->tx_bit = 7;
controller->mode = 0;
/* Toggle clock pin internal value when CPHA is 0 so that
it will really change in the middle of a bit. */
if (!(cpu->ios[M6811_SPCR] & M6811_CPHA))
controller->clk_pin = ~controller->clk_pin;
cpu->ios[M6811_SPDR] = val;
/* Activate transmission. */
m68hc11spi_clock (me, NULL);
break;
default:
return 0;
}
return nr_bytes;
}
const struct hw_descriptor dv_m68hc11spi_descriptor[] = {
{ "m68hc11spi", m68hc11spi_finish, },
{ NULL },
};

607
sim/m68hc11/dv-m68hc11tim.c Normal file
View File

@ -0,0 +1,607 @@
/* dv-m68hc11tim.c -- Simulation of the 68HC11 timer devices.
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
This file is part of the program GDB, the GNU debugger.
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 vertimn 2 of the License, or
(at your option) any later vertimn.
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
#include "sim-assert.h"
/* DEVICE
m68hc11tim - m68hc11 timer devices
DESCRIPTION
Implements the m68hc11 timer as described in Chapter 10
of the pink book.
PROPERTIES
none
PORTS
reset (input)
Reset the timer device. This port must be connected to
the cpu-reset output port.
*/
/* port ID's */
enum
{
RESET_PORT
};
static const struct hw_port_descriptor m68hc11tim_ports[] =
{
{ "reset", RESET_PORT, 0, input_port, },
{ NULL, },
};
/* Timer Controller information. */
struct m68hc11tim
{
unsigned long cop_delay;
unsigned long rti_delay;
unsigned long ovf_delay;
signed64 clock_prescaler;
signed64 tcnt_adjust;
/* Periodic timers. */
struct hw_event *rti_timer_event;
struct hw_event *cop_timer_event;
struct hw_event *tof_timer_event;
struct hw_event *cmp_timer_event;
};
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc. */
static hw_io_read_buffer_method m68hc11tim_io_read_buffer;
static hw_io_write_buffer_method m68hc11tim_io_write_buffer;
static hw_port_event_method m68hc11tim_port_event;
static hw_ioctl_method m68hc11tim_ioctl;
#define M6811_TIMER_FIRST_REG (M6811_TCTN)
#define M6811_TIMER_LAST_REG (M6811_PACNT)
static void
attach_m68hc11tim_regs (struct hw *me,
struct m68hc11tim *controller)
{
hw_attach_address (hw_parent (me), 0, io_map,
M6811_TIMER_FIRST_REG,
M6811_TIMER_LAST_REG - M6811_TIMER_FIRST_REG + 1,
me);
}
static void
m68hc11tim_finish (struct hw *me)
{
struct m68hc11tim *controller;
controller = HW_ZALLOC (me, struct m68hc11tim);
me->overlap_mode_hw = 1;
set_hw_data (me, controller);
set_hw_io_read_buffer (me, m68hc11tim_io_read_buffer);
set_hw_io_write_buffer (me, m68hc11tim_io_write_buffer);
set_hw_ports (me, m68hc11tim_ports);
set_hw_port_event (me, m68hc11tim_port_event);
#ifdef set_hw_ioctl
set_hw_ioctl (me, m68hc11tim_ioctl);
#else
me->to_ioctl = m68hc11tim_ioctl;
#endif
/* Preset defaults. */
controller->clock_prescaler = 1;
controller->tcnt_adjust = 0;
/* Attach ourself to our parent bus. */
attach_m68hc11tim_regs (me, controller);
}
/* An event arrives on an interrupt port. */
static void
m68hc11tim_port_event (struct hw *me,
int my_port,
struct hw *source,
int source_port,
int level)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
unsigned8 val;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
switch (my_port)
{
case RESET_PORT:
{
HW_TRACE ((me, "Timer reset"));
/* Cancel all timer events. */
if (controller->rti_timer_event)
{
hw_event_queue_deschedule (me, controller->rti_timer_event);
controller->rti_timer_event = 0;
}
if (controller->cop_timer_event)
{
hw_event_queue_deschedule (me, controller->cop_timer_event);
controller->cop_timer_event = 0;
}
if (controller->tof_timer_event)
{
hw_event_queue_deschedule (me, controller->tof_timer_event);
controller->tof_timer_event = 0;
}
if (controller->cmp_timer_event)
{
hw_event_queue_deschedule (me, controller->cmp_timer_event);
controller->cmp_timer_event = 0;
}
/* Reset the state of Timer registers. This also restarts
the timer events (overflow and RTI clock). */
val = 0;
m68hc11tim_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_TMSK2, 1);
m68hc11tim_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_TFLG2, 1);
m68hc11tim_io_write_buffer (me, &val, io_map,
(unsigned_word) M6811_PACTL, 1);
break;
}
default:
hw_abort (me, "Event on unknown port %d", my_port);
break;
}
}
enum event_type
{
COP_EVENT,
RTI_EVENT,
OVERFLOW_EVENT,
COMPARE_EVENT
};
void
m68hc11tim_timer_event (struct hw *me, void *data)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
enum event_type type;
unsigned long delay;
struct hw_event **eventp;
int check_interrupt = 0;
unsigned mask;
unsigned flags;
unsigned long tcnt;
int i;
controller = hw_data (me);
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
type = (enum event_type) ((long) data) & 0x0FF;
delay = 0;
switch (type)
{
case COP_EVENT:
eventp = &controller->cop_timer_event;
delay = controller->cop_delay;
check_interrupt = 1;
break;
case RTI_EVENT:
eventp = &controller->rti_timer_event;
delay = controller->rti_delay;
if (((long) (data) & 0x0100) == 0)
{
cpu->ios[M6811_TFLG2] |= M6811_RTIF;
check_interrupt = 1;
}
break;
case OVERFLOW_EVENT:
eventp = &controller->tof_timer_event;
delay = controller->ovf_delay;
cpu->ios[M6811_TFLG2] |= M6811_TOF;
break;
case COMPARE_EVENT:
eventp = &controller->cmp_timer_event;
/* Get current free running counter. */
tcnt = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ controller->clock_prescaler);
tcnt &= 0x0ffffL;
flags = cpu->ios[M6811_TMSK1];
mask = 0x80;
delay = 65536;
/* Scan each output compare register to see if one matches
the free running counter. Set the corresponding OCi flag
if the output compare is enabled. */
for (i = M6811_TOC1; i <= M6811_TOC5; i += 2, mask >>= 1)
{
unsigned short compare;
compare = (cpu->ios[i] << 8) + cpu->ios[i+1];
if (compare == tcnt && (flags & mask))
{
cpu->ios[M6811_TFLG1] |= mask;
check_interrupt++;
}
/* Compute how many times for the next match. */
if (compare > tcnt)
compare = compare - tcnt;
else
compare = compare - tcnt + 65536;
if (compare < delay)
delay = compare;
}
delay = delay * controller->clock_prescaler;
/* Deactivate the compare timer if no output compare is enabled. */
if ((flags & 0xF0) == 0)
delay = 0;
break;
default:
eventp = 0;
break;
}
if (*eventp)
{
hw_event_queue_deschedule (me, *eventp);
*eventp = 0;
}
if (delay != 0)
{
*eventp = hw_event_queue_schedule (me, delay,
m68hc11tim_timer_event,
(void*) type);
}
if (check_interrupt)
interrupts_update_pending (&cpu->cpu_interrupts);
}
/* Descriptions of the Timer I/O ports. These descriptions are only used to
give information of the Timer device under GDB. */
io_reg_desc tmsk2_desc[] = {
{ M6811_TOI, "TOI ", "Timer Overflow Interrupt Enable" },
{ M6811_RTII, "RTII ", "RTI Interrupt Enable" },
{ M6811_PAOVI, "PAOVI ", "Pulse Accumulator Overflow Interrupt Enable" },
{ M6811_PAII, "PAII ", "Pulse Accumulator Interrupt Enable" },
{ M6811_PR1, "PR1 ", "Timer prescaler (PR1)" },
{ M6811_PR0, "PR0 ", "Timer prescaler (PR0)" },
{ M6811_TPR_1, "TPR_1 ", "Timer prescaler div 1" },
{ M6811_TPR_4, "TPR_4 ", "Timer prescaler div 4" },
{ M6811_TPR_8, "TPR_8 ", "Timer prescaler div 8" },
{ M6811_TPR_16, "TPR_16", "Timer prescaler div 16" },
{ 0, 0, 0 }
};
io_reg_desc tflg2_desc[] = {
{ M6811_TOF, "TOF ", "Timer Overflow Bit" },
{ M6811_RTIF, "RTIF ", "Read Time Interrupt Flag" },
{ M6811_PAOVF, "PAOVF ", "Pulse Accumulator Overflow Interrupt Flag" },
{ M6811_PAIF, "PAIF ", "Pulse Accumulator Input Edge" },
{ 0, 0, 0 }
};
io_reg_desc pactl_desc[] = {
{ M6811_DDRA7, "DDRA7 ", "Data Direction for Port A bit-7" },
{ M6811_PAEN, "PAEN ", "Pulse Accumulator System Enable" },
{ M6811_PAMOD, "PAMOD ", "Pulse Accumulator Mode" },
{ M6811_PEDGE, "PEDGE ", "Pulse Accumulator Edge Control" },
{ M6811_RTR1, "RTR1 ", "RTI Interrupt rate select (RTR1)" },
{ M6811_RTR0, "RTR0 ", "RTI Interrupt rate select (RTR0)" },
{ 0, 0, 0 }
};
static double
to_realtime (sim_cpu *cpu, signed64 t)
{
return (double) (t) / (double) (cpu->cpu_frequency / 4);
}
static void
m68hc11tim_print_timer (struct hw *me, const char *name,
struct hw_event *event)
{
SIM_DESC sd;
sd = hw_system (me);
if (event == 0)
{
sim_io_printf (sd, " No %s interrupt will be raised.\n", name);
}
else
{
signed64 t;
double dt;
sim_cpu* cpu;
cpu = STATE_CPU (sd, 0);
t = hw_event_remain_time (me, event);
dt = to_realtime (cpu, t) * 1000.0;
sim_io_printf (sd, " Next %s interrupt in %ld cycles (%3.3f ms)\n",
name, (long) t, dt);
}
}
static void
m68hc11tim_info (struct hw *me)
{
SIM_DESC sd;
uint16 base = 0;
sim_cpu *cpu;
struct m68hc11tim *controller;
uint8 val;
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
sim_io_printf (sd, "M68HC11 Timer:\n");
base = cpu_get_io_base (cpu);
val = cpu->ios[M6811_TMSK2];
print_io_byte (sd, "TMSK2 ", tmsk2_desc, val, base + M6811_TMSK2);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_TFLG2];
print_io_byte (sd, "TFLG2", tflg2_desc, val, base + M6811_TFLG2);
sim_io_printf (sd, "\n");
val = cpu->ios[M6811_PACTL];
print_io_byte (sd, "PACTL", pactl_desc, val, base + M6811_PACTL);
sim_io_printf (sd, "\n");
/* Give info about the next timer interrupts. */
m68hc11tim_print_timer (me, "RTI", controller->rti_timer_event);
m68hc11tim_print_timer (me, "COP", controller->cop_timer_event);
m68hc11tim_print_timer (me, "OVERFLOW", controller->tof_timer_event);
m68hc11tim_print_timer (me, "COMPARE", controller->cmp_timer_event);
}
static int
m68hc11tim_ioctl (struct hw *me,
hw_ioctl_request request,
va_list ap)
{
m68hc11tim_info (me);
return 0;
}
/* generic read/write */
static unsigned
m68hc11tim_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
unsigned8 val;
HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
switch (base)
{
/* The cpu_absolute_cycle is updated after each instruction.
Reading in a 16-bit register will be split in two accesses
but this will be atomic within the simulator. */
case M6811_TCTN_H:
val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ (controller->clock_prescaler * 256));
break;
case M6811_TCTN_L:
val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ controller->clock_prescaler);
break;
default:
val = cpu->ios[base];
break;
}
*((unsigned8*) dest) = val;
return 1;
}
static unsigned
m68hc11tim_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
SIM_DESC sd;
struct m68hc11tim *controller;
sim_cpu *cpu;
unsigned8 val, n;
signed64 adj;
int reset_compare = 0;
HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
sd = hw_system (me);
cpu = STATE_CPU (sd, 0);
controller = hw_data (me);
val = *((const unsigned8*) source);
switch (base)
{
/* Set the timer counter low part, trying to preserve the low part.
We compute the absolute cycle adjustment that we have to apply
to obtain the timer current value. Computation must be made
in 64-bit to avoid overflow problems. */
case M6811_TCTN_L:
adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ (controller->clock_prescaler * (signed64) 256)) & 0x0FF;
adj = cpu->cpu_absolute_cycle
- (adj * controller->clock_prescaler * (signed64) 256)
- ((signed64) adj * controller->clock_prescaler);
controller->tcnt_adjust = adj;
reset_compare = 1;
break;
case M6811_TCTN_H:
adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
/ controller->clock_prescaler) & 0x0ff;
adj = cpu->cpu_absolute_cycle
- ((signed64) val * controller->clock_prescaler * (signed64) 256)
- (adj * controller->clock_prescaler);
controller->tcnt_adjust = adj;
reset_compare = 1;
break;
case M6811_TMSK2:
/* Timer prescaler cannot be changed after 64 bus cycles. */
if (cpu->cpu_absolute_cycle >= 64)
{
val &= ~(M6811_PR1 | M6811_PR0);
val |= cpu->ios[M6811_TMSK2] & (M6811_PR1 | M6811_PR0);
}
switch (val & (M6811_PR1 | M6811_PR0))
{
case 0:
n = 1;
break;
case M6811_PR0:
n = 4;
break;
case M6811_PR1:
n = 8;
break;
default:
case M6811_PR1 | M6811_PR0:
n = 16;
break;
}
if (controller->clock_prescaler != n)
{
controller->clock_prescaler = n;
controller->ovf_delay = n * 65536;
m68hc11tim_timer_event (me, (void*) (OVERFLOW_EVENT| 0x100));
}
cpu->ios[base] = val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
case M6811_PACTL:
n = (1 << ((val & (M6811_RTR1 | M6811_RTR0))));
cpu->ios[base] = val;
controller->rti_delay = (long) (n) * 8192;
m68hc11tim_timer_event (me, (void*) (RTI_EVENT| 0x100));
break;
case M6811_TFLG2:
if (val & M6811_TOF)
val &= ~M6811_TOF;
else
val |= cpu->ios[M6811_TFLG2] & M6811_TOF;
/* Clear the Real Time interrupt flag. */
if (val & M6811_RTIF)
val &= ~M6811_RTIF;
else
val |= cpu->ios[M6811_TFLG2] & M6811_RTIF;
cpu->ios[base] = val;
interrupts_update_pending (&cpu->cpu_interrupts);
break;
case M6811_TOC1:
case M6811_TOC2:
case M6811_TOC3:
case M6811_TOC4:
case M6811_TOC5:
cpu->ios[base] = val;
reset_compare = 1;
break;
default:
return 0;
}
/* Re-compute the next timer compare event. */
if (reset_compare)
{
m68hc11tim_timer_event (me, (void*) (COMPARE_EVENT));
}
return nr_bytes;
}
const struct hw_descriptor dv_m68hc11tim_descriptor[] = {
{ "m68hc11tim", m68hc11tim_finish, },
{ NULL },
};

361
sim/m68hc11/dv-nvram.c Normal file
View File

@ -0,0 +1,361 @@
/* dv-nvram.c -- Generic driver for a non volatile ram (battery saved)
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
(From a driver model Contributed by Cygnus Solutions.)
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "sim-main.h"
#include "hw-main.h"
#include "sim-assert.h"
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* DEVICE
nvram - Non Volatile Ram
DESCRIPTION
Implements a generic battery saved CMOS ram. This ram device does
not contain any realtime clock and does not generate any interrupt.
The ram content is loaded from a file and saved when it is changed.
It is intended to be generic.
PROPERTIES
overlap? <bool>
Boolean property which indicates whether the device can overlap
another device. By default, overlapping is not allowed.
reg <base> <length>
Base and size of the non-volatile ram bank.
file <path>
Path where the memory must be saved or loaded when we start.
mode {map | save-modified | save-all}
Controls how to load and save the memory content.
map The file is mapped in memory
save-modified The simulator keeps an open file descriptor to
the file and saves portion of memory which are
modified.
save-all The simulator saves the complete memory each time
it's modified (it does not keep an open file
descriptor).
PORTS
None.
NOTES
This device is independent of the Motorola 68hc11.
*/
/* static functions */
/* Control of how to access the ram and save its content. */
enum nvram_mode
{
/* Save the complete ram block each time it's changed.
We don't keep an open file descriptor. This should be
ok for small memory banks. */
NVRAM_SAVE_ALL,
/* Save only the memory bytes which are modified.
This mode means that we have to keep an open file
descriptor (O_RDWR). It's good for middle sized memory banks. */
NVRAM_SAVE_MODIFIED,
/* Map file in memory (not yet implemented).
This mode is suitable for large memory banks. We don't allocate
a buffer to represent the ram, instead it's mapped in memory
with mmap. */
NVRAM_MAP_FILE
};
struct nvram
{
address_word base_address; /* Base address of ram. */
unsigned size; /* Size of ram. */
unsigned8 *data; /* Pointer to ram memory. */
const char *file_name; /* Path of ram file. */
int fd; /* File description of opened ram file. */
enum nvram_mode mode; /* How load/save ram file. */
};
/* Finish off the partially created hw device. Attach our local
callbacks. Wire up our port names etc. */
static hw_io_read_buffer_method nvram_io_read_buffer;
static hw_io_write_buffer_method nvram_io_write_buffer;
static void
attach_nvram_regs (struct hw *me, struct nvram *controller)
{
unsigned_word attach_address;
int attach_space;
unsigned attach_size;
reg_property_spec reg;
int result, oerrno;
/* Get the flag that controls overlapping of ram bank to another device. */
if (hw_find_property (me, "overlap?") != NULL
&& hw_find_boolean_property (me, "overlap?"))
me->overlap_mode_hw = 1;
/* Get ram bank description (base and size). */
if (hw_find_property (me, "reg") == NULL)
hw_abort (me, "Missing \"reg\" property");
if (!hw_find_reg_array_property (me, "reg", 0, &reg))
hw_abort (me, "\"reg\" property must contain one addr/size entry");
hw_unit_address_to_attach_address (hw_parent (me),
&reg.address,
&attach_space,
&attach_address,
me);
hw_unit_size_to_attach_size (hw_parent (me),
&reg.size,
&attach_size, me);
hw_attach_address (hw_parent (me), 0,
attach_space, attach_address, attach_size,
me);
controller->mode = NVRAM_SAVE_ALL;
controller->base_address = attach_address;
controller->size = attach_size;
controller->fd = -1;
/* Get the file where the ram content must be loaded/saved. */
if(hw_find_property (me, "file") == NULL)
hw_abort (me, "Missing \"file\" property");
controller->file_name = hw_find_string_property (me, "file");
/* Get the mode which defines how to save the memory. */
if(hw_find_property (me, "mode") != NULL)
{
const char *value = hw_find_string_property (me, "mode");
if (strcmp (value, "map") == 0)
controller->mode = NVRAM_MAP_FILE;
else if (strcmp (value, "save-modified") == 0)
controller->mode = NVRAM_SAVE_MODIFIED;
else if (strcmp (value, "save-all") == 0)
controller->mode = NVRAM_SAVE_ALL;
else
hw_abort (me, "illegal value for mode parameter `%s': "
"use map, save-modified or save-all", value);
}
/* Initialize the ram by loading/mapping the file in memory.
If the file does not exist, create and give it some content. */
switch (controller->mode)
{
case NVRAM_MAP_FILE:
hw_abort (me, "'map' mode is not yet implemented, use 'save-modified'");
break;
case NVRAM_SAVE_MODIFIED:
case NVRAM_SAVE_ALL:
controller->data = (char*) malloc (attach_size);
if (controller->data == 0)
hw_abort (me, "Not enough memory, try to use the mode 'map'");
memset (controller->data, 0, attach_size);
controller->fd = open (controller->file_name, O_RDWR);
if (controller->fd < 0)
{
controller->fd = open (controller->file_name,
O_RDWR | O_CREAT, 0644);
if (controller->fd < 0)
hw_abort (me, "Cannot open or create file '%s'",
controller->file_name);
result = write (controller->fd, controller->data, attach_size);
if (result != attach_size)
{
oerrno = errno;
free (controller->data);
close (controller->fd);
errno = oerrno;
hw_abort (me, "Failed to save the ram content");
}
}
else
{
result = read (controller->fd, controller->data, attach_size);
if (result != attach_size)
{
oerrno = errno;
free (controller->data);
close (controller->fd);
errno = oerrno;
hw_abort (me, "Failed to load the ram content");
}
}
if (controller->mode == NVRAM_SAVE_ALL)
{
close (controller->fd);
controller->fd = -1;
}
break;
default:
break;
}
}
static void
nvram_finish (struct hw *me)
{
struct nvram *controller;
controller = HW_ZALLOC (me, struct nvram);
set_hw_data (me, controller);
set_hw_io_read_buffer (me, nvram_io_read_buffer);
set_hw_io_write_buffer (me, nvram_io_write_buffer);
/* Attach ourself to our parent bus. */
attach_nvram_regs (me, controller);
}
/* generic read/write */
static unsigned
nvram_io_read_buffer (struct hw *me,
void *dest,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct nvram *controller = hw_data (me);
HW_TRACE ((me, "read 0x%08lx %d [%ld]",
(long) base, (int) nr_bytes,
(long) (base - controller->base_address)));
base -= controller->base_address;
if (base + nr_bytes > controller->size)
nr_bytes = controller->size - base;
memcpy (dest, &controller->data[base], nr_bytes);
return nr_bytes;
}
static unsigned
nvram_io_write_buffer (struct hw *me,
const void *source,
int space,
unsigned_word base,
unsigned nr_bytes)
{
struct nvram *controller = hw_data (me);
HW_TRACE ((me, "write 0x%08lx %d [%ld]",
(long) base, (int) nr_bytes,
(long) (base - controller->base_address)));
base -= controller->base_address;
if (base + nr_bytes > controller->size)
nr_bytes = controller->size - base;
switch (controller->mode)
{
case NVRAM_SAVE_ALL:
{
int fd, result, oerrno;
fd = open (controller->file_name, O_WRONLY, 0644);
if (fd < 0)
{
return 0;
}
memcpy (&controller->data[base], source, nr_bytes);
result = write (fd, controller->data, controller->size);
oerrno = errno;
close (fd);
errno = oerrno;
if (result != controller->size)
{
return 0;
}
return nr_bytes;
}
case NVRAM_SAVE_MODIFIED:
{
off_t pos;
int result;
pos = lseek (controller->fd, (off_t) base, SEEK_SET);
if (pos != (off_t) base)
return 0;
result = write (controller->fd, source, nr_bytes);
if (result < 0)
return 0;
nr_bytes = result;
break;
}
default:
break;
}
memcpy (&controller->data[base], source, nr_bytes);
return nr_bytes;
}
const struct hw_descriptor dv_nvram_descriptor[] = {
{ "nvram", nvram_finish, },
{ NULL },
};

161
sim/m68hc11/emulos.c Normal file
View File

@ -0,0 +1,161 @@
/* emulos.c -- Small OS 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"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef WIN32
#include <sys/types.h>
#include <sys/time.h>
/* This file emulates some OS system calls.
It's basically used to give access to the host OS facilities
like: stdin, stdout, files, time of day. */
static int bench_mode = -1;
static struct timeval bench_start;
static struct timeval bench_stop;
void
emul_bench (struct _sim_cpu* cpu)
{
int op;
op = cpu_get_d (cpu);
switch (op)
{
case 0:
bench_mode = 0;
gettimeofday (&bench_start, 0);
break;
case 1:
gettimeofday (&bench_stop, 0);
if (bench_mode != 0)
printf ("bench start not called...\n");
bench_mode = 1;
break;
case 2:
{
int sz = 0;
int addr = cpu_get_x (cpu);
double t_start, t_stop, t;
char buf[1024];
op = cpu_get_y (cpu);
t_start = (double) (bench_start.tv_sec) * 1.0e6;
t_start += (double) (bench_start.tv_usec);
t_stop = (double) (bench_stop.tv_sec) * 1.0e6;
t_stop += (double) (bench_stop.tv_usec);
while (sz < 1024)
{
buf[sz] = memory_read8 (cpu, addr);
if (buf[sz] == 0)
break;
sz ++;
addr++;
}
buf[1023] = 0;
if (bench_mode != 1)
printf ("bench_stop not called");
bench_mode = -1;
t = t_stop - t_start;
printf ("%-40.40s [%6d] %3.3f us\n", buf,
op, t / (double) (op));
break;
}
}
}
#endif
void
emul_write(struct _sim_cpu* state)
{
int addr = cpu_get_x (state) & 0x0FFFF;
int size = cpu_get_d (state) & 0x0FFFF;
if (addr + size > 0x0FFFF) {
size = 0x0FFFF - addr;
}
state->cpu_running = 0;
while (size)
{
uint8 val = memory_read8 (state, addr);
write(0, &val, 1);
addr ++;
size--;
}
}
/* emul_exit () is used by the default startup code of GCC to implement
the exit (). For a real target, this will create an ILLEGAL fault.
But doing an exit () on a real target is really a non-sense.
exit () is important for the validation of GCC. The exit status
is passed in 'D' register. */
void
emul_exit (sim_cpu *cpu)
{
sim_engine_halt (CPU_STATE (cpu), cpu,
NULL, NULL_CIA, sim_exited,
cpu_get_d (cpu));
}
void
emul_os (int code, sim_cpu *proc)
{
proc->cpu_current_cycle = 8;
switch (code)
{
case 0x0:
break;
/* 0xCD 0x01 */
case 0x01:
emul_write (proc);
break;
/* 0xCD 0x02 */
case 0x02:
break;
/* 0xCD 0x03 */
case 0x03:
emul_exit (proc);
break;
/* 0xCD 0x04 */
case 0x04:
#ifndef WIN32
emul_bench (proc);
#endif
break;
default:
break;
}
}

1484
sim/m68hc11/gencode.c Normal file

File diff suppressed because it is too large Load Diff

517
sim/m68hc11/interp.c Normal file
View File

@ -0,0 +1,517 @@
/* interp.c -- Simulator for Motorola 68HC11
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
This file is part of GDB, the GNU debugger.
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 2, 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, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "sim-main.h"
#include "sim-assert.h"
#include "sim-hw.h"
#include "sim-options.h"
#include "hw-tree.h"
#include "hw-device.h"
#include "hw-ports.h"
#ifndef MONITOR_BASE
# define MONITOR_BASE (0x0C000)
# define MONITOR_SIZE (0x04000)
#endif
static void sim_get_info (SIM_DESC sd, char *cmd);
char *interrupt_names[] = {
"reset",
"nmi",
"int",
NULL
};
#ifndef INLINE
#if defined(__GNUC__) && defined(__OPTIMIZE__)
#define INLINE __inline__
#else
#define INLINE
#endif
#endif
struct sim_info_list
{
const char *name;
const char *device;
};
struct sim_info_list dev_list[] = {
{"cpu", "/m68hc11"},
{"timer", "/m68hc11/m68hc11tim"},
{"sio", "/m68hc11/m68hc11sio"},
{"spi", "/m68hc11/m68hc11spi"},
{"eeprom", "/m68hc11/m68hc11eepr"},
{0, 0}
};
/* Give some information about the simulator. */
static void
sim_get_info (SIM_DESC sd, char *cmd)
{
sim_cpu *cpu;
if (cmd != 0 && (cmd[0] == ' ' || cmd[0] == '-'))
{
int i;
struct hw *hw_dev;
cmd++;
for (i = 0; dev_list[i].name; i++)
if (strcmp (cmd, dev_list[i].name) == 0)
break;
if (dev_list[i].name == 0)
{
sim_io_eprintf (sd, "Device '%s' not found.\n", cmd);
sim_io_eprintf (sd, "Valid devices: cpu timer sio eeprom\n");
return;
}
hw_dev = sim_hw_parse (sd, dev_list[i].device);
if (hw_dev == 0)
{
sim_io_eprintf (sd, "Device '%s' not found\n", dev_list[i].device);
return;
}
hw_ioctl (hw_dev, 23, 0);
return;
}
cpu = STATE_CPU (sd, 0);
cpu_info (sd, cpu);
interrupts_info (sd, &cpu->cpu_interrupts);
}
void
sim_board_reset (SIM_DESC sd)
{
struct hw *hw_cpu;
sim_cpu *cpu;
cpu = STATE_CPU (sd, 0);
/* hw_cpu = sim_hw_parse (sd, "/"); */
hw_cpu = sim_hw_parse (sd, "/m68hc11");
if (hw_cpu == 0)
{
sim_io_eprintf (sd, "m68hc11 cpu not found in device tree.");
return;
}
cpu_reset (cpu);
hw_port_event (hw_cpu, 3, 0);
cpu_restart (cpu);
}
SIM_DESC
sim_open (SIM_OPEN_KIND kind, host_callback *callback,
struct _bfd *abfd, char **argv)
{
char **p;
SIM_DESC sd;
sim_cpu *cpu;
struct hw *device_tree;
sd = sim_state_alloc (kind, callback);
cpu = STATE_CPU (sd, 0);
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
/* for compatibility */
current_alignment = NONSTRICT_ALIGNMENT;
current_target_byte_order = BIG_ENDIAN;
cpu_initialize (sd, cpu);
cpu->cpu_use_elf_start = 1;
if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
return 0;
/* getopt will print the error message so we just have to exit if this fails.
FIXME: Hmmm... in the case of gdb we need getopt to call
print_filtered. */
if (sim_parse_args (sd, argv) != SIM_RC_OK)
{
/* Uninstall the modules to avoid memory leaks,
file descriptor leaks, etc. */
sim_module_uninstall (sd);
return 0;
}
device_tree = sim_hw_parse (sd, "/");
if (hw_tree_find_property (device_tree, "/m68hc11/reg") == 0)
{
/* Allocate core managed memory */
/* the monitor */
sim_do_commandf (sd, "memory region 0x%lx,0x%lx",
/* MONITOR_BASE, MONITOR_SIZE */
0x8000, 0x8000);
sim_do_command (sd, " memory region 0x000,0x8000");
sim_hw_parse (sd, "/m68hc11/reg 0x1000 0x03F");
}
if (hw_tree_find_property (device_tree, "/m68hc11/m68hc11sio/reg") == 0)
{
sim_hw_parse (sd, "/m68hc11/m68hc11sio/reg 0x2b 0x5");
sim_hw_parse (sd, "/m68hc11/m68hc11sio/backend stdio");
sim_hw_parse (sd, "/m68hc11 > cpu-reset reset /m68hc11/m68hc11sio");
}
if (hw_tree_find_property (device_tree, "/m68hc11/m68hc11tim/reg") == 0)
{
/* M68hc11 Timer configuration. */
sim_hw_parse (sd, "/m68hc11/m68hc11tim/reg 0x1b 0x5");
sim_hw_parse (sd, "/m68hc11 > cpu-reset reset /m68hc11/m68hc11tim");
}
/* Create the SPI device. */
if (hw_tree_find_property (device_tree, "/m68hc11/m68hc11spi/reg") == 0)
{
sim_hw_parse (sd, "/m68hc11/m68hc11spi/reg 0x28 0x3");
sim_hw_parse (sd, "/m68hc11 > cpu-reset reset /m68hc11/m68hc11spi");
}
if (hw_tree_find_property (device_tree, "/m68hc11/pram/reg") == 0)
{
/* M68hc11 persistent ram configuration. */
sim_hw_parse (sd, "/m68hc11/nvram/reg 0x0 256");
sim_hw_parse (sd, "/m68hc11/nvram/file m68hc11.ram");
sim_hw_parse (sd, "/m68hc11/nvram/mode save-modified");
sim_hw_parse (sd, "/m68hc11/nvram/overlap? true");
/*sim_hw_parse (sd, "/m68hc11 > cpu-reset reset /m68hc11/pram"); */
}
if (hw_tree_find_property (device_tree, "/m68hc11/m68hc11eepr/reg") == 0)
{
sim_hw_parse (sd, "/m68hc11/m68hc11eepr/reg 0xb000 512");
/* Connect the CPU reset to all devices. */
sim_hw_parse (sd, "/m68hc11 > cpu-reset reset /m68hc11/m68hc11eepr");
}
/* Check for/establish the a reference program image. */
if (sim_analyze_program (sd,
(STATE_PROG_ARGV (sd) != NULL
? *STATE_PROG_ARGV (sd)
: NULL), abfd) != SIM_RC_OK)
{
sim_module_uninstall (sd);
return 0;
}
/* Establish any remaining configuration options. */
if (sim_config (sd) != SIM_RC_OK)
{
sim_module_uninstall (sd);
return 0;
}
if (sim_post_argv_init (sd) != SIM_RC_OK)
{
/* Uninstall the modules to avoid memory leaks,
file descriptor leaks, etc. */
sim_module_uninstall (sd);
return 0;
}
if (abfd != NULL)
{
cpu->cpu_elf_start = bfd_get_start_address (abfd);
}
sim_board_reset (sd);
/* Fudge our descriptor. */
return sd;
}
void
sim_close (SIM_DESC sd, int quitting)
{
/* shut down modules */
sim_module_uninstall (sd);
/* Ensure that any resources allocated through the callback
mechanism are released: */
sim_io_shutdown (sd);
/* FIXME - free SD */
return;
}
void
sim_set_profile (int n)
{
}
void
sim_set_profile_size (int n)
{
}
/* Generic implementation of sim_engine_run that works within the
sim_engine setjmp/longjmp framework. */
void
sim_engine_run (SIM_DESC sd,
int next_cpu_nr, /* ignore */
int nr_cpus, /* ignore */
int siggnal) /* ignore */
{
sim_cpu *cpu;
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
cpu = STATE_CPU (sd, 0);
while (1)
{
cpu_single_step (cpu);
/* process any events */
if (sim_events_tickn (sd, cpu->cpu_current_cycle))
{
sim_events_process (sd);
}
}
}
int
sim_trace (SIM_DESC sd)
{
sim_resume (sd, 0, 0);
return 1;
}
void
sim_info (SIM_DESC sd, int verbose)
{
sim_io_eprintf (sd, "Simulator info:\n");
sim_io_eprintf (sd, " CPU Motorola 68HC11\n");
sim_get_info (sd, 0);
sim_module_info (sd, verbose || STATE_VERBOSE_P (sd));
}
SIM_RC
sim_create_inferior (SIM_DESC sd, struct _bfd *abfd,
char **argv, char **env)
{
sim_cpu *cpu;
int i;
cpu = STATE_CPU (sd, 0);
if (abfd != NULL)
{
cpu->cpu_elf_start = bfd_get_start_address (abfd);
}
/* reset all state information */
sim_board_reset (sd);
/* Get information about the number of pseudo registers. */
for (i = FIRST_SOFT_REGNUM; i <= ZD32_REGNUM; i++)
{
switch (i)
{
case TMP_REGNUM:
cpu->cpu_page0_reg[i - FIRST_SOFT_REGNUM] = 0;
break;
case Z_REGNUM:
case ZS_REGNUM:
cpu->cpu_page0_reg[i - FIRST_SOFT_REGNUM] = 2;
break;
case XY_REGNUM:
cpu->cpu_page0_reg[i - FIRST_SOFT_REGNUM] = 4;
break;
case FP_REGNUM:
cpu->cpu_page0_reg[i - FIRST_SOFT_REGNUM] = 6;
break;
default:
cpu->cpu_page0_reg[i - FIRST_SOFT_REGNUM]
= ((i - FIRST_SOFT_REGNUM) * 2) - 2;
break;
}
}
cpu->cpu_nb_pseudo_regs = 8;
return SIM_RC_OK;
}
void
sim_set_callbacks (host_callback *p)
{
/* m6811_callback = p; */
}
int
sim_fetch_register (SIM_DESC sd, int rn, unsigned char *memory, int length)
{
sim_cpu *cpu;
uint16 val;
cpu = STATE_CPU (sd, 0);
switch (rn)
{
case A_REGNUM:
val = cpu_get_a (cpu);
break;
case B_REGNUM:
val = cpu_get_b (cpu);
break;
case D_REGNUM:
val = cpu_get_d (cpu);
break;
case X_REGNUM:
val = cpu_get_x (cpu);
break;
case Y_REGNUM:
val = cpu_get_y (cpu);
break;
case SP_REGNUM:
val = cpu_get_sp (cpu);
break;
case PC_REGNUM:
val = cpu_get_pc (cpu);
break;
case PSW_REGNUM:
val = cpu_get_ccr (cpu);
break;
/* Read a pseudo register. Pseudo registers are located at
beginning of page 0. Each of them is 2 bytes. */
default:
if (rn < FIRST_SOFT_REGNUM || rn >= ZD32_REGNUM)
{
val = 0;
}
else
{
uint16 addr;
addr = cpu->cpu_page0_reg[rn - FIRST_SOFT_REGNUM];
val = memory_read16 (cpu, addr);
}
break;
}
memory[0] = val >> 8;
memory[1] = val & 0x0FF;
return 2;
}
int
sim_store_register (SIM_DESC sd, int rn, unsigned char *memory, int length)
{
uint16 val;
sim_cpu *cpu;
cpu = STATE_CPU (sd, 0);
val = *memory++;
if (length == 2)
val = (val << 8) | *memory;
switch (rn)
{
case D_REGNUM:
cpu_set_d (cpu, val);
break;
case A_REGNUM:
cpu_set_a (cpu, val);
break;
case B_REGNUM:
cpu_set_b (cpu, val);
break;
case X_REGNUM:
cpu_set_x (cpu, val);
break;
case Y_REGNUM:
cpu_set_y (cpu, val);
break;
case SP_REGNUM:
cpu_set_sp (cpu, val);
break;
case PC_REGNUM:
cpu_set_pc (cpu, val);
break;
case PSW_REGNUM:
cpu_set_ccr (cpu, val);
break;
/* Write a pseudo register. Pseudo registers are located at
beginning of page 0. Each of them is 2 bytes. */
default:
if (rn >= FIRST_SOFT_REGNUM && rn <= ZD32_REGNUM)
{
uint16 addr;
addr = cpu->cpu_page0_reg[rn - FIRST_SOFT_REGNUM];
memory_write16 (cpu, addr, val);
}
break;
}
return 2;
}
void
sim_size (int s)
{
;
}
void
sim_do_command (SIM_DESC sd, char *cmd)
{
char *mm_cmd = "memory-map";
char *int_cmd = "interrupt";
/* Commands available from GDB: */
if (sim_args_command (sd, cmd) != SIM_RC_OK)
{
if (strncmp (cmd, "info", sizeof ("info") - 1) == 0)
sim_get_info (sd, &cmd[4]);
else if (strncmp (cmd, "frame", sizeof ("frame") - 1) == 0)
cpu_print_frame (sd, STATE_CPU (sd, 0));
else if (strncmp (cmd, mm_cmd, strlen (mm_cmd) == 0))
sim_io_eprintf (sd,
"`memory-map' command replaced by `sim memory'\n");
else if (strncmp (cmd, int_cmd, strlen (int_cmd)) == 0)
sim_io_eprintf (sd, "`interrupt' command replaced by `sim watch'\n");
else
sim_io_eprintf (sd, "Unknown command `%s'\n", cmd);
}
}

297
sim/m68hc11/interrupts.c Normal file
View File

@ -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);
}

132
sim/m68hc11/interrupts.h Normal file
View File

@ -0,0 +1,132 @@
/* interrupts.h -- 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. */
#ifndef _M6811_SIM_INTERRUPTS_H
#define _M6811_SIM_INTERRUPTS_H
/* Definition of 68HC11 interrupts. These enum are used as an index
in the interrupt table. */
enum M6811_INT
{
M6811_INT_RESERVED1 = 0,
M6811_INT_RESERVED2,
M6811_INT_RESERVED3,
M6811_INT_RESERVED4,
M6811_INT_RESERVED5,
M6811_INT_RESERVED6,
M6811_INT_RESERVED7,
M6811_INT_RESERVED8,
M6811_INT_RESERVED9,
M6811_INT_RESERVED10,
M6811_INT_RESERVED11,
M6811_INT_SCI,
M6811_INT_SPI,
M6811_INT_AINPUT,
M6811_INT_AOVERFLOW,
M6811_INT_TCTN,
M6811_INT_OUTCMP5,
M6811_INT_OUTCMP4,
M6811_INT_OUTCMP3,
M6811_INT_OUTCMP2,
M6811_INT_OUTCMP1,
M6811_INT_INCMP3,
M6811_INT_INCMP2,
M6811_INT_INCMP1,
M6811_INT_RT,
M6811_INT_IRQ,
M6811_INT_XIRQ,
M6811_INT_SWI,
M6811_INT_ILLEGAL,
M6811_INT_COPRESET,
M6811_INT_COPFAIL,
M6811_INT_RESET,
M6811_INT_NUMBER
};
/* Structure to describe how to recognize an interrupt in the
68hc11 IO regs. */
struct interrupt_def
{
enum M6811_INT int_number;
unsigned char int_paddr;
unsigned char int_mask;
unsigned char enable_paddr;
unsigned char enabled_mask;
};
/* Management of 68HC11 interrupts:
- We use a table of 'interrupt_def' to describe the interrupts that must be
raised depending on IO register flags (enable and present flags).
- We keep a mask of pending interrupts. This mask is refreshed by
calling 'interrupts_update_pending'. It must be refreshed each time
an IO register is changed.
- 'interrupts_process' must be called after each insn. It has two purposes:
first it maintains a min/max count of CPU cycles between which interrupts
are masked; second it checks for pending interrupts and raise one if
interrupts are enabled. */
struct interrupts {
struct _sim_cpu *cpu;
/* Mask of current pending interrupts. */
unsigned long pending_mask;
/* Address of vector table. This is set depending on the
68hc11 init mode. */
uint16 vectors_addr;
/* Priority order of interrupts. This is controlled by setting the HPRIO
IO register. */
enum M6811_INT interrupt_order[M6811_INT_NUMBER];
/* Simulator statistics to report useful debug information to users. */
/* - Max/Min number of CPU cycles executed with interrupts masked. */
signed64 start_mask_cycle;
signed64 min_mask_cycles;
signed64 max_mask_cycles;
/* - Same for XIRQ. */
signed64 xirq_start_mask_cycle;
signed64 xirq_min_mask_cycles;
signed64 xirq_max_mask_cycles;
/* - Total number of interrupts raised. */
unsigned long nb_interrupts_raised;
};
extern int interrupts_initialize (struct _sim_cpu* cpu);
extern void interrupts_update_pending (struct interrupts* interrupts);
extern int interrupts_get_current (struct interrupts* interrupts);
extern int interrupts_process (struct interrupts* interrupts);
extern void interrupts_raise (struct interrupts* interrupts,
enum M6811_INT number);
extern void interrupts_info (SIM_DESC sd,
struct interrupts* interrupts);
#endif

639
sim/m68hc11/m68hc11_sim.c Normal file
View File

@ -0,0 +1,639 @@
/* m6811_cpu.c -- 68HC11 CPU 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"
#include "sim-assert.h"
#include "sim-module.h"
#include "sim-options.h"
void cpu_free_frame (sim_cpu* cpu, struct cpu_frame *frame);
enum {
OPTION_CPU_RESET = OPTION_START,
OPTION_EMUL_OS,
OPTION_CPU_CONFIG,
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 },
{ {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)
{
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:
cpu->cpu_emul_syscall = 1;
break;
case OPTION_CPU_CONFIG:
if (sscanf(arg, "0x%x", &val) == 1
|| sscanf(arg, "%d", &val) == 1)
{
cpu->cpu_config = val;
cpu->cpu_use_local_config = 1;
}
else
cpu->cpu_use_local_config = 0;
break;
case OPTION_CPU_MODE:
break;
}
return SIM_RC_OK;
}
/* Tentative to keep track of the cpu frame. */
struct cpu_frame*
cpu_find_frame (sim_cpu *cpu, uint16 sp)
{
struct cpu_frame_list *flist;
flist = cpu->cpu_frames;
while (flist)
{
struct cpu_frame *frame;
frame = flist->frame;
while (frame)
{
if (frame->sp_low <= sp && frame->sp_high >= sp)
{
cpu->cpu_current_frame = flist;
return frame;
}
frame = frame->up;
}
flist = flist->next;
}
return 0;
}
struct cpu_frame_list*
cpu_create_frame_list (sim_cpu *cpu)
{
struct cpu_frame_list *flist;
flist = (struct cpu_frame_list*) malloc (sizeof (struct cpu_frame_list));
flist->frame = 0;
flist->next = cpu->cpu_frames;
flist->prev = 0;
if (flist->next)
flist->next->prev = flist;
cpu->cpu_frames = flist;
cpu->cpu_current_frame = flist;
return flist;
}
void
cpu_remove_frame_list (sim_cpu *cpu, struct cpu_frame_list *flist)
{
struct cpu_frame *frame;
if (flist->prev == 0)
cpu->cpu_frames = flist->next;
else
flist->prev->next = flist->next;
if (flist->next)
flist->next->prev = flist->prev;
frame = flist->frame;
while (frame)
{
struct cpu_frame* up = frame->up;
cpu_free_frame (cpu, frame);
frame = up;
}
free (flist);
}
struct cpu_frame*
cpu_create_frame (sim_cpu *cpu, uint16 pc, uint16 sp)
{
struct cpu_frame *frame;
frame = (struct cpu_frame*) malloc (sizeof(struct cpu_frame));
frame->up = 0;
frame->pc = pc;
frame->sp_low = sp;
frame->sp_high = sp;
return frame;
}
void
cpu_free_frame (sim_cpu *cpu, struct cpu_frame *frame)
{
free (frame);
}
uint16
cpu_frame_reg (sim_cpu *cpu, uint16 rn)
{
struct cpu_frame *frame;
if (cpu->cpu_current_frame == 0)
return 0;
frame = cpu->cpu_current_frame->frame;
while (frame)
{
if (rn == 0)
return frame->sp_high;
frame = frame->up;
rn--;
}
return 0;
}
void
cpu_call (sim_cpu *cpu, uint16 addr)
{
#if HAVE_FRAME
uint16 pc = cpu->cpu_insn_pc;
uint16 sp;
struct cpu_frame_list *flist;
struct cpu_frame* frame;
struct cpu_frame* new_frame;
#endif
cpu_set_pc (cpu, addr);
#if HAVE_FRAME
sp = cpu_get_sp (cpu);
cpu->cpu_need_update_frame = 0;
flist = cpu->cpu_current_frame;
if (flist == 0)
flist = cpu_create_frame_list (cpu);
frame = flist->frame;
if (frame && frame->sp_low > sp)
frame->sp_low = sp;
new_frame = cpu_create_frame (cpu, pc, sp);
new_frame->up = frame;
flist->frame = new_frame;
#endif
}
void
cpu_update_frame (sim_cpu *cpu, int do_create)
{
#if HAVE_FRAME
struct cpu_frame *frame;
frame = cpu_find_frame (cpu, cpu_get_sp (cpu));
if (frame)
{
while (frame != cpu->cpu_current_frame->frame)
{
struct cpu_frame* up;
up = cpu->cpu_current_frame->frame->up;
cpu_free_frame (cpu, cpu->cpu_current_frame->frame);
cpu->cpu_current_frame->frame = up;
}
return;
}
if (do_create)
{
cpu_create_frame_list (cpu);
frame = cpu_create_frame (cpu, cpu_get_pc (cpu), cpu_get_sp (cpu));
cpu->cpu_current_frame->frame = frame;
}
#endif
}
void
cpu_return (sim_cpu *cpu)
{
#if HAVE_FRAME
uint16 sp = cpu_get_sp (cpu);
struct cpu_frame *frame;
struct cpu_frame_list *flist;
cpu->cpu_need_update_frame = 0;
flist = cpu->cpu_current_frame;
if (flist && flist->frame && flist->frame->up)
{
frame = flist->frame->up;
if (frame->sp_low <= sp && frame->sp_high >= sp)
{
cpu_free_frame (cpu, flist->frame);
flist->frame = frame;
return;
}
}
cpu_update_frame (cpu, 1);
#endif
}
void
cpu_print_frame (SIM_DESC sd, sim_cpu *cpu)
{
struct cpu_frame* frame;
int level = 0;
if (cpu->cpu_current_frame == 0 || cpu->cpu_current_frame->frame == 0)
{
sim_io_printf (sd, "No frame.\n");
return;
}
sim_io_printf (sd, " # PC SP-L SP-H\n");
frame = cpu->cpu_current_frame->frame;
while (frame)
{
sim_io_printf (sd, "%3d 0x%04x 0x%04x 0x%04x\n",
level, frame->pc, frame->sp_low, frame->sp_high);
frame = frame->up;
level++;
}
}
/* Set the stack pointer and re-compute the current frame. */
void
cpu_set_sp (sim_cpu *cpu, uint16 val)
{
cpu->cpu_regs.sp = val;
cpu_update_frame (cpu, 0);
}
int
cpu_initialize (SIM_DESC sd, sim_cpu *cpu)
{
int result;
sim_add_option_table (sd, 0, cpu_options);
memset (&cpu->cpu_regs, 0, sizeof(cpu->cpu_regs));
cpu->cpu_absolute_cycle = 0;
cpu->cpu_current_cycle = 0;
cpu->cpu_emul_syscall = 1;
cpu->cpu_running = 1;
cpu->cpu_stop_on_interrupt = 0;
cpu->cpu_frequency = 8 * 1000 * 1000;
cpu->cpu_frames = 0;
cpu->cpu_current_frame = 0;
cpu->cpu_use_elf_start = 0;
cpu->cpu_elf_start = 0;
cpu->cpu_use_local_config = 0;
cpu->cpu_config = M6811_NOSEC | M6811_NOCOP | M6811_ROMON |
M6811_EEON;
result = interrupts_initialize (cpu);
cpu->cpu_is_initialized = 1;
return result;
}
/* Reinitialize the processor after a reset. */
int
cpu_reset (sim_cpu *cpu)
{
cpu->cpu_need_update_frame = 0;
cpu->cpu_current_frame = 0;
while (cpu->cpu_frames)
cpu_remove_frame_list (cpu, cpu->cpu_frames);
/* Initialize the config register.
It is only initialized at reset time. */
memset (cpu->ios, 0, sizeof (cpu->ios));
cpu->ios[M6811_INIT] = 0x1;
/* Output compare registers set to 0xFFFF. */
cpu->ios[M6811_TOC1_H] = 0xFF;
cpu->ios[M6811_TOC1_L] = 0xFF;
cpu->ios[M6811_TOC2_H] = 0xFF;
cpu->ios[M6811_TOC2_L] = 0xFF;
cpu->ios[M6811_TOC3_H] = 0xFF;
cpu->ios[M6811_TOC4_L] = 0xFF;
cpu->ios[M6811_TOC5_H] = 0xFF;
cpu->ios[M6811_TOC5_L] = 0xFF;
/* Setup the processor registers. */
memset (&cpu->cpu_regs, 0, sizeof(cpu->cpu_regs));
cpu->cpu_absolute_cycle = 0;
cpu->cpu_current_cycle = 0;
cpu->cpu_is_initialized = 0;
/* Reinitialize the CPU operating mode. */
cpu->ios[M6811_HPRIO] = cpu->cpu_mode;
return 0;
}
/* Reinitialize the processor after a reset. */
int
cpu_restart (sim_cpu *cpu)
{
uint16 addr;
/* Get CPU starting address depending on the CPU mode. */
if (cpu->cpu_use_elf_start == 0)
{
switch ((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 = cpu->cpu_elf_start;
}
/* Setup the processor registers. */
cpu->cpu_insn_pc = addr;
cpu->cpu_regs.pc = addr;
cpu->cpu_regs.ccr = M6811_X_BIT | M6811_I_BIT | M6811_S_BIT;
cpu->cpu_absolute_cycle = 0;
cpu->cpu_is_initialized = 1;
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 val, uint16 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
cpu_ccr_update_tst8 (sim_cpu *proc, uint8 val)
{
cpu_set_ccr_V (proc, 0);
cpu_set_ccr_N (proc, val & 0x80 ? 1 : 0);
cpu_set_ccr_Z (proc, val == 0 ? 1 : 0);
}
uint16
cpu_fetch_relbranch (sim_cpu *cpu)
{
uint16 addr = (uint16) cpu_fetch8 (cpu);
if (addr & 0x0080)
{
addr |= 0xFF00;
}
addr += cpu->cpu_regs.pc;
return addr;
}
/* Push all the CPU registers (when an interruption occurs). */
void
cpu_push_all (sim_cpu *cpu)
{
cpu_push_uint16 (cpu, cpu->cpu_regs.pc);
cpu_push_uint16 (cpu, cpu->cpu_regs.iy);
cpu_push_uint16 (cpu, cpu->cpu_regs.ix);
cpu_push_uint16 (cpu, cpu->cpu_regs.d);
cpu_push_uint8 (cpu, cpu->cpu_regs.ccr);
}
/* Handle special instructions. */
void
cpu_special (sim_cpu *cpu, enum M6811_Special special)
{
switch (special)
{
case M6811_RTI:
{
uint8 ccr;
ccr = cpu_pop_uint8 (cpu);
cpu_set_ccr (cpu, ccr);
cpu_set_d (cpu, cpu_pop_uint16 (cpu));
cpu_set_x (cpu, cpu_pop_uint16 (cpu));
cpu_set_y (cpu, cpu_pop_uint16 (cpu));
cpu_set_pc (cpu, cpu_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 (cpu->cpu_use_elf_start)
{
cpu_set_pc (cpu, 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 (&cpu->cpu_interrupts, M6811_INT_SWI);
interrupts_process (&cpu->cpu_interrupts);
break;
case M6811_EMUL_SYSCALL:
case M6811_ILLEGAL:
if (cpu->cpu_emul_syscall)
{
uint8 op = memory_read8 (cpu,
cpu_get_pc (cpu) - 1);
if (op == 0x41)
{
cpu_set_pc (cpu, 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 (&cpu->cpu_interrupts, M6811_INT_ILLEGAL);
interrupts_process (&cpu->cpu_interrupts);
break;
case M6811_TEST:
{
SIM_DESC sd;
sd = CPU_STATE (cpu);
/* Breakpoint instruction if we are under gdb. */
if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
{
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;
}
}
}
void
cpu_single_step (sim_cpu *cpu)
{
cpu->cpu_current_cycle = 0;
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 (&cpu->cpu_interrupts))
{
cpu->cpu_absolute_cycle += cpu->cpu_current_cycle;
return;
}
/* printf("PC = 0x%04x\n", cpu_get_pc (cpu));*/
cpu_interp (cpu);
cpu->cpu_absolute_cycle += cpu->cpu_current_cycle;
}
/* VARARGS */
void
sim_memory_error (sim_cpu *cpu, SIM_SIGNAL excep,
uint16 addr, const char *message, ...)
{
char buf[1024];
va_list args;
va_start (args, message);
vsprintf (buf, message, args);
va_end (args);
printf("%s\n", buf);
cpu_memory_exception (cpu, excep, addr, buf);
}
void
cpu_memory_exception (sim_cpu *cpu, SIM_SIGNAL excep,
uint16 addr, const char *message)
{
if (cpu->cpu_running == 0)
return;
cpu_set_pc (cpu, cpu->cpu_insn_pc);
sim_engine_halt (CPU_STATE (cpu), cpu, NULL,
cpu_get_pc (cpu), sim_stopped, excep);
#if 0
cpu->mem_exception = excep;
cpu->fault_addr = addr;
cpu->fault_msg = strdup (message);
if (cpu->cpu_use_handler)
{
longjmp (&cpu->cpu_exception_handler, 1);
}
(* cpu->callback->printf_filtered)
(cpu->callback, "Fault at 0x%04x: %s\n", addr, message);
#endif
}
void
cpu_info (SIM_DESC sd, sim_cpu *cpu)
{
sim_io_printf (sd, "CPU info:\n");
sim_io_printf (sd, " Absolute cycle: %llu\n",
cpu->cpu_absolute_cycle);
sim_io_printf (sd, " Syscall emulation: %s\n",
cpu->cpu_emul_syscall ? "yes, via 0xcd <n>" : "no");
sim_io_printf (sd, " Memory errors detection: %s\n",
cpu->cpu_check_memory ? "yes" : "no");
sim_io_printf (sd, " Stop on interrupt: %s\n",
cpu->cpu_stop_on_interrupt ? "yes" : "no");
}

490
sim/m68hc11/sim-main.h Normal file
View File

@ -0,0 +1,490 @@
/* sim-main.h -- Simulator for Motorola 68HC11
Copyright (C) 1999, 2000 Free Software Foundation, Inc.
Written by Stephane Carrez (stcarrez@worldnet.fr)
This file is part of GDB, the GNU debugger.
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 2, 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, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifndef _SIM_MAIN_H
#define _SIM_MAIN_H
#define WITH_MODULO_MEMORY 1
#define WITH_WATCHPOINTS 1
#define SIM_HANDLES_LMA 1
#include "sim-basics.h"
typedef address_word sim_cia;
#include "sim-signal.h"
#include "sim-base.h"
#include "bfd.h"
#include "opcode/m68hc11.h"
#include "callback.h"
#include "remote-sim.h"
#include "opcode/m68hc11.h"
#include "sim-types.h"
typedef unsigned8 uint8;
typedef unsigned16 uint16;
typedef signed16 int16;
typedef unsigned32 uint32;
typedef signed32 int32;
typedef unsigned64 uint64;
typedef signed64 int64;
struct _sim_cpu;
#include "interrupts.h"
#include <setjmp.h>
#define X_REGNUM 0
#define D_REGNUM 1
#define Y_REGNUM 2
#define SP_REGNUM 3
#define PC_REGNUM 4
#define A_REGNUM 5
#define B_REGNUM 6
#define PSW_REGNUM 7
#define Z_REGNUM 8
#define FP_REGNUM 9
#define TMP_REGNUM 10
#define ZS_REGNUM 11
#define XY_REGNUM 12
#define ZD1_REGNUM 13
#define ZD32_REGNUM (ZD1_REGNUM+31)
#define FIRST_SOFT_REGNUM (Z_REGNUM)
#define MAX_SOFT_REG (ZD32_REGNUM - Z_REGNUM + 1)
typedef struct m6811_regs {
unsigned short d;
unsigned short ix;
unsigned short iy;
unsigned short sp;
unsigned short pc;
unsigned char ccr;
} m6811_regs;
/* Description of 68HC11 IO registers. Such description is only provided
for the info command to display the current setting of IO registers
from GDB. */
struct io_reg_desc
{
int mask;
const char *short_name;
const char *long_name;
};
typedef struct io_reg_desc io_reg_desc;
extern void print_io_reg_desc (SIM_DESC sd, io_reg_desc *desc, int val,
int mode);
extern void print_io_byte (SIM_DESC sd, const char *name,
io_reg_desc *desc, uint8 val, uint16 addr);
/* List of special 68HC11 instructions that are not handled by the
'gencode.c' generator. These complex instructions are implemented
by 'cpu_special'. */
enum M6811_Special
{
M6811_RTI,
M6811_WAI,
M6811_SWI,
M6811_TEST,
M6811_ILLEGAL,
M6811_EMUL_SYSCALL
};
#define CPU_POP 1
#define CPU_PUSH 2
#define MAX_PORTS 0x40
/* Tentative to keep track of the stack frame.
The frame is updated each time a call or a return are made.
We also have to take into account changes of stack pointer
(either thread switch or longjmp). */
struct cpu_frame
{
struct cpu_frame *up;
uint16 pc;
uint16 sp_low;
uint16 sp_high;
};
/* Represents a list of frames (or a thread). */
struct cpu_frame_list
{
struct cpu_frame_list *next;
struct cpu_frame_list *prev;
struct cpu_frame *frame;
};
struct _sim_cpu {
/* CPU registers. */
struct m6811_regs cpu_regs;
/* CPU interrupts. */
struct interrupts cpu_interrupts;
struct cpu_frame_list *cpu_frames;
struct cpu_frame_list *cpu_current_frame;
int cpu_need_update_frame;
/* CPU absolute cycle time. The cycle time is updated after
each instruction, by the number of cycles taken by the instruction.
It is cleared only when reset occurs. */
signed64 cpu_absolute_cycle;
/* Number of cycles to increment after the current instruction.
This is also the number of ticks for the generic event scheduler. */
uint8 cpu_current_cycle;
int cpu_emul_syscall;
int cpu_is_initialized;
int cpu_running;
int cpu_check_memory;
int cpu_stop_on_interrupt;
/* When this is set, start execution of program at address specified
in the ELF header. This is used for testing some programs that do not
have an interrupt table linked with them. Programs created during the
GCC validation are like this. A normal 68HC11 does not behave like
this (unless there is some OS or downloadable feature). */
int cpu_use_elf_start;
/* The starting address specified in ELF header. */
int cpu_elf_start;
uint16 cpu_insn_pc;
unsigned short cpu_nb_pseudo_regs;
uint16 cpu_page0_reg[MAX_SOFT_REG];
/* CPU frequency. This is the quartz frequency. It is divided by 4 to
get the cycle time. This is used for the timer rate and for the baud
rate generation. */
unsigned long cpu_frequency;
/* The mode in which the CPU is configured (MODA and MODB pins). */
unsigned int cpu_mode;
/* Initial value of the CONFIG register. */
uint8 cpu_config;
uint8 cpu_use_local_config;
uint8 ios[0x3F];
/* ... base type ... */
sim_cpu_base base;
};
/* Returns the cpu absolute cycle time (A virtual counter incremented
at each 68HC11 E clock). */
#define cpu_current_cycle(PROC) ((PROC)->cpu_absolute_cycle)
#define cpu_add_cycles(PROC,T) ((PROC)->cpu_current_cycle += (signed64) (T))
#define cpu_is_running(PROC) ((PROC)->cpu_running)
/* Get the IO/RAM base addresses depending on the M6811_INIT register. */
#define cpu_get_io_base(PROC) \
(((uint16)(((PROC)->ios[M6811_INIT]) & 0x0F))<<12)
#define cpu_get_reg_base(PROC) \
(((uint16)(((PROC)->ios[M6811_INIT]) & 0xF0))<<8)
/* Returns the different CPU registers. */
#define cpu_get_ccr(PROC) ((PROC)->cpu_regs.ccr)
#define cpu_get_pc(PROC) ((PROC)->cpu_regs.pc)
#define cpu_get_d(PROC) ((PROC)->cpu_regs.d)
#define cpu_get_x(PROC) ((PROC)->cpu_regs.ix)
#define cpu_get_y(PROC) ((PROC)->cpu_regs.iy)
#define cpu_get_sp(PROC) ((PROC)->cpu_regs.sp)
#define cpu_get_a(PROC) ((PROC->cpu_regs.d >> 8) & 0x0FF)
#define cpu_get_b(PROC) ((PROC->cpu_regs.d) & 0x0FF)
#define cpu_set_d(PROC,VAL) (((PROC)->cpu_regs.d) = (VAL))
#define cpu_set_x(PROC,VAL) (((PROC)->cpu_regs.ix) = (VAL))
#define cpu_set_y(PROC,VAL) (((PROC)->cpu_regs.iy) = (VAL))
#if 0
/* This is a function in m68hc11_sim.c to keep track of the frame. */
#define cpu_set_sp(PROC,VAL) (((PROC)->cpu_regs.sp) = (VAL))
#endif
#define cpu_set_pc(PROC,VAL) (((PROC)->cpu_regs.pc) = (VAL))
#define cpu_set_a(PROC,VAL) \
cpu_set_d(PROC,((VAL) << 8) | cpu_get_b(PROC))
#define cpu_set_b(PROC,VAL) \
cpu_set_d(PROC,((cpu_get_a(PROC)) << 8)|(VAL & 0x0FF))
#define cpu_set_ccr(PROC,VAL) ((PROC)->cpu_regs.ccr = (VAL))
#define cpu_get_ccr_H(PROC) ((cpu_get_ccr(PROC) & M6811_H_BIT) ? 1: 0)
#define cpu_get_ccr_X(PROC) ((cpu_get_ccr(PROC) & M6811_X_BIT) ? 1: 0)
#define cpu_get_ccr_S(PROC) ((cpu_get_ccr(PROC) & M6811_S_BIT) ? 1: 0)
#define cpu_get_ccr_N(PROC) ((cpu_get_ccr(PROC) & M6811_N_BIT) ? 1: 0)
#define cpu_get_ccr_V(PROC) ((cpu_get_ccr(PROC) & M6811_V_BIT) ? 1: 0)
#define cpu_get_ccr_C(PROC) ((cpu_get_ccr(PROC) & M6811_C_BIT) ? 1: 0)
#define cpu_get_ccr_Z(PROC) ((cpu_get_ccr(PROC) & M6811_Z_BIT) ? 1: 0)
#define cpu_get_ccr_I(PROC) ((cpu_get_ccr(PROC) & M6811_I_BIT) ? 1: 0)
#define cpu_set_ccr_flag(S,B,V) \
cpu_set_ccr(S,(cpu_get_ccr(S) & ~(B)) | ((V) ? B : 0))
#define cpu_set_ccr_H(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_H_BIT, VAL)
#define cpu_set_ccr_X(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_X_BIT, VAL)
#define cpu_set_ccr_S(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_S_BIT, VAL)
#define cpu_set_ccr_N(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_N_BIT, VAL)
#define cpu_set_ccr_V(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_V_BIT, VAL)
#define cpu_set_ccr_C(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_C_BIT, VAL)
#define cpu_set_ccr_Z(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_Z_BIT, VAL)
#define cpu_set_ccr_I(PROC,VAL) cpu_set_ccr_flag(PROC, M6811_I_BIT, VAL)
#undef inline
#define inline static __inline__
extern void cpu_memory_exception (struct _sim_cpu *proc,
SIM_SIGNAL excep,
uint16 addr,
const char *message);
inline uint8
memory_read8 (sim_cpu *cpu, uint16 addr)
{
uint8 val;
if (sim_core_read_buffer (CPU_STATE (cpu), cpu, 0, &val, addr, 1) != 1)
{
cpu_memory_exception (cpu, SIM_SIGSEGV, addr,
"Read error");
}
return val;
}
inline void
memory_write8 (sim_cpu *cpu, uint16 addr, uint8 val)
{
if (sim_core_write_buffer (CPU_STATE (cpu), cpu, 0, &val, addr, 1) != 1)
{
cpu_memory_exception (cpu, SIM_SIGSEGV, addr,
"Write error");
}
}
inline uint16
memory_read16 (sim_cpu *cpu, uint16 addr)
{
uint8 b[2];
if (sim_core_read_buffer (CPU_STATE (cpu), cpu, 0, b, addr, 2) != 2)
{
cpu_memory_exception (cpu, SIM_SIGSEGV, addr,
"Read error");
}
return (((uint16) (b[0])) << 8) | ((uint16) b[1]);
}
inline void
memory_write16 (sim_cpu *cpu, uint16 addr, uint16 val)
{
uint8 b[2];
b[0] = val >> 8;
b[1] = val;
if (sim_core_write_buffer (CPU_STATE (cpu), cpu, 0, b, addr, 2) != 2)
{
cpu_memory_exception (cpu, SIM_SIGSEGV, addr,
"Write error");
}
}
extern void
cpu_ccr_update_tst8 (sim_cpu *proc, uint8 val);
inline void
cpu_ccr_update_tst16 (sim_cpu *proc, uint16 val)
{
cpu_set_ccr_V (proc, 0);
cpu_set_ccr_N (proc, val & 0x8000 ? 1 : 0);
cpu_set_ccr_Z (proc, val == 0 ? 1 : 0);
}
inline void
cpu_ccr_update_shift8 (sim_cpu *proc, uint8 val)
{
cpu_set_ccr_N (proc, val & 0x80 ? 1 : 0);
cpu_set_ccr_Z (proc, val == 0 ? 1 : 0);
cpu_set_ccr_V (proc, cpu_get_ccr_N (proc) ^ cpu_get_ccr_C (proc));
}
inline void
cpu_ccr_update_shift16 (sim_cpu *proc, uint16 val)
{
cpu_set_ccr_N (proc, val & 0x8000 ? 1 : 0);
cpu_set_ccr_Z (proc, val == 0 ? 1 : 0);
cpu_set_ccr_V (proc, cpu_get_ccr_N (proc) ^ cpu_get_ccr_C (proc));
}
inline void
cpu_ccr_update_add8 (sim_cpu *proc, uint8 r, uint8 a, uint8 b)
{
cpu_set_ccr_C (proc, ((a & b) | (b & ~r) | (a & ~r)) & 0x80 ? 1 : 0);
cpu_set_ccr_V (proc, ((a & b & ~r) | (~a & ~b & r)) & 0x80 ? 1 : 0);
cpu_set_ccr_Z (proc, r == 0);
cpu_set_ccr_N (proc, r & 0x80 ? 1 : 0);
}
inline void
cpu_ccr_update_sub8 (sim_cpu *proc, uint8 r, uint8 a, uint8 b)
{
cpu_set_ccr_C (proc, ((~a & b) | (b & r) | (~a & r)) & 0x80 ? 1 : 0);
cpu_set_ccr_V (proc, ((a & ~b & ~r) | (~a & b & r)) & 0x80 ? 1 : 0);
cpu_set_ccr_Z (proc, r == 0);
cpu_set_ccr_N (proc, r & 0x80 ? 1 : 0);
}
inline void
cpu_ccr_update_add16 (sim_cpu *proc, uint16 r, uint16 a, uint16 b)
{
cpu_set_ccr_C (proc, ((a & b) | (b & ~r) | (a & ~r)) & 0x8000 ? 1 : 0);
cpu_set_ccr_V (proc, ((a & b & ~r) | (~a & ~b & r)) & 0x8000 ? 1 : 0);
cpu_set_ccr_Z (proc, r == 0);
cpu_set_ccr_N (proc, r & 0x8000 ? 1 : 0);
}
inline void
cpu_ccr_update_sub16 (sim_cpu *proc, uint16 r, uint16 a, uint16 b)
{
cpu_set_ccr_C (proc, ((~a & b) | (b & r) | (~a & r)) & 0x8000 ? 1 : 0);
cpu_set_ccr_V (proc, ((a & ~b & ~r) | (~a & b & r)) & 0x8000 ? 1 : 0);
cpu_set_ccr_Z (proc, r == 0);
cpu_set_ccr_N (proc, r & 0x8000 ? 1 : 0);
}
inline void
cpu_push_uint8 (sim_cpu *proc, uint8 val)
{
uint16 addr = proc->cpu_regs.sp;
memory_write8 (proc, addr, val);
proc->cpu_regs.sp = addr - 1;
proc->cpu_need_update_frame |= CPU_PUSH;
}
inline void
cpu_push_uint16 (sim_cpu *proc, uint16 val)
{
uint16 addr = proc->cpu_regs.sp - 1;
memory_write16 (proc, addr, val);
proc->cpu_regs.sp = addr - 1;
proc->cpu_need_update_frame |= CPU_PUSH;
}
inline uint8
cpu_pop_uint8 (sim_cpu *proc)
{
uint16 addr = proc->cpu_regs.sp;
uint8 val;
val = memory_read8 (proc, addr + 1);
proc->cpu_regs.sp = addr + 1;
proc->cpu_need_update_frame |= CPU_POP;
return val;
}
inline uint16
cpu_pop_uint16 (sim_cpu *proc)
{
uint16 addr = proc->cpu_regs.sp;
uint16 val;
val = memory_read16 (proc, addr + 1);
proc->cpu_regs.sp = addr + 2;
proc->cpu_need_update_frame |= CPU_POP;
return val;
}
inline uint8
cpu_fetch8 (sim_cpu *proc)
{
uint16 addr = proc->cpu_regs.pc;
uint8 val;
val = memory_read8 (proc, addr);
proc->cpu_regs.pc = addr + 1;
return val;
}
inline uint16
cpu_fetch16 (sim_cpu *proc)
{
uint16 addr = proc->cpu_regs.pc;
uint16 val;
val = memory_read16 (proc, addr);
proc->cpu_regs.pc = addr + 2;
return val;
}
extern void cpu_call (sim_cpu* proc, uint16 addr);
extern void cpu_special (sim_cpu *proc, enum M6811_Special special);
extern uint16 cpu_fetch_relbranch (sim_cpu *proc);
extern void cpu_push_all (sim_cpu *proc);
extern void cpu_single_step (sim_cpu *proc);
extern void cpu_info (SIM_DESC sd, sim_cpu *proc);
extern int cpu_initialize (SIM_DESC sd, sim_cpu *cpu);
extern void cpu_print_frame (SIM_DESC sd, sim_cpu *cpu);
extern void cpu_set_sp (sim_cpu *cpu, uint16 val);
extern uint16 cpu_frame_reg (sim_cpu *cpu, uint16 rn);
extern int cpu_reset (sim_cpu *cpu);
extern int cpu_restart (sim_cpu *cpu);
extern void sim_memory_error (sim_cpu *cpu, SIM_SIGNAL excep,
uint16 addr, const char *message, ...);
extern void emul_os (int op, sim_cpu *cpu);
extern void cpu_interp (sim_cpu *cpu);
/* The current state of the processor; registers, memory, etc. */
#define CIA_GET(CPU) (cpu_get_pc (CPU))
#define CIA_SET(CPU,VAL) (cpu_set_pc ((CPU), (VAL)))
#if (WITH_SMP)
#define STATE_CPU(sd,n) (&(sd)->cpu[n])
#else
#define STATE_CPU(sd,n) (&(sd)->cpu[0])
#endif
struct sim_state {
sim_cpu cpu[MAX_NR_PROCESSORS];
device *devices;
sim_state_base base;
};
extern void sim_set_profile (int n);
extern void sim_set_profile_size (int n);
extern void sim_board_reset (SIM_DESC sd);
#endif