add some comments
authorDoug Evans <dje@google.com>
Wed, 4 Nov 1998 19:27:20 +0000 (19:27 +0000)
committerDoug Evans <dje@google.com>
Wed, 4 Nov 1998 19:27:20 +0000 (19:27 +0000)
sim/common/cgen-engine.h [new file with mode: 0644]
sim/common/genmloop.sh

diff --git a/sim/common/cgen-engine.h b/sim/common/cgen-engine.h
new file mode 100644 (file)
index 0000000..191e904
--- /dev/null
@@ -0,0 +1,263 @@
+/* Simulator header for the cgen engine.
+   Copyright (C) 1998 Free Software Foundation, Inc.
+   Contributed by Cygnus Support.
+
+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.  */
+
+/* This file must be included after eng.h has been included
+   as it specifies the configuration of the engine.  */
+
+#ifndef CGEN_ENGINE_H
+#define CGEN_ENGINE_H
+
+/* Execution support.  */
+
+#if WITH_SCACHE
+
+/* instruction address
+   ??? This was intended to be a struct of two elements in the WITH_SCACHE_PBB
+   case.  The first element is the PCADDR, the second element is the SCACHE *.
+   Haven't found the time yet to make this work, but it is a nicer approach
+   than the current br_cache stuff.  */
+typedef PCADDR IADDR;
+/* current instruction address */
+typedef PCADDR CIA;
+/* argument to semantic functions */
+typedef SCACHE *SEM_ARG;
+/* semantic code's version of pc */
+#if WITH_SCACHE_PBB
+typedef SCACHE *SEM_PC;
+#else
+typedef PCADDR SEM_PC;
+#endif
+
+#else /* ! WITH_SCACHE */
+
+/* instruction address */
+typedef PCADDR IADDR;
+/* current instruction address */
+typedef PCADDR CIA;
+/* argument to semantic functions */
+typedef ARGBUF *SEM_ARG;
+/* semantic code's version of pc */
+typedef PCADDR SEM_PC;
+
+#endif /* ! WITH_SCACHE */
+
+/* Semantic functions come in six versions on two axes:
+   fast/full-featured, and using one of the simple/scache/compilation engines.
+   A full featured simulator is always provided.  --enable-sim-fast includes
+   support for fast execution by duplicating the semantic code but leaving
+   out all features like tracing and profiling.
+   Using the scache is selected with --enable-sim-scache.  */
+/* FIXME: --enable-sim-fast not implemented yet.  */
+/* FIXME: undecided how to handle WITH_SCACHE_PBB.  */
+
+/* Types of the machine generated extract and semantic fns.  */
+typedef void (EXTRACT_FN) (SIM_CPU *, PCADDR, insn_t, ARGBUF *);
+#ifdef HAVE_PARALLEL_EXEC
+typedef SEM_PC (SEMANTIC_FN) (SIM_CPU *, SEM_ARG, PAREXEC *);
+#else
+typedef SEM_PC (SEMANTIC_FN) (SIM_CPU *, SEM_ARG);
+#endif
+
+union sem {
+#if ! WITH_SEM_SWITCH_FULL
+  SEMANTIC_FN *sem_full;
+#endif
+#if ! WITH_SEM_SWITCH_FAST
+  SEMANTIC_FN *sem_fast;
+#endif
+#if WITH_SEM_SWITCH_FULL || WITH_SEM_SWITCH_FAST
+#ifdef __GNUC__
+  void *sem_case;
+#else
+  int sem_case;
+#endif
+#endif
+};
+
+/* Set the appropriate semantic handler in ABUF.  */
+
+#if WITH_SEM_SWITCH_FULL
+#ifdef __GNUC__
+#define SEM_SET_FULL_CODE(abuf, idesc) \
+  do { (abuf)->semantic.sem_case = (idesc)->sem_full_lab; } while (0)
+#else 
+#define SEM_SET_FULL_CODE(abuf, idesc) \
+  do { (abuf)->semantic.sem_case = (idesc)->num; } while (0)
+#endif
+#else
+#define SEM_SET_FULL_CODE(abuf, idesc) \
+  do { (abuf)->semantic.sem_full = (idesc)->sem_full; } while (0)
+#endif
+
+#if WITH_SEM_SWITCH_FAST
+#ifdef __GNUC__
+#define SEM_SET_FAST_CODE(abuf, idesc) \
+  do { (abuf)->semantic.sem_case = (idesc)->sem_fast_lab; } while (0)
+#else 
+#define SEM_SET_FAST_CODE(abuf, idesc) \
+  do { (abuf)->semantic.sem_case = (idesc)->num; } while (0)
+#endif
+#else
+#define SEM_SET_FAST_CODE(abuf, idesc) \
+  do { (abuf)->semantic.sem_fast = (idesc)->sem_fast; } while (0)
+#endif
+
+#define SEM_SET_CODE(abuf, idesc, fast_p) \
+do { \
+  if (fast_p) \
+    SEM_SET_FAST_CODE ((abuf), (idesc)); \
+  else \
+    SEM_SET_FULL_CODE ((abuf), (idesc)); \
+} while (0)
+
+#define IDESC_CTI_P(idesc) \
+     ((CGEN_ATTR_BOOLS (CGEN_INSN_ATTRS ((idesc)->opcode)) \
+       & (CGEN_ATTR_MASK (CGEN_INSN_COND_CTI) \
+         | CGEN_ATTR_MASK (CGEN_INSN_UNCOND_CTI))) \
+      != 0)
+#define IDESC_SKIP_P(idesc) \
+     ((CGEN_ATTR_BOOLS (CGEN_INSN_ATTRS ((idesc)->opcode)) \
+       & CGEN_ATTR_MASK (CGEN_INSN_SKIP_CTI)) \
+      != 0)
+\f
+/* Engine support.  */
+
+/* These are used so that we can compile two copies of the semantic code,
+   one with full feature support and one without that runs fast(er).  */
+/* FIXME: Eventually delete extraction if not using scache.  */
+#define EX_FN_NAME(cpu,fn) XCONCAT3 (cpu,_ex_,fn)
+#define SEM_FN_NAME(cpu,fn) XCONCAT3 (cpu,_sem_,fn)
+#define SEMF_FN_NAME(cpu,fn) XCONCAT3 (cpu,_semf_,fn)
+
+#if WITH_SCACHE
+
+#define CIA_ADDR(cia) (cia)
+
+/* semantics.c support */
+#define SEM_ARGBUF(sem_arg) (& (sem_arg) -> argbuf)
+#define SEM_INSN(sem_arg) shouldnt_be_used
+#define SEM_NEXT_VPC(sc, len) ((sc) + 1)
+
+#if WITH_SCACHE_PBB
+
+/* Update the instruction counter.  */
+#define PBB_UPDATE_INSN_COUNT(cpu,sc) \
+  (CPU_INSN_COUNT (cpu) += SEM_ARGBUF (sc) -> fields.chain.insn_count)
+
+/* Value for br_addr_ptr indicating branch wasn't taken.  */
+#define SEM_BRANCH_UNTAKEN ((SEM_PC *) 0)
+/* Value for br_addr_ptr indicating branch was taken to uncacheable
+   address (e.g. j reg).  */
+#define SEM_BRANCH_UNCACHEABLE ((SEM_PC *) 1)
+
+/* ??? Only necessary if SEM_BRANCH_VIA_CACHE will be used,
+   but for simplicity it's done this way.  */
+#define SEM_BRANCH_INIT_EXTRACT(abuf) \
+do { (abuf)->fields.cti.addr_cache = 0; } while (0)
+
+/* Do not append a `;' to invocations of this.
+   npc,npc_ptr are for communication between the cti insn and cti-chain.  */
+#define SEM_BRANCH_INIT \
+  PCADDR npc = 0; /* assign a value for -Wall */ \
+  SEM_PC *npc_ptr = SEM_BRANCH_UNTAKEN;
+/* SEM_IN_SWITCH is defined at the top of the mainloop.c files
+   generated by genmloop.sh.  It exists so generated semantic code needn't
+   care whether it's being put in a switch or in a function.  */
+#ifdef SEM_IN_SWITCH
+/* Do not append a `;' to invocations of this.
+   ??? Unnecessary here, but for consistency with ..._INIT.  */
+#define SEM_BRANCH_FINI \
+{ \
+  pbb_br_npc = npc; \
+  pbb_br_npc_ptr = npc_ptr; \
+}
+#else /* 1 semantic function per instruction */
+/* Do not append a `;' to invocations of this.
+   ??? Unnecessary here, but for consistency with ..._INIT.  */
+#define SEM_BRANCH_FINI \
+{ \
+  CPU_PBB_BR_NPC (current_cpu) = npc; \
+  CPU_PBB_BR_NPC_PTR (current_cpu) = npc_ptr; \
+}
+#endif
+
+/* Return address of cached branch address value.  */
+#define SEM_BRANCH_ADDR_CACHE(sem_arg) \
+  (& SEM_ARGBUF (sem_arg)->fields.cti.addr_cache)
+#define SEM_BRANCH_VIA_CACHE(cpu, sc, newval, pcvar, cachevarptr) \
+do { \
+  npc = (newval); \
+  npc_ptr = (cachevarptr); \
+} while (0)
+#define SEM_BRANCH_VIA_ADDR(cpu, sc, newval, pcvar) \
+do { \
+  npc = (newval); \
+  npc_ptr = SEM_BRANCH_UNCACHEABLE; \
+} while (0)
+
+#else /* ! WITH_SCACHE_PBB */
+
+#define SEM_BRANCH_INIT
+#define SEM_BRANCH_FINI
+
+#define SEM_BRANCH_ADDR_CACHE(sem_arg) shouldnt_be_used
+#define SEM_BRANCH_VIA_CACHE(cpu, sc, newval, pcvar, cachevar) \
+do { \
+  (pcvar) = (newval); \
+} while (0)
+#define SEM_BRANCH_VIA_ADDR(cpu, sc, newval, pcvar) \
+do { \
+  (pcvar) = (newval); \
+} while (0)
+
+#endif /* ! WITH_SCACHE_PBB */
+
+/* Return address a branch insn will branch to.
+   This is only used during tracing.  */
+#define SEM_NEW_PC_ADDR(new_pc) (new_pc)
+
+#else /* ! WITH_SCACHE */
+
+#define CIA_ADDR(cia) (cia)
+
+/* semantics.c support */
+#define SEM_ARGBUF(sem_arg) (sem_arg)
+#define SEM_INSN(sem_arg) (SEM_ARGBUF (sem_arg) -> insn)
+/* FIXME:wip */
+#define SEM_NEXT_VPC(abuf, len) ((abuf) -> addr + (abuf) -> length)
+
+#define SEM_BRANCH_INIT
+#define SEM_BRANCH_FINI
+
+#define SEM_BRANCH_ADDR_CACHE(sem_arg) shouldnt_be_used
+#define SEM_BRANCH_VIA_CACHE(cpu, abuf, newval, pcvar, cachevar) \
+do { \
+  (pcvar) = (newval); \
+} while (0)
+#define SEM_BRANCH_VIA_ADDR(cpu, abuf, newval, pcvar) \
+do { \
+  (pcvar) = (newval); \
+} while (0)
+
+#define SEM_NEW_PC_ADDR(new_pc) (new_pc)
+
+#endif /* ! WITH_SCACHE */
+
+#endif /* CGEN_ENGINE_H */
index a66f1bb4e00d83dda7fa86289a6dbd7624aaf618..91139914bd9c01ac342eb21b519644151cf16e69 100644 (file)
-# This shell script emits a C file. -*- C -*-
 # Generate the main loop of the simulator.
