ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[mupen64plus-pandora.git] / source / mupen64plus-core / src / main / main.c
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  *   Mupen64plus - main.c                                                  *
3  *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
4  *   Copyright (C) 2012 CasualJames                                        *
5  *   Copyright (C) 2008-2009 Richard Goedeken                              *
6  *   Copyright (C) 2008 Ebenblues Nmn Okaygo Tillin9                       *
7  *   Copyright (C) 2002 Hacktarux                                          *
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  *   This program is distributed in the hope that it will be useful,       *
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
17  *   GNU General Public License for more details.                          *
18  *                                                                         *
19  *   You should have received a copy of the GNU General Public License     *
20  *   along with this program; if not, write to the                         *
21  *   Free Software Foundation, Inc.,                                       *
22  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
23  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25 /* This is MUPEN64's main entry point. It contains code that is common
26  * to both the gui and non-gui versions of mupen64. See
27  * gui subdirectories for the gui-specific code.
28  * if you want to implement an interface, you should look here
29  */
30
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include <SDL.h>
35
36 #define M64P_CORE_PROTOTYPES 1
37 #include "api/m64p_types.h"
38 #include "api/callbacks.h"
39 #include "api/config.h"
40 #include "api/m64p_config.h"
41 #include "api/debugger.h"
42 #include "api/vidext.h"
43
44 #include "main.h"
45 #include "eventloop.h"
46 #include "rom.h"
47 #include "savestates.h"
48 #include "util.h"
49
50 #include "memory/memory.h"
51 #include "osal/files.h"
52 #include "osal/preproc.h"
53 #include "osd/osd.h"
54 #include "osd/screenshot.h"
55 #include "plugin/plugin.h"
56 #include "r4300/r4300.h"
57 #include "r4300/interupt.h"
58 #include "r4300/reset.h"
59
60 #ifdef DBG
61 #include "debugger/dbg_types.h"
62 #include "debugger/debugger.h"
63 #endif
64
65 #ifdef WITH_LIRC
66 #include "lirc.h"
67 #endif //WITH_LIRC
68
69 /* version number for Core config section */
70 #define CONFIG_PARAM_VERSION 1.01
71
72 /** globals **/
73 m64p_handle g_CoreConfig = NULL;
74
75 m64p_frame_callback g_FrameCallback = NULL;
76
77 int         g_MemHasBeenBSwapped = 0;   // store byte-swapped flag so we don't swap twice when re-playing game
78 int         g_EmulatorRunning = 0;      // need separate boolean to tell if emulator is running, since --nogui doesn't use a thread
79
80 /** static (local) variables **/
81 static int   l_CurrentFrame = 0;         // frame counter
82 static int   l_TakeScreenshot = 0;       // Tell OSD Rendering callback to take a screenshot just before drawing the OSD
83 static int   l_SpeedFactor = 100;        // percentage of nominal game speed at which emulator is running
84 static int   l_FrameAdvance = 0;         // variable to check if we pause on next frame
85 static int   l_MainSpeedLimit = 1;       // insert delay during vi_interrupt to keep speed at real-time
86
87 static osd_message_t *l_msgVol = NULL;
88 static osd_message_t *l_msgFF = NULL;
89 static osd_message_t *l_msgPause = NULL;
90
91 /*********************************************************************************************************
92 * static functions
93 */
94
95 static const char *get_savepathdefault(const char *configpath)
96 {
97     static char path[1024];
98
99     if (!configpath || (strlen(configpath) == 0)) {
100         snprintf(path, 1024, "%ssave%c", ConfigGetUserDataPath(), OSAL_DIR_SEPARATORS[0]);
101         path[1023] = 0;
102     } else {
103         snprintf(path, 1024, "%s%c", configpath, OSAL_DIR_SEPARATORS[0]);
104         path[1023] = 0;
105     }
106
107     /* create directory if it doesn't exist */
108     osal_mkdirp(path, 0700);
109
110     return path;
111 }
112
113
114 /*********************************************************************************************************
115 * helper functions
116 */
117
118
119 const char *get_savestatepath(void)
120 {
121     /* try to get the SaveStatePath string variable in the Core configuration section */
122     return get_savepathdefault(ConfigGetParamString(g_CoreConfig, "SaveStatePath"));
123 }
124
125 const char *get_savesrampath(void)
126 {
127     /* try to get the SaveSRAMPath string variable in the Core configuration section */
128     return get_savepathdefault(ConfigGetParamString(g_CoreConfig, "SaveSRAMPath"));
129 }
130
131 void main_message(m64p_msg_level level, unsigned int corner, const char *format, ...)
132 {
133     va_list ap;
134     char buffer[2049];
135     va_start(ap, format);
136     vsnprintf(buffer, 2047, format, ap);
137     buffer[2048]='\0';
138     va_end(ap);
139
140     /* send message to on-screen-display if enabled */
141     if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay"))
142         osd_new_message((enum osd_corner) corner, "%s", buffer);
143     /* send message to front-end */
144     DebugMessage(level, "%s", buffer);
145 }
146
147
148 /*********************************************************************************************************
149 * global functions, for adjusting the core emulator behavior
150 */
151
152 int main_set_core_defaults(void)
153 {
154     float fConfigParamsVersion;
155     int bSaveConfig = 0, bUpgrade = 0;
156
157     if (ConfigGetParameter(g_CoreConfig, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS)
158     {
159         DebugMessage(M64MSG_WARNING, "No version number in 'Core' config section. Setting defaults.");
160         ConfigDeleteSection("Core");
161         ConfigOpenSection("Core", &g_CoreConfig);
162         bSaveConfig = 1;
163     }
164     else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION))
165     {
166         DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'Core' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION);
167         ConfigDeleteSection("Core");
168         ConfigOpenSection("Core", &g_CoreConfig);
169         bSaveConfig = 1;
170     }
171     else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f)
172     {
173         float fVersion = (float) CONFIG_PARAM_VERSION;
174         ConfigSetParameter(g_CoreConfig, "Version", M64TYPE_FLOAT, &fVersion);
175         DebugMessage(M64MSG_INFO, "Updating parameter set version in 'Core' config section to %.2f", fVersion);
176         bUpgrade = 1;
177         bSaveConfig = 1;
178     }
179
180     /* parameters controlling the operation of the core */
181     ConfigSetDefaultFloat(g_CoreConfig, "Version", (float) CONFIG_PARAM_VERSION,  "Mupen64Plus Core config parameter set version number.  Please don't change this version number.");
182     ConfigSetDefaultBool(g_CoreConfig, "OnScreenDisplay", 1, "Draw on-screen display if True, otherwise don't draw OSD");
183 #if defined(DYNAREC)
184     ConfigSetDefaultInt(g_CoreConfig, "R4300Emulator", 2, "Use Pure Interpreter if 0, Cached Interpreter if 1, or Dynamic Recompiler if 2 or more");
185 #else
186     ConfigSetDefaultInt(g_CoreConfig, "R4300Emulator", 1, "Use Pure Interpreter if 0, Cached Interpreter if 1, or Dynamic Recompiler if 2 or more");
187 #endif
188     ConfigSetDefaultBool(g_CoreConfig, "NoCompiledJump", 0, "Disable compiled jump commands in dynamic recompiler (should be set to False) ");
189     ConfigSetDefaultBool(g_CoreConfig, "DisableExtraMem", 0, "Disable 4MB expansion RAM pack. May be necessary for some games");
190     ConfigSetDefaultBool(g_CoreConfig, "AutoStateSlotIncrement", 0, "Increment the save state slot after each save operation");
191     ConfigSetDefaultBool(g_CoreConfig, "EnableDebugger", 0, "Activate the R4300 debugger when ROM execution begins, if core was built with Debugger support");
192     ConfigSetDefaultInt(g_CoreConfig, "CurrentStateSlot", 0, "Save state slot (0-9) to use when saving/loading the emulator state");
193     ConfigSetDefaultString(g_CoreConfig, "ScreenshotPath", "", "Path to directory where screenshots are saved. If this is blank, the default value of ${UserConfigPath}/screenshot will be used");
194     ConfigSetDefaultString(g_CoreConfig, "SaveStatePath", "", "Path to directory where emulator save states (snapshots) are saved. If this is blank, the default value of ${UserConfigPath}/save will be used");
195     ConfigSetDefaultString(g_CoreConfig, "SaveSRAMPath", "", "Path to directory where SRAM/EEPROM data (in-game saves) are stored. If this is blank, the default value of ${UserConfigPath}/save will be used");
196     ConfigSetDefaultString(g_CoreConfig, "SharedDataPath", "", "Path to a directory to search when looking for shared data files");
197     ConfigSetDefaultBool(g_CoreConfig, "DelaySI", 0, "Delay interrupt after DMA SI read/write");
198     ConfigSetDefaultInt(g_CoreConfig, "CountPerOp", 2, "Force number of cycles per emulated instruction");
199
200     /* handle upgrades */
201     if (bUpgrade)
202     {
203         if (fConfigParamsVersion < 1.01f)
204         {  // added separate SaveSRAMPath parameter in v1.01
205             const char *pccSaveStatePath = ConfigGetParamString(g_CoreConfig, "SaveStatePath");
206             if (pccSaveStatePath != NULL)
207                 ConfigSetParameter(g_CoreConfig, "SaveSRAMPath", M64TYPE_STRING, pccSaveStatePath);
208         }
209     }
210
211     if (bSaveConfig)
212         ConfigSaveSection("Core");
213
214     /* set config parameters for keyboard and joystick commands */
215     return event_set_core_defaults();
216 }
217
218 void main_speeddown(int percent)
219 {
220     if (l_SpeedFactor - percent > 10)  /* 10% minimum speed */
221     {
222         l_SpeedFactor -= percent;
223         main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor);
224         audio.setSpeedFactor(l_SpeedFactor);
225         StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor);
226     }
227 }
228
229 void main_speedup(int percent)
230 {
231     if (l_SpeedFactor + percent < 300) /* 300% maximum speed */
232     {
233         l_SpeedFactor += percent;
234         main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor);
235         audio.setSpeedFactor(l_SpeedFactor);
236         StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor);
237     }
238 }
239
240 static void main_speedset(int percent)
241 {
242     if (percent < 1 || percent > 1000)
243     {
244         DebugMessage(M64MSG_WARNING, "Invalid speed setting %i percent", percent);
245         return;
246     }
247     // disable fast-forward if it's enabled
248     main_set_fastforward(0);
249     // set speed
250     l_SpeedFactor = percent;
251     main_message(M64MSG_STATUS, OSD_BOTTOM_LEFT, "%s %d%%", "Playback speed:", l_SpeedFactor);
252     audio.setSpeedFactor(l_SpeedFactor);
253     StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor);
254 }
255
256 void main_set_fastforward(int enable)
257 {
258     static int ff_state = 0;
259     static int SavedSpeedFactor = 100;
260
261     if (enable && !ff_state)
262     {
263         ff_state = 1; /* activate fast-forward */
264         SavedSpeedFactor = l_SpeedFactor;
265         l_SpeedFactor = 250;
266         audio.setSpeedFactor(l_SpeedFactor);
267         StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor);
268         // set fast-forward indicator
269         l_msgFF = osd_new_message(OSD_TOP_RIGHT, "Fast Forward");
270         osd_message_set_static(l_msgFF);
271         osd_message_set_user_managed(l_msgFF);
272     }
273     else if (!enable && ff_state)
274     {
275         ff_state = 0; /* de-activate fast-forward */
276         l_SpeedFactor = SavedSpeedFactor;
277         audio.setSpeedFactor(l_SpeedFactor);
278         StateChanged(M64CORE_SPEED_FACTOR, l_SpeedFactor);
279         // remove message
280         osd_delete_message(l_msgFF);
281         l_msgFF = NULL;
282     }
283
284 }
285
286 static void main_set_speedlimiter(int enable)
287 {
288     l_MainSpeedLimit = enable ? 1 : 0;
289 }
290
291 static int main_is_paused(void)
292 {
293     return (g_EmulatorRunning && rompause);
294 }
295
296 void main_toggle_pause(void)
297 {
298     if (!g_EmulatorRunning)
299         return;
300
301     if (rompause)
302     {
303         DebugMessage(M64MSG_STATUS, "Emulation continued.");
304         if(l_msgPause)
305         {
306             osd_delete_message(l_msgPause);
307             l_msgPause = NULL;
308         }
309         StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING);
310     }
311     else
312     {
313         if(l_msgPause)
314             osd_delete_message(l_msgPause);
315
316         DebugMessage(M64MSG_STATUS, "Emulation paused.");
317         l_msgPause = osd_new_message(OSD_MIDDLE_CENTER, "Paused");
318         osd_message_set_static(l_msgPause);
319         osd_message_set_user_managed(l_msgPause);
320         StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED);
321     }
322
323     rompause = !rompause;
324     l_FrameAdvance = 0;
325 }
326
327 void main_advance_one(void)
328 {
329     l_FrameAdvance = 1;
330     rompause = 0;
331     StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING);
332 }
333
334 static void main_draw_volume_osd(void)
335 {
336     char msgString[64];
337     const char *volString;
338
339     // this calls into the audio plugin
340     volString = audio.volumeGetString();
341     if (volString == NULL)
342     {
343         strcpy(msgString, "Volume Not Supported.");
344     }
345     else
346     {
347         sprintf(msgString, "%s: %s", "Volume", volString);
348     }
349
350     // create a new message or update an existing one
351     if (l_msgVol != NULL)
352         osd_update_message(l_msgVol, "%s", msgString);
353     else {
354         l_msgVol = osd_new_message(OSD_MIDDLE_CENTER, "%s", msgString);
355         osd_message_set_user_managed(l_msgVol);
356     }
357 }
358
359 /* this function could be called as a result of a keypress, joystick/button movement,
360    LIRC command, or 'testshots' command-line option timer */
361 void main_take_next_screenshot(void)
362 {
363     l_TakeScreenshot = l_CurrentFrame + 1;
364 }
365
366 void main_state_set_slot(int slot)
367 {
368     if (slot < 0 || slot > 9)
369     {
370         DebugMessage(M64MSG_WARNING, "Invalid savestate slot '%i' in main_state_set_slot().  Using 0", slot);
371         slot = 0;
372     }
373
374     savestates_select_slot(slot);
375     StateChanged(M64CORE_SAVESTATE_SLOT, slot);
376 }
377
378 void main_state_inc_slot(void)
379 {
380     savestates_inc_slot();
381 }
382
383 static unsigned char StopRumble[64] = {0x23, 0x01, 0x03, 0xc0, 0x1b, 0x00, 0x00, 0x00,
384                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
385                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
386                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
387                                        0, 0, 0, 0, 0, 0, 0, 0};
388
389 void main_state_load(const char *filename)
390 {
391     input.controllerCommand(0, StopRumble);
392     input.controllerCommand(1, StopRumble);
393     input.controllerCommand(2, StopRumble);
394     input.controllerCommand(3, StopRumble);
395
396     if (filename == NULL) // Save to slot
397         savestates_set_job(savestates_job_load, savestates_type_m64p, NULL);
398     else
399         savestates_set_job(savestates_job_load, savestates_type_unknown, filename);
400 }
401
402 void main_state_save(int format, const char *filename)
403 {
404     if (filename == NULL) // Save to slot
405         savestates_set_job(savestates_job_save, savestates_type_m64p, NULL);
406     else // Save to file
407         savestates_set_job(savestates_job_save, (savestates_type)format, filename);
408 }
409
410 m64p_error main_core_state_query(m64p_core_param param, int *rval)
411 {
412     switch (param)
413     {
414         case M64CORE_EMU_STATE:
415             if (!g_EmulatorRunning)
416                 *rval = M64EMU_STOPPED;
417             else if (rompause)
418                 *rval = M64EMU_PAUSED;
419             else
420                 *rval = M64EMU_RUNNING;
421             break;
422         case M64CORE_VIDEO_MODE:
423             if (!VidExt_VideoRunning())
424                 *rval = M64VIDEO_NONE;
425             else if (VidExt_InFullscreenMode())
426                 *rval = M64VIDEO_FULLSCREEN;
427             else
428                 *rval = M64VIDEO_WINDOWED;
429             break;
430         case M64CORE_SAVESTATE_SLOT:
431             *rval = savestates_get_slot();
432             break;
433         case M64CORE_SPEED_FACTOR:
434             *rval = l_SpeedFactor;
435             break;
436         case M64CORE_SPEED_LIMITER:
437             *rval = l_MainSpeedLimit;
438             break;
439         case M64CORE_VIDEO_SIZE:
440         {
441             int width, height;
442             if (!g_EmulatorRunning)
443                 return M64ERR_INVALID_STATE;
444             main_get_screen_size(&width, &height);
445             *rval = (width << 16) + height;
446             break;
447         }
448         case M64CORE_AUDIO_VOLUME:
449         {
450             if (!g_EmulatorRunning)
451                 return M64ERR_INVALID_STATE;    
452             return main_volume_get_level(rval);
453         }
454         case M64CORE_AUDIO_MUTE:
455             *rval = main_volume_get_muted();
456             break;
457         case M64CORE_INPUT_GAMESHARK:
458             *rval = event_gameshark_active();
459             break;
460         // these are only used for callbacks; they cannot be queried or set
461         case M64CORE_STATE_LOADCOMPLETE:
462         case M64CORE_STATE_SAVECOMPLETE:
463             return M64ERR_INPUT_INVALID;
464         default:
465             return M64ERR_INPUT_INVALID;
466     }
467
468     return M64ERR_SUCCESS;
469 }
470
471 m64p_error main_core_state_set(m64p_core_param param, int val)
472 {
473     switch (param)
474     {
475         case M64CORE_EMU_STATE:
476             if (!g_EmulatorRunning)
477                 return M64ERR_INVALID_STATE;
478             if (val == M64EMU_STOPPED)
479             {        
480                 /* this stop function is asynchronous.  The emulator may not terminate until later */
481                 main_stop();
482                 return M64ERR_SUCCESS;
483             }
484             else if (val == M64EMU_RUNNING)
485             {
486                 if (main_is_paused())
487                     main_toggle_pause();
488                 return M64ERR_SUCCESS;
489             }
490             else if (val == M64EMU_PAUSED)
491             {    
492                 if (!main_is_paused())
493                     main_toggle_pause();
494                 return M64ERR_SUCCESS;
495             }
496             return M64ERR_INPUT_INVALID;
497         case M64CORE_VIDEO_MODE:
498             if (!g_EmulatorRunning)
499                 return M64ERR_INVALID_STATE;
500             if (val == M64VIDEO_WINDOWED)
501             {
502                 if (VidExt_InFullscreenMode())
503                     gfx.changeWindow();
504                 return M64ERR_SUCCESS;
505             }
506             else if (val == M64VIDEO_FULLSCREEN)
507             {
508                 if (!VidExt_InFullscreenMode())
509                     gfx.changeWindow();
510                 return M64ERR_SUCCESS;
511             }
512             return M64ERR_INPUT_INVALID;
513         case M64CORE_SAVESTATE_SLOT:
514             if (val < 0 || val > 9)
515                 return M64ERR_INPUT_INVALID;
516             savestates_select_slot(val);
517             return M64ERR_SUCCESS;
518         case M64CORE_SPEED_FACTOR:
519             if (!g_EmulatorRunning)
520                 return M64ERR_INVALID_STATE;
521             main_speedset(val);
522             return M64ERR_SUCCESS;
523         case M64CORE_SPEED_LIMITER:
524             main_set_speedlimiter(val);
525             return M64ERR_SUCCESS;
526         case M64CORE_VIDEO_SIZE:
527         {
528             // the front-end app is telling us that the user has resized the video output frame, and so
529             // we should try to update the video plugin accordingly.  First, check state
530             int width, height;
531             if (!g_EmulatorRunning)
532                 return M64ERR_INVALID_STATE;
533             width = (val >> 16) & 0xffff;
534             height = val & 0xffff;
535             // then call the video plugin.  if the video plugin supports resizing, it will resize its viewport and call
536             // VidExt_ResizeWindow to update the window manager handling our opengl output window
537             gfx.resizeVideoOutput(width, height);
538             return M64ERR_SUCCESS;
539         }
540         case M64CORE_AUDIO_VOLUME:
541             if (!g_EmulatorRunning)
542                 return M64ERR_INVALID_STATE;
543             if (val < 0 || val > 100)
544                 return M64ERR_INPUT_INVALID;
545             return main_volume_set_level(val);
546         case M64CORE_AUDIO_MUTE:
547             if ((main_volume_get_muted() && !val) || (!main_volume_get_muted() && val))
548                 return main_volume_mute();
549             return M64ERR_SUCCESS;
550         case M64CORE_INPUT_GAMESHARK:
551             if (!g_EmulatorRunning)
552                 return M64ERR_INVALID_STATE;
553             event_set_gameshark(val);
554             return M64ERR_SUCCESS;
555         // these are only used for callbacks; they cannot be queried or set
556         case M64CORE_STATE_LOADCOMPLETE:
557         case M64CORE_STATE_SAVECOMPLETE:
558             return M64ERR_INPUT_INVALID;
559         default:
560             return M64ERR_INPUT_INVALID;
561     }
562 }
563
564 m64p_error main_get_screen_size(int *width, int *height)
565 {
566     gfx.readScreen(NULL, width, height, 0);
567     return M64ERR_SUCCESS;
568 }
569
570 m64p_error main_read_screen(void *pixels, int bFront)
571 {
572     int width_trash, height_trash;
573     gfx.readScreen(pixels, &width_trash, &height_trash, bFront);
574     return M64ERR_SUCCESS;
575 }
576
577 m64p_error main_volume_up(void)
578 {
579     int level = 0;
580     audio.volumeUp();
581     main_draw_volume_osd();
582     main_volume_get_level(&level);
583     StateChanged(M64CORE_AUDIO_VOLUME, level);
584     return M64ERR_SUCCESS;
585 }
586
587 m64p_error main_volume_down(void)
588 {
589     int level = 0;
590     audio.volumeDown();
591     main_draw_volume_osd();
592     main_volume_get_level(&level);
593     StateChanged(M64CORE_AUDIO_VOLUME, level);
594     return M64ERR_SUCCESS;
595 }
596
597 m64p_error main_volume_get_level(int *level)
598 {
599     *level = audio.volumeGetLevel();
600     return M64ERR_SUCCESS;
601 }
602
603 m64p_error main_volume_set_level(int level)
604 {
605     audio.volumeSetLevel(level);
606     main_draw_volume_osd();
607     level = audio.volumeGetLevel();
608     StateChanged(M64CORE_AUDIO_VOLUME, level);
609     return M64ERR_SUCCESS;
610 }
611
612 m64p_error main_volume_mute(void)
613 {
614     audio.volumeMute();
615     main_draw_volume_osd();
616     StateChanged(M64CORE_AUDIO_MUTE, main_volume_get_muted());
617     return M64ERR_SUCCESS;
618 }
619
620 int main_volume_get_muted(void)
621 {
622     return (audio.volumeGetLevel() == 0);
623 }
624
625 m64p_error main_reset(int do_hard_reset)
626 {
627     if (do_hard_reset)
628         reset_hard_job |= 1;
629     else
630         reset_soft();
631     return M64ERR_SUCCESS;
632 }
633
634 /*********************************************************************************************************
635 * global functions, callbacks from the r4300 core or from other plugins
636 */
637
638 static void video_plugin_render_callback(int bScreenRedrawn)
639 {
640     int bOSD = ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay");
641
642     // if the flag is set to take a screenshot, then grab it now
643     if (l_TakeScreenshot != 0)
644     {
645         // if the OSD is enabled, and the screen has not been recently redrawn, then we cannot take a screenshot now because
646         // it contains the OSD text.  Wait until the next redraw
647         if (!bOSD || bScreenRedrawn)
648         {
649             TakeScreenshot(l_TakeScreenshot - 1);  // current frame number +1 is in l_TakeScreenshot
650             l_TakeScreenshot = 0; // reset flag
651         }
652     }
653
654     // if the OSD is enabled, then draw it now
655     if (bOSD)
656     {
657         osd_render();
658     }
659 }
660
661 void new_frame(void)
662 {
663     if (g_FrameCallback != NULL)
664         (*g_FrameCallback)(l_CurrentFrame);
665
666     /* advance the current frame */
667     l_CurrentFrame++;
668
669     if (l_FrameAdvance) {
670         rompause = 1;
671         l_FrameAdvance = 0;
672         StateChanged(M64CORE_EMU_STATE, M64EMU_PAUSED);
673     }
674 }
675
676 void new_vi(void)
677 {
678     int Dif;
679     unsigned int CurrentFPSTime;
680     static unsigned int LastFPSTime = 0;
681     static unsigned int CounterTime = 0;
682     static unsigned int CalculatedTime ;
683     static int VI_Counter = 0;
684
685     double VILimitMilliseconds = 1000.0 / ROM_PARAMS.vilimit;
686     double AdjustedLimit = VILimitMilliseconds * 100.0 / l_SpeedFactor;  // adjust for selected emulator speed
687     int time;
688
689     start_section(IDLE_SECTION);
690     VI_Counter++;
691
692 #ifdef DBG
693     if(g_DebuggerActive) DebuggerCallback(DEBUG_UI_VI, 0);
694 #endif
695
696     if(LastFPSTime == 0)
697     {
698         LastFPSTime = CounterTime = SDL_GetTicks();
699         return;
700     }
701     CurrentFPSTime = SDL_GetTicks();
702     
703     Dif = CurrentFPSTime - LastFPSTime;
704     
705     if (Dif < AdjustedLimit) 
706     {
707         CalculatedTime = (unsigned int) (CounterTime + AdjustedLimit * VI_Counter);
708         time = (int)(CalculatedTime - CurrentFPSTime);
709         if (time > 0 && l_MainSpeedLimit)
710         {
711             DebugMessage(M64MSG_VERBOSE, "    new_vi(): Waiting %ims", time);
712             SDL_Delay(time);
713         }
714         CurrentFPSTime = CurrentFPSTime + time;
715     }
716
717     if (CurrentFPSTime - CounterTime >= 1000.0 ) 
718     {
719         CounterTime = SDL_GetTicks();
720         VI_Counter = 0 ;
721     }
722     
723     LastFPSTime = CurrentFPSTime ;
724     end_section(IDLE_SECTION);
725 }
726
727 /*********************************************************************************************************
728 * emulation thread - runs the core
729 */
730 m64p_error main_run(void)
731 {
732     /* take the r4300 emulator mode from the config file at this point and cache it in a global variable */
733     r4300emu = ConfigGetParamInt(g_CoreConfig, "R4300Emulator");
734
735     /* set some other core parameters based on the config file values */
736     savestates_set_autoinc_slot(ConfigGetParamBool(g_CoreConfig, "AutoStateSlotIncrement"));
737     savestates_select_slot(ConfigGetParamInt(g_CoreConfig, "CurrentStateSlot"));
738     no_compiled_jump = ConfigGetParamBool(g_CoreConfig, "NoCompiledJump");
739         if (delay_si==-1) delay_si = ConfigGetParamBool(g_CoreConfig, "DelaySI");
740     if (count_per_op==-1) count_per_op = ConfigGetParamInt(g_CoreConfig, "CountPerOp");
741     if (count_per_op <= 0)
742         count_per_op = 2;
743
744         // initialize memory, and do byte-swapping if it's not been done yet
745     if (g_MemHasBeenBSwapped == 0)
746     {
747         init_memory(1);
748         g_MemHasBeenBSwapped = 1;
749     }
750     else
751     {
752         init_memory(0);
753     }
754     // Attach rom to plugins
755     if (!gfx.romOpen())
756     {
757         free_memory(); return M64ERR_PLUGIN_FAIL;
758     }
759     if (!audio.romOpen())
760     {
761         gfx.romClosed(); free_memory(); return M64ERR_PLUGIN_FAIL;
762     }
763     if (!input.romOpen())
764     {
765         audio.romClosed(); gfx.romClosed(); free_memory(); return M64ERR_PLUGIN_FAIL;
766     }
767
768         
769     /* set up the SDL key repeat and event filter to catch keyboard/joystick commands for the core */
770     event_initialize();
771
772     /* initialize the on-screen display */
773     if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay"))
774     {
775         // init on-screen display
776         int width = 640, height = 480;
777         gfx.readScreen(NULL, &width, &height, 0); // read screen to get width and height
778         osd_init(width, height);
779     }
780
781     // setup rendering callback from video plugin to the core, for screenshots and On-Screen-Display
782     gfx.setRenderingCallback(video_plugin_render_callback);
783
784 #ifdef WITH_LIRC
785     lircStart();
786 #endif // WITH_LIRC
787
788 #ifdef DBG
789     if (ConfigGetParamBool(g_CoreConfig, "EnableDebugger"))
790         init_debugger();
791 #endif
792     /* Startup message on the OSD */
793     osd_new_message(OSD_MIDDLE_CENTER, "Mupen64Plus Started...");
794     g_EmulatorRunning = 1;
795     StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING);
796     /* call r4300 CPU core and run the game */
797     r4300_reset_hard();
798     r4300_reset_soft();
799     r4300_execute();
800
801     /* now begin to shut down */
802 #ifdef WITH_LIRC
803     lircStop();
804 #endif // WITH_LIRC
805
806 #ifdef DBG
807     if (g_DebuggerActive)
808         destroy_debugger();
809 #endif
810
811     if (ConfigGetParamBool(g_CoreConfig, "OnScreenDisplay"))
812     {
813         osd_exit();
814     }
815
816     rsp.romClosed();
817     input.romClosed();
818     audio.romClosed();
819     gfx.romClosed();
820     free_memory();
821
822     // clean up
823     g_EmulatorRunning = 0;
824     StateChanged(M64CORE_EMU_STATE, M64EMU_STOPPED);
825
826     return M64ERR_SUCCESS;
827 }
828
829 void main_stop(void)
830 {
831     /* note: this operation is asynchronous.  It may be called from a thread other than the
832        main emulator thread, and may return before the emulator is completely stopped */
833     if (!g_EmulatorRunning)
834         return;
835
836     DebugMessage(M64MSG_STATUS, "Stopping emulation.");
837     if(l_msgPause)
838     {
839         osd_delete_message(l_msgPause);
840         l_msgPause = NULL;
841     }
842     if(l_msgFF)
843     {
844         osd_delete_message(l_msgFF);
845         l_msgFF = NULL;
846     }
847     if(l_msgVol)
848     {
849         osd_delete_message(l_msgVol);
850         l_msgVol = NULL;
851     }
852     if (rompause)
853     {
854         rompause = 0;
855         StateChanged(M64CORE_EMU_STATE, M64EMU_RUNNING);
856     }
857     stop = 1;
858 #ifdef DBG
859     if(g_DebuggerActive)
860     {
861         debugger_step();
862     }
863 #endif        
864 }