public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] sim: example-synacor: a simple implementation for reference
@ 2021-01-03  7:24 Mike Frysinger
  2021-01-19  4:05 ` [PATCH v2] " Mike Frysinger
  0 siblings, 1 reply; 4+ messages in thread
From: Mike Frysinger @ 2021-01-03  7:24 UTC (permalink / raw)
  To: gdb-patches

Provide a simple example simulator for people porting to new targets
to use as a reference.  This one has the advantage of being used by
people and having a fun program available for it.

It doesn't require a special target -- the example simulators can be
built for any existing port.
---
 sim/ChangeLog                        |     5 +
 sim/configure                        |    17 +-
 sim/configure.ac                     |     9 +-
 sim/example-synacor/ChangeLog        |     5 +
 sim/example-synacor/Makefile.in      |    26 +
 sim/example-synacor/README           |    15 +
 sim/example-synacor/README.arch-spec |    73 +
 sim/example-synacor/aclocal.m4       |   119 +
 sim/example-synacor/config.in        |   248 +
 sim/example-synacor/configure        | 16038 +++++++++++++++++++++++++
 sim/example-synacor/configure.ac     |    14 +
 sim/example-synacor/interp.c         |   178 +
 sim/example-synacor/sim-main.c       |   530 +
 sim/example-synacor/sim-main.h       |    49 +
 14 files changed, 17321 insertions(+), 5 deletions(-)
 create mode 100644 sim/example-synacor/ChangeLog
 create mode 100644 sim/example-synacor/Makefile.in
 create mode 100644 sim/example-synacor/README
 create mode 100644 sim/example-synacor/README.arch-spec
 create mode 100644 sim/example-synacor/aclocal.m4
 create mode 100644 sim/example-synacor/config.in
 create mode 100755 sim/example-synacor/configure
 create mode 100644 sim/example-synacor/configure.ac
 create mode 100644 sim/example-synacor/interp.c
 create mode 100644 sim/example-synacor/sim-main.c
 create mode 100644 sim/example-synacor/sim-main.h

sim/:

	* configure.ac: Add --example-sims option.
	* configure: Regenerate.

sim/example-synacor/:

	* configure.ac, interp.c, Makefile.in, README, README.arch-spec,
	sim-main.c, sim-main.h: New files for example simulator.
	* aclocal.m4, config.in, configure: Regenerated.

diff --git a/sim/configure.ac b/sim/configure.ac
index d92ca25d3978..2d0fb13f368f 100644
--- a/sim/configure.ac
+++ b/sim/configure.ac
@@ -51,6 +51,11 @@ if test "${enable_sim}" != no; then
    fi
 fi
 
-AC_OUTPUT(Makefile)
+AC_ARG_ENABLE([example-sims],
+	      [AC_HELP_STRING([--enable-example-sims],
+			      [enable example GNU simulators])])
+if test "x${enable_example_sims}" = xyes; then
+  AC_CONFIG_SUBDIRS(example-synacor)
+fi
 
-exit 0
+AC_OUTPUT(Makefile)
diff --git a/sim/example-synacor/Makefile.in b/sim/example-synacor/Makefile.in
new file mode 100644
index 000000000000..edd77ac801e8
--- /dev/null
+++ b/sim/example-synacor/Makefile.in
@@ -0,0 +1,26 @@
+#    Makefile template for Configure for the example synacor simulator.
+#    Copyright (C) 2005-2021 Free Software Foundation, Inc.
+#    Written by Mike Frysinger <vapier@gentoo.org>
+#
+# 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) \
+	sim-resume.o \
+	interp.o \
+	sim-main.o
+
+## COMMON_POST_CONFIG_FRAG
diff --git a/sim/example-synacor/README b/sim/example-synacor/README
new file mode 100644
index 000000000000..93ffd53d5a49
--- /dev/null
+++ b/sim/example-synacor/README
@@ -0,0 +1,15 @@
+= OVERVIEW =
+
+The Synacor Challenge is a fun programming exercise with a number of puzzles
+built into it.  You can find more details about it here:
+https://challenge.synacor.com/
+
+The first puzzle is writing an interpreter for their custom ISA.  This is a
+simulator for that custom CPU.  The CPU is quite basic: it's 16-bit with only
+8 registers and a limited set of instructions.  This means the port will never
+grow new features.  See README.arch-spec for more details.
+
+Implementing it here ends up being quite useful: it acts as a simple constrained
+"real world" example for people who want to implement a new simulator for their
+own architecture.  We demonstrate all the basic fundamentals (registers, memory,
+branches, and tracing) that all ports should have.
diff --git a/sim/example-synacor/README.arch-spec b/sim/example-synacor/README.arch-spec
new file mode 100644
index 000000000000..da2d9d003670
--- /dev/null
+++ b/sim/example-synacor/README.arch-spec
@@ -0,0 +1,73 @@
+== architecture ==
+- three storage regions
+  - memory with 15-bit address space storing 16-bit values
+  - eight registers
+  - an unbounded stack which holds individual 16-bit values
+- all numbers are unsigned integers 0..32767 (15-bit)
+- all math is modulo 32768; 32758 + 15 => 5
+
+== binary format ==
+- each number is stored as a 16-bit little-endian pair (low byte, high byte)
+- numbers 0..32767 mean a literal value
+- numbers 32768..32775 instead mean registers 0..7
+- numbers 32776..65535 are invalid
+- programs are loaded into memory starting at address 0
+- address 0 is the first 16-bit value, address 1 is the second 16-bit value, etc
+
+== execution ==
+- After an operation is executed, the next instruction to read is immediately after the last argument of the current operation.
+  If a jump was performed, the next operation is instead the exact destination of the jump.
+- Encountering a register as an operation argument should be taken as reading from the register or setting into the register as appropriate.
+
+== hints ==
+- Start with operations 0, 19, and 21.
+- Here's a code for the challenge website: jTTockJlJiOC
+- The program "9,32768,32769,4,19,32768" occupies six memory addresses and should:
+  - Store into register 0 the sum of 4 and the value contained in register 1.
+  - Output to the terminal the character with the ascii code contained in register 0.
+
+== opcode listing ==
+halt: 0
+  stop execution and terminate the program
+set: 1 a b
+  set register <a> to the value of <b>
+push: 2 a
+  push <a> onto the stack
+pop: 3 a
+  remove the top element from the stack and write it into <a>; empty stack = error
+eq: 4 a b c
+  set <a> to 1 if <b> is equal to <c>; set it to 0 otherwise
+gt: 5 a b c
+  set <a> to 1 if <b> is greater than <c>; set it to 0 otherwise
+jmp: 6 a
+  jump to <a>
+jt: 7 a b
+  if <a> is nonzero, jump to <b>
+jf: 8 a b
+  if <a> is zero, jump to <b>
+add: 9 a b c
+  assign into <a> the sum of <b> and <c> (modulo 32768)
+mult: 10 a b c
+  store into <a> the product of <b> and <c> (modulo 32768)
+mod: 11 a b c
+  store into <a> the remainder of <b> divided by <c>
+and: 12 a b c
+  stores into <a> the bitwise and of <b> and <c>
+or: 13 a b c
+  stores into <a> the bitwise or of <b> and <c>
+not: 14 a b
+  stores 15-bit bitwise inverse of <b> in <a>
+rmem: 15 a b
+  read memory at address <b> and write it to <a>
+wmem: 16 a b
+  write the value from <b> into memory at address <a>
+call: 17 a
+  write the address of the next instruction to the stack and jump to <a>
+ret: 18
+  remove the top element from the stack and jump to it; empty stack = halt
+out: 19 a
+  write the character represented by ascii code <a> to the terminal
+in: 20 a
+  read a character from the terminal and write its ascii code to <a>; it can be assumed that once input starts, it will continue until a newline is encountered; this means that you can safely read whole lines from the keyboard and trust that they will be fully read
+noop: 21
+  no operation
diff --git a/sim/example-synacor/configure.ac b/sim/example-synacor/configure.ac
new file mode 100644
index 000000000000..d344fd9d3238
--- /dev/null
+++ b/sim/example-synacor/configure.ac
@@ -0,0 +1,14 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+sinclude(../common/acinclude.m4)
+
+SIM_AC_COMMON
+
+SIM_AC_OPTION_ENDIAN(LITTLE)
+SIM_AC_OPTION_ALIGNMENT(STRICT_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/example-synacor/interp.c b/sim/example-synacor/interp.c
new file mode 100644
index 000000000000..7b831101f809
--- /dev/null
+++ b/sim/example-synacor/interp.c
@@ -0,0 +1,178 @@
+/* Example synacor simulator.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   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/>.  */
+
+/* This file contains the main glue logic between the sim core and the target
+   specific simulator.  Normally this file will be kept small and the target
+   details will live in other files.
+
+   For more specific details on these functions, see the gdb/remote-sim.h
+   header file.  */
+
+#include "config.h"
+
+#include "sim-main.h"
+#include "sim-options.h"
+\f
+/* This function is the main loop.  It should process ticks and decode+execute
+   a single instruction.
+
+   Usually you do not need to change things here.  */
+
+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 (cpu);
+      if (sim_events_tick (sd))
+	sim_events_process (sd);
+    }
+}
+\f
+/* Initialize the simulator from scratch.  This is called once per lifetime of
+   the simulation.  Think of it as a processor reset.
+
+   Usually all cpu-specific setup is handled in the initialize_cpu callback.
+   If you want to do cpu-independent stuff, then it should go at the end (see
+   where memory is initialized).  */
+
+#define DEFAULT_MEM_SIZE (16 * 1024 * 1024)
+
+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 *callback,
+	  struct bfd *abfd, char * const *argv)
+{
+  char c;
+  int i;
+  SIM_DESC sd = sim_state_alloc (kind, callback);
+
+  /* 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;
+    }
+
+  /* XXX: Default to the Virtual environment.  */
+  if (STATE_ENVIRONMENT (sd) == ALL_ENVIRONMENT)
+    STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT;
+
+  /* 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;
+    }
+
+  /* Check for/establish the a reference program image.  */
+  if (sim_analyze_program (sd,
+			   (STATE_PROG_ARGV (sd) != NULL
+			    ? *STATE_PROG_ARGV (sd)
+			    : NULL), abfd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Establish any remaining 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);
+
+      initialize_cpu (sd, cpu);
+    }
+
+  /* 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_commandf (sd, "memory-size %#x", DEFAULT_MEM_SIZE);
+
+  return sd;
+}
+\f
+/* Prepare to run a program that has already been loaded into memory.
+
+   Usually you do not need to change things here.  */
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd, struct bfd *abfd,
+		     char * const *argv, char * const *env)
+{
+  SIM_CPU *cpu = STATE_CPU (sd, 0);
+  sim_cia addr;
+
+  /* Set the PC.  */
+  if (abfd != NULL)
+    addr = bfd_get_start_address (abfd);
+  else
+    addr = 0;
+  sim_pc_set (cpu, addr);
+
+  /* Standalone mode (i.e. `run`) will take care of the argv for us in
+     sim_open() -> sim_parse_args().  But in debug mode (i.e. 'target sim'
+     with `gdb`), we need to handle it because the user can change the
+     argv on the fly via gdb's 'run'.  */
+  if (STATE_PROG_ARGV (sd) != argv)
+    {
+      freeargv (STATE_PROG_ARGV (sd));
+      STATE_PROG_ARGV (sd) = dupargv (argv);
+    }
+
+  return SIM_RC_OK;
+}
diff --git a/sim/example-synacor/sim-main.c b/sim/example-synacor/sim-main.c
new file mode 100644
index 000000000000..92af7b4d096a
--- /dev/null
+++ b/sim/example-synacor/sim-main.c
@@ -0,0 +1,530 @@
+/* Example synacor simulator.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   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/>.  */
+
+/* This file contains the main simulator decoding logic.  i.e. everything that
+   is architecture specific.  */
+
+#include "config.h"
+
+#include "sim-main.h"
+\f
+/* Get the register number from the number.  */
+static unsigned16
+register_num (SIM_CPU *cpu, unsigned16 num)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+
+  if (num < 0x8000 || num >= 0x8008)
+    sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+
+  return num & 0xf;
+}
+
+/* Helper to process immediates according to the ISA.  */
+static unsigned16
+interp_num (SIM_CPU *cpu, unsigned16 num)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+
+  if (num < 0x8000)
+    {
+      /* Numbers 0..32767 mean a literal value.  */
+      TRACE_DECODE (cpu, "%#x is a literal", num);
+      return num;
+    }
+  else if (num < 0x8008)
+    {
+      /* Numbers 32768..32775 instead mean registers 0..7.  */
+      TRACE_DECODE (cpu, "%#x is register R%i", num, num & 0xf);
+      return cpu->regs[num & 0xf];
+    }
+  else
+    {
+      /* Numbers 32776..65535 are invalid.  */
+      TRACE_DECODE (cpu, "%#x is an invalid number", num);
+      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+    }
+}
+\f
+/* Decode & execute a single instruction.  */
+void step_once (SIM_CPU *cpu)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  unsigned16 iw1, num1;
+  sim_cia pc = sim_pc_get (cpu);
+
+  iw1 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);
+  TRACE_EXTRACT (cpu, "iw1: %#x", iw1);
+  /* This never happens, but technically is possible in the ISA.  */
+  num1 = interp_num (cpu, iw1);
+
+  if (num1 == 0)
+    {
+      /* halt: 0: Stop execution and terminate the program.  */
+      TRACE_INSN (cpu, "HALT");
+      sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
+    }
+  else if (num1 == 1)
+    {
+      /* set: 1 a b: Set register <a> to the value of <b>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      TRACE_EXTRACT (cpu, "SET %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "SET R%i %#x", num2, num3);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, num3);
+      cpu->regs[num2] = num3;
+
+      pc += 6;
+    }
+  else if (num1 == 2)
+    {
+      /* push: 2 a: Push <a> onto the stack.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "PUSH %#x", iw2);
+      TRACE_INSN (cpu, "PUSH %#x", num2);
+
+      sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, num2);
+      cpu->sp -= 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+
+      pc += 4;
+    }
+  else if (num1 == 3)
+    {
+      /* pop: 3 a: Remove the top element from the stack and write it into <a>.
+	 Empty stack = error.  */
+      unsigned16 iw2, num2, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "POP %#x", iw2);
+      TRACE_INSN (cpu, "POP R%i", num2);
+      cpu->sp += 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+      result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 4;
+    }
+  else if (num1 == 4)
+    {
+      /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
+	 otherwise.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 == num4);
+      TRACE_EXTRACT (cpu, "EQ %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "EQ R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x == %#x) = %i", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 5)
+    {
+      /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
+	 otherwise.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 > num4);
+      TRACE_EXTRACT (cpu, "GT %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "GT R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x > %#x) = %i", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 6)
+    {
+      /* jmp: 6 a: Jump to <a>.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      /* Addresses are 16-bit aligned.  */
+      num2 <<= 1;
+      TRACE_EXTRACT (cpu, "JMP %#x", iw2);
+      TRACE_INSN (cpu, "JMP %#x", num2);
+
+      pc = num2;
+      TRACE_BRANCH (cpu, "JMP %#x", pc);
+    }
+  else if (num1 == 7)
+    {
+      /* jt: 7 a b: If <a> is nonzero, jump to <b>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num3 <<= 1;
+      TRACE_EXTRACT (cpu, "JT %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "JT %#x %#x", num2, num3);
+      TRACE_DECODE (cpu, "JT %#x != 0 -> %s", num2, num2 ? "taken" : "nop");
+
+      if (num2)
+	{
+	  pc = num3;
+	  TRACE_BRANCH (cpu, "JT %#x", pc);
+	}
+      else
+	pc += 6;
+    }
+  else if (num1 == 8)
+    {
+      /* jf: 8 a b: If <a> is zero, jump to <b>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num3 <<= 1;
+      TRACE_EXTRACT (cpu, "JF %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "JF %#x %#x", num2, num3);
+      TRACE_DECODE (cpu, "JF %#x == 0 -> %s", num2, num2 ? "nop" : "taken");
+
+      if (!num2)
+	{
+	  pc = num3;
+	  TRACE_BRANCH (cpu, "JF %#x", pc);
+	}
+      else
+	pc += 6;
+    }
+  else if (num1 == 9)
+    {
+      /* add: 9 a b c: Assign <a> the sum of <b> and <c> (modulo 32768).  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 + num4) % 32768;
+      TRACE_EXTRACT (cpu, "ADD %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "ADD R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x + %#x) %% %i = %#x", num2, num3, num4,
+		    32768, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 10)
+    {
+      /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
+	 32768).  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 * num4) % 32768;
+      TRACE_EXTRACT (cpu, "MULT %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "MULT R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x * %#x) %% %i = %#x", num2, num3, num4,
+		    32768, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 11)
+    {
+      /* mod: 11 a b c: Store into <a> the remainder of <b> divided by <c>.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = num3 % num4;
+      TRACE_EXTRACT (cpu, "MOD %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "MOD R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = %#x %% %#x = %#x", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 12)
+    {
+      /* and: 12 a b c: Stores into <a> the bitwise and of <b> and <c>.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 & num4);
+      TRACE_EXTRACT (cpu, "AND %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "AND R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = %#x & %#x = %#x", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 13)
+    {
+      /* or: 13 a b c: Stores into <a> the bitwise or of <b> and <c>.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 | num4);
+      TRACE_EXTRACT (cpu, "OR %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "OR R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = %#x | %#x = %#x", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 14)
+    {
+      /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>.  */
+      unsigned16 iw2, iw3, num2, num3, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      result = (~num3) & 0x7fff;
+      TRACE_EXTRACT (cpu, "NOT %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "NOT R%i %#x", num2, num3);
+      TRACE_DECODE (cpu, "R%i = (~%#x) & 0x7fff = %#x", num2, num3, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 6;
+    }
+  else if (num1 == 15)
+    {
+      /* rmem: 15 a b: Read memory at address <b> and write it to <a>.  */
+      unsigned16 iw2, iw3, num2, num3, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num3 <<= 1;
+      TRACE_EXTRACT (cpu, "RMEM %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "RMEM R%i %#x", num2, num3);
+
+      TRACE_MEMORY (cpu, "reading %#x", num3);
+      result = sim_core_read_aligned_2 (cpu, pc, read_map, num3);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 6;
+    }
+  else if (num1 == 16)
+    {
+      /* wmem: 16 a b: Write the value from <b> into memory at address <a>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num2 <<= 1;
+      TRACE_EXTRACT (cpu, "WMEM %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "WMEM %#x %#x", num2, num3);
+
+      TRACE_MEMORY (cpu, "writing %#x to %#x", num3, num2);
+      sim_core_write_aligned_2 (cpu, pc, write_map, num2, num3);
+
+      pc += 6;
+    }
+  else if (num1 == 17)
+    {
+      /* call: 17 a: Write the address of the next instruction to the stack and
+	 jump to <a>.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      /* Addresses are 16-bit aligned.  */
+      num2 <<= 1;
+      TRACE_EXTRACT (cpu, "CALL %#x", iw2);
+      TRACE_INSN (cpu, "CALL %#x", num2);
+
+      TRACE_MEMORY (cpu, "pushing %#x onto stack", (pc + 4) >> 1);
+      sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, (pc + 4) >> 1);
+      cpu->sp -= 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+
+      pc = num2;
+      TRACE_BRANCH (cpu, "CALL %#x", pc);
+    }
+  else if (num1 == 18)
+    {
+      /* ret: 18: Remove the top element from the stack and jump to it; empty
+	 stack = halt.  */
+      unsigned16 result;
+
+      TRACE_INSN (cpu, "RET");
+      cpu->sp += 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+      result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
+      TRACE_MEMORY (cpu, "popping %#x off of stack", result << 1);
+
+      pc = result << 1;
+      TRACE_BRANCH (cpu, "RET -> %#x", pc);
+    }
+  else if (num1 == 19)
+    {
+      /* out: 19 a: Write the character <a> to the terminal.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "OUT %#x", iw2);
+      TRACE_INSN (cpu, "OUT %#x", num2);
+      TRACE_EVENTS (cpu, "write to stdout: %#x (%c)", num2, num2);
+
+      sim_io_printf (sd, "%c", num2);
+
+      pc += 4;
+    }
+  else if (num1 == 20)
+    {
+      /* in: 20 a: read a character from the terminal and write its ascii code
+	 to <a>.  It can be assumed that once input starts, it will continue
+	 until a newline is encountered.  This means that you can safely read
+	 lines from the keyboard and trust that they will be fully read.  */
+      unsigned16 iw2, num2;
+      char c;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "IN %#x", iw2);
+      TRACE_INSN (cpu, "IN %#x", num2);
+      sim_io_read_stdin (sd, &c, 1);
+      TRACE_EVENTS (cpu, "read from stdin: %#x (%c)", c, c);
+
+      /* The challenge uses lowercase for all inputs, so insert some low level
+	 helpers of our own to make it a bit nicer.  */
+      switch (c)
+	{
+	case 'Q':
+	  sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
+	  break;
+	}
+
+      TRACE_REGISTER (cpu, "R%i = %#x", iw2 & 0xf, c);
+      cpu->regs[iw2 & 0xf] = c;
+
+      pc += 4;
+    }
+  else if (num1 == 21)
+    {
+      /* noop: 21: no operation */
+      TRACE_INSN (cpu, "NOOP");
+
+      pc += 2;
+    }
+  else
+    sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
+
+  TRACE_REGISTER (cpu, "PC = %#x", pc);
+  sim_pc_set (cpu, pc);
+}
+\f
+/* Return the program counter for this cpu. */
+static sim_cia
+pc_get (sim_cpu *cpu)
+{
+  return cpu->pc;
+}
+
+/* Set the program counter for this cpu to the new pc value. */
+static void
+pc_set (sim_cpu *cpu, sim_cia pc)
+{
+  cpu->pc = pc;
+}
+
+/* Initialize the state for a single cpu.  Usuaully this involves clearing all
+   registers back to their reset state.  Should also hook up the fetch/store
+   helper functions too.  */
+void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu)
+{
+  memset (cpu->regs, 0, sizeof (cpu->regs));
+  cpu->pc = 0;
+  /* Make sure it's initialized outside of the 16-bit address space.  */
+  cpu->sp = 0x80000;
+
+  CPU_PC_FETCH (cpu) = pc_get;
+  CPU_PC_STORE (cpu) = pc_set;
+}
diff --git a/sim/example-synacor/sim-main.h b/sim/example-synacor/sim-main.h
new file mode 100644
index 000000000000..ba0e4efa12a9
--- /dev/null
+++ b/sim/example-synacor/sim-main.h
@@ -0,0 +1,49 @@
+/* Example synacor simulator.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   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"
+#include "sim-base.h"
+
+struct _sim_cpu {
+  /* ... simulator specific members ... */
+  unsigned16 regs[8];
+  sim_cia pc;
+
+  /* This isn't a real register, and the stack is not directly addressable,
+     so use memory outside of the 16-bit address space.  */
+  unsigned32 sp;
+
+  sim_cpu_base base;
+};
+
+struct sim_state {
+  sim_cpu *cpu[MAX_NR_PROCESSORS];
+
+  /* ... simulator specific members ... */
+  sim_state_base base;
+};
+
+extern void step_once (SIM_CPU *);
+extern void initialize_cpu (SIM_DESC, SIM_CPU *);
+
+#endif
-- 
2.28.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2] sim: example-synacor: a simple implementation for reference
  2021-01-03  7:24 [PATCH] sim: example-synacor: a simple implementation for reference Mike Frysinger
@ 2021-01-19  4:05 ` Mike Frysinger
  2021-02-04 10:57   ` Jeremy Bennett
  0 siblings, 1 reply; 4+ messages in thread
From: Mike Frysinger @ 2021-01-19  4:05 UTC (permalink / raw)
  To: gdb-patches

Provide a simple example simulator for people porting to new targets
to use as a reference.  This one has the advantage of being used by
people and having a fun program available for it.

It doesn't require a special target -- the example simulators can be
built for any existing port.
---
 sim/ChangeLog                               |     5 +
 sim/configure                               |    15 +-
 sim/configure.ac                            |     7 +
 sim/example-synacor/ChangeLog               |     5 +
 sim/example-synacor/Makefile.in             |    26 +
 sim/example-synacor/README                  |    15 +
 sim/example-synacor/README.arch-spec        |    73 +
 sim/example-synacor/aclocal.m4              |   119 +
 sim/example-synacor/config.in               |   242 +
 sim/example-synacor/configure               | 16029 ++++++++++++++++++
 sim/example-synacor/configure.ac            |    10 +
 sim/example-synacor/interp.c                |   178 +
 sim/example-synacor/sim-main.c              |   530 +
 sim/example-synacor/sim-main.h              |    49 +
 sim/testsuite/example-synacor/ChangeLog     |     5 +
 sim/testsuite/example-synacor/add.s         |    24 +
 sim/testsuite/example-synacor/allinsn.exp   |    15 +
 sim/testsuite/example-synacor/and.s         |    18 +
 sim/testsuite/example-synacor/call.s        |    14 +
 sim/testsuite/example-synacor/exit-0.s      |    10 +
 sim/testsuite/example-synacor/gt.s          |    31 +
 sim/testsuite/example-synacor/isa.inc       |   108 +
 sim/testsuite/example-synacor/jmp.s         |     9 +
 sim/testsuite/example-synacor/mem.s         |    25 +
 sim/testsuite/example-synacor/mod.s         |    18 +
 sim/testsuite/example-synacor/mult.s        |    18 +
 sim/testsuite/example-synacor/not.s         |    15 +
 sim/testsuite/example-synacor/or.s          |    18 +
 sim/testsuite/example-synacor/push-pop.s    |    22 +
 sim/testsuite/example-synacor/ret.s         |    13 +
 sim/testsuite/example-synacor/set.s         |    20 +
 sim/testsuite/example-synacor/testutils.inc |    31 +
 sim/testsuite/lib/sim-defs.exp              |     7 +
 33 files changed, 17723 insertions(+), 1 deletion(-)
 create mode 100644 sim/example-synacor/ChangeLog
 create mode 100644 sim/example-synacor/Makefile.in
 create mode 100644 sim/example-synacor/README
 create mode 100644 sim/example-synacor/README.arch-spec
 create mode 100644 sim/example-synacor/aclocal.m4
 create mode 100644 sim/example-synacor/config.in
 create mode 100755 sim/example-synacor/configure
 create mode 100644 sim/example-synacor/configure.ac
 create mode 100644 sim/example-synacor/interp.c
 create mode 100644 sim/example-synacor/sim-main.c
 create mode 100644 sim/example-synacor/sim-main.h
 create mode 100644 sim/testsuite/example-synacor/ChangeLog
 create mode 100644 sim/testsuite/example-synacor/add.s
 create mode 100644 sim/testsuite/example-synacor/allinsn.exp
 create mode 100644 sim/testsuite/example-synacor/and.s
 create mode 100644 sim/testsuite/example-synacor/call.s
 create mode 100644 sim/testsuite/example-synacor/exit-0.s
 create mode 100644 sim/testsuite/example-synacor/gt.s
 create mode 100644 sim/testsuite/example-synacor/isa.inc
 create mode 100644 sim/testsuite/example-synacor/jmp.s
 create mode 100644 sim/testsuite/example-synacor/mem.s
 create mode 100644 sim/testsuite/example-synacor/mod.s
 create mode 100644 sim/testsuite/example-synacor/mult.s
 create mode 100644 sim/testsuite/example-synacor/not.s
 create mode 100644 sim/testsuite/example-synacor/or.s
 create mode 100644 sim/testsuite/example-synacor/push-pop.s
 create mode 100644 sim/testsuite/example-synacor/ret.s
 create mode 100644 sim/testsuite/example-synacor/set.s
 create mode 100644 sim/testsuite/example-synacor/testutils.inc

diff --git a/sim/ChangeLog b/sim/ChangeLog
index 7b83115d9c5c..bd85fc1c2a64 100644
--- a/sim/ChangeLog
+++ b/sim/ChangeLog
@@ -1,3 +1,8 @@
+2021-01-03  Mike Frysinger  <vapier@gentoo.org>
+
+	* configure.ac: Add --example-sims option.
+	* configure: Regenerate.
+
 2021-01-18  Mike Frysinger  <vapier@gentoo.org>
 
 	* configure.ac: Call AC_PROG_CPP.
diff --git a/sim/configure.ac b/sim/configure.ac
index 99364cac7f4e..cb9c1c9f69ab 100644
--- a/sim/configure.ac
+++ b/sim/configure.ac
@@ -49,5 +49,12 @@ if test "${enable_sim}" != no; then
   fi
 fi
 
+AC_ARG_ENABLE([example-sims],
+	      [AC_HELP_STRING([--enable-example-sims],
+			      [enable example GNU simulators])])
+if test "x${enable_example_sims}" = xyes; then
+  AC_CONFIG_SUBDIRS(example-synacor)
+fi
+
 AC_CONFIG_FILES([Makefile testsuite/Makefile])
 AC_OUTPUT
diff --git a/sim/example-synacor/ChangeLog b/sim/example-synacor/ChangeLog
new file mode 100644
index 000000000000..f4eb00116888
--- /dev/null
+++ b/sim/example-synacor/ChangeLog
@@ -0,0 +1,5 @@
+2021-01-03  Mike Frysinger  <vapier@gentoo.org>
+
+	* configure.ac, interp.c, Makefile.in, README, README.arch-spec,
+	sim-main.c, sim-main.h: New files for example simulator.
+	* aclocal.m4, config.in, configure: Regenerated.
diff --git a/sim/example-synacor/Makefile.in b/sim/example-synacor/Makefile.in
new file mode 100644
index 000000000000..edd77ac801e8
diff --git a/sim/example-synacor/README b/sim/example-synacor/README
new file mode 100644
index 000000000000..93ffd53d5a49
--- /dev/null
+++ b/sim/example-synacor/README
@@ -0,0 +1,15 @@
+= OVERVIEW =
+
+The Synacor Challenge is a fun programming exercise with a number of puzzles
+built into it.  You can find more details about it here:
+https://challenge.synacor.com/
+
+The first puzzle is writing an interpreter for their custom ISA.  This is a
+simulator for that custom CPU.  The CPU is quite basic: it's 16-bit with only
+8 registers and a limited set of instructions.  This means the port will never
+grow new features.  See README.arch-spec for more details.
+
+Implementing it here ends up being quite useful: it acts as a simple constrained
+"real world" example for people who want to implement a new simulator for their
+own architecture.  We demonstrate all the basic fundamentals (registers, memory,
+branches, and tracing) that all ports should have.
diff --git a/sim/example-synacor/README.arch-spec b/sim/example-synacor/README.arch-spec
new file mode 100644
index 000000000000..da2d9d003670
--- /dev/null
+++ b/sim/example-synacor/README.arch-spec
@@ -0,0 +1,73 @@
+== architecture ==
+- three storage regions
+  - memory with 15-bit address space storing 16-bit values
+  - eight registers
+  - an unbounded stack which holds individual 16-bit values
+- all numbers are unsigned integers 0..32767 (15-bit)
+- all math is modulo 32768; 32758 + 15 => 5
+
+== binary format ==
+- each number is stored as a 16-bit little-endian pair (low byte, high byte)
+- numbers 0..32767 mean a literal value
+- numbers 32768..32775 instead mean registers 0..7
+- numbers 32776..65535 are invalid
+- programs are loaded into memory starting at address 0
+- address 0 is the first 16-bit value, address 1 is the second 16-bit value, etc
+
+== execution ==
+- After an operation is executed, the next instruction to read is immediately after the last argument of the current operation.
+  If a jump was performed, the next operation is instead the exact destination of the jump.
+- Encountering a register as an operation argument should be taken as reading from the register or setting into the register as appropriate.
+
+== hints ==
+- Start with operations 0, 19, and 21.
+- Here's a code for the challenge website: jTTockJlJiOC
+- The program "9,32768,32769,4,19,32768" occupies six memory addresses and should:
+  - Store into register 0 the sum of 4 and the value contained in register 1.
+  - Output to the terminal the character with the ascii code contained in register 0.
+
+== opcode listing ==
+halt: 0
+  stop execution and terminate the program
+set: 1 a b
+  set register <a> to the value of <b>
+push: 2 a
+  push <a> onto the stack
+pop: 3 a
+  remove the top element from the stack and write it into <a>; empty stack = error
+eq: 4 a b c
+  set <a> to 1 if <b> is equal to <c>; set it to 0 otherwise
+gt: 5 a b c
+  set <a> to 1 if <b> is greater than <c>; set it to 0 otherwise
+jmp: 6 a
+  jump to <a>
+jt: 7 a b
+  if <a> is nonzero, jump to <b>
+jf: 8 a b
+  if <a> is zero, jump to <b>
+add: 9 a b c
+  assign into <a> the sum of <b> and <c> (modulo 32768)
+mult: 10 a b c
+  store into <a> the product of <b> and <c> (modulo 32768)
+mod: 11 a b c
+  store into <a> the remainder of <b> divided by <c>
+and: 12 a b c
+  stores into <a> the bitwise and of <b> and <c>
+or: 13 a b c
+  stores into <a> the bitwise or of <b> and <c>
+not: 14 a b
+  stores 15-bit bitwise inverse of <b> in <a>
+rmem: 15 a b
+  read memory at address <b> and write it to <a>
+wmem: 16 a b
+  write the value from <b> into memory at address <a>
+call: 17 a
+  write the address of the next instruction to the stack and jump to <a>
+ret: 18
+  remove the top element from the stack and jump to it; empty stack = halt
+out: 19 a
+  write the character represented by ascii code <a> to the terminal
+in: 20 a
+  read a character from the terminal and write its ascii code to <a>; it can be assumed that once input starts, it will continue until a newline is encountered; this means that you can safely read whole lines from the keyboard and trust that they will be fully read
+noop: 21
+  no operation
diff --git a/sim/example-synacor/aclocal.m4 b/sim/example-synacor/aclocal.m4
new file mode 100644
index 000000000000..e9f11c775c31
diff --git a/sim/example-synacor/config.in b/sim/example-synacor/config.in
new file mode 100644
index 000000000000..cb5ea1b01c95
diff --git a/sim/example-synacor/configure b/sim/example-synacor/configure
new file mode 100755
index 000000000000..b8b0b4fa349b
diff --git a/sim/example-synacor/configure.ac b/sim/example-synacor/configure.ac
new file mode 100644
index 000000000000..ccb2ba8d7308
--- /dev/null
+++ b/sim/example-synacor/configure.ac
@@ -0,0 +1,10 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_INIT(Makefile.in)
+sinclude(../common/acinclude.m4)
+
+SIM_AC_COMMON
+
+SIM_AC_OPTION_ENDIAN(LITTLE)
+SIM_AC_OPTION_ALIGNMENT(STRICT_ALIGNMENT,STRICT_ALIGNMENT)
+
+SIM_AC_OUTPUT
diff --git a/sim/example-synacor/interp.c b/sim/example-synacor/interp.c
new file mode 100644
index 000000000000..7b831101f809
--- /dev/null
+++ b/sim/example-synacor/interp.c
@@ -0,0 +1,178 @@
+/* Example synacor simulator.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   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/>.  */
+
+/* This file contains the main glue logic between the sim core and the target
+   specific simulator.  Normally this file will be kept small and the target
+   details will live in other files.
+
+   For more specific details on these functions, see the gdb/remote-sim.h
+   header file.  */
+
+#include "config.h"
+
+#include "sim-main.h"
+#include "sim-options.h"
+\f
+/* This function is the main loop.  It should process ticks and decode+execute
+   a single instruction.
+
+   Usually you do not need to change things here.  */
+
+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 (cpu);
+      if (sim_events_tick (sd))
+	sim_events_process (sd);
+    }
+}
+\f
+/* Initialize the simulator from scratch.  This is called once per lifetime of
+   the simulation.  Think of it as a processor reset.
+
+   Usually all cpu-specific setup is handled in the initialize_cpu callback.
+   If you want to do cpu-independent stuff, then it should go at the end (see
+   where memory is initialized).  */
+
+#define DEFAULT_MEM_SIZE (16 * 1024 * 1024)
+
+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 *callback,
+	  struct bfd *abfd, char * const *argv)
+{
+  char c;
+  int i;
+  SIM_DESC sd = sim_state_alloc (kind, callback);
+
+  /* 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;
+    }
+
+  /* XXX: Default to the Virtual environment.  */
+  if (STATE_ENVIRONMENT (sd) == ALL_ENVIRONMENT)
+    STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT;
+
+  /* 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;
+    }
+
+  /* Check for/establish the a reference program image.  */
+  if (sim_analyze_program (sd,
+			   (STATE_PROG_ARGV (sd) != NULL
+			    ? *STATE_PROG_ARGV (sd)
+			    : NULL), abfd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Establish any remaining 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);
+
+      initialize_cpu (sd, cpu);
+    }
+
+  /* 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_commandf (sd, "memory-size %#x", DEFAULT_MEM_SIZE);
+
+  return sd;
+}
+\f
+/* Prepare to run a program that has already been loaded into memory.
+
+   Usually you do not need to change things here.  */
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd, struct bfd *abfd,
+		     char * const *argv, char * const *env)
+{
+  SIM_CPU *cpu = STATE_CPU (sd, 0);
+  sim_cia addr;
+
+  /* Set the PC.  */
+  if (abfd != NULL)
+    addr = bfd_get_start_address (abfd);
+  else
+    addr = 0;
+  sim_pc_set (cpu, addr);
+
+  /* Standalone mode (i.e. `run`) will take care of the argv for us in
+     sim_open() -> sim_parse_args().  But in debug mode (i.e. 'target sim'
+     with `gdb`), we need to handle it because the user can change the
+     argv on the fly via gdb's 'run'.  */
+  if (STATE_PROG_ARGV (sd) != argv)
+    {
+      freeargv (STATE_PROG_ARGV (sd));
+      STATE_PROG_ARGV (sd) = dupargv (argv);
+    }
+
+  return SIM_RC_OK;
+}
diff --git a/sim/example-synacor/sim-main.c b/sim/example-synacor/sim-main.c
new file mode 100644
index 000000000000..a7d748f3022c
--- /dev/null
+++ b/sim/example-synacor/sim-main.c
@@ -0,0 +1,530 @@
+/* Example synacor simulator.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   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/>.  */
+
+/* This file contains the main simulator decoding logic.  i.e. everything that
+   is architecture specific.  */
+
+#include "config.h"
+
+#include "sim-main.h"
+\f
+/* Get the register number from the number.  */
+static unsigned16
+register_num (SIM_CPU *cpu, unsigned16 num)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+
+  if (num < 0x8000 || num >= 0x8008)
+    sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+
+  return num & 0xf;
+}
+
+/* Helper to process immediates according to the ISA.  */
+static unsigned16
+interp_num (SIM_CPU *cpu, unsigned16 num)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+
+  if (num < 0x8000)
+    {
+      /* Numbers 0..32767 mean a literal value.  */
+      TRACE_DECODE (cpu, "%#x is a literal", num);
+      return num;
+    }
+  else if (num < 0x8008)
+    {
+      /* Numbers 32768..32775 instead mean registers 0..7.  */
+      TRACE_DECODE (cpu, "%#x is register R%i", num, num & 0xf);
+      return cpu->regs[num & 0xf];
+    }
+  else
+    {
+      /* Numbers 32776..65535 are invalid.  */
+      TRACE_DECODE (cpu, "%#x is an invalid number", num);
+      sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled, SIM_SIGILL);
+    }
+}
+\f
+/* Decode & execute a single instruction.  */
+void step_once (SIM_CPU *cpu)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  unsigned16 iw1, num1;
+  sim_cia pc = sim_pc_get (cpu);
+
+  iw1 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc);
+  TRACE_EXTRACT (cpu, "%04x: iw1: %#x", pc, iw1);
+  /* This never happens, but technically is possible in the ISA.  */
+  num1 = interp_num (cpu, iw1);
+
+  if (num1 == 0)
+    {
+      /* halt: 0: Stop execution and terminate the program.  */
+      TRACE_INSN (cpu, "HALT");
+      sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
+    }
+  else if (num1 == 1)
+    {
+      /* set: 1 a b: Set register <a> to the value of <b>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      TRACE_EXTRACT (cpu, "SET %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "SET R%i %#x", num2, num3);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, num3);
+      cpu->regs[num2] = num3;
+
+      pc += 6;
+    }
+  else if (num1 == 2)
+    {
+      /* push: 2 a: Push <a> onto the stack.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "PUSH %#x", iw2);
+      TRACE_INSN (cpu, "PUSH %#x", num2);
+
+      sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, num2);
+      cpu->sp -= 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+
+      pc += 4;
+    }
+  else if (num1 == 3)
+    {
+      /* pop: 3 a: Remove the top element from the stack and write it into <a>.
+	 Empty stack = error.  */
+      unsigned16 iw2, num2, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "POP %#x", iw2);
+      TRACE_INSN (cpu, "POP R%i", num2);
+      cpu->sp += 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+      result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 4;
+    }
+  else if (num1 == 4)
+    {
+      /* eq: 4 a b c: Set <a> to 1 if <b> is equal to <c>; set it to 0
+	 otherwise.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 == num4);
+      TRACE_EXTRACT (cpu, "EQ %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "EQ R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x == %#x) = %i", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 5)
+    {
+      /* gt: 5 a b c: Set <a> to 1 if <b> is greater than <c>; set it to 0
+	 otherwise.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 > num4);
+      TRACE_EXTRACT (cpu, "GT %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "GT R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x > %#x) = %i", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 6)
+    {
+      /* jmp: 6 a: Jump to <a>.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      /* Addresses are 16-bit aligned.  */
+      num2 <<= 1;
+      TRACE_EXTRACT (cpu, "JMP %#x", iw2);
+      TRACE_INSN (cpu, "JMP %#x", num2);
+
+      pc = num2;
+      TRACE_BRANCH (cpu, "JMP %#x", pc);
+    }
+  else if (num1 == 7)
+    {
+      /* jt: 7 a b: If <a> is nonzero, jump to <b>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num3 <<= 1;
+      TRACE_EXTRACT (cpu, "JT %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "JT %#x %#x", num2, num3);
+      TRACE_DECODE (cpu, "JT %#x != 0 -> %s", num2, num2 ? "taken" : "nop");
+
+      if (num2)
+	{
+	  pc = num3;
+	  TRACE_BRANCH (cpu, "JT %#x", pc);
+	}
+      else
+	pc += 6;
+    }
+  else if (num1 == 8)
+    {
+      /* jf: 8 a b: If <a> is zero, jump to <b>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num3 <<= 1;
+      TRACE_EXTRACT (cpu, "JF %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "JF %#x %#x", num2, num3);
+      TRACE_DECODE (cpu, "JF %#x == 0 -> %s", num2, num2 ? "nop" : "taken");
+
+      if (!num2)
+	{
+	  pc = num3;
+	  TRACE_BRANCH (cpu, "JF %#x", pc);
+	}
+      else
+	pc += 6;
+    }
+  else if (num1 == 9)
+    {
+      /* add: 9 a b c: Assign <a> the sum of <b> and <c> (modulo 32768).  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 + num4) % 32768;
+      TRACE_EXTRACT (cpu, "ADD %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "ADD R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x + %#x) %% %i = %#x", num2, num3, num4,
+		    32768, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 10)
+    {
+      /* mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo
+	 32768).  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 * num4) % 32768;
+      TRACE_EXTRACT (cpu, "MULT %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "MULT R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = (%#x * %#x) %% %i = %#x", num2, num3, num4,
+		    32768, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 11)
+    {
+      /* mod: 11 a b c: Store into <a> the remainder of <b> divided by <c>.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = num3 % num4;
+      TRACE_EXTRACT (cpu, "MOD %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "MOD R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = %#x %% %#x = %#x", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 12)
+    {
+      /* and: 12 a b c: Stores into <a> the bitwise and of <b> and <c>.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 & num4);
+      TRACE_EXTRACT (cpu, "AND %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "AND R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = %#x & %#x = %#x", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 13)
+    {
+      /* or: 13 a b c: Stores into <a> the bitwise or of <b> and <c>.  */
+      unsigned16 iw2, iw3, iw4, num2, num3, num4, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6);
+      num4 = interp_num (cpu, iw4);
+      result = (num3 | num4);
+      TRACE_EXTRACT (cpu, "OR %#x %#x %#x", iw2, iw3, iw4);
+      TRACE_INSN (cpu, "OR R%i %#x %#x", num2, num3, num4);
+      TRACE_DECODE (cpu, "R%i = %#x | %#x = %#x", num2, num3, num4, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 8;
+    }
+  else if (num1 == 14)
+    {
+      /* not: 14 a b: Stores 15-bit bitwise inverse of <b> in <a>.  */
+      unsigned16 iw2, iw3, num2, num3, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      result = (~num3) & 0x7fff;
+      TRACE_EXTRACT (cpu, "NOT %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "NOT R%i %#x", num2, num3);
+      TRACE_DECODE (cpu, "R%i = (~%#x) & 0x7fff = %#x", num2, num3, result);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 6;
+    }
+  else if (num1 == 15)
+    {
+      /* rmem: 15 a b: Read memory at address <b> and write it to <a>.  */
+      unsigned16 iw2, iw3, num2, num3, result;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num3 <<= 1;
+      TRACE_EXTRACT (cpu, "RMEM %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "RMEM R%i %#x", num2, num3);
+
+      TRACE_MEMORY (cpu, "reading %#x", num3);
+      result = sim_core_read_aligned_2 (cpu, pc, read_map, num3);
+
+      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
+      cpu->regs[num2] = result;
+
+      pc += 6;
+    }
+  else if (num1 == 16)
+    {
+      /* wmem: 16 a b: Write the value from <b> into memory at address <a>.  */
+      unsigned16 iw2, iw3, num2, num3;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4);
+      num3 = interp_num (cpu, iw3);
+      /* Addresses are 16-bit aligned.  */
+      num2 <<= 1;
+      TRACE_EXTRACT (cpu, "WMEM %#x %#x", iw2, iw3);
+      TRACE_INSN (cpu, "WMEM %#x %#x", num2, num3);
+
+      TRACE_MEMORY (cpu, "writing %#x to %#x", num3, num2);
+      sim_core_write_aligned_2 (cpu, pc, write_map, num2, num3);
+
+      pc += 6;
+    }
+  else if (num1 == 17)
+    {
+      /* call: 17 a: Write the address of the next instruction to the stack and
+	 jump to <a>.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      /* Addresses are 16-bit aligned.  */
+      num2 <<= 1;
+      TRACE_EXTRACT (cpu, "CALL %#x", iw2);
+      TRACE_INSN (cpu, "CALL %#x", num2);
+
+      TRACE_MEMORY (cpu, "pushing %#x onto stack", (pc + 4) >> 1);
+      sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, (pc + 4) >> 1);
+      cpu->sp -= 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+
+      pc = num2;
+      TRACE_BRANCH (cpu, "CALL %#x", pc);
+    }
+  else if (num1 == 18)
+    {
+      /* ret: 18: Remove the top element from the stack and jump to it; empty
+	 stack = halt.  */
+      unsigned16 result;
+
+      TRACE_INSN (cpu, "RET");
+      cpu->sp += 2;
+      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp);
+      result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp);
+      TRACE_MEMORY (cpu, "popping %#x off of stack", result << 1);
+
+      pc = result << 1;
+      TRACE_BRANCH (cpu, "RET -> %#x", pc);
+    }
+  else if (num1 == 19)
+    {
+      /* out: 19 a: Write the character <a> to the terminal.  */
+      unsigned16 iw2, num2;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = interp_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "OUT %#x", iw2);
+      TRACE_INSN (cpu, "OUT %#x", num2);
+      TRACE_EVENTS (cpu, "write to stdout: %#x (%c)", num2, num2);
+
+      sim_io_printf (sd, "%c", num2);
+
+      pc += 4;
+    }
+  else if (num1 == 20)
+    {
+      /* in: 20 a: read a character from the terminal and write its ascii code
+	 to <a>.  It can be assumed that once input starts, it will continue
+	 until a newline is encountered.  This means that you can safely read
+	 lines from the keyboard and trust that they will be fully read.  */
+      unsigned16 iw2, num2;
+      char c;
+
+      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2);
+      num2 = register_num (cpu, iw2);
+      TRACE_EXTRACT (cpu, "IN %#x", iw2);
+      TRACE_INSN (cpu, "IN %#x", num2);
+      sim_io_read_stdin (sd, &c, 1);
+      TRACE_EVENTS (cpu, "read from stdin: %#x (%c)", c, c);
+
+      /* The challenge uses lowercase for all inputs, so insert some low level
+	 helpers of our own to make it a bit nicer.  */
+      switch (c)
+	{
+	case 'Q':
+	  sim_engine_halt (sd, cpu, NULL, pc, sim_exited, 0);
+	  break;
+	}
+
+      TRACE_REGISTER (cpu, "R%i = %#x", iw2 & 0xf, c);
+      cpu->regs[iw2 & 0xf] = c;
+
+      pc += 4;
+    }
+  else if (num1 == 21)
+    {
+      /* noop: 21: no operation */
+      TRACE_INSN (cpu, "NOOP");
+
+      pc += 2;
+    }
+  else
+    sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL);
+
+  TRACE_REGISTER (cpu, "PC = %#x", pc);
+  sim_pc_set (cpu, pc);
+}
+\f
+/* Return the program counter for this cpu. */
+static sim_cia
+pc_get (sim_cpu *cpu)
+{
+  return cpu->pc;
+}
+
+/* Set the program counter for this cpu to the new pc value. */
+static void
+pc_set (sim_cpu *cpu, sim_cia pc)
+{
+  cpu->pc = pc;
+}
+
+/* Initialize the state for a single cpu.  Usuaully this involves clearing all
+   registers back to their reset state.  Should also hook up the fetch/store
+   helper functions too.  */
+void initialize_cpu (SIM_DESC sd, SIM_CPU *cpu)
+{
+  memset (cpu->regs, 0, sizeof (cpu->regs));
+  cpu->pc = 0;
+  /* Make sure it's initialized outside of the 16-bit address space.  */
+  cpu->sp = 0x80000;
+
+  CPU_PC_FETCH (cpu) = pc_get;
+  CPU_PC_STORE (cpu) = pc_set;
+}
diff --git a/sim/example-synacor/sim-main.h b/sim/example-synacor/sim-main.h
new file mode 100644
index 000000000000..ba0e4efa12a9
--- /dev/null
+++ b/sim/example-synacor/sim-main.h
@@ -0,0 +1,49 @@
+/* Example synacor simulator.
+
+   Copyright (C) 2005-2021 Free Software Foundation, Inc.
+   Contributed by Mike Frysinger.
+
+   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"
+#include "sim-base.h"
+
+struct _sim_cpu {
+  /* ... simulator specific members ... */
+  unsigned16 regs[8];
+  sim_cia pc;
+
+  /* This isn't a real register, and the stack is not directly addressable,
+     so use memory outside of the 16-bit address space.  */
+  unsigned32 sp;
+
+  sim_cpu_base base;
+};
+
+struct sim_state {
+  sim_cpu *cpu[MAX_NR_PROCESSORS];
+
+  /* ... simulator specific members ... */
+  sim_state_base base;
+};
+
+extern void step_once (SIM_CPU *);
+extern void initialize_cpu (SIM_DESC, SIM_CPU *);
+
+#endif
diff --git a/sim/testsuite/example-synacor/ChangeLog b/sim/testsuite/example-synacor/ChangeLog
new file mode 100644
index 000000000000..58295bb38fa0
--- /dev/null
+++ b/sim/testsuite/example-synacor/ChangeLog
@@ -0,0 +1,5 @@
+2021-01-13  Mike Frysinger  <vapier@gentoo.org>
+
+	* add.s, allinsn.exp, and.s, call.s, exit-0.s, gt.s, isa.inc, jmp.s,
+	mem.s, mod.s, mult.s, not.s, or.s, push-pop.s, ret.s, set.s,
+	testutils.inc: New files.
diff --git a/sim/testsuite/example-synacor/add.s b/sim/testsuite/example-synacor/add.s
new file mode 100644
index 000000000000..64a8c20d8edc
--- /dev/null
+++ b/sim/testsuite/example-synacor/add.s
@@ -0,0 +1,24 @@
+# check the ADD insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	SET r2, 2
+	ADD r2, r2, r2
+	EQ r3, r2, 4
+	JF r3, 2
+
+	ADD r1, 100, r2
+	EQ r4, r1, 104
+	JF r4, 2
+
+	# 0x7ffe == -2
+	ADD r0, r1, 0x7ffe
+	EQ r4, r0, 102
+	JF r4, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/allinsn.exp b/sim/testsuite/example-synacor/allinsn.exp
new file mode 100644
index 000000000000..a73312b8adfb
--- /dev/null
+++ b/sim/testsuite/example-synacor/allinsn.exp
@@ -0,0 +1,15 @@
+# Example synacor simulator testsuite.
+
+if [istarget *] {
+    # All machines.
+    set all_machs "example"
+
+    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/example-synacor/and.s b/sim/testsuite/example-synacor/and.s
new file mode 100644
index 000000000000..1d1885985590
--- /dev/null
+++ b/sim/testsuite/example-synacor/and.s
@@ -0,0 +1,18 @@
+# check the AND insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	AND r2, 0xfff, 0x7f0c
+	EQ r3, r2, 0xf0c
+	JF r3, 2
+
+	AND r2, r2, 0xf
+	EQ r3, r2, 0xc
+	JF r3, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/call.s b/sim/testsuite/example-synacor/call.s
new file mode 100644
index 000000000000..6d7b545a14bc
--- /dev/null
+++ b/sim/testsuite/example-synacor/call.s
@@ -0,0 +1,14 @@
+# check the CALL insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	CALL 3
+	HALT
+
+	POP r0
+	EQ r1, r0, 2
+	JF r1, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/exit-0.s b/sim/testsuite/example-synacor/exit-0.s
new file mode 100644
index 000000000000..2c1fb72f5d7c
--- /dev/null
+++ b/sim/testsuite/example-synacor/exit-0.s
@@ -0,0 +1,10 @@
+# check that the sim doesn't die immediately.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	NOOP
+	NOOP
+	NOOP
+	pass
diff --git a/sim/testsuite/example-synacor/gt.s b/sim/testsuite/example-synacor/gt.s
new file mode 100644
index 000000000000..aef28e34404c
--- /dev/null
+++ b/sim/testsuite/example-synacor/gt.s
@@ -0,0 +1,31 @@
+# check the GT insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	GT r0, 3, 2
+	EQ r1, r0, 1
+	JF r1, 2
+
+	GT r0, 2, 2
+	EQ r1, r0, 0
+	JF r1, 2
+
+	GT r0, 1, 2
+	EQ r1, r0, 0
+	JF r1, 2
+
+	SET r2, 3
+	SET r3, 4
+	GT r0, r2, r3
+	EQ r1, r0, 0
+	JF r1, 2
+	GT r0, r3, r2
+	EQ r1, r0, 1
+	JF r1, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/isa.inc b/sim/testsuite/example-synacor/isa.inc
new file mode 100644
index 000000000000..e2e1136ecfb7
--- /dev/null
+++ b/sim/testsuite/example-synacor/isa.inc
@@ -0,0 +1,108 @@
+# Macros for the fake ISA.  Keep in sync with example-synacor/README.arch-spec.
+
+# These .set macros will generate symbols in the output ELF, but it also allows
+# use to use them as arguments to the insns below.  Oh well.
+.set r0, 32768
+.set r1, r0+1
+.set r2, r0+2
+.set r3, r0+3
+.set r4, r0+4
+.set r5, r0+5
+.set r6, r0+6
+.set r7, r0+7
+
+# The target is little endian, so make sure we output the 16-bit words as such.
+.macro _op op:req, more:vararg
+	.byte \op & 0xff, (\op >> 8) & 0xff
+	.ifnb \more
+	_op \more
+	.endif
+.endm
+
+.macro HALT
+	_op 0
+.endm
+
+.macro SET a:req, b:req
+	_op 1, \a, \b
+.endm
+
+.macro PUSH a:req
+	_op 2, \a
+.endm
+
+.macro POP a:req
+	_op 3, \a
+.endm
+
+.macro EQ a:req, b:req, c:req
+	_op 4, \a, \b, \c
+.endm
+
+.macro GT a:req, b:req, c:req
+	_op 5, \a, \b, \c
+.endm
+
+.macro JMP a:req
+	_op 6, \a
+.endm
+
+.macro JT a:req, b:req
+	_op 7, \a, \b
+.endm
+
+.macro JF a:req, b:req
+	_op 8, \a, \b
+.endm
+
+.macro ADD a:req, b:req, c:req
+	_op 9, \a, \b, \c
+.endm
+
+.macro MULT a:req, b:req, c:req
+	_op 10, \a, \b, \c
+.endm
+
+.macro MOD a:req, b:req, c:req
+	_op 11, \a, \b, \c
+.endm
+
+.macro AND a:req, b:req, c:req
+	_op 12, \a, \b, \c
+.endm
+
+.macro OR a:req, b:req, c:req
+	_op 13, \a, \b, \c
+.endm
+
+.macro NOT a:req, b:req
+	_op 14, \a, \b
+.endm
+
+.macro RMEM a:req, b:req
+	_op 15, \a, \b
+.endm
+
+.macro WMEM a:req, b:req
+	_op 16, \a, \b
+.endm
+
+.macro CALL a:req
+	_op 17, \a
+.endm
+
+.macro RET
+	_op 18
+.endm
+
+.macro OUT a:req
+	_op 19, \a
+.endm
+
+.macro IN a:req
+	_op 20, \a
+.endm
+
+.macro NOOP
+	_op 21
+.endm
diff --git a/sim/testsuite/example-synacor/jmp.s b/sim/testsuite/example-synacor/jmp.s
new file mode 100644
index 000000000000..dcacf668adc4
--- /dev/null
+++ b/sim/testsuite/example-synacor/jmp.s
@@ -0,0 +1,9 @@
+# check the JMP insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+	pass
diff --git a/sim/testsuite/example-synacor/mem.s b/sim/testsuite/example-synacor/mem.s
new file mode 100644
index 000000000000..24aa0a9e6fc6
--- /dev/null
+++ b/sim/testsuite/example-synacor/mem.s
@@ -0,0 +1,25 @@
+# check the RMEM & WMEM insns.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 14
+	HALT
+	pass
+
+	# Read a constant address.
+	RMEM r0, 1
+	EQ r1, r0, 14
+	JF r1, 2
+
+	# Change the first JMP to skip HALT and hit the pass.
+	WMEM 1, 3
+
+	# Read an address in a register.
+	SET r2, 1
+	RMEM r0, r2
+	EQ r1, r0, 3
+	JF r1, 2
+
+	JMP 0
diff --git a/sim/testsuite/example-synacor/mod.s b/sim/testsuite/example-synacor/mod.s
new file mode 100644
index 000000000000..f0b4217574c5
--- /dev/null
+++ b/sim/testsuite/example-synacor/mod.s
@@ -0,0 +1,18 @@
+# check the MOD insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	MOD r0, 8, 3
+	EQ r1, r0, 2
+	JF r1, 2
+
+	MOD r0, r0, 2
+	EQ r1, r0, 0
+	JF r1, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/mult.s b/sim/testsuite/example-synacor/mult.s
new file mode 100644
index 000000000000..d323cf53aa77
--- /dev/null
+++ b/sim/testsuite/example-synacor/mult.s
@@ -0,0 +1,18 @@
+# check the MULT insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	MULT r0, 3, 2
+	EQ r1, r0, 6
+	JF r1, 2
+
+	MULT r0, r0, 8
+	EQ r1, r0, 48
+	JF r1, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/not.s b/sim/testsuite/example-synacor/not.s
new file mode 100644
index 000000000000..8a4a570ce6e0
--- /dev/null
+++ b/sim/testsuite/example-synacor/not.s
@@ -0,0 +1,15 @@
+# check the NOT insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	SET r2, 0xc
+	NOT r0, r2
+	EQ r3, r0, 0x7ff3
+	JF r3, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/or.s b/sim/testsuite/example-synacor/or.s
new file mode 100644
index 000000000000..f8f2ae3a1539
--- /dev/null
+++ b/sim/testsuite/example-synacor/or.s
@@ -0,0 +1,18 @@
+# check the OR insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	OR r2, 0xf, 0x80
+	EQ r3, r2, 0x8f
+	JF r3, 2
+
+	OR r2, r2, 0xff
+	EQ r3, r2, 0xff
+	JF r3, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/push-pop.s b/sim/testsuite/example-synacor/push-pop.s
new file mode 100644
index 000000000000..b8199c5d34e4
--- /dev/null
+++ b/sim/testsuite/example-synacor/push-pop.s
@@ -0,0 +1,22 @@
+# check the PUSH & POP insns.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	PUSH 1
+	SET r0, 3
+	PUSH r0
+	POP r1
+	POP r2
+	EQ r7, r0, 3
+	JF r7, 2
+	EQ r7, r1, 3
+	JF r7, 2
+	EQ r7, r2, 1
+	JF r7, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/ret.s b/sim/testsuite/example-synacor/ret.s
new file mode 100644
index 000000000000..63bfa7132cbe
--- /dev/null
+++ b/sim/testsuite/example-synacor/ret.s
@@ -0,0 +1,13 @@
+# check the RET insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 13
+	pass
+
+	SET r5, 2
+	PUSH r5
+	RET
+	HALT
diff --git a/sim/testsuite/example-synacor/set.s b/sim/testsuite/example-synacor/set.s
new file mode 100644
index 000000000000..8b441c762eaa
--- /dev/null
+++ b/sim/testsuite/example-synacor/set.s
@@ -0,0 +1,20 @@
+# check the SET insn.
+# mach: example
+
+.include "testutils.inc"
+
+	start
+	JMP 3
+	HALT
+
+	SET r2, 2
+	EQ r3, r2, 2
+	JF r3, 2
+	SET r1, 1
+	EQ r3, r1, 1
+	JF r3, 2
+	SET r0, r2
+	EQ r3, r0, 2
+	JF r3, 2
+
+	pass
diff --git a/sim/testsuite/example-synacor/testutils.inc b/sim/testsuite/example-synacor/testutils.inc
new file mode 100644
index 000000000000..0f286c6036ca
--- /dev/null
+++ b/sim/testsuite/example-synacor/testutils.inc
@@ -0,0 +1,31 @@
+.include "isa.inc"
+
+# MACRO: pass
+# Write 'pass' to stdout and quit
+	.macro pass
+	OUT 'p'
+	OUT 'a'
+	OUT 's'
+	OUT 's'
+	OUT '\n'
+	HALT
+	.endm
+
+# MACRO: fail
+# Write 'fail' to stdout and quit
+	.macro fail
+	OUT 'f'
+	OUT 'a'
+	OUT 'i'
+	OUT 'l'
+	OUT '\n'
+	HALT
+	.endm
+
+# MACRO: start
+# All assembler tests should start with a call to "start"
+	.macro start
+	.text
+.global _start
+_start:
+	.endm
diff --git a/sim/testsuite/lib/sim-defs.exp b/sim/testsuite/lib/sim-defs.exp
index 43a07050f508..04ab4685612b 100644
--- a/sim/testsuite/lib/sim-defs.exp
+++ b/sim/testsuite/lib/sim-defs.exp
@@ -378,6 +378,13 @@ proc run_sim_test { name requested_machs } {
 	    set options "$options timeout=$opts(timeout)"
 	}
 
+	if [string match "example" "$mach"] {
+	    set objcopy [find_binutils_prog objcopy]
+	    set comp_output [remote_exec host $objcopy "-O binary -j .text ${name}.x ${name}.bin"]
+	    file rename -force "${name}.bin" "${name}.x"
+	    append opts(sim,$mach) " --target binary"
+	}
+
 	set result [sim_run ${name}.x "$opts(sim,$mach) $global_sim_options" "$opts(progopts)" "" "$options"]
 	set return_code [lindex $result 0]
 	set output [lindex $result 1]
-- 
2.28.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] sim: example-synacor: a simple implementation for reference
  2021-01-19  4:05 ` [PATCH v2] " Mike Frysinger
