Update to new config scheme
authorMichael Meissner <gnu@the-meissners.org>
Fri, 8 Sep 1995 23:56:38 +0000 (23:56 +0000)
committerMichael Meissner <gnu@the-meissners.org>
Fri, 8 Sep 1995 23:56:38 +0000 (23:56 +0000)
sim/ppc/.Sanitize
sim/ppc/ChangeLog
sim/ppc/Makefile.in
sim/ppc/configure.in
sim/ppc/gen.c
sim/ppc/ppc-endian.c
sim/ppc/psim.c [new file with mode: 0644]

index 625fd3695d42b80d79c74fc60ba0f458f76c15c4..e99ca8a0fb56e6f0f47cb3d9356d3b4d69d3f242 100644 (file)
@@ -31,6 +31,7 @@ README.psim
 basics.h
 bits.c
 bits.h
+config.in
 configure
 configure.in
 core.c
index 6ba9a81f620eeaacbc417269b274003642f19c26..fdc580e152bb774a6ed68968ea9fbb9e7a664943 100644 (file)
@@ -1,3 +1,27 @@
+       * configure.in: Call AC_CONFIG_HEADER.  Don't try to use
+       bfd/hosts/*.h file or bfd/config/*.mh file.  Call AC_PROG_CC and
+       AC_PROG_RANLIB.  Substitute in values for CFLAGS, HDEFINES, AR,
+       and CC_FOR_BUILD.  Call AC_CHECK_HEADERS for various header files.
+       Touch stamp.h if creating config.h.
+       * configure: Rebuild.
+       * config.in: New file, created by autoheader.
+       * Makefile.in (AR): Define as @AR@.
+       (CC): New variable, defined as @CC@.
+       (CFLAGS): Define as @CFLAGS@.
+       (CC_FOR_BUILD): New variable, defined as @CC_FOR_BUILD@.
+       (RANLIB): Define as @RANLIB@.
+       (HDEFINES, TDEFINES): New variables.
+       (@host_makefile_frag@): Remove.
+       (mostlyclean): Make the same as clean, not distclean.
+       (clean): Remove config.log.
+       (distclean): Remove config.h and stamp-h.
+       (Makefile): Don't depend upon @frags@.  Just rebuild Makefile when
+       invoking config.status.
+       (config.h, stamp-h): New targets.
+       (gen, gen.o): Build with CC_FOR_BUILD, not CC.
+       (ppc-config.h): Rename from old config.h build.
+       * (basics.h,gen.c,ppc-endian.c,psim.c): Include ppc-config.h.
+       
 Fri Sep  8 09:51:03 1995  Michael Meissner  <meissner@tiktok.cygnus.com>
 
        * configure{,.in}: Don't include sysdep.h from bfd, since bfd no
index c4b1b5fe349c1b5ebd0037bd3d5d0856846aface..2756751cc0bf3805aadcc965adf115c11fa3e976 100644 (file)
@@ -49,19 +49,23 @@ docdir = $(datadir)/doc
 
 SHELL = /bin/sh
 
-# FIXME: use autoconf's AC_PROG_INSTALL
 INSTALL = $(srcroot)/install.sh -c
 INSTALL_PROGRAM = $(INSTALL)
 INSTALL_DATA = $(INSTALL)
 INSTALL_XFORM = $(INSTALL) -t='$(program_transform_name)'
 INSTALL_XFORM1= $(INSTALL_XFORM) -b=.1
 
-AR = ar
+AR = @AR@
 AR_FLAGS = rc
-CFLAGS = -g
+CC = @CC@
+CFLAGS = @CFLAGS@
+CC_FOR_BUILD = @CC_FOR_BUILD@
 BISON = bison
 MAKEINFO = makeinfo
-RANLIB = ranlib
+RANLIB = @RANLIB@
+
+HDEFINES = @HDEFINES@
+TDEFINES =
 
 .NOEXPORT:
 MAKEOVERRIDES=
@@ -76,10 +80,6 @@ CONFIG_FILE  = std-config.h
 LIBIBERTY_LIB  = ../../libiberty/libiberty.a
 BFD_LIB                = ../../bfd/libbfd.a
 
-#### Makefile fragments come in here.
-# @host_makefile_frag@
-###
-
 TARGETLIB = libsim.a
 
 all:   run libsim.a $(GDB_OBJ)
@@ -91,6 +91,7 @@ all:  run libsim.a $(GDB_OBJ)
 
 BASICS_H = \
        config.h \
+       ppc-config.h \
        words.h \
        ppc-endian.h \
        debug.h \
@@ -136,7 +137,8 @@ BUILT_SRC = \
        idecode.h idecode.c \
        semantics.h semantics.c \
        spreg.h spreg.c \
-       config.h
+       config.h \
+       ppc-config.h
 
 LIB_SRC = \
        psim.c \
@@ -201,7 +203,7 @@ bits.o: bits.c bits.h
 debug.o: debug.c $(BASICS_H)
 
 ppc-endian.o: ppc-endian.c ppc-endian.h \
-       config.h words.h sim_callbacks.h
+       config.h ppc-config.h words.h sim_callbacks.h
 
 system.o: system.c system.h $(CPU_H) $(IDECODE_H)
 
@@ -243,8 +245,8 @@ semantics.o: semantics.c semantics.h $(CPU_H) $(IDECODE_H)
 # Rules to create the built c source code files
 #
 
-config.h: $(CONFIG_FILE)
-       cp $(srcdir)/$(CONFIG_FILE) config.h
+ppc-config.h: $(CONFIG_FILE)
+       cp $(srcdir)/$(CONFIG_FILE) ppc-config.h
 
 
 tmp-gencode: gen ppc-instructions ppc-spr-table $(srcdir)/../../move-if-change
@@ -268,26 +270,31 @@ tmp-gencode: gen ppc-instructions ppc-spr-table $(srcdir)/../../move-if-change
 
 icache.h idecode.h idecode.c semantics.h semantics.c spreg.h spreg.c: tmp-gencode
 
-gen.o: gen.c config.h
+gen.o: gen.c config.h ppc-config.h
+       $(CC_FOR_BUILD) -c $(CFLAGS) $(HDEFINES) $(CSEARCH) $(CSWITCHES) $(srcdir)/gen.c
 
-gen: gen.o config.h $(LIBIBERTY_LIB) $(LIBS)
-       $(CC) $(CFLAGS) $(LDFLAGS) -o gen gen.o $(LIBIBERTY_LIB) $(LIBS)
+gen: gen.o config.h ppc-config.h $(LIBIBERTY_LIB) $(LIBS)
+       $(CC_FOR_BUILD) $(CFLAGS) $(LDFLAGS) -o gen gen.o $(LIBIBERTY_LIB) $(LIBS)
                
 #
 
 tags etags: TAGS
 
-TAGS: tmp-gencode config.h
+TAGS: tmp-gencode config.h ppc-config.h
        etags $(srcdir)/*.h $(srcdir)/*.c $(BUILT_SRC)
 
-clean:
-       rm -f tmp-* *.[oas] core psim run gen
+clean mostlyclean:
+       rm -f tmp-* *.[oas] core psim run gen config.log
+
+distclean realclean: clean
+       rm -f TAGS $(BUILT_SRC) Makefile config.cache config.status config.h stamp-h
 
-distclean mostlyclean realclean: clean
-       rm -f TAGS $(BUILT_SRC) Makefile config.cache config.log config.status
+Makefile: Makefile.in config.status
+       CONFIG_FILES=Makefile CONFIG_HEADERS= $(SHELL) ./config.status
 
-Makefile: Makefile.in config.status @frags@
-       $(SHELL) ./config.status
+config.h: stamp-h ; @true
+stamp-h: config.in config.status
+       CONFIG_FILES= CONFIG_HEADERS=config.h:config.in $(SHELL) ./config.status
 
 config.status: configure
        $(SHELL) ./config.status --recheck
index 19c46485bca765086f0ca2cdc740e7f814cbca3b..b964ece066ae7f349c31f82b7c284e564ff30669 100644 (file)
@@ -2,23 +2,31 @@ dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.3)dnl
 AC_INIT(Makefile.in)
 
+AC_CONFIG_HEADER(config.h:config.in)
+
 AC_CONFIG_AUX_DIR(`cd $srcdir;pwd`/../..)
 AC_CANONICAL_SYSTEM
 AC_ARG_PROGRAM
 
 . ${srcdir}/../../bfd/configure.host
 
-if test -f ${srcdir}/../../bfd/config/${my_host}.mh; then
-       host_makefile_frag=../../bfd/config/${my_host}.mh
+AC_PROG_CC
+AC_SUBST(CFLAGS)
+AC_SUBST(HDEFINES)
+AR=${AR-ar}
+AC_SUBST(AR)
+AC_PROG_RANLIB
+
+# Put a plausible default for CC_FOR_BUILD in Makefile.
+AC_C_CROSS
+if test "x$cross_compiling" = "xno"; then
+  CC_FOR_BUILD='$(CC)'
 else
-       host_makefile_frag=/dev/null
+  CC_FOR_BUILD=gcc
 fi
+AC_SUBST(CC_FOR_BUILD)
 
-frags=
-if test $host_makefile_frag != /dev/null; then
-       frags="$frags $host_makefile_frag"
-fi
-AC_SUBST_FILE(host_makefile_frag)
-AC_SUBST(frags)
+AC_CHECK_HEADERS(string.h strings.h stdlib.h time.h sys/times.h)
 
-AC_OUTPUT(Makefile)
+AC_OUTPUT(Makefile,
+[case x$CONFIG_HEADERS in xconfig.h:config.in) echo > stamp-h ;; esac])
index e6e5c161e10c494fee5084290cd45f4c46f38f54..9341cd8148673d01ee370072e8bf3ccc7b04d999 100644 (file)
@@ -42,6 +42,7 @@
 #include <string.h>
 
 #include "config.h"
+#include "ppc-config.h"
 
 #undef WITH_ASSERT
 #define WITH_ASSERT 1
index 0158116eb4147f375f4a8bac14350cf55fb800d2..734ea213e52e3894f7a2e8174c496ee6b66a3342 100644 (file)
@@ -28,6 +28,7 @@
 
 
 #include "config.h"
+#include "ppc-config.h"
 #include "words.h"
 #include "ppc-endian.h"
 #include "sim_callbacks.h"
diff --git a/sim/ppc/psim.c b/sim/ppc/psim.c
new file mode 100644 (file)
index 0000000..45fb821
--- /dev/null
@@ -0,0 +1,904 @@
+/*  This file is part of the program psim.
+
+    Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
+
+    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.
+    */
+
+
+#ifndef _PSIM_C_
+#define _PSIM_C_
+
+#include "config.h"
+#include "ppc-config.h"
+#include "inline.h"
+
+#ifndef STATIC_INLINE_PSIM
+#define STATIC_INLINE_PSIM STATIC_INLINE
+#endif
+
+#include <string.h>
+#include <setjmp.h>
+
+#include "cpu.h" /* includes psim.h */
+#include "idecode.h"
+
+#include "inline.c"
+
+
+/* system structure, actual size of processor array determined at
+   runtime */
+
+struct _psim {
+  event_queue *events;
+  device_node *devices;
+  core *memory;
+  /* escape routine for inner functions */
+  void *path_to_halt;
+  void *path_to_restart;
+  /* status from last halt */
+  psim_status halt_status;
+  /* the processes proper */
+  int nr_cpus;
+  int last_cpu; /* CPU that last (tried to) execute an instruction */
+  cpu *processors[0];
+};
+
+
+int current_target_byte_order;
+int current_host_byte_order;
+int current_environment;
+int current_alignment;
+
+INLINE_PSIM psim *
+psim_create(const char *file_name,
+           int nr_processors)
+{
+  int cpu_nr;
+  psim *system;
+
+  /* sanity check */
+  if (nr_processors <= 0
+      || (!WITH_SMP && nr_processors != 1))
+    error("psim_create() invalid number of cpus\n");
+
+  /* create things */
+  system = (psim*)zalloc(sizeof(psim)
+                        + sizeof(cpu*) * (nr_processors + 1));
+  system->nr_cpus = nr_processors;
+  system->events = event_queue_create();
+  system->devices = device_tree_create(file_name);
+  system->memory = core_create(system->devices, 0);
+  for (cpu_nr = 0; cpu_nr < nr_processors; cpu_nr++) {
+    system->processors[cpu_nr] = cpu_create(system,
+                                           system->memory,
+                                           system->events,
+                                           cpu_nr);
+  }
+
+  /* fill in the missing endian information */
+  current_target_byte_order
+    = (device_tree_find_boolean(system->devices, "/options/little-endian?")
+       ? LITTLE_ENDIAN
+       : BIG_ENDIAN);
+  if (WITH_TARGET_BYTE_ORDER
+      && WITH_TARGET_BYTE_ORDER != current_target_byte_order)
+    error("target byte order conflict\n");
+
+  current_host_byte_order = 1;
+  current_host_byte_order = (*(char*)(&current_host_byte_order)
+                            ? LITTLE_ENDIAN
+                            : BIG_ENDIAN);
+  if (WITH_HOST_BYTE_ORDER
+      && WITH_HOST_BYTE_ORDER != current_host_byte_order)
+    error("host byte order conflict\n");
+
+  /* fill in the missing OEA/VEA information */
+  current_environment = (device_tree_find_boolean(system->devices,
+                                                 "/options/vea?")
+                        ? VIRTUAL_ENVIRONMENT
+                        : OPERATING_ENVIRONMENT);
+
+  /* fill in the missing ALLIGNMENT information */
+  current_alignment = (device_tree_find_boolean(system->devices,
+                                               "/options/aligned?")
+                      ? STRICT_ALIGNMENT
+                      : NONSTRICT_ALIGNMENT);
+  if (WITH_ALIGNMENT
+      && CURRENT_ALIGNMENT != WITH_ALIGNMENT)
+    error("target alignment support conflict\n");
+
+  return system;
+}
+
+
+/* allow the simulation to stop/restart abnormaly */
+
+STATIC_INLINE_PSIM void
+psim_set_halt_and_restart(psim *system,
+                         void *halt_jmp_buf,
+                         void *restart_jmp_buf)
+{
+  system->path_to_halt = halt_jmp_buf;
+  system->path_to_restart = restart_jmp_buf;
+}
+
+STATIC_INLINE_PSIM void
+psim_clear_halt_and_restart(psim *system)
+{
+  system->path_to_halt = NULL;
+  system->path_to_restart = NULL;
+}
+
+INLINE_PSIM void
+psim_restart(psim *system,
+            int current_cpu)
+{
+  system->last_cpu = current_cpu;
+  longjmp(*(jmp_buf*)(system->path_to_restart), current_cpu + 1);
+}
+
+
+INLINE_PSIM void
+psim_halt(psim *system,
+         int current_cpu,
+         unsigned_word cia,
+         stop_reason reason,
+         int signal)
+{
+  system->last_cpu = current_cpu;
+  system->halt_status.cpu_nr = current_cpu;
+  system->halt_status.reason = reason;
+  system->halt_status.signal = signal;
+  system->halt_status.program_counter = cia;
+  longjmp(*(jmp_buf*)(system->path_to_halt), current_cpu + 1);
+}
+
+INLINE_PSIM psim_status
+psim_get_status(psim *system)
+{
+  return system->halt_status;
+}
+
+
+cpu *
+psim_cpu(psim *system,
+        int cpu_nr)
+{
+  if (cpu_nr < 0 || cpu_nr >= system->nr_cpus)
+    return NULL;
+  else
+    return system->processors[cpu_nr];
+}
+
+
+
+STATIC_INLINE_PSIM int
+sizeof_argument_strings(char **arg)
+{
+  int sizeof_strings = 0;
+
+  /* robust */
+  if (arg == NULL)
+    return 0;
+
+  /* add up all the string sizes (padding as we go) */
+  for (; *arg != NULL; arg++) {
+    int len = strlen(*arg) + 1;
+    sizeof_strings += ALIGN_8(len);
+  }
+
+  return sizeof_strings;
+}
+
+STATIC_INLINE_PSIM int
+number_of_arguments(char **arg)
+{
+  int nr;
+  if (arg == NULL)
+    return 0;
+  for (nr = 0; *arg != NULL; arg++, nr++);
+  return nr;
+}
+
+STATIC_INLINE_PSIM int
+sizeof_arguments(char **arg)
+{
+  return ALIGN_8((number_of_arguments(arg) + 1) * sizeof(unsigned_word));
+}
+
+STATIC_INLINE_PSIM void
+write_stack_arguments(psim *system,
+                     char **arg,
+                     unsigned_word start_block,
+                     unsigned_word start_arg)
+{
+  TRACE(trace_create_stack,
+       ("write_stack_arguments() - %s=0x%x %s=0x%x %s=0x%x %s=0x%x\n",
+        "system", system, "arg", arg,
+        "start_block", start_block, "start_arg", start_arg));
+  if (arg == NULL)
+    error("write_arguments: character array NULL\n");
+  /* only copy in arguments, memory is already zero */
+  for (; *arg != NULL; arg++) {
+    int len = strlen(*arg)+1;
+    TRACE(trace_create_stack,
+         ("write_stack_arguments - write %s=%s at %s=0x%x %s=0x%x %s=0x%x\n",
+          "**arg", *arg, "start_block", start_block,
+          "len", len, "start_arg", start_arg));
+    if (psim_write_memory(system, 0, *arg,
+                         start_block, len,
+                         raw_transfer, 0) != len)
+      error("write_arguments() - write of **arg (%s) at 0x%x failed\n",
+           *arg, start_block);
+    if (psim_write_memory(system, 0, &start_block,
+                         start_arg, sizeof(start_block),
+                         cooked_transfer, 0) != sizeof(start_block))
+      error("write_arguments() - write of *arg failed\n");
+    start_block += ALIGN_8(len);
+    start_arg += sizeof(start_block);
+  }
+}
+
+STATIC_INLINE_PSIM void
+create_elf_stack_frame(psim *system,
+                      unsigned_word bottom_of_stack,
+                      char **argv,
+                      char **envp)
+{
+  /* fixme - this is over aligned */
+
+  /* information block */
+  const unsigned sizeof_envp_block = sizeof_argument_strings(envp);
+  const unsigned_word start_envp_block = bottom_of_stack - sizeof_envp_block;
+  const unsigned sizeof_argv_block = sizeof_argument_strings(argv);
+  const unsigned_word start_argv_block = start_envp_block - sizeof_argv_block;
+
+  /* auxiliary vector - contains only one entry */
+  const unsigned sizeof_aux_entry = 2*sizeof(unsigned_word); /* magic */
+  const unsigned_word start_aux = start_argv_block - ALIGN_8(sizeof_aux_entry);
+
+  /* environment points (including null sentinal) */
+  const unsigned sizeof_envp = sizeof_arguments(envp);
+  const unsigned_word start_envp = start_aux - sizeof_envp;
+
+  /* argument pointers (including null sentinal) */
+  const int argc = number_of_arguments(argv);
+  const unsigned sizeof_argv = sizeof_arguments(argv);
+  const unsigned_word start_argv = start_envp - sizeof_argv;
+
+  /* link register save address - alligned to a 16byte boundary */
+  const unsigned_word top_of_stack = ((start_argv
+                                      - 2 * sizeof(unsigned_word))
+                                     & ~0xf);
+
+  /* force some stack space */
+  if (CURRENT_ENVIRONMENT == VIRTUAL_ENVIRONMENT
+      && core_stack_lower_bound(system->memory) > top_of_stack) {
+    unsigned_word extra_stack_space = (core_stack_lower_bound(system->memory)
+                                      - FLOOR_PAGE(top_of_stack));
+    TRACE(trace_create_stack,
+         ("create_elf_stack_frame() - growing stack by 0x%x\n",
+          extra_stack_space));
+    core_add_stack(system->memory, extra_stack_space);
+  }
+
+  /* install arguments on stack */
+  write_stack_arguments(system, envp, start_envp_block, start_envp);
+  write_stack_arguments(system, argv, start_argv_block, start_argv);
+
+  /* set up the registers */
+  psim_write_register(system, -1,
+                     &top_of_stack, "r1", cooked_transfer);
+  psim_write_register(system, -1,
+                     &argc, "r3", cooked_transfer);
+  psim_write_register(system, -1,
+                     &start_argv, "r4", cooked_transfer);
+  psim_write_register(system, -1,
+                     &start_envp, "r5", cooked_transfer);
+  psim_write_register(system, -1,
+                     &start_aux, "r6", cooked_transfer);
+}
+
+STATIC_INLINE_PSIM void
+create_aix_stack_frame(psim *system,
+                      unsigned_word bottom_of_stack,
+                      char **argv,
+                      char **envp)
+{
+  unsigned_word core_envp;
+  unsigned_word core_argv;
+  unsigned_word core_argc;
+  unsigned_word core_aux;
+  unsigned_word top_of_stack;
+
+  /* cheat - create an elf stack frame */
+  create_elf_stack_frame(system, bottom_of_stack, argv, envp);
+  
+  /* extract argument addresses from registers */
+  psim_read_register(system, 0, &top_of_stack, "r1", cooked_transfer);
+  psim_read_register(system, 0, &core_argc, "r3", cooked_transfer);
+  psim_read_register(system, 0, &core_argv, "r4", cooked_transfer);
+  psim_read_register(system, 0, &core_envp, "r5", cooked_transfer);
+  psim_read_register(system, 0, &core_aux, "r6", cooked_transfer);
+
+  /* check stack fits at least this much */
+  if (CURRENT_ENVIRONMENT == VIRTUAL_ENVIRONMENT
+      && core_stack_lower_bound(system->memory) > top_of_stack) {
+    unsigned_word extra_stack_space = (core_stack_lower_bound(system->memory)
+                                      - FLOOR_PAGE(top_of_stack));
+    TRACE(trace_create_stack,
+         ("create_aix_stack_frame() - growing stack by 0x%x\n",
+          extra_stack_space));
+    core_add_stack(system->memory, extra_stack_space);
+  }
+
+  /* extract arguments from registers */
+  error("create_aix_stack_frame() - what happens next?\n");
+}
+
+
+INLINE_PSIM void
+psim_load(psim *system)
+{
+  unsigned_word program_counter;
+  msreg msr;
+
+  /* load in core data */
+  core_init(system->memory);
+
+  /* set up all processor entry points (to same thing).  Maybe
+     someday, the device tree could include information specifying the
+     entry point for each processor, one day */
+  TRACE(trace_tbd,
+       ("TBD - device tree specifying entry point of each processor\n"));
+  program_counter = device_tree_find_int(system->devices,
+                                        "/options/program-counter");
+  psim_write_register(system, -1,
+                     &program_counter,
+                     "pc", cooked_transfer);
+  system->last_cpu = system->nr_cpus - 1; /* force loop to restart */
+
+  /* set up the MSR for at least be/le mode */
+  msr = (device_tree_find_boolean(system->devices,
+                                 "/options/little-endian?")
+        ? msr_little_endian_mode
+        : 0);
+  psim_write_register(system, -1,
+                     &msr,
+                     "msr", cooked_transfer);
+}
+
+INLINE_PSIM void
+psim_stack(psim *system,
+          char **argv,
+          char **envp)
+{
+  unsigned_word stack_pointer = device_tree_find_int(system->devices,
+                                                    "/options/stack-pointer");
+  if (device_tree_find_boolean(system->devices,
+                              "/options/elf?"))
+    create_elf_stack_frame(system, stack_pointer, argv, envp);
+  else
+    create_aix_stack_frame(system, stack_pointer, argv, envp);
+}
+
+
+
+/* EXECUTE REAL CODE: 
+
+   Unfortunatly, there are multiple cases to consider vis:
+
+       <icache> X <smp> X <events> X <keep-running-flag> X ...
+
+   Consequently this function is written in multiple different ways */
+
+STATIC_INLINE_PSIM void
+run_until_stop(psim *system,
+              volatile int *keep_running)
+{
+
+#if (WITH_IDECODE_CACHE == 0 && WITH_SMP == 0)
+
+  /* CASE 1: No instruction cache and no SMP.
+
+     In this case, we can take advantage of the fact that the current
+     instruction address does not need to be returned to the cpu
+     object after every execution of an instruction.  Instead it only
+     needs to be saved when either A. the main loop exits or B. a
+     cpu-{halt,restart} call forces the loop to be re-entered.  The
+     later functions always save the current cpu instruction
+     address. */
+
+  jmp_buf halt;
+  jmp_buf restart;
+  psim_set_halt_and_restart(system, &halt, &restart);
+  if (!setjmp(halt)) {
+    do {
+      if (!setjmp(restart)) {
+       cpu *const processor = system->processors[0];
+       unsigned_word cia = cpu_get_program_counter(processor);
+       do {
+         if (WITH_EVENTS) {
+           if (event_queue_tick(system->events)) {
+             cpu_set_program_counter(processor, cia);
+             event_queue_process(system->events);
+             cia = cpu_get_program_counter(processor);
+           }
+         }
+         {
+           instruction_word const instruction
+             = vm_instruction_map_read(cpu_instruction_map(processor),
+                                       processor, cia);
+           cia = idecode_issue(processor, instruction, cia);
+         }
+       } while (keep_running == NULL || *keep_running);
+       cpu_set_program_counter(processor, cia);
+      }
+    } while(keep_running == NULL || *keep_running);
+  }
+  psim_clear_halt_and_restart(system);
+#endif
+
+
+#if (WITH_IDECODE_CACHE > 0 && WITH_SMP == 0)
+
+  /* CASE 2: Instruction case but no SMP
+
+     Here, the additional complexity comes from there being two
+     different cache implementations.  A simple function address cache
+     or a full cracked instruction cache */
+
+  jmp_buf halt;
+  jmp_buf restart;
+  psim_set_halt_and_restart(system, &halt, &restart);
+  if (!setjmp(halt)) {
+    do {
+      if (!setjmp(restart)) {
+       cpu *const processor = system->processors[0];
+       unsigned_word cia = cpu_get_program_counter(processor);
+       do {
+         if (WITH_EVENTS)
+           if (event_queue_tick(system->events)) {
+             cpu_set_program_counter(processor, cia);
+             event_queue_process(system->events);
+             cia = cpu_get_program_counter(processor);
+           }
+         { 
+           idecode_cache *const cache_entry
+             = cpu_icache(processor) + (cia / 4 % IDECODE_CACHE_SIZE);
+           if (cache_entry->address == cia) {
+             idecode_semantic *const semantic = cache_entry->semantic;
+#if WITH_IDECODE_CACHE == 1
+             cia = semantic(processor, cache_entry->instruction, cia);
+#else
+             cia = semantic(processor, cache_entry, cia);
+#endif
+           }
+           else {
+             instruction_word const instruction
+               = vm_instruction_map_read(cpu_instruction_map(processor),
+                                         processor,
+                                         cia);
+#if WITH_IDECODE_CACHE == 1
+             idecode_semantic *const semantic = idecode(processor,
+                                                        instruction,
+                                                        cia);
+#else
+             idecode_semantic *const semantic = idecode(processor,
+                                                        instruction,
+                                                        cia,
+                                                        cache_entry);
+#endif
+             cache_entry->address = cia;
+             cache_entry->semantic = semantic;
+#if WITH_IDECODE_CACHE == 1
+             cache_entry->instruction = instruction;
+             cia = semantic(processor, instruction, cia);
+#else
+             cia = semantic(processor, cache_entry, cia);
+#endif
+           }
+         }
+       } while (keep_running == NULL || *keep_running);
+       cpu_set_program_counter(processor, cia);
+      }
+    } while(keep_running == NULL || *keep_running);
+  }
+  psim_clear_halt_and_restart(system);
+#endif
+
+
+#if (WITH_IDECODE_CACHE == 0 && WITH_SMP > 0)
+
+  /* CASE 3: No ICACHE but SMP
+
+     The complexity here comes from needing to correctly restart the
+     system when it is aborted.  In particular if cpu0 requests a
+     restart, the next cpu is still cpu1.  Cpu0 being restarted after
+     all the other CPU's and the event queue have been processed */
+
+  jmp_buf halt;
+  jmp_buf restart;
+  psim_set_halt_and_restart(system, &halt, &restart);
+
+  if (!setjmp(halt)) {
+    int first_cpu = setjmp(restart);
+    if (first_cpu == 0)
+      first_cpu = system->last_cpu + 1;
+    do {
+      int current_cpu;
+      for (current_cpu = first_cpu, first_cpu = 0;
+          current_cpu < system->nr_cpus + (WITH_EVENTS ? 1 : 0);
+          current_cpu++) {
+       if (WITH_EVENTS && current_cpu == system->nr_cpus) {
+         if (event_queue_tick(system->events))
+           event_queue_process(system->events);
+       }
+       else {
+         cpu *const processor = system->processors[current_cpu];
+         unsigned_word const cia = cpu_get_program_counter(processor);
+         instruction_word instruction =
+           vm_instruction_map_read(cpu_instruction_map(processor),
+                                   processor,
+                                   cia);
+         cpu_set_program_counter(processor,
+                                 idecode_issue(processor, instruction, cia));
+       }
+       if (!(keep_running == NULL || *keep_running)) {
+         system->last_cpu = current_cpu;
+         break;
+       }
+      }
+    } while (keep_running == NULL || *keep_running);
+  }
+  psim_clear_halt_and_restart(system);
+#endif
+
+#if (WITH_IDECODE_CACHE > 0 && WITH_SMP > 0)
+
+  /* CASE 4: ICACHE and SMP ...
+
+     This time, everything goes wrong.  Need to restart loops
+     correctly, need to save the program counter and finally need to
+     keep track of each processors current address! */
+
+  jmp_buf halt;
+  jmp_buf restart;
+  psim_set_halt_and_restart(system, &halt, &restart);
+
+  if (!setjmp(halt)) {
+    int first_cpu = setjmp(restart);
+    if (!first_cpu)
+      first_cpu = system->last_cpu + 1;
+    do {
+      int current_cpu;
+      for (current_cpu = first_cpu, first_cpu = 0;
+          current_cpu < system->nr_cpus + (WITH_EVENTS ? 1 : 0);
+          current_cpu++) {
+       if (WITH_EVENTS && current_cpu == system->nr_cpus) {
+         if (event_queue_tick(system->events))
+           event_queue_process(system->events);
+       }
+       else {
+         cpu *processor = system->processors[current_cpu];
+         unsigned_word const cia = cpu_get_program_counter(processor);
+         idecode_cache *cache_entry
+           = (cpu_icache(processor) + (cia / 4 % IDECODE_CACHE_SIZE));
+         if (cache_entry->address == cia) {
+           idecode_semantic *semantic = cache_entry->semantic;
+#if WITH_IDECODE_CACHE == 1
+           cpu_set_program_counter(processor,
+                                   semantic(processor,
+                                            cache_entry->instruction,
+                                            cia);
+#else
+           cpu_set_program_counter(processor,
+                                   semantic(processor,
+                                            cache_entry,
+                                            cia);
+#endif
+         }
+         else {
+           instruction_word instruction =
+             vm_instruction_map_read(cpu_instruction_map(processor),
+                                     processor,
+                                     cia);
+#if WITH_IDECODE_CACHE == 1
+           idecode_semantic *semantic = idecode(processor,
+                                                instruction,
+                                                cia);
+#else
+           idecode_semantic *semantic = idecode(processor,
+                                                instruction,
+                                                cia,
+                                                cache_entry);
+#endif
+           cache_entry->address = cia;
+           cache_entry->semantic = semantic;
+#if WITH_IDECODE_CACHE == 1
+           cache_entry->instruction = instruction;
+           cpu_set_program_counter(processor,
+                                   semantic(processor, instruction, cia));
+#else
+           cpu_set_program_counter(processor,
+                                   semantic(processor, cache_entry, cia);
+#endif
+         }
+       }
+       if (!(keep_running == NULL || *keep_running))
+         break;
+      }
+    } while (keep_running == NULL || *keep_running);
+  }
+  psim_clear_halt_and_restart(system);
+#endif
+}
+
+
+/* SIMULATE INSTRUCTIONS, various different ways of achieving the same
+   thing */
+
+INLINE_PSIM void
+psim_step(psim *system)
+{
+  volatile int keep_running = 0;
+  psim_run_until_stop(system, &keep_running);
+}
+
+INLINE_PSIM void
+psim_run(psim *system)
+{
+  run_until_stop(system, NULL);
+}
+
+INLINE_PSIM void
+psim_run_until_stop(psim *system,
+                   volatile int *keep_running)
+{
+  run_until_stop(system, keep_running);
+}
+
+
+
+/* storage manipulation functions */
+
+INLINE_PSIM void
+psim_read_register(psim *system,
+                  int which_cpu,
+                  void *buf,
+                  const char reg[],
+                  transfer_mode mode)
+{
+  register_descriptions description;
+  char cooked_buf[sizeof(natural_word)];
+  cpu *processor;
+
+  /* find our processor */
+  if (which_cpu < 0 || which_cpu > system->nr_cpus)
+    error("psim_read_register() - invalid processor %d\n", which_cpu);
+  if (which_cpu == system->nr_cpus)
+    which_cpu = system->last_cpu;
+  processor = system->processors[which_cpu];
+
+  /* find the register description */
+  description = register_description(reg);
+  if (description.type == reg_invalid)
+    error("psim_read_register() invalid register name `%s'\n", reg);
+
+  /* get the cooked value */
+  switch (description.type) {
+
+  case reg_gpr:
+    *(gpreg*)cooked_buf = cpu_registers(processor)->gpr[description.index];
+    break;
+
+  case reg_spr:
+    *(spreg*)cooked_buf = cpu_registers(processor)->spr[description.index];
+    break;
+    
+  case reg_sr:
+    *(sreg*)cooked_buf = cpu_registers(processor)->sr[description.index];
+    break;
+
+  case reg_fpr:
+    *(fpreg*)cooked_buf = cpu_registers(processor)->fpr[description.index];
+    break;
+
+  case reg_pc:
+    *(unsigned_word*)cooked_buf = cpu_get_program_counter(processor);
+    break;
+
+  case reg_cr:
+    *(creg*)cooked_buf = cpu_registers(processor)->cr;
+    break;
+
+  case reg_msr:
+    *(msreg*)cooked_buf = cpu_registers(processor)->msr;
+    break;
+
+  default:
+    printf_filtered("psim_read_register(processor=0x%x,buf=0x%x,reg=%s) %s\n",
+                   processor, buf, reg,
+                   "read of this register unimplemented");
+    break;
+
+  }
+
+  /* the PSIM internal values are in host order.  To fetch raw data,
+     they need to be converted into target order and then returned */
+  if (mode == raw_transfer) {
+    /* FIXME - assumes that all registers are simple integers */
+    switch (description.size) {
+    case 1: 
+      *(unsigned_1*)buf = H2T_1(*(unsigned_1*)cooked_buf);
+      break;
+    case 2:
+      *(unsigned_2*)buf = H2T_2(*(unsigned_2*)cooked_buf);
+      break;
+    case 4:
+      *(unsigned_4*)buf = H2T_4(*(unsigned_4*)cooked_buf);
+      break;
+    case 8:
+      *(unsigned_8*)buf = H2T_8(*(unsigned_8*)cooked_buf);
+      break;
+    }
+  }
+  else {
+    bcopy(cooked_buf, buf, description.size);
+  }
+
+}
+
+
+
+INLINE_PSIM void
+psim_write_register(psim *system,
+                   int which_cpu,
+                   const void *buf,
+                   const char reg[],
+                   transfer_mode mode)
+{
+  cpu *processor;
+  register_descriptions description;
+  char cooked_buf[sizeof(natural_word)];
+
+  /* find our processor */
+  if (which_cpu == -1) {
+    int i;
+    for (i = 0; i < system->nr_cpus; i++)
+      psim_write_register(system, i, buf, reg, mode);
+    return;
+  }
+  else if (which_cpu == system->nr_cpus) {
+    which_cpu = system->last_cpu;
+  }
+  else if (which_cpu < 0 || which_cpu >= system->nr_cpus) {
+    error("psim_read_register() - invalid processor %d\n", which_cpu);
+  }
+
+  processor = system->processors[which_cpu];
+
+  /* find the description of the register */
+  description = register_description(reg);
+  if (description.type == reg_invalid)
+    error("psim_write_register() invalid register name %s\n", reg);
+
+  /* If the data is comming in raw (target order), need to cook it
+     into host order before putting it into PSIM's internal structures */
+  if (mode == raw_transfer) {
+    switch (description.size) {
+    case 1: 
+      *(unsigned_1*)cooked_buf = T2H_1(*(unsigned_1*)buf);
+      break;
+    case 2:
+      *(unsigned_2*)cooked_buf = T2H_2(*(unsigned_2*)buf);
+      break;
+    case 4:
+      *(unsigned_4*)cooked_buf = T2H_4(*(unsigned_4*)buf);
+      break;
+    case 8:
+      *(unsigned_8*)cooked_buf = T2H_8(*(unsigned_8*)buf);
+      break;
+    }
+  }
+  else {
+    bcopy(buf, cooked_buf, description.size);
+  }
+
+  /* put the cooked value into the register */
+  switch (description.type) {
+
+  case reg_gpr:
+    cpu_registers(processor)->gpr[description.index] = *(gpreg*)cooked_buf;
+    break;
+
+  case reg_fpr:
+    cpu_registers(processor)->fpr[description.index] = *(fpreg*)cooked_buf;
+    break;
+
+  case reg_pc:
+    cpu_set_program_counter(processor, *(unsigned_word*)cooked_buf);
+    break;
+
+  case reg_spr:
+    cpu_registers(processor)->spr[description.index] = *(spreg*)cooked_buf;
+    break;
+
+  case reg_sr:
+    cpu_registers(processor)->sr[description.index] = *(sreg*)cooked_buf;
+    break;
+
+  case reg_cr:
+    cpu_registers(processor)->cr = *(creg*)cooked_buf;
+    break;
+
+  case reg_msr:
+    cpu_registers(processor)->msr = *(msreg*)cooked_buf;
+    break;
+
+  default:
+    printf_filtered("psim_write_register(processor=0x%x,cooked_buf=0x%x,reg=%s) %s\n",
+                   processor, cooked_buf, reg,
+                   "read of this register unimplemented");
+    break;
+
+  }
+
+}
+
+
+
+INLINE_PSIM unsigned
+psim_read_memory(psim *system,
+                int which_cpu,
+                void *buffer,
+                unsigned_word vaddr,
+                unsigned len,
+                transfer_mode mode)
+{
+  cpu *processor;
+  if (which_cpu < 0 || which_cpu > system->nr_cpus)
+    error("psim_read_memory() invalid cpu\n");
+  if (which_cpu == system->nr_cpus)
+    which_cpu = system->last_cpu;
+  processor = system->processors[which_cpu];
+  return vm_data_map_read_buffer(cpu_data_map(processor),
+                                buffer, vaddr, len, mode);
+}
+
+
+INLINE_PSIM unsigned
+psim_write_memory(psim *system,
+                 int which_cpu,
+                 const void *buffer,
+                 unsigned_word vaddr,
+                 unsigned len,
+                 transfer_mode mode,
+                 int violate_read_only_section)
+{
+  cpu *processor;
+  if (which_cpu < 0 || which_cpu > system->nr_cpus)
+    error("psim_read_memory() invalid cpu\n");
+  if (which_cpu == system->nr_cpus)
+    which_cpu = system->last_cpu;
+  processor = system->processors[which_cpu];
+  return vm_data_map_write_buffer(cpu_data_map(processor),
+                                 buffer, vaddr, len, mode, 1);
+}
+
+
+#endif /* _PSIM_C_ */