|
|
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.14! root 9: const char Opt_rcsid[] = "Hatari $Id: main.c,v 1.121 2008/03/28 22:33:11 eerot 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: *
1.1.1.14! root 379: * Currently only works on X11.
1.1.1.13 root 380: *
381: * SDL_syswm.h automatically includes everything else needed.
382: */
1.1.1.14! root 383:
! 384: #if HAVE_X11
1.1.1.13 root 385: #include <SDL_syswm.h>
1.1.1.14! root 386: #endif
1.1.1.13 root 387:
388: static void Main_Reparent_Window(void)
389: {
390: #if HAVE_X11
391: Window parent_win;
392: const char *parent_win_id;
393: SDL_SysWMinfo info;
394:
395: parent_win_id = getenv("PARENT_WIN_ID");
396: if (!parent_win_id) {
397: return;
398: }
399: parent_win = strtol(parent_win_id, NULL, 0);
400: if (!parent_win) {
401: Log_Printf(LOG_DEBUG, "Invalid PARENT_WIN_ID value '%s'\n", parent_win_id);
402: return;
403: }
404:
405: SDL_VERSION(&info.version);
406: if (!SDL_GetWMInfo(&info)) {
407: Log_Printf(LOG_DEBUG, "Failed to get SDL_GetWMInfo()\n");
408: return;
409: }
410:
1.1.1.14! root 411: /* hide WM window for Hatari */
! 412: XUnmapWindow(info.info.x11.display,
! 413: info.info.x11.wmwindow);
! 414: /* reparent main Hatari window to given parent */
1.1.1.13 root 415: XReparentWindow(info.info.x11.display,
416: info.info.x11.window,
417: parent_win, 0, 0);
418: #else
419: /* TODO: implement the Windows part. SDL sources offer example */
1.1.1.14! root 420: //Log_Printf(LOG_DEBUG, "Support for Hatari window reparenting not built in\n");
1.1.1.13 root 421: #endif /* HAVE_X11 */
422: }
423:
424:
425: /*-----------------------------------------------------------------------*/
426: /**
427: * Initialise emulation
428: */
1.1.1.8 root 429: static void Main_Init(void)
1.1 root 430: {
1.1.1.13 root 431: /* Open debug log file */
432: Log_Init();
433: Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n");
434:
435: /* Init SDL's video subsystem. Note: Audio and joystick subsystems
436: will be initialized later (failures there are not fatal). */
437: if (SDL_Init(SDL_INIT_VIDEO) < 0)
438: {
439: fprintf(stderr, "Could not initialize the SDL library:\n %s\n", SDL_GetError() );
440: exit(-1);
441: }
442:
443: SDLGui_Init();
444: Printer_Init();
445: RS232_Init();
446: Midi_Init();
447: Screen_Init();
448: Main_Reparent_Window();
449: HostScreen_Init();
450: #if ENABLE_DSP_EMU
451: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU)
452: {
453: DSP_Init();
454: }
455: #endif
456: Floppy_Init();
457: Init680x0(); /* Init CPU emulation */
458: Audio_Init();
459: Keymap_Init();
460:
461: /* Init HD emulation */
462: if (ConfigureParams.HardDisk.bUseHardDiskImage)
463: {
464: char *szHardDiskImage = ConfigureParams.HardDisk.szHardDiskImage;
465: if (HDC_Init(szHardDiskImage))
466: printf("Hard drive image %s mounted.\n", szHardDiskImage);
467: else
468: printf("Couldn't open HD file: %s, or no partitions\n", szHardDiskImage);
469: }
470: GemDOS_Init();
471: if (ConfigureParams.HardDisk.bUseHardDiskDirectories)
472: {
473: GemDOS_InitDrives();
474: }
475:
476: if (Reset_Cold()) /* Reset all systems, load TOS image */
477: {
478: /* If loading of the TOS failed, we bring up the GUI to let the
479: * user choose another TOS ROM file. */
480: Dialog_DoProperty();
481: }
482: if (!bTosImageLoaded || bQuitProgram)
483: {
484: fprintf(stderr, "Failed to load TOS image!\n");
485: SDL_Quit();
486: exit(-2);
487: }
488:
489: IoMem_Init();
490: NvRam_Init();
491: Joy_Init();
492: Sound_Init();
493:
494: /* Check passed disk image parameter, boot directly into emulator */
495: if (strlen(szBootDiskImage) > 0)
496: {
497: Floppy_InsertDiskIntoDrive(0, szBootDiskImage, sizeof(szBootDiskImage));
498: }
1.1 root 499: }
500:
1.1.1.6 root 501:
1.1.1.2 root 502: /*-----------------------------------------------------------------------*/
1.1.1.13 root 503: /**
504: * Un-Initialise emulation
505: */
1.1.1.8 root 506: static void Main_UnInit(void)
1.1 root 507: {
1.1.1.13 root 508: Screen_ReturnFromFullScreen();
509: Floppy_UnInit();
510: HDC_UnInit();
511: Midi_UnInit();
512: RS232_UnInit();
513: Printer_UnInit();
514: IoMem_UnInit();
515: NvRam_UnInit();
516: GemDOS_UnInitDrives();
517: Joy_UnInit();
518: if (Sound_AreWeRecording())
519: Sound_EndRecording();
520: Audio_UnInit();
521: SDLGui_UnInit();
522: #if ENABLE_DSP_EMU
523: if (ConfigureParams.System.nDSPType == DSP_TYPE_EMU)
524: {
525: DSP_UnInit();
526: }
527: HostScreen_UnInit();
528: #endif
529: Screen_UnInit();
530: Exit680x0();
1.1 root 531:
1.1.1.13 root 532: /* SDL uninit: */
533: SDL_Quit();
1.1.1.10 root 534:
1.1.1.13 root 535: /* Close debug log file */
536: Log_UnInit();
1.1 root 537: }
538:
1.1.1.6 root 539:
1.1.1.2 root 540: /*-----------------------------------------------------------------------*/
1.1.1.13 root 541: /**
542: * Main
543: */
1.1 root 544: int main(int argc, char *argv[])
545: {
1.1.1.13 root 546: /* Generate random seed */
547: srand(time(NULL));
548:
549: /* Initialize directory strings */
550: Paths_Init(argv[0]);
551:
552: /* no boot disk image */
553: szBootDiskImage[0] = 0;
1.1 root 554:
1.1.1.13 root 555: /* Set default configuration values: */
556: Configuration_SetDefault();
1.1.1.8 root 557:
1.1.1.13 root 558: /* Now load the values from the configuration file */
559: Configuration_Load(CONFDIR"/hatari.cfg"); /* Try the global configuration file first */
560: Configuration_Load(NULL); /* Now try the users configuration file */
1.1 root 561:
1.1.1.13 root 562: /* Check for any passed parameters, get boot disk */
563: Opt_ParseParameters(argc, argv, szBootDiskImage, sizeof(szBootDiskImage));
564: /* monitor type option might require "reset" -> TRUE */
565: Configuration_Apply(TRUE);
1.1.1.2 root 566:
1.1.1.13 root 567: #ifdef WIN32
568: Win_OpenCon();
569: #endif
1.1.1.7 root 570:
1.1.1.13 root 571: /* Needed on maemo but useful also with normal X11 window managers
572: * for window grouping when you have multiple Hatari SDL windows open
573: */
574: #if HAVE_SETENV
575: setenv("SDL_VIDEO_X11_WMCLASS", "hatari", 1);
576: #endif
1.1 root 577:
1.1.1.13 root 578: /* Init emulator system */
579: Main_Init();
1.1 root 580:
1.1.1.13 root 581: /* Check if SDL_Delay is accurate */
582: Main_CheckForAccurateDelays();
1.1.1.12 root 583:
1.1.1.13 root 584: /* Run emulation */
585: Main_UnPauseEmulation();
586: M68000_Start(); /* Start emulation */
1.1 root 587:
1.1.1.13 root 588: /* Un-init emulation system */
589: Main_UnInit();
1.1 root 590:
1.1.1.13 root 591: return 0;
1.1 root 592: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.