summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Buesch <m@bues.ch>2017-08-31 21:25:49 +0200
committerMichael Buesch <m@bues.ch>2017-08-31 21:25:49 +0200
commitec5f86da86cb60c071c42c3d9b3f4a3488140b7e (patch)
tree39d9831480debc5e75bccba6f0fab4171f42c569
downloadavremu-master.tar.xz
avremu-master.zip
Initial commitHEADmaster
Signed-off-by: Michael Buesch <m@bues.ch>
-rw-r--r--CMakeLists.txt5
-rw-r--r--client.c136
-rw-r--r--cmake.global13
-rw-r--r--emulator/CMakeLists.txt54
-rw-r--r--emulator/avr8emu.c407
-rw-r--r--emulator/avr8emu.h38
-rw-r--r--emulator/avr8emu_private.h60
-rw-r--r--emulator/cpu.c2667
-rw-r--r--emulator/cpu.h177
-rw-r--r--emulator/devices/ports.c259
-rw-r--r--emulator/devices/ports.h7
-rw-r--r--emulator/devices/timers.c28
-rw-r--r--emulator/devices/timers.h7
-rw-r--r--emulator/devprocess.c156
-rw-r--r--emulator/devprocess.h9
-rw-r--r--emulator/hardware.c270
-rw-r--r--emulator/hardware.h57
-rwxr-xr-xemulator/install.py50
-rw-r--r--emulator/ipc.c219
-rw-r--r--emulator/ipc.h62
-rw-r--r--emulator/list.h296
-rw-r--r--emulator/memory.c118
-rw-r--r--emulator/memory.h225
-rw-r--r--emulator/pinout.c129
-rw-r--r--emulator/pinout.h142
-rw-r--r--emulator/python.c206
-rw-r--r--emulator/subprocess.c167
-rw-r--r--emulator/subprocess.h116
-rw-r--r--emulator/util.c35
-rw-r--r--emulator/util.h74
-rwxr-xr-xgui/avr8emu156
31 files changed, 6345 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9021fa5
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,5 @@
+project(avremu C)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/cmake.global)
+
+add_subdirectory(emulator)
diff --git a/client.c b/client.c
new file mode 100644
index 0000000..af9e8ba
--- /dev/null
+++ b/client.c
@@ -0,0 +1,136 @@
+/*
+ * AVR8 emulator
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "emulator/avr8emu.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+
+
+static struct avr8emu *emulator;
+
+
+static void cb_status_message(struct avr8emu *emu, const char *msg)
+{
+ printf("MM %s", msg);
+}
+
+static const struct avr8emu_callbacks callbacks = {
+ .status_message = cb_status_message,
+};
+
+static void usage(char **argv)
+{
+ printf("Usage: %s BINARY_AVR_CODE\n", argv[0]);
+}
+
+static void signal_handler(int signr)
+{
+ int err;
+
+ if (emulator) {
+ do {
+ err = avr8emu_poll_status(emulator);
+ } while (!err);
+ avr8emu_remove(emulator);
+ }
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ struct sigaction sa;
+ const char *filename;
+ struct avr8emu *emu;
+ FILE *fd;
+ long int filesize;
+ char *bin;
+ int err;
+ size_t res;
+
+ if (argc != 2) {
+ usage(argv);
+ return 1;
+ }
+ filename = argv[1];
+
+ sa.sa_handler = signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ fd = fopen(filename, "r");
+ if (!fd) {
+ fprintf(stderr, "Could not open file %s\n", filename);
+ return 1;
+ }
+ err = fseek(fd, 0, SEEK_END);
+ if (err) {
+ fprintf(stderr, "Failed to seek in file %s\n", filename);
+ fclose(fd);
+ }
+ filesize = ftell(fd);
+ err = fseek(fd, 0, SEEK_SET);
+ if (err) {
+ fprintf(stderr, "Failed to seek in file %s\n", filename);
+ fclose(fd);
+ return 1;
+ }
+ bin = malloc(filesize);
+ if (!bin) {
+ fprintf(stderr, "Out of memory\n");
+ fclose(fd);
+ return 1;
+ }
+ res = fread(bin, filesize, 1, fd);
+ fclose(fd);
+ if (res != 1) {
+ fprintf(stderr, "Could not read file (%u)\n", res);
+ free(bin);
+ return 1;
+ }
+
+ emu = avr8emu_create(AVR_ATMEGA168, &callbacks);//FIXME
+ if (!emu) {
+ free(bin);
+ return 1;
+ }
+ err = avr8emu_initialize(emu, bin, filesize);
+ if (err) {
+ fprintf(stderr, "emulator init failed\n");
+ goto out;
+ }
+ err = avr8emu_reset(emu);
+ if (err) {
+ fprintf(stderr, "emulator reset failed\n");
+ goto out;
+ }
+ printf("emulator running...\n");
+ emulator = emu;
+ while (1) {
+ avr8emu_poll_messages(emu);
+ while (!avr8emu_poll_status(emu))
+ ;
+ }
+out:
+ avr8emu_remove(emu);
+ free(bin);
+}
diff --git a/cmake.global b/cmake.global
new file mode 100644
index 0000000..929a1ec
--- /dev/null
+++ b/cmake.global
@@ -0,0 +1,13 @@
+include(TestBigEndian)
+
+test_big_endian(BIGENDIAN)
+if (BIGENDIAN)
+ add_definitions(-DBIG_ENDIAN_HOST)
+else (BIGENDIAN)
+ add_definitions(-DLITTLE_ENDIAN_HOST)
+endif (BIGENDIAN)
+
+add_definitions("\"-Dinline=inline __attribute__((__always_inline__))\"")
+add_definitions("-D_GNU_SOURCE -D_BSD_SOURCE")
+
+set(AVREMU_CFLAGS "-std=gnu99 -O2 -fomit-frame-pointer -Wall -pipe")
diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt
new file mode 100644
index 0000000..2f44837
--- /dev/null
+++ b/emulator/CMakeLists.txt
@@ -0,0 +1,54 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/../cmake.global)
+
+include(FindPythonLibs)
+include(FindPythonInterp)
+
+include_directories(${PYTHON_INCLUDE_PATH})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../librazer)
+
+macro(ADD_PYTHON_MODULE _NAME _STATIC_SRCS)
+ if (NOT PYTHONINTERP_FOUND)
+ message(FATAL_ERROR "Python scripting language not found.")
+ endif (NOT PYTHONINTERP_FOUND)
+
+ add_library(${_NAME} MODULE ${ARGN})
+
+ set_target_properties(${_NAME} PROPERTIES
+ PREFIX "" # Remove the "lib" prefix
+ )
+
+ install(CODE "exec_program(${PYTHON_EXECUTABLE}
+ ARGS ${CMAKE_CURRENT_SOURCE_DIR}/install.py ${CMAKE_CURRENT_BINARY_DIR}
+ RETURN_VALUE err)
+ if (NOT err EQUAL 0)
+ message(FATAL_ERROR \"Failed to install python module ${_NAME}\")
+ endif (NOT err EQUAL 0)")
+endmacro (ADD_PYTHON_MODULE)
+
+
+option(RUNTIME_SANITY_CHECKS "Enable runtime sanity checks" ON)
+if (RUNTIME_SANITY_CHECKS)
+ add_definitions(-DRUNTIME_SANITY_CHECKS)
+endif (RUNTIME_SANITY_CHECKS)
+
+# Built-in devices
+set(SRC_DEVICES devices/ports.c
+ devices/timers.c)
+
+# The emulator core
+set(SRC_CORE cpu.c
+ devprocess.c
+ hardware.c
+ ipc.c
+ memory.c
+ pinout.c
+ python.c
+ subprocess.c
+ util.c)
+
+add_python_module(pyavr8emu SHARED ${SRC_CORE} ${SRC_DEVICES})
+
+set_target_properties(pyavr8emu PROPERTIES
+ COMPILE_FLAGS "${AVREMU_CFLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}")
+target_link_libraries(pyavr8emu pthread rt)
+
diff --git a/emulator/avr8emu.c b/emulator/avr8emu.c
new file mode 100644
index 0000000..cbc808b
--- /dev/null
+++ b/emulator/avr8emu.c
@@ -0,0 +1,407 @@
+/*
+ * AVR8 emulator
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "avr8emu.h"
+#include "avr8emu_private.h"
+#include "util.h"
+#include "cpu.h"
+#include "hardware.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+
+
+static void __attribute__((noreturn)) worker_shutdown(int error)
+{
+ cpu_exit();
+ avr_do_cleanup();
+ exit(error);
+}
+
+static void worker_signal_handler(int signr)
+{
+ switch (signr) {
+ case SIGSEGV:
+ fprintf(stderr, "Segmentation fault\n");
+ if (CHECK(1))
+ fprintf(stderr, "This is probably a bug in the emulator.\n");
+ else
+ fprintf(stderr, "This is either a bug in the running AVR "
+ "program or in the emulator software.\n");
+ dump_cpu(stderr);
+ worker_shutdown(1);
+ break;
+ case SIGTERM:
+ case SIGINT:
+ printf("Emulation worker shutting down...\n");
+ trap_cpu(CPU_TRAP_EXIT, 1);
+ dump_cpu(stdout);
+ worker_shutdown(0);
+ break;
+ default:
+ fprintf(stderr, "Worker caught unknown signal\n");
+ worker_shutdown(1);
+ }
+}
+
+static void worker_send_result(struct avr8emu_worker *wrk, int result)
+{
+ ssize_t res;
+
+ res = write(wrk->result_to_host, &result, sizeof(result));
+ if (res != sizeof(result))
+ fprintf(stderr, "Failed to send result to host.\n");
+}
+
+static char * receive_payload(struct avr8emu_worker *wrk, uint32_t payload_len)
+{
+ char *buf;
+ ssize_t res;
+
+ buf = malloc(payload_len);
+ if (!buf) {
+ fprintf(stderr, "Out of memory\n");
+ return NULL;
+ }
+ res = read(wrk->message_from_host, buf, payload_len);
+ if (res != payload_len) {
+ fprintf(stderr, "Payload receive error\n");
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+static void free_payload(char *buf, uint32_t payload_len)
+{
+ free(buf);
+}
+
+static void worker_handle_message(struct avr8emu_worker *wrk,
+ enum avr8emu_worker_message id,
+ uint32_t payload_len)
+{
+ char *buf;
+ int res;
+
+ switch (id) {
+ case WORKERMSG_INIT:
+ buf = receive_payload(wrk, payload_len);
+ if (!buf)
+ return;
+ res = cpu_initialize(buf, payload_len);
+ free_payload(buf, payload_len);
+ break;
+ case WORKERMSG_RESET:
+ res = cpu_reset();
+ break;
+ default:
+ res = -EINVAL;
+ fprintf(stderr, "Worker received unknown message.\n");
+ }
+ worker_send_result(wrk, res);
+}
+
+/* This is an emulator worker process. */
+static int worker_process(struct avr8emu_worker *wrk)
+{
+ struct sigaction sa;
+ fd_set fdset;
+ int err;
+ ssize_t len;
+ enum avr8emu_worker_message id;
+ uint32_t payload_len;
+
+ sa.sa_handler = worker_signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ err = avr_do_setup(wrk->type);
+ if (err)
+ return err;
+
+ FD_ZERO(&fdset);
+ FD_SET(wrk->message_from_host, &fdset);
+
+ while (1) {
+ err = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
+ if (err <= 0) {
+ if (errno == EINTR)
+ continue;
+ perror("Worker pipe select");
+ return 1;
+ }
+
+ len = read(wrk->message_from_host, &id, sizeof(id));
+ if (len != sizeof(id)) {
+ fprintf(stderr, "Worker ID receive error\n");
+ return 1;
+ }
+ len = read(wrk->message_from_host, &payload_len, sizeof(payload_len));
+ if (len != sizeof(payload_len)) {
+ fprintf(stderr, "Worker payload size receive error\n");
+ return 1;
+ }
+ worker_handle_message(wrk, id, payload_len);
+ }
+
+ return 1;
+}
+
+void send_host_message(enum avr8emu_host_message msg,
+ const void *payload, uint32_t payload_len)
+{//TODO
+}
+
+static int send_worker_message(struct avr8emu *emu,
+ enum avr8emu_worker_message msg,
+ const void *payload, uint32_t payload_len)
+{
+ ssize_t res;
+ int result_code;
+
+ res = write(emu->message_to_worker, &msg, sizeof(msg));
+ if (res != sizeof(msg)) {
+ fprintf(stderr, "Failed to send message ID\n");
+ return 1;
+ }
+ res = write(emu->message_to_worker, &payload_len, sizeof(payload_len));
+ if (res != sizeof(payload_len)) {
+ fprintf(stderr, "Failed to send payload size\n");
+ return 1;
+ }
+ if (payload_len) {
+ res = write(emu->message_to_worker, payload, payload_len);
+ if (res != payload_len) {
+ fprintf(stderr, "Failed to send payload\n");
+ return 1;
+ }
+ }
+ /* Read the result code. */
+ res = read(emu->result_from_worker, &result_code, sizeof(result_code));
+ if (res != sizeof(result_code)) {
+ fprintf(stderr, "Failed to read message result code\n");
+ return 1;
+ }
+ if (result_code)
+ fprintf(stderr, "Message %d failed with %d\n", msg, result_code);
+
+ return result_code;
+}
+
+static void close_two(int fds[2])
+{
+ close(fds[0]);
+ close(fds[1]);
+}
+
+static int fork_worker(struct avr8emu *emu)
+{
+ struct avr8emu_worker *wrk;
+ int err;
+ pid_t pid;
+ int host_pipes[2];
+ int worker_pipes[2];
+ int result_pipes[2];
+ int error_pipes[2];
+
+ err = pipe(host_pipes);
+ if (err) {
+ perror("Host communication pipes");
+ return err;
+ }
+ err = pipe(worker_pipes);
+ if (err) {
+ perror("Worker communication pipes");
+ close_two(host_pipes);
+ return err;
+ }
+ err = pipe(result_pipes);
+ if (err) {
+ perror("Result communication pipes");
+ close_two(host_pipes);
+ close_two(worker_pipes);
+ return err;
+ }
+ err = pipe(error_pipes);
+ if (err) {
+ perror("Error communication pipes");
+ close_two(host_pipes);
+ close_two(worker_pipes);
+ close_two(result_pipes);
+ }
+
+ pid = fork();
+ if (pid == (pid_t)0) {
+ /* This is the worker process. */
+ close(host_pipes[0]);
+ close(worker_pipes[1]);
+ close(result_pipes[0]);
+ close(error_pipes[0]);
+ wrk = avr_malloc(sizeof(*wrk));
+ if (!wrk)
+ exit(1);
+ wrk->type = emu->type;
+ wrk->message_to_host = host_pipes[1];
+ wrk->message_from_host = worker_pipes[0];
+ wrk->result_to_host = result_pipes[1];
+ wrk->error_pipe = error_pipes[1];
+ /* Map stderr and stdout to the error_pipe. Enable line-buffering. */
+ dup2(wrk->error_pipe, STDERR_FILENO);
+ dup2(wrk->error_pipe, STDOUT_FILENO);
+ setvbuf(stdout, NULL, _IOLBF, 0);
+ setvbuf(stderr, NULL, _IOLBF, 0);
+ /* Finally run the worker. */
+ err = worker_process(wrk);
+ exit(err);
+ } else if (pid < (pid_t)0) {
+ perror("Forking worker process");
+ close_two(host_pipes);
+ close_two(worker_pipes);
+ close_two(result_pipes);
+ close_two(error_pipes);
+ return -ENOMEM;
+ } else {
+ /* This is the host process. */
+ close(host_pipes[1]);
+ close(worker_pipes[0]);
+ close(result_pipes[1]);
+ close(error_pipes[1]);
+ emu->message_to_worker = worker_pipes[1];
+ emu->message_from_worker = host_pipes[0];
+ emu->result_from_worker = result_pipes[0];
+ emu->error_pipe = error_pipes[0];
+ emu->worker_pid = pid;
+ }
+
+ return 0;
+}
+
+static void kill_worker(struct avr8emu *emu)
+{
+ int err;
+
+ err = kill(emu->worker_pid, SIGTERM);
+ if (err) {
+ perror("Kill worker child process");
+ return;
+ }
+ waitpid(emu->worker_pid, NULL, 0);
+}
+
+struct avr8emu * avr8emu_create(enum avr_setup_type type,
+ const struct avr8emu_callbacks *cb)
+{
+ struct avr8emu *emu;
+ int err;
+
+ if (!cb)
+ return NULL;
+ emu = avr_malloc(sizeof(*emu));
+ if (!emu)
+ return NULL;
+ emu->type = type;
+ emu->callback = cb;
+ err = fork_worker(emu);
+ if (err) {
+ free(emu);
+ return NULL;
+ }
+
+ return emu;
+}
+
+void avr8emu_remove(struct avr8emu *emu)
+{
+ kill_worker(emu);
+ free(emu);
+}
+
+int avr8emu_initialize(struct avr8emu *emu, const char *code,
+ size_t code_size)
+{
+ return send_worker_message(emu, WORKERMSG_INIT, code, code_size);
+}
+
+int avr8emu_reset(struct avr8emu *emu)
+{
+ return send_worker_message(emu, WORKERMSG_RESET, NULL, 0);
+}
+
+void avr8emu_poll_messages(struct avr8emu *emu)
+{
+ fd_set fdset;
+ struct timeval tv;
+ int err;
+
+ FD_ZERO(&fdset);
+ FD_SET(emu->message_from_worker, &fdset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 5000;
+
+ err = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
+ if (err > 0) {
+ //TODO handle.
+ }
+}
+
+int avr8emu_poll_status(struct avr8emu *emu)
+{
+ char *buf = NULL;
+ size_t bufsize = 0;
+ ssize_t res;
+ FILE *file;
+ fd_set fdset;
+ int err = -1;
+ struct timeval tv;
+
+ FD_ZERO(&fdset);
+ FD_SET(emu->error_pipe, &fdset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 5000;
+
+ res = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
+ if (res > 0) {
+ file = fdopen(emu->error_pipe, "r");
+ if (!file) {
+ fprintf(stderr, "Failed to open error pipe\n");
+ return -1;
+ }
+ while (1) {
+ res = getline(&buf, &bufsize, file);
+ if (res <= 1)
+ break;
+ if (emu->callback)
+ emu->callback->status_message(emu, buf);
+ err = 0;
+ }
+ free(buf);
+ fclose(file);
+ }
+
+ return err;
+}
diff --git a/emulator/avr8emu.h b/emulator/avr8emu.h
new file mode 100644
index 0000000..104c9e3
--- /dev/null
+++ b/emulator/avr8emu.h
@@ -0,0 +1,38 @@
+#ifndef AVR8EMU_LIBRARY_H_
+#define AVR8EMU_LIBRARY_H_
+
+#include <stdlib.h>
+
+
+/* Type of an emulator instance. */
+struct avr8emu;
+
+
+
+struct avr8emu_callbacks {
+ void (*status_message)(struct avr8emu *emu, const char *msg);
+};
+
+/* Create a new emulator instance. */
+struct avr8emu * avr8emu_create(enum avr_setup_type type,
+ const struct avr8emu_callbacks *cb);
+/* Delete an emulator instance. */
+void avr8emu_remove(struct avr8emu *emu);
+
+/* Initialize the microcontroller. */
+int avr8emu_initialize(struct avr8emu *emu, const char *code,
+ size_t code_size);
+
+/* Reset the microcontroller. This will start execution of
+ * the code if invoked for the first time. */
+int avr8emu_reset(struct avr8emu *emu);
+
+/* Call this on a regular basis to poll any messages from the
+ * worker process. */
+void avr8emu_poll_messages(struct avr8emu *emu);
+/* Call this on a regular basis to poll any status messages
+ * from the worker process. */
+int avr8emu_poll_status(struct avr8emu *emu);
+
+
+#endif /* AVR8EMU_LIBRARY_H_ */
diff --git a/emulator/avr8emu_private.h b/emulator/avr8emu_private.h
new file mode 100644
index 0000000..98ca901
--- /dev/null
+++ b/emulator/avr8emu_private.h
@@ -0,0 +1,60 @@
+#ifndef AVR8EMU_LIBRARY_PRIVATE_H_
+#define AVR8EMU_LIBRARY_PRIVATE_H_
+
+#include "util.h"
+#include "avr8emu.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdarg.h>
+
+
+struct avr8emu {
+ /* Type of microcontroller we are emulating. */
+ enum avr_setup_type type;
+ /* Pipes for communication with the worker. */
+ int message_to_worker; /* Result is received in result_from_worker */
+ int result_from_worker; /* Result for message_to_worker */
+ int message_from_worker; /* Has no result pipe */
+ /* Pipe for receiving error and status messages from the worker. */
+ int error_pipe;
+ /* The worker process PID. */
+ pid_t worker_pid;
+ /* Callbacks to the emulator controller. */
+ const struct avr8emu_callbacks *callback;
+};
+
+struct avr8emu_worker {
+ /* Type of microcontroller we are emulating. */
+ enum avr_setup_type type;
+ /* Pipes for communication with the host. */
+ int message_to_host; /* Has no result pipe */
+ int message_from_host; /* Result is send through result_to_host */
+ int result_to_host; /* Result for message_from_host */
+ /* Pipe for sending error and status messages to the host. */
+ int error_pipe;
+ /* True, if the microcontroller is initialized. */
+ bool initialized;
+};
+
+#define AVR8EMU_MAX_MSG_SIZE (1024 * 8)
+
+/* Message from host to worker. */
+enum avr8emu_worker_message {
+ WORKERMSG_INIT,
+ WORKERMSG_RESET,
+};
+
+
+/* Messaging to the host. (From worker). This is reentrant safe, so it
+ * can be called from multiple worker threads. */
+enum avr8emu_host_message {
+ HOSTMSG_PORTIO, /* I/O access to an I/O port. */
+};
+
+void send_host_message(enum avr8emu_host_message msg,
+ const void *payload, uint32_t payload_len);
+
+
+#endif /* AVR8EMU_LIBRARY_PRIVATE_H_ */
diff --git a/emulator/cpu.c b/emulator/cpu.c
new file mode 100644
index 0000000..01512a5
--- /dev/null
+++ b/emulator/cpu.c
@@ -0,0 +1,2667 @@
+/*
+ * AVR emulator
+ * CPU emulation
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "cpu.h"
+#include "util.h"
+#include "hardware.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+
+/* The CPU */
+struct avr_cpu cpu;
+/* The flash memory */
+static char *code;
+
+/* The POSIX CPU thread. */
+static pthread_t cpu_thread;
+
+
+static uint8_t cpu_get_sreg(void);
+
+
+static inline bool in_cpu_thread(void)
+{
+ return !!pthread_equal(pthread_self(), cpu_thread);
+}
+
+/* Get the ID of the instruction that modified this lazy flag. */
+static inline uint8_t lazy_flag_get_insn(const struct lazy_flag *f)
+{
+ return (uint8_t)(f - cpu.lazy_flag_cache);
+}
+
+/* Get the stack pointer. */
+static inline uint16_t getSP(void)
+{
+ return le16_to_cpu(*((le16_t *)&memory.io[memory.sp_offset]));
+}
+
+/* Write the stack pointer. */
+static inline void setSP(uint16_t sp)
+{
+ *((le16_t *)&memory.io[memory.sp_offset]) = cpu_to_le16(sp);
+}
+
+/* Get the X pointer */
+static inline uint16_t getX(void)
+{
+ const uint8_t *x = &(cpu.r[X_LO]);
+ return le16_to_cpu(*(le16_t *)x);
+}
+
+/* Set the X pointer */
+static inline void setX(uint16_t val)
+{
+ le16_t *x = (le16_t *)(&(cpu.r[X_LO]));
+ *x = cpu_to_le16(val);
+}
+
+/* Get the Y pointer */
+static inline uint16_t getY(void)
+{
+ const uint8_t *y = &(cpu.r[Y_LO]);
+ return le16_to_cpu(*(le16_t *)y);
+}
+
+/* Set the Y pointer */
+static inline void setY(uint16_t val)
+{
+ le16_t *y = (le16_t *)(&(cpu.r[Y_LO]));
+ *y = cpu_to_le16(val);
+}
+
+/* Get the Z pointer */
+static inline uint16_t getZ(void)
+{
+ const uint8_t *z = &(cpu.r[Z_LO]);
+ return le16_to_cpu(*(le16_t *)z);
+}
+
+/* Set the Z pointer */
+static inline void setZ(uint16_t val)
+{
+ le16_t *z = (le16_t *)(&(cpu.r[Z_LO]));
+ *z = cpu_to_le16(val);
+}
+
+static void dump_stack(FILE *fd)
+{
+ uint16_t sp;
+ int i = 0, count = 40;
+
+ sp = getSP();
+ if (sp >= memory.size) {
+ fprintf(fd, "Invalid stackpointer. Not dumping.\n");
+ return;
+ }
+ fprintf(fd, "Stackdump (%d bytes):\n", count);
+ while (i < count && sp && sp < memory.size) {
+ if ((i != 0) && (i % 4 == 0))
+ fprintf(fd, "\n");
+ fprintf(fd, "<0x%04X>: 0x%02X ", sp, memory.ram[sp]);
+ sp++;
+ i++;
+ }
+ fprintf(fd, "\n");
+}
+
+#if 0
+static void dump_lazy_flag(FILE *fd, const struct lazy_flag *f)
+{
+ fprintf(fd, "insn: 0x%02X op[0]: 0x%02X op[1]: 0x%02X res: 0x%02X "
+ "op16[0]: 0x%04X op16[1]: 0x%04X res16: 0x%04X\n",
+ lazy_flag_get_insn(f), f->op[0], f->op[1], f->res,
+ f->op16[0], f->op16[1], f->res16);
+}
+
+static void dump_lazy_flags(FILE *fd)
+{
+ struct lazy_flag *f;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cpu.sreg_lazy); i++) {
+ f = cpu.sreg_lazy[i];
+ if (f) {
+ fprintf(fd, "LF%d -> ", i);
+ dump_lazy_flag(fd, cpu.sreg_lazy[i]);
+ }
+ }
+}
+#endif
+
+void dump_cpu(FILE *fd)
+{
+ int i;
+ uint8_t sreg;
+
+ fprintf(fd, "=== CPU dump ===\n");
+ fprintf(fd, "R: %u PC: 0x%08X (0x%08X) SP: 0x%04X Cycles: %llu\n",
+ (unsigned)cpu.running, cpu.pc, cpu.pc * 2, getSP(),
+ (unsigned long long)cpu.cycles_count);
+ sreg = cpu_get_sreg();
+ fprintf(fd, "SREG: 0x%02X IRQs: %llu TRAP: %u\n",
+ sreg, (unsigned long long)cpu.irq_count,
+ reload_var(cpu.trap));
+ if (cpu.r) {
+ for (i = 0; i < AVR_NR_REGS; i++) {
+ if ((i != 0) && (i % 4 == 0))
+ fprintf(fd, "\n");
+ fprintf(fd, "r%02u: 0x%02X ", i, cpu.r[i]);
+ }
+ fprintf(fd, "\n");
+ fprintf(fd, "X: 0x%04X Y: 0x%04X Z: 0x%04X\n",
+ getX(), getY(), getZ());
+ } else
+ fprintf(fd, "Invalid register file. Not dumping.\n");
+ dump_stack(fd);
+}
+
+static void __attribute__((__noreturn__)) fault(const char *fmt, ...)
+{
+ va_list va;
+
+ fprintf(stderr, "\n---=== FAULT ===---\n");
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+ fprintf(stderr, "\n");
+ dump_cpu(stderr);
+ if (in_cpu_thread()) {
+ pthread_exit(NULL);
+ } else {
+ fprintf(stderr, "\n>>> BUG: Faulting from outside of the CPU! <<<\n");
+ exit(1);
+ }
+ while (1) ;
+}
+
+/* Get the SREG Carry flag. */
+static bool sreg_get_C(void)
+{
+ const struct lazy_flag *f = cpu.sreg_lazy[SREG_C_BIT];
+ bool set;
+
+ if (cpu.sreg_valid & SREG_C)
+ return !!(cpu.sreg & SREG_C);
+
+ switch (lazy_flag_get_insn(f)) {
+ case INSN_SBCI:
+ case INSN_SUBI:
+ case INSN_CPI:
+ case INSN_CPC:
+ case INSN_SBC:
+ case INSN_CP:
+ case INSN_SUB:
+ set = !!(((~(f->op[0]) & f->op[1]) |
+ (f->op[1] & f->res) |
+ (f->res & ~(f->op[0]))) & (1 << 7));
+ break;
+ case INSN_ADD:
+ case INSN_ADC:
+ set = !!(((f->op[0] & f->op[1]) |
+ (f->op[1] & ~(f->res)) |
+ (~(f->res) & f->op[0])) & (1 << 7));
+ break;
+ case INSN_COM:
+ set = 1;
+ break;
+ case INSN_NEG:
+ set = !!(f->res);
+ break;
+ case INSN_LSR:
+ case INSN_ROR:
+ case INSN_ASR:
+ set = !!(f->op[0] & 0x01);
+ break;
+ case INSN_ADIW:
+ set = (!!(~(f->res16) & 0x8000) & !!(f->op16[0] & 0x8000));
+ break;
+ case INSN_SBIW:
+ set = (!!(f->res16 & 0x8000) & !!(~(f->op16[0]) & 0x8000));
+ break;
+ default:
+ if (!in_cpu_thread())
+ return 0;
+ fault("Lazy C flag: Unhandled instruction %u",
+ lazy_flag_get_insn(f));
+ }
+ cpu.sreg &= ~SREG_C;
+ cpu.sreg |= (set << SREG_C_BIT);
+ cpu.sreg_valid |= SREG_C;
+
+ return set;
+}
+
+/* Set the SREG Carry flag. */
+static inline void sreg_set_C(bool C)
+{
+ cpu.sreg &= ~SREG_C;
+ cpu.sreg |= (C << SREG_C_BIT);
+ cpu.sreg_valid |= SREG_C;
+}
+
+/* Set the SREG Carry flag. LAZY */
+static inline void sreg_set_C_lazy(uint8_t insn)
+{
+ cpu.sreg_lazy[SREG_C_BIT] = &(cpu.lazy_flag_cache[insn]);
+}
+
+/* Get the SREG Zero flag. */
+static bool sreg_get_Z(void)
+{
+ const struct lazy_flag *f = cpu.sreg_lazy[SREG_Z_BIT];
+ bool set;
+
+ if (cpu.sreg_valid & SREG_Z)
+ return !!(cpu.sreg & SREG_Z);
+
+ switch (lazy_flag_get_insn(f)) {
+ case INSN_SBCI:
+ case INSN_CPC:
+ case INSN_SBC:
+ if (f->res == 0) {
+ /* Take the previously set Z flag. */
+ set = !!(cpu.sreg & SREG_Z);
+ } else
+ set = 0;
+ break;
+ case INSN_SUBI:
+ case INSN_ORI:
+ case INSN_ANDI:
+ case INSN_CPI:
+ case INSN_COM:
+ case INSN_NEG:
+ case INSN_DEC:
+ case INSN_INC:
+ case INSN_LSR:
+ case INSN_ROR:
+ case INSN_ASR:
+ case INSN_ADD:
+ case INSN_CP:
+ case INSN_SUB:
+ case INSN_ADC:
+ case INSN_AND:
+ case INSN_EOR:
+ case INSN_OR:
+ set = (f->res == 0);
+ break;
+ case INSN_ADIW:
+ case INSN_SBIW:
+ set = (f->res16 == 0);
+ break;
+ default:
+ if (!in_cpu_thread())
+ return 0;
+ fault("Lazy Z flag: Unhandled instruction %u",
+ lazy_flag_get_insn(f));
+ }
+ cpu.sreg &= ~SREG_Z;
+ cpu.sreg |= (set << SREG_Z_BIT);
+ cpu.sreg_valid |= SREG_Z;
+
+ return set;
+}
+
+/* Set the SREG Zero flag. */
+static inline void sreg_set_Z(bool Z)
+{
+ cpu.sreg &= ~SREG_Z;
+ cpu.sreg |= (Z << SREG_Z_BIT);
+ cpu.sreg_valid |= SREG_Z;
+}
+
+/* Set the SREG Zero flag. LAZY */
+static inline void sreg_set_Z_lazy(uint8_t insn)
+{
+ cpu.sreg_lazy[SREG_Z_BIT] = &(cpu.lazy_flag_cache[insn]);
+}
+
+/* Get the SREG Negative flag. */
+static bool sreg_get_N(void)
+{
+ const struct lazy_flag *f = cpu.sreg_lazy[SREG_N_BIT];
+ bool set;
+
+ if (cpu.sreg_valid & SREG_N)
+ return !!(cpu.sreg & SREG_N);
+
+ switch (lazy_flag_get_insn(f)) {
+ case INSN_SBCI:
+ case INSN_SUBI:
+ case INSN_ORI:
+ case INSN_ANDI:
+ case INSN_CPI:
+ case INSN_COM:
+ case INSN_NEG:
+ case INSN_DEC:
+ case INSN_INC:
+ case INSN_ROR:
+ case INSN_ASR:
+ case INSN_CPC:
+ case INSN_SBC:
+ case INSN_ADD:
+ case INSN_CP:
+ case INSN_SUB:
+ case INSN_ADC:
+ case INSN_AND:
+ case INSN_EOR:
+ case INSN_OR:
+ set = !!(f->res & 0x80);
+ break;
+ case INSN_LSR:
+ set = 0;
+ break;
+ case INSN_ADIW:
+ case INSN_SBIW:
+ set = !!(f->res16 & 0x8000);
+ break;
+ default:
+ if (!in_cpu_thread())
+ return 0;
+ fault("Lazy N flag: Unhandled instruction %u",
+ lazy_flag_get_insn(f));
+ }
+ cpu.sreg &= ~SREG_N;
+ cpu.sreg |= (set << SREG_N_BIT);
+ cpu.sreg_valid |= SREG_N;
+
+ return set;
+}
+
+/* Set the SREG Negative flag. */
+static inline void sreg_set_N(bool N)
+{
+ cpu.sreg &= ~SREG_N;
+ cpu.sreg |= (N << SREG_N_BIT);
+ cpu.sreg_valid |= SREG_N;
+}
+
+/* Set the SREG Negative flag. LAZY */
+static inline void sreg_set_N_lazy(uint8_t insn)
+{
+ cpu.sreg_lazy[SREG_N_BIT] = &(cpu.lazy_flag_cache[insn]);
+}
+
+/* Get the SREG two's complement oVerflow flag. */
+static bool sreg_get_V(void)
+{
+ const struct lazy_flag *f = cpu.sreg_lazy[SREG_V_BIT];
+ bool set;
+
+ if (cpu.sreg_valid & SREG_V)
+ return !!(cpu.sreg & SREG_V);
+
+ switch (lazy_flag_get_insn(f)) {
+ case INSN_SBCI:
+ case INSN_SUBI:
+ case INSN_CPI:
+ case INSN_CPC:
+ case INSN_SBC:
+ case INSN_CP:
+ case INSN_SUB:
+ set = !!(((f->op[0] & ~(f->op[1]) & ~(f->res)) |
+ (~(f->op[0]) & f->op[1] & f->res)) & (1 << 7));
+ break;
+ case INSN_ADD:
+ case INSN_ADC:
+ set = !!(((f->op[0] & f->op[1] & ~(f->res)) |
+ (~(f->op[0]) & ~(f->op[1]) & f->res)) & (1 << 7));
+ break;
+ case INSN_ORI:
+ case INSN_ANDI:
+ case INSN_COM:
+ case INSN_AND:
+ case INSN_EOR:
+ case INSN_OR:
+ set = 0;
+ break;
+ case INSN_NEG:
+ case INSN_INC:
+ set = (f->res == 0x80);
+ break;
+ case INSN_DEC:
+ set = (f->op[0] == 0x80);
+ break;
+ case INSN_LSR:
+ set = !!(0 ^ (f->op[0] & 0x01));
+ break;
+ case INSN_ROR:
+ case INSN_ASR:
+ set = (!!(f->res & 0x80)) ^ (!!(f->op[0] & 0x01));
+ break;
+ case INSN_ADIW:
+ set = (!!(~(f->op16[0]) & 0x8000) & !!(f->res16 & 0x8000));
+ break;
+ case INSN_SBIW:
+ set = (!!(f->op16[0] & 0x8000) & !!(~(f->res16) & 0x8000));
+ break;
+ default:
+ if (!in_cpu_thread())
+ return 0;
+ fault("Lazy V flag: Unhandled instruction %u",
+ lazy_flag_get_insn(f));
+ }
+ cpu.sreg &= ~SREG_V;
+ cpu.sreg |= (set << SREG_V_BIT);
+ cpu.sreg_valid |= SREG_V;
+
+ return set;
+}
+
+/* Set the SREG two's complement oVerflow flag. */
+static inline void sreg_set_V(bool V)
+{
+ cpu.sreg &= ~SREG_V;
+ cpu.sreg |= (V << SREG_V_BIT);
+ cpu.sreg_valid |= SREG_V;
+}
+
+/* Set the SREG two's complement oVerflow flag. LAZY */
+static inline void sreg_set_V_lazy(uint8_t insn)
+{
+ cpu.sreg_lazy[SREG_V_BIT] = &(cpu.lazy_flag_cache[insn]);
+}
+
+/* Get the SREG Signed flag. */
+static bool sreg_get_S(void)
+{
+ bool set;
+
+ if (cpu.sreg_valid & SREG_S)
+ return !!(cpu.sreg & SREG_S);
+
+ /* S is always N XOR V. */
+ set = sreg_get_N() ^ sreg_get_V();
+
+ cpu.sreg &= ~SREG_S;
+ cpu.sreg |= (set << SREG_S_BIT);
+ cpu.sreg_valid |= SREG_S;
+
+ return set;
+}
+
+static inline void sreg_set_S(bool S)
+{
+ cpu.sreg &= ~SREG_S;
+ cpu.sreg |= (S << SREG_S_BIT);
+ cpu.sreg_valid |= SREG_S;
+}
+
+static inline void sreg_set_S_lazy(uint8_t insn)
+{
+ /* We don't need to set the pointer here, as S is calculated
+ * from the N and V flags. */
+ /*
+ cpu.sreg_lazy[SREG_S_BIT] = &(cpu.lazy_flag_cache[insn]);
+ */
+}
+
+/* Get the SREG Halfcarry flag. */
+static bool sreg_get_H(void)
+{
+ const struct lazy_flag *f = cpu.sreg_lazy[SREG_H_BIT];
+ bool set;
+
+ if (cpu.sreg_valid & SREG_H)
+ return !!(cpu.sreg & SREG_H);
+
+ switch (lazy_flag_get_insn(f)) {
+ case INSN_SBCI:
+ case INSN_SUBI:
+ case INSN_CPI:
+ case INSN_CPC:
+ case INSN_SBC:
+ case INSN_CP:
+ case INSN_SUB:
+ set = !!(((~(f->op[0]) & f->op[1]) |
+ (f->op[1] & f->res) |
+ (f->res & ~(f->op[0]))) & (1 << 3));
+ break;
+ case INSN_ADD:
+ case INSN_ADC:
+ set = !!(((f->op[0] & f->op[1]) |
+ (f->op[1] & ~(f->res)) |
+ (~(f->res) & f->op[0])) & (1 << 3));
+ break;
+ case INSN_NEG:
+ set = !!((f->res | f->op[0]) & (1 << 3));
+ break;
+ default:
+ if (!in_cpu_thread())
+ return 0;
+ fault("Lazy H flag: Unhandled instruction %u",
+ lazy_flag_get_insn(f));
+ }
+ cpu.sreg &= ~SREG_H;
+ cpu.sreg |= (set << SREG_H_BIT);
+ cpu.sreg_valid |= SREG_H;
+
+ return set;
+
+}
+
+/* Set the SREG Halfcarry flag. */
+static inline void sreg_set_H(bool H)
+{
+ cpu.sreg &= ~SREG_H;
+ cpu.sreg |= (H << SREG_H_BIT);
+ cpu.sreg_valid |= SREG_H;
+}
+
+/* Set the SREG Halfcarry flag. LAZY */
+static inline void sreg_set_H_lazy(uint8_t insn)
+{
+ cpu.sreg_lazy[SREG_H_BIT] = &(cpu.lazy_flag_cache[insn]);
+}
+
+/* Get the SREG Transfer bit flag. */
+static inline bool sreg_get_T(void)
+{
+ /* T is not lazy, so always valid. */
+ return !!(cpu.sreg & SREG_T);
+}
+
+/* Set the SREG Transfer bit flag. */
+static inline void sreg_set_T(bool T)
+{
+ cpu.sreg &= ~SREG_T;
+ cpu.sreg |= (T << SREG_T_BIT);
+}
+
+/* Get the SREG global Interrupt enable flag. */
+static inline bool sreg_get_I(void)
+{
+ /* I is not lazy, so always valid. */
+ return !!(cpu.sreg & SREG_I);
+}
+
+/* Set the SREG global Interrupt enable flag. */
+static inline void sreg_set_I(bool I)
+{
+ cpu.sreg &= ~SREG_I;
+ cpu.sreg |= (I << SREG_I_BIT);
+}
+
+#define LAZY_NONE 0xFFFFFFFF
+/* Setup lazy flags for an instruction that works on 8-bit operands.
+ * This whole function will optimize to only a few Host-CPU instructions
+ * without any branches. */
+static inline void lazy_flags_setup(const uint8_t insn,
+ const uint32_t oper0, const uint32_t oper1,
+ const uint32_t res,
+ const uint8_t sreg_invalidate_mask)
+{
+ struct lazy_flag *f = &(cpu.lazy_flag_cache[insn]);
+
+ break_linkage_on(!is_const_value(insn),
+ lazy_flags_setup_insn_not_const);
+ break_linkage_on(!is_const_value(sreg_invalidate_mask),
+ lazy_flags_setup_sreg_mask_not_const);
+
+ /* The if-branches are optimized away at compiletime, as this
+ * is an inline function. The conditionals always evaluate into
+ * constant expressions, so the compiler removes the entire branch
+ * instruction. */
+ if (insn >= FIRST_16BIT_INSN) {
+ if (!is_const_value(oper0) || oper0 != LAZY_NONE)
+ f->op16[0] = oper0;
+ if (!is_const_value(oper1) || oper1 != LAZY_NONE)
+ f->op16[1] = oper1;
+ if (!is_const_value(res) || res != LAZY_NONE)
+ f->res16 = res;
+ } else {
+ if (!is_const_value(oper0) || oper0 != LAZY_NONE)
+ f->op[0] = oper0;
+ if (!is_const_value(oper1) || oper1 != LAZY_NONE)
+ f->op[1] = oper1;
+ if (!is_const_value(res) || res != LAZY_NONE)
+ f->res = res;
+ }
+ /* The invalidation mask is constant. So the following if-branches
+ * will optimize away. */
+ if (sreg_invalidate_mask & SREG_C)
+ sreg_set_C_lazy(insn);
+ if (sreg_invalidate_mask & SREG_Z)
+ sreg_set_Z_lazy(insn);
+ if (sreg_invalidate_mask & SREG_N)
+ sreg_set_N_lazy(insn);
+ if (sreg_invalidate_mask & SREG_V)
+ sreg_set_V_lazy(insn);
+ if (sreg_invalidate_mask & SREG_S)
+ sreg_set_S_lazy(insn);
+ if (sreg_invalidate_mask & SREG_H)
+ sreg_set_H_lazy(insn);
+ /* Invalidate all flags that are changed by this instruction. */
+ cpu.sreg_valid &= ~sreg_invalidate_mask;
+}
+
+/* Get the whole sreg. Note that this function is expensive.
+ * Use the single bit fetch functions, if you only need a single
+ * bit or a subset of the sreg bits. */
+static uint8_t cpu_get_sreg(void)
+{
+ uint8_t sreg = 0;
+
+ sreg |= (uint8_t)sreg_get_C() << SREG_C_BIT;
+ sreg |= (uint8_t)sreg_get_Z() << SREG_Z_BIT;
+ sreg |= (uint8_t)sreg_get_N() << SREG_N_BIT;
+ sreg |= (uint8_t)sreg_get_V() << SREG_V_BIT;
+ sreg |= (uint8_t)sreg_get_S() << SREG_S_BIT;
+ sreg |= (uint8_t)sreg_get_H() << SREG_H_BIT;
+ sreg |= (uint8_t)sreg_get_T() << SREG_T_BIT;
+ sreg |= (uint8_t)sreg_get_I() << SREG_I_BIT;
+
+ return sreg;
+}
+
+/* Trigger the sideeffects of an I/O store (if registered any) */
+static inline void memory_store_sideeffect_trigger(uint16_t offset, uint8_t old_val,
+ uint8_t new_val)
+{
+ io_write_handler_t handler;
+
+ handler = memory.io_write_handlers[offset - IO_MEM_OFFSET];
+ if (handler)
+ handler(offset, old_val, new_val);
+}
+
+/* Trigger the sideeffects of an I/O load (if registered any) */
+static inline void memory_load_sideeffect_trigger(uint16_t offset)
+{
+ io_read_handler_t handler;
+
+ handler = memory.io_read_handlers[offset - IO_MEM_OFFSET];
+ if (handler)
+ handler(offset);
+}
+
+/* Store a value to memory and trigger sideeffects if needed. */
+static inline void memory_store(uint16_t offset, uint8_t value)
+{
+ uint8_t old, is_io_memory;
+
+ memory_lock(offset, &is_io_memory);
+ old = memory.ram[offset];
+ memory.ram[offset] = value;
+ if (is_io_memory)
+ memory_store_sideeffect_trigger(offset, old, value);
+ memory_unlock(is_io_memory);
+}
+
+/* Memory store to non-IO memory. */
+static inline void memory_store_nonio(uint16_t offset, uint8_t value)
+{
+ if (CHECK(mem_offset_in_io_region(offset))) {
+ fault("Attempt to store to I/O memory by an instruction "
+ "that should only access non-I/O memory.");
+ }
+ memory.ram[offset] = value;
+}
+
+/* Load a value from memory. Take care of locking. */
+static inline uint8_t memory_load(uint16_t offset)
+{
+ uint8_t ret, is_io_memory;
+
+ memory_lock(offset, &is_io_memory);
+ if (is_io_memory)
+ memory_load_sideeffect_trigger(offset);
+ ret = memory.ram[offset];
+ memory_unlock(is_io_memory);
+
+ return ret;
+}
+
+/* Memory load from non-IO memory. */
+static inline uint8_t memory_load_nonio(uint16_t offset)
+{
+ if (CHECK(mem_offset_in_io_region(offset))) {
+ fault("Attempt to load from I/O memory by an instruction "
+ "that should only access non-I/O memory.");
+ }
+ return memory.ram[offset];
+}
+
+static inline void insn_ret(void)
+{
+ uint32_t a, b, c;
+ uint16_t sp;
+
+ sp = getSP();
+ if (active_setup.pc_22bit) {
+ if (CHECK(sp >= memory.size - 3))
+ fault("RET instruction stack overflow");
+ c = memory_load_nonio(++sp);
+ b = memory_load_nonio(++sp);
+ a = memory_load_nonio(++sp);
+ setSP(sp);
+ cpu.pc = a | (b << 8) | (c << 16);
+ } else {
+ if (CHECK(sp >= memory.size - 2))
+ fault("RET instruction stack overflow");
+ b = memory_load_nonio(++sp);
+ a = memory_load_nonio(++sp);
+ setSP(sp);
+ cpu.pc = a | (b << 8);
+ }
+}
+
+static inline void insn_store_pc_on_stack(bool pc_22bit, uint8_t pc_inc)
+{
+ uint32_t ret_pc;
+ uint16_t sp;
+
+ ret_pc = cpu.pc + pc_inc;
+ sp = getSP();
+ if (pc_22bit) {
+ if (CHECK(sp < 3))
+ fault("PC -> STACK operation stack underflow");
+ memory_store_nonio(sp--, ret_pc);
+ memory_store_nonio(sp--, ret_pc >> 8);
+ memory_store_nonio(sp--, ret_pc >> 16);
+ setSP(sp);
+ } else {
+ if (CHECK(sp < 2))
+ fault("PC -> STACK operation stack underflow");
+ memory_store_nonio(sp--, ret_pc);
+ memory_store_nonio(sp--, ret_pc >> 8);
+ setSP(sp);
+ }
+}
+
+/* Get the low 16bits of a 16bit or 32bit instruction at PC. */
+static inline uint16_t get_lo16_insn(void)
+{
+ uint32_t byteoffset;
+
+ byteoffset = cpu.pc * 2;
+ if (CHECK(byteoffset >= active_setup.flash_size))
+ fault("CPU Program Counter >= flash size");
+ return le16_to_cpu(*(le16_t *)(code + byteoffset));
+}
+
+/* Get the high 16bits of a 32bit instruction at PC. */
+static inline uint16_t get_hi16_insn(void)
+{
+ uint32_t byteoffset;
+
+ byteoffset = (cpu.pc + 1) * 2;
+ if (CHECK(byteoffset >= active_setup.flash_size))
+ fault("HI16-fetch: CPU Program Counter >= flash size");
+ return le16_to_cpu(*(le16_t *)(code + byteoffset));
+}
+
+static inline bool is_two_word_instruction(uint16_t insn)
+{
+ switch (insn & 0xFE0F) {
+ case 0x9000: /* lds */
+ case 0x9200: /* sts */
+ return 1;
+ }
+
+ switch (insn & 0xFE0E) {
+ case 0x940c: /* jmp */
+ case 0x940e: /* call */
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Trap the CPU. This is called from outside of the CPU thread. */
+int trap_cpu(uint8_t id, bool synchronize)
+{
+ pthread_mutex_lock(&cpu.trap_mutex);
+ if (cpu.trap == id) {
+ pthread_mutex_unlock(&cpu.trap_mutex);
+ return 0;
+ }
+ cpu.trap = id;
+ if (synchronize)
+ pthread_cond_wait(&cpu.trapped_cond, &cpu.trap_mutex);
+ pthread_mutex_unlock(&cpu.trap_mutex);
+
+ return 0;
+}
+
+static int handle_trap(void)
+{
+ uint8_t id;
+ int stay_trapped;
+
+ pthread_mutex_lock(&cpu.trap_mutex);
+
+ id = cpu.trap;
+ if (unlikely(id == CPU_TRAP_CONTINUE)) {
+ stay_trapped = 0;
+ goto out_unlock;
+ }
+ pthread_cond_broadcast(&cpu.trapped_cond);//FIXME only broadcast after the first trap cycle?
+
+ switch (id) {
+ case CPU_TRAP_WAIT:
+ stay_trapped = 1;
+ break;
+ case CPU_TRAP_SINGLESTEP:
+ /* Continue execution. We will get trapped again
+ * after the next instruction. */
+ stay_trapped = 0;
+ break;
+ case CPU_TRAP_CONTINUE:
+ /* Continue execution. */
+ stay_trapped = 0;
+ break;
+ case CPU_TRAP_EXIT:
+ cpu.running = 0;
+ pthread_mutex_unlock(&cpu.trap_mutex);
+ pthread_exit(NULL);
+ return 1;
+ default:
+ pthread_mutex_unlock(&cpu.trap_mutex);
+ fault("Unknown CPU trap %u triggered\n", id);
+ return 1;
+ }
+out_unlock:
+ pthread_mutex_unlock(&cpu.trap_mutex);
+
+ if (stay_trapped)
+ pthread_yield();
+
+ return stay_trapped;
+}
+
+static void handle_breakpoint(bool programmed_bp)
+{
+ //FIXME: We don't really want to fault here.
+ // But do this for now.
+ fault("Breakpoint");
+}
+
+/* Trigger an IRQ. This is called from outside of the CPU thread. */
+int cpu_trigger_interrupt(uint8_t vector)
+{
+ struct avr_irq_descriptor *desc;
+
+ pthread_spin_lock(&cpu.irq_lock);
+ if (unlikely(cpu.irqs_pending == MAX_IRQS_PENDING)) {
+ pthread_spin_unlock(&cpu.irq_lock);
+ return -EBUSY;
+ }
+
+ /* Get a free IRQ descriptor. */
+ desc = list_entry(cpu.irqdesc_cache.next,
+ struct avr_irq_descriptor, list);
+ list_move_tail(&desc->list, &cpu.irqdesc_pending);
+ desc->vector = vector;
+ cpu.irqs_pending++;
+
+ pthread_spin_unlock(&cpu.irq_lock);
+
+ return 0;
+}
+
+static void handle_irqs(void)
+{
+ struct avr_irq_descriptor *desc;
+ uint8_t vector;
+
+ if (!sreg_get_I()) {
+ /* Global interrupts are disabled. Handle the IRQ later. */
+ return;
+ }
+
+ pthread_spin_lock(&cpu.irq_lock);
+ if (unlikely(!cpu.irqs_pending)) {
+ pthread_spin_unlock(&cpu.irq_lock);
+ return;
+ }
+ cpu.irqs_pending--;
+ /* Read the IRQ descriptor and return it to the cache. */
+ desc = list_entry(cpu.irqdesc_pending.next,
+ struct avr_irq_descriptor, list);
+ vector = desc->vector;
+ list_move(&desc->list, &cpu.irqdesc_cache);
+ pthread_spin_unlock(&cpu.irq_lock);
+
+ /* IRQs are entered with the I flag disabled. */
+ sreg_set_I(0);
+ /* Store PC on the stack, so we can later return from the
+ * IRQ with a reti instruction. */
+ insn_store_pc_on_stack(active_setup.pc_22bit, 0);
+ /* Jump to the IRQ vector. */
+ cpu.pc = vector;
+
+ cpu.irq_count++;
+}
+
+/* This is the function where the code gets executed. */
+static void * cpu_thread_function(void *unused)
+{
+ uint16_t insn;
+ bool enable_irqs = 0;
+ int res;
+
+//TODO implement memory RAMPD stuff.
+ cpu.cycles_count = 0;
+ while (1) {
+ if (unlikely(enable_irqs)) {
+ sreg_set_I(1);
+ enable_irqs = 0;
+ }
+next_insn_skip_irqcheck:
+ if (unlikely(reload_var(cpu.trap))) {
+ while (1) {
+ res = handle_trap();
+ if (res == 0)
+ break; /* Continue execution */
+ }
+ }
+ if (unlikely(cpu.breakpoint == cpu.pc))
+ handle_breakpoint(0);
+ if (unlikely(reload_var(cpu.irqs_pending)))
+ handle_irqs();
+
+ /* Get the instruction at PC and handle it. */
+ insn = get_lo16_insn();
+
+//printf("PC: 0x%08X\n", cpu.pc * 2);
+ switch (insn) {
+ case 0x0000: /* nop */
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ case 0x9409: /* ijmp */
+ cpu.pc = getZ();
+ cpu.cycles_count += 2;
+ continue;
+ case 0x9419: /* eijmp */
+ cpu.pc = getZ();
+ //TODO load EIND into PC.high
+ cpu.cycles_count += 2;
+ continue;
+ case 0x9508: /* ret */
+ insn_ret();
+ cpu.cycles_count += 4;
+ continue;
+ case 0x9509: /* icall */
+ insn_store_pc_on_stack(active_setup.pc_22bit, 1);
+ cpu.pc = getZ();
+ cpu.cycles_count += 3;
+ continue;
+ case 0x9518: /* reti */
+ insn_ret();
+ /* XXX Also execute the next insn with IRQs still disabled here? */
+ sreg_set_I(1);
+ cpu.cycles_count += 4;
+ continue;
+ case 0x9519: /* eicall */
+ insn_store_pc_on_stack(1, 1);
+ cpu.pc = getZ();
+ //TODO load EIND into PC.high
+ cpu.cycles_count += 4;
+ continue;
+ case 0x9588: /* sleep */
+ //TODO
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ case 0x95a8: /* wdr */
+ //TODO
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ case 0x95c8: { /* lpm */
+ uint16_t z = getZ();
+
+ if (CHECK(z >= active_setup.flash_size))
+ fault("LPM with Z >= flash size");
+ cpu.r[0] = code[z];
+ cpu.pc++;
+ cpu.cycles_count += 3;
+ continue;
+ }
+ case 0x95d8: { /* elpm */
+ uint32_t offset;
+
+ fault("ELPM is not implemented");
+ offset = getZ();
+ //TODO fetch RAMPZ
+ //TODO offset sanity check
+ cpu.r[0] = code[offset];
+ cpu.pc++;
+ cpu.cycles_count += 3;
+ continue;
+ }
+ case 0x95e8: { /* spm */
+ uint32_t offset;
+
+ fault("SPM is not implemented");
+ offset = getZ();
+ //TODO fetch RAMPZ
+ code[offset] = 0xFF;
+ code[offset + 1] = 0xFF;
+ //TODO write the stuff.
+ cpu.pc++;
+ //TODO set cpu.cycles_count
+ continue;
+ }
+ case 0x9598: /* break */
+ handle_breakpoint(1);
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+
+ switch (insn & 0xFF8F) {
+ case 0x9408: { /* bset */
+ uint8_t bitnr;
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ bitnr = (insn & 0x70) >> 4;
+ switch (bitnr) {
+ case SREG_C_BIT:
+ sreg_set_C(1);
+ break;
+ case SREG_Z_BIT:
+ sreg_set_Z(1);
+ break;
+ case SREG_N_BIT:
+ sreg_set_N(1);
+ break;
+ case SREG_V_BIT:
+ sreg_set_V(1);
+ break;
+ case SREG_S_BIT:
+ sreg_set_S(1);
+ break;
+ case SREG_H_BIT:
+ sreg_set_H(1);
+ break;
+ case SREG_T_BIT:
+ sreg_set_T(1);
+ break;
+ case SREG_I_BIT:
+ /* Enable IRQs after the _next_ instruction has finished. */
+ enable_irqs = 1;
+ goto next_insn_skip_irqcheck;
+ }
+ continue;
+ }
+ case 0x9488: { /* bclr */
+ uint8_t bitnr;
+
+ bitnr = (insn & 0x70) >> 4;
+ switch (bitnr) {
+ case SREG_C_BIT:
+ sreg_set_C(0);
+ break;
+ case SREG_Z_BIT:
+ sreg_set_Z(0);
+ break;
+ case SREG_N_BIT:
+ sreg_set_N(0);
+ break;
+ case SREG_V_BIT:
+ sreg_set_V(0);
+ break;
+ case SREG_S_BIT:
+ sreg_set_S(0);
+ break;
+ case SREG_H_BIT:
+ sreg_set_H(0);
+ break;
+ case SREG_T_BIT:
+ sreg_set_T(0);
+ break;
+ case SREG_I_BIT:
+ sreg_set_I(0);
+ break;
+ }
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ } }
+
+ switch (insn & 0xF000) {
+ case 0x4000: { /* sbci */
+ uint8_t reg, orig, val, res;
+
+ reg = ((insn & 0x00F0) >> 4) + 16;
+ val = ((insn & 0x0F00) >> 4) | (insn & 0x000F);
+ orig = cpu.r[reg];
+ res = orig - val - (uint8_t)sreg_get_C();
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_SBCI, orig, val, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x5000: { /* subi */
+ uint8_t reg, orig, val, res;
+
+ reg = ((insn & 0x00F0) >> 4) + 16;
+ val = ((insn & 0x0F00) >> 4) | (insn & 0x000F);
+ orig = cpu.r[reg];
+ res = orig - val;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_SUBI, orig, val, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x6000: { /* ori */
+ uint8_t reg, orig, val, res;
+
+ reg = ((insn & 0x00F0) >> 4) + 16;
+ val = ((insn & 0x0F00) >> 4) | (insn & 0x000F);
+ orig = cpu.r[reg];
+ res = orig | val;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_ORI, LAZY_NONE, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x7000: { /* andi */
+ uint8_t reg, orig, val, res;
+
+ reg = ((insn & 0x00F0) >> 4) + 16;
+ val = ((insn & 0x0F00) >> 4) | (insn & 0x000F);
+ orig = cpu.r[reg];
+ res = orig & val;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_ANDI, LAZY_NONE, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0xe000: { /* ldi */
+ uint8_t reg, val;
+
+ reg = ((insn & 0x00F0) >> 4) + 16;
+ val = ((insn & 0x0F00) >> 4) | (insn & 0x000F);
+ cpu.r[reg] = val;
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0xc000: { /* rjmp */
+ int32_t offset = 0;
+
+ if (insn & 0x0800)
+ offset = ~0x0FFF;
+ offset |= insn & 0x0FFF;
+ cpu.pc = cpu.pc + offset + 1;
+ if (CHECK(cpu.pc >= active_setup.flash_size / 2))
+ fault("RJMP destination PC >= flash length");
+ cpu.cycles_count += 2;
+
+ continue;
+ }
+ case 0xd000: { /* rcall */
+ int32_t offset = 0;
+
+ if (insn & 0x0800)
+ offset = ~0x0FFF;
+ offset |= insn & 0x0FFF;
+ insn_store_pc_on_stack(active_setup.pc_22bit, 1);
+ cpu.pc = cpu.pc + offset + 1;
+ if (CHECK(cpu.pc >= active_setup.flash_size / 2))
+ fault("RCALL destination PC >= flash length");
+ cpu.cycles_count += 3;
+
+ continue;
+ }
+ case 0x3000: { /* cpi */
+ uint8_t reg, orig, val, res;
+
+ reg = ((insn & 0x00F0) >> 4) + 16;
+ val = ((insn & 0x0F00) >> 4) | (insn & 0x000F);
+ orig = cpu.r[reg];
+ res = orig - val;
+
+ lazy_flags_setup(INSN_CPI, orig, val, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ } }
+
+ switch (insn & 0xFE0F) {
+ case 0x9400: { /* com */
+ uint8_t reg, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ res = ~(cpu.r[reg]);
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_COM, LAZY_NONE, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9401: { /* neg */
+ uint8_t reg, orig, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ res = (uint8_t)0x00 - orig;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_NEG, orig, LAZY_NONE, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x940a: { /* dec */
+ uint8_t reg, orig, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ res = orig - 1;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_DEC, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9403: { /* inc */
+ uint8_t reg, orig, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ res = orig + 1;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_INC, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9402: { /* swap */
+ uint8_t reg, orig;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ cpu.r[reg] = (orig >> 4) | (orig << 4);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9406: { /* lsr */
+ uint8_t reg, orig, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ res = orig >> 1;
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_LSR, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9407: { /* ror */
+ uint8_t reg, orig, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ res = (orig >> 1) | ((uint8_t)sreg_get_C() << 7);
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_ROR, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9006: /* elpm Z */
+ fault("ELPM Z is not implemented");//TODO
+ continue;
+ case 0x9007: /* elpm Z+ */
+ fault("ELPM Z+ is not implemented");//TODO
+ continue;
+ case 0x900c: { /* ld X */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t x;
+
+ x = cpu.r[X_LO];
+ if (CHECK(x >= memory.size))
+ fault("LD X pointer outside of memory region");
+ cpu.r[reg] = memory_load(x);
+ } else {
+ uint16_t x;
+
+ x = getX();
+ if (CHECK(x >= memory.size))
+ fault("LD X pointer outside of memory region");
+ cpu.r[reg] = memory_load(x);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x900d: { /* ld X+ */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t x;
+
+ x = cpu.r[X_LO];
+ if (CHECK(x >= memory.size))
+ fault("LD X+ pointer outside of memory region");
+ cpu.r[reg] = memory_load(x);
+ cpu.r[X_LO]++;
+ } else {
+ uint16_t x;
+
+ x = getX();
+ if (CHECK(x >= memory.size))
+ fault("LD X+ pointer outside of memory region");
+ cpu.r[reg] = memory_load(x);
+ setX(x + 1);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x900e: { /* ld -X */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t x;
+
+ x = cpu.r[X_LO] - 1;
+ if (CHECK(x >= memory.size))
+ fault("LD -X pointer outside of memory region");
+ cpu.r[reg] = memory_load(x);
+ cpu.r[X_LO] = x;
+ } else {
+ uint16_t x;
+
+ x = getX() - 1;
+ if (CHECK(x >= memory.size))
+ fault("LD -X pointer outside of memory region");
+ cpu.r[reg] = memory_load(x);
+ setX(x);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9009: { /* ld Y+ */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t y;
+
+ y = cpu.r[Y_LO];
+ if (CHECK(y >= memory.size))
+ fault("LD Y+ pointer outside of memory region");
+ cpu.r[reg] = memory_load(y);
+ cpu.r[Y_LO]++;
+ } else {
+ uint16_t y;
+
+ y = getY();
+ if (CHECK(y >= memory.size))
+ fault("LD Y+ pointer outside of memory region");
+ cpu.r[reg] = memory_load(y);
+ setY(y + 1);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x900a: { /* ld -Y */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t y;
+
+ y = cpu.r[Y_LO] - 1;
+ if (CHECK(y >= memory.size))
+ fault("LD -Y pointer outside of memory region");
+ cpu.r[reg] = memory_load(y);
+ cpu.r[Y_LO] = y;
+ } else {
+ uint16_t y;
+
+ y = getY() - 1;
+ if (CHECK(y >= memory.size))
+ fault("LD -Y pointer outside of memory region");
+ cpu.r[reg] = memory_load(y);
+ setY(y);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9001: { /* ld Z+ */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t z;
+
+ z = cpu.r[Z_LO];
+ if (CHECK(z >= memory.size))
+ fault("LD Z+ pointer outside of memory region");
+ cpu.r[reg] = memory_load(z);
+ cpu.r[Z_LO]++;
+ } else {
+ uint16_t z;
+
+ z = getZ();
+ if (CHECK(z >= memory.size))
+ fault("LD Z+ pointer outside of memory region");
+ cpu.r[reg] = memory_load(z);
+ setZ(z + 1);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9002: { /* ld -Z */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t z;
+
+ z = cpu.r[Z_LO] - 1;
+ if (CHECK(z >= memory.size))
+ fault("LD -Z pointer outside of memory region");
+ cpu.r[reg] = memory_load(z);
+ cpu.r[Z_LO] = z;
+ } else {
+ uint16_t z;
+
+ z = getZ() - 1;
+ if (CHECK(z >= memory.size))
+ fault("LD -Z pointer outside of memory region");
+ cpu.r[reg] = memory_load(z);
+ setZ(z);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9004: { /* lpm Z */
+ uint8_t reg;
+ uint16_t z;
+
+ reg = (insn & 0x01F0) >> 4;
+ z = getZ();
+ if (CHECK(z >= active_setup.flash_size))
+ fault("LPM Z pointer outside of flash region");
+ cpu.r[reg] = code[z];
+
+ cpu.pc++;
+ cpu.cycles_count += 3;
+ continue;
+ }
+ case 0x9005: { /* lpm Z+ */
+ uint8_t reg;
+ uint16_t z;
+
+ reg = (insn & 0x01F0) >> 4;
+ z = getZ();
+ if (CHECK(z >= active_setup.flash_size))
+ fault("LPM Z+ pointer outside of flash region");
+ cpu.r[reg] = code[z];
+ setZ(z + 1);
+
+ cpu.pc++;
+ cpu.cycles_count += 3;
+ continue;
+ }
+ case 0x920c: { /* st X */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t x;
+
+ x = cpu.r[X_LO];
+ if (CHECK(x >= memory.size))
+ fault("ST X pointer outside of memory region");
+ memory_store(x, cpu.r[reg]);
+ } else {
+ uint16_t x;
+
+ x = getX();
+ if (CHECK(x >= memory.size))
+ fault("ST X pointer outside of memory region");
+ memory_store(x, cpu.r[reg]);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x920d: { /* st X+ */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t x;
+
+ x = cpu.r[X_LO];
+ if (CHECK(x >= memory.size))
+ fault("ST X+ pointer outside of memory region");
+ memory_store(x, cpu.r[reg]);
+ cpu.r[X_LO]++;
+ } else {
+ uint16_t x;
+
+ x = getX();
+ if (CHECK(x >= memory.size))
+ fault("ST X+ pointer outside of memory region");
+ memory_store(x, cpu.r[reg]);
+ setX(x + 1);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x920e: { /* st -X */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t x;
+
+ x = cpu.r[X_LO] - 1;
+ if (CHECK(x >= memory.size))
+ fault("ST -X pointer outside of memory region");
+ memory_store(x, cpu.r[reg]);
+ cpu.r[X_LO] = x;
+ } else {
+ uint16_t x;
+
+ x = getX() - 1;
+ if (CHECK(x >= memory.size))
+ fault("ST -X pointer outside of memory region");
+ memory_store(x, cpu.r[reg]);
+ setX(x);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9209: { /* st Y+ */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t y;
+
+ y = cpu.r[Y_LO];
+ if (CHECK(y >= memory.size))
+ fault("ST Y+ pointer outside of memory region");
+ memory_store(y, cpu.r[reg]);
+ cpu.r[Y_LO]++;
+ } else {
+ uint16_t y;
+
+ y = getY();
+ if (CHECK(y >= memory.size))
+ fault("ST Y+ pointer outside of memory region");
+ memory_store(y, cpu.r[reg]);
+ setY(y + 1);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x920a: { /* st -Y */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t y;
+
+ y = cpu.r[Y_LO] - 1;
+ if (CHECK(y >= memory.size))
+ fault("ST -Y pointer outside of memory region");
+ memory_store(y, cpu.r[reg]);
+ cpu.r[Y_LO] = y;
+ } else {
+ uint16_t y;
+
+ y = getY() - 1;
+ if (CHECK(y >= memory.size))
+ fault("ST -Y pointer outside of memory region");
+ memory_store(y, cpu.r[reg]);
+ setY(y);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9201: { /* st Z+ */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t z;
+
+ z = cpu.r[Z_LO];
+ if (CHECK(z >= memory.size))
+ fault("ST Z+ pointer outside of memory region");
+ memory_store(z, cpu.r[reg]);
+ cpu.r[Z_LO]++;
+ } else {
+ uint16_t z;
+
+ z = getZ();
+ if (CHECK(z >= memory.size))
+ fault("ST Z+ pointer outside of memory region");
+ memory_store(z, cpu.r[reg]);
+ setZ(z + 1);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9202: { /* st -Z */
+ uint8_t reg;
+
+ reg = (insn & 0x01F0) >> 4;
+ if (memory.size <= 256) {
+ uint8_t z;
+
+ z = cpu.r[Z_LO] - 1;
+ if (CHECK(z >= memory.size))
+ fault("ST -Z pointer outside of memory region");
+ memory_store(z, cpu.r[reg]);
+ cpu.r[Z_LO] = z;
+ } else {
+ uint16_t z;
+
+ z = getZ() - 1;
+ if (CHECK(z >= memory.size))
+ fault("ST -Z pointer outside of memory region");
+ memory_store(z, cpu.r[reg]);
+ setZ(z);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x900f: { /* pop */
+ uint8_t reg;
+ uint16_t sp;
+
+ reg = (insn & 0x01F0) >> 4;
+ sp = getSP() + 1;
+ setSP(sp);
+ if (CHECK(sp >= memory.size))
+ fault("POP stack pointer out of memory region");
+ cpu.r[reg] = memory_load_nonio(sp);
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x920f: { /* push */
+ uint8_t reg;
+ uint16_t sp;
+
+ reg = (insn & 0x01F0) >> 4;
+ sp = getSP();
+ if (CHECK(sp >= memory.size))
+ fault("PUSH stack pointer out of memory region");
+ memory_store_nonio(sp, cpu.r[reg]);
+ setSP(sp - 1);
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9405: { /* asr */
+ uint8_t reg, orig, res;
+
+ reg = (insn & 0x01F0) >> 4;
+ orig = cpu.r[reg];
+ res = (orig >> 1) | (orig & 0x80);
+ cpu.r[reg] = res;
+
+ lazy_flags_setup(INSN_ASR, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9000: { /* lds (32-bit instruction) */
+ uint8_t reg;
+ uint16_t offset;
+
+ reg = (insn & 0x01F0) >> 4;
+ offset = get_hi16_insn();
+ if (CHECK(offset >= memory.size))
+ fault("LDS offset >= memory size");
+ cpu.r[reg] = memory_load(offset);
+
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9200: { /* sts (32-bit instruction) */
+ uint8_t reg;
+ uint16_t offset;
+
+ reg = (insn & 0x01F0) >> 4;
+ offset = get_hi16_insn();
+ if (CHECK(offset >= memory.size))
+ fault("STS offset >= memory size");
+ memory_store(offset, cpu.r[reg]);
+
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ continue;
+ } }
+
+ switch (insn & 0xFE0E) {
+ case 0x940c: { /* jmp (32-bit instruction) */
+ uint16_t hi16_insn;
+ uint32_t target;
+
+ hi16_insn = get_hi16_insn();
+ target = hi16_insn | ((insn & 0x0001) << 16) |
+ ((insn & 0x01F0) << (17 - 4));
+
+ cpu.pc = target;
+ cpu.cycles_count += 3;
+ continue;
+ }
+ case 0x940e: { /* call (32-bit instruction) */
+ uint16_t hi16_insn;
+ uint32_t target;
+
+ hi16_insn = get_hi16_insn();
+ target = hi16_insn | ((insn & 0x0001) << 16) |
+ ((insn & 0x01F0) << (17 - 4));
+ insn_store_pc_on_stack(active_setup.pc_22bit, 2);
+
+ cpu.pc = target;
+ cpu.cycles_count += 4;
+ continue;
+ } }
+
+ switch (insn & 0xD208) {
+ case 0x8200: { /* std Z */
+ uint8_t reg, displace;
+
+ reg = (insn & 0x01F0) >> 4;
+ displace = (insn & 0x0007) | ((insn & 0x0C00) >> 7) |
+ ((insn & 0x2000) >> 8);
+ if (memory.size <= 256) {
+ uint8_t z;
+
+ z = cpu.r[Z_LO] + displace;
+ if (CHECK(z >= memory.size))
+ fault("STD(ST) Z pointer outside of memory region");
+ memory_store(z, cpu.r[reg]);
+ } else {
+ uint16_t z;
+
+ z = getZ() + displace;
+ if (CHECK(z >= memory.size))
+ fault("STD(ST) Z pointer outside of memory region");
+ memory_store(z, cpu.r[reg]);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x8208: { /* std Y */
+ uint8_t reg, displace;
+
+ reg = (insn & 0x01F0) >> 4;
+ displace = (insn & 0x0007) | ((insn & 0x0C00) >> 7) |
+ ((insn & 0x2000) >> 8);
+ if (memory.size <= 256) {
+ uint8_t y;
+
+ y = cpu.r[Y_LO] + displace;
+ if (CHECK(y >= memory.size))
+ fault("STD(ST) Y pointer outside of memory region");
+ memory_store(y, cpu.r[reg]);
+ } else {
+ uint16_t y;
+
+ y = getY() + displace;
+ if (CHECK(y >= memory.size))
+ fault("STD(ST) Y pointer outside of memory region");
+ memory_store(y, cpu.r[reg]);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x8000: { /* ldd Z */
+ uint8_t reg, displace;
+
+ reg = (insn & 0x01F0) >> 4;
+ displace = (insn & 0x0007) | ((insn & 0x0C00) >> 7) |
+ ((insn & 0x2000) >> 8);
+ if (memory.size <= 256) {
+ uint8_t z;
+
+ z = cpu.r[Z_LO] + displace;
+ if (CHECK(z >= memory.size))
+ fault("LDD(LD) Z pointer outside of memory region");
+ cpu.r[reg] = memory_load(z);
+ } else {
+ uint16_t z;
+
+ z = getZ() + displace;
+ if (CHECK(z >= memory.size))
+ fault("LDD(LD) Z pointer outside of memory region");
+ cpu.r[reg] = memory_load(z);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x8008: { /* ldd Y */
+ uint8_t reg, displace;
+
+ reg = (insn & 0x01F0) >> 4;
+ displace = (insn & 0x0007) | ((insn & 0x0C00) >> 7) |
+ ((insn & 0x2000) >> 8);
+ if (memory.size <= 256) {
+ uint8_t y;
+
+ y = cpu.r[Y_LO] + displace;
+ if (CHECK(y >= memory.size))
+ fault("LDD(LD) Y pointer outside of memory region");
+ cpu.r[reg] = memory_load(y);
+ } else {
+ uint16_t y;
+
+ y = getY() + displace;
+ if (CHECK(y >= memory.size))
+ fault("LDD(LD) Y pointer outside of memory region");
+ cpu.r[reg] = memory_load(y);
+ }
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ } }
+
+ switch (insn & 0xFF00) {
+ case 0x0200: /* muls */
+ fault("MULS is not implemented");//TODO
+ continue;
+ case 0x0100: { /* movw */
+ uint8_t src, dst;
+
+ src = ((insn & 0x000F) * 2);
+ dst = ((insn & 0x00F0) >> 4) * 2;
+ cpu.r[dst] = cpu.r[src];
+ cpu.r[dst + 1] = cpu.r[src + 1];
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9600: { /* adiw */
+ uint8_t reg;
+ uint16_t orig, val, res;
+
+ reg = (((insn & 0x0030) >> 4) * 2) + 24;
+ val = ((insn & 0x000F) | ((insn & 0x00C0) >> 2));
+ orig = le16_to_cpu(*((le16_t *)&(cpu.r[reg])));
+ res = orig + val;
+ *((le16_t *)&(cpu.r[reg])) = cpu_to_le16(res);
+
+ lazy_flags_setup(INSN_ADIW, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9800: { /* cbi */
+ uint8_t io, bit, old_val, new_val;
+
+ bit = (insn & 0x0007);
+ io = (insn & 0x00F8) >> 3;
+
+ pthread_spin_lock(&memory.io_lock);
+ old_val = memory.io[io];
+ new_val = old_val & ~(1 << bit);
+ memory.io[io] = new_val;
+ memory_store_sideeffect_trigger(io + IO_MEM_OFFSET,
+ old_val, new_val);
+ pthread_spin_unlock(&memory.io_lock);
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9a00: { /* sbi */
+ uint8_t io, bit, old_val, new_val;
+
+ bit = (insn & 0x0007);
+ io = (insn & 0x00F8) >> 3;
+
+ pthread_spin_lock(&memory.io_lock);
+ old_val = memory.io[io];
+ new_val = old_val | (1 << bit);
+ memory.io[io] = new_val;
+ memory_store_sideeffect_trigger(io + IO_MEM_OFFSET,
+ old_val, new_val);
+ pthread_spin_unlock(&memory.io_lock);
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ }
+ case 0x9900: { /* sbic */
+ uint8_t io, bit;
+
+ bit = (insn & 0x0007);
+ io = (insn & 0x00F8) >> 3;
+ if (memory.io[io] & (1 << bit)) {
+ cpu.pc++;
+ cpu.cycles_count++;
+ } else {
+ uint16_t next_insn = get_hi16_insn();
+
+ /* If the next insn is 32bit we skip two words. */
+ if (is_two_word_instruction(next_insn)) {
+ cpu.pc += 3;
+ cpu.cycles_count += 3;
+ } else {
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ }
+ }
+
+ continue;
+ }
+ case 0x9b00: { /* sbis */
+ uint8_t io, bit;
+
+ bit = (insn & 0x0007);
+ io = (insn & 0x00F8) >> 3;
+ if (!(memory.io[io] & (1 << bit))) {
+ cpu.pc++;
+ cpu.cycles_count++;
+ } else {
+ uint16_t next_insn = get_hi16_insn();
+
+ /* If the next insn is 32bit we skip two words. */
+ if (is_two_word_instruction(next_insn)) {
+ cpu.pc += 3;
+ cpu.cycles_count += 3;
+ } else {
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ }
+ }
+
+ continue;
+ }
+ case 0x9700: { /* sbiw */
+ uint8_t reg;
+ uint16_t orig, val, res;
+
+ reg = (((insn & 0x0030) >> 4) * 2) + 24;
+ val = ((insn & 0x000F) | ((insn & 0x00C0) >> 2));
+ orig = le16_to_cpu(*((le16_t *)&(cpu.r[reg])));
+ res = orig - val;
+ *((le16_t *)&(cpu.r[reg])) = cpu_to_le16(res);
+
+ lazy_flags_setup(INSN_SBIW, orig, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count += 2;
+ continue;
+ } }
+
+ switch (insn & 0xFC00) {
+ case 0x0400: { /* cpc */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd - rr - (uint8_t)sreg_get_C();
+
+ lazy_flags_setup(INSN_CPC, rd, rr, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x0800: { /* sbc */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd - rr - (uint8_t)sreg_get_C();
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_SBC, rd, rr, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x0c00: { /* add */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd + rr;
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_ADD, rd, rr, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x1000: { /* cpse */
+ uint8_t reg_d, reg_r, rd, rr;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ if (rd != rr) {
+ cpu.pc++;
+ cpu.cycles_count++;
+ } else {
+ uint16_t next_insn = get_hi16_insn();
+
+ /* If the next insn is 32bit we skip two words. */
+ if (is_two_word_instruction(next_insn)) {
+ cpu.pc += 3;
+ cpu.cycles_count += 3;
+ } else {
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ }
+ }
+
+ continue;
+ }
+ case 0x1400: { /* cp */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd - rr;
+
+ lazy_flags_setup(INSN_CP, rd, rr, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x1800: { /* sub */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd - rr;
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_SUB, rd, rr, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x1c00: { /* adc */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd + rr + (uint8_t)sreg_get_C();
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_ADC, rd, rr, res,
+ SREG_H | SREG_S | SREG_V | SREG_N | SREG_Z | SREG_C);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x2000: { /* and */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd & rr;
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_AND, LAZY_NONE, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x2400: { /* eor */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd ^ rr;
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_EOR, LAZY_NONE, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x2800: { /* or */
+ uint8_t reg_d, reg_r, rd, rr, res;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ rd = cpu.r[reg_d];
+ rr = cpu.r[reg_r];
+ res = rd | rr;
+ cpu.r[reg_d] = res;
+
+ lazy_flags_setup(INSN_OR, LAZY_NONE, LAZY_NONE, res,
+ SREG_S | SREG_V | SREG_N | SREG_Z);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x2c00: { /* mov */
+ uint8_t reg_d, reg_r;
+
+ reg_d = (insn & 0x01F0) >> 4;
+ reg_r = (insn & 0x000F) | ((insn & 0x0200) >> 5);
+ cpu.r[reg_d] = cpu.r[reg_r];
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0x9c00: /* mul */
+ fault("MUL is not implemented");//TODO
+ continue;
+ case 0xf400: { /* brbc */
+ uint8_t bit;
+ int8_t offset = 0;
+ bool set;
+
+ bit = (insn & 0x0007);
+ if (insn & 0x0200)
+ offset = ~0x007F;
+ offset |= (insn & 0x03F8) >> 3;
+
+ switch (bit) {
+ case SREG_C_BIT:
+ set = sreg_get_C();
+ break;
+ case SREG_Z_BIT:
+ set = sreg_get_Z();
+ break;
+ case SREG_N_BIT:
+ set = sreg_get_N();
+ break;
+ case SREG_V_BIT:
+ set = sreg_get_V();
+ break;
+ case SREG_S_BIT:
+ set = sreg_get_S();
+ break;
+ case SREG_H_BIT:
+ set = sreg_get_H();
+ break;
+ case SREG_T_BIT:
+ set = sreg_get_T();
+ break;
+ case SREG_I_BIT:
+ set = sreg_get_I();
+ break;
+ default:
+ fault("BRBC internal emulator bug");
+ }
+ if (set) {
+ cpu.pc++;
+ cpu.cycles_count++;
+ } else {
+ cpu.pc += offset + 1;
+ cpu.cycles_count += 2;
+ }
+
+ continue;
+ }
+ case 0xf000: { /* brbs */
+ uint8_t bit;
+ int8_t offset = 0;
+ bool set;
+
+ bit = (insn & 0x0007);
+ if (insn & 0x0200)
+ offset = ~0x007F;
+ offset |= (insn & 0x03F8) >> 3;
+
+ switch (bit) {
+ case SREG_C_BIT:
+ set = sreg_get_C();
+ break;
+ case SREG_Z_BIT:
+ set = sreg_get_Z();
+ break;
+ case SREG_N_BIT:
+ set = sreg_get_N();
+ break;
+ case SREG_V_BIT:
+ set = sreg_get_V();
+ break;
+ case SREG_S_BIT:
+ set = sreg_get_S();
+ break;
+ case SREG_H_BIT:
+ set = sreg_get_H();
+ break;
+ case SREG_T_BIT:
+ set = sreg_get_T();
+ break;
+ case SREG_I_BIT:
+ set = sreg_get_I();
+ break;
+ default:
+ fault("BRBS internal emulator bug");
+ }
+ if (set) {
+ cpu.pc += offset + 1;
+ cpu.cycles_count += 2;
+ } else {
+ cpu.pc++;
+ cpu.cycles_count++;
+ }
+
+ continue;
+ } }
+
+ switch (insn & 0xFE08) {
+ case 0xf800: { /* bld */
+ uint8_t reg, bit, val;
+
+ reg = (insn & 0x01F0) >> 4;
+ bit = (insn & 0x0007);
+ val = (uint8_t)sreg_get_T() << bit;
+ cpu.r[reg] &= ~(1 << bit);
+ cpu.r[reg] |= val;
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0xfa00: { /* bst */
+ uint8_t reg, bit;
+
+ reg = (insn & 0x01F0) >> 4;
+ bit = (insn & 0x0007);
+ sreg_set_T(!!(cpu.r[reg] & (1 << bit)));
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0xfc00: { /* sbrc */
+ uint8_t reg, bit;
+
+ reg = (insn & 0x01F0) >> 4;
+ bit = (insn & 0x0007);
+ if (cpu.r[reg] & (1 << bit)) {
+ cpu.pc++;
+ cpu.cycles_count++;
+ } else {
+ uint16_t next_insn = get_hi16_insn();
+
+ /* If the next insn is 32bit we skip two words. */
+ if (is_two_word_instruction(next_insn)) {
+ cpu.pc += 3;
+ cpu.cycles_count += 3;
+ } else {
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ }
+ }
+
+ continue;
+ }
+ case 0xfe00: { /* sbrs */
+ uint8_t reg, bit;
+
+ reg = (insn & 0x01F0) >> 4;
+ bit = (insn & 0x0007);
+ if (!(cpu.r[reg] & (1 << bit))) {
+ cpu.pc++;
+ cpu.cycles_count++;
+ } else {
+ uint16_t next_insn = get_hi16_insn();
+
+ /* If the next insn is 32bit we skip two words. */
+ if (is_two_word_instruction(next_insn)) {
+ cpu.pc += 3;
+ cpu.cycles_count += 3;
+ } else {
+ cpu.pc += 2;
+ cpu.cycles_count += 2;
+ }
+ }
+
+ continue;
+ } }
+
+ switch (insn & 0xF800) {
+ case 0xb000: { /* in */
+ uint8_t reg, io;
+
+ reg = (insn & 0x01F0) >> 4;
+ io = (insn & 0x000F) | ((insn & 0x0600) >> 5);
+
+ pthread_spin_lock(&memory.io_lock);
+ memory_load_sideeffect_trigger(io + IO_MEM_OFFSET);
+ cpu.r[reg] = memory.io[io];
+ pthread_spin_unlock(&memory.io_lock);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ }
+ case 0xb800: { /* out */
+ uint8_t reg, io, old_val, new_val;
+
+ reg = (insn & 0x01F0) >> 4;
+ io = (insn & 0x000F) | ((insn & 0x0600) >> 5);
+
+ pthread_spin_lock(&memory.io_lock);
+ old_val = memory.io[io];
+ new_val = cpu.r[reg];
+ memory.io[io] = new_val;
+ memory_store_sideeffect_trigger(io + IO_MEM_OFFSET,
+ old_val, new_val);
+ pthread_spin_unlock(&memory.io_lock);
+
+ cpu.pc++;
+ cpu.cycles_count++;
+ continue;
+ } }
+
+ switch (insn & 0xFF88) {
+ case 0x0300: /* mulsu */
+ fault("MULSU is not implemented");//TODO
+ continue;
+ case 0x0388: /* fmulsu */
+ fault("FMULSU is not implemented");//TODO
+ continue;
+ case 0x0380: /* fmuls */
+ fault("FMULS is not implemented");//TODO
+ continue;
+ case 0x0308: /* fmul */
+ fault("FMUL is not implemented");//TODO
+ continue;
+ }
+
+ fault("Unknown instruction 0x%04X\n", insn);
+ };
+
+ return NULL;
+}
+
+static void cpu_io_write_sreg(uint16_t offset, uint8_t old_val, uint8_t new_val)
+{
+ cpu.sreg = new_val;
+ cpu.sreg_valid = 0xFF;
+}
+
+static void cpu_io_read_sreg(uint16_t offset)
+{
+ memory.ram[offset] = cpu_get_sreg();
+}
+
+static int register_cpu_io_handlers(void)
+{
+ int err = 0;
+
+ /* Register I/O handlers for CPU related I/O registers. */
+ err |= register_io_write_handler(cpu_io_write_sreg, IO_SREG);
+ err |= register_io_read_handler(cpu_io_read_sreg, IO_SREG);
+
+ if (err)
+ return -1;
+ return 0;
+}
+
+static int cpu_interrupts_init(void)
+{
+ int i, err;
+ struct avr_irq_descriptor *desc, *cache;
+
+ INIT_LIST_HEAD(&cpu.irqdesc_pending);
+ INIT_LIST_HEAD(&cpu.irqdesc_cache);
+ err = pthread_spin_init(&cpu.irq_lock, 0);
+ if (err)
+ return err;
+
+ err = -ENOMEM;
+ cache = avr_malloc(sizeof(*cache) * MAX_IRQS_PENDING);
+ if (!cache)
+ goto err_spin_destroy;
+ cpu.irq_cache_p = cache;
+ for (i = 0; i < MAX_IRQS_PENDING; i++) {
+ desc = &(cache[i]);
+
+ INIT_LIST_HEAD(&desc->list);
+
+ list_add(&desc->list, &cpu.irqdesc_cache);
+ }
+
+ return 0;
+err_spin_destroy:
+ pthread_spin_destroy(&cpu.irq_lock);
+ return err;
+}
+
+static void cpu_interrupts_exit(void)
+{
+ free(cpu.irq_cache_p);
+ pthread_spin_destroy(&cpu.irq_lock);
+}
+
+int cpu_initialize(const char *bin, size_t bin_size)
+{
+ if (bin_size > active_setup.flash_size) {
+ fprintf(stderr, "Code is bigger than the "
+ "microcontroller flash size.\n");
+ return -EINVAL;
+ }
+ code = avr_malloc(active_setup.flash_size);
+ if (!code)
+ return -ENOMEM;
+ memset(code, 0xFF, active_setup.flash_size);
+ memcpy(code, bin, bin_size);
+ cpu.initialized = 1;
+
+ return 0;
+}
+
+static void cpu_cleanup(void)
+{
+ if (cpu.running)
+ trap_cpu(CPU_TRAP_EXIT, 1);
+ if (cpu.initialized) {
+ cpu.initialized = 0;
+ cpu_interrupts_exit();
+ pthread_cond_destroy(&cpu.trapped_cond);
+ pthread_mutex_destroy(&cpu.trap_mutex);
+ }
+}
+
+int cpu_reset(void)
+{
+ int err;
+
+ cpu_cleanup();
+
+ memset(&cpu, 0, sizeof(cpu));
+ /* The application program will initialize the stack pointer.
+ * So we leave it zero. */
+
+ /* Registers are mapped to the start of MMIO space. */
+ cpu.r = memory.ram;
+ /* We start at the reset vector. */
+ cpu.pc = AVR_VECTOR_RESET;
+ /* Set the breakpoint so that it never triggers. */
+ cpu.breakpoint = ~0;
+ /* Init lazy flags. Set all to valid zeros. */
+ cpu.sreg = 0;
+ cpu.sreg_valid = 0xFF;
+ memset(&cpu.lazy_flag_cache, 0, sizeof(cpu.lazy_flag_cache));
+ /* Initialize the CPU trap. */
+ cpu.trap = CPU_TRAP_CONTINUE;
+ err = pthread_mutex_init(&cpu.trap_mutex, NULL);
+ if (err)
+ goto out_error;
+ err = pthread_cond_init(&cpu.trapped_cond, NULL);
+ if (err)
+ goto err_destr_trap;
+ err = register_cpu_io_handlers();
+ if (err)
+ goto err_destr_cond;
+ err = cpu_interrupts_init();
+ if (err)
+ goto err_destr_cond;
+
+ /* Fire up the CPU! */
+ cpu.running = 1;
+ err = pthread_create(&cpu_thread, NULL, cpu_thread_function, NULL);
+ if (err) {
+ cpu.running = 0;
+ perror("Failed to start CPU thread");
+ goto err_irq_exit;
+ }
+
+ return 0;
+
+err_irq_exit:
+ cpu_interrupts_exit();
+err_destr_cond:
+ pthread_cond_destroy(&cpu.trapped_cond);
+err_destr_trap:
+ pthread_mutex_destroy(&cpu.trap_mutex);
+out_error:
+ fprintf(stderr, "Failed to initialize the CPU data structure.\n");
+ return -ENOMEM;
+}
+
+void cpu_exit(void)
+{
+ cpu_cleanup();
+ free(code);
+ code = NULL;
+}
diff --git a/emulator/cpu.h b/emulator/cpu.h
new file mode 100644
index 0000000..6674ad6
--- /dev/null
+++ b/emulator/cpu.h
@@ -0,0 +1,177 @@
+#ifndef AVREMU_CPU_H_
+#define AVREMU_CPU_H_
+
+#include "util.h"
+#include "memory.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <pthread.h>
+
+
+#define AVR_VECTOR_RESET 0x000
+
+/* Status register bits. */
+#define SREG_C_BIT 0
+#define SREG_C (1 << SREG_C_BIT) /* Carry */
+#define SREG_Z_BIT 1
+#define SREG_Z (1 << SREG_Z_BIT) /* Zero */
+#define SREG_N_BIT 2
+#define SREG_N (1 << SREG_N_BIT) /* Negative */
+#define SREG_V_BIT 3
+#define SREG_V (1 << SREG_V_BIT) /* Two's complement overflow */
+#define SREG_S_BIT 4
+#define SREG_S (1 << SREG_S_BIT) /* Sign */
+#define SREG_H_BIT 5
+#define SREG_H (1 << SREG_H_BIT) /* Half carry */
+#define SREG_T_BIT 6
+#define SREG_T (1 << SREG_T_BIT) /* Bit copy storage */
+#define SREG_I_BIT 7
+#define SREG_I (1 << SREG_I_BIT) /* Global IRQ enable */
+
+
+/* Instruction identifiers for lazy SREG flags evaluation. */
+enum lazy_flag_insn {
+ /* Instructions with 8-bit operands. */
+ INSN_SBCI,
+ INSN_SUBI,
+ INSN_ORI,
+ INSN_ANDI,
+ INSN_CPI,
+ INSN_COM,
+ INSN_NEG,
+ INSN_DEC,
+ INSN_INC,
+ INSN_LSR,
+ INSN_ROR,
+ INSN_ASR,
+ INSN_CPC,
+ INSN_SBC,
+ INSN_ADD,
+ INSN_CP,
+ INSN_SUB,
+ INSN_ADC,
+ INSN_AND,
+ INSN_EOR,
+ INSN_OR,
+
+ /* Instructions with 16-bit operands */
+ INSN_ADIW,
+ INSN_SBIW,
+
+ /* The total number of lazy flag instructions. */
+ NR_LAZYFLAG_INSNS,
+ /* The first instruction with 16-bit operands. */
+ FIRST_16BIT_INSN = INSN_ADIW,
+};
+
+/* Context data for fetching a lazy sreg flag. */
+struct lazy_flag {
+ union {
+ /* 8-bit operations */
+ struct {
+ /* The operands to the instruction. */
+ uint8_t op[2];
+ /* The operation result. */
+ uint8_t res;
+ };
+ /* 16-bit operations */
+ struct {
+ /* The operands to the instruction. */
+ uint16_t op16[2];
+ /* The operation result. */
+ uint16_t res16;
+ };
+ };
+};
+
+struct avr_irq_descriptor {
+ struct list_head list;
+ /* The vector to jump to. */
+ uint8_t vector;
+};
+#define MAX_IRQS_PENDING 0xFF
+
+#define AVR_NR_REGS 32
+
+/* The AVR CPU */
+struct avr_cpu {
+ /* Program counter.
+ * The PC addresses 16-bit words (not bytes). */
+ uint32_t pc;
+ /* The PC where to break execution. */
+ uint32_t breakpoint;
+ /* Array of general purpose registers */
+ uint8_t *r;
+
+ /* SREG flags bitmap.
+ * Look at sreg_valid bitmap to check whether a bit in "sreg" is valid. */
+ uint8_t sreg;
+ /* Bitmap of the valid flags in the "sreg" bitmap. If a flag
+ * is invalid but needed it must be fetched lazyly. */
+ uint8_t sreg_valid;
+ /* Per-SREG-flag array of pointers to lazy_flag data structures. */
+ struct lazy_flag *sreg_lazy[8];
+
+ /* Per-instruction lazy_flag operand cache. */
+ struct lazy_flag lazy_flag_cache[NR_LAZYFLAG_INSNS];
+
+ /* Used to trap the CPU at a safe point from another thread. */
+ uint8_t trap;
+ pthread_mutex_t trap_mutex;
+ pthread_cond_t trapped_cond;
+
+ /* Interrupts */
+ uint8_t irqs_pending;
+ struct list_head irqdesc_pending;
+ struct list_head irqdesc_cache;
+ pthread_spinlock_t irq_lock;
+ void *irq_cache_p; /* Only used for freeing the data. */
+
+ bool initialized;
+ bool running;
+
+ /* Statistics */
+
+ /* CPU cycle counter. */
+ uint64_t cycles_count;
+ /* Interrupt counter. */
+ uint64_t irq_count;
+};
+
+/* Pointer registers */
+#define X_LO 26
+#define X_HI 27
+#define Y_LO 28
+#define Y_HI 29
+#define Z_LO 30
+#define Z_HI 31
+
+
+/* Initialize and reset the CPU data structures. */
+int cpu_initialize(const char *bin, size_t bin_size);
+void cpu_exit(void);
+
+int cpu_reset(void);
+
+void dump_cpu(FILE *fd);
+
+/* API for trapping and liberating the CPU from another thread.
+ * The CPU will be trapped before it fetches the next instruction. */
+enum cpu_trap_id {
+ CPU_TRAP_CONTINUE = 0,
+ CPU_TRAP_WAIT,
+ CPU_TRAP_SINGLESTEP,
+ CPU_TRAP_EXIT, /* Shutdown the CPU. */
+};
+int trap_cpu(uint8_t id, bool synchronize);
+
+/* Trigger an interrupt on the CPU.
+ * This will return -EBUSY if there are already
+ * MAX_IRQS_PENDING irqs pending. */
+int cpu_trigger_interrupt(uint8_t vector);
+
+extern struct avr_cpu cpu;
+
+#endif /* AVREMU_CPU_H_ */
diff --git a/emulator/devices/ports.c b/emulator/devices/ports.c
new file mode 100644
index 0000000..7d278ff
--- /dev/null
+++ b/emulator/devices/ports.c
@@ -0,0 +1,259 @@
+/*
+ * AVR emulator
+ * Ports
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "ports.h"
+#include "memory.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+
+
+enum port_voltage_level {
+ PORT_VOLT_TRISTATE = 0,
+ PORT_VOLT_HIGH,
+ PORT_VOLT_LOW,
+};
+
+struct port {
+ /* Spinlock for this port. */
+ pthread_spinlock_t lock;
+ /* Data direction of the individual port bits.
+ * Bit-set means output. */
+ uint8_t ddr;
+ /* PORTX register status. */
+ uint8_t port;
+ /* Voltage level status on the physical pin. */
+ enum port_voltage_level levels[8];
+};
+
+
+static struct port portb;
+static struct port portc;
+static struct port portd;
+
+
+static void port_read(struct port *port, uint16_t mem_offset)
+{
+ pthread_spin_lock(&port->lock);
+ memory.ram[mem_offset] = port->port;
+ pthread_spin_lock(&port->lock);
+}
+
+static void port_write(struct port *port, uint8_t new_val)
+{
+ pthread_spin_lock(&port->lock);
+ port->port = new_val;
+ //TODO
+ pthread_spin_unlock(&port->lock);
+}
+
+static void ddr_read(struct port *port, uint16_t mem_offset)
+{
+ pthread_spin_lock(&port->lock);
+ memory.ram[mem_offset] = port->ddr;
+ pthread_spin_unlock(&port->lock);
+}
+
+static void ddr_write(struct port *port, uint8_t new_val)
+{
+ pthread_spin_lock(&port->lock);
+ port->ddr = new_val;
+ pthread_spin_unlock(&port->lock);
+}
+
+static void pin_read(struct port *port, uint16_t mem_offset)
+{
+ int i;
+ uint8_t mask, data = 0;
+
+ pthread_spin_lock(&port->lock);
+ for (i = 0; i < 8; i++) {
+ mask = (1 << i);
+ if (unlikely(port->ddr & mask)) {
+ /* This is an output pin. */
+ if (port->port & mask)
+ data |= mask;
+ } else {
+ /* This is an input pin. */
+ switch (port->levels[i]) {
+ case PORT_VOLT_TRISTATE:
+ if (port->port & mask)
+ data |= mask;
+ break;
+ case PORT_VOLT_HIGH:
+ data |= mask;
+ break;
+ case PORT_VOLT_LOW:
+ break;
+ }
+ }
+ }
+ memory.ram[mem_offset] = data;
+ pthread_spin_lock(&port->lock);
+}
+
+static void io_portb_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val)
+{
+ port_write(&portb, new_val);
+}
+
+static void io_portc_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val)
+{
+ port_write(&portc, new_val);
+}
+
+static void io_portd_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val)
+{
+ port_write(&portd, new_val);
+}
+
+static void io_ddrb_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val)
+{
+ ddr_write(&portb, new_val);
+}
+
+static void io_ddrc_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val)
+{
+ ddr_write(&portc, new_val);
+}
+
+static void io_ddrd_write(uint16_t mem_offset, uint8_t old_val, uint8_t new_val)
+{
+ ddr_write(&portd, new_val);
+}
+
+static void io_portb_read(uint16_t mem_offset)
+{
+ port_read(&portb, mem_offset);
+}
+
+static void io_portc_read(uint16_t mem_offset)
+{
+ port_read(&portc, mem_offset);
+}
+
+static void io_portd_read(uint16_t mem_offset)
+{
+ port_read(&portd, mem_offset);
+}
+
+static void io_pinb_read(uint16_t mem_offset)
+{
+ pin_read(&portb, mem_offset);
+}
+
+static void io_pinc_read(uint16_t mem_offset)
+{
+ pin_read(&portc, mem_offset);
+}
+
+static void io_pind_read(uint16_t mem_offset)
+{
+ pin_read(&portd, mem_offset);
+}
+
+static void io_ddrb_read(uint16_t mem_offset)
+{
+ ddr_read(&portb, mem_offset);
+}
+
+static void io_ddrc_read(uint16_t mem_offset)
+{
+ ddr_read(&portc, mem_offset);
+}
+
+static void io_ddrd_read(uint16_t mem_offset)
+{
+ ddr_read(&portd, mem_offset);
+}
+
+static int port_init(struct port *port)
+{
+ int err;
+
+ memset(port, 0, sizeof(struct port));
+ err = pthread_spin_init(&port->lock, 0);
+ if (err) {
+ fprintf(stderr, "Ports: Failed to init spinlock\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static void port_cleanup(struct port *port)
+{
+ pthread_spin_destroy(&port->lock);
+}
+
+int avrdev_ports_init(void)
+{
+ int err;
+
+ err = port_init(&portb);
+ if (err)
+ goto error;
+ err = port_init(&portc);
+ if (err)
+ goto err_cleanup_portb;
+ err = port_init(&portd);
+ if (err)
+ goto err_cleanup_portc;
+
+ err = 0;
+ err |= register_io_write_handler(io_portb_write, IO_PORTB);
+ err |= register_io_write_handler(io_portc_write, IO_PORTC);
+ err |= register_io_write_handler(io_portd_write, IO_PORTD);
+ err |= register_io_write_handler(io_ddrb_write, IO_DDRB);
+ err |= register_io_write_handler(io_ddrc_write, IO_DDRC);
+ err |= register_io_write_handler(io_ddrd_write, IO_DDRD);
+ err |= register_io_read_handler(io_portb_read, IO_PORTB);
+ err |= register_io_read_handler(io_portc_read, IO_PORTC);
+ err |= register_io_read_handler(io_portd_read, IO_PORTD);
+ err |= register_io_read_handler(io_pinb_read, IO_PINB);
+ err |= register_io_read_handler(io_pinc_read, IO_PINC);
+ err |= register_io_read_handler(io_pind_read, IO_PIND);
+ err |= register_io_read_handler(io_ddrb_read, IO_DDRB);
+ err |= register_io_read_handler(io_ddrc_read, IO_DDRC);
+ err |= register_io_read_handler(io_ddrd_read, IO_DDRD);
+ if (err) {
+ err = -ENOMEM;
+ fprintf(stderr, "Failed to register port handlers.\n");
+ goto err_cleanup_portd;
+ }
+
+ return 0;
+
+err_cleanup_portd:
+ port_cleanup(&portd);
+err_cleanup_portc:
+ port_cleanup(&portc);
+err_cleanup_portb:
+ port_cleanup(&portb);
+error:
+ return err;
+}
+
+void avrdev_ports_exit(void)
+{//FIXME somebody must call me
+ port_cleanup(&portd);
+ port_cleanup(&portc);
+ port_cleanup(&portb);
+}
diff --git a/emulator/devices/ports.h b/emulator/devices/ports.h
new file mode 100644
index 0000000..cd89f40
--- /dev/null
+++ b/emulator/devices/ports.h
@@ -0,0 +1,7 @@
+#ifndef AVREMU_DEVICES_PORTS_H_
+#define AVREMU_DEVICES_PORTS_H_
+
+int avrdev_ports_init(void);
+void avrdev_ports_exit(void);
+
+#endif /* AVREMU_DEVICES_PORTS_H_ */
diff --git a/emulator/devices/timers.c b/emulator/devices/timers.c
new file mode 100644
index 0000000..2653852
--- /dev/null
+++ b/emulator/devices/timers.c
@@ -0,0 +1,28 @@
+/*
+ * AVR emulator
+ * Timer devices
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "memory.h"
+
+
+int avrdev_timers_init(void)
+{
+ return 0;
+}
+
+void avrdev_timers_exit(void)
+{
+}
diff --git a/emulator/devices/timers.h b/emulator/devices/timers.h
new file mode 100644
index 0000000..a007135
--- /dev/null
+++ b/emulator/devices/timers.h
@@ -0,0 +1,7 @@
+#ifndef AVREMU_DEVICES_TIMERS_H_
+#define AVREMU_DEVICES_TIMERS_H_
+
+int avrdev_timers_init(void);
+void avrdev_timers_exit(void);
+
+#endif /* AVREMU_DEVICES_TIMERS_H_ */
diff --git a/emulator/devprocess.c b/emulator/devprocess.c
new file mode 100644
index 0000000..1003839
--- /dev/null
+++ b/emulator/devprocess.c
@@ -0,0 +1,156 @@
+/*
+ * AVR emulator
+ * The device emulator subprocess
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "devprocess.h"
+#include "hardware.h"
+#include "cpu.h"
+
+#include <signal.h>
+#include <errno.h>
+
+
+/* Global pointer to the device instance from within the device process. */
+struct avremu_device_instance *avremu_dev;
+
+
+static void device_cleanup(void)
+{
+ cpu_exit();
+ dump_cpu(stdout);
+ avr_do_cleanup();
+}
+
+static void __attribute__((noreturn)) device_shutdown(int error)
+{
+ device_cleanup();
+ sendnotify_to_host(avremu_dev, HOSTMSG_EXIT);
+ exit(error >= 0 ? error : -error);
+}
+
+static void device_signal_handler(int signr)
+{
+ switch (signr) {
+ case SIGSEGV:
+ fprintf(stderr, "Segmentation fault\n");
+ if (CHECK(1))
+ fprintf(stderr, "This is probably a bug in the emulator.\n");
+ else
+ fprintf(stderr, "This is either a bug in the running AVR "
+ "program or in the emulator software.\n");
+ device_shutdown(1);
+ break;
+ case SIGTERM:
+ case SIGINT:
+ printf("Emulated device shutting down...\n");
+ device_shutdown(0);
+ break;
+ default:
+ fprintf(stderr, "Device caught unknown signal %d\n", signr);
+ device_shutdown(ENOSYS);
+ }
+}
+
+static int device_handle_msg_from_host(struct avrmsg_to_device *msg)
+{
+ char *data;
+ size_t size;
+ int err;
+ ipc_result_t result;
+
+//FIXME we should probably try to drain the buffer on error instead of exiting.
+
+ switch (msg->msg) {
+ case DEVMSG_LOADFLASH:
+ size = msg->size;
+ data = avr_malloc(size);
+ if (!data) {
+ ipc_send_result(&avremu_dev->msg_from_host, -ENOMEM);
+ return 0;
+ }
+ ipc_send_result(&avremu_dev->msg_from_host, 0);
+ err = ipc_payload_receive(&avremu_dev->msg_from_host, data, size);
+ if (err) {
+ ipc_send_result(&avremu_dev->msg_from_host, err);
+ free(data);
+ return 0;
+ }
+ result = cpu_initialize(data, size);
+ free(data);
+ ipc_send_result(&avremu_dev->msg_from_host, result);
+ if (!result)
+ printf("CPU initialized\n");
+ break;
+ case DEVMSG_RESET:
+ result = cpu_reset();
+ ipc_send_result(&avremu_dev->msg_from_host, result);
+ if (!result)
+ printf("CPU reset\n");
+ break;
+ }
+
+ return 0;
+}
+
+int avr_device_process_run(struct avremu_device_instance *dev)
+{
+ struct sigaction sa;
+ int err;
+ struct avrmsg_to_device msg;
+ sigset_t blockset;
+
+ avremu_dev = dev;
+
+ sa.sa_handler = device_signal_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ /* Block these signals while processing messages. */
+ sigemptyset(&blockset);
+ sigaddset(&blockset, SIGTERM);
+ sigaddset(&blockset, SIGINT);
+
+ err = avr_do_setup(dev->type);
+ if (err)
+ return err;
+ err = sendnotify_to_host(dev, HOSTMSG_READY);
+ if (err)
+ return -EIO;
+
+ while (1) {
+ err = pollmsg_from_host(dev, &msg);
+ if (err == 0)
+ continue;
+ if (unlikely(err < 0)) {
+ fprintf(stderr, "Device message RX failed %d\n", err);
+ err = -EIO;
+ goto error;
+ }
+ pthread_sigmask(SIG_BLOCK, &blockset, NULL);
+ err = device_handle_msg_from_host(&msg);
+ pthread_sigmask(SIG_UNBLOCK, &blockset, NULL);
+ if (unlikely(err))
+ goto error;
+ }
+
+ return ENOTTY;
+error:
+ device_cleanup();
+ return err;
+}
diff --git a/emulator/devprocess.h b/emulator/devprocess.h
new file mode 100644
index 0000000..4bdcf6a
--- /dev/null
+++ b/emulator/devprocess.h
@@ -0,0 +1,9 @@
+#ifndef AVREMU_DEVPROCESS_H_
+#define AVREMU_DEVPROCESS_H_
+
+#include "subprocess.h"
+
+
+int avr_device_process_run(struct avremu_device_instance *dev);
+
+#endif /* AVREMU_DEVPROCESS_H_ */
diff --git a/emulator/hardware.c b/emulator/hardware.c
new file mode 100644
index 0000000..ea3fb9e
--- /dev/null
+++ b/emulator/hardware.c
@@ -0,0 +1,270 @@
+/*
+ * AVR emulator
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "hardware.h"
+#include "memory.h"
+#include "pinout.h"
+
+#include "devices/timers.h"
+#include "devices/ports.h"
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+
+struct avr_setup active_setup;
+
+
+static void init_iomap(struct avr_memory *mem)
+{
+ int i;
+
+ for (i = 0; i < NR_VIRT_IO_PORTS; i++)
+ mem->physical_io_map[i] = IO_PORT_UNAVAILABLE;
+}
+
+#undef IO
+#define IO(virt, phys) mem->physical_io_map[IO_##virt] = (phys);
+
+static void mega8_io_setup(struct avr_memory *mem)
+{
+ init_iomap(mem);
+ IO(TWBR, 0x20);
+ IO(TWSR, 0x21);
+ IO(TWAR, 0x22);
+ IO(TWDR, 0x23);
+ IO(ADCL, 0x24);
+ IO(ADCH, 0x25);
+ IO(ADCSRA, 0x26);
+ IO(ADMUX, 0x27);
+ IO(ACSR, 0x28);
+ IO(UBRRL, 0x29);
+ IO(UCSRB, 0x2A);
+ IO(UCSRA, 0x2B);
+ IO(UDR, 0x2C);
+ IO(SPCR, 0x2D);
+ IO(SPSR, 0x2E);
+ IO(SPDR, 0x2F);
+ IO(PIND, 0x30);
+ IO(DDRD, 0x31);
+ IO(PORTD, 0x32);
+ IO(PINC, 0x33);
+ IO(DDRC, 0x34);
+ IO(PORTC, 0x35);
+ IO(PINB, 0x36);
+ IO(DDRB, 0x37);
+ IO(PORTB, 0x38);
+ IO(EECR, 0x3C);
+ IO(EEDR, 0x3D);
+ IO(EEARL, 0x3E);
+ IO(EEARH, 0x3F);
+ IO(UCSRC, 0x40);
+ IO(UBRRH, 0x40);
+ IO(WDTCR, 0x41);
+ IO(ASSR, 0x42);
+ IO(OCR2, 0x43);
+ IO(TCNT2, 0x44);
+ IO(TCCR2, 0x45);
+ IO(ICR1L, 0x46);
+ IO(ICR1H, 0x47);
+ IO(OCR1BL, 0x48);
+ IO(OCR1BH, 0x49);
+ IO(OCR1AL, 0x4A);
+ IO(OCR1AH, 0x4B);
+ IO(TCNT1L, 0x4C);
+ IO(TCNT1H, 0x4D);
+ IO(TCCR1B, 0x4E);
+ IO(TCCR1A, 0x4F);
+ IO(SFIOR, 0x50);
+ IO(OSCAL, 0x51);
+ IO(TCNT0, 0x52);
+ IO(TCCR0, 0x53);
+ IO(MCUCSR, 0x54);
+ IO(MCUCR, 0x55);
+ IO(TWCR, 0x56);
+ IO(SPMCR, 0x57);
+ IO(TIFR, 0x58);
+ IO(TIMSK, 0x59);
+ IO(GIFR, 0x5A);
+ IO(GICR, 0x5B);
+ IO(SPL, 0x5D);
+ IO(SPH, 0x5E);
+ IO(SREG, 0x5F);
+
+ //TODO init the peripheral hardware.
+}
+
+static void mega48_88_168_io_setup(struct avr_memory *mem)
+{
+ init_iomap(mem);
+ IO(PINB, 0x23);
+ IO(DDRB, 0x24);
+ IO(PORTB, 0x25);
+ IO(PINC, 0x26);
+ IO(DDRC, 0x27);
+ IO(PORTC, 0x28);
+ IO(PIND, 0x29);
+ IO(DDRD, 0x2A);
+ IO(PORTD, 0x2B);
+ IO(TIFR0, 0x35);
+ IO(TIFR1, 0x36);
+ IO(TIFR2, 0x37);
+ IO(PCIFR, 0x3B);
+ IO(EIFR, 0x3C);
+ IO(EIMSK, 0x3D);
+ IO(GPIOR0, 0x3E);
+ IO(EECR, 0x3F);
+ IO(EEDR, 0x40);
+ IO(EEARL, 0x41);
+ IO(EEARH, 0x42);
+ IO(GTCCR, 0x43);
+ IO(TCCR0A, 0x44);
+ IO(TCCR0B, 0x45);
+ IO(TCNT0, 0x46);
+ IO(OCR0A, 0x47);
+ IO(OCR0B, 0x48);
+ IO(GPIOR1, 0x4A);
+ IO(GPIOR2, 0x4B);
+ IO(SPCR, 0x4C);
+ IO(SPSR, 0x4D);
+ IO(SPDR, 0x4E);
+ IO(ACSR, 0x50);
+ IO(SMCR, 0x53);
+ IO(MCUSR, 0x54);
+ IO(MCUCR, 0x55);
+ IO(SPMCSR, 0x57);
+ IO(SPL, 0x5D);
+ IO(SPH, 0x5E);
+ IO(SREG, 0x5F);
+ IO(WDRCSR, 0x60);
+ IO(CLKPR, 0x61);
+ IO(PRR, 0x64);
+ IO(OSCAL, 0x66);
+ IO(PCICR, 0x68);
+ IO(EICRA, 0x69);
+ IO(PCMSK0, 0x6B);
+ IO(PCMSK1, 0x6C);
+ IO(PCMSK2, 0x6D);
+ IO(TIMSK0, 0x6E);
+ IO(TIMSK1, 0x6F);
+ IO(TIMSK2, 0x70);
+ IO(ADCL, 0x78);
+ IO(ADCH, 0x79);
+ IO(ADCSRA, 0x7A);
+ IO(ADCSRB, 0x7B);
+ IO(ADMUX, 0x7C);
+ IO(DIDR0, 0x7E);
+ IO(DIDR1, 0x7F);
+ IO(TCCR1A, 0x80);
+ IO(TCCR1B, 0x81);
+ IO(TCCR1C, 0x82);
+ IO(TCNT1L, 0x84);
+ IO(TCNT1H, 0x85);
+ IO(ICR1L, 0x86);
+ IO(ICR1H, 0x87);
+ IO(OCR1AL, 0x88);
+ IO(OCR1AH, 0x89);
+ IO(OCR1BL, 0x8A);
+ IO(OCR1BH, 0x8B);
+ IO(TCCR2A, 0xB0);
+ IO(TCCR2B, 0xB1);
+ IO(TCNT2, 0xB2);
+ IO(OCR2A, 0xB3);
+ IO(OCR2B, 0xB4);
+ IO(ASSR, 0xB6);
+ IO(TWBR, 0xB8);
+ IO(TWSR, 0xB9);
+ IO(TWAR, 0xBA);
+ IO(TWDR, 0xBB);
+ IO(TWCR, 0xBC);
+ IO(TWAMR, 0xBD);
+ IO(UCSR0A, 0xC0);
+ IO(UCSR0B, 0xC1);
+ IO(UCSR0C, 0xC2);
+ IO(UBRR0L, 0xC4);
+ IO(UBRR0H, 0xC5);
+ IO(UDR0, 0xC6);
+
+ //TODO init the peripheral hardware.
+ avrdev_timers_init();
+ avrdev_ports_init();
+}
+
+#undef IO
+
+#define kiB * 1024
+#define Mhz * 1000000
+static const struct avr_setup setup_list[] = {
+ [AVR_ATMEGA8] = {
+ .type = AVR_ATMEGA8,
+ .pinout = &atmega8_pinout,
+ .memory_size = 0x460,
+ .sram_offset = 0x60,
+ .sp_offset = 0x3D,
+ .pc_22bit = 0,
+ .flash_size = 8 kiB,
+ .eeprom_size = 512,
+ .max_hz = 16 Mhz,
+ .io_setup = mega8_io_setup,
+ },
+ [AVR_ATMEGA168] = {
+ .type = AVR_ATMEGA168,
+ .pinout = &atmega48_88_168_pinout,
+ .memory_size = 0x500,
+ .sram_offset = 0x100,
+ .sp_offset = 0x3D,
+ .pc_22bit = 0,
+ .flash_size = 16 kiB,
+ .eeprom_size = 512,
+ .max_hz = 20 Mhz,
+ .io_setup = mega48_88_168_io_setup,
+ },
+};
+
+void avr_do_cleanup(void)
+{
+ memory_cleanup();
+ free(active_setup.memory_data);
+}
+
+int avr_do_setup(enum avr_setup_type type)
+{
+ int err;
+
+ break_linkage_on(ARRAY_SIZE(setup_list) != AVR_NR_TYPES,
+ setup_not_defined_for_all_architectures);
+
+ if (type >= ARRAY_SIZE(setup_list)) {
+ fprintf(stderr, "Unsupported architecture\n");
+ return -ENODEV;
+ }
+ memcpy(&active_setup, &(setup_list[type]), sizeof(active_setup));
+ active_setup.memory_data = malloc(active_setup.memory_size);
+ if (!active_setup.memory_data) {
+ fprintf(stderr, "Out of memory\n");
+ return -ENOMEM;
+ }
+ err = memory_initialize();
+ if (err)
+ goto err_free_mem;
+ memset(&active_pinout_handlers, 0, sizeof(active_pinout_handlers));
+
+ return 0;
+err_free_mem:
+ free(active_setup.memory_data);
+ return err;
+}
diff --git a/emulator/hardware.h b/emulator/hardware.h
new file mode 100644
index 0000000..c5257eb
--- /dev/null
+++ b/emulator/hardware.h
@@ -0,0 +1,57 @@
+#ifndef AVREMU_HARDWARE_H_
+#define AVREMU_HARDWARE_H_
+
+#include "util.h"
+#include "pinout.h"
+
+#include <stdlib.h>
+
+
+struct avr_memory;
+typedef void (*avr_io_setup_func_t)(struct avr_memory *mem);
+
+
+
+/* The type of the microcontroller to emulate. */
+enum avr_setup_type {
+ AVR_ATMEGA8,
+ AVR_ATMEGA168,
+ //TODO
+ AVR_NR_TYPES,
+};
+
+struct avr_setup {
+ /* The device type */
+ enum avr_setup_type type;
+ /* Physical device pinout configuration. */
+ const struct avr_device_pinout *pinout;
+ /* Size of the memory data space including
+ * the register file, IO, MMIO and SRAM. */
+ size_t memory_size;
+ /* The offset where the general purpose SRAM starts. */
+ size_t sram_offset;
+ /* The offset of the stack pointer in the IO space. */
+ uint8_t sp_offset;
+ /* Using a 22 bit PC */
+ bool pc_22bit;
+ /* Size of the flash memory. */
+ size_t flash_size;
+ /* Size of the EEPROM memory. */
+ size_t eeprom_size;
+ /* Maximum CPU frequency in HZ. */
+ size_t max_hz;
+ /* Function to setup the virtual->physical IO map
+ * and initialize the peripheral IO devices. */
+ avr_io_setup_func_t io_setup;
+
+ /* Pointer to the memory data space (regs, IO, MMIO, SRAM) */
+ uint8_t *memory_data;//FIXME this should be moved to struct avr_memory.
+};
+
+extern struct avr_setup active_setup;
+
+int avr_do_setup(enum avr_setup_type type);
+void avr_do_cleanup(void);
+
+
+#endif /* AVREMU_HARDWARE_H_ */
diff --git a/emulator/install.py b/emulator/install.py
new file mode 100755
index 0000000..65a756c
--- /dev/null
+++ b/emulator/install.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+import sys
+from shutil import *
+from dircache import *
+
+bases = ( "/", "/usr", "/usr/local", "/opt" )
+instfiles = ( "pyrazer.so", )
+
+
+def usage():
+ print "Usage: %s MOD_SRC_DIR" % sys.argv[0]
+
+try:
+ srcdir = sys.argv[1]
+except IndexError:
+ usage()
+ sys.exit(1)
+
+pyver = sys.version.split()[0] # pyver == "X.X.X"
+pyver = pyver.split(".")
+major = pyver[0]
+minor = pyver[1]
+pydir = "python%s.%s" % (major, minor)
+modpath = "/lib/" + pydir + "/site-packages"
+
+for base in bases:
+ try:
+ if not pydir in listdir(base + "/lib"):
+ continue
+ full_modpath = base + modpath
+ # Probe whether it exists
+ listdir(full_modpath)
+ except OSError:
+ continue
+ print "Python module path found in " + full_modpath
+ try:
+ for f in instfiles:
+ copy(srcdir + "/" + f, full_modpath)
+ print "Installed \"%s\"" % f
+ except IOError, e:
+ print "ERROR: Could not install module \"%s\"" % f
+ print e
+ sys.exit(1)
+ sys.exit(0)
+
+
+print "ERROR: Python module install path not found."
+print "Python modules are usually found in /usr/lib/pythonX.X/site-packages"
+sys.exit(1)
diff --git a/emulator/ipc.c b/emulator/ipc.c
new file mode 100644
index 0000000..8131507
--- /dev/null
+++ b/emulator/ipc.c
@@ -0,0 +1,219 @@
+/*
+ * AVR emulator
+ * Interprocess communication
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "ipc.h"
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+
+#define IPC_POLL_MAX_USECS 10000
+
+
+int ipc_pipe_create(struct ipc_tx *tx, struct ipc_rx *rx)
+{
+ int message_pipe[2];
+ int result_pipe[2];
+ int err;
+
+ err = pipe(message_pipe);
+ if (err) {
+ perror("Create IPC message pipe");
+ return err;
+ }
+ err = pipe(result_pipe);
+ if (err) {
+ perror("Create IPC result pipe");
+ close(message_pipe[0]);
+ close(message_pipe[1]);
+ return err;
+ }
+ tx->message_pipe = message_pipe[1];
+ rx->message_pipe = message_pipe[0];
+ tx->result_pipe = result_pipe[0];
+ rx->result_pipe = result_pipe[1];
+
+ return 0;
+}
+
+void ipc_pipe_destroy_tx(struct ipc_tx *tx)
+{
+ if (tx->message_pipe != -1) {
+ close(tx->message_pipe);
+ tx->message_pipe = -1;
+ }
+ if (tx->result_pipe != -1) {
+ close(tx->result_pipe);
+ tx->result_pipe = -1;
+ }
+}
+
+void ipc_pipe_destroy_rx(struct ipc_rx *rx)
+{
+ if (rx->message_pipe != -1) {
+ close(rx->message_pipe);
+ rx->message_pipe = -1;
+ }
+ if (rx->result_pipe != -1) {
+ close(rx->result_pipe);
+ rx->result_pipe = -1;
+ }
+}
+
+void ipc_pipe_destroy(struct ipc_tx *tx, struct ipc_rx *rx)
+{
+ ipc_pipe_destroy_tx(tx);
+ ipc_pipe_destroy_rx(rx);
+}
+
+int ipc_dup2(struct ipc_tx *tx, int fd)
+{
+ return dup2(tx->message_pipe, fd);
+}
+
+int ipc_message_send(struct ipc_tx *tx,
+ const void *message, ipc_size_t msg_size,
+ bool read_result)
+{
+ ssize_t res;
+ ipc_result_t result_code;
+
+ res = write(tx->message_pipe, &msg_size, sizeof(msg_size));
+ if (unlikely(res != sizeof(msg_size)))
+ return -EIO;
+
+ res = write(tx->message_pipe, message, msg_size);
+ if (unlikely(res != msg_size))
+ return -EIO;
+
+ if (read_result) {
+ res = read(tx->result_pipe, &result_code, sizeof(result_code));
+ if (unlikely(res != sizeof(result_code)))
+ return -EIO;
+
+ return result_code;
+ }
+
+ return 0;
+}
+
+int ipc_message_poll(struct ipc_rx *rx,
+ void *message_buf, ipc_size_t buf_size)
+{
+ ssize_t res;
+ fd_set fdset;
+ ipc_size_t msg_size;
+ struct timeval tv = {
+ .tv_sec = 0,
+ .tv_usec = IPC_POLL_MAX_USECS,
+ };
+
+ FD_ZERO(&fdset);
+ FD_SET(rx->message_pipe, &fdset);
+ res = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
+ if (res <= 0) {
+ /* Timeout. No data. */
+ return 0;
+ }
+
+ res = read(rx->message_pipe, &msg_size, sizeof(msg_size));
+ if (unlikely(res != sizeof(msg_size)))
+ return -EIO;
+ if (unlikely(msg_size > buf_size))
+ return -ENOBUFS;
+ res = read(rx->message_pipe, message_buf, msg_size);
+ if (unlikely(res != msg_size))
+ return -EIO;
+
+ return msg_size;
+}
+
+int ipc_raw_line_poll(struct ipc_rx *rx,
+ char *line_buf, size_t buf_size)
+{
+ ssize_t res;
+ fd_set fdset;
+ struct timeval tv = {
+ .tv_sec = 0,
+ .tv_usec = IPC_POLL_MAX_USECS,
+ };
+ size_t i;
+ char c;
+
+ FD_ZERO(&fdset);
+ FD_SET(rx->message_pipe, &fdset);
+ res = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
+ if (res <= 0) {
+ /* Timeout. No data. */
+ return 0;
+ }
+
+ for (i = 0; ; i++) {
+ res = read(rx->message_pipe, &c, sizeof(c));
+ if (res != sizeof(c))
+ break;
+ if (unlikely(i >= buf_size - 1))
+ return -ENOBUFS;
+ line_buf[i] = c;
+ if (c == '\n') {
+ line_buf[i + 1] = '\0';
+ break;
+ }
+ }
+
+ return i;
+}
+
+int ipc_send_result(struct ipc_rx *rx, ipc_result_t result_code)
+{
+ ssize_t res;
+
+ res = write(rx->result_pipe, &result_code, sizeof(result_code));
+ if (unlikely(res != sizeof(result_code)))
+ return -EIO;
+
+ return 0;
+}
+
+int ipc_payload_send(struct ipc_tx *tx, const void *buf, size_t size)
+{
+ ssize_t res;
+ ipc_result_t result_code;
+
+ res = write(tx->message_pipe, buf, size);
+ if (unlikely(res != size))
+ return -EIO;
+
+ res = read(tx->result_pipe, &result_code, sizeof(result_code));
+ if (unlikely(res != sizeof(result_code)))
+ return -EIO;
+
+ return result_code;
+}
+
+int ipc_payload_receive(struct ipc_rx *rx, void *buf, size_t size)
+{
+ ssize_t res;
+
+ res = read(rx->message_pipe, buf, size);
+ if (unlikely(res != size))
+ return -EIO;
+
+ return 0;
+}
+
diff --git a/emulator/ipc.h b/emulator/ipc.h
new file mode 100644
index 0000000..eb0cb5c
--- /dev/null
+++ b/emulator/ipc.h
@@ -0,0 +1,62 @@
+#ifndef AVREMU_IPC_H_
+#define AVREMU_IPC_H_
+
+#include "util.h"
+
+#include <unistd.h>
+
+
+/* The sender side of an IPC connection. */
+struct ipc_tx {
+ /* Filedescriptor for the transmit side of the message pipe. */
+ int message_pipe;
+ /* Filedescriptor for the result message pipe. */
+ int result_pipe;
+};
+
+/* The receiver side of an IPC connection. */
+struct ipc_rx {
+ /* Filedescriptor for the receive side of the message pipe. */
+ int message_pipe;
+ /* Filedescriptor for the result message pipe. */
+ int result_pipe;
+};
+
+/* Type of a message result code. */
+typedef uint16_t ipc_result_t;
+/* Type of a message size descriptor. */
+typedef uint16_t ipc_size_t;
+
+
+/* Create a communication pipe. */
+int ipc_pipe_create(struct ipc_tx *tx, struct ipc_rx *rx);
+void ipc_pipe_destroy_tx(struct ipc_tx *tx);
+void ipc_pipe_destroy_rx(struct ipc_rx *rx);
+void ipc_pipe_destroy(struct ipc_tx *tx, struct ipc_rx *rx);
+
+/* Map a given file descriptor to an IPC pipe. */
+int ipc_dup2(struct ipc_tx *tx, int fd);
+
+/* Send a message through the IPC pipe.
+ * The result (if requested) is returned as a positive number.
+ * If an IPC error occured, a negative number is returned. */
+int ipc_message_send(struct ipc_tx *tx,
+ const void *message, ipc_size_t msg_size,
+ bool read_result);
+
+/* Poll a message pipe to receive the next message. */
+int ipc_message_poll(struct ipc_rx *rx,
+ void *message_buf, ipc_size_t buf_size);
+
+/* Poll a raw line of data from the pipe. */
+int ipc_raw_line_poll(struct ipc_rx *rx,
+ char *line_buf, size_t buf_size);
+
+/* Send the result for a message. */
+int ipc_send_result(struct ipc_rx *rx, ipc_result_t result_code);
+
+/* Payload data transmission. */
+int ipc_payload_send(struct ipc_tx *tx, const void *buf, size_t size);
+int ipc_payload_receive(struct ipc_rx *rx, void *buf, size_t size);
+
+#endif /* AVREMU_IPC_H_ */
diff --git a/emulator/list.h b/emulator/list.h
new file mode 100644
index 0000000..110c0bd
--- /dev/null
+++ b/emulator/list.h
@@ -0,0 +1,296 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+
+#define typeof __typeof__
+#define offsetof(type, member) __builtin_offsetof (type, member)
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+
+#ifdef RUNTIME_SANITY_CHECKS
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+#endif
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* _LIST_H */
diff --git a/emulator/memory.c b/emulator/memory.c
new file mode 100644
index 0000000..3c5d782
--- /dev/null
+++ b/emulator/memory.c
@@ -0,0 +1,118 @@
+/*
+ * AVR emulator
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "memory.h"
+#include "util.h"
+#include "cpu.h"
+#include "hardware.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+
+/* The memory (SRAM and MMIO). */
+struct avr_memory memory;
+
+
+int register_io_write_handler(io_write_handler_t handler, enum avr_io_port virt)
+{
+ uint16_t phys;
+
+ if (virt >= NR_VIRT_IO_PORTS)
+ goto unavailable;
+ phys = io_virt_to_phys(virt);
+ if (phys == IO_PORT_UNAVAILABLE)
+ goto unavailable;
+ phys -= IO_MEM_OFFSET;
+ if (memory.io_write_handlers[phys])
+ goto busy;
+ memory.io_write_handlers[phys] = handler;
+
+ return 0;
+unavailable:
+ fprintf(stderr, "IO-write: Tried to register handler for unavailable IO port\n");
+ return -EEXIST;
+busy:
+ fprintf(stderr, "IO-write: Tried to register an IO handler twice\n");
+ return -EALREADY;
+}
+
+int register_io_read_handler(io_read_handler_t handler, enum avr_io_port virt)
+{
+ uint16_t phys;
+
+ if (virt >= NR_VIRT_IO_PORTS)
+ goto unavailable;
+ phys = io_virt_to_phys(virt);
+ if (phys == IO_PORT_UNAVAILABLE)
+ goto unavailable;
+ phys -= IO_MEM_OFFSET;
+ if (memory.io_read_handlers[phys])
+ goto busy;
+ memory.io_read_handlers[phys] = handler;
+
+ return 0;
+unavailable:
+ fprintf(stderr, "IO-read: Tried to register handler for unavailable IO port\n");
+ return -EEXIST;
+busy:
+ fprintf(stderr, "IO-read: Tried to register an IO handler twice\n");
+ return -EALREADY;
+}
+
+void memory_cleanup(void)
+{
+ //TODO
+}
+
+int memory_initialize(void)
+{
+ int err;
+ size_t nr_io;
+
+ memory.ram = active_setup.memory_data;
+ memory.size = active_setup.memory_size;
+ memory.io = memory.ram + IO_MEM_OFFSET;
+ memory.sram_offset = active_setup.sram_offset;
+ err = pthread_spin_init(&memory.io_lock, 0);
+ if (err) {
+ fprintf(stderr, "Failed to init the I/O spinlock\n");
+ return -ENOMEM;
+ }
+ memory.sp_offset = active_setup.sp_offset;
+
+ nr_io = active_setup.sram_offset - IO_MEM_OFFSET;
+ memory.iospace_size = nr_io;
+
+ memory.io_write_handlers = malloc(sizeof(io_write_handler_t) * nr_io);
+ if (!memory.io_write_handlers) {
+ fprintf(stderr, "Failed to alloc IO write handlers\n");
+ return -ENOMEM;
+ }
+ memset(memory.io_write_handlers, 0, sizeof(io_write_handler_t) * nr_io);
+
+ memory.io_read_handlers = malloc(sizeof(io_read_handler_t) * nr_io);
+ if (!memory.io_read_handlers) {
+ fprintf(stderr, "Failed to alloc IO read handlers\n");
+ return -ENOMEM;
+ }
+ memset(memory.io_read_handlers, 0, sizeof(io_read_handler_t) * nr_io);
+
+ active_setup.io_setup(&memory);
+
+ return 0;
+}
diff --git a/emulator/memory.h b/emulator/memory.h
new file mode 100644
index 0000000..ba30999
--- /dev/null
+++ b/emulator/memory.h
@@ -0,0 +1,225 @@
+#ifndef AVREMU_MEMORY_H_
+#define AVREMU_MEMORY_H_
+
+#include "util.h"
+
+#include <stdint.h>
+#include <pthread.h>
+
+
+typedef void (*io_write_handler_t)(uint16_t mem_offset, uint8_t old_val, uint8_t new_val);
+typedef void (*io_read_handler_t)(uint16_t mem_offset);
+
+
+/* ==== MEMORY SPACE LOCKING ====
+ *
+ * Accessing the register file:
+ * From inside of the CPU the register file can
+ * be accessed without taking any locks.
+ * From outside of the CPU, the CPU must first be
+ * trapped to synchronize the register file.
+ *
+ * Accessing the conventional IO space or the MMIO space:
+ * The io_lock has to be acquired from any context.
+ *
+ * Accessing the SRAM memory space:
+ * From inside of the CPU the SRAM can
+ * be accessed without taking any locks.
+ * From outside of the CPU, the CPU must first be
+ * trapped to synchronize the SRAM.
+ *
+ * Exceptions:
+ * The Stack Pointer is accessed locklessly from inside
+ * of the CPU. From outside of the CPU, the CPU must first
+ * be trapped to synchronize and lock the stack pointer data.
+ */
+
+
+/* Abtract I/O port identifiers. */
+enum avr_io_port {
+ IO_TWBR,
+ IO_TWSR,
+ IO_TWAR,
+ IO_TWDR,
+ IO_ADCL,
+ IO_ADCH,
+ IO_ADCSRA,
+ IO_ADCSRB,
+ IO_ADMUX,
+ IO_ADCSR,
+ IO_UBRRL,
+ IO_UCSRB,
+ IO_UCSRA,
+ IO_UDR,
+ IO_SPCR,
+ IO_SPSR,
+ IO_SPDR,
+ IO_PIND,
+ IO_DDRD,
+ IO_PORTD,
+ IO_PINC,
+ IO_DDRC,
+ IO_PORTC,
+ IO_PINB,
+ IO_DDRB,
+ IO_PORTB,
+ IO_EECR,
+ IO_EEDR,
+ IO_EEARL,
+ IO_EEARH,
+ IO_UCSRC,
+ IO_UBRRH,
+ IO_WDTCR,
+ IO_ASSR,
+ IO_OCR2,
+ IO_TCNT2,
+ IO_TCCR2,
+ IO_ICR1L,
+ IO_ICR1H,
+ IO_OCR1BL,
+ IO_OCR1BH,
+ IO_OCR1AL,
+ IO_OCR1AH,
+ IO_TCNT1L,
+ IO_TCNT1H,
+ IO_TCCR1B,
+ IO_TCCR1A,
+ IO_SFIOR,
+ IO_OSCAL,
+ IO_TCNT0,
+ IO_TCCR0,
+ IO_MCUCSR,
+ IO_MCUCR,
+ IO_TWCR,
+ IO_SPMCR,
+ IO_TIFR,
+ IO_TIFR0,
+ IO_TIFR1,
+ IO_TIFR2,
+ IO_TIMSK,
+ IO_GIFR,
+ IO_GICR,
+ IO_SPL,
+ IO_SPH,
+ IO_SREG,
+ IO_PCIFR,
+ IO_EIFR,
+ IO_EIMSK,
+ IO_GPIOR0,
+ IO_GTCCR,
+ IO_TCCR0A,
+ IO_TCCR0B,
+ IO_OCR0A,
+ IO_OCR0B,
+ IO_GPIOR1,
+ IO_GPIOR2,
+ IO_ACSR,
+ IO_SMCR,
+ IO_MCUSR,
+ IO_SPMCSR,
+ IO_WDRCSR,
+ IO_CLKPR,
+ IO_PRR,
+ IO_PCICR,
+ IO_EICRA,
+ IO_PCMSK0,
+ IO_PCMSK1,
+ IO_PCMSK2,
+ IO_TIMSK0,
+ IO_TIMSK1,
+ IO_TIMSK2,
+ IO_DIDR0,
+ IO_DIDR1,
+ IO_TCCR1C,
+ IO_TCCR2A,
+ IO_TCCR2B,
+ IO_OCR2A,
+ IO_OCR2B,
+ IO_TWAMR,
+ IO_UCSR0A,
+ IO_UCSR0B,
+ IO_UCSR0C,
+ IO_UBRR0L,
+ IO_UBRR0H,
+ IO_UDR0,
+ NR_VIRT_IO_PORTS,
+};
+
+struct avr_memory {
+ /* The whole memory area. */
+ uint8_t *ram;
+ /* Size of the whole memory region. */
+ size_t size;
+ /* Conventional IO space. */
+ uint8_t *io;
+ /* Size of the IO and MMIO space. */
+ size_t iospace_size;
+ /* The offset where the general purpose SRAM starts. */
+ size_t sram_offset;
+
+ /* Lock for the IO and MMIO space. */
+ pthread_spinlock_t io_lock;
+
+ /* The offset of the stack pointer in the IO space. */
+ uint8_t sp_offset;
+
+ /* Array of I/O access handlers. One for each I/O and MMIO address. */
+ io_write_handler_t *io_write_handlers;
+ io_read_handler_t *io_read_handlers;
+
+ /* Map array for mapping abstract IO port IDs to their
+ * physical hardware address. */
+ uint16_t physical_io_map[NR_VIRT_IO_PORTS];
+};
+
+/* Value for the physical_io_map when the port doesn't exist. */
+#define IO_PORT_UNAVAILABLE 0xFFFF
+
+/* IO memory always starts at the 32th byte. */
+#define IO_MEM_OFFSET 32
+
+extern struct avr_memory memory;
+
+
+/* Map a virtual IO cookie to its physical address in the AVR memory space. */
+static inline uint16_t io_virt_to_phys(enum avr_io_port virt)
+{
+ return memory.physical_io_map[virt];
+}
+
+static inline bool mem_offset_in_io_region(uint16_t offset)
+{
+ return ((offset >= IO_MEM_OFFSET) && (offset < memory.sram_offset));
+}
+
+static inline void memory_lock(uint16_t offset, uint8_t *lock_flags)
+{
+ if (mem_offset_in_io_region(offset)) {
+ pthread_spin_lock(&memory.io_lock);
+ *lock_flags = 1;
+ return;
+ }
+ *lock_flags = 0;
+}
+
+static inline void memory_unlock(uint8_t lock_flags)
+{
+ if (lock_flags)
+ pthread_spin_unlock(&memory.io_lock);
+}
+
+
+/* Register a handler for an I/O register access.
+ * Note that the handler will be called from CPU thread context!
+ * That means it will run in concurrency to the main worker process thread.
+ * Use locking. */
+int register_io_write_handler(io_write_handler_t handler, enum avr_io_port virt);
+int register_io_read_handler(io_read_handler_t handler, enum avr_io_port virt);
+
+
+int memory_initialize(void);
+void memory_cleanup(void);
+
+
+
+#endif /* AVREMU_MEMORY_H_ */
diff --git a/emulator/pinout.c b/emulator/pinout.c
new file mode 100644
index 0000000..47499ad
--- /dev/null
+++ b/emulator/pinout.c
@@ -0,0 +1,129 @@
+/*
+ * AVR emulator
+ * Device pinout
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "pinout.h"
+#include "util.h"
+
+
+#undef p
+#undef PIN
+#define p(pinname) PINOUT_##pinname
+#define PIN(_number, ...) \
+ [_number - 1] = { \
+ .number = _number, \
+ .funcs = { __VA_ARGS__ }, \
+ }
+
+static struct avr_hwpin atmega48_88_168_pins[] = {
+ PIN(1, p(PC6), p(RESET), p(PCINT14)),
+ PIN(2, p(PD0), p(RXD), p(PCINT16)),
+ PIN(3, p(PD1), p(TXD), p(PCINT17)),
+ PIN(4, p(PD2), p(INT0), p(PCINT18)),
+ PIN(5, p(PD3), p(INT1), p(OC2B), p(PCINT19)),
+ PIN(6, p(PD4), p(T0), p(XCK), p(PCINT20)),
+ PIN(7, p(VCC)),
+ PIN(8, p(GND)),
+ PIN(9, p(PB6), p(TOSC1), p(XTAL1), p(PCINT6)),
+ PIN(10, p(PB7), p(TOSC2), p(XTAL2), p(PCINT7)),
+ PIN(11, p(PD5), p(T1), p(OC0B), p(PCINT21)),
+ PIN(12, p(PD6), p(AIN0), p(OC0A), p(PCINT22)),
+ PIN(13, p(PD7), p(AIN1), p(PCINT23)),
+ PIN(14, p(PB0), p(ICP1), p(CLKO), p(PCINT0)),
+ PIN(15, p(PB1), p(OC1A), p(PCINT1)),
+ PIN(16, p(PB2), p(SS), p(OC1B), p(PCINT2)),
+ PIN(17, p(PB3), p(MOSI), p(OC2A), p(PCINT3)),
+ PIN(18, p(PB4), p(MISO), p(PCINT4)),
+ PIN(19, p(PB5), p(SCK), p(PCINT5)),
+ PIN(20, p(AVCC)),
+ PIN(21, p(AREF)),
+ PIN(22, p(GND)),
+ PIN(23, p(PC0), p(ADC0), p(PCINT8)),
+ PIN(24, p(PC1), p(ADC1), p(PCINT9)),
+ PIN(25, p(PC2), p(ADC2), p(PCINT10)),
+ PIN(26, p(PC3), p(ADC3), p(PCINT11)),
+ PIN(27, p(PC4), p(ADC4), p(SDA), p(PCINT12)),
+ PIN(28, p(PC5), p(ADC5), p(SCL), p(PCINT13)),
+};
+
+struct avr_device_pinout atmega48_88_168_pinout = {
+ .pins = atmega48_88_168_pins,
+ .nr_pins = ARRAY_SIZE(atmega48_88_168_pins),
+};
+
+
+static struct avr_hwpin atmega8_pins[] = {
+ PIN(1, p(PC6), p(RESET)),
+ PIN(2, p(PD0), p(RXD)),
+ PIN(3, p(PD1), p(TXD)),
+ PIN(4, p(PD2), p(INT0)),
+ PIN(5, p(PD3), p(INT1)),
+ PIN(6, p(PD4), p(T0)),
+ PIN(7, p(VCC)),
+ PIN(8, p(GND)),
+ PIN(9, p(PB6), p(TOSC1), p(XTAL1)),
+ PIN(10, p(PB7), p(TOSC2), p(XTAL2)),
+ PIN(11, p(PD5), p(T1)),
+ PIN(12, p(PD6), p(AIN0)),
+ PIN(13, p(PD7), p(AIN1)),
+ PIN(14, p(PB0), p(ICP1)),
+ PIN(15, p(PB1), p(OC1A)),
+ PIN(16, p(PB2), p(SS), p(OC1B)),
+ PIN(17, p(PB3), p(MOSI), p(OC2)),
+ PIN(18, p(PB4), p(MISO)),
+ PIN(19, p(PB5), p(SCK)),
+ PIN(20, p(AVCC)),
+ PIN(21, p(AREF)),
+ PIN(22, p(GND)),
+ PIN(23, p(PC0), p(ADC0)),
+ PIN(24, p(PC1), p(ADC1)),
+ PIN(25, p(PC2), p(ADC2)),
+ PIN(26, p(PC3), p(ADC3)),
+ PIN(27, p(PC4), p(ADC4), p(SDA)),
+ PIN(28, p(PC5), p(ADC5), p(SCL)),
+};
+
+struct avr_device_pinout atmega8_pinout = {
+ .pins = atmega8_pins,
+ .nr_pins = ARRAY_SIZE(atmega8_pins),
+};
+
+#undef p
+#undef PIN
+
+int avr_get_pin_number(struct avr_device_pinout *pinout,
+ enum avr_pinout_function func)
+{
+ struct avr_hwpin *pin;
+ enum avr_pinout_function f;
+ size_t i, j;
+
+ for (i = 0; i < pinout->nr_pins; i++) {
+ pin = &(pinout->pins[i]);
+
+ for (j = 0; j < AVR_MAX_PIN_FUNCS; i++) {
+ f = pin->funcs[j];
+ if (f == PINOUT_NC)
+ break;
+ if (f == func)
+ return i + 1;
+ }
+ }
+
+ return -1;
+}
+
+struct avr_device_pinout_handlers active_pinout_handlers;
diff --git a/emulator/pinout.h b/emulator/pinout.h
new file mode 100644
index 0000000..87c9cf1
--- /dev/null
+++ b/emulator/pinout.h
@@ -0,0 +1,142 @@
+#ifndef AVREMU_PINOUT_H_
+#define AVREMU_PINOUT_H_
+
+#include <stdlib.h>
+
+
+/* Increase these constants as needed. */
+
+/* Maximum number of functions per pin. */
+#define AVR_MAX_PIN_FUNCS 10
+/* Maximum number of physical pins per device */
+#define AVR_MAX_NR_PINS 28
+
+
+
+/* The function of a physical pin on the device. */
+enum avr_pinout_function {
+ PINOUT_NC = 0, /* Not connected */
+ PINOUT_VCC,
+ PINOUT_GND,
+ PINOUT_AVCC,
+ PINOUT_AREF,
+ PINOUT_PB0,
+ PINOUT_PB1,
+ PINOUT_PB2,
+ PINOUT_PB3,
+ PINOUT_PB4,
+ PINOUT_PB5,
+ PINOUT_PB6,
+ PINOUT_PB7,
+ PINOUT_PC0,
+ PINOUT_PC1,
+ PINOUT_PC2,
+ PINOUT_PC3,
+ PINOUT_PC4,
+ PINOUT_PC5,
+ PINOUT_PC6,
+ PINOUT_PD0,
+ PINOUT_PD1,
+ PINOUT_PD2,
+ PINOUT_PD3,
+ PINOUT_PD4,
+ PINOUT_PD5,
+ PINOUT_PD6,
+ PINOUT_PD7,
+ PINOUT_RESET,
+ PINOUT_RXD,
+ PINOUT_TXD,
+ PINOUT_INT0,
+ PINOUT_INT1,
+ PINOUT_T0,
+ PINOUT_TOSC1,
+ PINOUT_TOSC2,
+ PINOUT_T1,
+ PINOUT_AIN0,
+ PINOUT_AIN1,
+ PINOUT_ICP1,
+ PINOUT_ADC5,
+ PINOUT_ADC4,
+ PINOUT_ADC3,
+ PINOUT_ADC2,
+ PINOUT_ADC1,
+ PINOUT_ADC0,
+ PINOUT_SCK,
+ PINOUT_MISO,
+ PINOUT_MOSI,
+ PINOUT_SS,
+ PINOUT_OC1A,
+ PINOUT_PCINT0,
+ PINOUT_PCINT1,
+ PINOUT_PCINT2,
+ PINOUT_PCINT3,
+ PINOUT_PCINT4,
+ PINOUT_PCINT5,
+ PINOUT_PCINT6,
+ PINOUT_PCINT7,
+ PINOUT_PCINT8,
+ PINOUT_PCINT9,
+ PINOUT_PCINT10,
+ PINOUT_PCINT11,
+ PINOUT_PCINT12,
+ PINOUT_PCINT13,
+ PINOUT_PCINT14,
+ PINOUT_PCINT16,
+ PINOUT_PCINT17,
+ PINOUT_PCINT18,
+ PINOUT_PCINT19,
+ PINOUT_PCINT20,
+ PINOUT_PCINT21,
+ PINOUT_PCINT22,
+ PINOUT_PCINT23,
+ PINOUT_OC2B,
+ PINOUT_XCK,
+ PINOUT_XTAL1,
+ PINOUT_XTAL2,
+ PINOUT_OC0B,
+ PINOUT_OC0A,
+ PINOUT_CLKO,
+ PINOUT_SCL,
+ PINOUT_SDA,
+ PINOUT_OC2A,
+ PINOUT_OC1B,
+ PINOUT_OC2,
+};
+
+/* Data structure of a hardware pin. */
+struct avr_hwpin {
+ /* The assigned pin number. */
+ unsigned int number;
+ /* The logical functions assigned to this pin. */
+ enum avr_pinout_function funcs[AVR_MAX_PIN_FUNCS];
+};
+
+/* Pinout definitions for a specific device. */
+struct avr_device_pinout {
+ /* Array of pins. */
+ struct avr_hwpin *pins;
+ /* Number of pins on this device. */
+ size_t nr_pins;
+};
+
+extern struct avr_device_pinout atmega48_88_168_pinout;
+extern struct avr_device_pinout atmega8_pinout;
+
+int avr_get_pin_number(struct avr_device_pinout *pinout,
+ enum avr_pinout_function func);
+
+
+typedef unsigned int (*pin_query_handler_t)(struct avr_hwpin *pin);
+typedef void (*pin_change_handler_t)(struct avr_hwpin *pin, unsigned int state);
+
+/* Dynamic handlers for a physical device pin. */
+struct avr_device_pinout_handlers {
+ /* Handlers to query the state of a pin from inside of the device. */
+ pin_query_handler_t query_handlers[AVR_MAX_NR_PINS];
+ /* Handlers to set the state of a pin from inside of the device. */
+ pin_change_handler_t change_handlers[AVR_MAX_NR_PINS];
+};
+
+extern struct avr_device_pinout_handlers active_pinout_handlers;
+
+#endif /* AVREMU_PINOUT_H_ */
diff --git a/emulator/python.c b/emulator/python.c
new file mode 100644
index 0000000..84b8bd9
--- /dev/null
+++ b/emulator/python.c
@@ -0,0 +1,206 @@
+/*
+ * AVR emulator
+ * Python interface
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include <Python.h>
+#include <errno.h>
+
+#include "subprocess.h"
+#include "util.h"
+
+
+#define MODNAME "pyavr8emu"
+
+
+static PyObject *pyavr_except;
+
+static void * raise_errno_exception(int err)
+{
+ int old_errno;
+
+ if (err < 0)
+ err = -err;
+ old_errno = errno;
+ errno = err;
+ PyErr_SetFromErrno(pyavr_except);
+ errno = old_errno;
+
+ return NULL;
+}
+
+/*****************************************************************************
+ * Instance-Object definitions *
+ *****************************************************************************/
+
+struct avremu_instance {
+ PyObject_HEAD
+ /* The host part of the emulator instance. */
+ struct avremu_host_instance host;
+};
+
+static PyObject * method_instance_load_flash(PyObject *self, PyObject *args)
+{
+ struct avremu_instance *in = (struct avremu_instance *)self;
+ struct avrmsg_to_device msg;
+ int err;
+ const char *image;
+ int image_size;
+
+ if (!PyArg_ParseTuple(args, "s#", &image, &image_size))
+ return NULL;
+
+ msg.msg = DEVMSG_LOADFLASH;
+ msg.size = image_size;
+ err = sendmsg_to_device(&in->host, &msg);
+ if (err)
+ return raise_errno_exception(err);
+ err = ipc_payload_send(&in->host.msg_to_device, image, image_size);
+ if (err)
+ return raise_errno_exception(err);
+
+ return Py_None;
+}
+
+static PyObject * method_instance_reset(PyObject *self, PyObject *args)
+{
+ struct avremu_instance *in = (struct avremu_instance *)self;
+ int err;
+
+ err = sendnotify_to_device(&in->host, DEVMSG_RESET);
+ if (err)
+ return raise_errno_exception(err);
+
+ return Py_None;
+}
+
+static PyObject * method_instance_poll_status_msg(PyObject *self, PyObject *args)
+{
+ struct avremu_instance *in = (struct avremu_instance *)self;
+ int err;
+ char buf[1024];
+
+ err = ipc_raw_line_poll(&in->host.status_from_device,
+ buf, sizeof(buf));
+ if (err == 0)
+ return Py_None;
+ if (err < 0)
+ return raise_errno_exception(err);
+
+ return PyString_FromString(buf);
+}
+
+static PyMethodDef avremu_instance_methods[] = {
+ { "loadFlash", method_instance_load_flash, METH_VARARGS,
+ "Load a binary flash image" },
+ { "reset", method_instance_reset, METH_NOARGS,
+ "Reset the emulated microcontroller" },
+ { "pollStatusMsg", method_instance_poll_status_msg, METH_NOARGS,
+ "Poll a status message line" },
+ { NULL, },
+};
+
+static PyObject * avremu_instance_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ struct avremu_instance *in;
+
+ in = (struct avremu_instance *)type->tp_alloc(type, 0);
+ if (!in)
+ return NULL;
+
+ return (PyObject *)in;
+}
+
+static int avremu_instance_init(PyObject *obj, PyObject *args, PyObject *kwds)
+{
+ struct avremu_instance *in = (struct avremu_instance *)obj;
+ int err, arch;
+ static char *kwlist[] = {"arch", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &arch))
+ return -1;
+
+ err = avremu_instance_create(&in->host, arch);
+ if (err) {
+ raise_errno_exception(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void avremu_instance_free(PyObject *obj)
+{
+ struct avremu_instance *in = (struct avremu_instance *)obj;
+
+ avremu_instance_destroy(&in->host);
+ obj->ob_type->tp_free(obj);
+}
+
+static PyTypeObject avremu_instance_type = {
+ PyObject_HEAD_INIT(NULL)
+ .tp_name = MODNAME ".Instance",
+ .tp_basicsize = sizeof(struct avremu_instance),
+ .tp_methods = avremu_instance_methods,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_new = avremu_instance_new,
+ .tp_init = avremu_instance_init,
+ .tp_dealloc = avremu_instance_free,
+ .tp_doc = "An emulator instance object",
+};
+
+/*****************************************************************************
+ * Module API functions *
+ *****************************************************************************/
+
+static PyMethodDef pyavr_methods[] = {
+ { NULL, },
+};
+
+/*****************************************************************************
+ * Initialization *
+ *****************************************************************************/
+
+#define def_const(m, x) PyModule_AddIntConstant((m), stringify(x), (x))
+
+PyMODINIT_FUNC initpyavr8emu(void)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&avremu_instance_type) < 0)
+ return;
+
+ pyavr_except = PyErr_NewException(MODNAME ".AvrException",
+ PyExc_Exception, NULL);
+ if (!pyavr_except)
+ return;
+ Py_INCREF(pyavr_except);
+
+ m = Py_InitModule(MODNAME, pyavr_methods);
+ if (!m) {
+ Py_DECREF(pyavr_except);
+ return;
+ }
+
+ /* Define constants */
+ def_const(m, AVR_ATMEGA8);
+ def_const(m, AVR_ATMEGA168);
+
+ /* Build objects */
+ PyModule_AddObject(m, "AvrException", pyavr_except);
+
+ Py_INCREF(&avremu_instance_type);
+ PyModule_AddObject(m, "Instance", (PyObject *)&avremu_instance_type);
+}
diff --git a/emulator/subprocess.c b/emulator/subprocess.c
new file mode 100644
index 0000000..969ae1b
--- /dev/null
+++ b/emulator/subprocess.c
@@ -0,0 +1,167 @@
+/*
+ * AVR emulator
+ * Emulator subprocess management
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "subprocess.h"
+#include "devprocess.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+
+static int fork_device_instance(struct avremu_host_instance *host)
+{
+ struct avremu_device_instance *dev;
+ int err;
+ pid_t pid;
+ struct avrmsg_to_host hostmsg;
+
+ dev = avr_malloc(sizeof(*dev));
+ if (!dev)
+ return -ENOMEM;
+ dev->type = host->type;
+ err = ipc_pipe_create(&host->msg_to_device, &dev->msg_from_host);
+ if (err) {
+ err = -ENOMEM;
+ goto err_free_dev;
+ }
+ err = ipc_pipe_create(&dev->msg_to_host, &host->msg_from_device);
+ if (err) {
+ err = -ENOMEM;
+ goto err_destroy1;
+ }
+ err = ipc_pipe_create(&dev->status_to_host, &host->status_from_device);
+ if (err) {
+ err = -ENOMEM;
+ goto err_destroy2;
+ }
+
+ pid = fork();
+ if (pid == (pid_t)0) {
+ /* DEVICE process */
+ ipc_pipe_destroy_tx(&host->msg_to_device);
+ ipc_pipe_destroy_rx(&host->msg_from_device);
+ ipc_pipe_destroy_rx(&host->status_from_device);
+ host = NULL;
+
+ /* Map stderr and stdout to the status pipe.
+ * Also enable line buffering. */
+ ipc_dup2(&dev->status_to_host, STDERR_FILENO);
+ ipc_dup2(&dev->status_to_host, STDOUT_FILENO);
+ setvbuf(stdout, NULL, _IOLBF, 0);
+ setvbuf(stderr, NULL, _IOLBF, 0);
+
+ /* Run the device process. */
+ err = avr_device_process_run(dev);
+ sendnotify_to_host(dev, HOSTMSG_EXIT);
+ exit(err >= 0 ? err : -err);
+ } else if (pid < (pid_t)0) {
+ /* Failure */
+ err = -ENOMEM;
+ goto err_destroy3;
+ } else {
+ /* HOST process */
+ ipc_pipe_destroy_tx(&dev->msg_to_host);
+ ipc_pipe_destroy_rx(&dev->msg_from_host);
+ ipc_pipe_destroy_tx(&dev->status_to_host);
+ free(dev);
+ dev = NULL;
+
+ host->device_pid = pid;
+ /* Wait for the device to initialize itself. */
+ while (1) {
+ err = pollmsg_from_device(host, &hostmsg);
+ if (err < 0) {
+ err = -EIO;
+ goto host_error;
+ }
+ if (err > 0) {
+ if (hostmsg.msg == HOSTMSG_READY)
+ break;
+ if (hostmsg.msg == HOSTMSG_EXIT) {
+ if (waitpid(pid, &err, 0) != pid)
+ err = -ESRCH;
+ else
+ err = WEXITSTATUS(err);
+ goto host_error;
+ }
+ }
+ }
+ host->device_running = 1;
+ return 0;
+
+ host_error:
+ ipc_pipe_destroy_tx(&host->msg_to_device);
+ ipc_pipe_destroy_rx(&host->msg_from_device);
+ ipc_pipe_destroy_rx(&host->status_from_device);
+ return err;
+ }
+
+ return 0;
+
+err_destroy3:
+ ipc_pipe_destroy(&dev->status_to_host, &host->status_from_device);
+err_destroy2:
+ ipc_pipe_destroy(&dev->msg_to_host, &host->msg_from_device);
+err_destroy1:
+ ipc_pipe_destroy(&host->msg_to_device, &dev->msg_from_host);
+err_free_dev:
+ free(dev);
+
+ return err;
+}
+
+static void kill_device_instance(struct avremu_host_instance *host)
+{
+ int err;
+
+printf("KILLING INSTANCE\n");
+ if (!host->device_running)
+ return;
+printf("SEND TERM\n");
+ err = kill(host->device_pid, SIGTERM);
+ if (err) {
+ perror("Kill device process");
+ return;
+ }
+printf("WAITING\n");
+ waitpid(host->device_pid, NULL, 0);
+ host->device_running = 0;
+printf("WAITING DONE.\n");
+}
+
+int avremu_instance_create(struct avremu_host_instance *host,
+ enum avr_setup_type type)
+{
+ int err;
+
+ host->type = type;
+ err = fork_device_instance(host);
+
+ return err;
+}
+
+void avremu_instance_destroy(struct avremu_host_instance *host)
+{
+ kill_device_instance(host);
+}
diff --git a/emulator/subprocess.h b/emulator/subprocess.h
new file mode 100644
index 0000000..04791ef
--- /dev/null
+++ b/emulator/subprocess.h
@@ -0,0 +1,116 @@
+#ifndef AVREMU_SUBPROCESS_H_
+#define AVREMU_SUBPROCESS_H_
+
+#include "hardware.h"
+#include "ipc.h"
+#include "util.h"
+
+#include <unistd.h>
+
+
+/* Instance of an emulated device on the controlling host process. */
+struct avremu_host_instance {
+ /* Type of microcontroller we are emulating. */
+ enum avr_setup_type type;
+ /* PID of the device process. */
+ pid_t device_pid;
+ /* Is the device subprocess running? */
+ bool device_running;
+
+ struct ipc_tx msg_to_device;
+ struct ipc_rx msg_from_device;
+ struct ipc_rx status_from_device;
+};
+
+/* Instance of an emulated device.
+ * This is the process running the actual code. */
+struct avremu_device_instance {
+ /* Type of microcontroller we are emulating. */
+ enum avr_setup_type type;
+
+ struct ipc_tx msg_to_host;
+ struct ipc_rx msg_from_host;
+ struct ipc_tx status_to_host;
+};
+
+int avremu_instance_create(struct avremu_host_instance *host,
+ enum avr_setup_type type);
+void avremu_instance_destroy(struct avremu_host_instance *host);
+
+
+enum avrmsg_to_device_type {
+ DEVMSG_LOADFLASH, /* Load a binary flash image. */
+ DEVMSG_RESET, /* Reset the microcontroller. */
+};
+
+struct avrmsg_to_device {
+ enum avrmsg_to_device_type msg;
+ union {
+ uint16_t size;
+ };
+};
+
+static inline
+int sendmsg_to_device(struct avremu_host_instance *host,
+ const struct avrmsg_to_device *msg)
+{
+ return ipc_message_send(&host->msg_to_device,
+ msg, sizeof(*msg), 1);
+}
+
+static inline
+int sendnotify_to_device(struct avremu_host_instance *host,
+ enum avrmsg_to_device_type msg)
+{
+ struct avrmsg_to_device m = {
+ .msg = msg,
+ };
+ return sendmsg_to_device(host, &m);
+}
+
+static inline
+int pollmsg_from_host(struct avremu_device_instance *dev,
+ struct avrmsg_to_device *msg)
+{
+ return ipc_message_poll(&dev->msg_from_host,
+ msg, sizeof(*msg));
+}
+
+
+enum avrmsg_to_host_type {
+ HOSTMSG_READY, /* Initialization complete. */
+ HOSTMSG_EXIT, /* The device process exited. */
+};
+
+struct avrmsg_to_host {
+ enum avrmsg_to_host_type msg;
+};
+
+static inline
+int sendmsg_to_host(struct avremu_device_instance *dev,
+ const struct avrmsg_to_host *msg,
+ bool read_result)
+{
+ return ipc_message_send(&dev->msg_to_host,
+ msg, sizeof(*msg), read_result);
+}
+
+static inline
+int sendnotify_to_host(struct avremu_device_instance *dev,
+ enum avrmsg_to_host_type msg)
+{
+ struct avrmsg_to_host m = {
+ .msg = msg,
+ };
+ return sendmsg_to_host(dev, &m, 0);
+}
+
+static inline
+int pollmsg_from_device(struct avremu_host_instance *host,
+ struct avrmsg_to_host *msg)
+{
+ return ipc_message_poll(&host->msg_from_device,
+ msg, sizeof(*msg));
+}
+
+#endif /* AVREMU_SUBPROCESS_H_ */
diff --git a/emulator/util.c b/emulator/util.c
new file mode 100644
index 0000000..69c119d
--- /dev/null
+++ b/emulator/util.c
@@ -0,0 +1,35 @@
+/*
+ * AVR emulator
+ * Utility functions
+ *
+ * Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * 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.
+ */
+
+#include "util.h"
+
+#include <stdio.h>
+#include <string.h>
+
+
+void * avr_malloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p)
+ memset(p, 0, size);
+ else
+ fprintf(stderr, "Out of memory\n");
+
+ return p;
+}
diff --git a/emulator/util.h b/emulator/util.h
new file mode 100644
index 0000000..321f46a
--- /dev/null
+++ b/emulator/util.h
@@ -0,0 +1,74 @@
+#ifndef AVREMU_UTIL_H_
+#define AVREMU_UTIL_H_
+
+#ifndef __GNUC__
+# error "Not using the GNU GCC compiler."
+#endif
+
+#include "list.h"
+#include <stdint.h>
+#include <stdlib.h>
+
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#ifdef RUNTIME_SANITY_CHECKS
+# define CHECK(x) unlikely(x)
+#else
+# define CHECK(x) 0
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+#define is_const_value(x) __builtin_constant_p(x)
+
+/* Force reload a variable from memory. */
+#define reload_var(x) (*((volatile typeof(x) *)&(x)))
+
+/* Break the linkage of the binary, if the constant condition is true.
+ * This is useful for compiletime checks of constant values. */
+#define break_linkage_on(constant_condition, reason) do { \
+ if (constant_condition) { \
+ extern void __avremu_bug__##reason(void); \
+ __avremu_bug__##reason(); \
+ } \
+} while (0)
+
+#define __stringify(x) #x
+#define stringify(x) __stringify(x)
+
+
+typedef uint16_t le16_t;
+typedef uint32_t le32_t;
+typedef _Bool bool;
+
+
+static inline uint16_t swap16(uint16_t x)
+{
+ return (x >> 8) | (x << 8);
+}
+
+static inline uint16_t le16_to_cpu(le16_t x)
+{
+#ifdef BIG_ENDIAN_HOST
+ return swap16((uint16_t)x);
+#else
+ return (uint16_t)x;
+#endif
+}
+
+static inline le16_t cpu_to_le16(uint16_t x)
+{
+#ifdef BIG_ENDIAN_HOST
+ return (le16_t)swap16(x);
+#else
+ return (le16_t)x;
+#endif
+}
+
+
+void * avr_malloc(size_t size);
+
+
+#endif /* AVREMU_UTIL_H_ */
diff --git a/gui/avr8emu b/gui/avr8emu
new file mode 100755
index 0000000..cae4ebc
--- /dev/null
+++ b/gui/avr8emu
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+#
+# AVR-8 emulator GUI
+#
+# Copyright (C) 2007 Michael Buesch <mb@bu3sch.de>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# 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.
+
+AVR8EMU_VERSION = "001"
+
+import tempfile
+import sys
+import os
+import pyavr8emu as emu
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+
+#FIXME
+inst = emu.Instance(0)
+
+
+class EmulatorStatus(QTextBrowser):
+ def __init__(self, parent=None):
+ QTextBrowser.__init__(self, parent)
+ self.setFontFamily("Courier")
+ self.setFontPointSize(10)
+ self.setLineWrapMode(QTextEdit.NoWrap)
+
+ self.timer = QTimer()
+ self.timer.setInterval(300)
+ self.timer.setSingleShot(False)
+ self.connect(self.timer, SIGNAL("timeout()"), self.pollMsgs)
+ self.timer.start()
+
+ def pollMsgs(self):
+ while True:
+ msg = inst.pollStatusMsg()
+ if not msg:
+ return
+ self.append(msg[:-1])
+
+class CodeEditor(QTextEdit):
+ def __init__(self, parent):
+ QTextEdit.__init__(self, parent)
+ self.setFontFamily("Courier")
+ self.setFontPointSize(12)
+ self.setLineWrapMode(QTextEdit.NoWrap)
+
+ def statusBar(self):
+ return self.parent().statusBar()
+
+ def loadFile(self, filename):
+ fd = QFile(filename)
+ if not fd.open(QIODevice.ReadOnly):
+ self.statusBar().showMessage("Could not open file %s" % filename)
+ return
+ data = fd.readAll()
+ fd.close()
+ if not data:
+ self.statusBar().showMessage("Could not read file %s" % filename)
+ return
+ self.setText(str(data))
+
+class CentralWidget(QWidget):
+ def __init__(self, mainwnd):
+ QWidget.__init__(self, mainwnd)
+
+ self.mainwnd = mainwnd
+ l = QVBoxLayout(self)
+ sp = QSplitter(self)
+ sp.setOrientation(Qt.Vertical)
+ self.editor = CodeEditor(self)
+ sp.addWidget(self.editor)
+ self.emuStatus = EmulatorStatus(self)
+ sp.addWidget(self.emuStatus)
+ l.addWidget(sp)
+
+ def statusBar(self):
+ return self.parent().statusBar()
+
+ def loadFile(self):
+ fn = QFileDialog.getOpenFileName(self, "Open assembly file")
+ if not fn:
+ return
+ inst.loadFlash(file(fn).read())
+#FIXME self.editor.loadFile(fn)
+
+ def __assemble(self, assembly):
+ #FIXME not portable
+ asmfile = tempfile.NamedTemporaryFile()
+ asmfile.write(assembly)
+ asmfile.flush()
+ os.system("avra %s" % asmfile.name)
+ hexfile = asmfile.name + ".hex"
+ binfile = asmfile.name + ".bin"
+ os.system("avr-objcopy -I ihex -O binary %s %s" % (hexfile, binfile))
+ fd = file(binfile)
+ bindata = fd.read()
+ fd.close()
+ os.system("rm %s.*" % asmfile.name)
+ print "Got %d bytes binary" % len(bindata)
+ return bindata
+
+ def reset(self):
+ inst.reset()
+
+class StatusBar(QStatusBar):
+ def showMessage(self, msg):
+ QStatusBar.showMessage(self, msg, 10000)
+
+class MainWindow(QMainWindow):
+ def __init__(self, parent=None):
+ QMainWindow.__init__(self, parent)
+ self.setWindowTitle("Fast! Atmel AVR 8-bit emulator")
+
+ self.setCentralWidget(CentralWidget(self))
+ self.setStatusBar(StatusBar(self))
+
+ mb = QMenuBar(self)
+ filemen = QMenu("&File", mb)
+ filemen.addAction("&Load...", self.centralWidget().loadFile)
+ filemen.addAction("&Reset", self.centralWidget().reset)
+ filemen.addSeparator()
+ filemen.addAction("&Exit", self.close)
+ mb.addMenu(filemen)
+ mb.addSeparator()
+ helpmen = QMenu("&Help", mb)
+ helpmen.addAction("&About", self.about)
+ mb.addMenu(helpmen)
+
+ self.setMenuBar(mb)
+
+ def about(self):
+ QMessageBox.information(self, "About",
+ "Fast! Atmel AVR 8-bit emulator\n"
+ "Version %s\n"
+ "Copyright (c) 2007 Michael Buesch"
+ % AVR8EMU_VERSION)
+
+def main():
+ app = QApplication(sys.argv)
+ mainwnd = MainWindow()
+ mainwnd.show()
+ sys.exit(app.exec_())
+
+if __name__ == "__main__":
+ main()
bues.ch cgit interface