aboutsummaryrefslogtreecommitdiff
path: root/src/interp
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-09-07 00:04:38 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-11-06 14:38:40 +0100
commit6d9d80362f9932bbc87e162b8ef7df06c73e27e1 (patch)
treee3e228c63fe26f07503f226de7fb5086b3dc2286 /src/interp
downloadnanowasm-6d9d80362f9932bbc87e162b8ef7df06c73e27e1.tar.gz
First commit
Diffstat (limited to 'src/interp')
-rw-r--r--src/interp/CMakeLists.txt21
-rw-r--r--src/interp/data_set.c24
-rw-r--r--src/interp/global/CMakeLists.txt11
-rw-r--r--src/interp/global/load.c34
-rw-r--r--src/interp/global/store.c34
-rw-r--r--src/interp/initexpr_set.c28
-rw-r--r--src/interp/linear/CMakeLists.txt11
-rw-r--r--src/interp/linear/load.c39
-rw-r--r--src/interp/linear/store.c40
-rw-r--r--src/interp/mem/CMakeLists.txt11
-rw-r--r--src/interp/mem/load.c27
-rw-r--r--src/interp/mem/store.c27
-rw-r--r--src/interp/ops.c278
-rw-r--r--src/interp/resume.c20
-rw-r--r--src/interp/routines/CMakeLists.txt13
-rw-r--r--src/interp/routines/execute.c83
-rw-r--r--src/interp/routines/find_export.c229
-rw-r--r--src/interp/routines/full.c63
-rw-r--r--src/interp/routines/limited.c54
-rw-r--r--src/interp/run.c16
-rw-r--r--src/interp/stack/CMakeLists.txt14
-rw-r--r--src/interp/stack/pop.c37
-rw-r--r--src/interp/stack/ptr.c21
-rw-r--r--src/interp/stack/push.c37
-rw-r--r--src/interp/stack/read.c37
-rw-r--r--src/interp/stack/write.c38
-rw-r--r--src/interp/start.c28
27 files changed, 1275 insertions, 0 deletions
diff --git a/src/interp/CMakeLists.txt b/src/interp/CMakeLists.txt
new file mode 100644
index 0000000..c38da8c
--- /dev/null
+++ b/src/interp/CMakeLists.txt
@@ -0,0 +1,21 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ data_set.c
+ initexpr_set.c
+ ops.c
+ run.c
+ resume.c
+ start.c
+)
+
+add_subdirectory(global)
+add_subdirectory(linear)
+add_subdirectory(mem)
+add_subdirectory(routines)
+add_subdirectory(stack)
diff --git a/src/interp/data_set.c b/src/interp/data_set.c
new file mode 100644
index 0000000..266852b
--- /dev/null
+++ b/src/interp/data_set.c
@@ -0,0 +1,24 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/opcodes.h>
+
+static const enum opcode opcodes[] =
+{
+ OP_END,
+ OP_I32_CONST,
+};
+
+const struct nwp_interp_set nwp_interp_data_set =
+{
+ opcodes,
+ sizeof opcodes / sizeof *opcodes
+};
diff --git a/src/interp/global/CMakeLists.txt b/src/interp/global/CMakeLists.txt
new file mode 100644
index 0000000..0f0e056
--- /dev/null
+++ b/src/interp/global/CMakeLists.txt
@@ -0,0 +1,11 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ load.c
+ store.c
+)
diff --git a/src/interp/global/load.c b/src/interp/global/load.c
new file mode 100644
index 0000000..26d8c8d
--- /dev/null
+++ b/src/interp/global/load.c
@@ -0,0 +1,34 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/global.h>
+#include <nw/mem.h>
+#include <nw/log.h>
+
+enum nw_state nwp_global_load(struct nw_interp *const i,
+ struct nw_sm_io *const io, const nw_varuint32 index)
+{
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const size_t offset = index * sizeof (struct nw_global);
+ const enum nw_state n = nwp_mem_load(&cfg->global, io, offset, cfg->user);
+
+ if (n == NW_FATAL)
+ {
+ static const char *const exc = "failed to load from global memory";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ }
+
+ return n;
+}
diff --git a/src/interp/global/store.c b/src/interp/global/store.c
new file mode 100644
index 0000000..91550d4
--- /dev/null
+++ b/src/interp/global/store.c
@@ -0,0 +1,34 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/global.h>
+#include <nw/interp.h>
+#include <nw/mem.h>
+#include <nw/log.h>
+
+enum nw_state nwp_global_store(struct nw_interp *const i,
+ struct nw_sm_io *const io, const nw_varuint32 index)
+{
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const size_t offset = index * sizeof (struct nw_global);
+ const enum nw_state n = nwp_mem_store(&cfg->global, io, offset, cfg->user);
+
+ if (n == NW_FATAL)
+ {
+ static const char *const exc = "failed to store into global memory";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ }
+
+ return n;
+}
diff --git a/src/interp/initexpr_set.c b/src/interp/initexpr_set.c
new file mode 100644
index 0000000..1e200f6
--- /dev/null
+++ b/src/interp/initexpr_set.c
@@ -0,0 +1,28 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/opcodes.h>
+
+static const enum opcode opcodes[] =
+{
+ OP_NOP,
+ OP_END,
+ OP_I32_CONST,
+ OP_I64_CONST,
+ OP_F32_CONST,
+ OP_F64_CONST
+};
+
+const struct nwp_interp_set nwp_interp_initexpr_set =
+{
+ opcodes,
+ sizeof opcodes / sizeof *opcodes
+};
diff --git a/src/interp/linear/CMakeLists.txt b/src/interp/linear/CMakeLists.txt
new file mode 100644
index 0000000..0f0e056
--- /dev/null
+++ b/src/interp/linear/CMakeLists.txt
@@ -0,0 +1,11 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ load.c
+ store.c
+)
diff --git a/src/interp/linear/load.c b/src/interp/linear/load.c
new file mode 100644
index 0000000..afc6154
--- /dev/null
+++ b/src/interp/linear/load.c
@@ -0,0 +1,39 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nanowasm/linear.h>
+#include <nw/interp.h>
+#include <nw/mem.h>
+#include <nw/log.h>
+
+enum nw_state nwp_linear_load(struct nw_interp *const i,
+ struct nw_sm_io *const io, const unsigned long offset)
+{
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const enum nw_state n = nwp_mem_load(&cfg->linear, io, offset, cfg->user);
+
+ if (n == NW_FATAL)
+ {
+ static const char *const exc = "failed to load from linear memory";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ }
+
+ return n;
+}
+
+enum nw_state nw_linear_load(struct nw_inst *const i,
+ struct nw_sm_io *const io, const unsigned long offset)
+{
+ return nwp_linear_load(&i->interp, io, offset);
+}
diff --git a/src/interp/linear/store.c b/src/interp/linear/store.c
new file mode 100644
index 0000000..6edffd9
--- /dev/null
+++ b/src/interp/linear/store.c
@@ -0,0 +1,40 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nanowasm/linear.h>
+#include <nw/linear.h>
+#include <nw/interp.h>
+#include <nw/mem.h>
+#include <nw/log.h>
+
+enum nw_state nwp_linear_store(struct nw_interp *const i,
+ struct nw_sm_io *const io, const unsigned long offset)
+{
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const enum nw_state n = nwp_mem_store(&cfg->linear, io, offset, cfg->user);
+
+ if (n == NW_FATAL)
+ {
+ static const char *const exc = "failed to store into linear memory";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ }
+
+ return n;
+}
+
+enum nw_state nw_linear_store(struct nw_inst *const i,
+ struct nw_sm_io *const io, const unsigned long offset)
+{
+ return nwp_linear_store(&i->interp, io, offset);
+}
diff --git a/src/interp/mem/CMakeLists.txt b/src/interp/mem/CMakeLists.txt
new file mode 100644
index 0000000..0f0e056
--- /dev/null
+++ b/src/interp/mem/CMakeLists.txt
@@ -0,0 +1,11 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ load.c
+ store.c
+)
diff --git a/src/interp/mem/load.c b/src/interp/mem/load.c
new file mode 100644
index 0000000..0ccf29b
--- /dev/null
+++ b/src/interp/mem/load.c
@@ -0,0 +1,27 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/mem.h>
+#include <nw/log.h>
+
+enum nw_state nwp_mem_load(const struct nw_mem_cfg *const cfg,
+ struct nw_sm_io *const io, const nw_varuint32 offset, void *const user)
+{
+ void *const dst = (unsigned char *)io->buf + io->read;
+ const int n = cfg->load(offset + io->read, dst, io->n - io->read, user);
+
+ if (n < 0)
+ return NW_FATAL;
+ else if ((io->read += n) >= io->n)
+ return NW_OK;
+
+ return NW_AGAIN;
+}
diff --git a/src/interp/mem/store.c b/src/interp/mem/store.c
new file mode 100644
index 0000000..cc58169
--- /dev/null
+++ b/src/interp/mem/store.c
@@ -0,0 +1,27 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/mem.h>
+#include <nw/log.h>
+
+enum nw_state nwp_mem_store(const struct nw_mem_cfg *const cfg,
+ struct nw_sm_io *const io, const nw_varuint32 offset, void *const user)
+{
+ const void *const src = (const unsigned char *)io->buf + io->read;
+ const int n = cfg->store(offset + io->read, src, io->n - io->read, user);
+
+ if (n < 0)
+ return NW_FATAL;
+ else if ((io->read += n) >= io->n)
+ return NW_OK;
+
+ return NW_AGAIN;
+}
diff --git a/src/interp/ops.c b/src/interp/ops.c
new file mode 100644
index 0000000..6135f96
--- /dev/null
+++ b/src/interp/ops.c
@@ -0,0 +1,278 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/opcodes.h>
+#include <nw/ops.h>
+#include <nw/interp.h>
+
+static void (*const ops[])(struct nw_interp *) =
+{
+ nwp_op_unreachable, /* OP_UNREACHABLE */
+ nwp_op_nop, /* OP_NOP */
+ nwp_op_block, /* OP_BLOCK */
+ nwp_op_loop, /* OP_LOOP */
+ NULL, /* OP_IF */
+ NULL, /* OP_ELSE */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nwp_op_end, /* OP_END */
+ nwp_op_br, /* OP_BR */
+ nwp_op_br_if, /* OP_BR_IF */
+ NULL, /* OP_BR_TABLE */
+ nwp_op_return, /* OP_RETURN */
+ nwp_op_call, /* OP_CALL */
+ nwp_op_call_indirect, /* OP_CALL_INDIRECT */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nwp_op_drop, /* OP_DROP */
+ NULL, /* OP_SELECT */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ nwp_op_get_local, /* OP_GET_LOCAL */
+ nwp_op_set_local, /* OP_SET_LOCAL */
+ nwp_op_tee_local, /* OP_TEE_LOCAL */
+ nwp_op_get_global, /* OP_GET_GLOBAL */
+ nwp_op_set_global, /* OP_SET_GLOBAL */
+ NULL,
+ NULL,
+ NULL,
+ nwp_op_i32_load, /* OP_I32_LOAD */
+ NULL, /* OP_I64_LOAD */
+ NULL, /* OP_F32_LOAD */
+ NULL, /* OP_F64_LOAD */
+ NULL, /* OP_I32_LOAD8_S */
+ nwp_op_i32_load8_u, /* OP_I32_LOAD8_U */
+ NULL, /* OP_I32_LOAD16_S */
+ NULL, /* OP_I32_LOAD16_U */
+ NULL, /* OP_I64_LOAD8_S */
+ NULL, /* OP_I64_LOAD8_U */
+ NULL, /* OP_I64_LOAD16_S */
+ NULL, /* OP_I64_LOAD16_U */
+ NULL, /* OP_I64_LOAD32_S */
+ NULL, /* OP_I64_LOAD32_U */
+ nwp_op_i32_store, /* OP_I32_STORE */
+ nwp_op_i64_store, /* OP_I64_STORE */
+ NULL, /* OP_F32_STORE */
+ NULL, /* OP_F64_STORE */
+ NULL, /* OP_I32_STORE8 */
+ NULL, /* OP_I32_STORE16 */
+ NULL, /* OP_I64_STORE8 */
+ NULL, /* OP_I64_STORE16 */
+ NULL, /* OP_I64_STORE32 */
+ NULL, /* OP_CURRENT_MEMORY */
+ NULL, /* OP_GROW_MEMORY */
+ nwp_op_i32_const, /* OP_I32_CONST */
+ nwp_op_i64_const, /* OP_I64_CONST */
+ NULL, /* OP_F32_CONST */
+ NULL, /* OP_F64_CONST */
+ nwp_op_i32_eqz, /* OP_I32_EQZ */
+ nwp_op_i32_eq, /* OP_I32_EQ */
+ nwp_op_i32_ne, /* OP_I32_NE */
+ nwp_op_i32_lt_s, /* OP_I32_LT_S */
+ NULL, /* OP_I32_LT_U */
+ NULL, /* OP_I32_GT_S */
+ NULL, /* OP_I32_GT_U */
+ NULL, /* OP_I32_LE_S */
+ NULL, /* OP_I32_LE_U */
+ nwp_op_i32_ge_s, /* OP_I32_GE_S */
+ nwp_op_i32_ge_u, /* OP_I32_GE_U */
+ NULL, /* OP_I64_EQZ */
+ NULL, /* OP_I64_EQ */
+ NULL, /* OP_I64_NE */
+ NULL, /* OP_I64_LT_S */
+ NULL, /* OP_I64_LT_U */
+ NULL, /* OP_I64_GT_S */
+ NULL, /* OP_I64_GT_U */
+ NULL, /* OP_I64_LE_S */
+ NULL, /* OP_I64_LE_U */
+ NULL, /* OP_I64_GE_S */
+ NULL, /* OP_I64_GE_U */
+ NULL, /* OP_F32_EQ */
+ NULL, /* OP_F32_NE */
+ NULL, /* OP_F32_LT */
+ NULL, /* OP_F32_GT */
+ NULL, /* OP_F32_LE */
+ NULL, /* OP_F32_GE */
+ NULL, /* OP_F64_EQ */
+ NULL, /* OP_F64_NE */
+ NULL, /* OP_F64_LT */
+ NULL, /* OP_F64_GT */
+ NULL, /* OP_F64_LE */
+ NULL, /* OP_F64_GE */
+ NULL, /* OP_I32_CLZ */
+ NULL, /* OP_I32_CTZ */
+ NULL, /* OP_I32_POPCNT */
+ nwp_op_i32_add, /* OP_I32_ADD */
+ nwp_op_i32_sub, /* OP_I32_SUB */
+ nwp_op_i32_mul, /* OP_I32_MUL */
+ NULL, /* OP_I32_DIV_S */
+ NULL, /* OP_I32_DIV_U */
+ NULL, /* OP_I32_REM_S */
+ NULL, /* OP_I32_REM_U */
+ nwp_op_i32_and, /* OP_I32_AND */
+ nwp_op_i32_or, /* OP_I32_OR */
+ NULL, /* OP_I32_XOR */
+ NULL, /* OP_I32_SHL */
+ NULL, /* OP_I32_SHR_S */
+ NULL, /* OP_I32_SHR_U */
+ NULL, /* OP_I32_ROTL */
+ NULL, /* OP_I32_ROTR */
+ NULL, /* OP_I64_CLZ */
+ NULL, /* OP_I64_CTZ */
+ NULL, /* OP_I64_POPCNT */
+ NULL, /* OP_I64_ADD */
+ NULL, /* OP_I64_SUB */
+ NULL, /* OP_I64_MUL */
+ NULL, /* OP_I64_DIV_S */
+ NULL, /* OP_I64_DIV_U */
+ NULL, /* OP_I64_REM_S */
+ NULL, /* OP_I64_REM_U */
+ NULL, /* OP_I64_AND */
+ NULL, /* OP_I64_OR */
+ NULL, /* OP_I64_XOR */
+ NULL, /* OP_I64_SHL */
+ NULL, /* OP_I64_SHR_S */
+ NULL, /* OP_I64_SHR_U */
+ NULL, /* OP_I64_ROTL */
+ NULL, /* OP_I64_ROTR */
+ NULL, /* OP_F32_ABS */
+ NULL, /* OP_F32_NEG */
+ NULL, /* OP_F32_CEIL */
+ NULL, /* OP_F32_FLOOR */
+ NULL, /* OP_F32_TRUNC */
+ NULL, /* OP_F32_NEAREST */
+ NULL, /* OP_F32_SQRT */
+ NULL, /* OP_F32_ADD */
+ NULL, /* OP_F32_SUB */
+ NULL, /* OP_F32_MUL */
+ NULL, /* OP_F32_DIV */
+ NULL, /* OP_F32_MIN */
+ NULL, /* OP_F32_MAX */
+ NULL, /* OP_F32_COPYSIGN */
+ NULL, /* OP_F64_ABS */
+ NULL, /* OP_F64_NEG */
+ NULL, /* OP_F64_CEIL */
+ NULL, /* OP_F64_FLOOR */
+ NULL, /* OP_F64_TRUNC */
+ NULL, /* OP_F64_NEAREST */
+ NULL, /* OP_F64_SQRT */
+ NULL, /* OP_F64_ADD */
+ NULL, /* OP_F64_SUB */
+ NULL, /* OP_F64_MUL */
+ NULL, /* OP_F64_DIV */
+ NULL, /* OP_F64_MIN */
+ NULL, /* OP_F64_MAX */
+ NULL, /* OP_F64_COPYSIGN */
+ NULL, /* OP_I32_WRAP_I64 */
+ NULL, /* OP_I32_TRUNC_S_F32 */
+ NULL, /* OP_I32_TRUNC_U_F32 */
+ NULL, /* OP_I32_TRUNC_S_F64 */
+ NULL, /* OP_I32_TRUNC_U_F64 */
+ NULL, /* OP_I64_EXTEND_S_I32 */
+ NULL, /* OP_I64_EXTEND_U_I32 */
+ NULL, /* OP_I64_TRUNC_S_F32 */
+ NULL, /* OP_I64_TRUNC_U_F32 */
+ NULL, /* OP_I64_TRUNC_S_F64 */
+ NULL, /* OP_I64_TRUNC_U_F64 */
+ NULL, /* OP_F32_CONVERT_S_I32 */
+ NULL, /* OP_F32_CONVERT_U_I32 */
+ NULL, /* OP_F32_CONVERT_S_I64 */
+ NULL, /* OP_F32_CONVERT_U_I64 */
+ NULL, /* OP_F32_DEMOTE_F64 */
+ NULL, /* OP_F64_CONVERT_S_I32 */
+ NULL, /* OP_F64_CONVERT_U_I32 */
+ NULL, /* OP_F64_CONVERT_S_I64 */
+ NULL, /* OP_F64_CONVERT_U_I64 */
+ NULL, /* OP_F64_PROMOTE_F32 */
+ NULL, /* OP_I32_REINTERPRET_F32 */
+ NULL, /* OP_I64_REINTERPRET_F64 */
+ NULL, /* OP_F32_REINTERPRET_I32 */
+ NULL, /* OP_F64_REINTERPRET_I64 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL /* OP_MISC */
+};
+
+const struct nwp_ops nwp_ops =
+{
+ ops,
+ sizeof ops / sizeof *ops
+};
diff --git a/src/interp/resume.c b/src/interp/resume.c
new file mode 100644
index 0000000..cf9ece2
--- /dev/null
+++ b/src/interp/resume.c
@@ -0,0 +1,20 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nanowasm/types.h>
+#include <nw/interp.h>
+
+void nwp_interp_resume(struct nw_interp *const i)
+{
+ if (i->set)
+ nwp_interp_limited(i);
+ else
+ nwp_interp_full(i);
+}
diff --git a/src/interp/routines/CMakeLists.txt b/src/interp/routines/CMakeLists.txt
new file mode 100644
index 0000000..b27172b
--- /dev/null
+++ b/src/interp/routines/CMakeLists.txt
@@ -0,0 +1,13 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ execute.c
+ find_export.c
+ full.c
+ limited.c
+)
diff --git a/src/interp/routines/execute.c b/src/interp/routines/execute.c
new file mode 100644
index 0000000..49711ae
--- /dev/null
+++ b/src/interp/routines/execute.c
@@ -0,0 +1,83 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nw/interp.h>
+#include <nw/io.h>
+#include <nw/log.h>
+#include <nw/opcodes.h>
+#include <nw/ops.h>
+#include <nw/routines.h>
+
+static enum nw_state execute(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+
+#ifdef NW_LOG
+ nwp_log("opcode: %s, pc=%#lx\n", nwp_op_tostr(b->op), b->pc);
+#endif
+ b->f(i);
+ i->fr.prev_op = b->op;
+ return NW_AGAIN;
+}
+
+static enum nw_state repeat(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+ struct nw_next *const next = &b->next;
+ const enum nw_state n = next->fn(next->user, next);
+
+ if (n)
+ return n;
+
+ return execute(i);
+}
+
+static enum nw_state exec_pc(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const long pc = b->pc;
+
+ if (cfg->pc)
+ {
+ const enum nw_state n = cfg->pc(pc, &b->next, cfg->user);
+
+ if (n == NW_FATAL)
+ {
+ static const char *const exc = "pc callback failed";
+
+ i->exception = exc;
+#ifdef NW_LOG
+ nwp_log("%s, pc: %ld\n", exc, pc);
+#endif
+ return NW_FATAL;
+ }
+ else if (n)
+ {
+ i->next = repeat;
+ return n;
+ }
+ }
+
+ return execute(i);
+}
+
+enum nw_state nwp_execute(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const enum nw_state n = cfg->tell(&b->pc, cfg->user);
+
+ if (n)
+ return n;
+
+ b->pc -= sizeof b->op;
+ i->next = exec_pc;
+ return NW_AGAIN;
+}
diff --git a/src/interp/routines/find_export.c b/src/interp/routines/find_export.c
new file mode 100644
index 0000000..effa8d1
--- /dev/null
+++ b/src/interp/routines/find_export.c
@@ -0,0 +1,229 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nanowasm/types.h>
+#include <nw/interp.h>
+#include <nw/io.h>
+#include <nw/log.h>
+#include <nw/routines.h>
+#include <string.h>
+
+static enum nw_state entry_loop(struct nw_interp *i);
+
+static enum nw_state get_index(struct nw_interp *const i)
+{
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ struct nw_sm_leb128 *const l = &e->leb128;
+ const enum nw_state n = nwp_varuint32(cfg, l, &e->index, cfg->user);
+
+ if (n)
+ return n;
+
+ i->next = e->next;
+ return NW_AGAIN;
+}
+
+static enum nw_state get_kind(struct nw_interp *const i)
+{
+ unsigned char kind;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ struct nw_sm_io io = {0};
+ enum nw_state n;
+
+ io.buf = &kind;
+ io.n = sizeof kind;
+
+ if ((n = nwp_io_read(cfg, &io, cfg->user)))
+ return n;
+ else if (kind >= NW_KINDS)
+ {
+ static const char *const exc = "invalid export kind";
+
+ i->exception = exc;
+#ifdef NW_LOG
+ nwp_log("%s: %#x\n", exc, (unsigned)kind);
+#endif
+ return NW_FATAL;
+ }
+
+ e->kind = kind;
+ i->next = get_index;
+ return NW_AGAIN;
+}
+
+static enum nw_state skip_index(struct nw_interp *const i)
+{
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ struct nw_sm_leb128 *const l = &e->leb128;
+ nw_varuint32 index;
+ const enum nw_state n = nwp_varuint32(cfg, l, &index, cfg->user);
+
+ if (n)
+ return n;
+
+ e->entry_i++;
+ i->next = entry_loop;
+ return NW_AGAIN;
+}
+
+static enum nw_state skip_kind(struct nw_interp *const i)
+{
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ unsigned char b;
+ struct nw_sm_io io = {0};
+ enum nw_state n;
+
+ io.buf = &b;
+ io.n = sizeof b;
+
+ if ((n = nwp_io_read(cfg, &io, cfg->user)))
+ return n;
+
+ i->next = skip_index;
+ return NW_AGAIN;
+}
+
+static enum nw_state skip_field_str(struct nw_interp *const i)
+{
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const struct nw_i_sm_exp *const e = &i->sm.export;
+ const long offset = e->offset + (e->len - e->len_i);
+ const enum nw_state n = cfg->seek(offset, cfg->user);
+
+ if (n)
+ return n;
+
+ i->next = skip_kind;
+ return NW_AGAIN;
+}
+
+static enum nw_state tell(struct nw_interp *const i)
+{
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const enum nw_state n = cfg->tell(&e->offset, cfg->user);
+
+ if (n)
+ return n;
+
+ i->next = skip_field_str;
+ return NW_AGAIN;
+}
+
+static enum nw_state compare(struct nw_interp *const i)
+{
+ unsigned char b;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ struct nw_sm_io io = {0};
+ enum nw_state n;
+
+ io.buf = &b;
+ io.n = sizeof b;
+
+ if ((n = nwp_io_read(cfg, &io, cfg->user)))
+ return n;
+ else if (b != e->sym[e->len_i++])
+ i->next = tell;
+ else if (e->len_i >= e->len)
+ i->next = get_kind;
+
+ return NW_AGAIN;
+}
+
+static enum nw_state get_len(struct nw_interp *const i)
+{
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ struct nw_sm_leb128 *const l = &e->leb128;
+ const enum nw_state n = nwp_varuint32(cfg, l, &e->len, cfg->user);
+
+ if (n)
+ return n;
+ else if (e->len != strlen(e->sym))
+ i->next = skip_field_str;
+ else
+ i->next = compare;
+
+ return NW_AGAIN;
+}
+
+static enum nw_state entry_loop(struct nw_interp *const i)
+{
+ struct nw_i_sm_exp *const e = &i->sm.export;
+
+ if (e->entry_i >= e->count)
+ {
+ static const char *const exc = "failed to find symbol";
+
+ i->exception = exc;
+#ifdef NW_LOG
+ nwp_log("%s: %s\n", exc, e->sym);
+#endif
+ return NW_FATAL;
+ }
+
+ e->len_i = 0;
+ i->next = get_len;
+ return NW_AGAIN;
+}
+
+static enum nw_state get_count(struct nw_interp *const i)
+{
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ struct nw_i_sm_exp *const e = &i->sm.export;
+ struct nw_sm_leb128 *const l = &e->leb128;
+ const enum nw_state n = nwp_varuint32(cfg, l, &e->count, cfg->user);
+
+ if (n)
+ return n;
+
+ i->next = entry_loop;
+ return NW_AGAIN;
+}
+
+static enum nw_state seek(struct nw_interp *const i)
+{
+ const struct nw_mod *const m = i->cfg.m;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const long offset = m->sections[NW_SECTION_EXPORT];
+ enum nw_state n;
+
+ if (!offset)
+ {
+ static const char *const exc = "section not found";
+
+ i->exception = exc;
+#ifdef NW_LOG
+ nwp_log("%s: %s\n", exc, "export");
+#endif
+ return NW_FATAL;
+ }
+ else if ((n = cfg->seek(offset, cfg->user)))
+ return n;
+
+ i->next = get_count;
+ return NW_AGAIN;
+}
+
+void nwp_find_export(struct nw_interp *const i, const char *const sym,
+ enum nw_state (*const next)(struct nw_interp *))
+{
+ const struct nw_i_sm_exp e = {0};
+ struct nw_i_sm_exp *const pe = &i->sm.export;
+
+ *pe = e;
+ pe->sym = sym;
+ pe->next = next;
+ i->next = seek;
+}
diff --git a/src/interp/routines/full.c b/src/interp/routines/full.c
new file mode 100644
index 0000000..c00f11f
--- /dev/null
+++ b/src/interp/routines/full.c
@@ -0,0 +1,63 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nw/interp.h>
+#include <nw/io.h>
+#include <nw/log.h>
+#include <nw/opcodes.h>
+#include <nw/ops.h>
+#include <nw/routines.h>
+
+static enum nw_state tell(struct nw_interp *const i)
+{
+ long offset;
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+ static const char *const exc = "invalid opcode";
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const enum nw_state n = cfg->tell(&offset, cfg->user);
+
+ if (n)
+ return n;
+
+ offset -= sizeof b->op;
+
+#ifdef NW_LOG
+ nwp_log("%s: %#x, offset=%#lx\n", exc, (unsigned)b->op, offset);
+#endif
+ i->exception = exc;
+ return NW_FATAL;
+}
+
+static enum nw_state get_bytecode(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+ const struct nw_io_cfg *const cfg = &i->cfg.io;
+ const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user);
+
+ if (n)
+ return n;
+ else if (b->op >= nwp_ops.n || !(b->f = nwp_ops.ops[b->op]))
+ {
+ i->next = tell;
+ return NW_AGAIN;
+ }
+
+ i->next = nwp_execute;
+ return NW_AGAIN;
+}
+
+void nwp_interp_full(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const pb = &i->sm.bytecode, b = {0};
+
+ b.io.buf = &pb->op;
+ b.io.n = sizeof pb->op;
+ *pb = b;
+ i->next = get_bytecode;
+}
diff --git a/src/interp/routines/limited.c b/src/interp/routines/limited.c
new file mode 100644
index 0000000..dc18ded
--- /dev/null
+++ b/src/interp/routines/limited.c
@@ -0,0 +1,54 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nanowasm/types.h>
+#include <nw/interp.h>
+#include <nw/io.h>
+#include <nw/log.h>
+#include <nw/routines.h>
+#include <stddef.h>
+
+static enum nw_state get_bytecode(struct nw_interp *const in)
+{
+ const struct nw_io_cfg *const cfg = &in->cfg.io;
+ struct nw_i_sm_b *const b = &in->sm.bytecode;
+ const struct nwp_interp_set *const set = in->set;
+ static const char *const exc = "invalid opcode";
+ const enum nw_state n = nwp_io_read(cfg, &b->io, cfg->user);
+ size_t i;
+
+ if (n)
+ return n;
+
+ for (i = 0; i < set->n; i++)
+ if (b->op == set->opcodes[i])
+ {
+ b->f = nwp_ops.ops[b->op];
+ in->next = nwp_execute;
+ return NW_AGAIN;
+ }
+
+#ifdef NW_LOG
+ nwp_log("%s: %#x\n", exc, (unsigned)b->op);
+#endif
+ in->exception = exc;
+ return NW_FATAL;
+}
+
+void nwp_interp_limited(struct nw_interp *const i)
+{
+ struct nw_i_sm_b *const b = &i->sm.bytecode;
+ struct nw_sm_io io = {0};
+
+ io.buf = &b->op;
+ io.n = sizeof b->op;
+ b->io = io;
+ i->next = get_bytecode;
+}
diff --git a/src/interp/run.c b/src/interp/run.c
new file mode 100644
index 0000000..f5e80e6
--- /dev/null
+++ b/src/interp/run.c
@@ -0,0 +1,16 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+
+enum nw_state nwp_interp_run(struct nw_interp *const i)
+{
+ return i->next(i);
+}
diff --git a/src/interp/stack/CMakeLists.txt b/src/interp/stack/CMakeLists.txt
new file mode 100644
index 0000000..51c7cb5
--- /dev/null
+++ b/src/interp/stack/CMakeLists.txt
@@ -0,0 +1,14 @@
+# nanowasm, a tiny WebAssembly/Wasm interpreter
+# Copyright (C) 2023-2025 Xavier Del Campo Romero
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+target_sources(${PROJECT_NAME} PRIVATE
+ pop.c
+ push.c
+ ptr.c
+ read.c
+ write.c
+)
diff --git a/src/interp/stack/pop.c b/src/interp/stack/pop.c
new file mode 100644
index 0000000..03ed689
--- /dev/null
+++ b/src/interp/stack/pop.c
@@ -0,0 +1,37 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/log.h>
+#include <nw/stack.h>
+
+enum nw_state nwp_stack_pop(struct nw_interp *const i,
+ struct nw_sm_io *const io)
+{
+ void *const dst = (unsigned char *)io->buf + io->read;
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const struct nw_fifo_cfg *const fc = &cfg->stack;
+ const int n = fc->pop(dst, io->n - io->read, cfg->user);
+
+ if (n < 0)
+ {
+ static const char *const exc = "failed to pop from stack";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ return NW_FATAL;
+ }
+ else if ((io->read += n) >= io->n)
+ return NW_OK;
+
+ return NW_AGAIN;
+}
diff --git a/src/interp/stack/ptr.c b/src/interp/stack/ptr.c
new file mode 100644
index 0000000..d5fa455
--- /dev/null
+++ b/src/interp/stack/ptr.c
@@ -0,0 +1,21 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/stack.h>
+#include <stddef.h>
+
+size_t nwp_stack_ptr(const struct nw_interp *const i)
+{
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const struct nw_fifo_cfg *const fc = &cfg->stack;
+
+ return fc->ptr(cfg->user);
+}
diff --git a/src/interp/stack/push.c b/src/interp/stack/push.c
new file mode 100644
index 0000000..b8b48ce
--- /dev/null
+++ b/src/interp/stack/push.c
@@ -0,0 +1,37 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/log.h>
+#include <nw/stack.h>
+
+enum nw_state nwp_stack_push(struct nw_interp *const i,
+ struct nw_sm_io *const io)
+{
+ const void *const src = (const unsigned char *)io->buf + io->read;
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const struct nw_fifo_cfg *const fc = &cfg->stack;
+ const int n = fc->push(src, io->n - io->read, cfg->user);
+
+ if (n < 0)
+ {
+ static const char *const exc = "failed to push to stack";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ return NW_FATAL;
+ }
+ else if ((io->read += n) >= io->n)
+ return NW_OK;
+
+ return NW_AGAIN;
+}
diff --git a/src/interp/stack/read.c b/src/interp/stack/read.c
new file mode 100644
index 0000000..8db6bcc
--- /dev/null
+++ b/src/interp/stack/read.c
@@ -0,0 +1,37 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/log.h>
+#include <nw/stack.h>
+
+enum nw_state nwp_stack_read(struct nw_interp *const i,
+ struct nw_sm_io *const io, const size_t offset)
+{
+ void *const dst = (unsigned char *)io->buf + io->read;
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const struct nw_fifo_cfg *const fc = &cfg->stack;
+ const int n = fc->read(offset + io->read, dst, io->n - io->read, cfg->user);
+
+ if (n < 0)
+ {
+ static const char *const exc = "failed to read from stack";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ return NW_FATAL;
+ }
+ else if ((io->read += n) >= io->n)
+ return NW_OK;
+
+ return NW_AGAIN;
+}
diff --git a/src/interp/stack/write.c b/src/interp/stack/write.c
new file mode 100644
index 0000000..73e749e
--- /dev/null
+++ b/src/interp/stack/write.c
@@ -0,0 +1,38 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nw/interp.h>
+#include <nw/log.h>
+#include <nw/stack.h>
+
+enum nw_state nwp_stack_write(struct nw_interp *const i,
+ struct nw_sm_io *const io, const size_t offset)
+{
+ const void *const src = (const unsigned char *)io->buf + io->read;
+ const struct nw_interp_cfg *const cfg = &i->cfg;
+ const struct nw_fifo_cfg *const fc = &cfg->stack;
+ const int n = fc->write(offset + io->read, src, io->n - io->read,
+ cfg->user);
+
+ if (n < 0)
+ {
+ static const char *const exc = "failed to write to stack";
+
+#ifdef NW_LOG
+ nwp_log("%s\n", exc);
+#endif
+ i->exception = exc;
+ return NW_FATAL;
+ }
+ else if ((io->read += n) >= io->n)
+ return NW_OK;
+
+ return NW_AGAIN;
+}
diff --git a/src/interp/start.c b/src/interp/start.c
new file mode 100644
index 0000000..2bade41
--- /dev/null
+++ b/src/interp/start.c
@@ -0,0 +1,28 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <nanowasm/types.h>
+#include <nw/interp.h>
+
+int nwp_interp_start(struct nw_interp *const i,
+ const struct nw_interp_cfg *const cfg,
+ const struct nwp_interp_set *const set)
+{
+ const struct nw_interp in = {0};
+ const struct nw_mod_out *const mout = &cfg->m->out;
+
+ *i = in;
+ i->cfg = *cfg;
+ i->set = set;
+ i->table.n_pages = mout->table.initial;
+ i->linear.n_pages = mout->linear.initial;
+ nwp_interp_resume(i);
+ return 0;
+}