2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
7 [ win32 emulation i/f ]
13 #if defined(_USE_AGAR)
15 #include "agar_main.h"
16 #include "agar_logger.h"
18 # elif defined(_USE_QT)
19 //#include <SDL/SDL.h>
21 #include "agar_logger.h"
25 #ifndef FD_BASE_NUMBER
26 #define FD_BASE_NUMBER 1
28 #ifndef QD_BASE_NUMBER
29 #define QD_BASE_NUMBER 1
32 // ----------------------------------------------------------------------------
34 // ----------------------------------------------------------------------------
35 #if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)
36 extern void get_long_full_path_name(_TCHAR* src, _TCHAR* dst);
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)
45 EMU::EMU(HWND hwnd, HINSTANCE hinst)
50 initialize_debug_log();
54 // store main window handle
55 main_window_handle = hwnd;
56 instance_handle = hinst;
61 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_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))) {
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);
83 pVMSemaphore = SDL_CreateSemaphore(1);
85 _TCHAR tmp_path[_MAX_PATH], *ptr;
86 GetModuleFileName(NULL, tmp_path, _MAX_PATH);
87 GetFullPathName(tmp_path, _MAX_PATH, app_path, &ptr);
91 // initialize d88 file info
92 memset(d88_file, 0, sizeof(d88_file));
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,
105 static const double late_table[5] = {0.05, 0.1, 0.2, 0.3, 0.4};
107 if(!(0 <= config.sound_frequency && config.sound_frequency < 8)) {
108 config.sound_frequency = 6; // default: 48KHz
110 if(!(0 <= config.sound_latency && config.sound_latency < 5)) {
111 config.sound_latency = 1; // default: 100msec
113 sound_rate = freq_table[config.sound_frequency];
114 sound_samples = (int)(sound_rate * late_table[config.sound_latency] + 0.5);
117 cpu_type = config.cpu_type;
119 #ifdef USE_SOUND_DEVICE_TYPE
120 sound_device_type = config.sound_device_type;
126 initialize_debugger();
132 initialize_printer();
137 # ifdef USE_DIRECT_SHOW
139 initialize_direct_show();
142 vm->initialize_sound(sound_rate, sound_samples);
144 now_suspended = false;
160 #ifdef USE_DIRECT_SHOW
161 release_direct_show();
166 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
167 if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
173 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
174 if(pVMSemaphore) SDL_DestroySemaphore(pVMSemaphore);
178 // ----------------------------------------------------------------------------
180 // ----------------------------------------------------------------------------
182 int EMU::frame_interval()
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);
193 return prev_interval;
195 return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);
198 return (int)(1024. * 1000. / FRAMES_PER_SEC + 0.5);
205 #ifdef USE_LASER_DISC
206 if(now_movie_play && !now_movie_pause) {
210 now_suspended = false;
212 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
213 if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
223 // virtual machine may be driven to fill sound buffer
224 int extra_frames = 0;
225 update_sound(&extra_frames);
227 // drive virtual machine
228 if(extra_frames == 0) {
230 // printf("VM:RUN() %d\n", AG_GetTicks());
233 rec_video_run_frames += extra_frames;
234 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
235 if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
242 // check if virtual machine should be reinitialized
243 bool reinitialize = false;
245 reinitialize |= (cpu_type != config.cpu_type);
246 cpu_type = config.cpu_type;
248 #ifdef USE_SOUND_DEVICE_TYPE
249 reinitialize |= (sound_device_type != config.sound_device_type);
250 sound_device_type = config.sound_device_type;
254 if(sound_ok && sound_started) {
255 #if defined(_USE_SDL) || defined(_USE_AGAR) || defined(_USE_QT)
261 sound_started = false;
263 // reinitialize virtual machine
264 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
265 if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
269 vm->initialize_sound(sound_rate, sound_samples);
271 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
272 if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
274 // restore inserted medias
277 // reset virtual machine
289 #ifdef USE_SPECIAL_RESET
290 void EMU::special_reset()
292 // reset virtual machine
305 void EMU::notify_power_off()
307 vm->notify_power_off();
311 _TCHAR* EMU::bios_path(_TCHAR* file_name)
313 static _TCHAR file_path[_MAX_PATH];
314 _stprintf_s(file_path, _MAX_PATH, _T("%s%s"), app_path, file_name);
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);
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);
334 #ifdef USE_LASER_DISC
335 #ifndef _USE_QT // WILLFIX
336 if(now_movie_play && !now_movie_pause) {
338 now_movie_pause = false;
343 now_suspended = true;
347 // ----------------------------------------------------------------------------
349 // ----------------------------------------------------------------------------
351 void EMU::get_host_time(cur_time_t* t)
353 #if defined(_USE_AGAR) || defined(_USE_SDL) || defined(_USE_QT)
356 tnow = std::time(NULL);
357 tm = std::localtime(&tnow);
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;
368 GetLocalTime(&sTime);
370 t->year = sTime.wYear;
371 t->month = sTime.wMonth;
373 t->day_of_week = sTime.wDayOfWeek;
374 t->hour = sTime.wHour;
375 t->minute = sTime.wMinute;
376 t->second = sTime.wSecond;
380 // ----------------------------------------------------------------------------
382 // ----------------------------------------------------------------------------
384 void EMU::initialize_printer()
386 prn_fio = new FILEIO();
391 void EMU::release_printer()
393 close_printer_file();
397 void EMU::reset_printer()
399 close_printer_file();
404 void EMU::update_printer()
406 if(prn_fio->IsOpened() && --prn_wait_frames == 0) {
407 close_printer_file();
411 void EMU::open_printer_file()
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);
419 void EMU::close_printer_file()
421 if(prn_fio->IsOpened()) {
422 // remove if the file size is less than 2 bytes
423 bool remove = (prn_fio->Ftell() < 2);
426 prn_fio->Remove(bios_path(prn_file_name));
431 void EMU::printer_out(uint8 value)
436 void EMU::printer_strobe(bool value)
438 bool falling = (prn_strobe && !value);
442 if(!prn_fio->IsOpened()) {
448 prn_fio->Fputc(prn_data);
450 #ifdef SUPPORT_VARIABLE_TIMING
451 prn_wait_frames = (int)(vm->frame_rate() * 10.0 + 0.5);
453 prn_wait_frames = (int)(FRAMES_PER_SEC * 10.0 + 0.5);
458 // ----------------------------------------------------------------------------
460 // ----------------------------------------------------------------------------
463 void EMU::initialize_debug_log()
465 if(_tfopen_s(&debug_log, _T("d:\\debug.log"), _T("w")) != 0) {
470 void EMU::release_debug_log()
478 void EMU::out_debug_log(const _TCHAR* format, ...)
483 static _TCHAR prev_buffer[1024] = {0};
485 va_start(ap, format);
486 _vstprintf_s(buffer, 1024, format, ap);
489 if(_tcscmp(prev_buffer, buffer) == 0) {
492 _tcscpy_s(prev_buffer, 1024, buffer);
495 _ftprintf(debug_log, _T("%s"), buffer);
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);
502 if(_tfopen_s(&debug_log, path, _T("w")) != 0) {
511 void EMU::out_message(const _TCHAR* format, ...)
514 va_start(ap, format);
515 _vstprintf_s(message, 1024, format, ap);
517 message_count = 4; // 4sec
520 // ----------------------------------------------------------------------------
522 // ----------------------------------------------------------------------------
524 void EMU::initialize_media()
527 memset(&cart_status, 0, sizeof(cart_status));
530 memset(disk_status, 0, sizeof(disk_status));
533 memset(&quickdisk_status, 0, sizeof(quickdisk_status));
536 memset(&tape_status, 0, sizeof(tape_status));
538 #ifdef USE_LASER_DISC
539 memset(&laser_disc_status, 0, sizeof(laser_disc_status));
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)
547 void EMU::write_protect_fd(int drv, bool flag)
549 vm->write_protect_fd(drv, flag);
551 bool EMU::is_write_protected_fd(int drv)
553 return vm->is_write_protect_fd(drv);
557 void EMU::update_media()
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);
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);
576 if(tape_status.wait_count != 0 && --tape_status.wait_count == 0) {
577 if(tape_status.play) {
578 vm->play_tape(tape_status.path);
580 vm->rec_tape(tape_status.path);
582 out_message(_T("CMT: %s"), tape_status.path);
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);
593 void EMU::restore_media()
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);
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);
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);
617 if(tape_status.path[0] != _T('\0')) {
618 if(tape_status.play) {
619 vm->play_tape(tape_status.path);
621 tape_status.path[0] = _T('\0');
625 #ifdef USE_LASER_DISC
626 if(laser_disc_status.path[0] != _T('\0')) {
627 vm->open_laser_disc(laser_disc_status.path);
633 void EMU::open_cart(int drv, _TCHAR* file_path)
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);
641 bool s = now_rec_sound;
642 bool v = now_rec_video;
645 if(s) start_rec_sound();
646 if(v) start_rec_video(-1);
650 void EMU::close_cart(int drv)
654 clear_media_status(&cart_status[drv]);
655 out_message(_T("Cart%d: Ejected"), drv + 1);
663 bool EMU::cart_inserted(int drv)
666 return vm->cart_inserted(drv);
674 void EMU::open_disk(int drv, _TCHAR* file_path, int offset)
677 if(vm->disk_inserted(drv)) {
680 #ifdef SUPPORT_VARIABLE_TIMING
681 disk_status[drv].wait_count = (int)(vm->frame_rate() / 2);
683 disk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
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);
690 _tcscpy_s(disk_status[drv].path, _MAX_PATH, file_path);
691 disk_status[drv].offset = offset;
695 void EMU::close_disk(int drv)
699 clear_media_status(&disk_status[drv]);
700 out_message(_T("FD%d: Ejected"), drv + FD_BASE_NUMBER);
704 bool EMU::disk_inserted(int drv)
707 return vm->disk_inserted(drv);
714 int EMU::get_access_lamp(void)
717 #if defined(USE_FD1) || defined(USE_QD1)
719 stat = vm->access_lamp(); // Return accessing drive number.
727 void EMU::open_quickdisk(int drv, _TCHAR* file_path)
730 if(vm->quickdisk_inserted(drv)) {
731 vm->close_quickdisk(drv);
733 #ifdef SUPPORT_VARIABLE_TIMING
734 quickdisk_status[drv].wait_count = (int)(vm->frame_rate() / 2);
736 quickdisk_status[drv].wait_count = (int)(FRAMES_PER_SEC / 2);
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);
743 _tcscpy_s(quickdisk_status[drv].path, _MAX_PATH, file_path);
747 void EMU::close_quickdisk(int drv)
750 vm->close_quickdisk(drv);
751 clear_media_status(&quickdisk_status[drv]);
752 out_message(_T("QD%d: Ejected"), drv + QD_BASE_NUMBER);
756 bool EMU::quickdisk_inserted(int drv)
759 return vm->quickdisk_inserted(drv);
767 void EMU::play_tape(_TCHAR* file_path)
769 if(vm->tape_inserted()) {
772 #ifdef SUPPORT_VARIABLE_TIMING
773 tape_status.wait_count = (int)(vm->frame_rate() / 2);
775 tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);
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);
782 _tcscpy_s(tape_status.path, _MAX_PATH, file_path);
783 tape_status.play = true;
786 void EMU::rec_tape(_TCHAR* file_path)
788 if(vm->tape_inserted()) {
791 #ifdef SUPPORT_VARIABLE_TIMING
792 tape_status.wait_count = (int)(vm->frame_rate() / 2);
794 tape_status.wait_count = (int)(FRAMES_PER_SEC / 2);
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);
801 _tcscpy_s(tape_status.path, _MAX_PATH, file_path);
802 tape_status.play = false;
805 void EMU::close_tape()
808 clear_media_status(&tape_status);
809 out_message(_T("CMT: Ejected"));
812 bool EMU::tape_inserted()
814 return vm->tape_inserted();
818 #ifdef USE_LASER_DISC
819 void EMU::open_laser_disc(_TCHAR* file_path)
821 if(vm->laser_disc_inserted()) {
822 vm->close_laser_disc();
824 #ifdef SUPPORT_VARIABLE_TIMING
825 laser_disc_status.wait_count = (int)(vm->frame_rate() / 2);
827 laser_disc_status.wait_count = (int)(FRAMES_PER_SEC / 2);
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);
834 _tcscpy_s(laser_disc_status.path, _MAX_PATH, file_path);
837 void EMU::close_laser_disc()
839 vm->close_laser_disc();
840 clear_media_status(&laser_disc_status);
841 out_message(_T("LD: Ejected"));
844 bool EMU::laser_disc_inserted()
846 return vm->laser_disc_inserted();
850 #ifdef USE_TAPE_BUTTON
851 void EMU::push_play()
856 void EMU::push_stop()
862 #ifdef USE_BINARY_FILE1
863 void EMU::load_binary(int drv, _TCHAR* file_path)
865 if(drv < MAX_BINARY) {
866 vm->load_binary(drv, file_path);
867 out_message(_T("Load: %s"), file_path);
871 void EMU::save_binary(int drv, _TCHAR* file_path)
873 if(drv < MAX_BINARY) {
874 vm->save_binary(drv, file_path);
875 out_message(_T("Save: %s"), file_path);
882 return vm->now_skip();
885 void EMU::update_config()
891 // ----------------------------------------------------------------------------
893 // ----------------------------------------------------------------------------
895 #define STATE_VERSION 1
897 void EMU::save_state()
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));
904 void EMU::load_state()
906 _TCHAR file_name[_MAX_PATH];
907 _stprintf_s(file_name, _MAX_PATH, _T("%s.sta"), _T(CONFIG_NAME));
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")));
915 DeleteFile(bios_path(_T("$temp$.sta")));
919 void EMU::save_state_tmp(_TCHAR* file_path)
921 FILEIO* fio = new FILEIO();
922 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
923 if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
925 if(fio->Fopen(file_path, FILEIO_WRITE_BINARY)) {
926 // save state file version
927 fio->FputUint32(STATE_VERSION);
929 save_config_state((void *)fio);
930 // save inserted medias
932 fio->Fwrite(&cart_status, sizeof(cart_status), 1);
935 fio->Fwrite(disk_status, sizeof(disk_status), 1);
936 fio->Fwrite(d88_file, sizeof(d88_file), 1);
939 fio->Fwrite(&quickdisk_status, sizeof(quickdisk_status), 1);
942 fio->Fwrite(&tape_status, sizeof(tape_status), 1);
944 #ifdef USE_LASER_DISC
945 fio->Fwrite(&laser_disc_status, sizeof(laser_disc_status), 1);
953 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
954 if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
959 bool EMU::load_state_tmp(_TCHAR* file_path)
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) {
967 if(load_config_state((void *)fio)) {
968 // load inserted medias
970 fio->Fread(&cart_status, sizeof(cart_status), 1);
973 fio->Fread(disk_status, sizeof(disk_status), 1);
974 fio->Fread(d88_file, sizeof(d88_file), 1);
977 fio->Fread(&quickdisk_status, sizeof(quickdisk_status), 1);
980 fio->Fread(&tape_status, sizeof(tape_status), 1);
982 #ifdef USE_LASER_DISC
983 fio->Fread(&laser_disc_status, sizeof(laser_disc_status), 1);
985 // check if virtual machine should be reinitialized
986 bool reinitialize = false;
988 reinitialize |= (cpu_type != config.cpu_type);
989 cpu_type = config.cpu_type;
991 #ifdef USE_SOUND_DEVICE_TYPE
992 reinitialize |= (sound_device_type != config.sound_device_type);
993 sound_device_type = config.sound_device_type;
997 if(sound_ok && sound_started) {
998 #if defined(_USE_SDL) || defined(_USE_AGAR) || defined(_USE_QT)
1004 sound_started = false;
1006 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
1007 if(pVMSemaphore) SDL_SemWait(pVMSemaphore);
1009 // reinitialize virtual machine
1012 vm->initialize_sound(sound_rate, sound_samples);
1014 #if defined(_USE_AGAR) || (_USE_SDL) || defined(_USE_QT)
1015 if(pVMSemaphore) SDL_SemPost(pVMSemaphore);
1018 // restore inserted medias
1021 if(vm->load_state(fio)) {
1022 // check end of state
1023 result = (fio->FgetInt32() == -1);