diff --git a/replay/Makefile.objs b/replay/Makefile.objs
new file mode 100644
index 0000000..926d50e
--- /dev/null
+++ b/replay/Makefile.objs
@@ -0,0 +1 @@
+obj-y += replay.o replay-events.o replay-internal.o replay-usb.o replay-icount.o replay-audio.o
replay-char.o replay-debug.o replay-input.o replay-net.o
diff --git a/replay/replay-audio.c b/replay/replay-audio.c
new file mode 100644
index 0000000..9814a25
--- /dev/null
+++ b/replay/replay-audio.c
@@ -0,0 +1,238 @@
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "replay.h"
+#include "replay-internal.h"
+#include "qemu/log.h"
+
+/* Sound card state */
+typedef struct
+{
+    void *instance;
+    const int event_id;
+    //void (const *callback)(void *, WAVEHDR *);
+#ifdef _WIN32
+    WAVEHDR *queue;
+#endif
+    /*! Maximum size of the queue */
+    int size;
+    /*! Current size of the queue */
+    sig_atomic_t cur_size;
+    unsigned int head, tail;
+} SoundQueue;
+
+
+static SoundQueue sound_in = {
+        .event_id = EVENT_SOUND_IN
+    },
+    sound_out = {
+        .event_id = EVENT_SOUND_OUT,
+        //.callback = winwave_callback_out_impl
+    };
+
+#ifdef _WIN32
+/*! Spinlock for sound events processing. */
+static spinlock_t sound_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/*****************************************************************************
+ *  Sound queue functions                                                    *
+ *****************************************************************************/
+
+/* callback functions */
+#ifdef _WIN32
+void winwave_callback_out_impl(void *dwInstance, WAVEHDR *h);
+void winwave_callback_in_impl(void *dwInstance, WAVEHDR *h);
+
+void replay_init_sound_in(void *instance, WAVEHDR *hdrs, int sz)
+{
+    sound_in.instance = instance;
+    sound_in.queue = hdrs;
+    sound_in.size = sz;
+    sound_in.head = 0;
+    sound_in.tail = 0;
+    sound_in.cur_size = 0;
+}
+
+void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz)
+{
+    sound_out.instance = instance;
+    sound_out.queue = hdrs;
+    sound_out.size = sz;
+    sound_out.head = 0;
+    sound_out.tail = 0;
+    sound_out.cur_size = 0;
+}
+
+static int sound_queue_add(SoundQueue *q, WAVEHDR *hdr)
+{
+    if (q->queue + q->tail != hdr)
+    {
+        // state was loaded and we need to reset the queue
+        if (q->cur_size == 0)
+        {
+            q->head = q->tail = hdr - q->queue;
+        }
+        else
+        {
+            fprintf(stderr, "Replay: Sound queue error\n");
+            exit(1);
+        }
+    }
+
+    if (q->cur_size == q->size)
+    {
+        if (replay_mode == REPLAY_PLAY)
+        {
+            return 1;
+        }
+
+        fprintf(stderr, "Replay: Sound queue overflow\n");
+        exit(1);
+    }
+
+    q->tail = (q->tail + 1) % q->size;
+    ++q->cur_size;
+
+    return 0;
+}
+
+void replay_save_sound_out(void)
+{
+    spin_lock(&sound_lock);
+    while (sound_out.cur_size != 0)
+    {
+        //printf("Saving from output queue %x\n", &sound_out.queue[sound_out.head]);
+        // put the message ID
+        replay_put_event(sound_out.event_id);
+        // save the buffer size
+        replay_put_dword(sound_out.queue[sound_out.head].dwBytesRecorded);
+        // perform winwave-specific actions
+        winwave_callback_out_impl(sound_out.instance, &sound_out.queue[sound_out.head]);
+        // goto the next buffer
+        sound_out.head = (sound_out.head + 1) % sound_out.size;
+        /* TODO!!! Maybe need some kind of lock here in SAVE mode ?? */
+        --sound_out.cur_size;
+    }
+    spin_unlock(&sound_lock);
+}
+
+
+void replay_save_sound_in(void)
+{
+    spin_lock(&sound_lock);
+    while (sound_in.cur_size != 0)
+    {
+        //printf("Saving from input queue %x\n", &sound_in.queue[sound_in.head]);
+
+        // put the message ID
+        replay_put_event(sound_in.event_id);
+        // save the buffer
+        replay_put_array(sound_in.queue[sound_in.head].lpData,
+            sound_in.queue[sound_in.head].dwBytesRecorded);
+        // perform winwave-specific actions
+        winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]);
+        // goto the next buffer
+        sound_in.head = (sound_in.head + 1) % sound_in.size;
+        /* TODO!!! Maybe need some kind of lock here in SAVE mode ?? */
+        --sound_in.cur_size;
+    }
+    spin_unlock(&sound_lock);
+}
+
+
+void replay_read_sound_out(void)
+{
+    if (sound_out.cur_size == 0)
+    {
+        fprintf(stderr, "Replay: Sound queue underflow\n");
+        exit(1);
+    }
+
+    // get the buffer size
+    sound_out.queue[sound_out.head].dwBytesRecorded = replay_get_dword();
+
+    replay_check_error();
+    replay_has_unread_data = 0;
+
+    // perform winwave-specific actions
+    winwave_callback_out_impl(sound_out.instance, &sound_out.queue[sound_out.head]);
+    sound_out.head = (sound_out.head + 1) % sound_out.size;
+    --sound_out.cur_size;
+}
+
+void replay_read_sound_in(void)
+{
+    if (sound_in.cur_size == 0)
+    {
+        fprintf(stderr, "Replay: Sound queue underflow\n");
+        exit(1);
+    }
+
+    // get the buffer size
+    size_t size;
+    replay_get_array(sound_in.queue[sound_in.head].lpData, &size);
+    sound_in.queue[sound_in.head].dwBytesRecorded = (unsigned int)size;
+
+    replay_check_error();
+    replay_has_unread_data = 0;
+
+    // perform winwave-specific actions
+    winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]);
+    sound_in.head = (sound_in.head + 1) % sound_in.size;
+    --sound_in.cur_size;
+}
+
+
+void replay_sound_in_event(WAVEHDR *hdr)
+{
+    spin_lock(&sound_lock);
+    if (sound_queue_add(&sound_in, hdr))
+    {
+        fprintf(stderr, "Replay: Input sound buffer overflow\n");
+        exit(1);
+    }
+    spin_unlock(&sound_lock);
+}
+
+
+
+int replay_sound_out_event(WAVEHDR *hdr)
+{
+    spin_lock(&sound_lock);
+    int result = sound_queue_add(&sound_out, hdr);
+    spin_unlock(&sound_lock);
+
+    return result;
+}
+#endif
+
+bool replay_has_sound_events(void)
+{
+    return sound_in.cur_size || sound_out.cur_size;
+}
+
+void replay_sound_flush_queue(void)
+{
+#ifdef _WIN32
+    spin_lock(&sound_lock);
+    while (sound_out.cur_size != 0)
+    {
+        // perform winwave-specific actions
+        winwave_callback_out_impl(sound_out.instance, &sound_out.queue[sound_out.head]);
+        // goto the next buffer
+        sound_out.head = (sound_out.head + 1) % sound_out.size;
+        /* TODO!!! Maybe need some kind of lock here in SAVE mode ?? */
+        --sound_out.cur_size;
+    }
+    while (sound_in.cur_size != 0)
+    {
+        // perform winwave-specific actions
+        winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]);
+        // goto the next buffer
+        sound_in.head = (sound_in.head + 1) % sound_in.size;
+        --sound_in.cur_size;
+    }
+    spin_unlock(&sound_lock);
+#endif  
+}
+
diff --git a/replay/replay-char.c b/replay/replay-char.c
new file mode 100644
index 0000000..99c4934
--- /dev/null
+++ b/replay/replay-char.c
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "replay.h"
+#include "replay-internal.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/char.h"
+
+#include "qemu/log.h"
+
+#define MAX_CHAR_DRIVERS MAX_SERIAL_PORTS
+/* Char drivers that generate qemu_chr_be_write events
+   that should be saved into the log. */
+static CharDriverState *char_drivers[MAX_CHAR_DRIVERS];
+
+/* Char event attributes. */
+typedef struct CharEvent
+{
+    int id;
+    uint8_t *buf;
+    size_t len;
+} CharEvent;
+
+static int find_char_driver(CharDriverState *chr)
+{
+    int i = 0;
+    while (i < MAX_CHAR_DRIVERS && char_drivers[i] != chr)
+        ++i;
+
+    return i >= MAX_CHAR_DRIVERS ? -1 : i;
+}
+
+
+void replay_register_char_driver(CharDriverState *chr)
+{
+    chr->replay = true;
+    int i = find_char_driver(NULL);
+
+    if (i < 0)
+    {
+        fprintf(stderr, "Replay: cannot register char driver\n");
+        exit(1);
+    }
+    else
+    {
+        
+        char_drivers[i] = chr;
+    }
+}
+
+void replay_delayed_register_char_driver(void)
+{
+    int i;
+    for (i = 0; i < MAX_SERIAL_PORTS; i++)
+        if (serial_hds[i] != NULL)
+        {
+            serial_hds[i]->replay = true;
+            replay_register_char_driver(serial_hds[i]);
+        }
+}
+
+void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+{
+    CharEvent *event = g_malloc0(sizeof(CharEvent));
+    
+    event->id = find_char_driver(s);
+    if (event->id < 0)
+    {
+        fprintf(stderr, "Replay: cannot find char driver\n");
+        exit(1);
+    }
+    event->buf = g_malloc(len);
+    memcpy(event->buf, buf, len);
+    event->len = len;
+    
+    replay_add_event(REPLAY_ASYNC_EVENT_CHAR, event);
+}
+
+void replay_event_char_run(void *opaque)
+{
+    CharEvent *event = (CharEvent*)opaque;
+
+    qemu_chr_be_write_impl(char_drivers[event->id], event->buf, (int)event->len);
+    
+    g_free(event->buf);
+    g_free(event);
+}
+
+void replay_event_char_save(void *opaque)
+{
+    CharEvent *event = (CharEvent*)opaque;
+    
+    replay_put_byte(event->id);
+    replay_put_array(event->buf, event->len);
+}
+
+void *replay_event_char_read(void)
+{
+    CharEvent *event = g_malloc0(sizeof(CharEvent));
+    
+    event->id = replay_get_byte();
+    replay_get_array_alloc(&event->buf, &event->len);
+    
+    return event;
+}
+
+void replay_event_char_skip(void)
+{
+    size_t size;
+    
+    fseek(replay_file, sizeof(uint8_t), SEEK_CUR);
+    if (fread(&size, sizeof(size), 1, replay_file) != 1)
+    {
+        fprintf(stderr, "Internal error while skipping char event\n");
+        exit(1);
+    }
+    fseek(replay_file, size, SEEK_CUR);
+}
diff --git a/replay/replay-debug.c b/replay/replay-debug.c
new file mode 100644
index 0000000..5d1cc95
--- /dev/null
+++ b/replay/replay-debug.c
@@ -0,0 +1,152 @@
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "exec/cpu-defs.h"
+
+#include "replay.h"
+#include "replay-internal.h"
+
+/* Reverse debugging data */
+
+/* Saved handler of the debug exception */
+static CPUDebugExcpHandler *prev_debug_excp_handler;
+/* Step of the last breakpoint hit. Used for seeking in reverse continue mode. */
+static uint64_t last_breakpoint_step;
+/* Start step, where reverse continue begins,
+   or target step for reverse stepping.*/
+static uint64_t last_reverse_step;
+/* Start step, where reverse continue begins.*/
+static uint64_t start_reverse_step;
+/* Previously loaded step for reverse continue */
+static SavedStateInfo *reverse_state;
+
+/*! Breakpoint handler for pass2 of reverse continue.
+    Stops the execution at previously saved breakpoint step. */
+static void reverse_continue_pass2_breakpoint_handler(CPUArchState *env)
+{
+    if (replay_get_current_step() == last_breakpoint_step)
+    {
+        CPUState *cpu = ENV_GET_CPU(env);
+        CPUDebugExcpHandler *handler = prev_debug_excp_handler;
+        prev_debug_excp_handler = NULL;
+
+        play_submode = REPLAY_PLAY_NORMAL;
+
+		cpu->exception_index = EXCP_DEBUG;
+        // invoke the breakpoint
+        cpu_set_debug_excp_handler(handler);
+        handler(env);
+        cpu_exit(cpu);
+    }
+}
+
+/*! Breakpoint handler for pass1 of reverse continue.
+    Saves last breakpoint hit and switches to pass2 when starting point is reached. */
+static void reverse_continue_pass1_breakpoint_handler(CPUArchState *env)
+{
+    if (replay_get_current_step() == last_reverse_step)
+    {
+        CPUState *cpu = ENV_GET_CPU(env);
+        // repeat first pass if breakpoint was not found
+        // on current iteration
+        if (last_breakpoint_step == reverse_state->step - 1
+            && reverse_state != saved_states)
+        {
+            last_reverse_step = reverse_state->step;
+            // load previous state
+            --reverse_state;
+            last_breakpoint_step = reverse_state->step - 1;
+            replay_seek_step(reverse_state->step);
+            // set break should be after seek, because seek resets break
+            replay_set_break(last_reverse_step);
+			cpu_loop_exit(cpu);
+        }
+        else
+        {
+            // this condition is needed, when no breakpoints were found
+            if (last_breakpoint_step == reverse_state->step - 1) 
+            {
+                ++last_breakpoint_step;
+            }
+            cpu_set_debug_excp_handler(reverse_continue_pass2_breakpoint_handler);
+			
+            reverse_continue_pass2_breakpoint_handler(env);
+            replay_seek_step(last_breakpoint_step);
+            cpu_loop_exit(cpu);
+        }
+    }
+    else
+    {
+        // watchpoint branch
+        last_breakpoint_step = replay_get_current_step();
+    }
+}
+
+
+void replay_reverse_breakpoint(void)
+{
+    // we started reverse execution from a breakpoint
+    if (replay_get_current_step() != start_reverse_step)
+    {
+        last_breakpoint_step = replay_get_current_step();
+    }
+}
+
+
+void replay_reverse_continue(void)
+{
+    if (replay_mode == REPLAY_PLAY && play_submode == REPLAY_PLAY_NORMAL)
+    {
+        tb_flush_all();
+        play_submode = REPLAY_PLAY_REVERSE;
+
+        last_reverse_step = replay_get_current_step();
+        start_reverse_step = replay_get_current_step();
+        // load initial state
+        reverse_state = find_nearest_state(replay_get_current_step());
+        replay_seek_step(reverse_state->step);
+        // run to current step
+        replay_set_break(last_reverse_step);
+        // decrement to allow breaking at the first step
+        last_breakpoint_step = reverse_state->step - 1;
+        prev_debug_excp_handler =
+            cpu_set_debug_excp_handler(reverse_continue_pass1_breakpoint_handler);
+    }
+}
+
+
+/*! Breakpoint handler for reverse stepping.
+    Stops at the desired step and skips other breakpoints. */
+static void reverse_step_breakpoint_handler(CPUArchState *env)
+{
+    if (replay_get_current_step() == last_reverse_step)
+    {
+        CPUState *cpu = ENV_GET_CPU(env);
+        CPUDebugExcpHandler *handler = prev_debug_excp_handler;
+        prev_debug_excp_handler = NULL;
+
+        play_submode = REPLAY_PLAY_NORMAL;
+
+        cpu->exception_index = EXCP_DEBUG;
+        // invoke the breakpoint
+        cpu_set_debug_excp_handler(handler);
+        handler(env);
+        cpu_exit(cpu);
+    }
+}
+
+
+void replay_reverse_step(void)
+{
+    if (replay_mode == REPLAY_PLAY && play_submode == REPLAY_PLAY_NORMAL
+        && replay_get_current_step() > 0)
+    {
+        tb_flush_all();
+        play_submode = REPLAY_PLAY_REVERSE;
+
+        last_reverse_step = replay_get_current_step() - 1;
+        replay_seek_step(last_reverse_step);
+
+        prev_debug_excp_handler =
+            cpu_set_debug_excp_handler(reverse_step_breakpoint_handler);
+    }
+}
diff --git a/replay/replay-events.c b/replay/replay-events.c
new file mode 100644
index 0000000..bd2d53f
--- /dev/null
+++ b/replay/replay-events.c
@@ -0,0 +1,463 @@
+#include <stdio.h>
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "qemu/queue.h"
+#include "replay.h"
+#include "replay-internal.h"
+#include "qemu/log.h"
+#include "qemu/event_notifier.h"
+#include "monitor/monitor.h"
+#include "block/thread-pool.h"
+#include "block/aio.h"
+#include "ui/input.h"
+
+typedef struct Event
+{
+    unsigned int event_id;
+    void *opaque;
+    void *opaque2;
+    uint64_t id;
+
+    QTAILQ_ENTRY(Event) events;
+} Event;
+
+static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
+
+static QemuMutex lock;
+static unsigned int read_event_id = -1;
+static uint64_t read_bh_id = -1;
+static int read_opt = -1;
+
+// Externals
+bool replay_events_enabled = false;
+
+// Functions
+
+static void replay_run_event(Event *event)
+{
+    switch (event->event_id)
+    {
+    case REPLAY_ASYNC_EVENT_NOTIFIER:
+        event_notifier_set((EventNotifier*)event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_NETWORK:
+        replay_net_send_packet(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_THREAD:
+        thread_pool_work((struct ThreadPool *)event->opaque, (struct ThreadPoolElement
*)event->opaque2);
+        break;
+    case REPLAY_ASYNC_EVENT_BH:
+        aio_bh_call(event->opaque);
+        break;
+#ifdef CONFIG_USB_LIBUSB
+    case REPLAY_ASYNC_EVENT_USB_CTRL:
+        replay_event_usb_ctrl(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_USB_DATA:
+        replay_event_usb_data(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_USB_ISO:
+        replay_event_usb_iso(event->opaque);
+        break;
+#endif
+    case REPLAY_ASYNC_EVENT_CHAR:
+        replay_event_char_run(event->opaque);
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT:
+        qemu_input_event_send_impl(NULL, (InputEvent*)event->opaque);
+        // Using local variables, when replaying. Do not free them.
+        if (replay_mode == REPLAY_SAVE)
+        {
+            qapi_free_InputEvent((InputEvent*)event->opaque);
+        }
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
+        qemu_input_event_sync_impl();
+        break;
+    default:
+        fprintf(stderr, "Replay: invalid async event ID (%d) in the queue\n", event->event_id);
+        exit(1);
+        break;
+    }
+}
+
+void replay_enable_events(void)
+{
+    replay_events_enabled = true;
+}
+
+void replay_flush_events(void)
+{
+    qemu_mutex_lock(&lock);
+    while (!QTAILQ_EMPTY(&events_list))
+    {
+        Event *event = QTAILQ_FIRST(&events_list);
+
+        // perform an action
+        replay_run_event(event);
+
+        // go to next event
+        QTAILQ_REMOVE(&events_list, event, events);
+
+        g_free(event);
+    }
+    qemu_mutex_unlock(&lock);
+}
+
+void replay_disable_events(void)
+{
+    replay_events_enabled = false;
+    // Flush events queue before waiting of completion
+    replay_flush_events();
+}
+
+void replay_clear_events(void)
+{
+    qemu_mutex_lock(&lock);
+    while (!QTAILQ_EMPTY(&events_list))
+    {
+        Event *event = QTAILQ_FIRST(&events_list);
+        // go to next event
+        QTAILQ_REMOVE(&events_list, event, events);
+
+        g_free(event);
+    }
+    qemu_mutex_unlock(&lock);
+}
+
+static void replay_add_event_internal(unsigned int event_id, void *opaque, void *opaque2, uint64_t
id)
+{
+    if (event_id >= REPLAY_ASYNC_COUNT)
+    {
+        fprintf(stderr, "Replay: invalid async event ID (%d)\n", event_id);
+        exit(1);
+    }
+    if (!replay_file || replay_mode == REPLAY_NONE
+        || !replay_events_enabled
+        || replay_get_play_submode() == REPLAY_PLAY_CHANGED)
+    {
+        Event e;
+        e.event_id = event_id;
+        e.opaque = opaque;
+        e.opaque2 = opaque2;
+        e.id = id;
+        replay_run_event(&e);
+        return;
+    }
+
+    Event *event = g_malloc0(sizeof(Event));
+    event->event_id = event_id;
+    event->opaque = opaque;
+    event->opaque2 = opaque2;
+    event->id = id;
+
+    qemu_mutex_lock(&lock);
+    QTAILQ_INSERT_TAIL(&events_list, event, events);
+    qemu_mutex_unlock(&lock);
+}
+
+void replay_add_event(unsigned int event_id, void *opaque)
+{
+    replay_add_event_internal(event_id, opaque, NULL, 0);
+}
+
+void replay_add_usb_event(unsigned int event_id, uint64_t id, void *opaque)
+{
+    replay_add_event_internal(event_id, opaque, NULL, id);
+}
+
+void replay_add_bh_event(void *bh, uint64_t id)
+{
+    replay_add_event_internal(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
+}
+
+void replay_add_event2(unsigned int event_id, void *opaque, void *opaque2)
+{
+    replay_add_event_internal(event_id, opaque, opaque2, 0);
+}
+
+void replay_add_thread_event(void *opaque, void *opaque2, uint64_t id)
+{
+    replay_add_event_internal(REPLAY_ASYNC_EVENT_THREAD, opaque, opaque2, id);
+}
+
+void replay_save_events(int opt)
+{
+    qemu_mutex_lock(&lock);
+    while (!QTAILQ_EMPTY(&events_list))
+    {
+        Event *event = QTAILQ_FIRST(&events_list);
+        if (replay_mode != REPLAY_PLAY)
+        {
+            // put the event into the file
+            if (opt == -1)
+            {
+                replay_put_event(EVENT_ASYNC);
+            }
+            else
+            {
+                replay_put_event(EVENT_ASYNC_OPT);
+                replay_put_byte(opt);
+            }
+            replay_put_dword(event->event_id);
+
+            // save event-specific data
+            switch (event->event_id)
+            {
+            case REPLAY_ASYNC_EVENT_NETWORK:
+                replay_net_save_packet(event->opaque);
+                break;
+#ifdef CONFIG_USB_LIBUSB
+            case REPLAY_ASYNC_EVENT_USB_CTRL:
+            case REPLAY_ASYNC_EVENT_USB_DATA:
+                replay_put_qword(event->id);
+                replay_event_save_usb_xfer(event->opaque);
+                break;
+            case REPLAY_ASYNC_EVENT_USB_ISO:
+                replay_put_qword(event->id);
+                replay_event_save_usb_iso_xfer(event->opaque);
+                break;
+#endif
+            case REPLAY_ASYNC_EVENT_THREAD:
+            case REPLAY_ASYNC_EVENT_BH:
+                replay_put_qword(event->id);
+                break;
+            case REPLAY_ASYNC_EVENT_CHAR:
+                replay_event_char_save(event->opaque);
+                break;
+            case REPLAY_ASYNC_EVENT_INPUT:
+                replay_save_input_event(event->opaque);
+                break;
+            }
+        }
+
+        // perform an action
+        replay_run_event(event);
+
+        // go to next event
+        QTAILQ_REMOVE(&events_list, event, events);
+
+        g_free(event);
+    }
+    qemu_mutex_unlock(&lock);
+}
+
+bool replay_has_events(void)
+{
+    return !QTAILQ_EMPTY(&events_list);
+}
+
+
+void replay_read_events(int opt)
+{
+    replay_fetch_data_kind();
+    while ((opt == -1 && replay_data_kind == EVENT_ASYNC)
+        || (opt != -1 && replay_data_kind == EVENT_ASYNC_OPT))
+    {
+        if (read_event_id == -1)
+        {
+            if (opt != -1)
+            {
+                read_opt = replay_get_byte();
+            }
+            read_event_id = replay_get_dword();
+            read_bh_id = -1;
+            replay_check_error();
+        }
+        
+        if (opt != read_opt)
+            break;
+        // Execute some events without searching them in the queue
+        Event e;
+        switch (read_event_id)
+        {
+        case REPLAY_ASYNC_EVENT_NOTIFIER:
+            // Nothing to read 
+            break;
+        case REPLAY_ASYNC_EVENT_NETWORK:
+            e.opaque = replay_net_read_packet();
+            e.event_id = read_event_id;
+            replay_run_event(&e);
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+            // continue with the next event
+            continue;
+        case REPLAY_ASYNC_EVENT_THREAD:
+        case REPLAY_ASYNC_EVENT_BH:
+            if (read_bh_id == -1)
+            {
+                read_bh_id = replay_get_qword();
+            }
+            break;
+#ifdef CONFIG_USB_LIBUSB
+        case REPLAY_ASYNC_EVENT_USB_CTRL:
+        case REPLAY_ASYNC_EVENT_USB_DATA:
+        case REPLAY_ASYNC_EVENT_USB_ISO:
+            if (read_bh_id == -1)
+            {
+                read_bh_id = replay_get_qword();
+            }
+            // read after finding event in the list
+            break;
+#endif
+        case REPLAY_ASYNC_EVENT_CHAR:
+            e.event_id = read_event_id;
+            e.opaque = replay_event_char_read();
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+
+            replay_run_event(&e);
+            // continue with the next event
+            continue;
+        case REPLAY_ASYNC_EVENT_INPUT:
+            e.event_id = read_event_id;
+            e.opaque = replay_read_input_event();
+
+            replay_run_event(&e);
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+            // continue with the next event
+            continue;
+        case REPLAY_ASYNC_EVENT_INPUT_SYNC:
+            e.event_id = read_event_id;
+            e.opaque = 0;
+            replay_run_event(&e);
+
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_opt = -1;
+            replay_fetch_data_kind();
+            // continue with the next event
+            continue;
+        default:
+            fprintf(stderr, "Unknown ID %d of replay event\n", read_event_id);
+            exit(1);
+            break;
+        }
+
+        qemu_mutex_lock(&lock);
+
+        Event *event = NULL;
+        Event *curr = NULL;
+        QTAILQ_FOREACH(curr, &events_list, events)
+        {
+            if (curr->event_id == read_event_id
+                && (read_bh_id == -1 || read_bh_id == curr->id))
+            {
+                event = curr;
+                break;
+            }
+        }
+
+        if (event)
+        {
+            // continue reading data
+#ifdef CONFIG_USB_LIBUSB
+            switch (read_event_id)
+            {
+            case REPLAY_ASYNC_EVENT_USB_CTRL:
+            case REPLAY_ASYNC_EVENT_USB_DATA:
+                replay_event_read_usb_xfer(event->opaque);
+                break;
+            case REPLAY_ASYNC_EVENT_USB_ISO:
+                replay_event_read_usb_iso_xfer(event->opaque);
+                break;
+            }
+#endif
+
+            QTAILQ_REMOVE(&events_list, event, events);
+
+            qemu_mutex_unlock(&lock);
+
+            // reset unread data and other parameters to allow
+            // reading other data from the log while
+            // running the event
+            replay_has_unread_data = 0;
+            read_event_id = -1;
+            read_bh_id = -1;
+            read_opt = -1;
+
+            replay_run_event(event);
+            g_free(event);
+
+            replay_fetch_data_kind();
+        }
+        else
+        {
+            qemu_mutex_unlock(&lock);
+            // No such event found in the queue
+            break;
+        }
+    }
+}
+
+
+void replay_init_events(void)
+{
+    read_event_id = -1;
+    qemu_mutex_init(&lock);
+}
+
+
+void replay_finish_events(void)
+{
+    replay_events_enabled = false;
+    replay_clear_events();
+    qemu_mutex_destroy(&lock);
+}
+
+
+void replay_skip_async_event(struct Monitor *mon, uint64_t step, bool opt)
+{
+    if (opt)
+    {
+        replay_get_byte();
+    }
+
+    int id = replay_get_dword();
+    switch (id)
+    {
+    case REPLAY_ASYNC_EVENT_NOTIFIER:
+        break;
+    case REPLAY_ASYNC_EVENT_NETWORK:
+        replay_net_skip_packet(mon, step);
+        break;
+    case REPLAY_ASYNC_EVENT_THREAD:
+    case REPLAY_ASYNC_EVENT_BH:
+        fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+        break;
+#ifdef CONFIG_USB_LIBUSB
+    case REPLAY_ASYNC_EVENT_USB_CTRL:
+    case REPLAY_ASYNC_EVENT_USB_DATA:
+        replay_get_qword();
+        replay_event_skip_usb_xfer();
+        break;
+    case REPLAY_ASYNC_EVENT_USB_ISO:
+        replay_get_qword();
+        replay_event_skip_usb_iso_xfer();
+        break;
+#endif
+    case REPLAY_ASYNC_EVENT_CHAR:
+        replay_event_char_skip();
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT:
+        replay_skip_input_event(mon, step);
+        break;
+    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
+        break;
+    default:
+        fprintf(stderr, "Unknown ID %d of replay event found while skipping\n", read_event_id);
+        exit(1);
+        break;
+    }
+}
+
diff --git a/replay/replay-icount.c b/replay/replay-icount.c
new file mode 100644
index 0000000..c216a94
--- /dev/null
+++ b/replay/replay-icount.c
@@ -0,0 +1,112 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+#include "sysemu/cpus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+#include "migration/vmstate.h"
+#include "replay.h"
+#include "replay-internal.h"
+
+int replay_icount;
+
+typedef struct {
+    /* Compensate for varying guest execution speed.  */
+    int64_t bias;
+    /* Timer for advancing VM clock, when all CPUs are sleeping */
+    QEMUTimer *icount_warp_timer;
+    int64_t vm_clock_warp_start;
+} ReplayIcount;
+static ReplayIcount icount_data;
+
+
+/* Return the virtual CPU time, based on the instruction counter.  */
+int64_t replay_get_icount(void)
+{
+    int64_t icount = replay_get_current_step();
+    return icount_data.bias + (icount << replay_icount);
+}
+
+static void replay_icount_warp_rt(void *opaque)
+{
+    if (icount_data.vm_clock_warp_start == -1) {
+        return;
+    }
+
+    if (runstate_is_running()) {
+        int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+        int64_t warp_delta = clock - icount_data.vm_clock_warp_start;
+        icount_data.bias += warp_delta;
+        if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
+            qemu_notify_event();
+        }
+    }
+    icount_data.vm_clock_warp_start = -1;
+}
+
+void replay_clock_warp(void)
+{
+    int64_t deadline;
+    if (!replay_checkpoint(9))
+        return;
+    /*
+     * If the CPUs have been sleeping, advance the vm_clock timer now.  This
+     * ensures that the deadline for the timer is computed correctly below.
+     * This also makes sure that the insn counter is synchronized before the
+     * CPU starts running, in case the CPU is woken by an event other than
+     * the earliest vm_clock timer.
+     */
+    if (icount_data.vm_clock_warp_start != -1) {
+        replay_icount_warp_rt(NULL);
+    }
+    if (!all_cpu_threads_idle() || !qemu_clock_has_timers(QEMU_CLOCK_VIRTUAL)) {
+        timer_del(icount_data.icount_warp_timer);
+        return;
+    }
+
+    icount_data.vm_clock_warp_start = qemu_clock_get_ns(QEMU_CLOCK_HOST);
+    deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
+    if (deadline > 0) {
+        /*
+         * Ensure the vm_clock proceeds even when the virtual CPU goes to
+         * sleep.  Otherwise, the CPU might be waiting for a future timer
+         * interrupt to wake it up, but the interrupt never comes because
+         * the vCPU isn't running any insns and thus doesn't advance the
+         * vm_clock.
+         *
+         * An extreme solution for this problem would be to never let VCPUs
+         * sleep in icount mode if there is a pending vm_clock timer; rather
+         * time could just advance to the next vm_clock event.  Instead, we
+         * do stop VCPUs and only advance vm_clock after some "real" time,
+         * (related to the time left until the next event) has passed.  This
+         * rt_clock timer will do this.  This avoids that the warps are too
+         * visible externally---for example, you will not be sending network
+         * packets continuously instead of every 100ms.
+         */
+        timer_mod_ns(icount_data.icount_warp_timer, icount_data.vm_clock_warp_start + deadline);
+    } else {
+        qemu_notify_event();
+    }
+}
+
+static const VMStateDescription vmstate_icount = {
+    .name = "icount",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT64(bias, ReplayIcount),
+        VMSTATE_TIMER(icount_warp_timer, ReplayIcount),
+        VMSTATE_INT64(vm_clock_warp_start, ReplayIcount),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void replay_configure_icount(void)
+{
+    vmstate_register(NULL, 0, &vmstate_icount, &icount_data);
+    icount_data.icount_warp_timer = timer_new_ns(QEMU_CLOCK_HOST, replay_icount_warp_rt, NULL);
+}
diff --git a/replay/replay-input.c b/replay/replay-input.c
new file mode 100644
index 0000000..c4dc7c5
--- /dev/null
+++ b/replay/replay-input.c
@@ -0,0 +1,152 @@
+#include "replay.h"
+#include "replay-internal.h"
+#include "qapi-types.h"
+#include "sysemu/sysemu.h"
+#include "monitor/monitor.h"
+#include "ui/input.h"
+#include "qemu/log.h"
+
+void replay_save_input_event(InputEvent *evt)
+{
+    replay_put_dword(evt->kind);
+
+    switch (evt->kind)
+    {
+    case INPUT_EVENT_KIND_KEY:
+        replay_put_dword(evt->key->key->kind);
+
+        switch (evt->key->key->kind)
+        {
+        case KEY_VALUE_KIND_NUMBER:
+            replay_put_qword(evt->key->key->number);
+            replay_put_byte(evt->key->down);
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            replay_put_dword(evt->key->key->qcode);
+            replay_put_byte(evt->key->down);
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        replay_put_dword(evt->btn->button);
+        replay_put_byte(evt->btn->down);
+        break;
+    case INPUT_EVENT_KIND_REL:
+        replay_put_dword(evt->rel->axis);
+        replay_put_qword(evt->rel->value);
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        replay_put_dword(evt->abs->axis);
+        replay_put_qword(evt->abs->value);
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
+}
+
+InputEvent *replay_read_input_event(void)
+{
+    static InputEvent evt;
+    static KeyValue keyValue;
+    static InputKeyEvent key;
+    key.key = &keyValue;
+    static InputBtnEvent btn;
+    static InputMoveEvent rel;
+    static InputMoveEvent abs;
+
+    evt.kind = replay_get_dword();
+    switch (evt.kind)
+    {
+    case INPUT_EVENT_KIND_KEY:
+        evt.key = &key;
+        evt.key->key->kind = replay_get_dword();
+
+        switch (evt.key->key->kind)
+        {
+        case KEY_VALUE_KIND_NUMBER:
+            evt.key->key->number = replay_get_qword();
+            evt.key->down = replay_get_byte();
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            evt.key->key->qcode = (QKeyCode)replay_get_dword();
+            evt.key->down = replay_get_byte();
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        evt.btn = &btn;
+        evt.btn->button = (InputButton)replay_get_dword();
+        evt.btn->down = replay_get_byte();
+        break;
+    case INPUT_EVENT_KIND_REL:
+        evt.rel = &rel;
+        evt.rel->axis = (InputAxis)replay_get_dword();
+        evt.rel->value = replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        evt.abs = &abs;
+        evt.abs->axis = (InputAxis)replay_get_dword();
+        evt.abs->value = replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
+
+    return &evt;
+}
+
+void replay_skip_input_event(struct Monitor *mon, uint64_t step)
+{
+    switch (replay_get_dword())
+    {
+    case INPUT_EVENT_KIND_KEY:
+        switch (replay_get_dword())
+        {
+        case KEY_VALUE_KIND_NUMBER:
+            {
+                int64_t number = replay_get_qword();
+                bool down = replay_get_byte();
+                monitor_printf(mon, "%" PRId64 ": Key %"PRIx64" %s\n", step, number, down ?
"pressed" : "released");
+            }
+            break;
+        case KEY_VALUE_KIND_QCODE:
+            {
+                int qcode = replay_get_dword();
+                bool down = replay_get_byte();
+                monitor_printf(mon, "%" PRId64 ": Key %d %s\n", step, qcode, down ? "pressed" :
"released");
+            }
+            break;
+        case KEY_VALUE_KIND_MAX:
+            /* keep gcc happy */
+            break;
+        }
+        break;
+    case INPUT_EVENT_KIND_BTN:
+        {
+            int btn = replay_get_dword();
+            bool down = replay_get_byte();
+            monitor_printf(mon, "%" PRId64 ": Mouse buttons %d %s\n", step, btn, down ? "pressed" :
"released");
+        }
+        break;
+    case INPUT_EVENT_KIND_REL:
+        replay_get_dword();
+        replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_ABS:
+        replay_get_dword();
+        replay_get_qword();
+        break;
+    case INPUT_EVENT_KIND_MAX:
+        /* keep gcc happy */
+        break;
+    }
+}
+
diff --git a/replay/replay-internal.c b/replay/replay-internal.c
new file mode 100644
index 0000000..96dda4d
--- /dev/null
+++ b/replay/replay-internal.c
@@ -0,0 +1,176 @@
+//#include "buffered_file.h"
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "qemu/queue.h"
+#include "replay-internal.h"
+
+volatile unsigned int replay_data_kind = -1;
+volatile unsigned int replay_has_unread_data;
+
+/* File for replay writing */
+FILE *replay_file;
+
+void replay_put_byte(unsigned char byte)
+{
+    if (replay_file)
+    {
+        fwrite(&byte, sizeof(byte), 1, replay_file);
+    }
+}
+
+void replay_put_event(unsigned char event)
+{
+    replay_put_byte(event);
+}
+
+
+void replay_put_word(uint16_t word)
+{
+    if (replay_file)
+        fwrite(&word, sizeof(word), 1, replay_file);
+}
+
+void replay_put_dword(unsigned int dword)
+{
+    if (replay_file)
+        fwrite(&dword, sizeof(dword), 1, replay_file);
+}
+
+void replay_put_qword(int64_t qword)
+{
+    if (replay_file)
+        fwrite(&qword, sizeof(qword), 1, replay_file);
+}
+
+
+void replay_put_array(const uint8_t *buf, size_t size)
+{
+    if (replay_file)
+    {
+        fwrite(&size, sizeof(size), 1, replay_file);
+        fwrite(buf, 1, size, replay_file);
+    }
+}
+
+
+
+unsigned char replay_get_byte(void)
+{
+    unsigned char byte;
+    if (replay_file)
+    {
+        fread(&byte, sizeof(byte), 1, replay_file);
+    }
+    return byte;
+}
+
+uint16_t replay_get_word(void)
+{
+    uint16_t word;
+    if (replay_file)
+        fread(&word, sizeof(word), 1, replay_file);
+
+    return word;
+}
+
+unsigned int replay_get_dword(void)
+{
+    unsigned int dword;
+    if (replay_file)
+        fread(&dword, sizeof(dword), 1, replay_file);
+
+    return dword;
+}
+
+int64_t replay_get_qword(void)
+{
+    int64_t qword;
+    if (replay_file)
+        fread(&qword, sizeof(qword), 1, replay_file);
+
+    return qword;
+}
+
+
+void replay_get_array(uint8_t *buf, size_t *size)
+{
+    if (replay_file)
+    {
+        fread(size, sizeof(*size), 1, replay_file);
+        fread(buf, 1, *size, replay_file);
+    }
+}
+
+void replay_get_array_alloc(uint8_t **buf, size_t *size)
+{
+    if (replay_file)
+    {
+        fread(size, sizeof(*size), 1, replay_file);
+        *buf = g_malloc(*size);
+        fread(*buf, 1, *size, replay_file);
+    }
+}
+
+void replay_check_error(void)
+{
+    if (replay_file)
+    {
+        if (feof(replay_file))
+        {
+            fprintf(stderr, "replay file is over\n");
+            exit(1);
+        }
+        else if (ferror(replay_file))
+        {
+            fprintf(stderr, "replay file is over or something goes wrong\n");
+            exit(1);
+        }
+    }
+}
+
+
+
+void replay_fetch_data_kind(void)
+{
+    if (replay_file)
+    {
+        if (!replay_has_unread_data)
+        {
+            replay_data_kind = replay_get_byte();
+            replay_check_error();
+            replay_has_unread_data = 1;
+        }
+    }
+}
+
+//! Saves cached instructions.
+void replay_save_instructions(void)
+{
+    if (replay_file && replay_mode == REPLAY_SAVE)
+    {
+        if (first_cpu != NULL && first_cpu->instructions_count > 0)
+        {
+            replay_put_event(EVENT_INSTRUCTION);
+            replay_put_dword(first_cpu->instructions_count);
+            replay_state.current_step += first_cpu->instructions_count;
+            first_cpu->instructions_count = 0;
+        }
+#ifdef _WIN32
+        replay_save_sound_in();
+        replay_save_sound_out();
+#endif
+    }
+}
+
+
+void validate_data_kind(int kind)
+{
+    replay_fetch_data_kind();
+    if (replay_data_kind != kind)
+    {
+        fprintf(stderr, "%" PRId64 ": read data kind %d instead of expected %d\n",
+            replay_get_current_step(), replay_data_kind, kind);
+        exit(1);
+    }
+}
+
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
new file mode 100644
index 0000000..10d5734
--- /dev/null
+++ b/replay/replay-internal.h
@@ -0,0 +1,192 @@
+#ifndef REPLAY_INTERNAL_H
+#define REPLAY_INTERNAL_H
+
+#include <stdio.h>
+
+// internal data for savevm
+#define EVENT_END_STARTUP           0
+// for time_t event
+#define EVENT_TIME_T	            1
+// for tm event
+#define EVENT_TM                    2
+// for outgoing sound event
+#define EVENT_SOUND_OUT             7
+// for incoming sound event
+#define EVENT_SOUND_IN              8
+// mouse input events
+#define EVENT_INPUT                 10
+// for software interrupt
+#define EVENT_INTERRUPT             15
+// for debug information
+#define EVENT_ASSERT                17
+// for timing information
+#define EVENT_TARGET_TIME           19
+// for shutdown request
+#define EVENT_SHUTDOWN              20
+// for save VM event
+#define EVENT_SAVE_VM_BEGIN         21
+// for save VM event
+#define EVENT_SAVE_VM_END           22
+// for emulated exceptions
+#define EVENT_EXCEPTION             23
+// for async events
+#define EVENT_ASYNC                 24
+#define EVENT_ASYNC_OPT             25
+// for int data
+#define EVENT_DATA_INT              26
+// for data buffer
+#define EVENT_DATA_BUFFER           27
+// for checkoint event
+#define EVENT_INSTRUCTION           32
+// for clock read/writes
+#define EVENT_CLOCK                 64
+// some of grteater codes are reserved for clocks
+
+// for checkpoint event
+#define EVENT_CHECKPOINT            96
+// end of log event
+#define EVENT_END                   127
+
+// limited by Ethernet frame size
+#define MAX_NET_PACKET_SIZE             1560
+
+/*! Information about saved VM state */
+struct SavedStateInfo
+{
+    // Offset in the replay log file where state is saved.
+    uint64_t file_offset;
+    // Step number, corresponding to the saved state.
+    uint64_t step;
+};
+
+struct Monitor;
+
+typedef struct ReplayState
+{
+    //! Cached clock values.
+    int64_t cached_clock[REPLAY_CLOCK_COUNT];
+    //! Nonzero, when next instruction is repeated one and was already
+    //! processed.
+    int skipping_instruction;
+    /*! Current step - number of processed instructions and timer events. */
+    uint64_t current_step;
+
+    /*! Temporary data for saving/loading replay file position. */
+    uint64_t file_offset;
+} ReplayState;
+extern ReplayState replay_state;
+
+/*! Reference to the saved state */
+typedef struct SavedStateInfo SavedStateInfo;
+
+extern volatile unsigned int replay_data_kind;
+extern volatile unsigned int replay_has_unread_data;
+
+extern int play_submode;
+extern SavedStateInfo *saved_states;
+
+/* File for replay writing */
+extern FILE *replay_file;
+
+void replay_put_byte(unsigned char byte);
+void replay_put_event(unsigned char event);
+void replay_put_word(uint16_t word);
+void replay_put_dword(unsigned int dword);
+void replay_put_qword(int64_t qword);
+void replay_put_array(const uint8_t *buf, size_t size);
+
+unsigned char replay_get_byte(void);
+uint16_t replay_get_word(void);
+unsigned int replay_get_dword(void);
+int64_t replay_get_qword(void);
+void replay_get_array(uint8_t *buf, size_t *size);
+void replay_get_array_alloc(uint8_t **buf, size_t *size);
+
+//! Checks error status of the file.
+void replay_check_error(void);
+
+//! Reads data type from the file and stores it in the 
+//! replay_data_kind variable.
+void replay_fetch_data_kind(void);
+
+//! Saves queued events (like instructions and sound).
+void replay_save_instructions(void);
+//! Checks that the next data is corresponding to the desired kind.
+//! Terminates the program in case of error.
+void validate_data_kind(int kind);
+
+
+/*! Saves events from queue into the file */
+void replay_save_events(int opt);
+/*! Returns true if there are any unsaved events in the queue */
+bool replay_has_events(void);
+/*! Read events from the file into the input queue */
+void replay_read_events(int opt);
+/*! Initializes events' processing internals */
+void replay_init_events(void);
+/*! Clears internal data structures for events handling */
+void replay_finish_events(void);
+/*! Skips async event in the log file.
+    Called by the replay_events function.
+    If the event is network packet it should be printed to monitor. */
+void replay_skip_async_event(struct Monitor *mon, uint64_t step, bool opt);
+/*! Clears events list before loading new VM state */
+void replay_clear_events(void);
+
+/*! Finds saved state info which is nearest before the specified step. */
+SavedStateInfo *find_nearest_state(uint64_t step);
+
+
+// USB events
+void replay_event_usb_ctrl(void *opaque);
+void replay_event_usb_data(void *opaque);
+void replay_event_usb_iso(void *opaque);
+void replay_event_save_usb_xfer(void *opaque);
+void replay_event_save_usb_iso_xfer(void *opaque);
+void replay_event_read_usb_xfer(void *opaque);
+void replay_event_read_usb_iso_xfer(void *opaque);
+void replay_event_skip_usb_xfer(void);
+void replay_event_skip_usb_iso_xfer(void);
+bool replay_usb_has_xfers(void);
+void replay_add_usb_event(unsigned int event_id, uint64_t id, void *opaque);
+
+/*! Called to run char device event. */
+void replay_event_char_run(void *opaque);
+/*! Writes char event to the file. */
+void replay_event_char_save(void *opaque);
+/*! Reads char event from the file. */
+void *replay_event_char_read(void);
+/*! Skips char event. */
+void replay_event_char_skip(void);
+/*! Flushes events queue */
+void replay_flush_events(void);
+
+// icount
+void replay_configure_icount(void);
+
+// input events
+void replay_save_input_event(InputEvent *evt);
+InputEvent *replay_read_input_event(void);
+void replay_skip_input_event(struct Monitor *mon, uint64_t step);
+
+// sound events
+bool replay_has_sound_events(void);
+void replay_save_sound_out(void);
+void replay_save_sound_in(void);
+void replay_read_sound_out(void);
+void replay_read_sound_in(void);
+void replay_sound_flush_queue(void);
+
+// Network events
+void replay_net_init(void);
+void replay_net_free(void);
+void replay_net_read_packets_data(void);
+void replay_net_write_packets_data(void);
+void replay_net_save_packet(void *opaque);
+void *replay_net_read_packet(void);
+void replay_net_skip_packet(struct Monitor *mon, uint64_t step);
+/*! Called to send packet that was read or received from external input
+    to the net queue. */
+void replay_net_send_packet(void *opaque);
+
+#endif // REPLAY_INTERNAL_H
diff --git a/replay/replay-net.c b/replay/replay-net.c
new file mode 100644
index 0000000..6c39713
--- /dev/null
+++ b/replay/replay-net.c
@@ -0,0 +1,367 @@
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+#include "qemu/log.h"
+#include "net/net.h"
+#include "slirp/slirp.h"
+#include "slirp/libslirp.h"
+#include "monitor/monitor.h"
+#include "replay.h"
+#include "replay-internal.h"
+
+/* Network data */
+NetClientState **vlan_states = NULL;
+size_t vlan_states_count = 0;
+size_t vlan_states_capacity = 0;
+
+/* Structure for storing information about the network packet */
+typedef struct
+{
+    /* Offset in the replay log file where packet is saved. */
+    uint64_t file_offset;
+    /* Number of step when packet came. */
+    uint64_t step;
+} QEMU_PACKED NetPacketInfo;
+
+typedef struct NetPacketQueue
+{
+    /* ID of the packet */
+    uint64_t id;
+    /* ID of the network client */
+    int32_t nc_id;
+    size_t size;
+    uint8_t buf[MAX_NET_PACKET_SIZE];
+    uint64_t offset;
+} NetPacketQueue;
+
+/* Network packets count. */
+static uint64_t net_packets_count;
+/* Capacity of the array for packets parameters. */
+static uint64_t net_packets_capacity;
+/* Array for storing network packets parameters. */
+static NetPacketInfo *net_packets;
+
+/* Data for network fuzzing - modified versions of packets */
+
+/* Packet data */
+typedef struct ModifiedNetPacketInfo
+{
+    /* ID of the packet */
+    int64_t id;
+    /* Size of the packet */
+    size_t size;
+    /* Contents of the packet */
+    uint8_t *buf;
+
+    QLIST_ENTRY(ModifiedNetPacketInfo) next;
+} ModifiedNetPacketInfo;
+
+static QLIST_HEAD(, ModifiedNetPacketInfo) modified_net_packets =
+    QLIST_HEAD_INITIALIZER(modified_net_packets);
+
+
+void replay_net_init(void)
+{
+    net_packets_count = 0;
+}
+
+void replay_net_read_packets_data(void)
+{
+    // read network packets info
+    net_packets_count = replay_get_qword();
+    net_packets_capacity = net_packets_count;
+    if (net_packets_count)
+    {
+        net_packets = (NetPacketInfo*)g_malloc(sizeof(NetPacketInfo) * net_packets_count);
+        if (fread(net_packets, sizeof(NetPacketInfo), net_packets_count, replay_file) !=
net_packets_count)
+        {
+            fprintf(stderr, "Internal error in replay_net_read_packets_data\n");
+            exit(1);
+        }
+    }
+}
+
+void replay_net_write_packets_data(void)
+{
+    // write network packets info
+    replay_put_qword(net_packets_count);
+    if (net_packets && net_packets_count)
+    {
+        // TODO ??? Save distinct fields in the loop
+        fwrite(net_packets, sizeof(NetPacketInfo), net_packets_count, replay_file);
+    }
+}
+
+void replay_add_network_client(NetClientState *nc)
+{
+    if (vlan_states_count == 0)
+    {
+        vlan_states = (NetClientState **)g_malloc(sizeof(*vlan_states));
+        vlan_states_count = 0;
+        vlan_states_capacity = 1;
+    }
+    else if (vlan_states_count == vlan_states_capacity)
+    {
+        vlan_states_capacity *= 2;
+        vlan_states = (NetClientState **)g_realloc(vlan_states, sizeof(*vlan_states) *
vlan_states_capacity);
+    }
+
+    vlan_states[vlan_states_count++] = nc;
+}
+
+void replay_net_free(void)
+{
+    // free modified packets
+    ModifiedNetPacketInfo *packet, *nextPacket;
+    QLIST_FOREACH_SAFE(packet, &modified_net_packets, next, nextPacket)
+    {
+        QLIST_REMOVE(packet, next);
+        g_free(packet->buf);
+        g_free(packet);
+    }
+
+    if (vlan_states)
+    {
+        g_free(vlan_states);
+        vlan_states = NULL;
+    }
+}
+
+
+//! Saves network packet into the log.
+void replay_save_net_packet(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    if (replay_file)
+    {
+        // save packet info into the array
+        if (net_packets_capacity == net_packets_count)
+        {
+            if (net_packets_capacity == 0)
+                net_packets_capacity = 1;
+            else
+                net_packets_capacity *= 2;
+            net_packets = (NetPacketInfo*)g_realloc(net_packets, net_packets_capacity *
sizeof(NetPacketInfo));
+        }
+
+        // add packet processing event to the queue
+        NetPacketQueue *p = (NetPacketQueue*)g_malloc0(sizeof(NetPacketQueue));
+        p->id = net_packets_count;
+        p->size = size;
+        if (net_hub_id_for_client(nc, &p->nc_id) < 0)
+        {
+            fprintf(stderr, "Replay: Cannot determine net client id\n");
+            exit(1);
+        }
+        memcpy(p->buf, buf, size);
+        replay_add_event(REPLAY_ASYNC_EVENT_NETWORK, p);
+
+        ++net_packets_count;
+    }
+}
+
+
+static ModifiedNetPacketInfo *find_modified_packet(int64_t id)
+{
+    ModifiedNetPacketInfo *packet = NULL;
+    QLIST_FOREACH(packet, &modified_net_packets, next)
+    {
+        if (packet->id == id)
+            return packet;
+    }
+
+    return NULL;
+}
+
+static NetClientState *replay_net_find_vlan(NetPacketQueue *packet)
+{
+    int i;
+    for (i = 0 ; i < vlan_states_count ; ++i)
+    {
+        int id = 0;
+        if (net_hub_id_for_client(vlan_states[i], &id) < 0)
+        {
+            fprintf(stderr, "Replay: Cannot determine net client id\n");
+            exit(1);
+        }
+        if (id == packet->nc_id)
+            return vlan_states[i];
+    }
+
+    fprintf(stderr, "Replay: please specify -net replay command-line option\n");
+    exit(1);
+
+    return NULL;
+}
+
+void replay_net_send_packet(void *opaque)
+{
+    NetPacketQueue *packet = (NetPacketQueue *)opaque;
+    NetClientState *vlan_state = replay_net_find_vlan(packet);
+
+    if (replay_mode == REPLAY_SAVE)
+    {
+        net_packets[packet->id].file_offset = packet->offset;
+        net_packets[packet->id].step = replay_get_current_step();
+
+        qemu_send_packet(vlan_state, packet->buf, packet->size);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        // fuzzing or not
+        ModifiedNetPacketInfo *mod_packet = find_modified_packet(packet->id);
+        if (mod_packet)
+        {
+            replay_set_play_changed();
+            qemu_send_packet(vlan_state, mod_packet->buf, mod_packet->size);
+        }
+        else
+        {
+            qemu_send_packet(vlan_state, packet->buf, packet->size);
+        }
+    }
+
+    g_free(packet);
+}
+
+int replay_get_packet(struct Monitor *mon, int64_t id)
+{
+    if (replay_mode == REPLAY_PLAY && replay_file)
+    {
+        if (id < 0 || id >= net_packets_count)
+            return 0;
+
+        uint64_t offset = ftello64(replay_file);
+
+        size_t size;
+        // find the packet
+        fseeko64(replay_file, net_packets[id].file_offset, SEEK_SET);
+        // skip the id and nc_id
+        fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+        fseek(replay_file, sizeof(uint32_t), SEEK_CUR);
+
+        monitor_printf(mon, "Packet: ");
+        // read the packet
+        if (fread(&size, sizeof(size), 1, replay_file) != 1)
+        {
+            fprintf(stderr, "Internal error in replay_get_packet\n");
+            exit(1);
+        }
+        while (size--)
+        {
+            uint8_t byte;
+            if (fread(&byte, sizeof(byte), 1, replay_file) != 1)
+            {
+                fprintf(stderr, "Internal error in replay_get_packet\n");
+                exit(1);
+            }
+            //monitor_printf(mon, "%02x", byte);
+            monitor_print_bytes(mon, &byte, sizeof(byte));
+        }
+        monitor_printf(mon, "#\n\n");
+
+        // restore original position
+        fseeko64(replay_file, offset, SEEK_SET);
+
+        //**************************** printing modified packet
+        ModifiedNetPacketInfo *packet = find_modified_packet(id);
+        if (packet != NULL)
+        {
+            monitor_printf(mon, "Modified packet: ");
+            size_t i = 0;
+            for ( ; i < packet->size ; ++i)
+                monitor_print_bytes(mon, &packet->buf[i], sizeof(uint8_t));
+
+            monitor_printf(mon, "#\n\n");
+        }
+        //****************************
+
+        return 1;
+    }
+
+    return 0;
+}
+
+
+int replay_set_packet(struct Monitor *mon, int64_t id, const char *bytes)
+{
+    if (replay_mode == REPLAY_PLAY && replay_file)
+    {
+        if (bytes == NULL)
+        {
+            // remove packet
+            ModifiedNetPacketInfo *packet = find_modified_packet(id);
+            if (packet == NULL)
+                return 0;
+
+            QLIST_REMOVE(packet, next);
+            g_free(packet->buf);
+            g_free(packet);
+
+            return 1;
+        }
+        else
+        {
+            ModifiedNetPacketInfo *packet = find_modified_packet(id);
+            if (!packet)
+            {
+                // add packet
+                packet = (ModifiedNetPacketInfo *)g_malloc(sizeof(*packet));
+                QLIST_INSERT_HEAD(&modified_net_packets, packet, next);
+                packet->id = id;
+            }
+            else
+            {
+                g_free(packet->buf);
+            }
+
+            packet->size = strlen(bytes) / 2;
+            packet->buf = g_malloc(packet->size);
+            size_t i = 0;
+            for ( ; i < packet->size ; ++i)
+            {
+                packet->buf[i] = replay_xtoi(bytes[2 * i]) * 16 + replay_xtoi(bytes[2 * i + 1]);
+            }
+
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void replay_net_save_packet(void *opaque)
+{
+    NetPacketQueue *p = (NetPacketQueue*)opaque;
+    p->offset = ftello64(replay_file);
+    replay_put_qword(p->id);
+    replay_put_dword(p->nc_id);
+    replay_put_array(p->buf, p->size);
+    replay_network_packets += p->size;
+}
+
+void *replay_net_read_packet(void)
+{
+    NetPacketQueue *p = g_malloc0(sizeof(NetPacketQueue));;
+    p->id = replay_get_qword();
+    p->nc_id = replay_get_dword();
+    replay_get_array(p->buf, &p->size);
+    replay_check_error();
+
+    return p;
+}
+
+void replay_net_skip_packet(struct Monitor *mon, uint64_t step)
+{
+    uint64_t id;
+    int nc_id;
+    size_t size;
+    if (fread(&id, sizeof(id), 1, replay_file) != 1
+        || fread(&nc_id, sizeof(nc_id), 1, replay_file) != 1
+        || fread(&size, sizeof(size), 1, replay_file) != 1)
+    {
+        fprintf(stderr, "Internal error while skipping network packet\n");
+        exit(1);
+    }
+    fseek(replay_file, size, SEEK_CUR);
+    monitor_printf(mon, "%" PRId64 ": Net packet net=%d id=%" PRId64 "\n", step, nc_id, id);
+}
+
diff --git a/replay/replay-usb.c b/replay/replay-usb.c
new file mode 100644
index 0000000..9ee96e8
--- /dev/null
+++ b/replay/replay-usb.c
@@ -0,0 +1,241 @@
+#include "qemu-common.h"
+#include "replay.h"
+#include "replay-internal.h"
+#include "hw/usb.h"
+
+#ifdef CONFIG_USB_LIBUSB
+#include "hw/host-libusb.h"
+
+static uint64_t replay_get_xfer_id(struct libusb_transfer *xfer)
+{
+    USBHostRequest *r = xfer->user_data;
+    USBHostDevice *host = r->host;
+
+    return ((uint64_t)host->match.vendor_id << 32)
+           | host->match.product_id;
+}
+
+static uint64_t replay_get_iso_xfer_id(struct libusb_transfer *xfer)
+{
+    USBHostIsoXfer *r = xfer->user_data;
+    USBHostDevice  *host = r->ring->host;
+
+    return ((uint64_t)host->match.vendor_id << 32)
+           | host->match.product_id;
+}
+
+void replay_req_complete_ctrl(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_CTRL,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+void replay_req_register_ctrl(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_CTRL,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+
+void replay_event_usb_ctrl(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+
+    usb_host_req_complete_ctrl(xfer);
+}
+
+void replay_event_save_usb_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostRequest *r = xfer->user_data;
+    if (replay_mode == REPLAY_SAVE)
+    {
+        // save data
+        replay_put_dword(xfer->status);
+        replay_put_dword(xfer->actual_length);
+        replay_put_array(xfer->buffer, r->in ? xfer->length : 0);
+    }
+}
+
+void replay_event_save_usb_iso_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostIsoXfer *iso = xfer->user_data;
+    int i;
+    if (replay_mode == REPLAY_SAVE)
+    {
+        bool in = iso->ring->ep->pid == USB_TOKEN_IN;
+        // save data
+        replay_put_dword(xfer->status);
+        replay_put_dword(xfer->num_iso_packets);
+        for (i = 0 ; i < xfer->num_iso_packets ; ++i)
+        {
+            // all other fields of the packet are not used
+            unsigned int len = xfer->iso_packet_desc[i].actual_length;
+            //replay_put_dword(len);
+            if (/*len && */in)
+            {
+                replay_put_array(usb_host_get_iso_packet_buffer(iso, i), len);
+            }
+        }
+    }
+}
+
+void replay_event_read_usb_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostRequest *r = xfer->user_data;
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        // read data here
+        xfer->status = replay_get_dword();
+        xfer->actual_length = replay_get_dword();
+        size_t sz;
+        replay_get_array(xfer->buffer, &sz);
+        if (r->in && xfer->length != (int)sz)
+        {
+            fprintf(stderr, "Replay: trying to read USB control/data buffer with unexpected
size\n");
+            exit(1);
+        }
+    }
+}
+
+void replay_event_read_usb_iso_xfer(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+    USBHostIsoXfer *iso = xfer->user_data;
+    int i;
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        bool in = iso->ring->ep->pid == USB_TOKEN_IN;
+        // read data here
+        xfer->status = replay_get_dword();
+        xfer->num_iso_packets = replay_get_dword();
+        for (i = 0 ; i < xfer->num_iso_packets ; ++i)
+        {
+            // all other fields of the packet are not used
+            //unsigned int len = replay_get_dword();
+            if (in)
+            {
+                size_t sz;
+                replay_get_array(usb_host_get_iso_packet_buffer(iso, i), &sz);
+                xfer->iso_packet_desc[i].actual_length = (unsigned int)sz;
+            }
+        }
+    }
+}
+
+void replay_event_skip_usb_xfer(void)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        size_t sz;
+        // data/ctrl
+        replay_get_dword();
+        replay_get_dword();
+        if (fread(&sz, sizeof(sz), 1, replay_file) != 1)
+        {
+            fprintf(stderr, "Internal error while skipping usb xfer\n");
+            exit(1);
+        }
+        fseek(replay_file, sz, SEEK_CUR);
+    }
+}
+
+void replay_event_skip_usb_iso_xfer(void)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        // status
+        replay_get_dword();
+        // count
+        int num = replay_get_dword();
+        // packets
+        while (num--)
+        {
+            size_t sz;
+            if (fread(&sz, sizeof(sz), 1, replay_file) != 1)
+            {
+                fprintf(stderr, "Internal error while skipping usb iso xfer\n");
+                exit(1);
+            }
+            fseek(replay_file, sz, SEEK_CUR);
+        }
+    }
+}
+
+
+void replay_req_complete_data(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_DATA,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+void replay_req_register_data(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_DATA,
+                              replay_get_xfer_id(xfer), xfer);
+    }
+}
+
+
+void replay_event_usb_data(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+
+    usb_host_req_complete_data(xfer);
+}
+
+
+void replay_req_complete_iso(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_ISO,
+                              replay_get_iso_xfer_id(xfer), xfer);
+    }
+}
+
+void replay_req_register_iso(struct libusb_transfer *xfer)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+    USBHostIsoXfer *r = xfer->user_data;
+    USBHostDevice  *s = r->ring->host;
+
+        replay_add_usb_event(REPLAY_ASYNC_EVENT_USB_ISO,
+                              replay_get_iso_xfer_id(xfer), xfer);
+    }
+}
+
+
+void replay_event_usb_iso(void *opaque)
+{
+    struct libusb_transfer *xfer = opaque;
+
+    usb_host_req_complete_iso(xfer);
+}
+
+#endif
+
+bool replay_usb_has_xfers(void)
+{
+#ifdef CONFIG_USB_LIBUSB
+    return usb_host_has_xfers();
+#else
+    return false;
+#endif
+}
diff --git a/replay/replay.c b/replay/replay.c
new file mode 100644
index 0000000..6897c88
--- /dev/null
+++ b/replay/replay.c
@@ -0,0 +1,1604 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qemu-common.h"
+#include "exec/cpu-common.h"
+
+#include "replay.h"
+#include "replay-internal.h"
+
+#include "exec/cpu-defs.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+#include "monitor/monitor.h"
+#include "ui/keymaps.h"
+#include "exec/exec-all.h"
+#include "qapi/qmp/qlist.h"
+#include "qemu/thread.h"
+#include "sysemu/char.h"
+#include "cpu.h"
+#include "hw/ide/internal.h"
+#include "hw/ide/pci.h"
+#include "block/block_int.h"
+#include "sysemu/blockdev.h"
+
+// Current version of the replay mechanism.
+// Increase it when file format changes.
+#define REPLAY_VERSION              0xe02001
+// Size of replay log header
+#define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
+
+int replay_mode = REPLAY_NONE;
+/*! Stores current submode for PLAY mode */
+int play_submode = REPLAY_PLAY_UNKNOWN;
+
+uint64_t replay_network_packets;
+
+ReplayState replay_state;
+
+/*! Step for stopping execution at. */
+static uint64_t replay_break_step = -1;
+/*! Non-zero, when debug events should be written */
+static int debug_mode = 0;
+
+/* Offsets of clocks used for switching from replay to execution */
+int64_t realtime_clock_replay_offset;
+int64_t real_ticks_replay_offset;
+
+/* Name of replay file  */
+static char *replay_filename;
+/* Suffix for the disk images filenames */
+char *replay_image_suffix;
+
+int not_compatible_replay_param = 0;
+
+/* 
+    Auto-saving for VM states data 
+*/
+
+/* Minimum capacity of saved states information array */
+#define SAVED_STATES_MIN_CAPACITY   128
+/* Format of the name for the saved state */
+#define SAVED_STATE_NAME_FORMAT     "replay_%" PRId64
+
+/* Timer for auto-save VM states */
+static QEMUTimer *save_timer;
+/* Save state period in seconds */
+static uint64_t save_state_period;
+/* List of the saved states information */
+SavedStateInfo *saved_states;
+/* Number of saved states */
+static size_t saved_states_count;
+/* Capacity of the buffer for saved states */
+static size_t saved_states_capacity;
+/* Number of last loaded/saved state */
+static uint64_t current_saved_state;
+
+//* Target time */
+
+/* Timer for auto-save target RTC */
+static QEMUTimer *target_timer;
+/* Target timer saving period */
+static uint64_t target_timer_period;
+/* Additional parameter for function which provides time */
+static void *replay_target_time_dev;
+/* Pointer to function which provides time */
+static replay_get_date_func replay_get_target_time;
+
+/*****************************************************************************
+ *  Replay functions
+ *****************************************************************************/
+
+int replay_get_play_submode(void)
+{
+    return play_submode;
+}
+
+
+void replay_set_play_changed(void)
+{
+    if (replay_mode == REPLAY_PLAY)
+    {
+        replay_change(NULL);
+    }
+}
+
+
+static void replay_pre_save(void *opaque)
+{
+    ReplayState *state = opaque;
+    state->file_offset = ftello64(replay_file);
+}
+
+
+static int replay_post_load(void *opaque, int version_id)
+{
+    first_cpu->instructions_count = 0;
+
+    // seek the replay file
+    ReplayState *state = opaque;
+    fseeko64(replay_file, state->file_offset, SEEK_SET);
+    replay_has_unread_data = 0;
+
+    return 0;
+}
+
+
+static const VMStateDescription vmstate_replay = {
+    .name = "replay",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = replay_pre_save,
+    .post_load = replay_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT64_ARRAY(cached_clock, ReplayState, REPLAY_CLOCK_COUNT),
+        VMSTATE_INT32(skipping_instruction, ReplayState),
+        VMSTATE_UINT64(current_step, ReplayState),
+        VMSTATE_UINT64(file_offset, ReplayState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static void replay_savevm(void *opaque)
+{
+    char name[128];
+    uint64_t offset;
+
+#ifdef CONFIG_USB_LIBUSB
+    if (replay_usb_has_xfers())
+    {
+        // Retry of saving VM state, when USB host controller is not ready.
+        // We cannot save or interrupt non-finished transfers, so
+        // just wait for finishing them later.
+        timer_mod(save_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1);
+        return;
+    }
+#endif
+
+    offset = ftello64(replay_file);
+
+    replay_save_instructions();
+
+    replay_put_event(EVENT_SAVE_VM_BEGIN);
+
+    vm_stop(RUN_STATE_SAVE_VM);
+
+    // save VM state 
+    sprintf(name, SAVED_STATE_NAME_FORMAT, current_saved_state);
+    if (save_vmstate(default_mon, name) > 0)
+    {
+        // if period is 0, save only once
+        if (save_state_period != 0)
+        {
+            timer_mod(save_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + save_state_period);
+        }
+
+        // add more memory to buffer
+        if (saved_states_count >= saved_states_capacity)
+        {
+            saved_states_capacity += SAVED_STATES_MIN_CAPACITY;
+            saved_states = g_realloc(saved_states, saved_states_capacity * sizeof(SavedStateInfo));
+            if (!saved_states)
+            {
+                saved_states_count = 0;
+                fprintf(stderr, "Replay: Saved states memory reallocation failed.\n");
+                exit(1);
+            }
+        }
+        // save state ID into the buffer
+        saved_states[saved_states_count].file_offset = offset;
+        saved_states[saved_states_count].step = replay_get_current_step();
+        ++saved_states_count;
+        ++current_saved_state;
+    }
+    else
+    {
+        fprintf(stderr, "Cannot save simulator states for replay.\n");
+    }
+
+    replay_put_event(EVENT_SAVE_VM_END);
+
+    tb_flush_all();
+
+    vm_start();
+}
+
+
+static void replay_save_target_timer(void *opaque)
+{
+    struct tm datetime;
+
+    if (replay_mode != REPLAY_SAVE
+        || !replay_get_target_time)
+    {
+        return;
+    }
+
+    // get time
+    replay_get_target_time(replay_target_time_dev, &datetime);
+
+    replay_put_event(EVENT_TARGET_TIME);
+    replay_put_dword(datetime.tm_sec);
+    replay_put_dword(datetime.tm_min);
+    replay_put_dword(datetime.tm_hour);
+    replay_put_dword(datetime.tm_mday);
+    replay_put_dword(datetime.tm_mon);
+    replay_put_dword(datetime.tm_year);
+
+    time_t ticks = time(NULL);
+    struct tm *host = localtime(&ticks);
+    replay_put_dword(host->tm_sec);
+    replay_put_dword(host->tm_min);
+    replay_put_dword(host->tm_hour);
+    replay_put_dword(host->tm_mday);
+    replay_put_dword(host->tm_mon + 1);
+    replay_put_dword(host->tm_year + 1900);
+
+    timer_mod(target_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + target_timer_period);
+}
+
+unsigned long long prev_time = 0;
+
+static void replay_enable(const char *fname, int mode)
+{
+    const char *fmode = NULL;
+    if (replay_file)
+    {
+        fprintf(stderr, "some replay operation is already started\n");
+        return;
+    }
+
+    if (use_icount == 2) {
+        fprintf(stderr, "replay: icount auto mode is not tested yet\n");
+        exit(1);
+    }
+
+    switch (mode)
+    {
+    case REPLAY_SAVE:
+        fmode = "wb";
+        break;
+    case REPLAY_PLAY:
+        fmode = "rb";
+        play_submode = REPLAY_PLAY_NORMAL;
+        break;
+    default:
+        fprintf(stderr, "internal error: invalid replay mode\n");
+        exit(1);
+    }
+
+    atexit(replay_finish);
+
+    replay_file = fopen(fname, fmode);
+    if (replay_file == NULL)
+    {
+        fprintf(stderr, "open %s: %s\n", fname, strerror(errno));
+        exit(1);
+    }
+
+    replay_filename = g_strdup(fname);
+
+    // init variables
+    replay_mode = mode;
+    replay_has_unread_data = 0;
+    replay_data_kind = -1;
+    replay_state.skipping_instruction = 0;
+    replay_state.current_step = 0;
+    replay_break_step = -1;
+    current_saved_state = 0;
+
+    replay_net_init();
+
+    // skip file header for SAVE and check it for PLAY
+    if (replay_mode == REPLAY_SAVE)
+    {
+        fseek(replay_file, HEADER_SIZE, SEEK_SET);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        unsigned int version = replay_get_dword();
+        uint64_t offset = replay_get_qword();
+        if (version != REPLAY_VERSION)
+        {
+            fprintf(stderr, "Replay: invalid input log file version\n");
+            exit(1);
+        }
+        // read states table
+        fseeko64(replay_file, offset, SEEK_SET);
+        saved_states_count = replay_get_qword();
+        saved_states_capacity = saved_states_count;
+        if (saved_states_count)
+        {
+            saved_states = g_malloc(sizeof(SavedStateInfo) * saved_states_count);
+            fread(saved_states, sizeof(SavedStateInfo), saved_states_count, replay_file);
+        }
+        replay_net_read_packets_data();
+
+        // go to the beginning
+        fseek(replay_file, 12, SEEK_SET);
+    }
+
+    replay_init_events();
+
+    vmstate_register(NULL, 0, &vmstate_replay, &replay_state);
+
+    prev_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
+}
+
+
+void replay_configure(QemuOpts *opts, int mode)
+{
+    const char *fname;
+    
+    fname = qemu_opt_get(opts, "fname");
+    if (!fname)
+    {
+        fprintf(stderr, "File name not specified for replay\n");
+        exit(1);
+    }
+
+    const char *suffix = qemu_opt_get(opts, "suffix");
+    if (suffix)
+    {
+        replay_image_suffix = g_strdup(suffix);
+    }
+    else
+    {
+        replay_image_suffix = g_strdup("replay_qcow");
+    }
+    
+    save_state_period = 1000LL * qemu_opt_get_number(opts, "period", 0);
+    debug_mode = qemu_opt_get_bool(opts, "debug", 0);
+
+    replay_icount = (int)qemu_opt_get_number(opts, "icount", 0);
+    if (replay_icount)
+    {
+        replay_configure_icount();
+    }
+    
+    if (mode == REPLAY_SAVE)
+    {
+        target_timer_period = 1000LL * qemu_opt_get_number(opts, "rtc", 0);
+    }
+
+    replay_enable(fname, mode);
+}
+
+
+void replay_init_timer(void)
+{
+    replay_enable_events();
+
+    /* create timer for states auto-saving */
+    if (replay_mode == REPLAY_SAVE)
+    {
+        saved_states_count = 0;
+        if (!saved_states)
+        {
+            saved_states = g_malloc(sizeof(SavedStateInfo) * SAVED_STATES_MIN_CAPACITY);
+            saved_states_capacity = SAVED_STATES_MIN_CAPACITY;
+        }
+        if (save_state_period)
+        {
+            save_timer = timer_new_ms(QEMU_CLOCK_REALTIME, replay_savevm, NULL);
+            timer_mod(save_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
+        }
+        replay_put_event(EVENT_END_STARTUP);
+        /* Save it right now without waiting for timer */
+        replay_savevm(NULL);
+
+        // periodic saving of target time into the log
+        if (target_timer_period)
+        {
+            target_timer = timer_new_ms(QEMU_CLOCK_REALTIME, replay_save_target_timer, NULL);
+            timer_mod(target_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME));
+        }
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        // load VM state by seeking to step 0
+        replay_state.current_step = -1;
+        replay_seek_step(0);
+        replay_set_break(-1);
+    }
+}
+
+
+void replay_change(struct Monitor *mon)
+{
+    if (play_submode == REPLAY_PLAY_NORMAL)
+    {
+        // cannot use breakpoints in this mode
+        replay_break_step = -1;
+        // init clock offsets to make them smooth
+        realtime_clock_replay_offset = get_clock_realtime() - get_clock_realtime_impl();
+        real_ticks_replay_offset = cpu_get_real_ticks() - cpu_get_real_ticks_impl();
+        // switch play mode
+        tb_flush_all();
+        play_submode = REPLAY_PLAY_CHANGED;
+        replay_flush_events();
+        replay_sound_flush_queue();
+    }
+    else if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        if (mon)
+            monitor_printf(mon, "Execute replay_seek to the specified state before starting
replay_change.\n");
+        return;
+    }
+    else
+    {
+        fprintf(stderr, "Cannot start replay_change\n");
+        exit(1);
+    }
+}
+
+
+
+//! Closes replay file.
+void replay_finish(void)
+{
+    if (replay_mode == REPLAY_NONE)
+        return;
+    replay_save_instructions();
+
+    // finalize the file
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_SAVE)
+        {
+            uint64_t offset;
+            // write end event
+            replay_put_event(EVENT_END);
+
+            // write states table
+            offset = ftello64(replay_file);
+            replay_put_qword(saved_states_count);
+            if (saved_states && saved_states_count)
+            {
+                // TODO ??? Save distinct fields in the loop
+                fwrite(saved_states, sizeof(SavedStateInfo), saved_states_count, replay_file);
+            }
+            replay_net_write_packets_data();
+
+            // write header
+            fseek(replay_file, 0, SEEK_SET);
+            replay_put_dword(REPLAY_VERSION);
+            replay_put_qword(offset);
+        }
+
+        // close file
+        fclose(replay_file);
+        replay_file = NULL;
+    }
+
+    replay_net_free();
+
+    // deallocate everything
+    if (save_timer)
+    {
+        timer_del(save_timer);
+        timer_free(save_timer);
+        save_timer = NULL;
+    }
+    if (target_timer)
+    {
+        timer_del(target_timer);
+        timer_free(target_timer);
+        target_timer = NULL;
+    }
+    if (saved_states)
+    {
+        g_free(saved_states);
+        saved_states = NULL;
+    }
+
+    if (replay_filename)
+    {
+        g_free(replay_filename);
+        replay_filename = NULL;
+    }
+
+    if (replay_image_suffix)
+    {
+        g_free(replay_image_suffix);
+        replay_image_suffix = NULL;
+    }
+
+    replay_finish_events();
+}
+
+
+
+//! Saves the value of the desired clock into the output file.
+void replay_save_clock(unsigned int kind, int64_t clock)
+{
+    replay_save_instructions();
+
+    if (kind >= REPLAY_CLOCK_COUNT)
+    {
+        fprintf(stderr, "invalid clock ID %d for replay\n", kind);
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        replay_put_event(EVENT_CLOCK + kind);
+        replay_put_qword(clock);
+    }
+}
+
+
+//! Reads next clock value from the file.
+//! If clock kind read from the file is different from the parameter,
+//! the value is not used.
+//! If the parameter is -1, the clock value is read to the cache anyway.
+static void replay_read_next_clock(unsigned int kind)
+{
+    replay_fetch_data_kind();
+    if (replay_file)
+    {
+        unsigned int read_kind = replay_data_kind - EVENT_CLOCK;
+
+        if (kind != -1 && read_kind != kind)
+        {
+            return;
+        }
+        if (read_kind >= REPLAY_CLOCK_COUNT)
+        {
+            fprintf(stderr, "invalid clock ID %d was read from replay\n", read_kind);
+            exit(1);
+        }
+
+        int64_t clock = replay_get_qword();
+
+        replay_check_error();
+        replay_has_unread_data = 0;
+
+        replay_state.cached_clock[read_kind] = clock;
+    }
+}
+
+
+//! Checks SAVEVM event while reading event log. 
+static void check_savevm(void)
+{
+    replay_fetch_data_kind();
+    if (replay_data_kind != EVENT_SAVE_VM_BEGIN && replay_data_kind != EVENT_SAVE_VM_END)
+    {
+        fprintf(stderr, "read wrong data kind %d within savevm\n", replay_data_kind);
+        exit(1);
+    }
+    replay_has_unread_data = 0;
+}
+
+//! Skips clock events saved to file while saving the VM state.
+static void replay_skip_savevm(void)
+{
+    char name[128];
+
+    replay_has_unread_data = 0;
+
+    if (runstate_is_running())
+    {
+        vm_stop(RUN_STATE_RESTORE_VM);
+    }
+    else
+    {
+        // Read saved timers from event log
+        cpu_disable_ticks();
+        // Read checkpoints
+        while (replay_checkpoint(8)) {
+            /* Nothing */
+        }
+    }
+
+    ++current_saved_state;
+    sprintf(name, SAVED_STATE_NAME_FORMAT, current_saved_state);
+    load_vmstate(name);
+
+    // check the closing event
+    check_savevm();
+
+    tb_flush_all();
+
+    // Read saved timers from event log
+    cpu_enable_ticks();
+
+    vm_start();
+}
+
+//! Skips async events until some sync event will be found.
+static int skip_async_events(int stop_event)
+{
+    /* nothing to skip - not all instructions used 
+       or timer is not processed yet*/
+    if (first_cpu != NULL && first_cpu->instructions_count != 0 && replay_has_unread_data)
+    {
+        return 0;
+    }
+
+    int skipping = 1;
+    while (skipping)
+    {
+        replay_fetch_data_kind();
+        if (stop_event == replay_data_kind)
+            skipping = 0;
+        switch (replay_data_kind)
+        {
+        case EVENT_END:
+            fprintf(stderr, "Replay log file was successfully played\n");
+            exit(1);
+            break;
+        case EVENT_SOUND_OUT:
+#ifdef _WIN32
+            replay_read_sound_out();
+#endif
+            break;
+        case EVENT_SOUND_IN:
+#ifdef _WIN32
+            replay_read_sound_in();
+#endif
+            break;
+        case EVENT_SAVE_VM_BEGIN:
+            // cannot correctly load VM while in CPU thread
+            if (qemu_in_vcpu_thread())
+            {
+                skipping = 0;
+                break;
+            }
+            replay_skip_savevm();
+            break;
+        case EVENT_SHUTDOWN:
+            replay_has_unread_data = 0;
+            qemu_system_shutdown_request_impl();
+            break;
+        case EVENT_TARGET_TIME:
+            replay_has_unread_data = 0;
+            // target time
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            // host time
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            replay_get_dword();
+            break;
+        case EVENT_INSTRUCTION:
+            first_cpu->instructions_count = replay_get_dword();
+            skipping = 0;
+            break;
+        default:
+            // clock, time_t, checkpoint and other events
+            skipping = 0;
+            break;
+        }
+    }
+    
+    return 0;
+}
+
+
+
+//! Skips async events invocations from the input,
+//! until required data kind is found.
+static int skip_async_events_until(unsigned int kind)
+{
+    while (true)
+    {
+        if (skip_async_events(kind))
+        {
+            return 1;
+        }
+
+        if (replay_data_kind != kind)
+        {
+            // TODO: fix this
+            if (replay_data_kind == EVENT_ASYNC && kind == EVENT_INSTRUCTION)
+            {
+                return 0;
+            }
+
+            fprintf(stderr, "%" PRId64 ": read data kind %d instead of expected %d\n", 
+                replay_get_current_step(), replay_data_kind, kind);
+            exit(1);
+        }
+        break;
+    }
+
+    return 0;
+}
+
+
+void replay_assert(uint64_t value, const char *message)
+{
+    if (!replay_file || !debug_mode)
+        return;
+
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+
+        replay_put_event(EVENT_ASSERT);
+        replay_put_qword(value);
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+    
+        if (skip_async_events_until(EVENT_ASSERT))
+        {
+            // No assert was written here
+            // TODO: do not exit?
+            fprintf(stderr, "Cannot read assert\n");
+            exit(1);
+        }
+
+        int64_t val = replay_get_qword();
+
+        replay_check_error();
+        replay_has_unread_data = 0;
+
+        if (value != val)
+        {
+            fprintf(stderr, "%" PRId64 ": invalid assert: read %" PRIx64 " instead of expected %"
PRIx64 " (%s)\n", 
+                replay_get_current_step(), val, value, message ? message : "---");
+            exit(1);
+        }
+    }
+}
+
+
+
+//! Reads next clock event from the input.
+int64_t replay_read_clock(unsigned int kind)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        fprintf(stderr, "Cannot read log in replay run mode\n");
+        exit(1);
+    }
+
+    if (kind >= REPLAY_CLOCK_COUNT)
+    {
+        fprintf(stderr, "invalid clock ID %d for replay\n", kind);
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        if (!skip_async_events(EVENT_CLOCK + kind))
+        {
+            if (replay_data_kind == EVENT_CLOCK + kind)
+            {
+                replay_read_next_clock(kind);
+            }
+        }
+        int64_t ret = replay_state.cached_clock[kind];
+        
+        return ret;
+    }
+
+    fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__);
+    exit(1);
+}
+
+int replay_has_timer_request(void)
+{
+    if (replay_state.skipping_instruction)
+    {
+        return 0;
+    }
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+            return 1;
+
+        skip_async_events(EVENT_ASYNC);
+        if (replay_data_kind == EVENT_ASYNC)
+            return 1;
+
+        {
+            if (replay_get_current_step() == replay_break_step)
+                return 1;
+
+            return 0;
+        }
+    }
+    else if (replay_mode == REPLAY_SAVE)
+    {
+        if (replay_has_events())
+        {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+void replay_write_interrupt_request(void)
+{
+    replay_save_instructions();
+    replay_put_event(EVENT_INTERRUPT);
+}
+
+
+int replay_read_interrupt_request(void)
+{
+    // try to read event
+    if (replay_mode == REPLAY_PLAY)
+    {
+        skip_async_events(EVENT_INTERRUPT);
+        if (replay_data_kind == EVENT_INTERRUPT)
+        {
+        }
+    }
+    int ret = replay_data_kind == EVENT_INTERRUPT;
+    
+    return ret;
+}
+
+void replay_reset_interrupt_request(void)
+{
+    if (replay_data_kind == EVENT_INTERRUPT)
+    {
+        replay_has_unread_data = 0;
+    }
+}
+
+int replay_interrupt_request(void)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_write_interrupt_request();
+        return 1;
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+        
+        if (replay_read_interrupt_request())
+        {
+            replay_reset_interrupt_request();
+            return 1;
+        }
+        else
+        {
+            // no action
+            // wait here in outer loop
+        }
+
+        return 0;
+    }
+
+    return 1;
+}
+
+
+bool replay_exception(void)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+        replay_put_event(EVENT_EXCEPTION);
+        return true;
+    }
+    else if (replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+            return true;
+    
+        skip_async_events(EVENT_EXCEPTION);
+        if (replay_data_kind == EVENT_EXCEPTION)
+        {
+            replay_has_unread_data = 0;
+            return true;
+        }
+        return false;
+    }
+
+    return true;
+}
+
+
+
+void replay_shutdown_request(void)
+{
+    if (replay_mode == REPLAY_SAVE)
+    {
+        replay_put_event(EVENT_SHUTDOWN);
+    }
+}
+
+bool replay_has_code(void)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+        return true;
+
+    if (replay_has_instruction())
+        return true;
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        skip_async_events(EVENT_INTERRUPT);
+        if (replay_data_kind == EVENT_INTERRUPT)
+            return true;
+        skip_async_events(EVENT_EXCEPTION);
+        if (replay_data_kind == EVENT_EXCEPTION)
+            return true;
+        return false;
+    }
+
+    return true;
+}
+
+int replay_has_instruction(void)
+{
+    int res = 1;
+    if (replay_state.skipping_instruction)
+    {
+        return 1;
+    }
+
+    if (replay_mode == REPLAY_PLAY)
+    {
+        skip_async_events(EVENT_INSTRUCTION);
+        if (replay_data_kind != EVENT_INSTRUCTION
+            && replay_data_kind != EVENT_ASYNC)
+        {
+            res = 0;
+        }
+    }
+    return res;
+}
+
+
+void replay_instruction(int process_timer)
+{
+    if (replay_state.skipping_instruction) {
+        replay_state.skipping_instruction = 0;
+
+        return;
+    }
+
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_SAVE)
+        {
+            if (replay_has_sound_events())
+            {
+                replay_save_instructions();
+            }
+
+            if (process_timer && replay_has_events())
+            {
+                replay_save_instructions();
+                // events will be after the last instruction
+                replay_save_events(-1);
+            }
+            else
+            {
+                // instruction - increase the step counter
+                ++first_cpu->instructions_count;
+            }
+        }
+        else if (replay_mode == REPLAY_PLAY)
+        {
+            if (play_submode == REPLAY_PLAY_CHANGED)
+            {
+                replay_state.current_step += first_cpu->instructions_count;
+                first_cpu->instructions_count = 0;
+                return;
+            }
+        
+            if (skip_async_events_until(EVENT_INSTRUCTION))
+            {
+                // execution should be stopped in changed replay mode
+                return;
+            }
+            // No timer is expected right now
+            if (first_cpu->instructions_count >= 1)
+            {
+                // stop only at instruction
+                if (replay_get_current_step() == replay_break_step)
+                {
+                    replay_break_step = -1;
+
+                    // for stopping VM
+                    if (play_submode == REPLAY_PLAY_NORMAL)
+                    {
+                        first_cpu->exception_index = EXCP_DEBUG;
+                        monitor_printf(default_mon, "Execution has stopped.\n");
+                        vm_stop(EXCP_DEBUG);
+                    }
+                    else if (play_submode == REPLAY_PLAY_GOTO)
+                    {
+                        play_submode = REPLAY_PLAY_NORMAL;
+                        cpu_handle_debug_exception(first_cpu->env_ptr);
+                    }
+                    else if (play_submode == REPLAY_PLAY_REVERSE)
+                    {
+						cpu_handle_debug_exception(first_cpu->env_ptr);
+                        return;
+                    }
+                    // for breaking execution loop
+                    cpu_exit(first_cpu);
+                    return;
+                }
+
+                ++replay_state.current_step;
+                --first_cpu->instructions_count;
+                if (first_cpu->instructions_count == 0)
+                    replay_has_unread_data = 0;
+            }
+            else
+            {
+                replay_read_events(-1);
+            }
+        }
+    }
+}
+
+
+bool replay_has_checkpoint(unsigned int checkpoint)
+{
+    if (replay_file && replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+            return true;
+    
+        if (skip_async_events(EVENT_CHECKPOINT + checkpoint))
+        {
+            return false;
+        }
+        if (replay_data_kind == EVENT_CHECKPOINT + checkpoint)
+        {
+            return true;
+        }
+        // not checking the checkpoint id
+        // assume that this function is called at the right place
+        if (replay_data_kind == EVENT_ASYNC_OPT)
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+//! Should be called at optional check points in the execution.
+//! These check points are skipped, if they were not meet.
+//! Saves checkpoint in the SAVE mode and validates (skipping
+//! the interrupts) in the PLAY mode.
+//! Returns 0 in PLAY mode if checkpoint was not found.
+//! Returns 1 in all other cases.
+//! Used checkpoints: 0 1 2 3 5 6 7 8 9
+int replay_checkpoint(unsigned int checkpoint)
+{
+    replay_save_instructions();
+
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_PLAY)
+        {
+            if (play_submode == REPLAY_PLAY_CHANGED)
+                return 1;
+            if (skip_async_events(EVENT_CHECKPOINT + checkpoint))
+            {
+                return 0;
+            }
+            if (replay_data_kind != EVENT_CHECKPOINT + checkpoint)
+            {
+                if (replay_data_kind == EVENT_ASYNC_OPT)
+                {
+                    replay_read_events(checkpoint);
+                    replay_fetch_data_kind();
+                    return replay_data_kind != EVENT_ASYNC_OPT;
+                }
+                return 0;
+            }
+            replay_has_unread_data = 0;
+            replay_read_events(checkpoint);
+            replay_fetch_data_kind();
+            return replay_data_kind != EVENT_ASYNC_OPT;
+        }
+        else if (replay_mode == REPLAY_SAVE)
+        {
+            replay_put_event(EVENT_CHECKPOINT + checkpoint);
+            replay_save_events(checkpoint);
+        }
+    }
+
+    return 1;
+}
+
+
+void replay_save_time_t(time_t tm)
+{
+    replay_save_instructions();
+
+    if (replay_file)
+    {
+        replay_put_event(EVENT_TIME_T);
+        if (sizeof(tm) == 4)
+        {
+            replay_put_dword(tm);
+        }
+        else if (sizeof(tm) == 8)
+        {
+            replay_put_qword(tm);
+        }
+        else
+        {
+            fprintf(stderr, "invalid time_t sizeof: %u\n", (unsigned)sizeof(tm));
+            exit(1);
+        }
+    }
+}
+
+time_t replay_read_time_t(void)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        fprintf(stderr, "Cannot read log in replay run mode\n");
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        time_t tm;
+
+        if (skip_async_events_until(EVENT_TIME_T))
+        {
+            // TODO: return cached value
+            fprintf(stderr, "Cannot read time_t when stopped\n");
+            exit(1);
+        }
+
+        if (sizeof(tm) == 4)
+        {
+            tm = replay_get_dword();
+        }
+        else if (sizeof(tm) == 8)
+        {
+            tm = replay_get_qword();
+        }
+        else
+        {
+            fprintf(stderr, "invalid time_t sizeof: %u\n", (unsigned)sizeof(tm));
+            exit(1);
+        }
+
+        replay_check_error();
+
+        replay_has_unread_data = 0;
+
+        return tm;
+    }
+
+    fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__);
+    exit(1);
+}
+
+void replay_save_tm(struct tm *tm)
+{
+    replay_save_instructions();
+
+    if (replay_file)
+    {
+        replay_put_event(EVENT_TM);
+
+        replay_put_dword(tm->tm_sec);
+        replay_put_dword(tm->tm_min);
+        replay_put_dword(tm->tm_hour);
+        replay_put_dword(tm->tm_mday);
+        replay_put_dword(tm->tm_mon);
+        replay_put_dword(tm->tm_year);
+        replay_put_dword(tm->tm_wday);
+        replay_put_dword(tm->tm_yday);
+        replay_put_dword(tm->tm_isdst);
+    }
+}
+
+void replay_read_tm(struct tm *tm)
+{
+    if (play_submode == REPLAY_PLAY_CHANGED)
+    {
+        fprintf(stderr, "Cannot read log in replay run mode\n");
+        exit(1);
+    }
+
+    if (replay_file)
+    {
+        if (skip_async_events_until(EVENT_TM))
+        {
+            return;
+        }
+
+        tm->tm_sec = replay_get_dword();
+        tm->tm_min = replay_get_dword();
+        tm->tm_hour = replay_get_dword();
+        tm->tm_mday = replay_get_dword();
+        tm->tm_mon = replay_get_dword();
+        tm->tm_year = replay_get_dword();
+        tm->tm_wday = replay_get_dword();
+        tm->tm_yday = replay_get_dword();
+        tm->tm_isdst = replay_get_dword();
+
+        replay_check_error();
+        replay_has_unread_data = 0;
+
+        return;
+    }
+
+    fprintf(stderr, "REPLAY INTERNAL ERROR %d\n", __LINE__);
+    exit(1);
+}
+
+
+
+void replay_undo_last_instruction(void)
+{
+    if (replay_mode == REPLAY_SAVE
+        || play_submode == REPLAY_PLAY_CHANGED)
+    {
+        first_cpu->instructions_count--;
+    }
+    else
+    {
+        replay_state.skipping_instruction = 1;
+    }
+}
+
+
+uint64_t replay_get_current_step(void)
+{
+    if (first_cpu == NULL)
+        return 0;
+    if (replay_file)
+    {
+        if (replay_mode == REPLAY_SAVE)
+        {
+            return replay_state.current_step + first_cpu->instructions_count;
+        }
+    }
+    return replay_state.current_step;
+}
+
+
+void replay_set_break(uint64_t step)
+{
+    replay_break_step = step;
+}
+
+
+SavedStateInfo *find_nearest_state(uint64_t step)
+{
+    SavedStateInfo *first = saved_states;
+    SavedStateInfo *last = saved_states + saved_states_count;
+    while (first < last - 1)
+    {
+        SavedStateInfo *next = first + (last - first) / 2;
+        if (next->step > step)
+            last = next;
+        else
+            first = next;
+    }
+
+    return first;
+}
+
+
+int replay_seek_step(uint64_t step)
+{
+    if (replay_mode != REPLAY_PLAY)
+        return 0;
+
+    // load VM state, if possible
+    if (saved_states_count > 0)
+    {
+        // find VM state to load
+        SavedStateInfo *first = find_nearest_state(step);
+
+        if (first->step <= step 
+            && (replay_get_current_step() > step 
+                || replay_get_current_step() < first->step
+                // always load the state, if something was changed
+                || play_submode == REPLAY_PLAY_CHANGED))
+        {
+            char name[128];
+            bool running = runstate_is_running();
+            if (running && !qemu_in_vcpu_thread())
+            {
+                vm_stop(RUN_STATE_RESTORE_VM);
+            }
+            else
+            {
+                cpu_disable_ticks();
+            }
+            
+            replay_clear_events();
+            replay_sound_flush_queue();
+
+            // reset the mode before loading to allow correct timers operation
+            if (/*play_submode == REPLAY_PLAY_STOPPED
+                || */play_submode == REPLAY_PLAY_CHANGED)
+            {
+                play_submode = REPLAY_PLAY_NORMAL;
+            }
+            
+            current_saved_state = first - saved_states;
+            sprintf(name, SAVED_STATE_NAME_FORMAT, current_saved_state);
+            if (load_vmstate(name) < 0)
+            {
+                fprintf(stderr, "Replay: cannot load VM state while seeking\n");
+                exit(1);
+            }
+            // check end event
+            check_savevm();
+
+            tb_flush_all();
+
+            cpu_enable_ticks();
+            if (running && !qemu_in_vcpu_thread()) 
+            {
+                vm_start();
+            }
+
+            replay_fetch_data_kind();
+            while (replay_data_kind >= EVENT_CLOCK && replay_data_kind < EVENT_CLOCK +
REPLAY_CLOCK_COUNT)
+            {
+                replay_read_next_clock(-1);
+                replay_fetch_data_kind();
+            }
+        }
+    }
+
+    // setup the breakpoint
+    if (step >= replay_get_current_step())
+    {
+        replay_set_break(step);
+        return 1;
+    }
+
+    return 0;
+}
+
+
+
+void replay_goto_step(uint64_t step)
+{
+    if (replay_seek_step(step))
+    {
+        play_submode = REPLAY_PLAY_GOTO;
+    }
+}
+
+
+void replay_events(struct Monitor *mon)
+{
+    if (replay_mode == REPLAY_PLAY && replay_file) 
+    {
+        int ok = 1;
+        // becomes 1 when grey key is pressed or released
+        uint64_t offset = ftello64(replay_file);
+        uint64_t step = 0;
+        fseek(replay_file, HEADER_SIZE, SEEK_SET);
+
+        // read whole file and print the events
+        while (ok && !feof(replay_file)) 
+        {
+            uint8_t data;
+            size_t size;
+            fread(&data, sizeof(data), 1, replay_file);
+            switch (data) {
+            case EVENT_END_STARTUP:
+                break;
+            case EVENT_TIME_T:
+                fseek(replay_file, sizeof(time_t), SEEK_CUR);
+                break;
+            case EVENT_TM:
+                fseek(replay_file, sizeof(uint32_t) * 9, SEEK_CUR);
+                break;
+            case EVENT_SOUND_OUT:
+                fseek(replay_file, sizeof(uint32_t), SEEK_CUR);
+                break;
+            case EVENT_SOUND_IN:
+                fread(&size, sizeof(size), 1, replay_file);
+                fseek(replay_file, size, SEEK_CUR);
+                break;
+            case EVENT_SAVE_VM_BEGIN:
+                monitor_printf(mon, "%" PRId64 ": VM state\n", step);
+                break;
+            case EVENT_SAVE_VM_END:
+                break;
+            case EVENT_INTERRUPT:
+            case EVENT_EXCEPTION:
+                break;
+            case EVENT_SHUTDOWN:
+                monitor_printf(mon, "%" PRId64 ": Request for closing simulator window\n", step);
+                break;
+            case EVENT_ASSERT:
+                fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+                break;
+            case EVENT_TARGET_TIME:
+                {
+                    int32_t sec, min, hour, day, month, year,
+                        hsec, hmin, hhour, hday, hmonth, hyear;
+                    sec = replay_get_dword();
+                    min = replay_get_dword();
+                    hour = replay_get_dword();
+                    day = replay_get_dword();
+                    month = replay_get_dword();
+                    year = replay_get_dword();
+                    hsec = replay_get_dword();
+                    hmin = replay_get_dword();
+                    hhour = replay_get_dword();
+                    hday = replay_get_dword();
+                    hmonth = replay_get_dword();
+                    hyear = replay_get_dword();
+                    monitor_printf(mon, "%" PRId64 ": Target time %d.%02d.%02d-%02d:%02d:%02d  Host
time %d.%02d.%02d-%02d:%02d:%02d\n", 
+                        step, year, month, day, hour, min, sec, hyear, hmonth, hday, hhour, hmin,
hsec);
+                }
+                break;
+            case EVENT_INSTRUCTION:
+                {
+                    uint32_t count;
+                    fread(&count, sizeof(count), 1, replay_file);
+                    step += count;
+                }
+                break;
+            case EVENT_DATA_INT:
+                fseek(replay_file, sizeof(uint32_t), SEEK_CUR);
+                break;
+            case EVENT_DATA_BUFFER:
+                fread(&size, sizeof(size), 1, replay_file);
+                fseek(replay_file, size, SEEK_CUR);
+                break;
+            case EVENT_END:
+                ok = 0;
+                break;
+            case EVENT_ASYNC:
+            case EVENT_ASYNC_OPT:
+                replay_skip_async_event(mon, step, data == EVENT_ASYNC_OPT);
+                break;
+            default:
+                if (data >= EVENT_CLOCK && data < EVENT_CHECKPOINT)
+                {
+                    fseek(replay_file, sizeof(uint64_t), SEEK_CUR);
+                }
+                else if (data >= EVENT_CHECKPOINT && data < EVENT_END)
+                {
+                    // skip
+                }
+                else
+                {
+                    ok = 0;
+                    monitor_printf(mon, "%" PRId64 ": Found unknown event %d\n", step, data);
+                }
+                break;
+            }
+        }
+        fseeko64(replay_file, offset, SEEK_SET);
+    }
+}
+
+
+int replay_xtoi(char c)
+{
+    switch (c)
+    {
+    case 'a':
+    case 'A':
+        return 10;
+    case 'b':
+    case 'B':
+        return 11;
+    case 'c':
+    case 'C':
+        return 12;
+    case 'd':
+    case 'D':
+        return 13;
+    case 'e':
+    case 'E':
+        return 14;
+    case 'f':
+    case 'F':
+        return 15;
+    default:
+        if (isdigit(c))
+            return c - '0';
+
+        return 0;
+    }
+
+    return 0;
+}
+
+int64_t replay_get_file_offset(void)
+{
+    return (int64_t) ftello64(replay_file);
+}
+
+void replay_register_get_time(replay_get_date_func f, void *opaque)
+{
+    if (replay_mode != REPLAY_NONE)
+    {
+        replay_get_target_time = f;
+        replay_target_time_dev = opaque;
+    }
+}
+
+void replay_data_int(int *data)
+{
+    if (replay_file && replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+        skip_async_events(EVENT_DATA_INT);
+        validate_data_kind(EVENT_DATA_INT);
+        *data = replay_get_dword();
+        replay_check_error();
+        replay_has_unread_data = 0;
+    }
+    else if (replay_file && replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+        replay_put_event(EVENT_DATA_INT);
+        replay_put_dword(*data);
+    }
+}
+
+void replay_data_buffer(unsigned char *data, size_t size)
+{
+    if (replay_file && replay_mode == REPLAY_PLAY)
+    {
+        if (play_submode == REPLAY_PLAY_CHANGED)
+        {
+            fprintf(stderr, "Cannot read log in replay run mode\n");
+            exit(1);
+        }
+        size_t read_size = 0;
+        skip_async_events(EVENT_DATA_BUFFER);
+        validate_data_kind(EVENT_DATA_BUFFER);
+        replay_get_array(data, &read_size);
+        replay_check_error();
+        if (read_size != size)
+        {
+            fprintf(stderr, "Replay: read non-matching size of the buffer\n");
+            exit(1);
+        }
+        replay_has_unread_data = 0;
+    }
+    else if (replay_file && replay_mode == REPLAY_SAVE)
+    {
+        replay_save_instructions();
+        replay_put_event(EVENT_DATA_BUFFER);
+        replay_put_array(data, size);
+    }
+}
+
diff --git a/replay/replay.h b/replay/replay.h
new file mode 100644
index 0000000..fc72a92
--- /dev/null
+++ b/replay/replay.h
@@ -0,0 +1,249 @@
+#ifndef REPLAY_H
+#define REPLAY_H
+
+#include <time.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+#include "qemu/option_int.h"
+#include "qemu/typedefs.h"
+
+struct QemuOpts;
+struct VncDisplay;
+struct Monitor;
+struct ISADevice;
+typedef struct CharDriverState CharDriverState;
+struct libusb_transfer;
+
+/* replay modes */
+#define REPLAY_NONE 0
+#define REPLAY_SAVE 1
+#define REPLAY_PLAY 2
+/* replay submodes */
+#define REPLAY_PLAY_UNKNOWN 0
+/* Normal log replaying */
+#define REPLAY_PLAY_NORMAL  1
+/* Replaying for reverse execution */
+#define REPLAY_PLAY_REVERSE 2
+/* Running from the some starting point in the replay mode
+   or after changing registers, memory, or network packet.  */
+#define REPLAY_PLAY_CHANGED 3
+/* Replaying for seeking to the specified step of the trace */
+#define REPLAY_PLAY_GOTO    4
+
+/* replay clock kinds */
+/* rdtsc */
+#define REPLAY_CLOCK_REAL_TICKS 0
+/* host_clock */
+#define REPLAY_CLOCK_REALTIME   1
+/* vm_clock */
+#define REPLAY_CLOCK_VIRTUAL    2
+
+#define REPLAY_CLOCK_COUNT      3
+
+/* replay async event kinds */
+#define REPLAY_ASYNC_EVENT_NOTIFIER    1
+#define REPLAY_ASYNC_EVENT_NETWORK     2
+#define REPLAY_ASYNC_EVENT_THREAD      3
+#define REPLAY_ASYNC_EVENT_BH          4
+#define REPLAY_ASYNC_EVENT_USB_CTRL    5
+#define REPLAY_ASYNC_EVENT_USB_DATA    6
+#define REPLAY_ASYNC_EVENT_USB_ISO     7
+#define REPLAY_ASYNC_EVENT_CHAR        8
+#define REPLAY_ASYNC_EVENT_INPUT       9
+#define REPLAY_ASYNC_EVENT_INPUT_SYNC  10
+
+#define REPLAY_ASYNC_COUNT             11
+
+extern int replay_mode;
+extern char *replay_image_suffix;
+/*! Shift value for icount based on replay or zero, if it is disabled. */
+extern int replay_icount;
+extern int not_compatible_replay_param;
+extern uint64_t replay_network_packets;
+
+/*! Returns replay play submode */
+int replay_get_play_submode(void);
+/*! Enter "something changed" replay submode */
+void replay_set_play_changed(void);
+void replay_init_overlay(void);
+/*! Enables recording or saving event log with specified parameters */
+void replay_configure(struct QemuOpts *opts, int mode);
+//void replay_enable(const char *fname, int mode);
+void replay_init_timer(void);
+void replay_add_network_client(struct NetClientState *nc);
+/*! Retrieves name of the file, which is used for recording
+    or replaying execution log. */
+const char *replay_get_filename(void);
+int64_t replay_get_file_offset(void);
+
+//! Puts debug information into the log in save mode
+//! and checks this information in play mode.
+void replay_assert(uint64_t value, const char *message);
+
+void replay_save_clock(unsigned int kind, int64_t clock);
+int64_t replay_read_clock(unsigned int kind);
+
+//! Called to write software interrupt event into the log.
+//! This event enables breaking the translation block execution.
+void replay_write_interrupt_request(void);
+//! Tries to read interrupt event from the file 
+//! and sets interrupt pending flag if the event was read.
+//! Returns non-zero, when interrupt request is pending.
+int replay_read_interrupt_request(void);
+//! Resets interrupt request flag.
+void replay_reset_interrupt_request(void);
+//! Called by interrupt handlers to write or read
+//! interrupt processing events.
+//! \return non-zero if interrupt should be processed
+int replay_interrupt_request(void);
+
+//! Called by exception handler to write or read
+//! exception processing events.
+bool replay_exception(void);
+
+int replay_has_timer_request(void);
+
+//! Called when qemu shutdown is requested.
+void replay_shutdown_request(void);
+
+//! Closes replay file.
+void replay_finish(void);
+
+//! Returns non-zero if next event is instruction.
+int replay_has_instruction(void);
+//! Returns true if next event can be processed by cpu-exec.
+bool replay_has_code(void);
+//! Should be called at check points in the execution.
+//! Saves instructions count in the SAVE mode and validates (skipping
+//! the interrupts) in the PLAY mode.
+//! \param process_timer Contains non-zero value, when timer requests should be processed.
+//!                      This parameter is not taken into account in PLAY mode.
+void replay_instruction(int process_timer);
+bool replay_has_checkpoint(unsigned int checkpoint);
+int replay_checkpoint(unsigned int checkpoint);
+
+void replay_save_time_t(time_t tm);
+time_t replay_read_time_t(void);
+
+void replay_save_tm(struct tm *tm);
+void replay_read_tm(struct tm *tm);
+
+void replay_save_net_packet(NetClientState *nc, const uint8_t *buf, size_t size);
+
+/* Sound functions may be called from another thread */
+#ifdef _WIN32
+void replay_sound_in_event(WAVEHDR *hdr);
+/*! Adds header to the queue.
+    In SAVE mode this header is queued for saving into log.
+    In PLAY mode this header is queued for reading from log.
+    Returns 1 in PLAY mode when queue is full.
+    Otherwise returns 0.
+*/
+int replay_sound_out_event(WAVEHDR *hdr);
+//! Initializes queue for sound input.
+void replay_init_sound_in(void *instance, WAVEHDR *hdrs, int sz);
+//! Initializes queue for sound output.
+void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz);
+#endif
+
+void replay_undo_last_instruction(void);
+
+/* Functions to retrieve information about replay process.
+   Used by monitor module. */
+
+/*! Returns number of passed instructions and timer events. */
+uint64_t replay_get_current_step(void);
+/*! Sets step where execution should be stopped. */
+void replay_set_break(uint64_t step);
+/*! Seeks to the specified step. 
+    Loads VM state, if possible, and sets break to specified step.
+    Returns nonzero, when seeking was successful.
+    */
+int replay_seek_step(uint64_t step);
+/*! Calls replay_seek_step and switches to REPLAY_PLAY_GOTO mode. */
+void replay_goto_step(uint64_t step);
+
+/*! Prints events from the log. */
+void replay_events(struct Monitor *mon);
+/*! Prints desired network packet contents. 
+    Returns non-zero, if packet was successfully printed.
+ */
+int replay_get_packet(struct Monitor *mon, int64_t id);
+/*! Adds desired packet to the list of the "modified" packets. 
+    When replaying, packet with the specified id read from the file
+    will be replaced with packet, added with this command.
+    If bytes is null, modified packet with desired id will be 
+    removed from the list.
+    Returns non-zero, if packet was successfully added or removed.
+ */
+int replay_set_packet(struct Monitor *mon, int64_t id, const char *bytes);
+/* utility function */
+int replay_xtoi(char);
+
+/*! Initializes reverse continue execution.
+    Called by debugger module. */
+void replay_reverse_continue(void);
+/*! Initializes reverse step execution.
+    Called by debugger module. */
+void replay_reverse_step(void);
+/* Called instead of invoking breakpoint in reverse continue mode. */
+void replay_reverse_breakpoint(void);
+
+/* Save and load USB events */
+/*! Called in save mode when xfer for ctrl usb data is completed */
+void replay_req_complete_ctrl(struct libusb_transfer *xfer);
+/*! Called in play mode to register xfer to be completed by reading it from the log */
+void replay_req_register_ctrl(struct libusb_transfer *xfer);
+/*! Called in save mode when xfer for usb data request is completed */
+void replay_req_complete_data(struct libusb_transfer *xfer);
+/*! Called in play mode to register xfer to be completed by reading it from the log */
+void replay_req_register_data(struct libusb_transfer *xfer);
+/*! Called in save mode when iso xfer for usb data request is completed */
+void replay_req_complete_iso(struct libusb_transfer *xfer);
+/*! Called in play mode to register iso xfer to be completed by reading it from the log */
+void replay_req_register_iso(struct libusb_transfer *xfer);
+
+#define REPLAY_ASSERT(val) replay_assert(val, NULL)
+#define REPLAY_ASSERT_M(val, msg) replay_assert(val, msg)
+
+typedef void (*replay_get_date_func)(struct ISADevice*, struct tm *);
+
+/*! Registers function which gets time from target OS. */
+void replay_register_get_time(replay_get_date_func f, void *opaque);
+
+/* Async events functions */
+/*! Adds specified async event to the queue */
+void replay_add_event(unsigned int event_id, void *opaque);
+/*! Adds specified async event with 2 parameters to the queue */
+void replay_add_event2(unsigned int event_id, void *opaque, void *opaque2);
+/*! Adds BH event to the queue */
+void replay_add_bh_event(void *bh, uint64_t id);
+/*! Adds thread event to the queue */
+void replay_add_thread_event(void *opaque, void *opaque2, uint64_t id);
+
+void replay_enable_events(void);
+void replay_disable_events(void);
+
+/*! Registers char driver to save it's events */
+void replay_register_char_driver(CharDriverState *chr);
+
+/*! Writes or reads integer value to/from replay log. */
+void replay_data_int(int *data);
+/*! Macro for using replay_data_int function */
+#define REPLAY_DATA_INT(var, value) ((replay_mode == REPLAY_NONE ? var = (value) : replay_mode ==
REPLAY_SAVE ? (var = (value), replay_data_int(&var), var) : /* PLAY */ (replay_data_int(&var),
var)))
+/*! Writes or reads buffer to/from replay log. */
+void replay_data_buffer(unsigned char *data, size_t size);
+/*! Macro for using replay_data_buffer function */
+#define REPLAY_DATA_VAR(var) replay_data_buffer((unsigned char*)&(var), sizeof(var))
+/*! Saves write to char device event to the log */
+void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
+
+/* Returns the virtual CPU time, based on the instruction counter.  */
+int64_t replay_get_icount(void);
+void replay_clock_warp(void);
+
+/*! Starts normal execution from the current replay step. */
