diff --git a/sim/configure.tgt b/sim/configure.tgt
index d112e72..026b81f 100644
--- a/sim/configure.tgt
+++ b/sim/configure.tgt
@@ -111,6 +111,10 @@ case "${target}" in
    powerpc*-*-*)
        SIM_ARCH(ppc)
        ;;
+   ft32-*-*)
+       SIM_ARCH(ft32)
+       sim_testsuite=yes
+       ;;
    v850*-*-*)
        SIM_ARCH(v850)
        sim_igen=yes
diff --git a/sim/ft32/Makefile.in b/sim/ft32/Makefile.in
new file mode 100644
index 0000000..f16039a
--- /dev/null
+++ b/sim/ft32/Makefile.in
@@ -0,0 +1,48 @@
+#    Makefile template for Configure for the ft32 sim library.
+#    Copyright (C) 2008-2015 Free Software Foundation, Inc.
+#    Written by FTDI
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+## COMMON_PRE_CONFIG_FRAG
+
+
+SIM_OBJS = \
+	$(SIM_NEW_COMMON_OBJS) \
+	interp.o	\
+	sim-bits.o	\
+	sim-config.o	\
+	sim-core.o	\
+	sim-cpu.o	\
+	sim-endian.o	\
+	sim-engine.o	\
+	sim-events.o	\
+	sim-fpu.o	\
+	sim-hload.o	\
+	sim-io.o	\
+	sim-load.o	\
+	sim-memopt.o	\
+	sim-module.o	\
+	sim-options.o	\
+	sim-profile.o	\
+	sim-reason.o	\
+	sim-reg.o	\
+	sim-resume.o	\
+	sim-signal.o	\
+	sim-stop.o	\
+	sim-trace.o	\
+	sim-utils.o	\
+	$(SIM_EXTRA_OBJS)
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/ft32/config.in b/sim/ft32/config.in
new file mode 100644
index 0000000..6003e58
--- /dev/null
+++ b/sim/ft32/config.in
@@ -0,0 +1,163 @@
+/* config.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#undef ENABLE_NLS
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the <fpu_control.h> header file. */
+#undef HAVE_FPU_CONTROL_H
+
+/* Define to 1 if you have the `getrusage' function. */
+#undef HAVE_GETRUSAGE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the `time' function. */
+#undef HAVE_TIME
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <windows.h> header file. */
+#undef HAVE_WINDOWS_H
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#undef HAVE_ZLIB_H
+
+/* Define to 1 if you have the `__setfpucw' function. */
+#undef HAVE___SETFPUCW
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Name of this package. */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Additional package description */
+#undef PKGVERSION
+
+/* Bug reporting address */
+#undef REPORT_BUGS_TO
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+#include "tconfig.h"
diff --git a/sim/ft32/configure.ac b/sim/ft32/configure.ac
new file mode 100644
index 0000000..2107f74
--- /dev/null
+++ b/sim/ft32/configure.ac
@@ -0,0 +1,15 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.64)dnl
+AC_INIT(Makefile.in)
+sinclude(../common/acinclude.m4)
+
+SIM_AC_COMMON
+
+SIM_AC_OPTION_ENDIAN(LITTLE_ENDIAN)
+SIM_AC_OPTION_ALIGNMENT(STRICT_ALIGNMENT)
+SIM_AC_OPTION_HOSTENDIAN
+SIM_AC_OPTION_ENVIRONMENT
+SIM_AC_OPTION_INLINE
+SIM_AC_OPTION_WARNINGS
+
+SIM_AC_OUTPUT
diff --git a/sim/ft32/ft32-sim.h b/sim/ft32/ft32-sim.h
new file mode 100644
index 0000000..1d664ba
--- /dev/null
+++ b/sim/ft32/ft32-sim.h
@@ -0,0 +1,43 @@
+/* Simulator for the FT32 processor
+
+   Copyright (C) 2008-2015 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+   This file is part of 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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _FT32_SIM_H_
+#define _FT32_SIM_H_
+
+#include <stdint.h>
+
+#include "gdb/sim-ft32.h"
+
+#define FT32_HARD_FP 29
+#define FT32_HARD_CC 30
+#define FT32_HARD_SP 31
+
+struct ft32_cpu_state {
+  uint32_t regs[32];
+  uint32_t pc;
+  uint64_t num_i;
+  uint64_t cycles;
+  uint64_t next_tick_cycle;
+  int pm_unlock;
+  uint32_t pm_addr;
+  int exception;
+};
+
+#endif  /* _FT32_SIM_H_ */
diff --git a/sim/ft32/interp.c b/sim/ft32/interp.c
new file mode 100644
index 0000000..0c88e69
--- /dev/null
+++ b/sim/ft32/interp.c
@@ -0,0 +1,913 @@
+/* Simulator for the FT32 processor
+
+   Copyright (C) 2008-2015 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+   This file is part of 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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "bfd.h"
+#include "gdb/callback.h"
+#include "libiberty.h"
+#include "gdb/remote-sim.h"
+
+#include "sim-main.h"
+#include "sim-options.h"
+
+#include "opcode/ft32.h"
+
+/*
+ * FT32 is a Harvard architecture: RAM and code occupy
+ * different address spaces.
+ *
+ * sim and gdb model FT32 memory by adding 0x800000 to RAM
+ * addresses. This means that sim/gdb can treat all addresses
+ * similarly.
+ * 
+ * The address space looks like:
+ *
+ *    00000   start of code memory
+ *    3ffff   end of code memory
+ *   800000   start of RAM
+ *   80ffff   end of RAM
+ */
+
+#define RAM_BIAS  0x800000  /* Bias added to RAM addresses.  */
+
+static unsigned long
+ft32_extract_unsigned_integer (unsigned char *addr, int len)
+{
+  unsigned long retval;
+  unsigned char *p;
+  unsigned char *startaddr = (unsigned char *) addr;
+  unsigned char *endaddr = startaddr + len;
+
+  /* Start at the most significant end of the integer, and work towards
+     the least significant.  */
+  retval = 0;
+
+  for (p = endaddr; p > startaddr;)
+    retval = (retval << 8) | * -- p;
+
+  return retval;
+}
+
+static void
+ft32_store_unsigned_integer (unsigned char *addr, int len, unsigned long val)
+{
+  unsigned char *p;
+  unsigned char *startaddr = (unsigned char *)addr;
+  unsigned char *endaddr = startaddr + len;
+
+  for (p = startaddr; p < endaddr; p++)
+    {
+      *p = val & 0xff;
+      val >>= 8;
+    }
+}
+
+/*
+ * Align EA according to its size DW.
+ * The FT32 ignores the low bit of a 16-bit addresss,
+ * and the low two bits of a 32-bit address.
+ */
+static uint32_t ft32_align (uint32_t dw, uint32_t ea)
+{
+  switch (dw)
+    {
+    case 1:
+      ea &= ~1;
+      break;
+    case 2:
+      ea &= ~3;
+      break;
+    default:
+      break;
+    }
+  return ea;
+}
+
+/* Read an item from memory address EA, sized DW.  */
+static uint32_t
+ft32_read_item (SIM_DESC sd, int dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint8_t byte[4];
+  uint32_t r;
+
+  ea = ft32_align (dw, ea);
+
+  switch (dw) {
+    case 0:
+      return sim_core_read_aligned_1 (cpu, cia, read_map, ea);
+    case 1:
+      return sim_core_read_aligned_2 (cpu, cia, read_map, ea);
+    case 2:
+      return sim_core_read_aligned_4 (cpu, cia, read_map, ea);
+    default:
+      abort ();
+  }
+}
+
+/* Write item V to memory address EA, sized DW.  */
+static void
+ft32_write_item (SIM_DESC sd, int dw, uint32_t ea, uint32_t v)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint8_t byte[4];
+
+  ea = ft32_align (dw, ea);
+
+  switch (dw) {
+    case 0:
+      sim_core_write_aligned_1 (cpu, cia, write_map, ea, v);
+      break;
+    case 1:
+      sim_core_write_aligned_2 (cpu, cia, write_map, ea, v);
+      break;
+    case 2:
+      sim_core_write_aligned_4 (cpu, cia, write_map, ea, v);
+      break;
+    default:
+      abort();
+  }
+}
+
+#define ILLEGAL() \
+  sim_engine_halt (sd, cpu, NULL, insnpc, sim_signalled, SIM_SIGILL)
+
+static uint32_t cpu_mem_read (SIM_DESC sd, uint32_t dw, uint32_t ea)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  uint32_t insnpc = cpu->state.pc;
+  uint32_t r;
+  uint8_t byte[4];
+
+  ea &= 0x1ffff;
+  if (ea & ~0xffff)
+    {
+      /* Simulate some IO devices */
+      switch (ea)
+	{
+	case 0x1fff4:
+	  /* Read the simulator cycle timer.  */
+	  return cpu->state.cycles / 100;
+	default:
+	  sim_io_eprintf (sd, "Illegal IO read address %08x, pc %#x\n",
+			  ea, insnpc);
+	  ILLEGAL ();
+	}
+    }
+  return ft32_read_item (sd, dw, RAM_BIAS + ea);
+}
+
+static void cpu_mem_write (SIM_DESC sd, uint32_t dw, uint32_t ea, uint32_t d)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  ea &= 0x1ffff;
+  if (ea & 0x10000)
+    {
+      /* Simulate some IO devices */
+      switch (ea)
+	{
+	case 0x10000:
+	  /* Console output */
+	  putchar (d & 0xff);
+	  break;
+	case 0x1fc80:
+	  /* Unlock the PM write port */
+	  cpu->state.pm_unlock = (d == 0x1337f7d1);
+	  break;
+	case 0x1fc84:
+	  /* Set the PM write address register */
+	  cpu->state.pm_addr = d;
+	  break;
+	case 0x1fc88:
+	  /* Write to PM */
+	  ft32_write_item (sd, dw, cpu->state.pm_addr, d);
+	  break;
+	case 0x1fffc:
+	  /* Normal exit.  */
+	  sim_engine_halt (sd, cpu, NULL, cpu->state.pc, sim_exited, cpu->state.regs[0]);
+	  break;
+	case 0x1fff8:
+	  sim_io_printf (sd, "Debug write %08x\n", d);
+	  break;
+	default:
+	  sim_io_eprintf (sd, "Unknown IO write %08x to to %08x\n", d, ea);
+	}
+    }
+  else
+    ft32_write_item (sd, dw, RAM_BIAS + ea, d);
+}
+
+#define GET_BYTE(ea)    cpu_mem_read (sd, 0, (ea))
+#define PUT_BYTE(ea, d) cpu_mem_write (sd, 0, (ea), (d))
+
+/* LSBS (n) is a mask of the least significant N bits.  */
+#define LSBS(n) ((1U << (n)) - 1)
+
+static void ft32_push (SIM_DESC sd, uint32_t v)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  cpu->state.regs[FT32_HARD_SP] -= 4;
+  cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+  cpu_mem_write (sd, 2, cpu->state.regs[FT32_HARD_SP], v);
+}
+
+static uint32_t ft32_pop (SIM_DESC sd)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  uint32_t r = cpu_mem_read (sd, 2, cpu->state.regs[FT32_HARD_SP]);
+  cpu->state.regs[FT32_HARD_SP] += 4;
+  cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+  return r;
+}
+
+/* Extract the low SIZ bits of N as an unsigned number.  */
+static int nunsigned (int siz, int n)
+{
+  return n & LSBS (siz);
+}
+
+/* Extract the low SIZ bits of N as a signed number.  */
+static int nsigned (int siz, int n)
+{
+  int shift = (sizeof (int) * 8) - siz;
+  return (n << shift) >> shift;
+}
+
+/* Signed division N / D, matching hw behavior for (MIN_INT, -1).  */
+static uint32_t ft32sdiv (uint32_t n, uint32_t d)
+{
+  if (n == 0x80000000UL && d == 0xffffffffUL)
+    return 0x80000000UL;
+  else
+    return (uint32_t)((int)n / (int)d);
+}
+
+/* Signed modulus N % D, matching hw behavior for (MIN_INT, -1).  */
+static uint32_t ft32smod (uint32_t n, uint32_t d)
+{
+  if (n == 0x80000000UL && d == 0xffffffffUL)
+    return 0;
+  else
+    return (uint32_t)((int)n % (int)d);
+}
+
+/* Circular rotate right N by B bits.  */
+static uint32_t ror (uint32_t n, uint32_t b)
+{
+  b &= 31;
+  return (n >> b) | (n << (32 - b));
+}
+
+/* Implement the BINS machine instruction.
+   See FT32 Programmer's Reference for details.  */
+static uint32_t bins (uint32_t d, uint32_t f, uint32_t len, uint32_t pos)
+{
+  uint32_t bitmask = LSBS (len) << pos;
+  return (d & ~bitmask) | ((f << pos) & bitmask);
+}
+
+/* Implement the FLIP machine instruction.
+   See FT32 Programmer's Reference for details.  */
+static uint32_t flip (uint32_t x, uint32_t b)
+{
+  if (b & 1)
+    x = (x & 0x55555555) <<  1 | (x & 0xAAAAAAAA) >>  1;
+  if (b & 2)
+    x = (x & 0x33333333) <<  2 | (x & 0xCCCCCCCC) >>  2;
+  if (b & 4)
+    x = (x & 0x0F0F0F0F) <<  4 | (x & 0xF0F0F0F0) >>  4;
+  if (b & 8)
+    x = (x & 0x00FF00FF) <<  8 | (x & 0xFF00FF00) >>  8;
+  if (b & 16)
+    x = (x & 0x0000FFFF) << 16 | (x & 0xFFFF0000) >> 16;
+  return x;
+}
+
+static void
+step_once (SIM_DESC sd)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+  address_word cia = CIA_GET (cpu);
+  uint32_t inst;
+  uint32_t dw;
+  uint32_t cb;
+  uint32_t r_d;
+  uint32_t cr;
+  uint32_t cv;
+  uint32_t bt;
+  uint32_t r_1;
+  uint32_t rimm;
+  uint32_t r_2;
+  uint32_t k20;
+  uint32_t pa;
+  uint32_t aa;
+  uint32_t k16;
+  uint32_t k8;
+  uint32_t al;
+  uint32_t r_1v;
+  uint32_t rimmv;
+  uint32_t bit_pos;
+  uint32_t bit_len;
+  uint32_t upper;
+  uint32_t insnpc;
+
+  if (cpu->state.cycles >= cpu->state.next_tick_cycle)
+    {
+      cpu->state.next_tick_cycle += 100000;
+      ft32_push (sd, cpu->state.pc);
+      cpu->state.pc = 12;  /* interrupt 1.  */
+    }
+  inst = ft32_read_item (sd, 2, cpu->state.pc);
+  cpu->state.cycles += 1;
+
+  /* Handle "call 8" (which is FT32's "break" equivalent) here.  */
+  if (inst == 0x00340002)
+    {
+      sim_engine_halt (sd, cpu, NULL,
+		       cpu->state.pc,
+		       sim_stopped, SIM_SIGTRAP);
+      goto escape;
+    }
+
+  dw   =              (inst >> FT32_FLD_DW_BIT) & LSBS (FT32_FLD_DW_SIZ);
+  cb   =              (inst >> FT32_FLD_CB_BIT) & LSBS (FT32_FLD_CB_SIZ);
+  r_d  =              (inst >> FT32_FLD_R_D_BIT) & LSBS (FT32_FLD_R_D_SIZ);
+  cr   =              (inst >> FT32_FLD_CR_BIT) & LSBS (FT32_FLD_CR_SIZ);
+  cv   =              (inst >> FT32_FLD_CV_BIT) & LSBS (FT32_FLD_CV_SIZ);
+  bt   =              (inst >> FT32_FLD_BT_BIT) & LSBS (FT32_FLD_BT_SIZ);
+  r_1  =              (inst >> FT32_FLD_R_1_BIT) & LSBS (FT32_FLD_R_1_SIZ);
+  rimm =              (inst >> FT32_FLD_RIMM_BIT) & LSBS (FT32_FLD_RIMM_SIZ);
+  r_2  =              (inst >> FT32_FLD_R_2_BIT) & LSBS (FT32_FLD_R_2_SIZ);
+  k20  = nsigned (20, (inst >> FT32_FLD_K20_BIT) & LSBS (FT32_FLD_K20_SIZ));
+  pa   =              (inst >> FT32_FLD_PA_BIT) & LSBS (FT32_FLD_PA_SIZ);
+  aa   =              (inst >> FT32_FLD_AA_BIT) & LSBS (FT32_FLD_AA_SIZ);
+  k16  =              (inst >> FT32_FLD_K16_BIT) & LSBS (FT32_FLD_K16_SIZ);
+  k8   = nsigned (8,  (inst >> FT32_FLD_K8_BIT) & LSBS (FT32_FLD_K8_SIZ));
+  al   =              (inst >> FT32_FLD_AL_BIT) & LSBS (FT32_FLD_AL_SIZ);
+
+  r_1v = cpu->state.regs[r_1];
+  rimmv = (rimm & 0x400) ? nsigned (10, rimm) : cpu->state.regs[rimm & 0x1f];
+
+  bit_pos = rimmv & 31;
+  bit_len = 0xf & (rimmv >> 5);
+  if (bit_len == 0)
+    bit_len = 16;
+
+  upper = (inst >> 27);
+
+  insnpc = cpu->state.pc;
+  cpu->state.pc += 4;
+  switch (upper)
+    {
+    case FT32_PAT_TOC:
+    case FT32_PAT_TOCI:
+      {
+	int take = (cr == 3) || ((1 & (cpu->state.regs[28 + cr] >> cb)) == cv);
+	if (take)
+	  {
+	    cpu->state.cycles += 1;
+	    if (bt)
+	      ft32_push (sd, cpu->state.pc); /* this is a call.  */
+	    if (upper == FT32_PAT_TOC)
+	      cpu->state.pc = pa << 2;
+	    else
+	      cpu->state.pc = cpu->state.regs[r_2];
+	    if (cpu->state.pc == 0x8)
+		goto escape;
+	  }
+      }
+      break;
+
+    case FT32_PAT_ALUOP:
+    case FT32_PAT_CMPOP:
+      {
+	uint32_t result;
+	switch (al)
+	  {
+	  case 0x0: result = r_1v + rimmv; break;
+	  case 0x1: result = ror (r_1v, rimmv); break;
+	  case 0x2: result = r_1v - rimmv; break;
+	  case 0x3: result = (r_1v << 10) | (1023 & rimmv); break;
+	  case 0x4: result = r_1v & rimmv; break;
+	  case 0x5: result = r_1v | rimmv; break;
+	  case 0x6: result = r_1v ^ rimmv; break;
+	  case 0x7: result = ~(r_1v ^ rimmv); break;
+	  case 0x8: result = r_1v << rimmv; break;
+	  case 0x9: result = r_1v >> rimmv; break;
+	  case 0xa: result = (int32_t)r_1v >> rimmv; break;
+	  case 0xb: result = bins (r_1v, rimmv >> 10, bit_len, bit_pos); break;
+	  case 0xc: result = nsigned (bit_len, r_1v >> bit_pos); break;
+	  case 0xd: result = nunsigned (bit_len, r_1v >> bit_pos); break;
+	  case 0xe: result = flip (r_1v, rimmv); break;
+	  default:
+	    sim_io_eprintf (sd, "Unhandled alu %#x\n", al);
+	    ILLEGAL ();
+	  }
+	if (upper == FT32_PAT_ALUOP)
+	  cpu->state.regs[r_d] = result;
+	else
+	  {
+	    uint32_t dwmask = 0;
+	    int dwsiz = 0;
+	    int zero;
+	    int sign;
+	    int ahi;
+	    int bhi;
+	    int overflow;
+	    int carry;
+	    int bit;
+	    uint64_t ra;
+	    uint64_t rb;
+	    int above;
+	    int greater;
+	    int greatereq;
+
+	    switch (dw)
+	      {
+	      case 0: dwsiz = 7;  dwmask = 0xffU; break;
+	      case 1: dwsiz = 15; dwmask = 0xffffU; break;
+	      case 2: dwsiz = 31; dwmask = 0xffffffffU; break;
+	      }
+
+	    zero = (0 == (result & dwmask));
+	    sign = 1 & (result >> dwsiz);
+	    ahi = 1 & (r_1v >> dwsiz);
+	    bhi = 1 & (rimmv >> dwsiz);
+	    overflow = (sign != ahi) & (ahi == !bhi);
+	    bit = (dwsiz + 1);
+	    ra = r_1v & dwmask;
+	    rb = rimmv & dwmask;
+	    switch (al)
+	      {
+	      case 0x0: carry = 1 & ((ra + rb) >> bit); break;
+	      case 0x2: carry = 1 & ((ra - rb) >> bit); break;
+	      default:  carry = 0; break;
+	      }
+	    above = (!carry & !zero);
+	    greater = (sign == overflow) & !zero;
+	    greatereq = (sign == overflow);
+
+	    cpu->state.regs[r_d] = (
+	      (above << 6) |
+	      (greater << 5) |
+	      (greatereq << 4) |
+	      (sign << 3) |
+	      (overflow << 2) |
+	      (carry << 1) |
+	      (zero << 0));
+	  }
+      }
+      break;
+
+    case FT32_PAT_LDK:
+      cpu->state.regs[r_d] = k20;
+      break;
+
+    case FT32_PAT_LPM:
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, pa << 2);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_LPMI:
+      cpu->state.regs[r_d] = ft32_read_item (sd, dw, cpu->state.regs[r_1] + k8);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_STA:
+      cpu_mem_write (sd, dw, aa, cpu->state.regs[r_d]);
+      break;
+
+    case FT32_PAT_STI:
+      cpu_mem_write (sd, dw, cpu->state.regs[r_d] + k8, cpu->state.regs[r_1]);
+      break;
+
+    case FT32_PAT_LDA:
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, aa);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_LDI:
+      cpu->state.regs[r_d] = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+      cpu->state.cycles += 1;
+      break;
+
+    case FT32_PAT_EXA:
+      {
+	uint32_t tmp;
+	tmp = cpu_mem_read (sd, dw, aa);
+	cpu_mem_write (sd, dw, aa, cpu->state.regs[r_d]);
+	cpu->state.regs[r_d] = tmp;
+	cpu->state.cycles += 1;
+      }
+      break;
+
+    case FT32_PAT_EXI:
+      {
+	uint32_t tmp;
+	tmp = cpu_mem_read (sd, dw, cpu->state.regs[r_1] + k8);
+	cpu_mem_write (sd, dw, cpu->state.regs[r_1] + k8, cpu->state.regs[r_d]);
+	cpu->state.regs[r_d] = tmp;
+	cpu->state.cycles += 1;
+      }
+      break;
+
+    case FT32_PAT_PUSH:
+      ft32_push (sd, r_1v);
+      break;
+
+    case FT32_PAT_LINK:
+      ft32_push (sd, cpu->state.regs[r_d]);
+      cpu->state.regs[r_d] = cpu->state.regs[FT32_HARD_SP];
+      cpu->state.regs[FT32_HARD_SP] -= k16;
+      cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+      break;
+
+    case FT32_PAT_UNLINK:
+      cpu->state.regs[FT32_HARD_SP] = cpu->state.regs[r_d];
+      cpu->state.regs[FT32_HARD_SP] &= 0xffff;
+      cpu->state.regs[r_d] = ft32_pop (sd);
+      break;
+
+    case FT32_PAT_POP:
+      cpu->state.cycles += 1;
+      cpu->state.regs[r_d] = ft32_pop (sd);
+      break;
+
+    case FT32_PAT_RETURN:
+      cpu->state.pc = ft32_pop (sd);
+      break;
+
+    case FT32_PAT_FFUOP:
+      switch (al)
+	{
+	case 0x0:
+	  cpu->state.regs[r_d] = r_1v / rimmv;
+	  break;
+	case 0x1:
+	  cpu->state.regs[r_d] = r_1v % rimmv;
+	  break;
+	case 0x2:
+	  cpu->state.regs[r_d] = ft32sdiv (r_1v, rimmv);
+	  break;
+	case 0x3:
+	  cpu->state.regs[r_d] = ft32smod (r_1v, rimmv);
+	  break;
+
+	case 0x4:
+	  {
+	    /* strcmp instruction.  */
+	    uint32_t a = r_1v;
+	    uint32_t b = rimmv;
+	    uint32_t i = 0;
+	    while ((GET_BYTE (a + i) != 0) &&
+		   (GET_BYTE (a + i) == GET_BYTE (b + i)))
+	      i++;
+	    cpu->state.regs[r_d] = GET_BYTE (a + i) - GET_BYTE (b + i);
+	  }
+	  break;
+
+	case 0x5:
+	  {
+	    /* memcpy instruction.  */
+	    uint32_t src = r_1v;
+	    uint32_t dst = cpu->state.regs[r_d];
+	    uint32_t i;
+	    for (i = 0; i < rimmv; i++)
+	      PUT_BYTE (dst + i, GET_BYTE (src + i));
+	  }
+	  break;
+	case 0x6:
+	  {
+	    /* strlen instruction.  */
+	    uint32_t src = r_1v;
+	    uint32_t i;
+	    for (i = 0; GET_BYTE (src + i) != 0; i++)
+	      ;
+	    cpu->state.regs[r_d] = i;
+	  }
+	  break;
+	case 0x7:
+	  {
+	    /* memset instruction.  */
+	    uint32_t dst = cpu->state.regs[r_d];
+	    uint32_t i;
+	    for (i = 0; i < rimmv; i++)
+	      PUT_BYTE (dst + i, r_1v);
+	  }
+	  break;
+	case 0x8:
+	  cpu->state.regs[r_d] = r_1v * rimmv;
+	  break;
+	case 0x9:
+	  cpu->state.regs[r_d] = ((uint64_t)r_1v * (uint64_t)rimmv) >> 32;
+	  break;
+	case 0xa:
+	  {
+	    /* stpcpy instruction.  */
+	    uint32_t src = r_1v;
+	    uint32_t dst = cpu->state.regs[r_d];
+	    uint32_t i;
+	    for (i = 0; GET_BYTE (src + i) != 0; i++)
+	      PUT_BYTE (dst + i, GET_BYTE (src + i));
+	    PUT_BYTE (dst + i, 0);
+	    cpu->state.regs[r_d] = dst + i;
+	  }
+	  break;
+	case 0xe:
+	  {
+	    /* streamout instruction.  */
+	    uint32_t i;
+	    uint32_t src = cpu->state.regs[r_1];
+	    for (i = 0; i < rimmv; i += (1 << dw))
+	      {
+		cpu_mem_write (sd,
+			       dw,
+			       cpu->state.regs[r_d],
+			       cpu_mem_read (sd, dw, src));
+		src += (1 << dw);
+	      }
+	  }
+	  break;
+	default:
+	  sim_io_eprintf (sd, "Unhandled ffu %#x at %08x\n", al, insnpc);
+	  ILLEGAL ();
+	}
+      break;
+
+    default:
+      sim_io_eprintf (sd, "Unhandled pattern %d at %08x\n", upper, insnpc);
+      ILLEGAL ();
+    }
+  cpu->state.num_i++;
+
+escape:
+  ;
+}
+
+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)
+    {
+      step_once (sd);
+      if (sim_events_tick (sd))
+	sim_events_process (sd);
+    }
+}
+
+int
+sim_write (SIM_DESC sd,
+	   SIM_ADDR addr,
+	   const unsigned char *buffer,
+	   int size)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  return sim_core_write_buffer (sd, cpu, write_map, buffer, addr, size);
+}
+
+int
+sim_read (SIM_DESC sd,
+	  SIM_ADDR addr,
+	  unsigned char *buffer,
+	  int size)
+{
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  return sim_core_read_buffer (sd, cpu, read_map, buffer, addr, size);
+}
+
+static uint32_t *
+ft32_lookup_register (SIM_CPU *cpu, int nr)
+{
+  /* Handle the register number translation here.
+   * Sim registers are 0-31.
+   * Other tools (gcc, gdb) use:
+   * 0 - fp
+   * 1 - sp
+   * 2 - r0
+   * 31 - cc
+   */
+
+  if ((nr < 0) || (nr > 32))
+    {
+      sim_io_eprintf (CPU_STATE (cpu), "unknown register %i\n", nr);
+      abort ();
+    }
+
+  switch (nr)
+    {
+    case FT32_FP_REGNUM:
+      return &cpu->state.regs[FT32_HARD_FP];
+    case FT32_SP_REGNUM:
+      return &cpu->state.regs[FT32_HARD_SP];
+    case FT32_CC_REGNUM:
+      return &cpu->state.regs[FT32_HARD_CC];
+    case FT32_PC_REGNUM:
+      return &cpu->state.pc;
+    default:
+      return &cpu->state.regs[nr - 2];
+    }
+}
+
+static int
+ft32_reg_store (SIM_CPU *cpu,
+		int rn,
+		unsigned char *memory,
+		int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	*ft32_lookup_register (cpu, rn) = ft32_extract_unsigned_integer (memory, 4);
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+static int
+ft32_reg_fetch (SIM_CPU *cpu,
+		int rn,
+		unsigned char *memory,
+		int length)
+{
+  if (0 <= rn && rn <= 32)
+    {
+      if (length == 4)
+	ft32_store_unsigned_integer (memory, 4, *ft32_lookup_register (cpu, rn));
+
+      return 4;
+    }
+  else
+    return 0;
+}
+
+static sim_cia
+ft32_pc_get (SIM_CPU *cpu)
+{
+  return 32;
+}
+
+static void
+ft32_pc_set (SIM_CPU *cpu, sim_cia newpc)
+{
+  cpu->state.pc = newpc;
+}
+
+/* Cover function of sim_state_free to free the cpu buffers as well.  */
+
+static void
+free_state (SIM_DESC sd)
+{
+  if (STATE_MODULES (sd) != NULL)
+    sim_module_uninstall (sd);
+  sim_cpu_free_all (sd);
+  sim_state_free (sd);
+}
+
+SIM_DESC
+sim_open (SIM_OPEN_KIND kind,
+	  host_callback *cb,
+	  struct bfd *abfd,
+	  char **argv)
+{
+  char c;
+  size_t i;
+  SIM_DESC sd = sim_state_alloc (kind, cb);
+
+  /* The cpu data is kept in a separately allocated chunk of memory.  */
+  if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
+    {
+      free_state (sd);
+      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)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Allocate external memory if none specified by user.
+     Use address 4 here in case the user wanted address 0 unmapped.  */
+  if (sim_core_read_buffer (sd, NULL, read_map, &c, 4, 1) == 0)
+    {
+      sim_do_command (sd, "memory region 0x00000000,0x40000");
+      sim_do_command (sd, "memory region 0x800000,0x10000");
+    }
+
+  /* Check for/establish the reference program image.  */
+  if (sim_analyze_program (sd,
+			   (STATE_PROG_ARGV (sd) != NULL
+			    ? *STATE_PROG_ARGV (sd)
+			    : NULL), abfd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Configure/verify the target byte order and other runtime
+     configuration options.  */
+  if (sim_config (sd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  if (sim_post_argv_init (sd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* CPU specific initialization.  */
+  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
+    {
+      SIM_CPU *cpu = STATE_CPU (sd, i);
+
+      CPU_REG_FETCH (cpu) = ft32_reg_fetch;
+      CPU_REG_STORE (cpu) = ft32_reg_store;
+      CPU_PC_FETCH (cpu) = ft32_pc_get;
+      CPU_PC_STORE (cpu) = ft32_pc_set;
+    }
+
+  return sd;
+}
+
+void
+sim_close (SIM_DESC sd, int quitting)
+{
+  sim_module_uninstall (sd);
+}
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd,
+		     struct bfd *abfd,
+		     char **argv,
+		     char **env)
+{
+  uint32_t addr;
+  sim_cpu *cpu = STATE_CPU (sd, 0);
+
+  /* Set the PC.  */
+  if (abfd != NULL)
+    addr = bfd_get_start_address (abfd);
+  else
+    addr = 0;
+
+  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+    {
+      freeargv (STATE_PROG_ARGV (sd));
+      STATE_PROG_ARGV (sd) = dupargv (argv);
+    }
+  cpu->state.regs[FT32_HARD_SP] = addr;
+  cpu->state.num_i = 0;
+  cpu->state.cycles = 0;
+  cpu->state.next_tick_cycle = 100000;
+
+  return SIM_RC_OK;
+}
diff --git a/sim/ft32/sim-main.h b/sim/ft32/sim-main.h
new file mode 100644
index 0000000..369dc52
--- /dev/null
+++ b/sim/ft32/sim-main.h
@@ -0,0 +1,58 @@
+/* Simulator for FTDI FT32 processor.
+
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+   Contributed by FTDI <support@ftdichip.com>
+
+   This file is part of 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 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef SIM_MAIN_H
+#define SIM_MAIN_H
+
+#include "sim-basics.h"
+
+typedef address_word sim_cia;
+
+#include "sim-base.h"
+#include "bfd.h"
+
+typedef struct _sim_cpu SIM_CPU;
+
+#include "ft32-sim.h"
+
+struct _sim_cpu {
+
+  /* The following are internal simulator state variables: */
+#define CIA_GET(CPU) ((CPU)->state.pc + 0)
+#define CIA_SET(CPU,CIA) ((CPU)->state.pc = (CIA))
+
+  struct ft32_cpu_state state;
+
+  sim_cpu_base base;
+};
+
+struct sim_state {
+
+  sim_cpu *cpu[MAX_NR_PROCESSORS];
+#if (WITH_SMP)
+#define STATE_CPU(sd,n) ((sd)->cpu[n])
+#else
+#define STATE_CPU(sd,n) ((sd)->cpu[0])
+#endif
+
+  sim_state_base base;
+};
+
+#endif
diff --git a/sim/testsuite/sim/ft32/ChangeLog b/sim/testsuite/sim/ft32/ChangeLog
new file mode 100644
index 0000000..592fdd5
--- /dev/null
+++ b/sim/testsuite/sim/ft32/ChangeLog
@@ -0,0 +1,3 @@
+2015-02-23  James Bowman  <james.bowman@ftdichip.com>
+
+	* basic.s, allinsn.exp, testutils.inc: New files.
diff --git a/sim/testsuite/sim/ft32/allinsn.exp b/sim/testsuite/sim/ft32/allinsn.exp
new file mode 100644
index 0000000..730b422
--- /dev/null
+++ b/sim/testsuite/sim/ft32/allinsn.exp
@@ -0,0 +1,15 @@
+# ft32 simulator testsuite
+
+if [istarget ft32-*] {
+    # all machines
+    set all_machs "ft32"
+
+    foreach src [lsort [glob -nocomplain $srcdir/$subdir/*.s]] {
+	# If we're only testing specific files and this isn't one of them,
+	# skip it.
+	if ![runtest_file_p $runtests $src] {
+	    continue
+	}
+	run_sim_test $src $all_machs
+    }
+}
diff --git a/sim/testsuite/sim/ft32/basic.s b/sim/testsuite/sim/ft32/basic.s
new file mode 100644
index 0000000..5bba2a5
--- /dev/null
+++ b/sim/testsuite/sim/ft32/basic.s
@@ -0,0 +1,862 @@
+# check that basic insns work.
+# mach: ft32
+
+.include "testutils.inc"
+
+	start
+
+	ldk     $r4,10
+	add     $r4,$r4,23
+	EXPECT  $r4,33
+
+# lda, sta
+	.data
+tmp:    .long     0
+	.text
+
+	xor.l     $r0,$r0,$r0
+	EXPECT    $r0,0x00000000
+	xor.l     $r0,$r0,$r0
+	add.l     $r0,$r0,1
+	EXPECT    $r0,0x00000001
+ 
+	ldk.l     $r0,0x4567
+	EXPECT    $r0,0x00004567
+
+	lpm.l     $r0,k_12345678
+	EXPECT    $r0,0x12345678
+
+	sta.l     tmp,$r0
+	lda.l     $r1,tmp
+	EXPECT    $r1,0x12345678
+
+	lda.b     $r1,tmp
+	EXPECT    $r1,0x00000078
+
+	lda.b     $r1,tmp+1
+	EXPECT    $r1,0x00000056
+
+	lda.b     $r1,tmp+2
+	EXPECT    $r1,0x00000034
+
+	lda.b     $r1,tmp+3
+	EXPECT    $r1,0x00000012
+
+	sta.b     tmp+1,$r0
+	lda.l     $r1,tmp+0
+	EXPECT    $r1,0x12347878
+
+# immediate
+	ldk.l     $r1,12
+	add.l     $r1,$r1,4
+	EXPECT    $r1,0x00000010
+	add.l     $r1,$r1,0x1ff
+	EXPECT    $r1,0x0000020f
+	add.l     $r1,$r1,-0x200
+	EXPECT    $r1,0x0000000f
+      
+# addk
+	xor.l     $r1,$r0,$r0
+	add.l     $r2,$r1,127
+	EXPECT    $r2,0x0000007f
+
+	add.l     $r2,$r2,127
+	EXPECT    $r2,0x000000fe
+
+	add.l     $r2,$r2,-127
+	EXPECT    $r2,0x0000007f
+
+	add.l     $r2,$r2,-128
+	EXPECT    $r2,0xffffffff
+
+	add.l     $r2,$r2,1
+	EXPECT    $r2,0x00000000
+
+# mul
+	ldk.l     $r1,100
+	ldk.l     $r2,77
+	mul.l     $r3,$r1,$r2
+	EXPECT    $r3,0x00001e14
+
+	# 0x12345678 ** 2 = 0x14b66dc1df4d840L
+	mul.l     $r3,$r0,$r0
+	EXPECT    $r3,0x1df4d840
+	muluh.l   $r3,$r0,$r0
+	EXPECT    $r3,0x014b66dc
+
+# push and pop
+	push.l    $r0
+	EXPECT    $sp,0x0000fffc
+	ldi.l     $r3,$sp,0
+	EXPECT    $r3,0x12345678
+
+	pop.l     $r4
+	EXPECT    $sp,0x00000000
+	EXPECT    $r4,0x12345678
+
+	ldk.l     $r1,0x1111
+	push.l    $r1
+	ldk.l     $r1,0x2222
+	push.l    $r1
+	ldk.l     $r1,0x3333
+	push.l    $r1
+	ldk.l     $r1,0x4444
+	push.l    $r1
+	EXPECT    $sp,0x0000fff0
+	pop.l     $r1
+	EXPECT    $r1,0x00004444
+	pop.l     $r1
+	EXPECT    $r1,0x00003333
+	pop.l     $r1
+	EXPECT    $r1,0x00002222
+	pop.l     $r1
+	EXPECT    $r1,0x00001111
+
+# push and pop with $sp changes
+	ldk.l     $r1,0xa111
+	push.l    $r1
+	sub.l     $sp,$sp,4
+	ldk.l     $r1,0xa222
+	push.l    $r1
+	add.l     $sp,$sp,-36
+	add.l     $sp,$sp,36
+	pop.l     $r1
+	EXPECT    $r1,0x0000a222
+	add.l     $sp,$sp,4
+	pop.l     $r1
+	EXPECT    $r1,0x0000a111
+
+# sti
+	ldk.l     $r2,80
+	EXPECT    $r2,0x00000050
+	sti.l     $r2,0,$r0
+	lda.l     $r1,80
+	EXPECT    $r1,0x12345678
+
+	ldk.l     $r3,0xF0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x123456f0
+
+	add.l     $r2,$r2,1
+	sti.l     $r2,0,$r0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x1234f078
+
+	add.l     $r2,$r2,1
+	sti.l     $r2,0,$r0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x12f05678
+
+	add.l     $r2,$r2,1
+	sti.l     $r2,0,$r0
+	sti.b     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0xf0345678
+
+	ldk.l     $r2,80
+	sti.l     $r2,0,$r0
+	ldk.s     $r3,0xbeef
+	sti.s     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0x1234beef
+	add.l     $r2,$r2,2
+	sti.s     $r2,0,$r3
+	lda.l     $r1,80
+	EXPECT    $r1,0xbeefbeef
+
+# lpmi
+
+	ldk.l     $r1,k_12345678
+	lpmi.l    $r2,$r1,0
+	EXPECT    $r2,0x12345678
+
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000078
+
+	add.l     $r1,$r1,1
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000056
+
+	add.l     $r1,$r1,1
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000034
+
+	add.l     $r1,$r1,1
+	lpmi.b    $r2,$r1,0
+	EXPECT    $r2,0x00000012
+
+	lpmi.l    $r2,$r1,4
+	EXPECT    $r2,0xabcdef01
+
+	lpmi.l    $r2,$r1,-4
+	EXPECT    $r2,0x10111213
+
+	lpmi.b    $r2,$r1,-4
+	EXPECT    $r2,0x00000010
+
+	ldk.l     $r1,k_12345678
+	lpmi.s    $r2,$r1,0
+	EXPECT    $r2,0x00005678
+	lpmi.s    $r2,$r1,2
+	EXPECT    $r2,0x00001234
+	lpmi.b    $r2,$r1,6
+	EXPECT    $r2,0x000000cd
+	lpmi.b    $r2,$r1,7
+	EXPECT    $r2,0x000000ab
+	lpmi.b    $r2,$r1,-1
+	EXPECT    $r2,0x00000010
+	lpmi.s    $r2,$r1,-2
+	EXPECT    $r2,0x00001011
+
+	ldk.l     $r1,k_12345678-127
+	lpmi.b    $r2,$r1,127
+	EXPECT    $r2,0x00000078
+
+	ldk.l     $r1,k_12345678+128
+	lpmi.b    $r2,$r1,-128
+	EXPECT    $r2,0x00000078
+
+# shifts
+
+	lpm.l     $r0,k_12345678
+	ldk.l     $r2,4
+	ashl.l    $r1,$r0,$r2
+	EXPECT    $r1,0x23456780
+	lshr.l    $r1,$r0,$r2
+	EXPECT    $r1,0x01234567
+	ashr.l    $r1,$r0,$r2
+	EXPECT    $r1,0x01234567
+
+	lpm.l     $r0,k_abcdef01
+	ashl.l    $r1,$r0,$r2
+	EXPECT    $r1,0xbcdef010
+	lshr.l    $r1,$r0,$r2
+	EXPECT    $r1,0x0abcdef0
+	ashr.l    $r1,$r0,$r2
+	EXPECT    $r1,0xfabcdef0
+
+# rotate right
+      
+	lpm.l     $r0,k_12345678
+	ror.l     $r1,$r0,0
+	EXPECT    $r1,0x12345678
+	ror.l     $r1,$r0,12
+	EXPECT    $r1,0x67812345
+	ror.l     $r1,$r0,-4
+	EXPECT    $r1,0x23456781
+
+# jmpx
+	ldk       $r28,0xaaaaa
+	jmpx      0,$r28,1,failcase
+	jmpx      1,$r28,0,failcase
+	jmpx      2,$r28,1,failcase
+	jmpx      3,$r28,0,failcase
+	jmpx      4,$r28,1,failcase
+	jmpx      5,$r28,0,failcase
+	jmpx      6,$r28,1,failcase
+	jmpx      7,$r28,0,failcase
+	jmpx      8,$r28,1,failcase
+	jmpx      9,$r28,0,failcase
+	jmpx      10,$r28,1,failcase
+	jmpx      11,$r28,0,failcase
+	jmpx      12,$r28,1,failcase
+	jmpx      13,$r28,0,failcase
+	jmpx      14,$r28,1,failcase
+	jmpx      15,$r28,0,failcase
+	jmpx      16,$r28,1,failcase
+	jmpx      17,$r28,0,failcase
+	jmpx      18,$r28,1,failcase
+	jmpx      19,$r28,0,failcase
+
+	move      $r29,$r28
+	ldk       $r28,0
+	jmpx      0,$r29,1,failcase
+	jmpx      1,$r29,0,failcase
+	jmpx      2,$r29,1,failcase
+	jmpx      3,$r29,0,failcase
+	jmpx      4,$r29,1,failcase
+	jmpx      5,$r29,0,failcase
+	jmpx      6,$r29,1,failcase
+	jmpx      7,$r29,0,failcase
+	jmpx      8,$r29,1,failcase
+	jmpx      9,$r29,0,failcase
+	jmpx      10,$r29,1,failcase
+	jmpx      11,$r29,0,failcase
+	jmpx      12,$r29,1,failcase
+	jmpx      13,$r29,0,failcase
+	jmpx      14,$r29,1,failcase
+	jmpx      15,$r29,0,failcase
+	jmpx      16,$r29,1,failcase
+	jmpx      17,$r29,0,failcase
+	jmpx      18,$r29,1,failcase
+	jmpx      19,$r29,0,failcase
+
+	move      $r30,$r29
+	ldk       $r29,0
+	jmpx      0,$r30,1,failcase
+	jmpx      1,$r30,0,failcase
+	jmpx      2,$r30,1,failcase
+	jmpx      3,$r30,0,failcase
+	jmpx      4,$r30,1,failcase
+	jmpx      5,$r30,0,failcase
+	jmpx      6,$r30,1,failcase
+	jmpx      7,$r30,0,failcase
+	jmpx      8,$r30,1,failcase
+	jmpx      9,$r30,0,failcase
+	jmpx      10,$r30,1,failcase
+	jmpx      11,$r30,0,failcase
+	jmpx      12,$r30,1,failcase
+	jmpx      13,$r30,0,failcase
+	jmpx      14,$r30,1,failcase
+	jmpx      15,$r30,0,failcase
+	jmpx      16,$r30,1,failcase
+	jmpx      17,$r30,0,failcase
+	jmpx      18,$r30,1,failcase
+	jmpx      19,$r30,0,failcase
+
+# callx
+	ldk       $r30,0xaaaaa
+	callx     0,$r30,0,skip1
+	jmp       failcase
+	callx     1,$r30,1,skip1
+	jmp       failcase
+	callx     2,$r30,0,skip1
+	jmp       failcase
+	callx     3,$r30,1,skip1
+	jmp       failcase
+
+	callx     0,$r30,1,skip1
+	ldk       $r30,0x123
+	EXPECT    $r30,0x123
+
+#define BIT(N,M)  ((((N) & 15) << 5) | (M))
+# bextu
+	bextu.l   $r1,$r0,(0<<5)|0
+	EXPECT    $r1,0x00005678
+	bextu.l   $r1,$r0,(4<<5)|0
+	EXPECT    $r1,0x00000008
+	bextu.l   $r1,$r0,(4<<5)|4
+	EXPECT    $r1,0x00000007
+	bextu.l   $r1,$r0,(4<<5)|28
+	EXPECT    $r1,0x00000001
+	bextu.l   $r1,$r0,(8<<5)|16
+	EXPECT    $r1,0x00000034
+	ldk.l     $r2,-1
+	bextu.l   $r1,$r2,(6<<5)|(3)
+	EXPECT    $r1,0x0000003f
+
+# bexts
+	bexts.l   $r1,$r0,(8<<5)|0
+	EXPECT    $r1,0x00000078
+	bexts.l   $r1,$r0,(0<<5)|16
+	EXPECT    $r1,0x00001234
+	bexts.l   $r1,$r0,(4<<5)|0
+	EXPECT    $r1,0xfffffff8
+	# extract the '5' digit in widths 4-1
+	bexts.l   $r1,$r0,(4<<5)|12
+	EXPECT    $r1,0x00000005
+	bexts.l   $r1,$r0,(3<<5)|12
+	EXPECT    $r1,0xfffffffd
+	bexts.l   $r1,$r0,(2<<5)|12
+	EXPECT    $r1,0x00000001
+	bexts.l   $r1,$r0,(1<<5)|12
+	EXPECT    $r1,0xffffffff
+
+# btst
+	# low four bits should be 0,0,0,1
+	btst.l    $r0,(1<<5)|0
+	jmpc      nz,failcase
+	btst.l    $r0,(1<<5)|1
+	jmpc      nz,failcase
+	btst.l    $r0,(1<<5)|2
+	jmpc      nz,failcase
+	btst.l    $r0,(1<<5)|3
+	jmpc      z,failcase
+
+	# the 6 bit field starting at position 24 is positive
+	btst.l    $r0,(6<<5)|24
+	jmpc      s,failcase
+	# the 5 bit field starting at position 24 is negative
+	btst.l    $r0,(5<<5)|24
+	jmpc      ns,failcase
+
+	EXPECT    $r0,0x12345678
+
+# bins
+	bins.l    $r1,$r0,(8 << 5) | (0)
+	EXPECT    $r1,0x12345600
+
+	bins.l    $r1,$r0,(0 << 5) | (8)
+	EXPECT    $r1,0x12000078
+
+	ldk.l     $r1,(0xff << 10) | (8 << 5) | (8)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x1234ff78
+
+	call      litr1
+	.long     (0x8dd1 << 10) | (0 << 5) | (0)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x12348dd1
+
+	call      litr1
+	.long     (0x8dd1 << 10) | (0 << 5) | (16)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x8dd15678
+
+	ldk.l     $r1,(0xde << 10) | (8 << 5) | (0)
+	bins.l    $r1,$r0,$r1
+	EXPECT    $r1,0x123456de
+
+# ldl
+	ldk.l     $r0,0
+	ldl.l     $r3,$r0,0
+	EXPECT    $r3,0x00000000
+	ldk.l     $r0,-1
+	ldl.l     $r3,$r0,-1
+	EXPECT    $r3,0xffffffff
+	ldk.l     $r0,(0x12345678 >> 10)
+	ldl.l     $r3,$r0,(0x12345678 & 0x3ff)
+	EXPECT    $r3,0x12345678
+	ldk.l     $r0,(0xe2345678 >> 10)
+	ldl.l     $r3,$r0,(0xe2345678 & 0x3ff)
+	EXPECT    $r3,0xe2345678
+
+# flip
+	ldk.l     $r0,0x0000001
+	flip.l    $r1,$r0,0
+	EXPECT    $r1,0x00000001
+
+	lpm.l     $r0,k_12345678
+	flip.l    $r1,$r0,0
+	EXPECT    $r1,0x12345678
+	flip.l    $r1,$r0,24
+	EXPECT    $r1,0x78563412
+	flip.l    $r1,$r0,31
+	EXPECT    $r1,0x1e6a2c48
+
+# stack push pop
+
+	EXPECT    $sp,0x00000000
+	ldk.l     $r6,0x6666
+	push.l    $r6
+	or.l      $r0,$r0,$r0      # xxx
+	EXPECT    $sp,0x0000fffc
+	ldi.l     $r1,$sp,0
+	EXPECT    $r1,0x00006666
+	pop.l     $r1
+	EXPECT    $r1,0x00006666
+	EXPECT    $sp,0x00000000
+
+# call/return
+	call      fowia
+	push.l    $r1
+	call      fowia
+	pop.l     $r2
+	sub.l     $r1,$r1,$r2
+	EXPECT    $r1,0x00000008
+
+# add,carry
+
+	ldk.l     $r0,0
+	ldk.l     $r1,0
+	call      add64
+	EXPECT    $r1,0x00000000
+	EXPECT    $r0,0x00000000
+
+	lpm.l     $r0,k_abcdef01
+	lpm.l     $r1,k_abcdef01
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0x579bde02
+
+	ldk.l     $r0,4
+	ldk.l     $r1,-5
+	call      add64
+	EXPECT    $r1,0x00000000
+	EXPECT    $r0,0xffffffff
+
+	ldk.l     $r0,5
+	ldk.l     $r1,-5
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0x00000000
+
+	lpm.l     $r0,k_12345678
+	ldk.l     $r1,-1
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0x12345677
+
+	ldk.l     $r0,-1
+	ldk.l     $r1,-1
+	call      add64
+	EXPECT    $r1,0x00000001
+	EXPECT    $r0,0xfffffffe
+
+# inline literal
+	call      lit
+	.long     0xdecafbad
+	EXPECT    $r0,0xdecafbad
+
+	ldk.l     $r1,0xee
+	call      lit
+	ldk.l     $r1,0xfe
+	EXPECT    $r1,0x000000ee
+
+	call      lit
+	.long     0x01020304
+	EXPECT    $r0,0x01020304
+
+	call      lit
+	.long     lit
+	calli     $r0
+	.long     0xffaa55aa
+	EXPECT    $r0,0xffaa55aa
+
+# comparisons
+	ldk.l     $r0,-100
+	ldk.l     $r1,100
+	cmp.l     $r0,$r1
+
+	ldk.l     $r2,0
+	jmpc      lt,.c1
+	ldk.l     $r2,1
+.c1:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      gt,.c2
+	ldk.l     $r2,1
+.c2:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      a,.c3
+	ldk.l     $r2,1
+.c3:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      b,.c4
+	ldk.l     $r2,1
+.c4:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      be,.c5
+	ldk.l     $r2,1
+.c5:
+	EXPECT    $r2,0x00000001
+
+# 8-bit comparisons
+	ldk.l     $r0,0x8fe
+	ldk.l     $r1,0x708
+	cmp.b     $r0,$r1
+
+	ldk.l     $r2,0
+	jmpc      lt,.8c1
+	ldk.l     $r2,1
+.8c1:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      gt,.8c2
+	ldk.l     $r2,1
+.8c2:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      a,.8c3
+	ldk.l     $r2,1
+.8c3:
+	EXPECT    $r2,0x00000000
+
+	ldk.l     $r2,0
+	jmpc      b,.8c4
+	ldk.l     $r2,1
+.8c4:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r2,0
+	jmpc      be,.8c5
+	ldk.l     $r2,1
+.8c5:
+	EXPECT    $r2,0x00000001
+
+	ldk.l     $r0,0x8aa
+	ldk.l     $r1,0x7aa
+	cmp.b     $r0,$r1
+
+	ldk.l     $r2,0
+	jmpc      z,.8c6
+	ldk.l     $r2,1
+.8c6:
+	EXPECT    $r2,0x00000000
+
+	ldk.b     $r0,1
+	ldk.b     $r2,0xe0
+	cmp.b     $r2,0x1c0
+	jmpc      a,.8c7
+	ldk.b     $r0,0
+.8c7:
+	EXPECT    $r0,0x00000001
+
+# conditional call
+	cmp.l     $r0,$r0
+	callc     z,lit
+	.long     0xccddeeff
+	callc     nz,zr0
+	EXPECT    $r0,0xccddeeff
+
+# modify return address
+	ldk.l     $r0,0x66
+	call      skip1
+	ldk.l     $r0,0xAA
+	EXPECT    $r0,0x00000066
+
+	ldk.l     $r0,0x77
+	call      skip2
+	ldk.l     $r0,0xBB
+	EXPECT    $r0,0x00000077
+
+# simple recursive function
+	ldk.l     $r0,1
+	call      factorial
+	EXPECT    $r0,0x00000001
+	ldk.l     $r0,2
+	call      factorial
+	EXPECT    $r0,0x00000002
+	ldk.l     $r0,3
+	call      factorial
+	EXPECT    $r0,0x00000006
+	ldk.l     $r0,4
+	call      factorial
+	EXPECT    $r0,0x00000018
+	ldk.l     $r0,5
+	call      factorial
+	EXPECT    $r0,0x00000078
+	ldk.l     $r0,6
+	call      factorial
+	EXPECT    $r0,0x000002d0
+	ldk.l     $r0,7
+	call      factorial
+	EXPECT    $r0,0x000013b0
+	ldk.l     $r0,12
+	call      factorial
+	EXPECT    $r0,0x1c8cfc00
+
+# read sp after a call
+	call      nullfunc
+	EXPECT    $sp,0x00000000 
+
+# CALLI->RETURN
+	ldk.l     $r4,nullfunc
+	calli     $r4
+	EXPECT    $sp,0x00000000 
+
+# Link/unlink
+	ldk.l     $r14,0x17566
+
+	link      $r14,48
+	EXPECT    $r14,0x0000fffc
+	sub.l     $sp,$sp,200
+	unlink    $r14
+	EXPECT    $r14,0x00017566
+
+# LINK->UNLINK
+	link      $r14,48
+	unlink    $r14
+	EXPECT    $r14,0x00017566
+
+# LINK->JUMPI
+	ldk.l     $r3,.here
+	link      $r14,48
+	jmpi      $r3
+	jmp       failcase
+.here:
+	unlink    $r14
+	EXPECT    $r14,0x00017566
+
+# LINK->RETURN
+# (This is a nonsense combination, but can still exericse it by
+# using a negative parameter for the link.  "link $r14,-4" leaves
+# $sp exactly unchanged.)
+	ldk.l     $r0,.returnhere
+	push.l    $r0
+	link      $r14,0xfffc
+	return
+.returnhere:
+	EXPECT    $sp,0x00000000 
+
+# LPMI->CALLI
+	ldk.l     $r0,k_abcdef01
+	ldk.l     $r1,increment
+	lpmi.l    $r0,$r0,0
+	calli     $r1
+	EXPECT    $r0,0xabcdef02
+
+# STRLen
+	lpm.l     $r4,str3
+	sta.l     tmp,$r4
+	ldk.l     $r0,tmp
+	strlen.b  $r1,$r0
+	EXPECT    $r1,0x00000003
+	strlen.s  $r1,$r0
+	EXPECT    $r1,0x00000003
+	strlen.l  $r1,$r0
+	EXPECT    $r1,0x00000003
+
+	ldk.l     $r4,0
+	sta.b     4,$r4
+	strlen.l  $r1,$r0
+	EXPECT    $r1,0x00000000
+
+	ldk.l     $r4,-1
+	sta.l     4,$r4
+	lpm.l     $r4,str3
+	sta.l     8,$r4
+	strlen.l  $r1,$r0
+	EXPECT    $r1,0x00000007
+
+# MEMSet
+	ldk.l     $r0,4
+	ldk.l     $r1,0xaa
+	memset.s  $r0,$r1,8
+	ldk.l     $r1,0x55
+	memset.b  $r0,$r1,5
+	lda.l     $r0,4
+	EXPECT    $r0,0x55555555
+	lda.l     $r0,8
+	EXPECT    $r0,0xaaaaaa55
+
+# first cycle after mispredict
+	ldk.l     $r0,3
+	cmp.l     $r0,$r0
+	jmpc      nz,failcase
+	add.l     $r0,$r0,7
+	EXPECT    $r0,0x0000000a 
+	jmpc      nz,failcase
+	push.l    $r0
+	EXPECT    $sp,0x0000fffc 
+	pop.l     $r0
+
+# $sp access after stall
+	lpm.l     $r13,0
+	push.l    $r0
+	EXPECT    $sp,0x0000fffc
+	pop.l     $r0
+
+	push.l    $r0
+	add.l     $sp,$sp,-484
+	EXPECT    $sp,0x0000fe18
+	EXPECT    $sp,0x0000fe18
+	EXPECT    $sp,0x0000fe18
+	add.l     $sp,$sp,484
+	EXPECT    $sp,0x0000fffc
+	pop.l     $r0
+
+# atomic exchange
+	lpm.l     $r0,k_12345678
+	lpm.l     $r1,k_abcdef01
+	sta.l     100,$r1
+	exa.l     $r0,100
+	EXPECT    $r0,0xabcdef01
+	lda.l     $r0,100
+	EXPECT    $r0,0x12345678
+
+	lpm.l     $r0,k_12345678
+	lpm.l     $r1,k_abcdef01
+	sta.l     144,$r1
+	ldk.l     $r7,20
+	exi.l     $r0,$r7,124
+	EXPECT    $r0,0xabcdef01
+	lda.l     $r0,144
+	EXPECT    $r0,0x12345678
+
+	lpm.l     $r0,k_12345678
+	lpm.l     $r1,k_abcdef01
+	push      $r1
+	exi.l     $r0,$sp,0
+	EXPECT    $r0,0xabcdef01
+	pop.l     $r0
+	EXPECT    $r0,0x12345678
+
+# final stack check
+	EXPECT    $sp,0x00000000
+
+	PASS
+
+# --------------------------------------------------
+
+skip1:          # skip the instruction after the call
+	pop.l     $r1
+	add.l     $r1,$r1,4
+	push.l    $r1
+	return
+
+skipparent:     # skip the instruction after the caller's call
+	ldi.l     $r1,$sp,4
+	add.l     $r1,$r1,4
+	sti.l     $sp,4,$r1
+	return
+skip2:
+	call      skipparent
+	return
+
+add64:
+	addcc.l   $r0,$r1
+	add.l     $r0,$r0,$r1
+	ldk.l     $r1,0
+	jmpc      nc,.done
+	ldk.l     $r1,1
+.done:
+	return
+
+fowia:  # find out where I'm at
+	ldi.l     $r1,$sp,0
+	return
+
+lit:    # load literal to $r0
+	pop.l     $r14
+	lpmi.l    $r0,$r14,0
+	add.l     $r14,$r14,4
+	jmpi      $r14
+zr0:
+	ldk.l     $r0,0
+	return
+litr1:
+	ldi.l     $r1,$sp,0
+	add.l     $r1,$r1,4
+	sti.l     $sp,0,$r1
+	lpmi.l    $r1,$r1,-4
+	return
+
+factorial:
+	ldk.l     $r1,1
+	cmp.l     $r0,$r1
+	jmpc      z,.factdone
+	push.l    $r0
+	add.l     $r0,$r0,-1
+	call      factorial
+	pop.l     $r1
+	mul.l     $r0,$r0,$r1
+.factdone:
+	return
+
+nullfunc:
+	return
+
+increment:
+	add.l     $r0,$r0,1
+	return
+
+	.long   0x10111213
+k_12345678:
+	.long   0x12345678
+k_abcdef01:
+	.long   0xabcdef01
+str3:
+	.string   "abc"
diff --git a/sim/testsuite/sim/ft32/testutils.inc b/sim/testsuite/sim/ft32/testutils.inc
new file mode 100644
index 0000000..c07811f
--- /dev/null
+++ b/sim/testsuite/sim/ft32/testutils.inc
@@ -0,0 +1,65 @@
+
+# Write ch to the standard output
+	.macro outch  ch
+	ldk   $r0,\ch
+	sta   0x10000,$r0
+	.endm
+
+# End the test with return code c
+	.macro  exit c
+	ldk   $r0,\c
+	sta   0x1fffc,$r0
+	.endm
+
+# All assembler tests should start with this macro "start"
+	.macro start
+	.text
+
+	jmp     __start
+	jmp     __start
+	reti
+
+	.data
+ccsave: .long   0
+	.text
+
+# Fiddling to load $cc from the following word in program memory
+loadcc:
+	exi     $r29,$sp,0
+	lpmi    $cc,$r29,0
+	add     $r29,$r29,4
+	exi     $r29,$sp,0
+	return
+
+failcase:
+	outch 'f'
+	outch 'a'
+	outch 'i'
+	outch 'l'
+	outch '\n'
+	exit  1
+
+__start:
+
+	.endm
+
+# At the end of the test, the code should reach this macro PASS
+	.macro PASS
+	outch 'p'
+	outch 'a'
+	outch 's'
+	outch 's'
+	outch '\n'
+	exit  0
+	.endm
+
+# Confirm that reg has value, and fail immediately if not
+# Preserves all registers
+	.macro EXPECT reg,value
+	sta   ccsave,$cc
+	call  loadcc
+	.long \value
+	cmp   \reg,$cc
+	jmpc  nz,failcase
+	lda   $cc,ccsave
+	.endm