@ 2021-02-04 10:57   ` Jeremy Bennett
  2021-02-04 22:50     ` Mike Frysinger
  0 siblings, 1 reply; 4+ messages in thread
From: Jeremy Bennett @ 2021-02-04 10:57 UTC (permalink / raw)
  To: gdb-patches

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 19/01/2021 04:05, Mike Frysinger via Gdb-patches wrote:
> Provide a simple example simulator for people porting to new
> targets to use as a reference.  This one has the advantage of being
> used by people and having a fun program available for it.

Hi Mike,

Good to see your proposal and the lack of a RISC-V simulator being
addressed again.

There has been a patch for a full CGEN based simulator for RISC-V
waiting to complete review since late 2019. Here is the thread which
starts with the CPU description and then has the simulator port. It is
configured to only generate the simulator, not the assembler/disassembler.

https://sourceware.org/pipermail/binutils/2019-September/108051.html

The patch is derived from a CGEN port which is actively used out of
tree, and supports all the ratified RISC-V extensions (the out of tree
version also supports the unratified bitmanip extension).

Would it be more useful to pursue the CGEN based simulator, giving
more flexibility for development as new extensions are ratified. I can
ask Ed Jones to resubmit his patch, rebased on the current
top-of-tree. We'd really appreciate your help in getting this patch
adopted.