-# Syntax: genmloop.sh /bin/sh [options] cpu mainloop.in
-# Options: [-mono|-multi] -scache -fast -parallel
+# Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+# Contributed by Cygnus Support.
+#
+# This file is part of the GNU simulators.
+#
+# 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.
+#
+# This file creates two files: eng.hin and mloop.cin.
+# eng.hin defines a few macros that specify what kind of engine was selected
+# based on the arguments to this script.
+# mloop.cin contains the engine.
+#
+# ??? Rename mloop.c to eng.c?
+# ??? Rename mainloop.in to engine.in?
+# ??? Rename this file to genengine.sh?
+#
+# Syntax: genmloop.sh [options]
+#
+# Options:
+#
+# -mono | -multi
+#    - specify single cpu or multiple cpus (number specifyable at runtime),
+#      maximum number is a configuration parameter
+#    - -multi wip
 #
-# -scache: use the scache
 # -fast: include support for fast execution in addition to full featured mode
+#
+#      Full featured mode is for tracing, profiling, etc. and is always
+#      provided.  Fast mode contains no frills, except speed.
+#      A target need only provide a "full" version of one of
+#      simple,scache,pbb.  If the target wants it can also provide a fast
+#      version of same.  It can't provide more than this.
+#      ??? Later add ability to have another set of full/fast semantics
+#      for use in with-devices/with-smp situations (pbb can be inappropriate
+#      here).
+#
+# -full-switch: same as -fast but for full featured version of -switch
+#      Only needed if -fast present.
+#
+# -simple: simple execution engine (the default)
+#
+#      This engine fetches and executes one instruction at a time.
+#      ??? The implementation is currently slower than necessary for
+#      simplicity.  Instead of storing extract insn fields in ARGBUF,
+#      they should just be extracted from the insn when needed.
+#
+# -scache: use the scache to speed things up (not always a win)
+#
+#      This engine caches the extracted instruction before executing it.
+#      When executing instructions they are first looked up in the scache.
+#
+# -pbb: same as -scache but extract a (pseudo-) basic block at a time
+#
+#      This engine is basically identical to the scache version except that
+#      extraction is done a pseudo-basic-block at a time and the address of
+#      the scache entry of a branch target is recorded as well.
+#      Additional speedups are then possible by defering Ctrl-C checking
+#      to the end of basic blocks and by threading the insns together.
+#      We call them pseudo-basic-block's instead of just basic-blocks because
+#      they're not necessarily basic-blocks, though normally are.
+#
 # -parallel: cpu can execute multiple instructions parallely
 #
