diff --git a/desmume/ChangeLog b/desmume/ChangeLog
index 3f950b4f3..1f7292589 100644
--- a/desmume/ChangeLog
+++ b/desmume/ChangeLog
@@ -18,6 +18,7 @@
- Added basic key mapping configuration to application preferences (thanks to Julio GorgŽ). [Jeff]
- Added keyboard shortcuts for Execute, Pause and Reset command (thanks to Julio GorgŽ). [Jeff]
- Default key mappings are no longer case sensitive. [Jeff]
+ - Added ability to limit speed. [Jeff]
Windows port:
- Removed the bug report link with a define, to avoid reports from betas/external builds [shash]
- Added the version on window bar to recognize versions from screenshots [shash]
diff --git a/desmume/src/GPU.h b/desmume/src/GPU.h
index 0cb6bfd79..252dba1fb 100644
--- a/desmume/src/GPU.h
+++ b/desmume/src/GPU.h
@@ -804,6 +804,7 @@ void GPU_setMasterBrightness (GPU *gpu, u16 val);
#define GPU_setWINOBJ(gpu, val) {gpu->WINOBJ = val&0x1F; gpu->WINOBJ_SPECIAL = (val>>5)&1;}
// Blending
+void SetupFinalPixelBlitter (GPU *gpu);
#define GPU_setBLDCNT_LOW(gpu, val) {gpu->BLDCNT = (gpu->BLDCNT&0xFF00) | val; SetupFinalPixelBlitter (gpu);}
#define GPU_setBLDCNT_HIGH(gpu, val) {gpu->BLDCNT = (gpu->BLDCNT&0xFF) | (val<<8); SetupFinalPixelBlitter (gpu);}
#define GPU_setBLDCNT(gpu, val) {gpu->BLDCNT = val; SetupFinalPixelBlitter (gpu);}
diff --git a/desmume/src/cocoa/DeSmuME.cbp b/desmume/src/cocoa/DeSmuME.cbp
index dd60be004..f7221a788 100644
--- a/desmume/src/cocoa/DeSmuME.cbp
+++ b/desmume/src/cocoa/DeSmuME.cbp
@@ -72,6 +72,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA]]>
+
+
@@ -139,6 +141,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA]]>
+
+
+
+
+
diff --git a/desmume/src/cocoa/DeSmuME.xcodeproj/project.pbxproj b/desmume/src/cocoa/DeSmuME.xcodeproj/project.pbxproj
index 74c6c3e39..349bc6149 100644
--- a/desmume/src/cocoa/DeSmuME.xcodeproj/project.pbxproj
+++ b/desmume/src/cocoa/DeSmuME.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 7233955A0E596E9100999693 /* speed_limit_selection_window.m in Sources */ = {isa = PBXBuildFile; fileRef = 723395590E596E9100999693 /* speed_limit_selection_window.m */; };
726D68AC0E310B1800800002 /* French.nib in Resources */ = {isa = PBXBuildFile; fileRef = 726D68AA0E310B1800800002 /* French.nib */; };
726D68AD0E310B1800800002 /* French.strings in Resources */ = {isa = PBXBuildFile; fileRef = 726D68AB0E310B1800800002 /* French.strings */; };
7277B62F0D9D9AEA00D283BD /* DeSmuME.icns in Resources */ = {isa = PBXBuildFile; fileRef = 7277B62E0D9D9AEA00D283BD /* DeSmuME.icns */; };
@@ -56,6 +57,8 @@
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; };
+ 723395580E596E9100999693 /* speed_limit_selection_window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = speed_limit_selection_window.h; path = dialogs/speed_limit_selection_window.h; sourceTree = ""; };
+ 723395590E596E9100999693 /* speed_limit_selection_window.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = speed_limit_selection_window.m; path = dialogs/speed_limit_selection_window.m; sourceTree = ""; };
726D68AA0E310B1800800002 /* French.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = French.nib; sourceTree = ""; };
726D68AB0E310B1800800002 /* French.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = French.strings; sourceTree = ""; };
7277B62E0D9D9AEA00D283BD /* DeSmuME.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = DeSmuME.icns; sourceTree = ""; };
@@ -159,6 +162,8 @@
080E96DDFE201D6D7F000001 /* Cocoa Port */ = {
isa = PBXGroup;
children = (
+ 723395580E596E9100999693 /* speed_limit_selection_window.h */,
+ 723395590E596E9100999693 /* speed_limit_selection_window.m */,
7277B8EA0D9F25F700D283BD /* about.m */,
729BEC5C0D9D55DB00ED561B /* globals.h */,
729BEC600D9D55DB00ED561B /* main.m */,
@@ -423,6 +428,7 @@
729BECE00D9D57AF00ED561B /* wifi.c in Sources */,
729BECFA0D9D589E00ED561B /* OGLRender.c in Sources */,
7277B8EB0D9F25F700D283BD /* about.m in Sources */,
+ 7233955A0E596E9100999693 /* speed_limit_selection_window.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/desmume/src/cocoa/English.strings b/desmume/src/cocoa/English.strings
index f7282ce6c..684577be9 100644
Binary files a/desmume/src/cocoa/English.strings and b/desmume/src/cocoa/English.strings differ
diff --git a/desmume/src/cocoa/French.strings b/desmume/src/cocoa/French.strings
index 0eba3ddaa..ddb1f40e9 100644
--- a/desmume/src/cocoa/French.strings
+++ b/desmume/src/cocoa/French.strings
@@ -3,6 +3,8 @@
/* General */
"DeSmuME Emulator" = "DeSmuME Emulator";
+//"OK" = ?;
+//"Cancel" = ?;
/* Menu Headers */
"File" = "Fichier";
@@ -31,6 +33,17 @@
"Auto" = "Automatique";
"Off" = "Off";
"Skip %d" = "%d";
+//"Speed Limit" = ?;
+//"25% Speed Limit" = ?;
+//"50% Speed Limit" = ?;
+//"75% Speed Limit" = ?;
+//"100% Speed Limit" = ?;
+//"200% Speed Limit" = ?;
+//"No Speed Limit" = ?;
+//"Custom Speed Limit" = ?;
+//"Custom Speed Limit Window" = ?;
+//"Set Max Speed:" = ?;
+"Speed %d%%" = "%d%%";
"Set FAT Image File..." = "Définir le fichier d'image FAT...";
/* View Menu */
diff --git a/desmume/src/cocoa/Japanese.strings b/desmume/src/cocoa/Japanese.strings
index 1000570c4..539a0b88f 100644
Binary files a/desmume/src/cocoa/Japanese.strings and b/desmume/src/cocoa/Japanese.strings differ
diff --git a/desmume/src/cocoa/main.m b/desmume/src/cocoa/main.m
index 915a32b27..8865df40d 100644
--- a/desmume/src/cocoa/main.m
+++ b/desmume/src/cocoa/main.m
@@ -66,6 +66,17 @@ extern NSMenuItem *execute_item;
extern NSMenuItem *pause_item;
extern NSMenuItem *reset_item;
+extern NSMenuItem *frame_skip_auto_item;
+extern NSMenuItem *frame_skip_item[];
+
+extern NSMenuItem *speed_limit_25_item;
+extern NSMenuItem *speed_limit_50_item;
+extern NSMenuItem *speed_limit_75_item;
+extern NSMenuItem *speed_limit_100_item;
+extern NSMenuItem *speed_limit_200_item;
+extern NSMenuItem *speed_limit_none_item;
+extern NSMenuItem *speed_limit_custom_item;
+
extern NSMenuItem *save_state_as_item;
extern NSMenuItem *load_state_from_item;
@@ -77,9 +88,6 @@ extern NSMenuItem *mute_item;
extern NSMenuItem *saveSlot_item[];
extern NSMenuItem *loadSlot_item[];
-extern NSMenuItem *frame_skip_auto_item;
-extern NSMenuItem *frame_skip_item[];
-
//extern NSMenuItem *clear_all_saves_item; waiting for more functionality from saves.h
extern NSMenuItem *rom_info_item;
@@ -241,6 +249,37 @@ void CreateMenu(AppDelegate *delegate)
[frame_skip_menu release];
}
+ //Speed limit menu
+
+ temp = [emulation_menu addItemWithTitle:NSLocalizedString(@"Speed Limit", nil) action:nil keyEquivalent:@""];
+
+ NSMenu *speed_limit_menu = [[NSMenu alloc] initWithTitle:NSLocalizedString(@"Speed Limit", nil)];
+ if(speed_limit_menu != nil)
+ {
+ [temp setSubmenu:speed_limit_menu];
+
+ speed_limit_25_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"25% Speed Limit", nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+ speed_limit_50_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"50% Speed Limit", nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+ speed_limit_75_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"75% Speed Limit", nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+
+ [speed_limit_menu addItem:[NSMenuItem separatorItem]];
+
+ speed_limit_100_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"100% Speed Limit", nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+
+ [speed_limit_menu addItem:[NSMenuItem separatorItem]];
+
+ speed_limit_200_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"200% Speed Limit", nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+ speed_limit_none_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"No Speed Limit" , nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+
+ [speed_limit_menu addItem:[NSMenuItem separatorItem]];
+
+ speed_limit_custom_item = [speed_limit_menu addItemWithTitle:NSLocalizedString(@"Custom Speed Limit", nil) action:@selector(setSpeedLimitFromMenuItem:) keyEquivalent:@""];
+
+ [speed_limit_menu release];
+ }
+
+ //
+
[emulation_menu addItem:[NSMenuItem separatorItem]];
[emulation_menu addItemWithTitle:NSLocalizedString(@"Set FAT Image File...", nil) action:@selector(pickFlash) keyEquivalent:@""];
diff --git a/desmume/src/cocoa/main_window.m b/desmume/src/cocoa/main_window.m
index 20ef79738..0f8fd7446 100644
--- a/desmume/src/cocoa/main_window.m
+++ b/desmume/src/cocoa/main_window.m
@@ -27,6 +27,7 @@
#import "input.h"
#import "rom_info.h"
#import "preferences.h"
+#import "speed_limit_selection_window.h"
//How much padding to put around the video output
#define WINDOW_BORDER_PADDING 5
@@ -54,6 +55,13 @@ NSMenuItem *loadSlot_item[MAX_SLOTS] = { nil, nil, nil, nil, nil, nil, nil, nil,
NSMenuItem *rom_info_item = nil;
NSMenuItem *frame_skip_auto_item = nil;
NSMenuItem *frame_skip_item[MAX_FRAME_SKIP] = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil };
+NSMenuItem *speed_limit_25_item = nil;
+NSMenuItem *speed_limit_50_item = nil;
+NSMenuItem *speed_limit_75_item = nil;
+NSMenuItem *speed_limit_100_item = nil;
+NSMenuItem *speed_limit_200_item = nil;
+NSMenuItem *speed_limit_none_item = nil;
+NSMenuItem *speed_limit_custom_item = nil;
NSMenuItem *volume_item[10] = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil };
NSMenuItem *mute_item = nil;
@@ -323,6 +331,69 @@ NSMenuItem *screenshot_to_file_item = nil;
}
}
+- (void)setSpeedLimit:(int)speedLimit
+{
+ [super setSpeedLimit:speedLimit];
+
+ //Set the correct menu states
+ speedLimit = [super speedLimit];
+ int standard_size = 0;
+
+ if([speed_limit_25_item target] == self)
+ if(speedLimit == 25){ [speed_limit_25_item setState:NSOnState]; standard_size=1; }
+ else [speed_limit_25_item setState:NSOffState];
+
+ if([speed_limit_50_item target] == self)
+ if(speedLimit == 50){ [speed_limit_50_item setState:NSOnState]; standard_size=1; }
+ else [speed_limit_50_item setState:NSOffState];
+
+ if([speed_limit_75_item target] == self)
+ if(speedLimit == 75){ [speed_limit_75_item setState:NSOnState]; standard_size=1; }
+ else [speed_limit_75_item setState:NSOffState];
+
+ if([speed_limit_100_item target] == self)
+ if(speedLimit == 100){ [speed_limit_100_item setState:NSOnState]; standard_size=1; }
+ else [speed_limit_100_item setState:NSOffState];
+
+ if([speed_limit_200_item target] == self)
+ if(speedLimit == 200){ [speed_limit_200_item setState:NSOnState]; standard_size=1; }
+ else [speed_limit_200_item setState:NSOffState];
+
+ if([speed_limit_none_item target] == self)
+ if(speedLimit == 0){ [speed_limit_none_item setState:NSOnState]; standard_size=1; }
+ else [speed_limit_none_item setState:NSOffState];
+
+ if([speed_limit_custom_item target] == self)
+ if(!standard_size)[speed_limit_custom_item setState:NSOnState];
+ else [speed_limit_custom_item setState:NSOffState];
+
+}
+
+- (void)setSpeedLimitFromMenuItem:(id)sender
+{
+ if(sender == speed_limit_25_item )[self setSpeedLimit: 25];
+ if(sender == speed_limit_50_item )[self setSpeedLimit: 50];
+ if(sender == speed_limit_75_item )[self setSpeedLimit: 75];
+ if(sender == speed_limit_100_item )[self setSpeedLimit: 100];
+ if(sender == speed_limit_200_item )[self setSpeedLimit: 200];
+ if(sender == speed_limit_none_item)[self setSpeedLimit: 0];
+
+ if(sender == speed_limit_custom_item)
+ {
+ //create
+ SpeedLimitSelectionWindow *s_window = [[SpeedLimitSelectionWindow alloc] initWithDS:self];
+
+ //show & run
+ NSWindowController *window_controller = [[NSWindowController alloc] initWithWindow:s_window];
+ [window_controller showWindow:nil];
+ [s_window runModal];
+
+ //release
+ [s_window release];
+ [window_controller release];
+ }
+}
+
- (void)closeROM
{
[super closeROM];
@@ -1192,6 +1263,15 @@ NSMenuItem *screenshot_to_file_item = nil;
else
[frame_skip_auto_item setState:NSOffState];
+ [speed_limit_25_item setTarget:self];
+ [speed_limit_50_item setTarget:self];
+ [speed_limit_75_item setTarget:self];
+ [speed_limit_100_item setTarget:self];
+ [speed_limit_200_item setTarget:self];
+ [speed_limit_none_item setTarget:self];
+ [speed_limit_custom_item setTarget:self];
+ [self setSpeedLimit:[self speedLimit]]; //this will set the checks correctly
+
//VIEW menu
//view options now target this window
diff --git a/desmume/src/cocoa/nds_control.h b/desmume/src/cocoa/nds_control.h
index da42583f2..e94e7080c 100644
--- a/desmume/src/cocoa/nds_control.h
+++ b/desmume/src/cocoa/nds_control.h
@@ -61,6 +61,7 @@
int volume;
volatile int frame_skip;
+ volatile int speed_limit;
NSString *current_file;
NSString *flash_file;
@@ -104,6 +105,8 @@
- (void)reset;
- (void)setFrameSkip:(int)frameskip; //negative is auto, 0 is off, more than 0 is the amount of frames to skip before showing a frame
- (int)frameSkip; //defaults to -1
+- (void)setSpeedLimit:(int)percent; //0 is off, 1-1000 is the pertance speed it runs at, anything else does nothing
+- (int)speedLimit;
//touch screen
- (void)touch:(NSPoint)point;
diff --git a/desmume/src/cocoa/nds_control.m b/desmume/src/cocoa/nds_control.m
index 7b4ba45b6..eb8a1682a 100644
--- a/desmume/src/cocoa/nds_control.m
+++ b/desmume/src/cocoa/nds_control.m
@@ -38,6 +38,7 @@ bool timer_based;
//ds screens are 59.8 frames per sec, so 1/59.8 seconds per frame
//times one million for microseconds per frame
+#define DS_SECONDS_PER_FRAME (1.0 / 59.8)
#define DS_MICROSECONDS_PER_FRAME (1.0 / 59.8) * 1000000.0
//accessed from other files
@@ -71,6 +72,7 @@ struct NDS_fw_config_data firmware;
display_object = nil;
error_object = nil;
frame_skip = -1; //default to auto frame skip
+ speed_limit = 100; //default to max speed = normal speed
gui_thread = [NSThread currentThread];
current_file = nil;
flash_file = nil;
@@ -518,6 +520,19 @@ struct NDS_fw_config_data firmware;
return frame_skip;
}
+- (void)setSpeedLimit:(int)speedLimit
+{
+ if(speedLimit < 0)return;
+ if(speedLimit > 1000)return;
+
+ speed_limit = speedLimit;
+}
+
+- (int)speedLimit
+{
+ return speed_limit;
+}
+
- (void)touch:(NSPoint)point
{
NDS_setTouchPos((unsigned short)point.x, (unsigned short)point.y);
@@ -1211,8 +1226,8 @@ struct NDS_fw_config_data firmware;
u32 cycles = 0;
+ NSDate *frame_end_date;
unsigned long long frame_start_time, frame_end_time;
-
int frames_to_skip = 0;
//program main loop
@@ -1226,6 +1241,9 @@ struct NDS_fw_config_data firmware;
paused = false;
+ int speed_limit_this_frame = speed_limit; //dont let speed limit change midframe
+ if(speed_limit_this_frame)frame_end_date = [[NSDate alloc] initWithTimeIntervalSinceNow: DS_SECONDS_PER_FRAME / ((float)speed_limit_this_frame / 100.0)];
+
Microseconds((struct UnsignedWide*)&frame_start_time);
[execution_lock lock];
@@ -1240,13 +1258,21 @@ struct NDS_fw_config_data firmware;
[execution_lock unlock];
+ Microseconds((struct UnsignedWide*)&frame_end_time);
+
+ //speed limit
+ if(speed_limit_this_frame)
+ {
+ [NSThread sleepUntilDate:frame_end_date];
+ [frame_end_date release];
+ }
+
if(frames_to_skip > 0)
frames_to_skip--;
else
{
- Microseconds((struct UnsignedWide*)&frame_end_time);
if(frame_skip < 0)
{ //automatic
diff --git a/desmume/src/windows/OGLRender.c b/desmume/src/windows/OGLRender.c
index a33387b64..0983c7025 100644
--- a/desmume/src/windows/OGLRender.c
+++ b/desmume/src/windows/OGLRender.c
@@ -29,6 +29,7 @@
#include
#include
#else
+ #define __forceinline
#include
#include
#endif