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