-# FIXME: "multi" support is wip.
-
+#      This option is specified in addition to -simple, -scache, -pbb.
+#
+# -switch file: specify file containing semantics implemented as a switch()
+#
+# -cpu <cpu-family>
+#
+#      Specify the cpu family name.
+#
+# -infile <input-file>
+#
+#      Specify the mainloop.in input file.
+#
+# Only one of -scache/-pbb may be selected.
+# -simple is the default.
+#
+####
+#
 # TODO
-# - move this C code to mainloop.in
-# - keep genmloop.sh
-# - build exec.in from .cpu file
-# - have each cpu provide handwritten cycle.in
-# - integrate with common/sim-engine.[ch]
-# - for sparc, have two main loops, outer one handles delay slot when npc != 0
-#   - inner loop does not handle delay slots, pc = pc + 4
+# - build mainloop.in from .cpu file
 
 type=mono
 #scache=
 #fast=
+#full_switch=
+#pbb=
 #parallel=
+switch=
+cpu="unknown"
+infile=""
 
-shell=$1 ; shift
-
-while true
+while test $# -gt 0
 do
        case $1 in
        -mono) type=mono ;;
        -multi) type=multi ;;
-       -no-scache) ;;
-       -scache) scache=yes ;;
        -no-fast) ;;
        -fast) fast=yes ;;
+       -full-switch) full_switch=yes ;;
+       -simple) ;;
+       -scache) scache=yes ;;
+       -pbb) pbb=yes ;;
        -no-parallel) ;;
        -parallel) parallel=yes ;;
-       *) break ;;
+       -switch) shift ; switch=$1 ;;
+       -cpu) shift ; cpu=$1 ;;
+       -infile) shift ; infile=$1 ;;
+       *) echo "unknown option: $1" >&2 ; exit 1 ;;
        esac
        shift
 done
 
-cpu=$1
-file=$2
+# Argument validation.
 
-cat <<EOF
-/* This file is is generated by the genmloop script.  DO NOT EDIT! */
+if [ x$scache = xyes -a x$pbb = xyes ] ; then
+    echo "only one of -scache and -pbb may be selected" >&2
+    exit 1
+fi
 
-/* Main loop for CGEN-based simulators.
-   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
-   Contributed by Cygnus Support.
+if [ "x$cpu" = xunknown ] ; then
+    echo "cpu family not specified" >&2
+    exit 1
+fi
 
-This file is part of the GNU simulators.
+if [ "x$infile" = x ] ; then
+    echo "mainloop.in not specified" >&2
+    exit 1
+fi
 
-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.
+lowercase='abcdefghijklmnopqrstuvwxyz'
+uppercase='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+CPU=`echo ${cpu} | tr "${lowercase}" "${uppercase}"`
 
-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.  */
+rm -f eng.hin
+exec 1>eng.hin
 
-/* We want the scache version of SEM_ARG.
-   This is used by the switch() version of the semantic code.  */
-EOF
+echo "/* engine configuration for ${cpu} */"
+echo ""
 
-if [ x$scache = xyes ] ; then
-       echo "#define SCACHE_P"
+echo "/* WITH_FAST: non-zero if a fast version of the engine is available"
+echo "   in addition to the full-featured version.  */"
+if [ x$fast = xyes ] ; then
+       echo "#define WITH_FAST 1"
 else
-       echo '/*#define SCACHE_P*/'
-       echo '#undef WITH_SCACHE'
-       echo '#define WITH_SCACHE 0'
+       echo "#define WITH_FAST 0"
 fi
 
-cat <<EOF
+echo ""
+echo "/* WITH_SCACHE_PBB_${CPU}: non-zero if the pbb engine was selected.  */"
+if [ x$pbb = xyes ] ; then
+       echo "#define WITH_SCACHE_PBB_${CPU} 1"
+else
+       echo "#define WITH_SCACHE_PBB_${CPU} 0"
+fi
+
+echo ""
+echo "/* HAVE_PARALLEL_EXEC: defined if cpu can parallelly execute > 1 insn.  */"
+if [ x$parallel = xyes ] ; then
+       echo "#define HAVE_PARALLEL_EXEC"
+else
+       echo "#undef HAVE_PARALLEL_EXEC"
+fi
+
+if [ "x$switch" != x ] ; then
+       echo ""
+       echo "/* WITH_SEM_SWITCH_FULL: non-zero if full-featured engine is"
+       echo "   implemented as a switch().  */"
+       if [ x$fast != xyes -o x$full_switch = xyes ] ; then
+               echo "#define WITH_SEM_SWITCH_FULL 1"
+       else
+               echo "#define WITH_SEM_SWITCH_FULL 0"
+       fi
+       echo ""
+       echo "/* WITH_SEM_SWITCH_FAST: non-zero if fast engine is"
+       echo "   implemented as a switch().  */"
+       if [ x$fast = xyes ] ; then
+               echo "#define WITH_SEM_SWITCH_FAST 1"
+       else
+               echo "#define WITH_SEM_SWITCH_FAST 0"
+       fi
+fi
+
+##########################################################################
+
+rm -f tmp-mloop.cin mloop.cin
+exec 1>tmp-mloop.cin
+
+# We use @cpu@ instead of ${cpu} because we still want to run sed to handle
+# transformation of @cpu@ for mainloop.in.
+
+cat << EOF
+/* This file is generated by the genmloop script.  DO NOT EDIT! */
+
+/* Enable switch() support in cgen headers.  */
+#define SEM_IN_SWITCH
 
 #define WANT_CPU
