OSDN Git Service

[VM][FM7] Fix state save/load.
[csp-qt/common_source_project-fm7.git] / source / src / emu.cpp
index b4c9fc5..4b6e1ae 100644 (file)
-/*\r
-       Skelton for retropc emulator\r
-\r
-       Author : Takeda.Toshiya\r
-       Date   : 2006.08.18 -\r
-\r
-       [ win32 emulation i/f ]\r
-*/\r
-\r
-#include "emu.h"\r
-#include "vm/vm.h"\r
-#include "fileio.h"\r
-#if defined(_USE_AGAR)\r
-#include <SDL/SDL.h>\r
-#include "agar_main.h"\r
-#include "agar_logger.h"\r
-#include <ctime>\r
-# elif defined(_USE_QT)\r
-//#include <SDL/SDL.h>\r
-#include "qt_main.h"\r
-#include "agar_logger.h"\r
-#include <ctime>\r
-# endif\r
-\r
-#ifndef FD_BASE_NUMBER\r
-#define FD_BASE_NUMBER 1\r
-#endif\r
-#ifndef QD_BASE_NUMBER\r
-#define QD_BASE_NUMBER 1\r
-#endif\r
-\r
-// ----------------------------------------------------------------------------\r
-// initialize\r
-// ----------------------------------------------------------------------------\r
-#if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)\r
-extern void get_long_full_path_name(_TCHAR* src, _TCHAR* dst);\r
-#include <string>\r
-#endif\r
-\r
-#if defined(_USE_QGAR)\r
-EMU::EMU(AG_Window *hwnd, AG_Widget *hinst)\r
-#elif defined(_USE_QT)\r
-EMU::EMU(Ui_MainWindow *hwnd, GLDrawClass *hinst)\r
-#else\r
-EMU::EMU(HWND hwnd, HINSTANCE hinst)\r
-#endif\r
-{\r
-#ifdef _DEBUG_LOG\r
-       // open debug logfile\r
-       initialize_debug_log();\r
-#endif\r
-       message_count = 0;\r
-       \r
-       // store main window handle\r
-       main_window_handle = hwnd;\r
-        instance_handle = hinst;\r
-        \r
-       // get module path\r
-       // Initialize keymod.\r
-       modkey_status = 0;\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        std::string tmps;\r
-#ifndef _USE_QT\r
-        _TCHAR tmp_path[PATH_MAX], *ptr;\r
-        my_procname.copy(tmp_path, PATH_MAX, 0);\r
-        get_long_full_path_name(tmp_path, app_path);\r
-        //AGAR_DebugLog("APPPATH=%s\n", app_path);\r
-        if(AG_UsingGL(AGDRIVER(main_window_handle))) {\r
-          use_opengl = true;\r
-          use_opencl = false;\r
-       } else {\r
-          use_opencl = false;\r
-          use_opengl = false;\r
-       }\r
-#else\r
-        _TCHAR tmp_path[PATH_MAX], *ptr;\r
-        my_procname.copy(tmp_path, PATH_MAX, 0);\r
-        get_long_full_path_name(tmp_path, app_path);\r
-        //AGAR_DebugLog("APPPATH=%s\n", app_path);\r
-       use_opengl = true;\r
-        use_opencl = false;\r
-#endif\r
-        pVMSemaphore = SDL_CreateSemaphore(1);\r
-#else\r
-       _TCHAR tmp_path[_MAX_PATH], *ptr;\r
-        GetModuleFileName(NULL, tmp_path, _MAX_PATH);\r
-       GetFullPathName(tmp_path, _MAX_PATH, app_path, &ptr);\r
-       *ptr = _T('\0');\r
-#endif \r
-#ifdef USE_FD1\r
-       // initialize d88 file info\r
-       memset(d88_file, 0, sizeof(d88_file));\r
-#endif\r
-       \r
-       // load sound config\r
-       static const int freq_table[8] = {\r
-               2000, 4000, 8000, 11025, 22050, 44100,\r
-#ifdef OVERRIDE_SOUND_FREQ_48000HZ\r
-               OVERRIDE_SOUND_FREQ_48000HZ,\r
-#else\r
-               48000,\r
-#endif\r
-               96000,\r
-       };\r
-       static const double late_table[5] = {0.05, 0.1, 0.2, 0.3, 0.4};\r
-       \r
-       if(!(0 <= config.sound_frequency && config.sound_frequency < 8)) {\r
-               config.sound_frequency = 6;     // default: 48KHz\r
-       }\r
-       if(!(0 <= config.sound_latency && config.sound_latency < 5)) {\r
-               config.sound_latency = 1;       // default: 100msec\r
-       }\r
-       sound_rate = freq_table[config.sound_frequency];\r
-       sound_samples = (int)(sound_rate * late_table[config.sound_latency] + 0.5);\r
-       \r
-#ifdef USE_CPU_TYPE\r
-       cpu_type = config.cpu_type;\r
-#endif\r
-#ifdef USE_SOUND_DEVICE_TYPE\r
-       sound_device_type = config.sound_device_type;\r
-#endif\r
-       \r
-       // initialize\r
-       vm = new VM(this);\r
-#ifdef USE_DEBUGGER\r
-       initialize_debugger();\r
-#endif\r
-       initialize_input();\r
-       initialize_screen();\r
-       initialize_sound();\r
-       initialize_media();\r
-       initialize_printer();\r
-#ifdef USE_SOCKET\r
-       initialize_socket();\r
-#endif\r
-#ifdef USE_DIRECT_SHOW\r
-       CoInitialize(NULL);\r
-       initialize_direct_show();\r
-#endif\r
-       vm->initialize_sound(sound_rate, sound_samples);\r
-       vm->reset();\r
-       now_suspended = false;\r
-}\r
-\r
-EMU::~EMU()\r
-{\r
-#ifdef USE_DEBUGGER\r
-       release_debugger();\r
-#endif\r
-       release_input();\r
-       release_screen();\r
-       release_sound();\r
-       release_printer();\r
-#ifdef USE_SOCKET\r
-       release_socket();\r
-#endif\r
-#ifdef USE_DIRECT_SHOW\r
-       release_direct_show();\r
-       CoInitialize(NULL);\r
-#endif\r
-\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-       if(pVMSemaphore) SDL_SemWait(pVMSemaphore);\r
-#endif\r
-       delete vm;\r
-#ifdef _DEBUG_LOG\r
-       release_debug_log();\r
-#endif\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-      if(pVMSemaphore) SDL_DestroySemaphore(pVMSemaphore);\r
-#endif\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// drive machine\r
-// ----------------------------------------------------------------------------\r
-\r
-int EMU::frame_interval()\r
-{\r
-#if 1\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-       static int prev_interval = 0;\r
-       static double prev_fps = -1;\r
-       double fps = vm->frame_rate();\r
-       if(prev_fps != fps) {\r
-               prev_interval = (int)(1024. * 1000. / fps + 0.5);\r
-               prev_fps = fps;\r
-       }\r
-       return prev_interval;\r
-#else\r
-       return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);\r
-#endif\r
-#else\r
-        return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);\r
-#endif\r
-}\r
-\r
-int EMU::run()\r
-{\r
-       if(now_suspended) {\r
-#ifdef USE_LASER_DISC\r
-               if(now_movie_play && !now_movie_pause) {\r
-                       play_movie();\r
-               }\r
-#endif\r
-               now_suspended = false;\r
-       }\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        if(pVMSemaphore) SDL_SemWait(pVMSemaphore);\r
-#endif\r
-       \r
-       update_input();\r
-       update_media();\r
-       update_printer();\r
-#ifdef USE_SOCKET\r
-       update_socket();\r
-#endif\r
-       \r
-       // virtual machine may be driven to fill sound buffer\r
-       int extra_frames = 0;\r
-       update_sound(&extra_frames);\r
-       \r
-       // drive virtual machine\r
-       if(extra_frames == 0) {\r
-               vm->run();\r
-//             printf("VM:RUN() %d\n", AG_GetTicks());\r
-               extra_frames = 1;\r
-       }\r
-       rec_video_run_frames += extra_frames;\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        if(pVMSemaphore) SDL_SemPost(pVMSemaphore);\r
-#endif\r
-       return extra_frames;\r
-}\r
-\r
-void EMU::reset()\r
-{\r
-       // check if virtual machine should be reinitialized\r
-       bool reinitialize = false;\r
-#ifdef USE_CPU_TYPE\r
-       reinitialize |= (cpu_type != config.cpu_type);\r
-       cpu_type = config.cpu_type;\r
-#endif\r
-#ifdef USE_SOUND_DEVICE_TYPE\r
-       reinitialize |= (sound_device_type != config.sound_device_type);\r
-       sound_device_type = config.sound_device_type;\r
-#endif\r
-       if(reinitialize) {\r
-               // stop sound\r
-               if(sound_ok && sound_started) {\r
-#if defined(_USE_SDL) || defined(_USE_AGAR) || defined(_USE_QT)\r
-                       //bSndExit = true;\r
-                       SDL_PauseAudio(1);\r
-#else\r
-                       lpdsb->Stop();\r
-#endif\r
-                       sound_started = false;\r
-               }\r
-               // reinitialize virtual machine\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        if(pVMSemaphore) SDL_SemWait(pVMSemaphore);\r
-#endif\r
-               delete vm;\r
-               vm = new VM(this);\r
-               vm->initialize_sound(sound_rate, sound_samples);\r
-               vm->reset();\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        if(pVMSemaphore) SDL_SemPost(pVMSemaphore);\r
-#endif\r
-               // restore inserted medias\r
-               restore_media();\r
-       } else {\r
-          // reset virtual machine\r
-               vm->reset();\r
-       }\r
-       \r
-       // reset printer\r
-       reset_printer();\r
-       \r
-       // restart recording\r
-       restart_rec_sound();\r
-       restart_rec_video();\r
-}\r
-\r
-#ifdef USE_SPECIAL_RESET\r
-void EMU::special_reset()\r
-{\r
-       // reset virtual machine\r
-       vm->special_reset();\r
-       \r
-       // reset printer\r
-       reset_printer();\r
-       \r
-       // restart recording\r
-       restart_rec_sound();\r
-       restart_rec_video();\r
-}\r
-#endif\r
-\r
-#ifdef USE_POWER_OFF\r
-void EMU::notify_power_off()\r
-{\r
-       vm->notify_power_off();\r
-}\r
-#endif\r
-\r
-_TCHAR* EMU::bios_path(_TCHAR* file_name)\r
-{\r
-#if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)\r
-        static _TCHAR file_path[_MAX_PATH];\r
-        strcpy(file_path, app_path);\r
-        strcat(file_path, file_name);\r
-#if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)\r
-        AGAR_DebugLog(AGAR_LOG_INFO, "LOAD BIOS: %s\n", file_path);\r
-#endif\r
-#else\r
-        static _TCHAR file_path[_MAX_PATH];\r
-       _stprintf(file_path, _T("%s%s"), app_path, file_name);\r
-        printf("LOAD: %s\n", file_path);\r
-#endif\r
-       return file_path;\r
-}\r
-\r
-void EMU::suspend()\r
-{\r
-       if(!now_suspended) {\r
-#ifdef USE_LASER_DISC\r
-               if(now_movie_play && !now_movie_pause) {\r
-                       pause_movie();\r
-                       now_movie_pause = false;\r
-               }\r
-#endif\r
-               mute_sound();\r
-               now_suspended = true;\r
-       }\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// timer\r
-// ----------------------------------------------------------------------------\r
-\r
-void EMU::get_host_time(cur_time_t* t)\r
-{\r
-#if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)\r
-        std::tm *tm;\r
-        std::time_t tnow;\r
-        tnow = std::time(NULL);\r
-        tm = std::localtime(&tnow);\r
-\r
-       t->year = tm->tm_year + 1900;\r
-       t->month = tm->tm_mon + 1;\r
-       t->day = tm->tm_mday;\r
-       t->day_of_week = tm->tm_wday;\r
-       t->hour = tm->tm_hour;\r
-       t->minute = tm->tm_min;\r
-       t->second = tm->tm_sec;\r
-#else\r
-        SYSTEMTIME sTime;\r
-       GetLocalTime(&sTime);\r
-       \r
-       t->year = sTime.wYear;\r
-       t->month = sTime.wMonth;\r
-       t->day = sTime.wDay;\r
-       t->day_of_week = sTime.wDayOfWeek;\r
-       t->hour = sTime.wHour;\r
-       t->minute = sTime.wMinute;\r
-       t->second = sTime.wSecond;\r
-#endif\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// printer\r
-// ----------------------------------------------------------------------------\r
-\r
-void EMU::initialize_printer()\r
-{\r
-       prn_fio = new FILEIO();\r
-       prn_data = -1;\r
-       prn_strobe = false;\r
-}\r
-\r
-void EMU::release_printer()\r
-{\r
-       close_printer_file();\r
-       delete prn_fio;\r
-}\r
-\r
-void EMU::reset_printer()\r
-{\r
-       close_printer_file();\r
-       prn_data = -1;\r
-       prn_strobe = false;\r
-}\r
-\r
-void EMU::update_printer()\r
-{\r
-       if(prn_fio->IsOpened() && --prn_wait_frames == 0) {\r
-               close_printer_file();\r
-       }\r
-}\r
-\r
-void EMU::open_printer_file()\r
-{\r
-       cur_time_t time;\r
-       get_host_time(&time);\r
-       _stprintf(prn_file_name, _T("prn_%d-%0.2d-%0.2d_%0.2d-%0.2d-%0.2d.txt"), time.year, time.month, time.day, time.hour, time.minute, time.second);\r
-       prn_fio->Fopen(bios_path(prn_file_name), FILEIO_WRITE_BINARY);\r
-}\r
-\r
-void EMU::close_printer_file()\r
-{\r
-       if(prn_fio->IsOpened()) {\r
-               // remove if the file size is less than 2 bytes\r
-               bool remove = (prn_fio->Ftell() < 2);\r
-               prn_fio->Fclose();\r
-               if(remove) {\r
-                       prn_fio->Remove(bios_path(prn_file_name));\r
-               }\r
-       }\r
-}\r
-\r
-void EMU::printer_out(uint8 value)\r
-{\r
-       prn_data = value;\r
-}\r
-\r
-void EMU::printer_strobe(bool value)\r
-{\r
-       bool falling = (prn_strobe && !value);\r
-       prn_strobe = value;\r
-       \r
-       if(falling) {\r
-               if(!prn_fio->IsOpened()) {\r
-                       if(prn_data == -1) {\r
-                               return;\r
-                       }\r
-                       open_printer_file();\r
-               }\r
-               prn_fio->Fputc(prn_data);\r
-               // wait 10sec\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-               prn_wait_frames = (int)(vm->frame_rate() * 10.0 + 0.5);\r
-#else\r
-               prn_wait_frames = (int)(FRAMES_PER_SEC * 10.0 + 0.5);\r
-#endif\r
-       }\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// debug log\r
-// ----------------------------------------------------------------------------\r
-\r
-#ifdef _DEBUG_LOG\r
-void EMU::initialize_debug_log()\r
-{\r
-       debug_log = _tfopen(_T("d:\\debug.log"), _T("w"));\r
-}\r
-\r
-void EMU::release_debug_log()\r
-{\r
-       if(debug_log) {\r
-               fclose(debug_log);\r
-       }\r
-}\r
-#endif\r
-\r
-void EMU::out_debug_log(const _TCHAR* format, ...)\r
-{\r
-#ifdef _DEBUG_LOG\r
-       va_list ap;\r
-       _TCHAR buffer[1024];\r
-       static _TCHAR prev_buffer[1024] = {0};\r
-       \r
-       va_start(ap, format);\r
-       _vstprintf(buffer, format, ap);\r
-       va_end(ap);\r
-       \r
-       if(_tcscmp(prev_buffer, buffer) == 0) {\r
-               return;\r
-       }\r
-       _tcscpy(prev_buffer, buffer);\r
-       \r
-       if(debug_log) {\r
-               _ftprintf(debug_log, _T("%s"), buffer);\r
-               static int size = 0;\r
-               if((size += _tcslen(buffer)) > 0x8000000) { // 128MB\r
-                       static int index = 1;\r
-                       TCHAR path[_MAX_PATH];\r
-                       _stprintf(path, _T("d:\\debug_#%d.log"), ++index);\r
-                       fclose(debug_log);\r
-                       debug_log = _tfopen(path, _T("w"));\r
-                       size = 0;\r
-               }\r
-       }\r
-#endif\r
-}\r
-\r
-void EMU::out_message(const _TCHAR* format, ...)\r
-{\r
-       va_list ap;\r
-       va_start(ap, format);\r
-       _vstprintf(message, format, ap);\r
-       va_end(ap);\r
-       message_count = 4; // 4sec\r
-}\r
-\r
-// ----------------------------------------------------------------------------\r
-// user interface\r
-// ----------------------------------------------------------------------------\r
-\r
-void EMU::initialize_media()\r
-{\r
-#ifdef USE_CART1\r
-       memset(&cart_status, 0, sizeof(cart_status));\r
-#endif\r
-#ifdef USE_FD1\r
-       memset(disk_status, 0, sizeof(disk_status));\r
-#endif\r
-#ifdef USE_QD1\r
-       memset(&quickdisk_status, 0, sizeof(quickdisk_status));\r
-#endif\r
-#ifdef USE_TAPE\r
-       memset(&tape_status, 0, sizeof(tape_status));\r
-#endif\r
-#ifdef USE_LASER_DISC\r
-       memset(&laser_disc_status, 0, sizeof(laser_disc_status));\r
-#endif\r
-}\r
-\r
-#if defined(USE_FD1) || defined(USE_FD2) || defined(USE_FD3) || defined(USE_FD4) || \\r
-    defined(USE_FD5) || defined(USE_FD6) || defined(USE_FD7) || defined(USE_FD8)\r
-\r
-\r
-void EMU::write_protect_fd(int drv, bool flag)\r
-{\r
-  vm->write_protect_fd(drv, flag);\r
-}\r
-bool EMU::is_write_protected_fd(int drv)\r
-{\r
-  return vm->is_write_protect_fd(drv);\r
-}\r
-#endif\r
-\r
-void EMU::update_media()\r
-{\r
-#ifdef USE_FD1\r
-       for(int drv = 0; drv < MAX_FD; drv++) {\r
-               if(disk_status[drv].wait_count != 0 && --disk_status[drv].wait_count == 0) {\r
-                       vm->open_disk(drv, disk_status[drv].path, disk_status[drv].offset);\r
-                       out_message(_T("FD%d: %s"), drv + FD_BASE_NUMBER, disk_status[drv].path);\r
-               }\r
-       }\r
-#endif\r
-#ifdef USE_QD1\r
-       for(int drv = 0; drv < MAX_QD; drv++) {\r
-               if(quickdisk_status[drv].wait_count != 0 && --quickdisk_status[drv].wait_count == 0) {\r
-                       vm->open_quickdisk(drv, quickdisk_status[drv].path);\r
-                       out_message(_T("QD%d: %s"), drv + QD_BASE_NUMBER, quickdisk_status[drv].path);\r
-               }\r
-       }\r
-#endif\r
-#ifdef USE_TAPE\r
-       if(tape_status.wait_count != 0 && --tape_status.wait_count == 0) {\r
-               if(tape_status.play) {\r
-                       vm->play_tape(tape_status.path);\r
-               } else {\r
-                       vm->rec_tape(tape_status.path);\r
-               }\r
-               out_message(_T("CMT: %s"), tape_status.path);\r
-       }\r
-#endif\r
-#ifdef USE_LASER_DISC\r
-       if(laser_disc_status.wait_count != 0 && --laser_disc_status.wait_count == 0) {\r
-               vm->open_laser_disc(laser_disc_status.path);\r
-               out_message(_T("LD: %s"), laser_disc_status.path);\r
-       }\r
-#endif\r
-}\r
-\r
-void EMU::restore_media()\r
-{\r
-#ifdef USE_CART1\r
-       for(int drv = 0; drv < MAX_CART; drv++) {\r
-               if(cart_status[drv].path[0] != _T('\0')) {\r
-                       vm->open_cart(drv, cart_status[drv].path);\r
-               }\r
-       }\r
-#endif\r
-#ifdef USE_FD1\r
-       for(int drv = 0; drv < MAX_FD; drv++) {\r
-               if(disk_status[drv].path[0] != _T('\0')) {\r
-                       vm->open_disk(drv, disk_status[drv].path, disk_status[drv].offset);\r
-               }\r
-       }\r
-#endif\r
-#ifdef USE_QD1\r
-       for(int drv = 0; drv < MAX_QD; drv++) {\r
-               if(quickdisk_status[drv].path[0] != _T('\0')) {\r
-                       vm->open_quickdisk(drv, quickdisk_status[drv].path);\r
-               }\r
-       }\r
-#endif\r
-#ifdef USE_TAPE\r
-       if(tape_status.path[0] != _T('\0')) {\r
-               if(tape_status.play) {\r
-                       vm->play_tape(tape_status.path);\r
-               } else {\r
-                       tape_status.path[0] = _T('\0');\r
-               }\r
-       }\r
-#endif\r
-#ifdef USE_LASER_DISC\r
-       if(laser_disc_status.path[0] != _T('\0')) {\r
-               vm->open_laser_disc(laser_disc_status.path);\r
-       }\r
-#endif\r
-}\r
-\r
-#ifdef USE_CART1\r
-void EMU::open_cart(int drv, _TCHAR* file_path)\r
-{\r
-       if(drv < MAX_CART) {\r
-               vm->open_cart(drv, file_path);\r
-               _tcscpy(cart_status[drv].path, file_path);\r
-               out_message(_T("Cart%d: %s"), drv + 1, file_path);\r
-               \r
-               // restart recording\r
-               bool s = now_rec_sound;\r
-               bool v = now_rec_video;\r
-               stop_rec_sound();\r
-               stop_rec_video();\r
-               if(s) start_rec_sound();\r
-               if(v) start_rec_video(-1);\r
-       }\r
-}\r
-\r
-void EMU::close_cart(int drv)\r
-{\r
-       if(drv < MAX_CART) {\r
-               vm->close_cart(drv);\r
-               clear_media_status(&cart_status[drv]);\r
-               out_message(_T("Cart%d: Ejected"), drv + 1);\r
-               \r
-               // stop recording\r
-               stop_rec_video();\r
-               stop_rec_sound();\r
-       }\r
-}\r
-\r
-bool EMU::cart_inserted(int drv)\r
-{\r
-       if(drv < MAX_CART) {\r
-               return vm->cart_inserted(drv);\r
-       } else {\r
-               return false;\r
-       }\r
-}\r
-#endif\r
-\r
-#ifdef USE_FD1\r
-void EMU::open_disk(int drv, _TCHAR* file_path, int offset)\r
-{\r
-       if(drv < MAX_FD) {\r
-               if(vm->disk_inserted(drv)) {\r
-                       vm->close_disk(drv);\r
-                       // wait 0.5sec\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-                       disk_status[drv].wait_count = (int)(vm->frame_rate() / 2);\r
-#else\r
-                       disk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);\r
-#endif\r
-                       out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);\r
-               } else if(disk_status[drv].wait_count == 0) {\r
-                       vm->open_disk(drv, file_path, offset);\r
-                       out_message(_T("FD%d: %s"), drv + FD_BASE_NUMBER, file_path);\r
-               }\r
-               _tcscpy(disk_status[drv].path, file_path);\r
-               disk_status[drv].offset = offset;\r
-       }\r
-}\r
-\r
-void EMU::close_disk(int drv)\r
-{\r
-       if(drv < MAX_FD) {\r
-               vm->close_disk(drv);\r
-               clear_media_status(&disk_status[drv]);\r
-               out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);\r
-       }\r
-}\r
-\r
-bool EMU::disk_inserted(int drv)\r
-{\r
-       if(drv < MAX_FD) {\r
-               return vm->disk_inserted(drv);\r
-       } else {\r
-               return false;\r
-       }\r
-}\r
-#endif\r
-\r
-int EMU::get_access_lamp(void)\r
-{\r
-   int stat = 0;\r
-#if defined(USE_FD1) || defined(USE_QD1)\r
-   LockVM();\r
-   stat = vm->access_lamp(); // Return accessing drive number.\r
-   UnlockVM();\r
-#endif\r
-   return stat;\r
-}\r
-\r
-\r
-#ifdef USE_QD1\r
-void EMU::open_quickdisk(int drv, _TCHAR* file_path)\r
-{\r
-       if(drv < MAX_QD) {\r
-               if(vm->quickdisk_inserted(drv)) {\r
-                       vm->close_quickdisk(drv);\r
-                       // wait 0.5sec\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-                       quickdisk_status[drv].wait_count = (int)(vm->frame_rate() / 2);\r
-#else\r
-                       quickdisk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);\r
-#endif\r
-                       out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);\r
-               } else if(quickdisk_status[drv].wait_count == 0) {\r
-                       vm->open_quickdisk(drv, file_path);\r
-                       out_message(_T("QD%d: %s"), drv + QD_BASE_NUMBER, file_path);\r
-               }\r
-               _tcscpy(quickdisk_status[drv].path, file_path);\r
-       }\r
-}\r
-\r
-void EMU::close_quickdisk(int drv)\r
-{\r
-       if(drv < MAX_QD) {\r
-               vm->close_quickdisk(drv);\r
-               clear_media_status(&quickdisk_status[drv]);\r
-               out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);\r
-       }\r
-}\r
-\r
-bool EMU::quickdisk_inserted(int drv)\r
-{\r
-       if(drv < MAX_QD) {\r
-               return vm->quickdisk_inserted(drv);\r
-       } else {\r
-               return false;\r
-       }\r
-}\r
-#endif\r
-\r
-#ifdef USE_TAPE\r
-void EMU::play_tape(_TCHAR* file_path)\r
-{\r
-       if(vm->tape_inserted()) {\r
-               vm->close_tape();\r
-               // wait 0.5sec\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-               tape_status.wait_count = (int)(vm->frame_rate() / 2);\r
-#else\r
-               tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);\r
-#endif\r
-               out_message(_T("CMT: Ejected"));\r
-       } else if(tape_status.wait_count == 0) {\r
-               vm->play_tape(file_path);\r
-               out_message(_T("CMT: %s"), file_path);\r
-       }\r
-       _tcscpy(tape_status.path, file_path);\r
-       tape_status.play = true;\r
-}\r
-\r
-void EMU::rec_tape(_TCHAR* file_path)\r
-{\r
-       if(vm->tape_inserted()) {\r
-               vm->close_tape();\r
-               // wait 0.5sec\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-               tape_status.wait_count = (int)(vm->frame_rate() / 2);\r
-#else\r
-               tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);\r
-#endif\r
-               out_message(_T("CMT: Ejected"));\r
-       } else if(tape_status.wait_count == 0) {\r
-               vm->rec_tape(file_path);\r
-               out_message(_T("CMT: %s"), file_path);\r
-       }\r
-       _tcscpy(tape_status.path, file_path);\r
-       tape_status.play = false;\r
-}\r
-\r
-void EMU::close_tape()\r
-{\r
-       vm->close_tape();\r
-       clear_media_status(&tape_status);\r
-       out_message(_T("CMT: Ejected"));\r
-}\r
-\r
-bool EMU::tape_inserted()\r
-{\r
-       return vm->tape_inserted();\r
-}\r
-#endif\r
-\r
-#ifdef USE_LASER_DISC\r
-void EMU::open_laser_disc(_TCHAR* file_path)\r
-{\r
-       if(vm->laser_disc_inserted()) {\r
-               vm->close_laser_disc();\r
-               // wait 0.5sec\r
-#ifdef SUPPORT_VARIABLE_TIMING\r
-               laser_disc_status.wait_count = (int)(vm->frame_rate() / 2);\r
-#else\r
-               laser_disc_status.wait_count = (int)(FRAMES_PER_SEC / 2);\r
-#endif\r
-               out_message(_T("LD: Ejected"));\r
-       } else if(laser_disc_status.wait_count == 0) {\r
-               vm->open_laser_disc(file_path);\r
-               out_message(_T("LD: %s"), file_path);\r
-       }\r
-       _tcscpy(laser_disc_status.path, file_path);\r
-}\r
-\r
-void EMU::close_laser_disc()\r
-{\r
-       vm->close_laser_disc();\r
-       clear_media_status(&laser_disc_status);\r
-       out_message(_T("LD: Ejected"));\r
-}\r
-\r
-bool EMU::laser_disc_inserted()\r
-{\r
-       return vm->laser_disc_inserted();\r
-}\r
-#endif\r
-\r
-#ifdef USE_TAPE_BUTTON\r
-void EMU::push_play()\r
-{\r
-       vm->push_play();\r
-}\r
-\r
-void EMU::push_stop()\r
-{\r
-       vm->push_stop();\r
-}\r
-#endif\r
-\r
-#ifdef USE_BINARY_FILE1\r
-void EMU::load_binary(int drv, _TCHAR* file_path)\r
-{\r
-       if(drv < MAX_BINARY) {\r
-               vm->load_binary(drv, file_path);\r
-               out_message(_T("Load: %s"), file_path);\r
-       }\r
-}\r
-\r
-void EMU::save_binary(int drv, _TCHAR* file_path)\r
-{\r
-       if(drv < MAX_BINARY) {\r
-               vm->save_binary(drv, file_path);\r
-               out_message(_T("Save: %s"), file_path);\r
-       }\r
-}\r
-#endif\r
-\r
-bool EMU::now_skip()\r
-{\r
-       return vm->now_skip();\r
-}\r
-\r
-void EMU::update_config()\r
-{\r
-       vm->update_config();\r
-}\r
-\r
-#ifdef USE_STATE\r
-// ----------------------------------------------------------------------------\r
-// state\r
-// ----------------------------------------------------------------------------\r
-\r
-#define STATE_VERSION  1\r
-\r
-void EMU::save_state()\r
-{\r
-       _TCHAR file_name[_MAX_PATH];\r
-       _stprintf(file_name, _T("%s.sta"), _T(CONFIG_NAME));\r
-       save_state_tmp(bios_path(file_name));\r
-}\r
-\r
-void EMU::load_state()\r
-{\r
-       _TCHAR file_name[_MAX_PATH];\r
-       _stprintf(file_name, _T("%s.sta"), _T(CONFIG_NAME));\r
-       save_state_tmp(bios_path(_T("$temp$.sta")));\r
-       if(!load_state_tmp(bios_path(file_name))) {\r
-               load_state_tmp(bios_path(_T("$temp$.sta")));\r
-       }\r
-       DeleteFile(bios_path(_T("$temp$.sta")));\r
-}\r
-\r
-void EMU::save_state_tmp(_TCHAR* file_path)\r
-{\r
-       FILEIO* fio = new FILEIO();\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        if(pVMSemaphore) SDL_SemWait(pVMSemaphore);\r
-#endif\r
-       if(fio->Fopen(file_path, FILEIO_WRITE_BINARY)) {\r
-               // save state file version\r
-               fio->FputUint32(STATE_VERSION);\r
-               // save config\r
-               save_config_state((void *)fio);\r
-               // save inserted medias\r
-#ifdef USE_CART1\r
-               fio->Fwrite(&cart_status, sizeof(cart_status), 1);\r
-#endif\r
-#ifdef USE_FD1\r
-               fio->Fwrite(disk_status, sizeof(disk_status), 1);\r
-               fio->Fwrite(d88_file, sizeof(d88_file), 1);\r
-#endif\r
-#ifdef USE_QD1\r
-               fio->Fwrite(&quickdisk_status, sizeof(quickdisk_status), 1);\r
-#endif\r
-#ifdef USE_TAPE\r
-               fio->Fwrite(&tape_status, sizeof(tape_status), 1);\r
-#endif\r
-#ifdef USE_LASER_DISC\r
-               fio->Fwrite(&laser_disc_status, sizeof(laser_disc_status), 1);\r
-#endif\r
-               // save vm state\r
-               vm->save_state(fio);\r
-               // end of state file\r
-               fio->FputInt32(-1);\r
-               fio->Fclose();\r
-       }\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-        if(pVMSemaphore) SDL_SemPost(pVMSemaphore);\r
-#endif\r
-       delete fio;\r
-}\r
-\r
-bool EMU::load_state_tmp(_TCHAR* file_path)\r
-{\r
-       bool result = false;\r
-       FILEIO* fio = new FILEIO();\r
-       if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {\r
-               // check state file version\r
-               if(fio->FgetUint32() == STATE_VERSION) {\r
-                       // load config\r
-                       if(load_config_state((void *)fio)) {\r
-                               // load inserted medias\r
-#ifdef USE_CART1\r
-                               fio->Fread(&cart_status, sizeof(cart_status), 1);\r
-#endif\r
-#ifdef USE_FD1\r
-                               fio->Fread(disk_status, sizeof(disk_status), 1);\r
-                               fio->Fread(d88_file, sizeof(d88_file), 1);\r
-#endif\r
-#ifdef USE_QD1\r
-                               fio->Fread(&quickdisk_status, sizeof(quickdisk_status), 1);\r
-#endif\r
-#ifdef USE_TAPE\r
-                               fio->Fread(&tape_status, sizeof(tape_status), 1);\r
-#endif\r
-#ifdef USE_LASER_DISC\r
-                               fio->Fread(&laser_disc_status, sizeof(laser_disc_status), 1);\r
-#endif\r
-                               // check if virtual machine should be reinitialized\r
-                               bool reinitialize = false;\r
-#ifdef USE_CPU_TYPE\r
-                               reinitialize |= (cpu_type != config.cpu_type);\r
-                               cpu_type = config.cpu_type;\r
-#endif\r
-#ifdef USE_SOUND_DEVICE_TYPE\r
-                               reinitialize |= (sound_device_type != config.sound_device_type);\r
-                               sound_device_type = config.sound_device_type;\r
-#endif\r
-                               if(reinitialize) {\r
-                                       // stop sound\r
-                                       if(sound_ok && sound_started) {\r
-#if defined(_USE_SDL) || defined(_USE_AGAR) || defined(_USE_QT)\r
-                                               //bSndExit = true;\r
-                                               SDL_PauseAudio(1);\r
-#else\r
-                                               lpdsb->Stop();\r
-#endif\r
-                                               sound_started = false;\r
-                                       }\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-                                       if(pVMSemaphore) SDL_SemWait(pVMSemaphore);\r
-#endif\r
-                                       // reinitialize virtual machine\r
-                                       delete vm;\r
-                                       vm = new VM(this);\r
-                                       vm->initialize_sound(sound_rate, sound_samples);\r
-                                       vm->reset();\r
-#if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)\r
-                                       if(pVMSemaphore) SDL_SemPost(pVMSemaphore);\r
-#endif\r
-                               }\r
-                               // restore inserted medias\r
-                               restore_media();\r
-                               // load vm state\r
-                               if(vm->load_state(fio)) {\r
-                                       // check end of state\r
-                                       result = (fio->FgetInt32() == -1);\r
-                               }\r
-                       }\r
-               }\r
-               fio->Fclose();\r
-       }\r
-       delete fio;\r
-       return result;\r
-}\r
-#endif\r
-\r
+/*
+       Skelton for retropc emulator
+
+       Author : Takeda.Toshiya
+       Date   : 2006.08.18 -
+
+       [ win32 emulation i/f ]
+*/
+
+#if defined(_USE_QT)
+#include <string>
+#endif
+#include "emu.h"
+#include "vm/vm.h"
+#include "fifo.h"
+#include "fileio.h"
+
+#ifndef FD_BASE_NUMBER
+#define FD_BASE_NUMBER 1
+#endif
+#ifndef QD_BASE_NUMBER
+#define QD_BASE_NUMBER 1
+#endif
+
+// ----------------------------------------------------------------------------
+// initialize
+// ----------------------------------------------------------------------------
+#if defined(_USE_QT)
+// Please permit at least them m(.. )m
+//extern void get_long_full_path_name(_TCHAR* src, _TCHAR* dst);
+#include <string>
+#endif
+
+#if defined(_USE_QT)
+EMU::EMU(class Ui_MainWindow *hwnd, GLDrawClass *hinst)
+#elif defined(OSD_WIN32)
+EMU::EMU(HWND hwnd, HINSTANCE hinst)
+#else
+EMU::EMU()
+#endif
+{
+       message_count = 0;
+       // store main window handle
+#ifdef USE_FD1
+       // initialize d88 file info
+       memset(d88_file, 0, sizeof(d88_file));
+#endif
+#ifdef USE_BUBBLE1
+       // initialize d88 file info
+       memset(b77_file, 0, sizeof(b77_file));
+#endif
+       // load sound config
+       static const int freq_table[8] = {
+               2000, 4000, 8000, 11025, 22050, 44100,
+#ifdef OVERRIDE_SOUND_FREQ_48000HZ
+               OVERRIDE_SOUND_FREQ_48000HZ,
+#else
+               48000,
+#endif
+               96000,
+       };
+       static const double late_table[5] = {0.05, 0.1, 0.2, 0.3, 0.4};
+       
+       if(!(0 <= config.sound_frequency && config.sound_frequency < 8)) {
+               config.sound_frequency = 6;     // default: 48KHz
+       }
+       if(!(0 <= config.sound_latency && config.sound_latency < 5)) {
+               config.sound_latency = 1;       // default: 100msec
+       }
+       sound_rate = freq_table[config.sound_frequency];
+       sound_samples = (int)(sound_rate * late_table[config.sound_latency] + 0.5);
+
+#ifdef USE_CPU_TYPE
+       cpu_type = config.cpu_type;
+#endif
+#ifdef USE_SOUND_DEVICE_TYPE
+       sound_device_type = config.sound_device_type;
+#endif
+#ifdef USE_PRINTER
+       printer_device_type = config.printer_device_type;
+#endif
+       
+       // initialize osd
+       osd = new OSD();
+#if defined(OSD_QT)
+       osd->main_window_handle = hwnd;
+       osd->glv = hinst;
+       osd->host_cpus = 4;
+#elif defined(OSD_WIN32)
+       osd->main_window_handle = hwnd;
+       osd->instance_handle = hinst;
+#endif
+       osd->initialize(sound_rate, sound_samples);
+       // initialize vm
+       osd->vm = vm = new VM(this);
+       // Below is temporally workaround. I will fix ASAP (or give up): 20160311 K.Ohta
+       // Problems seem to be resolved. See fm7.cpp. 20160319 K.Ohta
+       // Still not resolved with FM-7/77 :-( 20160407 K.Ohta
+#if defined(_FM7) || defined(_FMNEW7) || defined(_FM8) || \
+       defined(_FM77_VARIANTS)
+       delete vm;
+       osd->vm = vm = new VM(this);
+#endif
+#ifdef USE_AUTO_KEY
+       initialize_auto_key();
+#endif
+#ifdef USE_DEBUGGER
+       initialize_debugger();
+#endif
+       initialize_media();
+       vm->initialize_sound(sound_rate, sound_samples);
+#ifdef USE_SOUND_VOLUME
+       for(int i = 0; i < USE_SOUND_VOLUME; i++) {
+               vm->set_sound_device_volume(i, config.sound_volume_l[i], config.sound_volume_r[i]);
+       }
+#endif
+       vm->reset();
+       now_suspended = false;
+}
+
+EMU::~EMU()
+{
+#ifdef USE_AUTO_KEY
+       release_auto_key();
+#endif
+#ifdef USE_DEBUGGER
+       release_debugger();
+#endif
+       delete vm;
+       osd->release();
+       delete osd;
+#ifdef _DEBUG_LOG
+       release_debug_log();
+#endif
+}
+
+#ifdef OSD_QT
+EmuThreadClass *EMU::get_parent_handler()
+{
+       return osd->get_parent_handler();
+}
+
+void EMU::set_parent_handler(EmuThreadClass *p, DrawThreadClass *q)
+{
+       osd->set_parent_thread(p);
+       osd->set_draw_thread(q);
+}
+
+void EMU::set_host_cpus(int v)
+{
+       osd->host_cpus = (v <= 0) ? 1 : v;
+}
+
+int EMU::get_host_cpus()
+{
+       return osd->host_cpus;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// drive machine
+// ----------------------------------------------------------------------------
+
+int EMU::get_frame_interval()
+{
+#ifdef SUPPORT_VARIABLE_TIMING
+       static int prev_interval = 0;
+       static double prev_fps = -1;
+       double fps = vm->get_frame_rate();
+       if(prev_fps != fps) {
+               prev_interval = (int)(1024. * 1000. / fps + 0.5);
+               prev_fps = fps;
+       }
+       return prev_interval;
+#else
+       return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);
+#endif
+}
+
+bool EMU::is_frame_skippable()
+{
+       return vm->is_frame_skippable();
+}
+
+int EMU::run()
+{
+       if(now_suspended) {
+               osd->restore();
+               now_suspended = false;
+       }
+       osd->update_input();
+#ifdef USE_AUTO_KEY
+       update_auto_key();
+#endif
+#ifdef USE_JOYSTICK
+       update_joystick();
+#endif
+       
+#ifdef USE_SOCKET
+#if !defined(_USE_QT) // Temporally
+       osd->update_socket();
+#endif
+#endif
+       update_media();
+       
+       // virtual machine may be driven to fill sound buffer
+       int extra_frames = 0;
+       osd->update_sound(&extra_frames);
+       
+       // drive virtual machine
+       if(extra_frames == 0) {
+               osd->lock_vm();
+               vm->run();
+               extra_frames = 1;
+               osd->unlock_vm();
+       }
+       osd->add_extra_frames(extra_frames);
+       return extra_frames;
+}
+
+void EMU::reset()
+{
+       // check if virtual machine should be reinitialized
+       bool reinitialize = false;
+#ifdef USE_CPU_TYPE
+       reinitialize |= (cpu_type != config.cpu_type);
+       cpu_type = config.cpu_type;
+#endif
+#ifdef USE_SOUND_DEVICE_TYPE
+       reinitialize |= (sound_device_type != config.sound_device_type);
+       sound_device_type = config.sound_device_type;
+#endif
+#ifdef USE_PRINTER
+       reinitialize |= (printer_device_type != config.printer_device_type);
+       printer_device_type = config.printer_device_type;
+#endif
+       if(reinitialize) {
+               // stop sound
+               osd->stop_sound();
+               // reinitialize virtual machine
+               osd->lock_vm();         
+               delete vm;
+               osd->vm = vm = new VM(this);
+               vm->initialize_sound(sound_rate, sound_samples);
+#ifdef USE_SOUND_VOLUME
+               for(int i = 0; i < USE_SOUND_VOLUME; i++) {
+                       vm->set_sound_device_volume(i, config.sound_volume_l[i], config.sound_volume_r[i]);
+               }
+#endif
+               vm->reset();
+               osd->unlock_vm();
+               // restore inserted medias
+               restore_media();
+       } else {
+               // reset virtual machine
+               osd->lock_vm();         
+               vm->reset();
+               osd->unlock_vm();               
+       }
+       
+       // restart recording
+#if !defined(_USE_QT) // Temporally
+       osd->restart_record_sound();
+       osd->restart_record_video();
+#endif 
+}
+
+#ifdef USE_SPECIAL_RESET
+void EMU::special_reset()
+{
+       // reset virtual machine
+       osd->lock_vm();         
+       vm->special_reset();
+       osd->unlock_vm();
+       // restart recording
+#if !defined(_USE_QT) // Temporally
+       restart_record_sound();
+       restart_record_video();
+#endif 
+}
+#endif
+
+#ifdef USE_NOTIFY_POWER_OFF
+void EMU::notify_power_off()
+{
+       vm->notify_power_off();
+}
+#endif
+
+void EMU::power_off()
+{
+       osd->power_off();
+}
+
+void EMU::suspend()
+{
+       if(!now_suspended) {
+               osd->suspend();
+               now_suspended = true;
+       }
+}
+
+void EMU::lock_vm()
+{
+       osd->lock_vm();
+}
+
+void EMU::unlock_vm()
+{
+       osd->unlock_vm();
+}
+
+void EMU::force_unlock_vm()
+{
+       osd->force_unlock_vm();
+}
+
+
+bool EMU::is_vm_locked()
+{
+       return osd->is_vm_locked();
+}
+
+// ----------------------------------------------------------------------------
+// input
+// ----------------------------------------------------------------------------
+
+#ifdef OSD_QT
+void EMU::key_modifiers(uint32_t mod)
+{
+       osd->key_modifiers(mod);
+}
+
+       # ifdef USE_MOUSE
+void EMU::set_mouse_pointer(int x, int y)
+{
+       osd->set_mouse_pointer(x, y);
+}
+
+void EMU::set_mouse_button(int button)
+{
+       osd->set_mouse_button(button);
+}
+
+int EMU::get_mouse_button()
+{
+       return osd->get_mouse_button();
+}
+       #endif
+#endif
+
+void EMU::key_down(int code, bool repeat)
+{
+       osd->key_down(code, repeat);
+}
+
+void EMU::key_up(int code)
+{
+       osd->key_up(code);
+}
+
+void EMU::key_lost_focus()
+{
+       osd->key_lost_focus();
+}
+
+#ifdef ONE_BOARD_MICRO_COMPUTER
+void EMU::press_button(int num)
+{
+       int code = vm_buttons[num].code;
+       
+       if(code) {
+               osd->key_down_native(code, false);
+               osd->get_key_buffer()[code] = KEY_KEEP_FRAMES;
+       } else {
+               // code=0: reset virtual machine
+               vm->reset();
+       }
+}
+#endif
+
+#ifdef USE_MOUSE
+void EMU::enable_mouse()
+{
+       osd->enable_mouse();
+}
+
+void EMU::disable_mouse()
+{
+       osd->disable_mouse();
+}
+
+void EMU::toggle_mouse()
+{
+       osd->toggle_mouse();
+}
+
+bool EMU::is_mouse_enabled()
+{
+       return osd->is_mouse_enabled();
+}
+#endif
+
+#ifdef USE_AUTO_KEY
+void EMU::initialize_auto_key()
+{
+       auto_key_buffer = new FIFO(65536);
+       auto_key_buffer->clear();
+       auto_key_phase = auto_key_shift = 0;
+       osd->now_auto_key = false;
+}
+
+void EMU::release_auto_key()
+{
+       if(auto_key_buffer) {
+               auto_key_buffer->release();
+               delete auto_key_buffer;
+       }
+}
+
+void EMU::start_auto_key()
+{
+       auto_key_phase = 1;
+       auto_key_shift = 0;
+       osd->now_auto_key = true;
+}
+
+void EMU::stop_auto_key()
+{
+       if(auto_key_shift) {
+               osd->key_up_native(VK_LSHIFT);
+       }
+       auto_key_phase = auto_key_shift = 0;
+       osd->now_auto_key = false;
+}
+
+#ifndef USE_AUTO_KEY_SHIFT
+#define USE_AUTO_KEY_SHIFT 0
+#endif
+#ifndef VK_LSHIFT
+#define VK_LSHIFT 0xA0
+#endif
+
+void EMU::update_auto_key()
+{
+       switch(auto_key_phase) {
+       case 1:
+               if(auto_key_buffer && !auto_key_buffer->empty()) {
+                       // update shift key status
+                       int shift = auto_key_buffer->read_not_remove(0) & 0x100;
+                       if(shift && !auto_key_shift) {
+                               osd->key_down_native(VK_LSHIFT, false);
+                       } else if(!shift && auto_key_shift) {
+                               osd->key_up_native(VK_LSHIFT);
+                       }
+                       auto_key_shift = shift;
+                       auto_key_phase++;
+                       break;
+               }
+       case 3 + USE_AUTO_KEY_SHIFT:
+               if(auto_key_buffer && !auto_key_buffer->empty()) {
+                       osd->key_down_native(auto_key_buffer->read_not_remove(0) & 0xff, false);
+               }
+               auto_key_phase++;
+               break;
+       case USE_AUTO_KEY + USE_AUTO_KEY_SHIFT:
+               if(auto_key_buffer && !auto_key_buffer->empty()) {
+                       osd->key_up_native(auto_key_buffer->read_not_remove(0) & 0xff);
+               }
+               auto_key_phase++;
+               break;
+       case USE_AUTO_KEY_RELEASE + USE_AUTO_KEY_SHIFT:
+               if(auto_key_buffer && !auto_key_buffer->empty()) {
+                       // wait enough while vm analyzes one line
+                       if(auto_key_buffer->read() == 0xd) {
+                               auto_key_phase++;
+                               break;
+                       }
+               }
+       case 30:
+               if(auto_key_buffer && !auto_key_buffer->empty()) {
+                       auto_key_phase = 1;
+               } else {
+                       stop_auto_key();
+               }
+               break;
+       default:
+               if(auto_key_phase) {
+                       auto_key_phase++;
+               }
+       }
+}
+#endif
+
+#ifdef USE_JOYSTICK
+void EMU::update_joystick()
+{
+       uint32_t *joy_buffer = osd->get_joy_buffer();
+       uint8_t *key_buffer = osd->get_key_buffer();
+       
+       memset(joy_status, 0, sizeof(joy_status));
+       
+       for(int i = 0; i < 4; i++) {
+               for(int j = 0; j < 16; j++) {
+                       if(config.joy_buttons[i][j] < 0) {
+                               int code = -config.joy_buttons[i][j];
+                               if(code < 256 && key_buffer[code]) {
+                                       joy_status[i] |= (1 << j);
+                               }
+                       } else {
+                               int stick = config.joy_buttons[i][j] >> 4;
+                               int button = config.joy_buttons[i][j] & 15;
+                               if(stick < 2 && (joy_buffer[stick & 3] & (1 << button))) {
+                                       joy_status[i] |= (1 << j);
+                               }
+                       }
+               }
+       }
+}
+#endif
+
+const uint8_t* EMU::get_key_buffer()
+{
+       return (const uint8_t*)osd->get_key_buffer();
+}
+
+#ifdef USE_JOYSTICK
+const uint32_t* EMU::get_joy_buffer()
+{
+       return (const uint32_t*)joy_status;
+}
+#endif
+
+#ifdef USE_MOUSE
+const int32_t* EMU::get_mouse_buffer()
+{
+       return (const int32_t*)osd->get_mouse_buffer();
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// screen
+// ----------------------------------------------------------------------------
+
+int EMU::get_window_mode_width(int mode)
+{
+       return osd->get_window_mode_width(mode);
+}
+
+int EMU::get_window_mode_height(int mode)
+{
+       return osd->get_window_mode_height(mode);
+}
+
+void EMU::set_host_window_size(int window_width, int window_height, bool window_mode)
+{
+       osd->set_host_window_size(window_width, window_height, window_mode);
+}
+
+void EMU::set_vm_screen_size(int screen_width, int screen_height, int window_width, int window_height, int window_width_aspect, int window_height_aspect)
+{
+       osd->set_vm_screen_size(screen_width, screen_height, window_width, window_height, window_width_aspect, window_height_aspect);
+}
+
+int EMU::get_vm_window_width()
+{
+       return osd->get_vm_window_width();
+}
+
+int EMU::get_vm_window_height()
+{
+       return osd->get_vm_window_height();
+}
+
+int EMU::get_vm_window_width_aspect()
+{
+       return osd->get_vm_window_width_aspect();
+}
+
+int EMU::get_vm_window_height_aspect()
+{
+       return osd->get_vm_window_height_aspect();
+}
+
+#if defined(USE_MINIMUM_RENDERING)
+bool EMU::is_screen_changed()
+{
+       return vm->is_screen_changed();
+}
+#endif
+
+int EMU::draw_screen()
+{
+       return osd->draw_screen();
+}
+
+scrntype_t* EMU::get_screen_buffer(int y)
+{
+       return osd->get_vm_screen_buffer(y);
+}
+
+#ifdef USE_CRT_FILTER
+void EMU::screen_skip_line(bool skip_line)
+{
+       osd->screen_skip_line = skip_line;
+}
+#endif
+
+#ifdef ONE_BOARD_MICRO_COMPUTER
+void EMU::reload_bitmap()
+{
+       osd->reload_bitmap();
+}
+#endif
+
+#ifdef OSD_WIN32
+void EMU::update_screen(HDC hdc)
+{
+       osd->update_screen(hdc);
+}
+#endif
+
+void EMU::capture_screen()
+{
+       osd->capture_screen();
+}
+
+bool EMU::start_record_video(int fps)
+{
+       return osd->start_record_video(fps);
+}
+
+void EMU::stop_record_video()
+{
+       osd->stop_record_video();
+}
+
+bool EMU::is_video_recording()
+{
+       return osd->now_record_video;
+}
+
+// ----------------------------------------------------------------------------
+// sound
+// ----------------------------------------------------------------------------
+
+void EMU::mute_sound()
+{
+       osd->mute_sound();
+}
+
+void EMU::start_record_sound()
+{
+       osd->start_record_sound();
+}
+
+void EMU::stop_record_sound()
+{
+       osd->stop_record_sound();
+}
+
+bool EMU::is_sound_recording()
+{
+       return osd->now_record_sound;
+}
+
+// ----------------------------------------------------------------------------
+// video
+// ----------------------------------------------------------------------------
+
+#if defined(USE_MOVIE_PLAYER) || defined(USE_VIDEO_CAPTURE)
+void EMU::get_video_buffer()
+{
+       osd->get_video_buffer();
+}
+
+void EMU::mute_video_dev(bool l, bool r)
+{
+       osd->mute_video_dev(l, r);
+}
+#endif
+
+#ifdef USE_MOVIE_PLAYER
+bool EMU::open_movie_file(const _TCHAR* file_path)
+{
+       return osd->open_movie_file(file_path);
+}
+
+void EMU::close_movie_file()
+{
+       osd->close_movie_file();
+}
+
+void EMU::play_movie()
+{
+       osd->play_movie();
+}
+
+void EMU::stop_movie()
+{
+       osd->stop_movie();
+}
+
+void EMU::pause_movie()
+{
+       osd->pause_movie();
+}
+
+double EMU::get_movie_frame_rate()
+{
+       return osd->get_movie_frame_rate();
+}
+
+int EMU::get_movie_sound_rate()
+{
+       return osd->get_movie_sound_rate();
+}
+
+void EMU::set_cur_movie_frame(int frame, bool relative)
+{
+       osd->set_cur_movie_frame(frame, relative);
+}
+
+uint32_t EMU::get_cur_movie_frame()
+{
+       return osd->get_cur_movie_frame();
+}
+#endif
+
+#ifdef USE_VIDEO_CAPTURE
+int EMU::get_cur_capture_dev_index()
+{
+       return osd->get_cur_capture_dev_index();
+}
+
+int EMU::get_num_capture_devs()
+{
+       return osd->get_num_capture_devs();
+}
+
+_TCHAR* EMU::get_capture_dev_name(int index)
+{
+       return osd->get_capture_dev_name(index);
+}
+
+void EMU::open_capture_dev(int index, bool pin)
+{
+       osd->open_capture_dev(index, pin);
+}
+
+void EMU::close_capture_dev()
+{
+       osd->close_capture_dev();
+}
+
+void EMU::show_capture_dev_filter()
+{
+       osd->show_capture_dev_filter();
+}
+
+void EMU::show_capture_dev_pin()
+{
+       osd->show_capture_dev_pin();
+}
+
+void EMU::show_capture_dev_source()
+{
+       osd->show_capture_dev_source();
+}
+
+void EMU::set_capture_dev_channel(int ch)
+{
+       osd->set_capture_dev_channel(ch);
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// printer
+// ----------------------------------------------------------------------------
+
+#ifdef USE_PRINTER
+void EMU::create_bitmap(bitmap_t *bitmap, int width, int height)
+{
+       osd->create_bitmap(bitmap, width, height);
+}
+
+void EMU::release_bitmap(bitmap_t *bitmap)
+{
+       osd->release_bitmap(bitmap);
+}
+
+void EMU::create_font(font_t *font, const _TCHAR *family, int width, int height, int rotate, bool bold, bool italic)
+{
+       osd->create_font(font, family, width, height, rotate, bold, italic);
+}
+
+void EMU::release_font(font_t *font)
+{
+       osd->release_font(font);
+}
+
+void EMU::create_pen(pen_t *pen, int width, uint8_t r, uint8_t g, uint8_t b)
+{
+       osd->create_pen(pen, width, r, g, b);
+}
+
+void EMU::release_pen(pen_t *pen)
+{
+       osd->release_pen(pen);
+}
+
+void EMU::clear_bitmap(bitmap_t *bitmap, uint8_t r, uint8_t g, uint8_t b)
+{
+       osd->clear_bitmap(bitmap, r, g, b);
+}
+
+int EMU::get_text_width(bitmap_t *bitmap, font_t *font, const char *text)
+{
+       return osd->get_text_width(bitmap, font, text);
+}
+
+void EMU::draw_text_to_bitmap(bitmap_t *bitmap, font_t *font, int x, int y, const char *text, uint8_t r, uint8_t g, uint8_t b)
+{
+       osd->draw_text_to_bitmap(bitmap, font, x, y, text, r, g, b);
+}
+
+void EMU::draw_line_to_bitmap(bitmap_t *bitmap, pen_t *pen, int sx, int sy, int ex, int ey)
+{
+       osd->draw_line_to_bitmap(bitmap, pen, sx, sy, ex, ey);
+}
+
+void EMU::draw_rectangle_to_bitmap(bitmap_t *bitmap, int x, int y, int width, int height, uint8_t r, uint8_t g, uint8_t b)
+{
+       osd->draw_rectangle_to_bitmap(bitmap, x, y, width, height, r, g, b);
+}
+
+void EMU::draw_point_to_bitmap(bitmap_t *bitmap, int x, int y, uint8_t r, uint8_t g, uint8_t b)
+{
+       osd->draw_point_to_bitmap(bitmap, x, y, r, g, b);
+}
+
+void EMU::stretch_bitmap(bitmap_t *dest, int dest_x, int dest_y, int dest_width, int dest_height, bitmap_t *source, int source_x, int source_y, int source_width, int source_height)
+{
+       osd->stretch_bitmap(dest, dest_x, dest_y, dest_width, dest_height, source, source_x, source_y, source_width, source_height);
+}
+
+void EMU::write_bitmap_to_file(bitmap_t *bitmap, const _TCHAR *file_path)
+{
+       osd->write_bitmap_to_file(bitmap, file_path);
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// socket
+// ----------------------------------------------------------------------------
+
+#ifdef USE_SOCKET
+int EMU::get_socket(int ch)
+{
+       return osd->get_socket(ch);
+}
+
+void EMU::notify_socket_connected(int ch)
+{
+       osd->notify_socket_connected(ch);
+}
+
+void EMU::notify_socket_disconnected(int ch)
+{
+       osd->notify_socket_disconnected(ch);
+}
+
+bool EMU::initialize_socket_tcp(int ch)
+{
+       return osd->initialize_socket_tcp(ch);
+}
+
+bool EMU::initialize_socket_udp(int ch)
+{
+       return osd->initialize_socket_udp(ch);
+}
+
+bool EMU::connect_socket(int ch, uint32_t ipaddr, int port)
+{
+       return osd->connect_socket(ch, ipaddr, port);
+}
+
+void EMU::disconnect_socket(int ch)
+{
+       osd->disconnect_socket(ch);
+}
+bool EMU::listen_socket(int ch)
+{
+       return osd->listen_socket(ch);
+}
+
+void EMU::send_socket_data_tcp(int ch)
+{
+       osd->send_socket_data_tcp(ch);
+}
+
+void EMU::send_socket_data_udp(int ch, uint32_t ipaddr, int port)
+{
+       osd->send_socket_data_udp(ch, ipaddr, port);
+}
+
+void EMU::send_socket_data(int ch)
+{
+       osd->send_socket_data(ch);
+}
+
+void EMU::recv_socket_data(int ch)
+{
+       osd->recv_socket_data(ch);
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// debug log
+// ----------------------------------------------------------------------------
+
+#ifdef _DEBUG_LOG
+void EMU::initialize_debug_log()
+{
+       _TCHAR path[_MAX_PATH];
+       debug_log = _tfopen(create_date_file_path(_T("log")), _T("w"));
+}
+
+void EMU::release_debug_log()
+{
+       if(debug_log) {
+               fclose(debug_log);
+               debug_log = NULL;
+       }
+}
+#endif
+
+#ifdef _DEBUG_LOG
+static _TCHAR prev_buffer[1024] = {0};
+#endif
+
+void EMU::out_debug_log(const _TCHAR* format, ...)
+{
+#ifdef _DEBUG_LOG
+       va_list ap;
+       _TCHAR buffer[1024];
+       
+       va_start(ap, format);
+       my_vstprintf_s(buffer, 1024, format, ap);
+       va_end(ap);
+       
+       if(_tcscmp(prev_buffer, buffer) == 0) {
+               return;
+       }
+       my_tcscpy_s(prev_buffer, 1024, buffer);
+       
+#if defined(_USE_QT) || defined(_USE_AGAR) || defined(_USE_SDL)
+       AGAR_DebugLog(AGAR_LOG_DEBUG, "%s", buffer);
+#else
+       if(debug_log) {
+               _ftprintf(debug_log, _T("%s"), buffer);
+               static int size = 0;
+               if((size += _tcslen(buffer)) > 0x8000000) { // 128MB
+                       fclose(debug_log);
+                       debug_log = _tfopen(create_date_file_path(_T("log")), _T("w"));
+                       size = 0;
+               }
+       }
+#endif
+#endif
+}
+
+void EMU::force_out_debug_log(const _TCHAR* format, ...)
+{
+#ifdef _DEBUG_LOG
+       va_list ap;
+       _TCHAR buffer[1024];
+       
+       va_start(ap, format);
+       my_vstprintf_s(buffer, 1024, format, ap);
+       va_end(ap);
+       my_tcscpy_s(prev_buffer, 1024, buffer);
+       
+#if defined(_USE_QT) || defined(_USE_AGAR) || defined(_USE_SDL)
+       AGAR_DebugLog(AGAR_LOG_DEBUG, "%s", buffer);
+#else
+       if(debug_log) {
+               _ftprintf(debug_log, _T("%s"), buffer);
+               static int size = 0;
+               if((size += _tcslen(buffer)) > 0x8000000) { // 128MB
+                       fclose(debug_log);
+                       debug_log = _tfopen(create_date_file_path(_T("log")), _T("w"));
+                       size = 0;
+               }
+       }
+#endif
+#endif
+}
+
+void EMU::out_message(const _TCHAR* format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       my_vstprintf_s(message, 1024, format, ap); // Security for MSVC:C6386.
+       va_end(ap);
+       message_count = 4; // 4sec
+}
+
+// ----------------------------------------------------------------------------
+// misc
+// ----------------------------------------------------------------------------
+
+
+void EMU::sleep(uint32_t ms)
+{
+       osd->sleep(ms);
+}
+
+
+// ----------------------------------------------------------------------------
+// user interface
+// ----------------------------------------------------------------------------
+
+static uint8_t hex2uint8(char *value)
+{
+       char tmp[3];
+       memset(tmp, 0, sizeof(tmp));
+       memcpy(tmp, value, 2);
+       return (uint8_t)strtoul(tmp, NULL, 16);
+}
+
+static uint16_t hex2uint16(char *value)
+{
+       char tmp[5];
+       memset(tmp, 0, sizeof(tmp));
+       memcpy(tmp, value, 4);
+       return (uint16_t)strtoul(tmp, NULL, 16);
+}
+
+static bool hex2bin(const _TCHAR* file_path, const _TCHAR* dest_path)
+{
+       bool result = false;
+       FILEIO *fio_s = new FILEIO();
+       if(fio_s->Fopen(file_path, FILEIO_READ_BINARY)) {
+               int length = 0;
+               char line[1024];
+               uint8_t buffer[0x10000];
+               memset(buffer, 0xff, sizeof(buffer));
+               while(fio_s->Fgets(line, sizeof(line)) != NULL) {
+                       if(line[0] != ':') continue;
+                       int bytes = hex2uint8(line + 1);
+                       int offset = hex2uint16(line + 3);
+                       uint8_t record_type = hex2uint8(line + 7);
+                       if(record_type == 0x01) break;
+                       if(record_type != 0x00) continue;
+                       for(int i = 0; i < bytes; i++) {
+                               if(offset + i < sizeof(buffer)) {
+                                       if(length < offset + i) {
+                                               length = offset + i;
+                                       }
+                                       buffer[offset + i] = hex2uint8(line + 9 + 2 * i);
+                               }
+                       }
+               }
+               if(length > 0) {
+                       FILEIO *fio_d = new FILEIO();
+                       if(fio_d->Fopen(dest_path, FILEIO_WRITE_BINARY)) {
+                               fio_d->Fwrite(buffer, length, 1);
+                               fio_d->Fclose();
+                               result = true;
+                       }
+                       delete fio_d;
+               }
+               fio_s->Fclose();
+       }
+       delete fio_s;
+       return result;
+}
+
+void EMU::initialize_media()
+{
+#ifdef USE_CART1
+       memset(&cart_status, 0, sizeof(cart_status));
+#endif
+#ifdef USE_FD1
+       memset(floppy_disk_status, 0, sizeof(floppy_disk_status));
+#endif
+#ifdef USE_QD1
+       memset(&quick_disk_status, 0, sizeof(quick_disk_status));
+#endif
+#ifdef USE_TAPE
+       memset(&tape_status, 0, sizeof(tape_status));
+#endif
+#ifdef USE_COMPACT_DISC
+       memset(&compact_disc_status, 0, sizeof(compact_disc_status));
+#endif
+#ifdef USE_LASER_DISC
+       memset(&laser_disc_status, 0, sizeof(laser_disc_status));
+#endif
+}
+
+
+void EMU::update_media()
+{
+#ifdef USE_FD1
+       for(int drv = 0; drv < MAX_FD; drv++) {
+               if(floppy_disk_status[drv].wait_count != 0 && --floppy_disk_status[drv].wait_count == 0) {
+                       vm->open_floppy_disk(drv, floppy_disk_status[drv].path, floppy_disk_status[drv].bank);
+                       out_message(_T("FD%d: %s"), drv + FD_BASE_NUMBER, floppy_disk_status[drv].path);
+               }
+       }
+#endif
+#ifdef USE_QD1
+       for(int drv = 0; drv < MAX_QD; drv++) {
+               if(quick_disk_status[drv].wait_count != 0 && --quick_disk_status[drv].wait_count == 0) {
+                       vm->open_quick_disk(drv, quick_disk_status[drv].path);
+                       out_message(_T("QD%d: %s"), drv + QD_BASE_NUMBER, quick_disk_status[drv].path);
+               }
+       }
+#endif
+#ifdef USE_TAPE
+       if(tape_status.wait_count != 0 && --tape_status.wait_count == 0) {
+               if(tape_status.play) {
+                       vm->play_tape(tape_status.path);
+               } else {
+                       vm->rec_tape(tape_status.path);
+               }
+               out_message(_T("CMT: %s"), tape_status.path);
+       }
+#endif
+#ifdef USE_COMPACT_DISC
+       if(compact_disc_status.wait_count != 0 && --compact_disc_status.wait_count == 0) {
+               vm->open_compact_disc(compact_disc_status.path);
+               out_message(_T("CD: %s"), compact_disc_status.path);
+       }
+#endif
+#ifdef USE_LASER_DISC
+       if(laser_disc_status.wait_count != 0 && --laser_disc_status.wait_count == 0) {
+               vm->open_laser_disc(laser_disc_status.path);
+               out_message(_T("LD: %s"), laser_disc_status.path);
+       }
+#endif
+#ifdef USE_BUBBLE1
+       for(int drv = 0; drv < MAX_BUBBLE; drv++) {
+               if(bubble_casette_status[drv].wait_count != 0 && --bubble_casette_status[drv].wait_count == 0) {
+                       vm->open_bubble_casette(drv, bubble_casette_status[drv].path, bubble_casette_status[drv].bank);
+                       out_message(_T("Bubble%d: %s"), drv, bubble_casette_status[drv].path);
+               }
+       }
+#endif
+}
+
+void EMU::restore_media()
+{
+#ifdef USE_CART1
+       for(int drv = 0; drv < MAX_CART; drv++) {
+               if(cart_status[drv].path[0] != _T('\0')) {
+                       if(check_file_extension(cart_status[drv].path, _T(".hex")) && hex2bin(cart_status[drv].path, create_local_path(_T("hex2bin.$$$")))) {
+                               vm->open_cart(drv, create_local_path(_T("hex2bin.$$$")));
+                               FILEIO::RemoveFile(create_local_path(_T("hex2bin.$$$")));
+                       } else {
+                               vm->open_cart(drv, cart_status[drv].path);
+                       }
+               }
+       }
+#endif
+#ifdef USE_FD1
+       for(int drv = 0; drv < MAX_FD; drv++) {
+               if(floppy_disk_status[drv].path[0] != _T('\0')) {
+                       vm->open_floppy_disk(drv, floppy_disk_status[drv].path, floppy_disk_status[drv].bank);
+               }
+       }
+#endif
+#ifdef USE_QD1
+       for(int drv = 0; drv < MAX_QD; drv++) {
+               if(quick_disk_status[drv].path[0] != _T('\0')) {
+                       vm->open_quick_disk(drv, quick_disk_status[drv].path);
+               }
+       }
+#endif
+#ifdef USE_TAPE
+       if(tape_status.path[0] != _T('\0')) {
+               if(tape_status.play) {
+                       vm->play_tape(tape_status.path);
+               } else {
+                       tape_status.path[0] = _T('\0');
+               }
+       }
+#endif
+#ifdef USE_COMPACT_DISC
+       if(compact_disc_status.path[0] != _T('\0')) {
+               vm->open_compact_disc(compact_disc_status.path);
+       }
+#endif
+#ifdef USE_LASER_DISC
+       if(laser_disc_status.path[0] != _T('\0')) {
+               vm->open_laser_disc(laser_disc_status.path);
+       }
+#endif
+#ifdef USE_BUBBLE1
+       for(int drv = 0; drv < MAX_BUBBLE; drv++) {
+               if(bubble_casette_status[drv].path[0] != _T('\0')) {
+                       vm->open_bubble_casette(drv, bubble_casette_status[drv].path, bubble_casette_status[drv].bank);
+               }
+       }
+#endif
+}
+
+#ifdef USE_CART1
+void EMU::open_cart(int drv, const _TCHAR* file_path)
+{
+       if(drv < MAX_CART) {
+               if(check_file_extension(file_path, _T(".hex")) && hex2bin(file_path, create_local_path(_T("hex2bin.$$$")))) {
+                       vm->open_cart(drv, create_local_path(_T("hex2bin.$$$")));
+                       FILEIO::RemoveFile(create_local_path(_T("hex2bin.$$$")));
+               } else {
+                       vm->open_cart(drv, file_path);
+               }
+               my_tcscpy_s(cart_status[drv].path, _MAX_PATH, file_path);
+               out_message(_T("Cart%d: %s"), drv + 1, file_path);
+               
+               // restart recording
+               bool s = osd->now_record_sound;
+               bool v = osd->now_record_video;
+               stop_record_sound();
+               stop_record_video();
+               if(s) osd->start_record_sound();
+               if(v) osd->start_record_video(-1);
+       }
+}
+
+void EMU::close_cart(int drv)
+{
+       if(drv < MAX_CART) {
+               vm->close_cart(drv);
+               clear_media_status(&cart_status[drv]);
+               out_message(_T("Cart%d: Ejected"), drv + 1);
+               
+               // stop recording
+               stop_record_video();
+               stop_record_sound();
+       }
+}
+
+bool EMU::is_cart_inserted(int drv)
+{
+       if(drv < MAX_CART) {
+               return vm->is_cart_inserted(drv);
+       } else {
+               return false;
+       }
+}
+#endif
+
+#ifdef USE_FD1
+void EMU::open_floppy_disk(int drv, const _TCHAR* file_path, int bank)
+{
+       if(drv < MAX_FD) {
+               if(vm->is_floppy_disk_inserted(drv)) {
+                       vm->close_floppy_disk(drv);
+                       // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+                       floppy_disk_status[drv].wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+                       floppy_disk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+                       out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);
+               } else if(floppy_disk_status[drv].wait_count == 0) {
+                       vm->open_floppy_disk(drv, file_path, bank);
+                       out_message(_T("FD%d: %s"), drv + FD_BASE_NUMBER, file_path);
+               }
+               my_tcscpy_s(floppy_disk_status[drv].path, _MAX_PATH, file_path);
+               floppy_disk_status[drv].bank = bank;
+       }
+}
+
+void EMU::close_floppy_disk(int drv)
+{
+       if(drv < MAX_FD) {
+               vm->close_floppy_disk(drv);
+               clear_media_status(&floppy_disk_status[drv]);
+               out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);
+       }
+}
+
+bool EMU::is_floppy_disk_inserted(int drv)
+{
+       if(drv < MAX_FD) {
+               return vm->is_floppy_disk_inserted(drv);
+       } else {
+               return false;
+       }
+}
+
+void EMU::is_floppy_disk_protected(int drv, bool value)
+{
+       if(drv < MAX_FD) {
+               vm->is_floppy_disk_protected(drv, value);
+       }
+}
+
+bool EMU::is_floppy_disk_protected(int drv)
+{
+       if(drv < MAX_FD) {
+               return vm->is_floppy_disk_protected(drv);
+       } else {
+               return false;
+       }
+}
+#endif
+
+#ifdef USE_QD1
+void EMU::open_quick_disk(int drv, const _TCHAR* file_path)
+{
+       if(drv < MAX_QD) {
+               if(vm->is_quick_disk_inserted(drv)) {
+                       vm->close_quick_disk(drv);
+                       // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+                       quick_disk_status[drv].wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+                       quick_disk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+                       out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);
+               } else if(quick_disk_status[drv].wait_count == 0) {
+                       vm->open_quick_disk(drv, file_path);
+                       out_message(_T("QD%d: %s"), drv + QD_BASE_NUMBER, file_path);
+               }
+               my_tcscpy_s(quick_disk_status[drv].path, _MAX_PATH, file_path);
+       }
+}
+
+void EMU::close_quick_disk(int drv)
+{
+       if(drv < MAX_QD) {
+               vm->close_quick_disk(drv);
+               clear_media_status(&quick_disk_status[drv]);
+               out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);
+       }
+}
+
+bool EMU::is_quick_disk_inserted(int drv)
+{
+       if(drv < MAX_QD) {
+               return vm->is_quick_disk_inserted(drv);
+       } else {
+               return false;
+       }
+}
+#endif
+
+#ifdef USE_TAPE
+void EMU::play_tape(const _TCHAR* file_path)
+{
+       if(vm->is_tape_inserted()) {
+               vm->close_tape();
+               // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+               tape_status.wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+               tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+               out_message(_T("CMT: Ejected"));
+       } else if(tape_status.wait_count == 0) {
+               vm->play_tape(file_path);
+               out_message(_T("CMT: %s"), file_path);
+       }
+       my_tcscpy_s(tape_status.path, _MAX_PATH, file_path);
+       tape_status.play = true;
+}
+
+void EMU::rec_tape(const _TCHAR* file_path)
+{
+       if(vm->is_tape_inserted()) {
+               vm->close_tape();
+               // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+               tape_status.wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+               tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+               out_message(_T("CMT: Ejected"));
+       } else if(tape_status.wait_count == 0) {
+               vm->rec_tape(file_path);
+               out_message(_T("CMT: %s"), file_path);
+       }
+       my_tcscpy_s(tape_status.path, _MAX_PATH, file_path);
+       tape_status.play = false;
+}
+
+void EMU::close_tape()
+{
+       vm->close_tape();
+       clear_media_status(&tape_status);
+       out_message(_T("CMT: Ejected"));
+}
+
+bool EMU::is_tape_inserted()
+{
+       return vm->is_tape_inserted();
+}
+
+#ifndef TAPE_BINARY_ONLY
+bool EMU::is_tape_playing()
+{
+       return vm->is_tape_playing();
+}
+
+bool EMU::is_tape_recording()
+{
+       return vm->is_tape_recording();
+}
+
+int EMU::get_tape_position()
+{
+       return vm->get_tape_position();
+}
+#endif
+
+#ifdef USE_TAPE_BUTTON
+void EMU::push_play()
+{
+       vm->push_play();
+}
+
+void EMU::push_stop()
+{
+       vm->push_stop();
+}
+
+void EMU::push_fast_forward()
+{
+       vm->push_fast_forward();
+}
+
+void EMU::push_fast_rewind()
+{
+       vm->push_fast_rewind();
+}
+
+void EMU::push_apss_forward()
+{
+       vm->push_apss_forward();
+}
+
+void EMU::push_apss_rewind()
+{
+       vm->push_apss_rewind();
+}
+#endif
+#endif
+
+#ifdef USE_COMPACT_DISC
+void EMU::open_compact_disc(const _TCHAR* file_path)
+{
+       if(vm->is_compact_disc_inserted()) {
+               vm->close_compact_disc();
+               // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+               compact_disc_status.wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+               compact_disc_status.wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+               out_message(_T("CD: Ejected"));
+       } else if(compact_disc_status.wait_count == 0) {
+               vm->open_compact_disc(file_path);
+               out_message(_T("CD: %s"), file_path);
+       }
+       my_tcscpy_s(compact_disc_status.path, _MAX_PATH, file_path);
+}
+
+void EMU::close_compact_disc()
+{
+       vm->close_compact_disc();
+       clear_media_status(&compact_disc_status);
+       out_message(_T("CD: Ejected"));
+}
+
+bool EMU::is_compact_disc_inserted()
+{
+       return vm->is_compact_disc_inserted();
+}
+#endif
+
+#ifdef USE_LASER_DISC
+void EMU::open_laser_disc(const _TCHAR* file_path)
+{
+       if(vm->is_laser_disc_inserted()) {
+               vm->close_laser_disc();
+               // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+               laser_disc_status.wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+               laser_disc_status.wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+               out_message(_T("LD: Ejected"));
+       } else if(laser_disc_status.wait_count == 0) {
+               vm->open_laser_disc(file_path);
+               out_message(_T("LD: %s"), file_path);
+       }
+       my_tcscpy_s(laser_disc_status.path, _MAX_PATH, file_path);
+}
+
+void EMU::close_laser_disc()
+{
+       vm->close_laser_disc();
+       clear_media_status(&laser_disc_status);
+       out_message(_T("LD: Ejected"));
+}
+
+bool EMU::is_laser_disc_inserted()
+{
+       return vm->is_laser_disc_inserted();
+}
+#endif
+
+#ifdef USE_BINARY_FILE1
+void EMU::load_binary(int drv, const _TCHAR* file_path)
+{
+       if(drv < MAX_BINARY) {
+               if(check_file_extension(file_path, _T(".hex")) && hex2bin(file_path, create_local_path(_T("hex2bin.$$$")))) {
+                       vm->load_binary(drv, create_local_path(_T("hex2bin.$$$")));
+                       FILEIO::RemoveFile(create_local_path(_T("hex2bin.$$$")));
+               } else {
+                       vm->load_binary(drv, file_path);
+               }
+               out_message(_T("Load: %s"), file_path);
+       }
+}
+
+void EMU::save_binary(int drv, const _TCHAR* file_path)
+{
+       if(drv < MAX_BINARY) {
+               vm->save_binary(drv, file_path);
+               out_message(_T("Save: %s"), file_path);
+       }
+}
+#endif
+#ifdef USE_BUBBLE1
+void EMU::open_bubble_casette(int drv, const _TCHAR* file_path, int bank)
+{
+       if(drv < MAX_BUBBLE) {
+               if(vm->is_bubble_casette_inserted(drv)) {
+                       vm->close_bubble_casette(drv);
+                       // wait 0.5sec
+#ifdef SUPPORT_VARIABLE_TIMING
+                       bubble_casette_status[drv].wait_count = (int)(vm->get_frame_rate() / 2);
+#else
+                       bubble_casette_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
+#endif
+                       out_message(_T("Bubble%d: Ejected"), drv + 1);
+               } else if(bubble_casette_status[drv].wait_count == 0) {
+                       vm->open_bubble_casette(drv, file_path, bank);
+                       out_message(_T("Bubble%d: %s"), drv + 1, file_path);
+               }
+               my_tcscpy_s(bubble_casette_status[drv].path, _MAX_PATH, file_path);
+               bubble_casette_status[drv].bank = bank;
+       }
+}
+
+void EMU::close_bubble_casette(int drv)
+{
+       if(drv < MAX_BUBBLE) {
+               vm->close_bubble_casette(drv);
+               clear_media_status(&bubble_casette_status[drv]);
+               out_message(_T("Bubble%d: Ejected"), drv + 1);
+       }
+}
+
+bool EMU::is_bubble_casette_inserted(int drv)
+{
+       if(drv < MAX_BUBBLE) {
+               return vm->is_bubble_casette_inserted(drv);
+       } else {
+               return false;
+       }
+}
+
+bool EMU::is_bubble_casette_protected(int drv)
+{
+       if(drv < MAX_BUBBLE) {
+               return vm->is_bubble_casette_protected(drv);
+       } else {
+               return false;
+       }
+}
+
+void EMU::is_bubble_casette_protected(int drv, bool flag)
+{
+       if(drv < MAX_BUBBLE) {
+               return vm->is_bubble_casette_protected(drv, flag);
+       } else {
+               return false;
+       }
+}
+#endif
+
+#ifdef USE_ACCESS_LAMP
+uint32_t EMU::get_access_lamp_status()
+{
+       return vm->get_access_lamp_status();
+}
+#endif
+
+#ifdef USE_LED_DEVICE
+uint32_t EMU::get_led_status()
+{
+       return vm->get_led_status();
+}
+#endif
+
+#ifdef USE_SOUND_VOLUME
+void EMU::set_sound_device_volume(int ch, int decibel_l, int decibel_r)
+{
+       vm->set_sound_device_volume(ch, decibel_l, decibel_r);
+}
+#endif
+
+void EMU::update_config()
+{
+       vm->update_config();
+}
+
+
+
+// ----------------------------------------------------------------------------
+// state
+// ----------------------------------------------------------------------------
+
+#ifdef USE_STATE
+#define STATE_VERSION  2
+
+void EMU::save_state()
+{
+       save_state_tmp(create_local_path(_T("%s.sta"), _T(CONFIG_NAME)));
+}
+
+void EMU::load_state()
+{
+       if(FILEIO::IsFileExisting(create_local_path(_T("%s.sta"), _T(CONFIG_NAME)))) {
+               save_state_tmp(create_local_path(_T("$temp$.sta")));
+               if(!load_state_tmp(create_local_path(_T("%s.sta"), _T(CONFIG_NAME)))) {
+                       out_debug_log(_T("failed to load state file\n"));
+                       load_state_tmp(create_local_path(_T("$temp$.sta")));
+               }
+               FILEIO::RemoveFile(create_local_path(_T("$temp$.sta")));
+       }
+}
+
+void EMU::save_state_tmp(const _TCHAR* file_path)
+{
+       FILEIO* fio = new FILEIO();
+       osd->lock_vm();
+       if(fio->Fopen(file_path, FILEIO_WRITE_BINARY)) {
+               // save state file version
+               fio->FputUint32(STATE_VERSION);
+               // save config
+               save_config_state((void *)fio);
+               // save inserted medias
+#ifdef USE_CART1
+               fio->Fwrite(&cart_status, sizeof(cart_status), 1);
+#endif
+#ifdef USE_FD1
+               fio->Fwrite(floppy_disk_status, sizeof(floppy_disk_status), 1);
+               fio->Fwrite(d88_file, sizeof(d88_file), 1);
+#endif
+#ifdef USE_QD1
+               fio->Fwrite(&quick_disk_status, sizeof(quick_disk_status), 1);
+#endif
+#ifdef USE_TAPE
+               fio->Fwrite(&tape_status, sizeof(tape_status), 1);
+#endif
+#ifdef USE_COMPACT_DISC
+               fio->Fwrite(&compact_disc_status, sizeof(compact_disc_status), 1);
+#endif
+#ifdef USE_LASER_DISC
+               fio->Fwrite(&laser_disc_status, sizeof(laser_disc_status), 1);
+#endif
+               // save vm state
+               vm->save_state(fio);
+               // end of state file
+               fio->FputInt32(-1);
+               fio->Fclose();
+       }
+       osd->unlock_vm();
+       delete fio;
+}
+
+bool EMU::load_state_tmp(const _TCHAR* file_path)
+{
+       bool result = false;
+       FILEIO* fio = new FILEIO();
+       osd->lock_vm();
+       if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
+               // check state file version
+               if(fio->FgetUint32() == STATE_VERSION) {
+                       // load config
+                       if(load_config_state((void *)fio)) {
+                               // load inserted medias
+#ifdef USE_CART1
+                               fio->Fread(&cart_status, sizeof(cart_status), 1);
+#endif
+#ifdef USE_FD1
+                               fio->Fread(floppy_disk_status, sizeof(floppy_disk_status), 1);
+                               fio->Fread(d88_file, sizeof(d88_file), 1);
+#endif
+#ifdef USE_QD1
+                               fio->Fread(&quick_disk_status, sizeof(quick_disk_status), 1);
+#endif
+#ifdef USE_TAPE
+                               fio->Fread(&tape_status, sizeof(tape_status), 1);
+#endif
+#ifdef USE_COMPACT_DISC
+                               fio->Fread(&compact_disc_status, sizeof(compact_disc_status), 1);
+#endif
+#ifdef USE_LASER_DISC
+                               fio->Fread(&laser_disc_status, sizeof(laser_disc_status), 1);
+#endif
+                               // check if virtual machine should be reinitialized
+                               bool reinitialize = false;
+#ifdef USE_CPU_TYPE
+                               reinitialize |= (cpu_type != config.cpu_type);
+                               cpu_type = config.cpu_type;
+#endif
+#ifdef USE_SOUND_DEVICE_TYPE
+                               reinitialize |= (sound_device_type != config.sound_device_type);
+                               sound_device_type = config.sound_device_type;
+#endif
+#ifdef USE_PRINTER
+                               reinitialize |= (printer_device_type != config.printer_device_type);
+                               printer_device_type = config.printer_device_type;
+#endif
+                               if(reinitialize) {
+                                       // stop sound
+                                       //osd->lock_vm();
+                                       // reinitialize virtual machine
+                                       osd->stop_sound();
+                                       delete vm;
+                                       osd->vm = vm = new VM(this);
+                                       vm->initialize_sound(sound_rate, sound_samples);
+#ifdef USE_SOUND_VOLUME
+                                       for(int i = 0; i < USE_SOUND_VOLUME; i++) {
+                                               vm->set_sound_device_volume(i, config.sound_volume_l[i], config.sound_volume_r[i]);
+                                       }
+#endif
+                                       vm->reset();
+                                       //osd->unlock_vm();
+                               }
+                               // restore inserted medias
+                               restore_media();
+                               // load vm state
+                               if(vm->load_state(fio)) {
+                                       // check end of state
+                                       result = (fio->FgetInt32() == -1);
+                               }
+                       }
+               }
+               fio->Fclose();
+       }
+       osd->unlock_vm();
+       delete fio;
+       return result;
+}
+#endif