New simulator.
This commit is contained in:
parent
3c765a5497
commit
e0709f5044
178
sim/m68hc11/ChangeLog
Normal file
178
sim/m68hc11/ChangeLog
Normal 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
174
sim/m68hc11/config.in
Normal 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
35
sim/m68hc11/configure.in
Normal 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
582
sim/m68hc11/dv-m68hc11.c
Normal 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, ®))
|
||||
hw_abort (me, "\"reg\" property must contain one addr/size entry");
|
||||
|
||||
hw_unit_address_to_attach_address (hw_parent (me),
|
||||
®.address,
|
||||
&controller->attach_space,
|
||||
&controller->attach_address,
|
||||
me);
|
||||
hw_unit_size_to_attach_size (hw_parent (me),
|
||||
®.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 },
|
||||
};
|
||||
|
620
sim/m68hc11/dv-m68hc11eepr.c
Normal file
620
sim/m68hc11/dv-m68hc11eepr.c
Normal 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, ®))
|
||||
hw_abort (me, "\"reg\" property must contain one addr/size entry");
|
||||
|
||||
hw_unit_address_to_attach_address (hw_parent (me),
|
||||
®.address,
|
||||
&attach_space,
|
||||
&attach_address,
|
||||
me);
|
||||
hw_unit_size_to_attach_size (hw_parent (me),
|
||||
®.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
664
sim/m68hc11/dv-m68hc11sio.c
Normal 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
508
sim/m68hc11/dv-m68hc11spi.c
Normal 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
607
sim/m68hc11/dv-m68hc11tim.c
Normal 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
361
sim/m68hc11/dv-nvram.c
Normal 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, ®))
|
||||
hw_abort (me, "\"reg\" property must contain one addr/size entry");
|
||||
|
||||
hw_unit_address_to_attach_address (hw_parent (me),
|
||||
®.address,
|
||||
&attach_space,
|
||||
&attach_address,
|
||||
me);
|
||||
hw_unit_size_to_attach_size (hw_parent (me),
|
||||
®.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
161
sim/m68hc11/emulos.c
Normal 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
1484
sim/m68hc11/gencode.c
Normal file
File diff suppressed because it is too large
Load Diff
517
sim/m68hc11/interp.c
Normal file
517
sim/m68hc11/interp.c
Normal 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
297
sim/m68hc11/interrupts.c
Normal 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
132
sim/m68hc11/interrupts.h
Normal 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
639
sim/m68hc11/m68hc11_sim.c
Normal 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
490
sim/m68hc11/sim-main.h
Normal 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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user