|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - main.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: Main initialization and event handling routines.
1.1 root 8: */
1.1.1.13! root 9: const char Opt_rcsid[] = "Hatari $Id: main.c,v 1.119 2008/03/17 14:15:36 thothy Exp $";
1.1 root 10:
1.1.1.13! root 11: #include "config.h"
1.1 root 12:
1.1.1.13! root 13: #include <time.h>
1.1 root 14: #include <SDL.h>
15:
1.1.1.13! root 16: #include "config.h"
1.1 root 17: #include "main.h"
18: #include "configuration.h"
1.1.1.12 root 19: #include "options.h"
1.1 root 20: #include "dialog.h"
21: #include "audio.h"
22: #include "joy.h"
23: #include "file.h"
24: #include "floppy.h"
25: #include "gemdos.h"
1.1.1.4 root 26: #include "hdc.h"
1.1 root 27: #include "ikbd.h"
1.1.1.10 root 28: #include "ioMem.h"
1.1.1.5 root 29: #include "keymap.h"
1.1.1.10 root 30: #include "log.h"
1.1 root 31: #include "m68000.h"
32: #include "memorySnapShot.h"
33: #include "misc.h"
1.1.1.8 root 34: #include "midi.h"
1.1.1.13! root 35: #include "nvram.h"
! 36: #include "paths.h"
1.1 root 37: #include "printer.h"
1.1.1.8 root 38: #include "reset.h"
1.1 root 39: #include "rs232.h"
40: #include "screen.h"
1.1.1.5 root 41: #include "sdlgui.h"
1.1 root 42: #include "shortcut.h"
43: #include "sound.h"
1.1.1.8 root 44: #include "stMemory.h"
1.1 root 45: #include "tos.h"
1.1.1.5 root 46: #include "vdi.h"
1.1 root 47: #include "video.h"
48: #include "ymFormat.h"
1.1.1.2 root 49: #include "debugui.h"
1.1 root 50:
1.1.1.13! root 51: #include "hatari-glue.h"
! 52:
! 53: #include "falcon/hostscreen.h"
! 54: #if ENABLE_DSP_EMU
! 55: #include "falcon/dsp.h"
! 56: #endif
1.1 root 57:
58:
1.1.1.12 root 59: BOOL bQuitProgram = FALSE; /* Flag to quit program cleanly */
60: BOOL bEnableDebug = FALSE; /* Enable debug UI? */
61: static BOOL bEmulationActive = TRUE; /* Run emulation when started */
62: static BOOL bAccurateDelays; /* Host system has an accurate SDL_Delay()? */
63: static BOOL bIgnoreNextMouseMotion = FALSE; /* Next mouse motion will be ignored (needed after SDL_WarpMouse) */
1.1.1.13! root 64: static char szBootDiskImage[FILENAME_MAX]; /* boot disk path or empty */
! 65:
! 66: extern char sWorkingDir[FILENAME_MAX];
1.1 root 67:
68:
69: /*-----------------------------------------------------------------------*/
1.1.1.13! root 70: /**
! 71: * Save/Restore snapshot of local variables('MemorySnapShot_Store' handles type)
! 72: */
1.1 root 73: void Main_MemorySnapShot_Capture(BOOL bSave)
74: {
1.1.1.13! root 75: int nBytes;
1.1 root 76:
1.1.1.13! root 77: /* Save/Restore details */
! 78: /* Only save/restore area of memory machine ie set to, eg 1Mb */
! 79: if (bSave)
! 80: {
! 81: nBytes = STRamEnd;
! 82: MemorySnapShot_Store(&nBytes, sizeof(nBytes));
! 83: MemorySnapShot_Store(STRam, nBytes);
! 84: }
! 85: else
! 86: {
! 87: MemorySnapShot_Store(&nBytes, sizeof(nBytes));
! 88: MemorySnapShot_Store(STRam, nBytes);
! 89: }
! 90: /* And Cart/TOS/Hardware area */
! 91: MemorySnapShot_Store(&STRam[0xE00000], 0x200000);
! 92: MemorySnapShot_Store(szBootDiskImage, sizeof(szBootDiskImage));
! 93: MemorySnapShot_Store(sWorkingDir, sizeof(sWorkingDir)); /* TODO: Remove this line? */
1.1 root 94: }
95:
96:
97: /*-----------------------------------------------------------------------*/
1.1.1.13! root 98: /**
! 99: * Pause emulation, stop sound
! 100: */
1.1 root 101: void Main_PauseEmulation(void)
102: {
1.1.1.13! root 103: if ( bEmulationActive )
! 104: {
! 105: Audio_EnableAudio(FALSE);
! 106: bEmulationActive = FALSE;
! 107: }
1.1 root 108: }
109:
1.1.1.2 root 110: /*-----------------------------------------------------------------------*/
1.1.1.13! root 111: /**
! 112: * Start emulation
! 113: */
1.1 root 114: void Main_UnPauseEmulation(void)
115: {
1.1.1.13! root 116: if ( !bEmulationActive )
! 117: {
! 118: Sound_ResetBufferIndex();
! 119: Audio_EnableAudio(ConfigureParams.Sound.bEnableSound);
! 120: Screen_SetFullUpdate(); /* Cause full screen update (to clear all) */
1.1 root 121:
1.1.1.13! root 122: bEmulationActive = TRUE;
! 123: }
1.1 root 124: }
125:
1.1.1.13! root 126: /*-----------------------------------------------------------------------*/
! 127: /**
! 128: * Optionally ask user whether to quit and set bQuitProgram accordingly
! 129: */
! 130: void Main_RequestQuit(void)
! 131: {
! 132: if (ConfigureParams.Memory.bAutoSave)
! 133: {
! 134: bQuitProgram = TRUE;
! 135: MemorySnapShot_Capture(ConfigureParams.Memory.szAutoSaveFileName, FALSE);
! 136: }
! 137: else if (ConfigureParams.Log.bConfirmQuit)
! 138: {
! 139: bQuitProgram = FALSE; /* if set TRUE, dialog exits */
! 140: bQuitProgram = DlgAlert_Query("All unsaved data will be lost.\nDo you really want to quit?");
! 141: }
! 142: else
! 143: {
! 144: bQuitProgram = TRUE;
! 145: }
! 146:
! 147: if (bQuitProgram)
! 148: {
! 149: /* Assure that CPU core shuts down */
! 150: M68000_SetSpecial(SPCFLAG_BRK);
! 151: }
! 152: }
1.1.1.7 root 153:
1.1.1.12 root 154: /*-----------------------------------------------------------------------*/
1.1.1.13! root 155: /**
! 156: * This function waits on each emulated VBL to synchronize the real time
! 157: * with the emulated ST.
! 158: * Unfortunately SDL_Delay and other sleep functions like usleep or nanosleep
! 159: * are very inaccurate on some systems like Linux 2.4 or Mac OS X (they can only
! 160: * wait for a multiple of 10ms due to the scheduler on these systems), so we have
! 161: * to "busy wait" there to get an accurate timing.
! 162: */
1.1.1.12 root 163: void Main_WaitOnVbl(void)
164: {
1.1.1.13! root 165: int nCurrentMilliTicks;
! 166: static int nDestMilliTicks = 0;
! 167: int nFrameDuration;
! 168: signed int nDelay;
! 169:
! 170: nCurrentMilliTicks = SDL_GetTicks();
! 171:
! 172: nFrameDuration = 1000/nScreenRefreshRate;
! 173: nDelay = nDestMilliTicks - nCurrentMilliTicks;
! 174:
! 175: /* Do not wait if we are in max speed mode or if we are totally out of sync */
! 176: if (ConfigureParams.System.nMinMaxSpeed == MINMAXSPEED_MAX
! 177: || nDelay < -4*nFrameDuration)
! 178: {
! 179: /* Only update nDestMilliTicks for next VBL */
! 180: nDestMilliTicks = nCurrentMilliTicks + nFrameDuration;
! 181: return;
! 182: }
1.1.1.12 root 183:
1.1.1.13! root 184: if (bAccurateDelays)
! 185: {
! 186: /* Accurate sleeping is possible -> use SDL_Delay to free the CPU */
! 187: if (nDelay > 1)
! 188: SDL_Delay(nDelay - 1);
! 189: }
! 190: else
! 191: {
! 192: /* No accurate SDL_Delay -> only wait if more than 5ms to go... */
! 193: if (nDelay > 5)
! 194: SDL_Delay(nDelay<10 ? nDelay-1 : 9);
! 195: }
! 196:
! 197: /* Now busy-wait for the right tick: */
! 198: while (nDelay > 0)
! 199: {
! 200: nCurrentMilliTicks = SDL_GetTicks();
! 201: nDelay = nDestMilliTicks - nCurrentMilliTicks;
! 202: }
! 203:
! 204: /* Update nDestMilliTicks for next VBL */
! 205: nDestMilliTicks += nFrameDuration;
1.1.1.12 root 206: }
207:
208:
209: /*-----------------------------------------------------------------------*/
1.1.1.13! root 210: /**
! 211: * Since SDL_Delay and friends are very inaccurate on some systems, we have
! 212: * to check if we can rely on this delay function.
! 213: */
1.1.1.12 root 214: static void Main_CheckForAccurateDelays(void)
215: {
1.1.1.13! root 216: int nStartTicks, nEndTicks;
1.1.1.12 root 217:
1.1.1.13! root 218: /* Force a task switch now, so we have a longer timeslice afterwards */
! 219: SDL_Delay(10);
1.1.1.12 root 220:
1.1.1.13! root 221: nStartTicks = SDL_GetTicks();
! 222: SDL_Delay(1);
! 223: nEndTicks = SDL_GetTicks();
! 224:
! 225: /* If the delay took longer than 10ms, we are on an inaccurate system! */
! 226: bAccurateDelays = ((nEndTicks - nStartTicks) < 9);
! 227:
! 228: if (bAccurateDelays)
! 229: Log_Printf(LOG_DEBUG, "Host system has accurate delays. (%d)\n", nEndTicks - nStartTicks);
! 230: else
! 231: Log_Printf(LOG_DEBUG, "Host system does not have accurate delays. (%d)\n", nEndTicks - nStartTicks);
1.1.1.12 root 232: }
233:
234:
1.1 root 235: /* ----------------------------------------------------------------------- */
1.1.1.13! root 236: /**
! 237: * Set mouse pointer to new coordinates and set flag to ignore the mouse event
! 238: * that is generated by SDL_WarpMouse().
! 239: */
1.1.1.9 root 240: void Main_WarpMouse(int x, int y)
241: {
1.1.1.13! root 242: SDL_WarpMouse(x, y); /* Set mouse pointer to new position */
! 243: bIgnoreNextMouseMotion = TRUE; /* Ignore mouse motion event from SDL_WarpMouse */
1.1.1.9 root 244: }
245:
246:
247: /* ----------------------------------------------------------------------- */
1.1.1.13! root 248: /**
! 249: * Handle mouse motion event.
! 250: */
1.1.1.12 root 251: static void Main_HandleMouseMotion(SDL_Event *pEvent)
252: {
253: int dx, dy;
254: static int ax = 0, ay = 0;
255:
1.1.1.13! root 256:
1.1.1.12 root 257: if (bIgnoreNextMouseMotion)
258: {
259: bIgnoreNextMouseMotion = FALSE;
260: return;
261: }
262:
263: dx = pEvent->motion.xrel;
264: dy = pEvent->motion.yrel;
265:
1.1.1.13! root 266: /* In zoomed low res mode, we divide dx and dy by the zoom factor so that
! 267: * the ST mouse cursor stays in sync with the host mouse. However, we have
! 268: * to take care of lowest bit of dx and dy which will get lost when
! 269: * dividing. So we store these bits in ax and ay and add them to dx and dy
! 270: * the next time. */
! 271: if (nScreenZoomX != 1)
! 272: {
1.1.1.12 root 273: dx += ax;
1.1.1.13! root 274: ax = dx % nScreenZoomX;
! 275: dx /= nScreenZoomX;
1.1.1.12 root 276: }
1.1.1.13! root 277: if (nScreenZoomY != 1)
1.1.1.12 root 278: {
279: dy += ay;
1.1.1.13! root 280: ay = dy % nScreenZoomY;
! 281: dy /= nScreenZoomY;
1.1.1.12 root 282: }
283:
284: KeyboardProcessor.Mouse.dx += dx;
285: KeyboardProcessor.Mouse.dy += dy;
286: }
287:
288:
289: /* ----------------------------------------------------------------------- */
1.1.1.13! root 290: /**
! 291: * SDL message handler.
! 292: * Here we process the SDL events (keyboard, mouse, ...) and map it to
! 293: * Atari IKBD events.
! 294: */
1.1.1.8 root 295: void Main_EventHandler(void)
1.1 root 296: {
1.1.1.13! root 297: SDL_Event event;
! 298:
! 299: if (SDL_PollEvent(&event))
! 300: {
! 301: switch (event.type)
! 302: {
1.1 root 303:
1.1.1.13! root 304: case SDL_QUIT:
! 305: Main_RequestQuit();
! 306: break;
! 307:
! 308: case SDL_MOUSEMOTION: /* Read/Update internal mouse position */
! 309: Main_HandleMouseMotion(&event);
! 310: break;
! 311:
! 312: case SDL_MOUSEBUTTONDOWN:
! 313: if (event.button.button == SDL_BUTTON_LEFT)
! 314: {
! 315: if (Keyboard.LButtonDblClk == 0)
! 316: Keyboard.bLButtonDown |= BUTTON_MOUSE; /* Set button down flag */
! 317: }
! 318: else if (event.button.button == SDL_BUTTON_RIGHT)
! 319: {
! 320: Keyboard.bRButtonDown |= BUTTON_MOUSE;
! 321: }
! 322: else if (event.button.button == SDL_BUTTON_MIDDLE)
! 323: {
! 324: /* Start double-click sequence in emulation time */
! 325: Keyboard.LButtonDblClk = 1;
! 326: }
! 327: else if (event.button.button == SDL_BUTTON_WHEELDOWN)
! 328: {
! 329: /* Simulate pressing the "cursor down" key */
! 330: IKBD_PressSTKey(0x50, TRUE);
! 331: }
! 332: else if (event.button.button == SDL_BUTTON_WHEELUP)
! 333: {
! 334: /* Simulate pressing the "cursor up" key */
! 335: IKBD_PressSTKey(0x48, TRUE);
! 336: }
! 337: break;
! 338:
! 339: case SDL_MOUSEBUTTONUP:
! 340: if (event.button.button == SDL_BUTTON_LEFT)
! 341: {
! 342: Keyboard.bLButtonDown &= ~BUTTON_MOUSE;
! 343: }
! 344: else if (event.button.button == SDL_BUTTON_RIGHT)
! 345: {
! 346: Keyboard.bRButtonDown &= ~BUTTON_MOUSE;
! 347: }
! 348: else if (event.button.button == SDL_BUTTON_WHEELDOWN)
! 349: {
! 350: /* Simulate releasing the "cursor down" key */
! 351: IKBD_PressSTKey(0x50, FALSE);
! 352: }
! 353: else if (event.button.button == SDL_BUTTON_WHEELUP)
! 354: {
! 355: /* Simulate releasing the "cursor up" key */
! 356: IKBD_PressSTKey(0x48, FALSE);
! 357: }
! 358: break;
! 359:
! 360: case SDL_KEYDOWN:
! 361: Keymap_KeyDown(&event.key.keysym);
! 362: break;
! 363:
! 364: case SDL_KEYUP:
! 365: Keymap_KeyUp(&event.key.keysym);
! 366: break;
! 367: }
! 368: }
1.1 root 369: }
370:
371:
1.1.1.2 root 372: /*-----------------------------------------------------------------------*/
1.1.1.13! root 373: /**
! 374: * Reparent Hatari window if so requested. Needs to be done inside
! 375: * Hatari because if SDL itself is requested to reparent itself,
! 376: * SDL window stops accepting any input (specifically done like
! 377: * this in SDL backends for some reason).
! 378: *
! 379: * Should work on X11 and Windows.
! 380: *
! 381: * SDL_syswm.h automatically includes everything else needed.
! 382: */
! 383: #include <SDL_syswm.h>
! 384:
! 385: static void Main_Reparent_Window(void)
! 386: {
! 387: #if HAVE_X11
! 388: Window parent_win;
! 389: const char *parent_win_id;
! 390: SDL_SysWMinfo info;
! 391:
! 392: parent_win_id = getenv("PARENT_WIN_ID");
! 393: if (!parent_win_id) {
! 394: return;
! 395: }
! 396: parent_win = strtol(parent_win_id, NULL, 0);
! 397: if (!parent_win) {
! 398: Log_Printf(LOG_DEBUG, "Invalid PARENT_WIN_ID value '%s'\n", parent_win_id);
! 399: return;
! 400: }
! 401:
! 402: SDL_VERSION(&info.version);
! 403: if (!SDL_GetWMInfo(&info)) {
! 404: Log_Printf(LOG_DEBUG, "Failed to get SDL_GetWMInfo()\n");
! 405: return;
! 406: }
! 407:
! 408: /* reparent Hatari window to parent */
! 409: XReparentWindow(info.info.x11.display,
! 410: info.info.x11.window,
! 411: parent_win, 0, 0);
! 412: /* remove WM window for Hatari */
! 413: XDestroyWindow(info.info.x11.display,
! 414: info.info.x11.wmwindow);
! 415: #else
! 416: /* TODO: implement the Windows part. SDL sources offer example */
! 417: Log_Printf(LOG_DEBUG, "Support for Hatari window reparenting not built in\n");
! 418: #endif /* HAVE_X11 */
! 419: }
! 420:
! 421:
! 422: /*-----------------------------------------------------------------------*/
! 423: /**
! 424: * Initialise emulation
! 425: */
1.1.1.8 root 426: static void Main_Init(void)
1.1 root 427: {
1.1.1.13! root 428: /* Open debug log file */
! 429: Log_Init();
! 430: Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n");
! 431:
! 432: /* Init SDL's video subsystem. Note: Audio and joystick subsystems
! 433: will be initialized later (failures there are not fatal). */
! 434: if (SDL_Init(SDL_INIT_VIDEO) < 0)
! 435: {
! 436: fprintf(stderr, "Could not initialize the SDL library:\n %s\n", SDL_GetError() );
! 437: exit(-1);
! 438: }
! 439:
! 440: SDLGui_Init();
! 441: Printer_Init();
! 442: RS232_Init();
! 443: Midi_Init();
! 444: Screen_Init();
! 445: Main_Reparent_Window();
! 446: HostScreen_Init();
! 447: #if ENABLE_DSP_EMU
! 448: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU)
! 449: {
! 450: DSP_Init();
! 451: }
! 452: #endif
! 453: Floppy_Init();
! 454: Init680x0(); /* Init CPU emulation */
! 455: Audio_Init();
! 456: Keymap_Init();
! 457:
! 458: /* Init HD emulation */
! 459: if (ConfigureParams.HardDisk.bUseHardDiskImage)
! 460: {
! 461: char *szHardDiskImage = ConfigureParams.HardDisk.szHardDiskImage;
! 462: if (HDC_Init(szHardDiskImage))
! 463: printf("Hard drive image %s mounted.\n", szHardDiskImage);
! 464: else
! 465: printf("Couldn't open HD file: %s, or no partitions\n", szHardDiskImage);
! 466: }
! 467: GemDOS_Init();
! 468: if (ConfigureParams.HardDisk.bUseHardDiskDirectories)
! 469: {
! 470: GemDOS_InitDrives();
! 471: }
! 472:
! 473: if (Reset_Cold()) /* Reset all systems, load TOS image */
! 474: {
! 475: /* If loading of the TOS failed, we bring up the GUI to let the
! 476: * user choose another TOS ROM file. */
! 477: Dialog_DoProperty();
! 478: }
! 479: if (!bTosImageLoaded || bQuitProgram)
! 480: {
! 481: fprintf(stderr, "Failed to load TOS image!\n");
! 482: SDL_Quit();
! 483: exit(-2);
! 484: }
! 485:
! 486: IoMem_Init();
! 487: NvRam_Init();
! 488: Joy_Init();
! 489: Sound_Init();
! 490:
! 491: /* Check passed disk image parameter, boot directly into emulator */
! 492: if (strlen(szBootDiskImage) > 0)
! 493: {
! 494: Floppy_InsertDiskIntoDrive(0, szBootDiskImage, sizeof(szBootDiskImage));
! 495: }
1.1 root 496: }
497:
1.1.1.6 root 498:
1.1.1.2 root 499: /*-----------------------------------------------------------------------*/
1.1.1.13! root 500: /**
! 501: * Un-Initialise emulation
! 502: */
1.1.1.8 root 503: static void Main_UnInit(void)
1.1 root 504: {
1.1.1.13! root 505: Screen_ReturnFromFullScreen();
! 506: Floppy_UnInit();
! 507: HDC_UnInit();
! 508: Midi_UnInit();
! 509: RS232_UnInit();
! 510: Printer_UnInit();
! 511: IoMem_UnInit();
! 512: NvRam_UnInit();
! 513: GemDOS_UnInitDrives();
! 514: Joy_UnInit();
! 515: if (Sound_AreWeRecording())
! 516: Sound_EndRecording();
! 517: Audio_UnInit();
! 518: SDLGui_UnInit();
! 519: #if ENABLE_DSP_EMU
! 520: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU)
! 521: {
! 522: DSP_UnInit();
! 523: }
! 524: HostScreen_UnInit();
! 525: #endif
! 526: Screen_UnInit();
! 527: Exit680x0();
1.1 root 528:
1.1.1.13! root 529: /* SDL uninit: */
! 530: SDL_Quit();
1.1.1.10 root 531:
1.1.1.13! root 532: /* Close debug log file */
! 533: Log_UnInit();
1.1 root 534: }
535:
1.1.1.6 root 536:
1.1.1.2 root 537: /*-----------------------------------------------------------------------*/
1.1.1.13! root 538: /**
! 539: * Main
! 540: */
1.1 root 541: int main(int argc, char *argv[])
542: {
1.1.1.13! root 543: /* Generate random seed */
! 544: srand(time(NULL));
! 545:
! 546: /* Initialize directory strings */
! 547: Paths_Init(argv[0]);
! 548:
! 549: /* no boot disk image */
! 550: szBootDiskImage[0] = 0;
1.1 root 551:
1.1.1.13! root 552: /* Set default configuration values: */
! 553: Configuration_SetDefault();
1.1.1.8 root 554:
1.1.1.13! root 555: /* Now load the values from the configuration file */
! 556: Configuration_Load(CONFDIR"/hatari.cfg"); /* Try the global configuration file first */
! 557: Configuration_Load(NULL); /* Now try the users configuration file */
1.1 root 558:
1.1.1.13! root 559: /* Check for any passed parameters, get boot disk */
! 560: Opt_ParseParameters(argc, argv, szBootDiskImage, sizeof(szBootDiskImage));
! 561: /* monitor type option might require "reset" -> TRUE */
! 562: Configuration_Apply(TRUE);
1.1.1.2 root 563:
1.1.1.13! root 564: #ifdef WIN32
! 565: Win_OpenCon();
! 566: #endif
1.1.1.7 root 567:
1.1.1.13! root 568: /* Needed on maemo but useful also with normal X11 window managers
! 569: * for window grouping when you have multiple Hatari SDL windows open
! 570: */
! 571: #if HAVE_SETENV
! 572: setenv("SDL_VIDEO_X11_WMCLASS", "hatari", 1);
! 573: #endif
1.1 root 574:
1.1.1.13! root 575: /* Init emulator system */
! 576: Main_Init();
1.1 root 577:
1.1.1.13! root 578: /* Check if SDL_Delay is accurate */
! 579: Main_CheckForAccurateDelays();
1.1.1.12 root 580:
1.1.1.13! root 581: /* Switch immediately to fullscreen if user wants to */
! 582: if (ConfigureParams.Screen.bFullScreen)
! 583: Screen_EnterFullScreen();
1.1.1.2 root 584:
1.1.1.13! root 585: /* Run emulation */
! 586: Main_UnPauseEmulation();
! 587: M68000_Start(); /* Start emulation */
1.1 root 588:
1.1.1.13! root 589: /* Un-init emulation system */
! 590: Main_UnInit();
1.1 root 591:
1.1.1.13! root 592: return 0;
1.1 root 593: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.