-#define WANT_CPU_@CPU@
+#define WANT_CPU_${CPU}
 
 #include "sim-main.h"
 #include "bfd.h"
@@ -92,186 +222,44 @@ cat <<EOF
 #include "cpu-sim.h"
 #include "sim-assert.h"
 
-/* Tell sim_main_loop to use the scache if it's active.
-   Collecting profile data and tracing slow us down so we don't do them in
-   "fast mode".
-   There are 2 possibilities on 2 axes:
-   - use or don't use the scache
-   - run normally (full featured) or run fast
-   Supporting all four possibilities in one executable is a bit much but
-   supporting full/fast seems reasonable.
-   If the scache is configured in it is always used.
-   ??? Need to see whether it speeds up profiling significantly or not.
-   Speeding up tracing doesn't seem worth it.
-   ??? Sometimes supporting more than one set of semantic functions will make
-   the simulator too large - this should be configurable.
-*/
-
-#if WITH_SCACHE
-#define RUN_FAST_P(cpu) (STATE_RUN_FAST_P (CPU_STATE (cpu)))
-#else
-#define RUN_FAST_P(cpu) 0
-#endif
-
-#ifndef SIM_PRE_EXEC_HOOK
-#define SIM_PRE_EXEC_HOOK(state)
-#endif
-
-#ifndef SIM_POST_EXEC_HOOK
-#define SIM_POST_EXEC_HOOK(state)
-#endif
-
-#if 0 /* FIXME:experiment */
-/* "sc" is local to the calling function.
-   It is done this way to keep the internals of the implementation out of
-   the description file.  */
-#define EXTRACT(cpu, pc, insn, sc, num, fast_p) \
-@cpu@_extract (cpu, pc, insn, sc + num, fast_p)
-
-#define EXECUTE(cpu, sc, num, fast_p) \
-@cpu@_execute (cpu, sc + num, fast_p)
-#endif
-
-#define GET_ATTR(cpu, num, attr) \
-CGEN_INSN_ATTR (sc[num].argbuf.opcode, CGEN_INSN_##attr)
-
 EOF
 
-${SHELL} $file support
-
-cat <<EOF
+${SHELL} $infile support
 
-static volatile int keep_running;
-/* FIXME: Should each cpu have its own copy?  */
-static volatile enum sim_stop pending_reason;
-static volatile int pending_sigrc;
+##########################################################################
 
-/* Want to measure simulator speed even in fast mode.  */
-static unsigned long insn_count;
-static SIM_ELAPSED_TIME start_time;
+# Simple engine: fetch an instruction, execute the instruction.
 
-/* Forward decls of cpu-specific functions.  */
-static void engine_resume (SIM_DESC, int, int);
-static void engine_resume_full (SIM_DESC);
-${scache+static void engine_resume_fast (SIM_DESC);}
+if [ x$scache != xyes -a x$pbb != xyes ] ; then
 
-/* Stop the simulation for REASON/SIGRC.
-   CPU is the cpu being stopped [at address PC].
-   If CPU is NULL, all cpu's are stopping for the same reason.  */
+    cat << EOF
 
-int
-@cpu@_engine_stop (SIM_DESC sd, SIM_CPU *cpu, PCADDR pc,
-                  enum sim_stop reason, int sigrc)
-{
-  keep_running = 0;
-  pending_reason = reason;
-  pending_sigrc = sigrc;
-  return 1;
-}
+#define FAST_P 0
 
 void
-@cpu@_engine_run (SIM_DESC sd, int step, int siggnal)
+${cpu}_engine_run_full (SIM_CPU *current_cpu)
 {
-#if WITH_SCACHE
-  if (USING_SCACHE_P (sd))
-    scache_flush (sd);
-#endif
-  engine_resume (sd, step, siggnal);
-}
-
-static void
-engine_resume (SIM_DESC sd, int step, int siggnal)
-{
-  sim_cpu *current_cpu = STATE_CPU (sd, 0);
-  /* These are volatile to survive setjmp.  */
-  volatile sim_cpu *cpu = current_cpu;
-  volatile sim_engine *engine = STATE_ENGINE (sd);
-  jmp_buf buf;
-  int jmpval;
-
-  keep_running = ! step;
-  start_time = sim_elapsed_time_get ();
-  /* FIXME: Having this global can slow things down a teensy bit.
-     After things are working see about moving engine_resume_{full,fast}
-     back into this function.  */
-  insn_count = 0;
-
-  engine->jmpbuf = &buf;
-  sim_engine_set_run_state (sd, sim_running, 0);
-  pending_reason = sim_running;
-  pending_sigrc = 0;
-
-  /* ??? Restart support to be added in time.  */
-
-  if (setjmp (buf))
-    {
-      /* Account for the last insn executed.  */
-      ++insn_count;
-      TRACE_INSN_FINI ((sim_cpu *) cpu, 1);
-    }
-  else
-    {
-      /* The computed goto switch can be used, and while the number of blocks
-        may swamp the relatively few that this function contains, when running
-        with the scache we put the actual semantic code in their own
-        functions.  */
-
-EOF
-
-if [ x$fast = xyes ] ; then
-       cat <<EOF
-      if (step
-          || !RUN_FAST_P (current_cpu))
-       engine_resume_full (sd);
-      else
-       engine_resume_fast (sd);
-EOF
-else
-       cat <<EOF
-      engine_resume_full (sd);
-EOF
-fi
-
-cat <<EOF
-
-      /* If the loop exits, either we single-stepped or @cpu@_engine_stop
-        was called.  */
-      if (step)
-       sim_engine_set_run_state (sd, sim_stopped, SIM_SIGTRAP);
-      else
-       sim_engine_set_run_state (sd, pending_reason, pending_sigrc);
-    }
-
-  engine->jmpbuf = NULL;
-  PROFILE_EXEC_TIME (CPU_PROFILE_DATA (cpu))
-    += sim_elapsed_time_since (start_time);
-  PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu))
-    += insn_count;
-}
+#define FAST_P 0
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE cache[MAX_LIW_INSNS];
+  SCACHE *sc = &cache[0];
 
 EOF
 
