OSDN Git Service

[General] Convert sourcecode's CRLF format: DOS(WINDOWS) to Unix, to apply patches...
[csp-qt/common_source_project-fm7.git] / source / src / emu.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.08.18 -
6
7         [ win32 emulation i/f ]
8 */
9
10 #include "emu.h"
11 #include "vm/vm.h"
12 #include "fileio.h"
13 #if defined(_USE_AGAR)
14 #include <SDL/SDL.h>
15 #include "agar_main.h"
16 #include "agar_logger.h"
17 #include <ctime>
18 # elif defined(_USE_QT)
19 //#include <SDL/SDL.h>
20 #include "qt_main.h"
21 #include "agar_logger.h"
22 #include <ctime>
23 # endif
24
25 #ifndef FD_BASE_NUMBER
26 #define FD_BASE_NUMBER 1
27 #endif
28 #ifndef QD_BASE_NUMBER
29 #define QD_BASE_NUMBER 1
30 #endif
31
32 // ----------------------------------------------------------------------------
33 // initialize
34 // ----------------------------------------------------------------------------
35 #if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)
36 extern void get_long_full_path_name(_TCHAR* src, _TCHAR* dst);
37 #include <string>
38 #endif
39
40 #if defined(_USE_QGAR)
41 EMU::EMU(AG_Window *hwnd, AG_Widget *hinst)
42 #elif defined(_USE_QT)
43 EMU::EMU(Ui_MainWindow *hwnd, GLDrawClass *hinst)
44 #else
45 EMU::EMU(HWND hwnd, HINSTANCE hinst)
46 #endif
47 {
48 #ifdef _DEBUG_LOG
49         // open debug logfile
50         initialize_debug_log();
51 #endif
52         message_count = 0;
53         
54         // store main window handle
55         main_window_handle = hwnd;
56         instance_handle = hinst;
57         
58         // get module path
59         // Initialize keymod.
60         modkey_status = 0;
61 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
62         std::string tmps;
63 #ifndef _USE_QT
64         _TCHAR tmp_path[PATH_MAX], *ptr;
65         my_procname.copy(tmp_path, PATH_MAX, 0);
66         get_long_full_path_name(tmp_path, app_path);
67         //AGAR_DebugLog("APPPATH=%s\n", app_path);
68         if(AG_UsingGL(AGDRIVER(main_window_handle))) {
69            use_opengl = true;
70            use_opencl = false;
71         } else {
72            use_opencl = false;
73            use_opengl = false;
74         }
75 #else
76         _TCHAR tmp_path[PATH_MAX], *ptr;
77         my_procname.copy(tmp_path, PATH_MAX, 0);
78         get_long_full_path_name(tmp_path, app_path);
79         //AGAR_DebugLog("APPPATH=%s\n", app_path);
80         use_opengl = true;
81         use_opencl = false;
82 #endif
83         pVMSemaphore = SDL_CreateSemaphore(1);
84 #else
85         _TCHAR tmp_path[_MAX_PATH], *ptr;
86         GetModuleFileName(NULL, tmp_path, _MAX_PATH);
87         GetFullPathName(tmp_path, _MAX_PATH, app_path, &ptr);
88         *ptr = _T('\0');
89 #endif  
90 #ifdef USE_FD1
91         // initialize d88 file info
92         memset(d88_file, 0, sizeof(d88_file));
93 #endif
94         
95         // load sound config
96         static const int freq_table[8] = {
97                 2000, 4000, 8000, 11025, 22050, 44100,
98 #ifdef OVERRIDE_SOUND_FREQ_48000HZ
99                 OVERRIDE_SOUND_FREQ_48000HZ,
100 #else
101                 48000,
102 #endif
103                 96000,
104         };
105         static const double late_table[5] = {0.05, 0.1, 0.2, 0.3, 0.4};
106         
107         if(!(0 <= config.sound_frequency && config.sound_frequency < 8)) {
108                 config.sound_frequency = 6;     // default: 48KHz
109         }
110         if(!(0 <= config.sound_latency && config.sound_latency < 5)) {
111                 config.sound_latency = 1;       // default: 100msec
112         }
113         sound_rate = freq_table[config.sound_frequency];
114         sound_samples = (int)(sound_rate * late_table[config.sound_latency] + 0.5);
115         
116 #ifdef USE_CPU_TYPE
117         cpu_type = config.cpu_type;
118 #endif
119 #ifdef USE_SOUND_DEVICE_TYPE
120         sound_device_type = config.sound_device_type;
121 #endif
122         
123         // initialize
124         vm = new VM(this);
125 #ifdef USE_DEBUGGER
126         initialize_debugger();
127 #endif
128         initialize_input();
129         initialize_screen();
130         initialize_sound();
131         initialize_media();
132         initialize_printer();
133 #ifdef USE_SOCKET
134         initialize_socket();
135 #endif
136 #ifndef _USE_QT
137 # ifdef USE_DIRECT_SHOW
138         CoInitialize(NULL);
139         initialize_direct_show();
140 # endif
141 #endif
142         vm->initialize_sound(sound_rate, sound_samples);
143         vm->reset();
144         now_suspended = false;
145 }
146
147 EMU::~EMU()
148 {
149 #ifdef USE_DEBUGGER
150         release_debugger();
151 #endif
152         release_input();
153         release_screen();
154         release_sound();
155         release_printer();
156 #ifdef USE_SOCKET
157         release_socket();
158 #endif
159 #ifndef _USE_QT
160 #ifdef USE_DIRECT_SHOW
161         release_direct_show();
162         CoInitialize(NULL);
163 #endif
164 #endif
165    
166 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
167        if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
168 #endif
169         delete vm;
170 #ifdef _DEBUG_LOG
171         release_debug_log();
172 #endif
173 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
174       if(pVMSemaphore) SDL_DestroySemaphore(pVMSemaphore);
175 #endif
176 }
177
178 // ----------------------------------------------------------------------------
179 // drive machine
180 // ----------------------------------------------------------------------------
181
182 int EMU::frame_interval()
183 {
184 #if 1
185 #ifdef SUPPORT_VARIABLE_TIMING
186         static int prev_interval = 0;
187         static double prev_fps = -1;
188         double fps = vm->frame_rate();
189         if(prev_fps != fps) {
190                 prev_interval = (int)(1024. * 1000. / fps + 0.5);
191                 prev_fps = fps;
192         }
193         return prev_interval;
194 #else
195         return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);
196 #endif
197 #else
198         return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);
199 #endif
200 }
201
202 int EMU::run()
203 {
204         if(now_suspended) {
205 #ifdef USE_LASER_DISC
206                 if(now_movie_play && !now_movie_pause) {
207                         play_movie();
208                 }
209 #endif
210                 now_suspended = false;
211         }
212 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
213         if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
214 #endif
215         
216         update_input();
217         update_media();
218         update_printer();
219 #ifdef USE_SOCKET
220         update_socket();
221 #endif
222         
223         // virtual machine may be driven to fill sound buffer
224         int extra_frames = 0;
225         update_sound(&extra_frames);
226         
227         // drive virtual machine
228         if(extra_frames == 0) {
229                 vm->run();
230 //              printf("VM:RUN() %d\n", AG_GetTicks());
231                 extra_frames = 1;
232         }
233         rec_video_run_frames += extra_frames;
234 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
235         if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
236 #endif
237         return extra_frames;
238 }
239
240 void EMU::reset()
241 {
242         // check if virtual machine should be reinitialized
243         bool reinitialize = false;
244 #ifdef USE_CPU_TYPE
245         reinitialize |= (cpu_type != config.cpu_type);
246         cpu_type = config.cpu_type;
247 #endif
248 #ifdef USE_SOUND_DEVICE_TYPE
249         reinitialize |= (sound_device_type != config.sound_device_type);
250         sound_device_type = config.sound_device_type;
251 #endif
252         if(reinitialize) {
253                 // stop sound
254                 if(sound_ok && sound_started) {
255 #if defined(_USE_SDL) || defined(_USE_AGAR) || defined(_USE_QT)
256                         //bSndExit = true;
257                         SDL_PauseAudio(1);
258 #else
259                         lpdsb->Stop();
260 #endif
261                         sound_started = false;
262                 }
263                 // reinitialize virtual machine
264 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
265         if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
266 #endif
267                 delete vm;
268                 vm = new VM(this);
269                 vm->initialize_sound(sound_rate, sound_samples);
270                 vm->reset();
271 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
272         if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
273 #endif
274                 // restore inserted medias
275                 restore_media();
276         } else {
277            // reset virtual machine
278                 vm->reset();
279         }
280         
281         // reset printer
282         reset_printer();
283         
284         // restart recording
285         restart_rec_sound();
286         restart_rec_video();
287 }
288
289 #ifdef USE_SPECIAL_RESET
290 void EMU::special_reset()
291 {
292         // reset virtual machine
293         vm->special_reset();
294         
295         // reset printer
296         reset_printer();
297         
298         // restart recording
299         restart_rec_sound();
300         restart_rec_video();
301 }
302 #endif
303
304 #ifdef USE_POWER_OFF
305 void EMU::notify_power_off()
306 {
307         vm->notify_power_off();
308 }
309 #endif
310
311 _TCHAR* EMU::bios_path(_TCHAR* file_name)
312 {
313         static _TCHAR file_path[_MAX_PATH];
314         _stprintf_s(file_path, _MAX_PATH, _T("%s%s"), app_path, file_name);
315         return file_path;
316 //#if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)
317 //        static _TCHAR file_path[_MAX_PATH];
318 //        strcpy(file_path, app_path);
319 //        strcat(file_path, file_name);
320 //#if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)
321 //        AGAR_DebugLog(AGAR_LOG_INFO, "LOAD BIOS: %s\n", file_path);
322 //#endif
323 //#else
324 //        static _TCHAR file_path[_MAX_PATH];
325 //      _stprintf(file_path, _T("%s%s"), app_path, file_name);
326 //        printf("LOAD: %s\n", file_path);
327 //#endif
328 //      return file_path;
329 }
330
331 void EMU::suspend()
332 {
333         if(!now_suspended) {
334 #ifdef USE_LASER_DISC
335 #ifndef _USE_QT // WILLFIX
336            if(now_movie_play && !now_movie_pause) {
337                         pause_movie();
338                         now_movie_pause = false;
339                 }
340 #endif
341 #endif
342                 mute_sound();
343                 now_suspended = true;
344         }
345 }
346
347 // ----------------------------------------------------------------------------
348 // timer
349 // ----------------------------------------------------------------------------
350
351 void EMU::get_host_time(cur_time_t* t)
352 {
353 #if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)
354         std::tm *tm;
355         std::time_t tnow;
356         tnow = std::time(NULL);
357         tm = std::localtime(&tnow);
358
359         t->year = tm->tm_year + 1900;
360         t->month = tm->tm_mon + 1;
361         t->day = tm->tm_mday;
362         t->day_of_week = tm->tm_wday;
363         t->hour = tm->tm_hour;
364         t->minute = tm->tm_min;
365         t->second = tm->tm_sec;
366 #else
367         SYSTEMTIME sTime;
368         GetLocalTime(&sTime);
369         
370         t->year = sTime.wYear;
371         t->month = sTime.wMonth;
372         t->day = sTime.wDay;
373         t->day_of_week = sTime.wDayOfWeek;
374         t->hour = sTime.wHour;
375         t->minute = sTime.wMinute;
376         t->second = sTime.wSecond;
377 #endif
378 }
379
380 // ----------------------------------------------------------------------------
381 // printer
382 // ----------------------------------------------------------------------------
383
384 void EMU::initialize_printer()
385 {
386         prn_fio = new FILEIO();
387         prn_data = -1;
388         prn_strobe = false;
389 }
390
391 void EMU::release_printer()
392 {
393         close_printer_file();
394         delete prn_fio;
395 }
396
397 void EMU::reset_printer()
398 {
399         close_printer_file();
400         prn_data = -1;
401         prn_strobe = false;
402 }
403
404 void EMU::update_printer()
405 {
406         if(prn_fio->IsOpened() && --prn_wait_frames == 0) {
407                 close_printer_file();
408         }
409 }
410
411 void EMU::open_printer_file()
412 {
413         cur_time_t time;
414         get_host_time(&time);
415         _stprintf_s(prn_file_name, _MAX_PATH, _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);
416         prn_fio->Fopen(bios_path(prn_file_name), FILEIO_WRITE_BINARY);
417 }
418
419 void EMU::close_printer_file()
420 {
421         if(prn_fio->IsOpened()) {
422                 // remove if the file size is less than 2 bytes
423                 bool remove = (prn_fio->Ftell() < 2);
424                 prn_fio->Fclose();
425                 if(remove) {
426                         prn_fio->Remove(bios_path(prn_file_name));
427                 }
428         }
429 }
430
431 void EMU::printer_out(uint8 value)
432 {
433         prn_data = value;
434 }
435
436 void EMU::printer_strobe(bool value)
437 {
438         bool falling = (prn_strobe && !value);
439         prn_strobe = value;
440         
441         if(falling) {
442                 if(!prn_fio->IsOpened()) {
443                         if(prn_data == -1) {
444                                 return;
445                         }
446                         open_printer_file();
447                 }
448                 prn_fio->Fputc(prn_data);
449                 // wait 10sec
450 #ifdef SUPPORT_VARIABLE_TIMING
451                 prn_wait_frames = (int)(vm->frame_rate() * 10.0 + 0.5);
452 #else
453                 prn_wait_frames = (int)(FRAMES_PER_SEC * 10.0 + 0.5);
454 #endif
455         }
456 }
457
458 // ----------------------------------------------------------------------------
459 // debug log
460 // ----------------------------------------------------------------------------
461
462 #ifdef _DEBUG_LOG
463 void EMU::initialize_debug_log()
464 {
465         if(_tfopen_s(&debug_log, _T("d:\\debug.log"), _T("w")) != 0) {
466                 debug_log = NULL;
467         }
468 }
469
470 void EMU::release_debug_log()
471 {
472         if(debug_log) {
473                 fclose(debug_log);
474         }
475 }
476 #endif
477
478 void EMU::out_debug_log(const _TCHAR* format, ...)
479 {
480 #ifdef _DEBUG_LOG
481         va_list ap;
482         _TCHAR buffer[1024];
483         static _TCHAR prev_buffer[1024] = {0};
484         
485         va_start(ap, format);
486         _vstprintf_s(buffer, 1024, format, ap);
487         va_end(ap);
488         
489         if(_tcscmp(prev_buffer, buffer) == 0) {
490                 return;
491         }
492         _tcscpy_s(prev_buffer, 1024, buffer);
493         
494         if(debug_log) {
495                 _ftprintf(debug_log, _T("%s"), buffer);
496                 static int size = 0;
497                 if((size += _tcslen(buffer)) > 0x8000000) { // 128MB
498                         static int index = 1;
499                         TCHAR path[_MAX_PATH];
500                         _stprintf_s(path, _MAX_PATH, _T("d:\\debug_#%d.log"), ++index);
501                         fclose(debug_log);
502                         if(_tfopen_s(&debug_log, path, _T("w")) != 0) {
503                                 debug_log = NULL;
504                         }
505                         size = 0;
506                 }
507         }
508 #endif
509 }
510
511 void EMU::out_message(const _TCHAR* format, ...)
512 {
513         va_list ap;
514         va_start(ap, format);
515         _vstprintf_s(message, 1024, format, ap);
516         va_end(ap);
517         message_count = 4; // 4sec
518 }
519
520 // ----------------------------------------------------------------------------
521 // user interface
522 // ----------------------------------------------------------------------------
523
524 void EMU::initialize_media()
525 {
526 #ifdef USE_CART1
527         memset(&cart_status, 0, sizeof(cart_status));
528 #endif
529 #ifdef USE_FD1
530         memset(disk_status, 0, sizeof(disk_status));
531 #endif
532 #ifdef USE_QD1
533         memset(&quickdisk_status, 0, sizeof(quickdisk_status));
534 #endif
535 #ifdef USE_TAPE
536         memset(&tape_status, 0, sizeof(tape_status));
537 #endif
538 #ifdef USE_LASER_DISC
539         memset(&laser_disc_status, 0, sizeof(laser_disc_status));
540 #endif
541 }
542
543 #if defined(USE_FD1) || defined(USE_FD2) || defined(USE_FD3) || defined(USE_FD4) || \
544     defined(USE_FD5) || defined(USE_FD6) || defined(USE_FD7) || defined(USE_FD8)
545
546
547 void EMU::write_protect_fd(int drv, bool flag)
548 {
549   vm->write_protect_fd(drv, flag);
550 }
551 bool EMU::is_write_protected_fd(int drv)
552 {
553   return vm->is_write_protect_fd(drv);
554 }
555 #endif
556
557 void EMU::update_media()
558 {
559 #ifdef USE_FD1
560         for(int drv = 0; drv < MAX_FD; drv++) {
561                 if(disk_status[drv].wait_count != 0 && --disk_status[drv].wait_count == 0) {
562                         vm->open_disk(drv, disk_status[drv].path, disk_status[drv].offset);
563                         out_message(_T("FD%d: %s"), drv + FD_BASE_NUMBER, disk_status[drv].path);
564                 }
565         }
566 #endif
567 #ifdef USE_QD1
568         for(int drv = 0; drv < MAX_QD; drv++) {
569                 if(quickdisk_status[drv].wait_count != 0 && --quickdisk_status[drv].wait_count == 0) {
570                         vm->open_quickdisk(drv, quickdisk_status[drv].path);
571                         out_message(_T("QD%d: %s"), drv + QD_BASE_NUMBER, quickdisk_status[drv].path);
572                 }
573         }
574 #endif
575 #ifdef USE_TAPE
576         if(tape_status.wait_count != 0 && --tape_status.wait_count == 0) {
577                 if(tape_status.play) {
578                         vm->play_tape(tape_status.path);
579                 } else {
580                         vm->rec_tape(tape_status.path);
581                 }
582                 out_message(_T("CMT: %s"), tape_status.path);
583         }
584 #endif
585 #ifdef USE_LASER_DISC
586         if(laser_disc_status.wait_count != 0 && --laser_disc_status.wait_count == 0) {
587                 vm->open_laser_disc(laser_disc_status.path);
588                 out_message(_T("LD: %s"), laser_disc_status.path);
589         }
590 #endif
591 }
592
593 void EMU::restore_media()
594 {
595 #ifdef USE_CART1
596         for(int drv = 0; drv < MAX_CART; drv++) {
597                 if(cart_status[drv].path[0] != _T('\0')) {
598                         vm->open_cart(drv, cart_status[drv].path);
599                 }
600         }
601 #endif
602 #ifdef USE_FD1
603         for(int drv = 0; drv < MAX_FD; drv++) {
604                 if(disk_status[drv].path[0] != _T('\0')) {
605                         vm->open_disk(drv, disk_status[drv].path, disk_status[drv].offset);
606                 }
607         }
608 #endif
609 #ifdef USE_QD1
610         for(int drv = 0; drv < MAX_QD; drv++) {
611                 if(quickdisk_status[drv].path[0] != _T('\0')) {
612                         vm->open_quickdisk(drv, quickdisk_status[drv].path);
613                 }
614         }
615 #endif
616 #ifdef USE_TAPE
617         if(tape_status.path[0] != _T('\0')) {
618                 if(tape_status.play) {
619                         vm->play_tape(tape_status.path);
620                 } else {
621                         tape_status.path[0] = _T('\0');
622                 }
623         }
624 #endif
625 #ifdef USE_LASER_DISC
626         if(laser_disc_status.path[0] != _T('\0')) {
627                 vm->open_laser_disc(laser_disc_status.path);
628         }
629 #endif
630 }
631
632 #ifdef USE_CART1
633 void EMU::open_cart(int drv, _TCHAR* file_path)
634 {
635         if(drv < MAX_CART) {
636                 vm->open_cart(drv, file_path);
637                 _tcscpy_s(cart_status[drv].path, _MAX_PATH, file_path);
638                 out_message(_T("Cart%d: %s"), drv + 1, file_path);
639                 
640                 // restart recording
641                 bool s = now_rec_sound;
642                 bool v = now_rec_video;
643                 stop_rec_sound();
644                 stop_rec_video();
645                 if(s) start_rec_sound();
646                 if(v) start_rec_video(-1);
647         }
648 }
649
650 void EMU::close_cart(int drv)
651 {
652         if(drv < MAX_CART) {
653                 vm->close_cart(drv);
654                 clear_media_status(&cart_status[drv]);
655                 out_message(_T("Cart%d: Ejected"), drv + 1);
656                 
657                 // stop recording
658                 stop_rec_video();
659                 stop_rec_sound();
660         }
661 }
662
663 bool EMU::cart_inserted(int drv)
664 {
665         if(drv < MAX_CART) {
666                 return vm->cart_inserted(drv);
667         } else {
668                 return false;
669         }
670 }
671 #endif
672
673 #ifdef USE_FD1
674 void EMU::open_disk(int drv, _TCHAR* file_path, int offset)
675 {
676         if(drv < MAX_FD) {
677                 if(vm->disk_inserted(drv)) {
678                         vm->close_disk(drv);
679                         // wait 0.5sec
680 #ifdef SUPPORT_VARIABLE_TIMING
681                         disk_status[drv].wait_count = (int)(vm->frame_rate() / 2);
682 #else
683                         disk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
684 #endif
685                         out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);
686                 } else if(disk_status[drv].wait_count == 0) {
687                         vm->open_disk(drv, file_path, offset);
688                         out_message(_T("FD%d: %s"), drv + FD_BASE_NUMBER, file_path);
689                 }
690                 _tcscpy_s(disk_status[drv].path, _MAX_PATH, file_path);
691                 disk_status[drv].offset = offset;
692         }
693 }
694
695 void EMU::close_disk(int drv)
696 {
697         if(drv < MAX_FD) {
698                 vm->close_disk(drv);
699                 clear_media_status(&disk_status[drv]);
700                 out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);
701         }
702 }
703
704 bool EMU::disk_inserted(int drv)
705 {
706         if(drv < MAX_FD) {
707                 return vm->disk_inserted(drv);
708         } else {
709                 return false;
710         }
711 }
712 #endif
713
714 int EMU::get_access_lamp(void)
715 {
716    int stat = 0;
717 #if defined(USE_FD1) || defined(USE_QD1)
718    LockVM();
719    stat = vm->access_lamp(); // Return accessing drive number.
720    UnlockVM();
721 #endif
722    return stat;
723 }
724
725
726 #ifdef USE_QD1
727 void EMU::open_quickdisk(int drv, _TCHAR* file_path)
728 {
729         if(drv < MAX_QD) {
730                 if(vm->quickdisk_inserted(drv)) {
731                         vm->close_quickdisk(drv);
732                         // wait 0.5sec
733 #ifdef SUPPORT_VARIABLE_TIMING
734                         quickdisk_status[drv].wait_count = (int)(vm->frame_rate() / 2);
735 #else
736                         quickdisk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
737 #endif
738                         out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);
739                 } else if(quickdisk_status[drv].wait_count == 0) {
740                         vm->open_quickdisk(drv, file_path);
741                         out_message(_T("QD%d: %s"), drv + QD_BASE_NUMBER, file_path);
742                 }
743                 _tcscpy_s(quickdisk_status[drv].path, _MAX_PATH, file_path);
744         }
745 }
746
747 void EMU::close_quickdisk(int drv)
748 {
749         if(drv < MAX_QD) {
750                 vm->close_quickdisk(drv);
751                 clear_media_status(&quickdisk_status[drv]);
752                 out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);
753         }
754 }
755
756 bool EMU::quickdisk_inserted(int drv)
757 {
758         if(drv < MAX_QD) {
759                 return vm->quickdisk_inserted(drv);
760         } else {
761                 return false;
762         }
763 }
764 #endif
765
766 #ifdef USE_TAPE
767 void EMU::play_tape(_TCHAR* file_path)
768 {
769         if(vm->tape_inserted()) {
770                 vm->close_tape();
771                 // wait 0.5sec
772 #ifdef SUPPORT_VARIABLE_TIMING
773                 tape_status.wait_count = (int)(vm->frame_rate() / 2);
774 #else
775                 tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);
776 #endif
777                 out_message(_T("CMT: Ejected"));
778         } else if(tape_status.wait_count == 0) {
779                 vm->play_tape(file_path);
780                 out_message(_T("CMT: %s"), file_path);
781         }
782         _tcscpy_s(tape_status.path, _MAX_PATH, file_path);
783         tape_status.play = true;
784 }
785
786 void EMU::rec_tape(_TCHAR* file_path)
787 {
788         if(vm->tape_inserted()) {
789                 vm->close_tape();
790                 // wait 0.5sec
791 #ifdef SUPPORT_VARIABLE_TIMING
792                 tape_status.wait_count = (int)(vm->frame_rate() / 2);
793 #else
794                 tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);
795 #endif
796                 out_message(_T("CMT: Ejected"));
797         } else if(tape_status.wait_count == 0) {
798                 vm->rec_tape(file_path);
799                 out_message(_T("CMT: %s"), file_path);
800         }
801         _tcscpy_s(tape_status.path, _MAX_PATH, file_path);
802         tape_status.play = false;
803 }
804
805 void EMU::close_tape()
806 {
807         vm->close_tape();
808         clear_media_status(&tape_status);
809         out_message(_T("CMT: Ejected"));
810 }
811
812 bool EMU::tape_inserted()
813 {
814         return vm->tape_inserted();
815 }
816 #endif
817
818 #ifdef USE_LASER_DISC
819 void EMU::open_laser_disc(_TCHAR* file_path)
820 {
821         if(vm->laser_disc_inserted()) {
822                 vm->close_laser_disc();
823                 // wait 0.5sec
824 #ifdef SUPPORT_VARIABLE_TIMING
825                 laser_disc_status.wait_count = (int)(vm->frame_rate() / 2);
826 #else
827                 laser_disc_status.wait_count = (int)(FRAMES_PER_SEC / 2);
828 #endif
829                 out_message(_T("LD: Ejected"));
830         } else if(laser_disc_status.wait_count == 0) {
831                 vm->open_laser_disc(file_path);
832                 out_message(_T("LD: %s"), file_path);
833         }
834         _tcscpy_s(laser_disc_status.path, _MAX_PATH, file_path);
835 }
836
837 void EMU::close_laser_disc()
838 {
839         vm->close_laser_disc();
840         clear_media_status(&laser_disc_status);
841         out_message(_T("LD: Ejected"));
842 }
843
844 bool EMU::laser_disc_inserted()
845 {
846         return vm->laser_disc_inserted();
847 }
848 #endif
849
850 #ifdef USE_TAPE_BUTTON
851 void EMU::push_play()
852 {
853         vm->push_play();
854 }
855
856 void EMU::push_stop()
857 {
858         vm->push_stop();
859 }
860 #endif
861
862 #ifdef USE_BINARY_FILE1
863 void EMU::load_binary(int drv, _TCHAR* file_path)
864 {
865         if(drv < MAX_BINARY) {
866                 vm->load_binary(drv, file_path);
867                 out_message(_T("Load: %s"), file_path);
868         }
869 }
870
871 void EMU::save_binary(int drv, _TCHAR* file_path)
872 {
873         if(drv < MAX_BINARY) {
874                 vm->save_binary(drv, file_path);
875                 out_message(_T("Save: %s"), file_path);
876         }
877 }
878 #endif
879
880 bool EMU::now_skip()
881 {
882         return vm->now_skip();
883 }
884
885 void EMU::update_config()
886 {
887         vm->update_config();
888 }
889
890 #ifdef USE_STATE
891 // ----------------------------------------------------------------------------
892 // state
893 // ----------------------------------------------------------------------------
894
895 #define STATE_VERSION   1
896
897 void EMU::save_state()
898 {
899         _TCHAR file_name[_MAX_PATH];
900         _stprintf_s(file_name, _MAX_PATH, _T("%s.sta"), _T(CONFIG_NAME));
901         save_state_tmp(bios_path(file_name));
902 }
903
904 void EMU::load_state()
905 {
906         _TCHAR file_name[_MAX_PATH];
907         _stprintf_s(file_name, _MAX_PATH, _T("%s.sta"), _T(CONFIG_NAME));
908         FILEIO ffp;
909         if(ffp.IsFileExists(bios_path(file_name))) {
910                 save_state_tmp(bios_path(_T("$temp$.sta")));
911                 if(!load_state_tmp(bios_path(file_name))) {
912                         out_debug_log("failed to load state file\n");
913                         load_state_tmp(bios_path(_T("$temp$.sta")));
914                 }
915                 DeleteFile(bios_path(_T("$temp$.sta")));
916         }
917 }
918
919 void EMU::save_state_tmp(_TCHAR* file_path)
920 {
921         FILEIO* fio = new FILEIO();
922 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
923         if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
924 #endif
925         if(fio->Fopen(file_path, FILEIO_WRITE_BINARY)) {
926                 // save state file version
927                 fio->FputUint32(STATE_VERSION);
928                 // save config
929                 save_config_state((void *)fio);
930                 // save inserted medias
931 #ifdef USE_CART1
932                 fio->Fwrite(&cart_status, sizeof(cart_status), 1);
933 #endif
934 #ifdef USE_FD1
935                 fio->Fwrite(disk_status, sizeof(disk_status), 1);
936                 fio->Fwrite(d88_file, sizeof(d88_file), 1);
937 #endif
938 #ifdef USE_QD1
939                 fio->Fwrite(&quickdisk_status, sizeof(quickdisk_status), 1);
940 #endif
941 #ifdef USE_TAPE
942                 fio->Fwrite(&tape_status, sizeof(tape_status), 1);
943 #endif
944 #ifdef USE_LASER_DISC
945                 fio->Fwrite(&laser_disc_status, sizeof(laser_disc_status), 1);
946 #endif
947                 // save vm state
948                 vm->save_state(fio);
949                 // end of state file
950                 fio->FputInt32(-1);
951                 fio->Fclose();
952         }
953 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
954         if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
955 #endif
956         delete fio;
957 }
958
959 bool EMU::load_state_tmp(_TCHAR* file_path)
960 {
961         bool result = false;
962         FILEIO* fio = new FILEIO();
963         if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
964                 // check state file version
965                 if(fio->FgetUint32() == STATE_VERSION) {
966                         // load config
967                         if(load_config_state((void *)fio)) {
968                                 // load inserted medias
969 #ifdef USE_CART1
970                                 fio->Fread(&cart_status, sizeof(cart_status), 1);
971 #endif
972 #ifdef USE_FD1
973                                 fio->Fread(disk_status, sizeof(disk_status), 1);
974                                 fio->Fread(d88_file, sizeof(d88_file), 1);
975 #endif
976 #ifdef USE_QD1
977                                 fio->Fread(&quickdisk_status, sizeof(quickdisk_status), 1);
978 #endif
979 #ifdef USE_TAPE
980                                 fio->Fread(&tape_status, sizeof(tape_status), 1);
981 #endif
982 #ifdef USE_LASER_DISC
983                                 fio->Fread(&laser_disc_status, sizeof(laser_disc_status), 1);
984 #endif
985                                 // check if virtual machine should be reinitialized
986                                 bool reinitialize = false;
987 #ifdef USE_CPU_TYPE
988                                 reinitialize |= (cpu_type != config.cpu_type);
989                                 cpu_type = config.cpu_type;
990 #endif
991 #ifdef USE_SOUND_DEVICE_TYPE
992                                 reinitialize |= (sound_device_type != config.sound_device_type);
993                                 sound_device_type = config.sound_device_type;
994 #endif
995                                 if(reinitialize) {
996                                         // stop sound
997                                         if(sound_ok && sound_started) {
998 #if defined(_USE_SDL) || defined(_USE_AGAR) || defined(_USE_QT)
999                                                 //bSndExit = true;
1000                                                 SDL_PauseAudio(1);
1001 #else
1002                                                 lpdsb->Stop();
1003 #endif
1004                                                 sound_started = false;
1005                                         }
1006 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
1007                                         if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
1008 #endif
1009                                         // reinitialize virtual machine
1010                                         delete vm;
1011                                         vm = new VM(this);
1012                                         vm->initialize_sound(sound_rate, sound_samples);
1013                                         vm->reset();
1014 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
1015                                         if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
1016 #endif
1017                                 }
1018                                 // restore inserted medias
1019                                 restore_media();
1020                                 // load vm state
1021                                 if(vm->load_state(fio)) {
1022                                         // check end of state
1023                                         result = (fio->FgetInt32() == -1);
1024                                 }
1025                         }
1026                 }
1027                 fio->Fclose();
1028         }
1029         delete fio;
1030         return result;
1031 }
1032 #endif
1033