Best wishes,


Jeremy

>
> It doesn't require a special target -- the example simulators can
> be built for any existing port. --- sim/ChangeLog
> |     5 + sim/configure                               |    15 +-
> sim/configure.ac                            |     7 +
> sim/example-synacor/ChangeLog               |     5 +
> sim/example-synacor/Makefile.in             |    26 +
> sim/example-synacor/README                  |    15 +
> sim/example-synacor/README.arch-spec        |    73 +
> sim/example-synacor/aclocal.m4              |   119 +
> sim/example-synacor/config.in               |   242 +
> sim/example-synacor/configure               | 16029
> ++++++++++++++++++ sim/example-synacor/configure.ac            |
> 10 + sim/example-synacor/interp.c                |   178 +
> sim/example-synacor/sim-main.c              |   530 +
> sim/example-synacor/sim-main.h              |    49 +
> sim/testsuite/example-synacor/ChangeLog     |     5 +
> sim/testsuite/example-synacor/add.s         |    24 +
> sim/testsuite/example-synacor/allinsn.exp   |    15 +
> sim/testsuite/example-synacor/and.s         |    18 +
> sim/testsuite/example-synacor/call.s        |    14 +
> sim/testsuite/example-synacor/exit-0.s      |    10 +
> sim/testsuite/example-synacor/gt.s          |    31 +
> sim/testsuite/example-synacor/isa.inc       |   108 +
> sim/testsuite/example-synacor/jmp.s         |     9 +
> sim/testsuite/example-synacor/mem.s         |    25 +
> sim/testsuite/example-synacor/mod.s         |    18 +
> sim/testsuite/example-synacor/mult.s        |    18 +
> sim/testsuite/example-synacor/not.s         |    15 +
> sim/testsuite/example-synacor/or.s          |    18 +
> sim/testsuite/example-synacor/push-pop.s    |    22 +
> sim/testsuite/example-synacor/ret.s         |    13 +
> sim/testsuite/example-synacor/set.s         |    20 +
> sim/testsuite/example-synacor/testutils.inc |    31 +
> sim/testsuite/lib/sim-defs.exp              |     7 + 33 files
> changed, 17723 insertions(+), 1 deletion(-) create mode 100644
> sim/example-synacor/ChangeLog create mode 100644
> sim/example-synacor/Makefile.in create mode 100644
> sim/example-synacor/README create mode 100644
> sim/example-synacor/README.arch-spec create mode 100644
> sim/example-synacor/aclocal.m4 create mode 100644
> sim/example-synacor/config.in create mode 100755
> sim/example-synacor/configure create mode 100644
> sim/example-synacor/configure.ac create mode 100644
> sim/example-synacor/interp.c create mode 100644
> sim/example-synacor/sim-main.c create mode 100644
> sim/example-synacor/sim-main.h create mode 100644
> sim/testsuite/example-synacor/ChangeLog create mode 100644
> sim/testsuite/example-synacor/add.s create mode 100644
> sim/testsuite/example-synacor/allinsn.exp create mode 100644
> sim/testsuite/example-synacor/and.s create mode 100644
> sim/testsuite/example-synacor/call.s create mode 100644
> sim/testsuite/example-synacor/exit-0.s create mode 100644
> sim/testsuite/example-synacor/gt.s create mode 100644
> sim/testsuite/example-synacor/isa.inc create mode 100644
> sim/testsuite/example-synacor/jmp.s create mode 100644
> sim/testsuite/example-synacor/mem.s create mode 100644
> sim/testsuite/example-synacor/mod.s create mode 100644
> sim/testsuite/example-synacor/mult.s create mode 100644
> sim/testsuite/example-synacor/not.s create mode 100644
> sim/testsuite/example-synacor/or.s create mode 100644
> sim/testsuite/example-synacor/push-pop.s create mode 100644
> sim/testsuite/example-synacor/ret.s create mode 100644
> sim/testsuite/example-synacor/set.s create mode 100644
> sim/testsuite/example-synacor/testutils.inc
>
> diff --git a/sim/ChangeLog b/sim/ChangeLog index
> 7b83115d9c5c..bd85fc1c2a64 100644 --- a/sim/ChangeLog +++
> b/sim/ChangeLog @@ -1,3 +1,8 @@ +2021-01-03  Mike Frysinger
> <vapier@gentoo.org> + +	* configure.ac: Add --example-sims option.
> +	* configure: Regenerate. + 2021-01-18  Mike Frysinger
> <vapier@gentoo.org>
>
> * configure.ac: Call AC_PROG_CPP. diff --git a/sim/configure.ac
> b/sim/configure.ac index 99364cac7f4e..cb9c1c9f69ab 100644 ---
> a/sim/configure.ac +++ b/sim/configure.ac @@ -49,5 +49,12 @@ if
> test "${enable_sim}" != no; then fi fi
>
> +AC_ARG_ENABLE([example-sims], +
> [AC_HELP_STRING([--enable-example-sims], +			      [enable example
> GNU simulators])]) +if test "x${enable_example_sims}" = xyes; then
> +  AC_CONFIG_SUBDIRS(example-synacor) +fi +
> AC_CONFIG_FILES([Makefile testsuite/Makefile]) AC_OUTPUT diff --git
> a/sim/example-synacor/ChangeLog b/sim/example-synacor/ChangeLog new
> file mode 100644 index 000000000000..f4eb00116888 --- /dev/null +++
> b/sim/example-synacor/ChangeLog @@ -0,0 +1,5 @@ +2021-01-03  Mike
> Frysinger  <vapier@gentoo.org> + +	* configure.ac, interp.c,
> Makefile.in, README, README.arch-spec, +	sim-main.c, sim-main.h:
> New files for example simulator. +	* aclocal.m4, config.in,
> configure: Regenerated. diff --git
> a/sim/example-synacor/Makefile.in
> b/sim/example-synacor/Makefile.in new file mode 100644 index
> 000000000000..edd77ac801e8 diff --git a/sim/example-synacor/README
> b/sim/example-synacor/README new file mode 100644 index
> 000000000000..93ffd53d5a49 --- /dev/null +++
> b/sim/example-synacor/README @@ -0,0 +1,15 @@ += OVERVIEW = + +The
> Synacor Challenge is a fun programming exercise with a number of
> puzzles +built into it.  You can find more details about it here:
> +https://challenge.synacor.com/ + +The first puzzle is writing an
> interpreter for their custom ISA.  This is a +simulator for that
> custom CPU.  The CPU is quite basic: it's 16-bit with only +8
> registers and a limited set of instructions.  This means the port
> will never +grow new features.  See README.arch-spec for more
> details. + +Implementing it here ends up being quite useful: it
> acts as a simple constrained +"real world" example for people who
> want to implement a new simulator for their +own architecture.  We
> demonstrate all the basic fundamentals (registers, memory,
> +branches, and tracing) that all ports should have. diff --git
> a/sim/example-synacor/README.arch-spec
> b/sim/example-synacor/README.arch-spec new file mode 100644 index
> 000000000000..da2d9d003670 --- /dev/null +++
> b/sim/example-synacor/README.arch-spec @@ -0,0 +1,73 @@ +==
> architecture == +- three storage regions +  - memory with 15-bit
> address space storing 16-bit values +  - eight registers +  - an
> unbounded stack which holds individual 16-bit values +- all numbers
> are unsigned integers 0..32767 (15-bit) +- all math is modulo
> 32768; 32758 + 15 => 5 + +== binary format == +- each number is
> stored as a 16-bit little-endian pair (low byte, high byte) +-
> numbers 0..32767 mean a literal value +- numbers 32768..32775
> instead mean registers 0..7 +- numbers 32776..65535 are invalid +-
> programs are loaded into memory starting at address 0 +- address 0
> is the first 16-bit value, address 1 is the second 16-bit value,
> etc + +== execution == +- After an operation is executed, the next
> instruction to read is immediately after the last argument of the
> current operation. +  If a jump was performed, the next operation
> is instead the exact destination of the jump. +- Encountering a
> register as an operation argument should be taken as reading from
> the register or setting into the register as appropriate. + +==
> hints == +- Start with operations 0, 19, and 21. +- Here's a code
> for the challenge website: jTTockJlJiOC +- The program
> "9,32768,32769,4,19,32768" occupies six memory addresses and
> should: +  - Store into register 0 the sum of 4 and the value
> contained in register 1. +  - Output to the terminal the character
> with the ascii code contained in register 0. + +== opcode listing
> == +halt: 0 +  stop execution and terminate the program +set: 1 a
> b +  set register <a> to the value of <b> +push: 2 a +  push <a>
> onto the stack +pop: 3 a +  remove the top element from the stack
> and write it into <a>; empty stack = error +eq: 4 a b c +  set <a>
> to 1 if <b> is equal to <c>; set it to 0 otherwise +gt: 5 a b c +
> set <a> to 1 if <b> is greater than <c>; set it to 0 otherwise
> +jmp: 6 a +  jump to <a> +jt: 7 a b +  if <a> is nonzero, jump to
> <b> +jf: 8 a b +  if <a> is zero, jump to <b> +add: 9 a b c +
> assign into <a> the sum of <b> and <c> (modulo 32768) +mult: 10 a b
> c +  store into <a> the product of <b> and <c> (modulo 32768) +mod:
> 11 a b c +  store into <a> the remainder of <b> divided by <c>
> +and: 12 a b c +  stores into <a> the bitwise and of <b> and <c>
> +or: 13 a b c +  stores into <a> the bitwise or of <b> and <c>
> +not: 14 a b +  stores 15-bit bitwise inverse of <b> in <a> +rmem:
> 15 a b +  read memory at address <b> and write it to <a> +wmem: 16
> a b +  write the value from <b> into memory at address <a> +call:
> 17 a +  write the address of the next instruction to the stack and
> jump to <a> +ret: 18 +  remove the top element from the stack and
> jump to it; empty stack = halt +out: 19 a +  write the character
> represented by ascii code <a> to the terminal +in: 20 a +  read a
> character from the terminal and write its ascii code to <a>; it can
> be assumed that once input starts, it will continue until a newline
> is encountered; this means that you can safely read whole lines
> from the keyboard and trust that they will be fully read +noop: 21
> +  no operation diff --git a/sim/example-synacor/aclocal.m4
> b/sim/example-synacor/aclocal.m4 new file mode 100644 index
> 000000000000..e9f11c775c31 diff --git
> a/sim/example-synacor/config.in b/sim/example-synacor/config.in new
> file mode 100644 index 000000000000..cb5ea1b01c95 diff --git
> a/sim/example-synacor/configure b/sim/example-synacor/configure new
> file mode 100755 index 000000000000..b8b0b4fa349b diff --git
> a/sim/example-synacor/configure.ac
> b/sim/example-synacor/configure.ac new file mode 100644 index
> 000000000000..ccb2ba8d7308 --- /dev/null +++
> b/sim/example-synacor/configure.ac @@ -0,0 +1,10 @@ +dnl Process
> this file with autoconf to produce a configure script.
> +AC_INIT(Makefile.in) +sinclude(../common/acinclude.m4) +
> +SIM_AC_COMMON + +SIM_AC_OPTION_ENDIAN(LITTLE)
> +SIM_AC_OPTION_ALIGNMENT(STRICT_ALIGNMENT,STRICT_ALIGNMENT) +
> +SIM_AC_OUTPUT diff --git a/sim/example-synacor/interp.c
> b/sim/example-synacor/interp.c new file mode 100644 index
> 000000000000..7b831101f809 --- /dev/null +++
> b/sim/example-synacor/interp.c @@ -0,0 +1,178 @@ +/* Example
> synacor simulator. + +   Copyright (C) 2005-2021 Free Software
> Foundation, Inc. +   Contributed by Mike Frysinger. + +   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/>.  */ + +/* This file contains the
> main glue logic between the sim core and the target +   specific
> simulator.  Normally this file will be kept small and the target +
> details will live in other files. + +   For more specific details
> on these functions, see the gdb/remote-sim.h +   header file.  */
> + +#include "config.h" + +#include "sim-main.h" +#include
> "sim-options.h" +\f +/* This function is the main loop.  It should
> process ticks and decode+execute +   a single instruction. + +
> Usually you do not need to change things here.  */ + +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 (cpu); +      if (sim_events_tick (sd)) +
> sim_events_process (sd); +    } +} +\f +/* Initialize the simulator
> from scratch.  This is called once per lifetime of +   the
> simulation.  Think of it as a processor reset. + +   Usually all
> cpu-specific setup is handled in the initialize_cpu callback. +
> If you want to do cpu-independent stuff, then it should go at the
> end (see +   where memory is initialized).  */ + +#define
> DEFAULT_MEM_SIZE (16 * 1024 * 1024) + +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 *callback, +	  struct bfd *abfd, char * const *argv)
> +{ +  char c; +  int i; +  SIM_DESC sd = sim_state_alloc (kind,
> callback); + +  /* 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; +    } + +  /* XXX: Default to the Virtual environment.
> */ +  if (STATE_ENVIRONMENT (sd) == ALL_ENVIRONMENT) +
> STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT; + +  /* 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; +    } + +  /*
> Check for/establish the a reference program image.  */ +  if
> (sim_analyze_program (sd, +			   (STATE_PROG_ARGV (sd) != NULL +
> ? *STATE_PROG_ARGV (sd) +			    : NULL), abfd) != SIM_RC_OK) +
> { +      free_state (sd); +      return 0; +    } + +  /* Establish
> any remaining 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); + +      initialize_cpu
> (sd, cpu); +    } + +  /* 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_commandf (sd, "memory-size
> %#x", DEFAULT_MEM_SIZE); + +  return sd; +} +\f +/* Prepare to run a
> program that has already been loaded into memory. + +   Usually you
> do not need to change things here.  */ + +SIM_RC
> +sim_create_inferior (SIM_DESC sd, struct bfd *abfd, +		     char *
> const *argv, char * const *env) +{ +  SIM_CPU *cpu = STATE_CPU (sd,
> 0); +  sim_cia addr; + +  /* Set the PC.  */ +  if (abfd != NULL) +
> addr = bfd_get_start_address (abfd); +  else +    addr = 0; +
> sim_pc_set (cpu, addr); + +  /* Standalone mode (i.e. `run`) will
> take care of the argv for us in +     sim_open() ->
> sim_parse_args().  But in debug mode (i.e. 'target sim' +     with
> `gdb`), we need to handle it because the user can change the +
> argv on the fly via gdb's 'run'.  */ +  if (STATE_PROG_ARGV (sd) !=
> argv) +    { +      freeargv (STATE_PROG_ARGV (sd)); +
> STATE_PROG_ARGV (sd) = dupargv (argv); +    } + +  return
> SIM_RC_OK; +} diff --git a/sim/example-synacor/sim-main.c
> b/sim/example-synacor/sim-main.c new file mode 100644 index
> 000000000000..a7d748f3022c --- /dev/null +++
> b/sim/example-synacor/sim-main.c @@ -0,0 +1,530 @@ +/* Example
> synacor simulator. + +   Copyright (C) 2005-2021 Free Software
> Foundation, Inc. +   Contributed by Mike Frysinger. + +   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/>.  */ + +/* This file contains the
> main simulator decoding logic.  i.e. everything that +   is
> architecture specific.  */ + +#include "config.h" + +#include
> "sim-main.h" +\f +/* Get the register number from the number.  */
> +static unsigned16 +register_num (SIM_CPU *cpu, unsigned16 num) +{
> +  SIM_DESC sd = CPU_STATE (cpu); + +  if (num < 0x8000 || num >=
> 0x8008) +    sim_engine_halt (sd, cpu, NULL, cpu->pc,
> sim_signalled, SIM_SIGILL); + +  return num & 0xf; +} + +/* Helper
> to process immediates according to the ISA.  */ +static unsigned16
> +interp_num (SIM_CPU *cpu, unsigned16 num) +{ +  SIM_DESC sd =
> CPU_STATE (cpu); + +  if (num < 0x8000) +    { +      /* Numbers
> 0..32767 mean a literal value.  */ +      TRACE_DECODE (cpu, "%#x
> is a literal", num); +      return num; +    } +  else if (num <
> 0x8008) +    { +      /* Numbers 32768..32775 instead mean
> registers 0..7.  */ +      TRACE_DECODE (cpu, "%#x is register
> R%i", num, num & 0xf); +      return cpu->regs[num & 0xf]; +    } +
> else +    { +      /* Numbers 32776..65535 are invalid.  */ +
> TRACE_DECODE (cpu, "%#x is an invalid number", num); +
> sim_engine_halt (sd, cpu, NULL, cpu->pc, sim_signalled,
> SIM_SIGILL); +    } +} +\f +/* Decode & execute a single
> instruction.  */ +void step_once (SIM_CPU *cpu) +{ +  SIM_DESC sd =
> CPU_STATE (cpu); +  unsigned16 iw1, num1; +  sim_cia pc =
> sim_pc_get (cpu); + +  iw1 = sim_core_read_aligned_2 (cpu, pc,
> exec_map, pc); +  TRACE_EXTRACT (cpu, "%04x: iw1: %#x", pc, iw1); +
> /* This never happens, but technically is possible in the ISA.  */
> +  num1 = interp_num (cpu, iw1); + +  if (num1 == 0) +    { +
> /* halt: 0: Stop execution and terminate the program.  */ +
> TRACE_INSN (cpu, "HALT"); +      sim_engine_halt (sd, cpu, NULL,
> pc, sim_exited, 0); +    } +  else if (num1 == 1) +    { +      /*
> set: 1 a b: Set register <a> to the value of <b>.  */ +
> unsigned16 iw2, iw3, num2, num3; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> register_num (cpu, iw2); +      iw3 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 4); +      num3 = interp_num (cpu, iw3); +
> TRACE_EXTRACT (cpu, "SET %#x %#x", iw2, iw3); +      TRACE_INSN
> (cpu, "SET R%i %#x", num2, num3); + +      TRACE_REGISTER (cpu,
> "R%i = %#x", num2, num3); +      cpu->regs[num2] = num3; + +
> pc += 6; +    } +  else if (num1 == 2) +    { +      /* push: 2 a:
> Push <a> onto the stack.  */ +      unsigned16 iw2, num2; + +
> iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +
> num2 = interp_num (cpu, iw2); +      TRACE_EXTRACT (cpu, "PUSH
> %#x", iw2); +      TRACE_INSN (cpu, "PUSH %#x", num2); + +
> sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, num2); +
> cpu->sp -= 2; +      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp); + +
> pc += 4; +    } +  else if (num1 == 3) +    { +      /* pop: 3 a:
> Remove the top element from the stack and write it into <a>. +
> Empty stack = error.  */ +      unsigned16 iw2, num2, result; + +
> iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +
> num2 = register_num (cpu, iw2); +      TRACE_EXTRACT (cpu, "POP
> %#x", iw2); +      TRACE_INSN (cpu, "POP R%i", num2); +
> cpu->sp += 2; +      TRACE_REGISTER (cpu, "SP = %#x", cpu->sp); +
> result = sim_core_read_aligned_2 (cpu, pc, read_map, cpu->sp); + +
> TRACE_REGISTER (cpu, "R%i = %#x", num2, result); +
> cpu->regs[num2] = result; + +      pc += 4; +    } +  else if (num1
> == 4) +    { +      /* eq: 4 a b c: Set <a> to 1 if <b> is equal to
> <c>; set it to 0 +	 otherwise.  */ +      unsigned16 iw2, iw3, iw4,
> num2, num3, num4, result; + +      iw2 = sim_core_read_aligned_2
> (cpu, pc, exec_map, pc + 2); +      num2 = register_num (cpu,
> iw2); +      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc +
> 4); +      num3 = interp_num (cpu, iw3); +      iw4 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6); +      num4 =
> interp_num (cpu, iw4); +      result = (num3 == num4); +
> TRACE_EXTRACT (cpu, "EQ %#x %#x %#x", iw2, iw3, iw4); +
> TRACE_INSN (cpu, "EQ R%i %#x %#x", num2, num3, num4); +
> TRACE_DECODE (cpu, "R%i = (%#x == %#x) = %i", num2, num3, num4,
> result); + +      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
> +      cpu->regs[num2] = result; + +      pc += 8; +    } +  else
> if (num1 == 5) +    { +      /* gt: 5 a b c: Set <a> to 1 if <b> is
> greater than <c>; set it to 0 +	 otherwise.  */ +      unsigned16
> iw2, iw3, iw4, num2, num3, num4, result; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> register_num (cpu, iw2); +      iw3 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 4); +      num3 = interp_num (cpu, iw3); +
> iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6); +
> num4 = interp_num (cpu, iw4); +      result = (num3 > num4); +
> TRACE_EXTRACT (cpu, "GT %#x %#x %#x", iw2, iw3, iw4); +
> TRACE_INSN (cpu, "GT R%i %#x %#x", num2, num3, num4); +
> TRACE_DECODE (cpu, "R%i = (%#x > %#x) = %i", num2, num3, num4,
> result); + +      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
> +      cpu->regs[num2] = result; + +      pc += 8; +    } +  else
> if (num1 == 6) +    { +      /* jmp: 6 a: Jump to <a>.  */ +
> unsigned16 iw2, num2; + +      iw2 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 2); +      num2 = interp_num (cpu, iw2); +
> /* Addresses are 16-bit aligned.  */ +      num2 <<= 1; +
> TRACE_EXTRACT (cpu, "JMP %#x", iw2); +      TRACE_INSN (cpu, "JMP
> %#x", num2); + +      pc = num2; +      TRACE_BRANCH (cpu, "JMP
> %#x", pc); +    } +  else if (num1 == 7) +    { +      /* jt: 7 a
> b: If <a> is nonzero, jump to <b>.  */ +      unsigned16 iw2, iw3,
> num2, num3; + +      iw2 = sim_core_read_aligned_2 (cpu, pc,
> exec_map, pc + 2); +      num2 = interp_num (cpu, iw2); +      iw3
> = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4); +      num3
> = interp_num (cpu, iw3); +      /* Addresses are 16-bit aligned.
> */ +      num3 <<= 1; +      TRACE_EXTRACT (cpu, "JT %#x %#x", iw2,
> iw3); +      TRACE_INSN (cpu, "JT %#x %#x", num2, num3); +
> TRACE_DECODE (cpu, "JT %#x != 0 -> %s", num2, num2 ? "taken" :
> "nop"); + +      if (num2) +	{ +	  pc = num3; +	  TRACE_BRANCH
> (cpu, "JT %#x", pc); +	} +      else +	pc += 6; +    } +  else if
> (num1 == 8) +    { +      /* jf: 8 a b: If <a> is zero, jump to
> <b>.  */ +      unsigned16 iw2, iw3, num2, num3; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> interp_num (cpu, iw2); +      iw3 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 4); +      num3 = interp_num (cpu, iw3); +
> /* Addresses are 16-bit aligned.  */ +      num3 <<= 1; +
> TRACE_EXTRACT (cpu, "JF %#x %#x", iw2, iw3); +      TRACE_INSN
> (cpu, "JF %#x %#x", num2, num3); +      TRACE_DECODE (cpu, "JF %#x
> == 0 -> %s", num2, num2 ? "nop" : "taken"); + +      if (!num2) +
> { +	  pc = num3; +	  TRACE_BRANCH (cpu, "JF %#x", pc); +	} +
> else +	pc += 6; +    } +  else if (num1 == 9) +    { +      /* add:
> 9 a b c: Assign <a> the sum of <b> and <c> (modulo 32768).  */ +
> unsigned16 iw2, iw3, iw4, num2, num3, num4, result; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> register_num (cpu, iw2); +      iw3 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 4); +      num3 = interp_num (cpu, iw3); +
> iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6); +
> num4 = interp_num (cpu, iw4); +      result = (num3 + num4) %
> 32768; +      TRACE_EXTRACT (cpu, "ADD %#x %#x %#x", iw2, iw3,
> iw4); +      TRACE_INSN (cpu, "ADD R%i %#x %#x", num2, num3,
> num4); +      TRACE_DECODE (cpu, "R%i = (%#x + %#x) %% %i = %#x",
> num2, num3, num4, +		    32768, result); + +      TRACE_REGISTER
> (cpu, "R%i = %#x", num2, result); +      cpu->regs[num2] = result;
> + +      pc += 8; +    } +  else if (num1 == 10) +    { +      /*
> mult: 10 a b c: Store into <a> the product of <b> and <c> (modulo +
> 32768).  */ +      unsigned16 iw2, iw3, iw4, num2, num3, num4,
> result; + +      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map,
> pc + 2); +      num2 = register_num (cpu, iw2); +      iw3 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4); +      num3 =
> interp_num (cpu, iw3); +      iw4 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 6); +      num4 = interp_num (cpu, iw4); +
> result = (num3 * num4) % 32768; +      TRACE_EXTRACT (cpu, "MULT
> %#x %#x %#x", iw2, iw3, iw4); +      TRACE_INSN (cpu, "MULT R%i %#x
> %#x", num2, num3, num4); +      TRACE_DECODE (cpu, "R%i = (%#x *
> %#x) %% %i = %#x", num2, num3, num4, +		    32768, result); + +
> TRACE_REGISTER (cpu, "R%i = %#x", num2, result); +
> cpu->regs[num2] = result; + +      pc += 8; +    } +  else if (num1
> == 11) +    { +      /* mod: 11 a b c: Store into <a> the remainder
> of <b> divided by <c>.  */ +      unsigned16 iw2, iw3, iw4, num2,
> num3, num4, result; + +      iw2 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 2); +      num2 = register_num (cpu, iw2); +
> iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4); +
> num3 = interp_num (cpu, iw3); +      iw4 = sim_core_read_aligned_2
> (cpu, pc, exec_map, pc + 6); +      num4 = interp_num (cpu, iw4); +
> result = num3 % num4; +      TRACE_EXTRACT (cpu, "MOD %#x %#x %#x",
> iw2, iw3, iw4); +      TRACE_INSN (cpu, "MOD R%i %#x %#x", num2,
> num3, num4); +      TRACE_DECODE (cpu, "R%i = %#x %% %#x = %#x",
> num2, num3, num4, result); + +      TRACE_REGISTER (cpu, "R%i =
> %#x", num2, result); +      cpu->regs[num2] = result; + +      pc
> += 8; +    } +  else if (num1 == 12) +    { +      /* and: 12 a b
> c: Stores into <a> the bitwise and of <b> and <c>.  */ +
> unsigned16 iw2, iw3, iw4, num2, num3, num4, result; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> register_num (cpu, iw2); +      iw3 = sim_core_read_aligned_2 (cpu,
> pc, exec_map, pc + 4); +      num3 = interp_num (cpu, iw3); +
> iw4 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6); +
> num4 = interp_num (cpu, iw4); +      result = (num3 & num4); +
> TRACE_EXTRACT (cpu, "AND %#x %#x %#x", iw2, iw3, iw4); +
> TRACE_INSN (cpu, "AND R%i %#x %#x", num2, num3, num4); +
> TRACE_DECODE (cpu, "R%i = %#x & %#x = %#x", num2, num3, num4,
> result); + +      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
> +      cpu->regs[num2] = result; + +      pc += 8; +    } +  else
> if (num1 == 13) +    { +      /* or: 13 a b c: Stores into <a> the
> bitwise or of <b> and <c>.  */ +      unsigned16 iw2, iw3, iw4,
> num2, num3, num4, result; + +      iw2 = sim_core_read_aligned_2
> (cpu, pc, exec_map, pc + 2); +      num2 = register_num (cpu,
> iw2); +      iw3 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc +
> 4); +      num3 = interp_num (cpu, iw3); +      iw4 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 6); +      num4 =
> interp_num (cpu, iw4); +      result = (num3 | num4); +
> TRACE_EXTRACT (cpu, "OR %#x %#x %#x", iw2, iw3, iw4); +
> TRACE_INSN (cpu, "OR R%i %#x %#x", num2, num3, num4); +
> TRACE_DECODE (cpu, "R%i = %#x | %#x = %#x", num2, num3, num4,
> result); + +      TRACE_REGISTER (cpu, "R%i = %#x", num2, result);
> +      cpu->regs[num2] = result; + +      pc += 8; +    } +  else
> if (num1 == 14) +    { +      /* not: 14 a b: Stores 15-bit bitwise
> inverse of <b> in <a>.  */ +      unsigned16 iw2, iw3, num2, num3,
> result; + +      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map,
> pc + 2); +      num2 = register_num (cpu, iw2); +      iw3 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4); +      num3 =
> interp_num (cpu, iw3); +      result = (~num3) & 0x7fff; +
> TRACE_EXTRACT (cpu, "NOT %#x %#x", iw2, iw3); +      TRACE_INSN
> (cpu, "NOT R%i %#x", num2, num3); +      TRACE_DECODE (cpu, "R%i =
> (~%#x) & 0x7fff = %#x", num2, num3, result); + +
> TRACE_REGISTER (cpu, "R%i = %#x", num2, result); +
> cpu->regs[num2] = result; + +      pc += 6; +    } +  else if (num1
> == 15) +    { +      /* rmem: 15 a b: Read memory at address <b>
> and write it to <a>.  */ +      unsigned16 iw2, iw3, num2, num3,
> result; + +      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map,
> pc + 2); +      num2 = register_num (cpu, iw2); +      iw3 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4); +      num3 =
> interp_num (cpu, iw3); +      /* Addresses are 16-bit aligned.  */
> +      num3 <<= 1; +      TRACE_EXTRACT (cpu, "RMEM %#x %#x", iw2,
> iw3); +      TRACE_INSN (cpu, "RMEM R%i %#x", num2, num3); + +
> TRACE_MEMORY (cpu, "reading %#x", num3); +      result =
> sim_core_read_aligned_2 (cpu, pc, read_map, num3); + +
> TRACE_REGISTER (cpu, "R%i = %#x", num2, result); +
> cpu->regs[num2] = result; + +      pc += 6; +    } +  else if (num1
> == 16) +    { +      /* wmem: 16 a b: Write the value from <b> into
> memory at address <a>.  */ +      unsigned16 iw2, iw3, num2, num3;
> + +      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map, pc +
> 2); +      num2 = interp_num (cpu, iw2); +      iw3 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 4); +      num3 =
> interp_num (cpu, iw3); +      /* Addresses are 16-bit aligned.  */
> +      num2 <<= 1; +      TRACE_EXTRACT (cpu, "WMEM %#x %#x", iw2,
> iw3); +      TRACE_INSN (cpu, "WMEM %#x %#x", num2, num3); + +
> TRACE_MEMORY (cpu, "writing %#x to %#x", num3, num2); +
> sim_core_write_aligned_2 (cpu, pc, write_map, num2, num3); + +
> pc += 6; +    } +  else if (num1 == 17) +    { +      /* call: 17
> a: Write the address of the next instruction to the stack and +
> jump to <a>.  */ +      unsigned16 iw2, num2; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> interp_num (cpu, iw2); +      /* Addresses are 16-bit aligned.  */
> +      num2 <<= 1; +      TRACE_EXTRACT (cpu, "CALL %#x", iw2); +
> TRACE_INSN (cpu, "CALL %#x", num2); + +      TRACE_MEMORY (cpu,
> "pushing %#x onto stack", (pc + 4) >> 1); +
> sim_core_write_aligned_2 (cpu, pc, write_map, cpu->sp, (pc + 4) >>
> 1); +      cpu->sp -= 2; +      TRACE_REGISTER (cpu, "SP = %#x",
> cpu->sp); + +      pc = num2; +      TRACE_BRANCH (cpu, "CALL %#x",
> pc); +    } +  else if (num1 == 18) +    { +      /* ret: 18:
> Remove the top element from the stack and jump to it; empty +
> stack = halt.  */ +      unsigned16 result; + +      TRACE_INSN
> (cpu, "RET"); +      cpu->sp += 2; +      TRACE_REGISTER (cpu, "SP
> = %#x", cpu->sp); +      result = sim_core_read_aligned_2 (cpu, pc,
> read_map, cpu->sp); +      TRACE_MEMORY (cpu, "popping %#x off of
> stack", result << 1); + +      pc = result << 1; +
> TRACE_BRANCH (cpu, "RET -> %#x", pc); +    } +  else if (num1 ==
> 19) +    { +      /* out: 19 a: Write the character <a> to the
> terminal.  */ +      unsigned16 iw2, num2; + +      iw2 =
> sim_core_read_aligned_2 (cpu, pc, exec_map, pc + 2); +      num2 =
> interp_num (cpu, iw2); +      TRACE_EXTRACT (cpu, "OUT %#x", iw2);
> +      TRACE_INSN (cpu, "OUT %#x", num2); +      TRACE_EVENTS (cpu,
> "write to stdout: %#x (%c)", num2, num2); + +      sim_io_printf
> (sd, "%c", num2); + +      pc += 4; +    } +  else if (num1 == 20)
> +    { +      /* in: 20 a: read a character from the terminal and
> write its ascii code +	 to <a>.  It can be assumed that once input
> starts, it will continue +	 until a newline is encountered.  This
> means that you can safely read +	 lines from the keyboard and trust
> that they will be fully read.  */ +      unsigned16 iw2, num2; +
> char c; + +      iw2 = sim_core_read_aligned_2 (cpu, pc, exec_map,
> pc + 2); +      num2 = register_num (cpu, iw2); +
> TRACE_EXTRACT (cpu, "IN %#x", iw2); +      TRACE_INSN (cpu, "IN
> %#x", num2); +      sim_io_read_stdin (sd, &c, 1); +
> TRACE_EVENTS (cpu, "read from stdin: %#x (%c)", c, c); + +      /*
> The challenge uses lowercase for all inputs, so insert some low
> level +	 helpers of our own to make it a bit nicer.  */ +
> switch (c) +	{ +	case 'Q': +	  sim_engine_halt (sd, cpu, NULL, pc,
> sim_exited, 0); +	  break; +	} + +      TRACE_REGISTER (cpu, "R%i =
> %#x", iw2 & 0xf, c); +      cpu->regs[iw2 & 0xf] = c; + +      pc
> += 4; +    } +  else if (num1 == 21) +    { +      /* noop: 21: no
> operation */ +      TRACE_INSN (cpu, "NOOP"); + +      pc += 2; +
> } +  else +    sim_engine_halt (sd, cpu, NULL, pc, sim_signalled,
> SIM_SIGILL); + +  TRACE_REGISTER (cpu, "PC = %#x", pc); +
> sim_pc_set (cpu, pc); +} +\f +/* Return the program counter for this
> cpu. */ +static sim_cia +pc_get (sim_cpu *cpu) +{ +  return
> cpu->pc; +} + +/* Set the program counter for this cpu to the new
> pc value. */ +static void +pc_set (sim_cpu *cpu, sim_cia pc) +{ +
> cpu->pc = pc; +} + +/* Initialize the state for a single cpu.
> Usuaully this involves clearing all +   registers back to their
> reset state.  Should also hook up the fetch/store +   helper
> functions too.  */ +void initialize_cpu (SIM_DESC sd, SIM_CPU
> *cpu) +{ +  memset (cpu->regs, 0, sizeof (cpu->regs)); +  cpu->pc =
> 0; +  /* Make sure it's initialized outside of the 16-bit address
> space.  */ +  cpu->sp = 0x80000; + +  CPU_PC_FETCH (cpu) = pc_get;
> +  CPU_PC_STORE (cpu) = pc_set; +} diff --git
> a/sim/example-synacor/sim-main.h b/sim/example-synacor/sim-main.h
> new file mode 100644 index 000000000000..ba0e4efa12a9 ---
> /dev/null +++ b/sim/example-synacor/sim-main.h @@ -0,0 +1,49 @@ +/*
> Example synacor simulator. + +   Copyright (C) 2005-2021 Free
> Software Foundation, Inc. +   Contributed by Mike Frysinger. + +
> 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" +#include
> "sim-base.h" + +struct _sim_cpu { +  /* ... simulator specific
> members ... */ +  unsigned16 regs[8]; +  sim_cia pc; + +  /* This
> isn't a real register, and the stack is not directly addressable, +
> so use memory outside of the 16-bit address space.  */ +
> unsigned32 sp; + +  sim_cpu_base base; +}; + +struct sim_state { +
> sim_cpu *cpu[MAX_NR_PROCESSORS]; + +  /* ... simulator specific
> members ... */ +  sim_state_base base; +}; + +extern void step_once
> (SIM_CPU *); +extern void initialize_cpu (SIM_DESC, SIM_CPU *); +
> +#endif diff --git a/sim/testsuite/example-synacor/ChangeLog
> b/sim/testsuite/example-synacor/ChangeLog new file mode 100644
> index 000000000000..58295bb38fa0 --- /dev/null +++
> b/sim/testsuite/example-synacor/ChangeLog @@ -0,0 +1,5 @@
> +2021-01-13  Mike Frysinger  <vapier@gentoo.org> + +	* add.s,
> allinsn.exp, and.s, call.s, exit-0.s, gt.s, isa.inc, jmp.s, +
> mem.s, mod.s, mult.s, not.s, or.s, push-pop.s, ret.s, set.s, +
> testutils.inc: New files. diff --git
> a/sim/testsuite/example-synacor/add.s
> b/sim/testsuite/example-synacor/add.s new file mode 100644 index
> 000000000000..64a8c20d8edc --- /dev/null +++
> b/sim/testsuite/example-synacor/add.s @@ -0,0 +1,24 @@ +# check the
> ADD insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	SET r2, 2 +	ADD r2, r2, r2 +	EQ r3, r2, 4 +	JF r3,
> 2 + +	ADD r1, 100, r2 +	EQ r4, r1, 104 +	JF r4, 2 + +	# 0x7ffe ==
> -2 +	ADD r0, r1, 0x7ffe +	EQ r4, r0, 102 +	JF r4, 2 + +	pass diff
> --git a/sim/testsuite/example-synacor/allinsn.exp
> b/sim/testsuite/example-synacor/allinsn.exp new file mode 100644
> index 000000000000..a73312b8adfb --- /dev/null +++
> b/sim/testsuite/example-synacor/allinsn.exp @@ -0,0 +1,15 @@ +#
> Example synacor simulator testsuite. + +if [istarget *] { +    #
> All machines. +    set all_machs "example" + +    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/example-synacor/and.s
> b/sim/testsuite/example-synacor/and.s new file mode 100644 index
> 000000000000..1d1885985590 --- /dev/null +++
> b/sim/testsuite/example-synacor/and.s @@ -0,0 +1,18 @@ +# check the
> AND insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	AND r2, 0xfff, 0x7f0c +	EQ r3, r2, 0xf0c +	JF r3,
> 2 + +	AND r2, r2, 0xf +	EQ r3, r2, 0xc +	JF r3, 2 + +	pass diff
> --git a/sim/testsuite/example-synacor/call.s
> b/sim/testsuite/example-synacor/call.s new file mode 100644 index
> 000000000000..6d7b545a14bc --- /dev/null +++
> b/sim/testsuite/example-synacor/call.s @@ -0,0 +1,14 @@ +# check
> the CALL insn. +# mach: example + +.include "testutils.inc" + +
> start +	CALL 3 +	HALT + +	POP r0 +	EQ r1, r0, 2 +	JF r1, 2 + +
> pass diff --git a/sim/testsuite/example-synacor/exit-0.s
> b/sim/testsuite/example-synacor/exit-0.s new file mode 100644 index
> 000000000000..2c1fb72f5d7c --- /dev/null +++
> b/sim/testsuite/example-synacor/exit-0.s @@ -0,0 +1,10 @@ +# check
> that the sim doesn't die immediately. +# mach: example + +.include
> "testutils.inc" + +	start +	NOOP +	NOOP +	NOOP +	pass diff --git
> a/sim/testsuite/example-synacor/gt.s
> b/sim/testsuite/example-synacor/gt.s new file mode 100644 index
> 000000000000..aef28e34404c --- /dev/null +++
> b/sim/testsuite/example-synacor/gt.s @@ -0,0 +1,31 @@ +# check the
> GT insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	GT r0, 3, 2 +	EQ r1, r0, 1 +	JF r1, 2 + +	GT r0,
> 2, 2 +	EQ r1, r0, 0 +	JF r1, 2 + +	GT r0, 1, 2 +	EQ r1, r0, 0 +	JF
> r1, 2 + +	SET r2, 3 +	SET r3, 4 +	GT r0, r2, r3 +	EQ r1, r0, 0 +	JF
> r1, 2 +	GT r0, r3, r2 +	EQ r1, r0, 1 +	JF r1, 2 + +	pass diff --git
> a/sim/testsuite/example-synacor/isa.inc
> b/sim/testsuite/example-synacor/isa.inc new file mode 100644 index
> 000000000000..e2e1136ecfb7 --- /dev/null +++
> b/sim/testsuite/example-synacor/isa.inc @@ -0,0 +1,108 @@ +# Macros
> for the fake ISA.  Keep in sync with
> example-synacor/README.arch-spec. + +# These .set macros will
> generate symbols in the output ELF, but it also allows +# use to
> use them as arguments to the insns below.  Oh well. +.set r0,
> 32768 +.set r1, r0+1 +.set r2, r0+2 +.set r3, r0+3 +.set r4, r0+4
> +.set r5, r0+5 +.set r6, r0+6 +.set r7, r0+7 + +# The target is
> little endian, so make sure we output the 16-bit words as such.
> +.macro _op op:req, more:vararg +	.byte \op & 0xff, (\op >> 8) &
> 0xff +	.ifnb \more +	_op \more +	.endif +.endm + +.macro HALT +	_op
> 0 +.endm + +.macro SET a:req, b:req +	_op 1, \a, \b +.endm +
> +.macro PUSH a:req +	_op 2, \a +.endm + +.macro POP a:req +	_op 3,
> \a +.endm + +.macro EQ a:req, b:req, c:req +	_op 4, \a, \b, \c
> +.endm + +.macro GT a:req, b:req, c:req +	_op 5, \a, \b, \c +.endm
> + +.macro JMP a:req +	_op 6, \a +.endm + +.macro JT a:req, b:req +
> _op 7, \a, \b +.endm + +.macro JF a:req, b:req +	_op 8, \a, \b
> +.endm + +.macro ADD a:req, b:req, c:req +	_op 9, \a, \b, \c
> +.endm + +.macro MULT a:req, b:req, c:req +	_op 10, \a, \b, \c
> +.endm + +.macro MOD a:req, b:req, c:req +	_op 11, \a, \b, \c
> +.endm + +.macro AND a:req, b:req, c:req +	_op 12, \a, \b, \c
> +.endm + +.macro OR a:req, b:req, c:req +	_op 13, \a, \b, \c
> +.endm + +.macro NOT a:req, b:req +	_op 14, \a, \b +.endm + +.macro
> RMEM a:req, b:req +	_op 15, \a, \b +.endm + +.macro WMEM a:req,
> b:req +	_op 16, \a, \b +.endm + +.macro CALL a:req +	_op 17, \a
> +.endm + +.macro RET +	_op 18 +.endm + +.macro OUT a:req +	_op 19,
> \a +.endm + +.macro IN a:req +	_op 20, \a +.endm + +.macro NOOP +
> _op 21 +.endm diff --git a/sim/testsuite/example-synacor/jmp.s
> b/sim/testsuite/example-synacor/jmp.s new file mode 100644 index
> 000000000000..dcacf668adc4 --- /dev/null +++
> b/sim/testsuite/example-synacor/jmp.s @@ -0,0 +1,9 @@ +# check the
> JMP insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT +	pass diff --git
> a/sim/testsuite/example-synacor/mem.s
> b/sim/testsuite/example-synacor/mem.s new file mode 100644 index
> 000000000000..24aa0a9e6fc6 --- /dev/null +++
> b/sim/testsuite/example-synacor/mem.s @@ -0,0 +1,25 @@ +# check the
> RMEM & WMEM insns. +# mach: example + +.include "testutils.inc" + +
> start +	JMP 14 +	HALT +	pass + +	# Read a constant address. +	RMEM
> r0, 1 +	EQ r1, r0, 14 +	JF r1, 2 + +	# Change the first JMP to skip
> HALT and hit the pass. +	WMEM 1, 3 + +	# Read an address in a
> register. +	SET r2, 1 +	RMEM r0, r2 +	EQ r1, r0, 3 +	JF r1, 2 + +
> JMP 0 diff --git a/sim/testsuite/example-synacor/mod.s
> b/sim/testsuite/example-synacor/mod.s new file mode 100644 index
> 000000000000..f0b4217574c5 --- /dev/null +++
> b/sim/testsuite/example-synacor/mod.s @@ -0,0 +1,18 @@ +# check the
> MOD insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	MOD r0, 8, 3 +	EQ r1, r0, 2 +	JF r1, 2 + +	MOD r0,
> r0, 2 +	EQ r1, r0, 0 +	JF r1, 2 + +	pass diff --git
> a/sim/testsuite/example-synacor/mult.s
> b/sim/testsuite/example-synacor/mult.s new file mode 100644 index
> 000000000000..d323cf53aa77 --- /dev/null +++
> b/sim/testsuite/example-synacor/mult.s @@ -0,0 +1,18 @@ +# check
> the MULT insn. +# mach: example + +.include "testutils.inc" + +
> start +	JMP 3 +	HALT + +	MULT r0, 3, 2 +	EQ r1, r0, 6 +	JF r1, 2 +
> +	MULT r0, r0, 8 +	EQ r1, r0, 48 +	JF r1, 2 + +	pass diff --git
> a/sim/testsuite/example-synacor/not.s
> b/sim/testsuite/example-synacor/not.s new file mode 100644 index
> 000000000000..8a4a570ce6e0 --- /dev/null +++
> b/sim/testsuite/example-synacor/not.s @@ -0,0 +1,15 @@ +# check the
> NOT insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	SET r2, 0xc +	NOT r0, r2 +	EQ r3, r0, 0x7ff3 +	JF
> r3, 2 + +	pass diff --git a/sim/testsuite/example-synacor/or.s
> b/sim/testsuite/example-synacor/or.s new file mode 100644 index
> 000000000000..f8f2ae3a1539 --- /dev/null +++
> b/sim/testsuite/example-synacor/or.s @@ -0,0 +1,18 @@ +# check the
> OR insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	OR r2, 0xf, 0x80 +	EQ r3, r2, 0x8f +	JF r3, 2 + +
> OR r2, r2, 0xff +	EQ r3, r2, 0xff +	JF r3, 2 + +	pass diff --git
> a/sim/testsuite/example-synacor/push-pop.s
> b/sim/testsuite/example-synacor/push-pop.s new file mode 100644
> index 000000000000..b8199c5d34e4 --- /dev/null +++
> b/sim/testsuite/example-synacor/push-pop.s @@ -0,0 +1,22 @@ +#
> check the PUSH & POP insns. +# mach: example + +.include
> "testutils.inc" + +	start +	JMP 3 +	HALT + +	PUSH 1 +	SET r0, 3 +
> PUSH r0 +	POP r1 +	POP r2 +	EQ r7, r0, 3 +	JF r7, 2 +	EQ r7, r1, 3
> +	JF r7, 2 +	EQ r7, r2, 1 +	JF r7, 2 + +	pass diff --git
> a/sim/testsuite/example-synacor/ret.s
> b/sim/testsuite/example-synacor/ret.s new file mode 100644 index
> 000000000000..63bfa7132cbe --- /dev/null +++
> b/sim/testsuite/example-synacor/ret.s @@ -0,0 +1,13 @@ +# check the
> RET insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 13 +	pass + +	SET r5, 2 +	PUSH r5 +	RET +	HALT diff --git
> a/sim/testsuite/example-synacor/set.s
> b/sim/testsuite/example-synacor/set.s new file mode 100644 index
> 000000000000..8b441c762eaa --- /dev/null +++
> b/sim/testsuite/example-synacor/set.s @@ -0,0 +1,20 @@ +# check the
> SET insn. +# mach: example + +.include "testutils.inc" + +	start +
> JMP 3 +	HALT + +	SET r2, 2 +	EQ r3, r2, 2 +	JF r3, 2 +	SET r1, 1 +
> EQ r3, r1, 1 +	JF r3, 2 +	SET r0, r2 +	EQ r3, r0, 2 +	JF r3, 2 + +
> pass diff --git a/sim/testsuite/example-synacor/testutils.inc
> b/sim/testsuite/example-synacor/testutils.inc new file mode 100644
> index 000000000000..0f286c6036ca --- /dev/null +++
> b/sim/testsuite/example-synacor/testutils.inc @@ -0,0 +1,31 @@
> +.include "isa.inc" + +# MACRO: pass +# Write 'pass' to stdout and
> quit +	.macro pass +	OUT 'p' +	OUT 'a' +	OUT 's' +	OUT 's' +	OUT
> '\n' +	HALT +	.endm + +# MACRO: fail +# Write 'fail' to stdout and
> quit +	.macro fail +	OUT 'f' +	OUT 'a' +	OUT 'i' +	OUT 'l' +	OUT
> '\n' +	HALT +	.endm + +# MACRO: start +# All assembler tests should
> start with a call to "start" +	.macro start +	.text +.global
> _start +_start: +	.endm diff --git a/sim/testsuite/lib/sim-defs.exp
> b/sim/testsuite/lib/sim-defs.exp index 43a07050f508..04ab4685612b
> 100644 --- a/sim/testsuite/lib/sim-defs.exp +++
> b/sim/testsuite/lib/sim-defs.exp @@ -378,6 +378,13 @@ proc
> run_sim_test { name requested_machs } { set options "$options
> timeout=$opts(timeout)" }
>
> +	if [string match "example" "$mach"] { +	    set objcopy
> [find_binutils_prog objcopy] +	    set comp_output [remote_exec
> host $objcopy "-O binary -j .text ${name}.x ${name}.bin"] +
> file rename -force "${name}.bin" "${name}.x" +	    append
> opts(sim,$mach) " --target binary" +	} + set result [sim_run
> ${name}.x "$opts(sim,$mach) $global_sim_options" "$opts(progopts)"
> "" "$options"] set return_code [lindex $result 0] set output
> [lindex $result 1]
>


- -- 
Cell: +44 (7970) 676050
SkypeID: jeremybennett
Twitter: @jeremypbennett
Email: jeremy.bennett@embecosm.com
Web: www.embecosm.com
PGP key: 1024D/BEF58172FB4754E1 2009-03-20
-----BEGIN PGP SIGNATURE-----

iF0EARECAB0WIQRASGDWqmhRZUfAaPW+9YFy+0dU4QUCYBvTCgAKCRC+9YFy+0dU
4RPoAJ9M55mCdgF1p7G3Da947lDVeLAAMwCfcL7cr5P14DJh5bng7xO6dwahlsQ=
=6SkR
-----END PGP SIGNATURE-----

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2] sim: example-synacor: a simple implementation for reference
  2021-02-04 10:57   ` Jeremy Bennett
@ 2021-02-04 22:50     ` Mike Frysinger
  0 siblings, 0 replies; 4+ messages in thread
From: Mike Frysinger @ 2021-02-04 22:50 UTC (permalink / raw)
  To: Jeremy Bennett; +Cc: gdb-patches

On 04 Feb 2021 10:57, Jeremy Bennett wrote:
> On 19/01/2021 04:05, Mike Frysinger via Gdb-patches wrote:
> > Provide a simple example simulator for people porting to new
> > targets to use as a reference.  This one has the advantage of being
> > used by people and having a fun program available for it.
> 
> Good to see your proposal and the lack of a RISC-V simulator being
> addressed again.

this thread isn't about RISC-V.  guessing you meant to respond to a diff one.
-mike

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-02-04 22:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-03  7:24 [PATCH] sim: example-synacor: a simple implementation for reference Mike Frysinger
2021-01-19  4:05 ` [PATCH v2] " Mike Frysinger
2021-02-04 10:57   ` Jeremy Bennett
2021-02-04 22:50     ` Mike Frysinger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).