-##########################################################################
-
-if [ x$scache = xyes ] ; then
-       cat <<EOF
-
-static void
-engine_resume_full (SIM_DESC sd)
-{
-#define FAST_P 0
-  /* current_{state,cpu} exist for the generated code to use.  */
-  SIM_DESC current_state = sd;
-  sim_cpu *current_cpu = STATE_CPU (sd, 0);
+if [ x$parallel = xyes ] ; then
+  cat << EOF
+  PAREXEC pbufs[MAX_PARALLEL_INSNS];
+  PAREXEC *par_exec;
 
 EOF
+fi
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
-${SHELL} $file init
+${SHELL} $infile init
 
 if [ x$parallel = xyes ] ; then
-cat << EOF
+  cat << EOF
 
 #if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
   {
@@ -288,87 +276,118 @@ cat << EOF
 EOF
 fi
 
-cat <<EOF
+cat << EOF
 
   do
     {
-      /* FIXME: Later check every insn for events and such.  */
-
-      SIM_PRE_EXEC_HOOK (current_cpu);
+/* begin full-{extract,exec}-simple */
+EOF
 
-      {
-       unsigned int hash;
-       SCACHE *sc;
-       PCADDR pc = PC;
+${SHELL} $infile extract-simple
+echo ""
+${SHELL} $infile full-exec-simple
 
-       /* First step: look up current insn in hash table.  */
-       hash = SCACHE_HASH_PC (sd, pc);
-       sc = CPU_SCACHE_CACHE (current_cpu) + hash;
+cat << EOF
+/* end full-{extract,exec}-simple */
 
-       /* If the entry isn't the one we want (cache miss),
-          fetch and decode the instruction.  */
-       if (sc->argbuf.addr != pc)
-         {
-           insn_t insn;
+      ++ CPU_INSN_COUNT (current_cpu);
+    }
+  while (0 /*CPU_RUNNING_P (current_cpu)*/);
+#undef FAST_P
+}
 
-           PROFILE_COUNT_SCACHE_MISS (current_cpu);
+#undef FAST_P
 
-/* begin full-extract-scache */
 EOF
 
-${SHELL} $file full-extract-scache
+####################################
 
-cat <<EOF
-/* end full-extract-scache */
-         }
-       else
-         {
-           PROFILE_COUNT_SCACHE_HIT (current_cpu);
-           /* Make core access statistics come out right.
-              The size is a guess, but it's currently not used either.  */
-           PROFILE_COUNT_CORE (current_cpu, pc, 2, exec_map);
-         }
+# Simple engine: fast version.
+# ??? A somewhat dubious effort, but for completeness' sake.
 
-/* begin full-exec-scache */
-EOF
+if [ x$fast = xyes ] ; then
 
-${SHELL} $file full-exec-scache
+    cat << EOF
 
-cat <<EOF
-/* end full-exec-scache */
-      }
+#define FAST_P 1
 
-      SIM_POST_EXEC_HOOK (current_cpu);
+FIXME
 
-      ++insn_count;
-    }
-  while (keep_running);
 #undef FAST_P
-}
+
 EOF
 
+fi # -fast
+
+fi # simple engine
+
 ##########################################################################
 
-else # ! WITH_SCACHE
-       cat <<EOF
+# Scache engine: lookup insn in scache, fetch if missing, then execute it.
+
+if [ x$scache = xyes ] ; then
+
+    cat << EOF
 
-static void
-engine_resume_full (SIM_DESC sd)
+static INLINE SCACHE *
+${cpu}_scache_lookup (SIM_CPU *current_cpu, SCACHE *scache,
+                     unsigned int hash_mask, int FAST_P)
 {
+  /* First step: look up current insn in hash table.  */
+  PCADDR pc = PC;
+  SCACHE *sc = scache + SCACHE_HASH_PC (pc, hash_mask);
+
+  /* If the entry isn't the one we want (cache miss),
+     fetch and decode the instruction.  */
+  if (sc->argbuf.addr != pc)
+    {
+      insn_t insn;
+
+      if (FAST_P)
+       PROFILE_COUNT_SCACHE_MISS (current_cpu);
+
+/* begin extract-scache */
+EOF
+
+${SHELL} $infile extract-scache
+
+cat << EOF
+/* end extract-scache */
+    }
+  else if (FAST_P)
+    {
+      PROFILE_COUNT_SCACHE_HIT (current_cpu);
+      /* Make core access statistics come out right.
+        The size is a guess, but it's currently not used either.  */
+      PROFILE_COUNT_CORE (current_cpu, pc, 2, exec_map);
+    }
+}
+
 #define FAST_P 0
