OSDN Git Service

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