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

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).