-  SIM_DESC current_state = sd;
-  sim_cpu *current_cpu = STATE_CPU (sd, 0);
-  SCACHE cache[MAX_LIW_INSNS];
-  SCACHE *sc = &cache[0];
+
+void
+${cpu}_engine_run_full (SIM_CPU *current_cpu)
+{
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+  unsigned int hash_mask = CPU_SCACHE_HASH_MASK (current_cpu);
 
 EOF
 
+if [ x$parallel = xyes ] ; then
+  cat << EOF
+  PAREXEC pbufs[MAX_PARALLEL_INSNS];
+  PAREXEC *par_exec;
+
+EOF
+fi
+
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
-${SHELL} $file init
+${SHELL} $infile init
 
 if [ x$parallel = xyes ] ; then
-cat << EOF
+  cat << EOF
 
 #if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
   {
@@ -385,58 +404,67 @@ cat << EOF
 EOF
 fi
 
-cat <<EOF
+cat << EOF
 
   do
     {
-      /* FIXME: Later check every insn for events and such.  */
+      PCADDR new_pc;
+      SCACHE *sc;
 
-      SIM_PRE_EXEC_HOOK (current_cpu);
+      sc = ${cpu}_scache_lookup (current_cpu, scache, hash_mask, FAST_P);
 
-      {
-/* begin full-{extract,exec}-noscache */
+/* begin full-exec-scache */
 EOF
 
-${SHELL} $file full-extract-noscache
-echo ""
-${SHELL} $file full-exec-noscache
+${SHELL} $infile full-exec-scache
 
-cat <<EOF
-/* end full-{extract,exec}-noscache */
-      }
+cat << EOF
+/* end full-exec-scache */
 
-      SIM_POST_EXEC_HOOK (current_cpu);
+      CPU (h_pc) = new_pc;
 
-      ++insn_count;
+      ++ CPU_INSN_COUNT (current_cpu);
     }
-  while (keep_running);
-#undef FAST_P
+  while (0 /*CPU_RUNNING_P (current_cpu)*/);
 }
 
+#undef FAST_P
+
 EOF
-fi # ! WITH_SCACHE
 
-##########################################################################
+####################################
+
+# Scache engine: fast version.
 
 if [ x$fast = xyes ] ; then
-    if [ x$scache = xyes ] ; then
-       cat <<EOF
 
-static void
-engine_resume_fast (SIM_DESC sd)
-{
+    cat << EOF
+
 #define FAST_P 1
-  SIM_DESC current_state = sd;
-  sim_cpu *current_cpu = STATE_CPU (sd, 0);
+
+void
+${cpu}_engine_run_fast (SIM_CPU *current_cpu)
+{
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+  unsigned int hash_mask = CPU_SCACHE_HASH_MASK (current_cpu);
 
 EOF
 
+if [ x$parallel = xyes ] ; then
+  cat << EOF
+  PAREXEC pbufs[MAX_PARALLEL_INSNS];
+  PAREXEC *par_exec;
+
+EOF
+fi
+
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
-${SHELL} $file init
+${SHELL} $infile init
 
 if [ x$parallel = xyes ] ; then
-cat << EOF
+  cat << EOF
 
 #if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
   {
@@ -451,17 +479,17 @@ cat << EOF
 #endif
 
 EOF
-fi
+fi # parallel = yes
 
-cat <<EOF
+cat << EOF
 
-#if defined (WITH_SEM_SWITCH_FAST) && defined (__GNUC__)
+#if WITH_SEM_SWITCH_FAST && defined (__GNUC__)
   {
     if (! CPU_IDESC_SEM_INIT_P (current_cpu))
       {
 /* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
 #define DEFINE_LABELS
-#include "sem-switch.c"
+#include "$switch"
        CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
       }
   }
@@ -469,107 +497,474 @@ cat <<EOF
 
   do
     {
-      {
-       unsigned int hash;
-       SCACHE *sc;
-       PCADDR pc = PC;
+      PCADDR new_pc;
+      SCACHE *sc;
 
-       /* First step: look up current insn in hash table.  */
-       hash = SCACHE_HASH_PC (sd, pc);
-       sc = CPU_SCACHE_CACHE (current_cpu) + hash;
+      sc = ${cpu}_scache_lookup (current_cpu, scache, hash_mask, FAST_P);
 
-       /* If the entry isn't the one we want (cache miss),
-          fetch and decode the instruction.  */
-       if (sc->argbuf.addr != pc)
-         {
-           insn_t insn;
+/* begin fast-exec-scache */
+EOF
+
+${SHELL} $infile fast-exec-scache
+
+cat << EOF
+/* end fast-exec-scache */
+
+      CPU (h_pc) = new_pc;
+
+      ++ CPU_INSN_COUNT (current_cpu);
+    }
+  while (0 /*CPU_RUNNING_P (current_cpu)*/);
+}
+
+#undef FAST_P
 
-/* begin fast-extract-scache */
 EOF
 
-${SHELL} $file fast-extract-scache
+fi # -fast
 
-cat <<EOF
-/* end fast-extract-scache */
-         }
+fi # -scache
 
-/* begin fast-exec-scache */
+##########################################################################
+
+# Compilation engine: lookup insn in scache, extract a pbb
+# (pseudo-basic-block) if missing, then execute the pbb.
+# A "pbb" is a sequence of insns up to the next cti insn or until
+# some prespecified maximum.
+# CTI: control transfer instruction.
+
+if [ x$pbb = xyes ] ; then
+
+    cat << EOF
+
+/* Record address of cti terminating a pbb.  */
+#define SET_CTI_VPC(sc) do { cti_sc = (sc); } while (0)
+/* Record number of [real] insns in pbb.  */
+#define SET_INSN_COUNT(n) do { insn_count = (n); } while (0)
+
+/* Fetch and extract a pseudo-basic-block.
+   FAST_P is non-zero if no tracing/profiling/etc. is wanted.  */
+
+INLINE SEM_PC
+${cpu}_pbb_begin (SIM_CPU *current_cpu, int FAST_P)
+{
+  SEM_PC new_vpc;
+  PCADDR pc;
+  SCACHE *sc;
+  int max_insns = CPU_SCACHE_MAX_CHAIN_LENGTH (current_cpu);
+
+  pc = GET_H_PC ();
+
+  new_vpc = scache_lookup_or_alloc (current_cpu, pc, max_insns, &sc);
+  if (! new_vpc)
+    {
+      int insn_count = 0;
+      SCACHE *orig_sc = sc;
+      SCACHE *cti_sc = NULL;
+      int slice_insns = CPU_MAX_SLICE_INSNS (current_cpu);
+
+      /* First figure out how many instructions to compile.
+        MAX_INSNS is the size of the allocated buffer, which includes space
+        for before/after handlers if they're being used.
+        SLICE_INSNS is the maxinum number of real insns that can be
+        executed.  Zero means "as many as we want".  */
+      /* ??? max_insns is serving two incompatible roles.
+        1) Number of slots available in scache buffer.
+        2) Number of real insns to execute.
+        They're incompatible because there are virtual insns emitted too
+        (chain,cti-chain,before,after handlers).  */
+
+      if (slice_insns == 1)
+       {
+         /* No need to worry about extra slots required for virtual insns
+            and parallel exec support because MAX_CHAIN_LENGTH is
+            guaranteed to be big enough to execute at least 1 insn!  */
+         max_insns = 1;
+       }
+      else
+       {
+         /* Allow enough slop so that while compiling insns, if max_insns > 0
+            then there's guaranteed to be enough space to emit one real insn.
+            MAX_CHAIN_LENGTH is typically much longer than
+            the normal number of insns between cti's anyway.  */
+         max_insns -= (1 /* one for the trailing chain insn */
+                       + (FAST_P
+                          ? 0
+                          : (1 + MAX_PARALLEL_INSNS) /* before+after */)
+                       + (MAX_PARALLEL_INSNS > 1
+                          ? (MAX_PARALLEL_INSNS * 2)
+                          : 0));
+
+         /* Account for before/after handlers.  */
+         if (! FAST_P)
+           slice_insns *= 3;
+
+         if (slice_insns > 0
+             && slice_insns < max_insns)
+           max_insns = slice_insns;
+       }
+
+      new_vpc = sc;
+
+      /* SC,PC must be updated to point passed the last entry used.
+        SET_CTI_VPC must be called if pbb is terminated by a cti.
+        SET_INSN_COUNT must be called to record number of real insns in
+        pbb [could be computed by us of course, extra cpu but perhaps
+        negligible enough].  */
+
+/* begin extract-pbb */
 EOF
 
-${SHELL} $file fast-exec-scache
+${SHELL} $infile extract-pbb
 
-cat <<EOF
-/* end fast-exec-scache */
+cat << EOF
+/* end extract-pbb */
 
+      /* The last one is a pseudo-insn to link to the next chain.
+        It is also used to record the insn count for this chain.  */
+      {
+       const IDESC *id;
+
+       /* Was pbb terminated by a cti?  */
+       if (cti_sc)
+         {
+           id = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_CTI_CHAIN];
+         }
+       else
+         {
+           id = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_CHAIN];
+         }
+       SEM_SET_CODE (&sc->argbuf, id, FAST_P);
+       sc->argbuf.idesc = id;
+       sc->argbuf.addr = pc;
+       sc->argbuf.fields.chain.insn_count = insn_count;
+       sc->argbuf.fields.chain.next = 0;
+       ++sc;
       }
 
-      ++insn_count;
+      /* Update the pointer to the next free entry.  */
+      CPU_SCACHE_NEXT_FREE (current_cpu) = sc;
+      /* Record length of chain if profiling.
+        This includes virtual insns since they count against
+        max_insns too.  */
+      if (! FAST_P)
+       PROFILE_COUNT_SCACHE_CHAIN_LENGTH (current_cpu, sc - orig_sc);
     }
-  while (keep_running);
-#undef FAST_P
+
+  return new_vpc;
 }
 
-EOF
+/* Chain to the next block from a non-cti terminated previous block.  */
 
-##########################################################################
+INLINE SEM_PC
+${cpu}_pbb_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg)
+{
+  ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+
+  PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg);
+
+  SET_H_PC (abuf->addr);
+
+  /* If not running forever, exit back to main loop.  */
+  if (CPU_MAX_SLICE_INSNS (current_cpu) != 0)
+    CPU_RUNNING_P (current_cpu) = 0;
+
+  /* If chained to next block, go straight to it.  */
+  if (abuf->fields.chain.next)
+    return abuf->fields.chain.next;
+  /* See if next block has already been compiled.  */
+  abuf->fields.chain.next = scache_lookup (current_cpu, abuf->addr);
+  if (abuf->fields.chain.next)
+    return abuf->fields.chain.next;
+  /* Nope, so next insn is a virtual insn to invoke the compiler
+     (begin a pbb).  */
+  return CPU_SCACHE_PBB_BEGIN (current_cpu);
+}
 
-else # ! WITH_SCACHE
-       cat <<EOF
+/* Chain to the next block from a cti terminated previous block.
+   NEW_VPC_PTR is one of SEM_BRANCH_UNTAKEN, SEM_BRANCH_UNCACHEABLE, or
+   a pointer to a location containing the SEM_PC of the branch's address.
+   NEW_PC is the target's branch address, and is only valid if
+   NEW_VPC_PTR != SEM_BRANCH_UNTAKEN.  */
 
-static void
-engine_resume_fast (SIM_DESC sd)
+INLINE SEM_PC
+${cpu}_pbb_cti_chain (SIM_CPU *current_cpu, SEM_ARG sem_arg,
+                    SEM_PC *new_vpc_ptr, PCADDR new_pc)
 {
-#define FAST_P 1
-  SIM_DESC current_state = sd;
-  sim_cpu *current_cpu = STATE_CPU (sd, 0);
-  SCACHE cache[MAX_LIW_INSNS];
-  SCACHE *sc = &cache[0];
+  ARGBUF *abuf;
+
+  PBB_UPDATE_INSN_COUNT (current_cpu, sem_arg);
+
+  /* If not running forever, exit back to main loop.  */
+  if (CPU_MAX_SLICE_INSNS (current_cpu) != 0)
+    CPU_RUNNING_P (current_cpu) = 0;
+
+  /* Restart compiler if we branched to an uncacheable address
+     (e.g. "j reg").  */
+  if (new_vpc_ptr == SEM_BRANCH_UNCACHEABLE)
+    {
+      SET_H_PC (new_pc);
+      return CPU_SCACHE_PBB_BEGIN (current_cpu);
+    }
+
+  /* If branch wasn't taken, update the pc and set BR_ADDR_PTR to our
+     next chain ptr.  */
+  if (new_vpc_ptr == SEM_BRANCH_UNTAKEN)
+    {
+      abuf = SEM_ARGBUF (sem_arg);
+      SET_H_PC (abuf->addr);
+      new_vpc_ptr = &abuf->fields.chain.next;
+    }
+  else
+    {
+      SET_H_PC (new_pc);
+    }
+
+  /* If chained to next block, go straight to it.  */
+  if (*new_vpc_ptr)
+    return *new_vpc_ptr;
+  /* See if next block has already been compiled.  */
+  *new_vpc_ptr = scache_lookup (current_cpu, GET_H_PC ());
+  if (*new_vpc_ptr)
+    return *new_vpc_ptr;
+  /* Nope, so next insn is a virtual insn to invoke the compiler
+     (begin a pbb).  */
+  return CPU_SCACHE_PBB_BEGIN (current_cpu);
+}
+
+/* x-before handler.
+   This is called before each insn.  */
+
+void
+${cpu}_pbb_before (SIM_CPU *current_cpu, SCACHE *sc)
+{
+  SEM_ARG sem_arg = sc;
+  const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+  int first_p = abuf->fields.before.first_p;
+  const ARGBUF *cur_abuf = SEM_ARGBUF (sc + 1);
+  const IDESC *cur_idesc = cur_abuf->idesc;
+  PCADDR pc = cur_abuf->addr;
+
+  PROFILE_COUNT_INSN (current_cpu, pc, cur_idesc->num);
+
+  /* If this isn't the first insn, finish up the previous one.  */
+
+  if (! first_p)
+    {
+      if (PROFILE_MODEL_P (current_cpu))
+       {
+         const SEM_ARG prev_sem_arg = sc - 1;
+         const ARGBUF *prev_abuf = SEM_ARGBUF (prev_sem_arg);
+         const IDESC *prev_idesc = prev_abuf->idesc;
+         int cycles;
+
+         cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
+         ${cpu}_model_insn_after (current_cpu, 0 /*last_p*/, cycles);
+       }
+
+      TRACE_INSN_FINI (current_cpu, 0 /*last_p*/);
+    }
+
+  /* FIXME: Later make cover macros: PROFILE_INSN_{INIT,FINI}.  */
+  if (PROFILE_MODEL_P (current_cpu))
+    ${cpu}_model_insn_before (current_cpu, first_p);
+
+  TRACE_INSN_INIT (current_cpu, first_p);
+  TRACE_INSN (current_cpu, cur_idesc->opcode, cur_abuf, cur_abuf->addr);
+}
+
+/* x-after handler.
+   This is called after a serial insn or at the end of a group of parallel
+   insns.  */
+
+void
+${cpu}_pbb_after (SIM_CPU *current_cpu, SCACHE *sc)
+{
+  SEM_ARG sem_arg = sc;
+  const ARGBUF *abuf = SEM_ARGBUF (sem_arg);
+
+  if (PROFILE_MODEL_P (current_cpu))
+    {
+      const SEM_ARG prev_sem_arg = sc - 1;
+      const ARGBUF *prev_abuf = SEM_ARGBUF (prev_sem_arg);
+      const IDESC *prev_idesc = prev_abuf->idesc;
+      int cycles;
+
+      cycles = (*prev_idesc->timing->model_fn) (current_cpu, prev_sem_arg);
+      ${cpu}_model_insn_after (current_cpu, 1 /*last_p*/, cycles);
+    }
+  TRACE_INSN_FINI (current_cpu, 1 /*last_p*/);
+}
+
+#define FAST_P 0
+
+void
+${cpu}_engine_run_full (SIM_CPU *current_cpu)
+{
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+  /* virtual program counter */
+  SEM_PC vpc;
+#if WITH_SEM_SWITCH_FULL
+  /* For communication between cti's and cti-chain.  */
+  PCADDR pbb_br_npc;
+  SEM_PC *pbb_br_npc_ptr;
+#endif
+
+EOF
+
+if [ x$parallel = xyes ] ; then
+  cat << EOF
+  PAREXEC pbufs[MAX_PARALLEL_INSNS];
+  PAREXEC *par_exec = &pbufs[0];
 
 EOF
+fi
 
 # Any initialization code before looping starts.
 # Note that this code may declare some locals.
-${SHELL} $file init
+${SHELL} $infile init
 
-if [ x$parallel = xyes ] ; then
 cat << EOF
 
-#if defined (HAVE_PARALLEL_EXEC) && defined (__GNUC__)
-  {
-    if (! CPU_IDESC_READ_INIT_P (current_cpu))
-      {
-/* ??? Later maybe paste read.c in when building mainloop.c.  */
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+      /* ??? 'twould be nice to move this up a level and only call it once.
+        On the other hand, in the "let's go fast" case the test is only done
+        once per pbb (since we only return to the main loop at the end of
+        a pbb).  And in the "let's run until we're done" case we don't return
+        until the program exits.  */
+
+#if WITH_SEM_SWITCH_FULL && defined (__GNUC__)
+/* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
 #define DEFINE_LABELS
-#include "readx.c"
-       CPU_IDESC_READ_INIT_P (current_cpu) = 1;
-      }
-  }
+#include "$switch"
+#endif
+
+      /* Initialize the "begin (compile) a pbb" virtual insn.  */
+      vpc = CPU_SCACHE_PBB_BEGIN (current_cpu);
+      SEM_SET_FULL_CODE (SEM_ARGBUF (vpc),
+                        & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN]);
+      vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN];
+
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
+
+  CPU_RUNNING_P (current_cpu) = 1;
+  /* ??? In the case where we're returning to the main loop after every
+     pbb we don't want to call pbb_begin each time (which hashes on the pc
+     and does a table lookup).  A way to speed this up is to save vpc
+     between calls.  */
+  vpc = ${cpu}_pbb_begin (current_cpu, FAST_P);
+
+  do
+    {
+/* begin full-exec-pbb */
+EOF
+
+${SHELL} $infile full-exec-pbb
+
+cat << EOF
+/* end full-exec-pbb */
+    }
+  while (CPU_RUNNING_P (current_cpu));
+}
+
+#undef FAST_P
+
+EOF
+
+####################################
+
+# Compile engine: fast version.
+
+if [ x$fast = xyes ] ; then
+
+    cat << EOF
+
+#define FAST_P 1
+
+void
+${cpu}_engine_run_fast (SIM_CPU *current_cpu)
+{
+  SIM_DESC current_state = CPU_STATE (current_cpu);
+  SCACHE *scache = CPU_SCACHE_CACHE (current_cpu);
+  /* virtual program counter */
+  SEM_PC vpc;
+#if WITH_SEM_SWITCH_FAST
+  /* For communication between cti's and cti-chain.  */
+  PCADDR pbb_br_npc;
+  SEM_PC *pbb_br_npc_ptr;
 #endif
 
+EOF
+
+if [ x$parallel = xyes ] ; then
+  cat << EOF
+  PAREXEC pbufs[MAX_PARALLEL_INSNS];
+  PAREXEC *par_exec = &pbufs[0];
+
 EOF
 fi
 
-cat <<EOF
+# Any initialization code before looping starts.
+# Note that this code may declare some locals.
+${SHELL} $infile init
+
+cat << EOF
+
+  if (! CPU_IDESC_SEM_INIT_P (current_cpu))
+    {
+      /* ??? 'twould be nice to move this up a level and only call it once.
+        On the other hand, in the "let's go fast" case the test is only done
+        once per pbb (since we only return to the main loop at the end of
+        a pbb).  And in the "let's run until we're done" case we don't return
+        until the program exits.  */
+
+#if WITH_SEM_SWITCH_FAST && defined (__GNUC__)
+/* ??? Later maybe paste sem-switch.c in when building mainloop.c.  */
+#define DEFINE_LABELS
+#include "$switch"
+#endif
+
+      /* Initialize the "begin (compile) a pbb" virtual insn.  */
+      vpc = CPU_SCACHE_PBB_BEGIN (current_cpu);
+      SEM_SET_FAST_CODE (SEM_ARGBUF (vpc),
+                        & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN]);
+      vpc->argbuf.idesc = & CPU_IDESC (current_cpu) [${CPU}_INSN_X_BEGIN];
+
+      CPU_IDESC_SEM_INIT_P (current_cpu) = 1;
+    }
+
+  CPU_RUNNING_P (current_cpu) = 1;
+  /* ??? In the case where we're returning to the main loop after every
+     pbb we don't want to call pbb_begin each time (which hashes on the pc
+     and does a table lookup).  A way to speed this up is to save vpc
+     between calls.  */
+  vpc = ${cpu}_pbb_begin (current_cpu, FAST_P);
 
   do
     {
-/* begin fast-{extract,exec}-noscache */
+/* begin fast-exec-pbb */
 EOF
 
-${SHELL} $file fast-extract-noscache
-echo ""
-${SHELL} $file fast-exec-noscache
-
-cat <<EOF
-/* end fast-{extract,exec}-noscache */
+${SHELL} $infile fast-exec-pbb
 
-      ++insn_count;
+cat << EOF
+/* end fast-exec-pbb */
     }
-  while (keep_running);
-#undef FAST_P
+  while (CPU_RUNNING_P (current_cpu));
 }
 
-EOF
+#undef FAST_P
 
-    fi # ! WITH_SCACHE
+EOF
 fi # -fast
+
+fi # -pbb
+
+sed -e "s/@cpu@/$cpu/g" -e "s/@CPU@/$CPU/g" < tmp-mloop.cin > mloop.cin
+rc=$?
+rm -f tmp-mloop.cin
+
+exit $rc