mirror of https://github.com/LIJI32/SameBoy.git
Initial emulation support
This commit is contained in:
parent
4c5d896630
commit
be765a3e7e
|
@ -2,8 +2,32 @@
|
|||
#import "GBHorizontalLayout.h"
|
||||
#import "GBVerticalLayout.h"
|
||||
#import "GBViewMetal.h"
|
||||
#import "GBAudioClient.h"
|
||||
#include <Core/gb.h>
|
||||
|
||||
@implementation GBViewController
|
||||
{
|
||||
GB_gameboy_t _gb;
|
||||
GBView *_gbView;
|
||||
volatile bool _running;
|
||||
volatile bool _stopping;
|
||||
GBLayout *_currentLayout;
|
||||
GBHorizontalLayout *_horizontalLayout;
|
||||
GBVerticalLayout *_verticalLayout;
|
||||
UIImageView *_backgroundView;
|
||||
UIImageView *_dpadView;
|
||||
UIImageView *_aButtonView;
|
||||
UIImageView *_bButtonView;
|
||||
UIImageView *_startButtonView;
|
||||
UIImageView *_selectButtonView;
|
||||
NSCondition *_audioLock;
|
||||
GB_sample_t *_audioBuffer;
|
||||
size_t _audioBufferSize;
|
||||
size_t _audioBufferPosition;
|
||||
size_t _audioBufferNeeded;
|
||||
GBAudioClient *_audioClient;
|
||||
}
|
||||
|
||||
static void positionView(UIImageView *view, CGPoint position)
|
||||
{
|
||||
double center = view.image.size.width / 2 * [UIScreen mainScreen].scale;
|
||||
|
@ -17,19 +41,63 @@ static void positionView(UIImageView *view, CGPoint position)
|
|||
|
||||
}
|
||||
|
||||
@implementation GBViewController
|
||||
static void loadBootROM(GB_gameboy_t *gb, GB_boot_rom_t type)
|
||||
{
|
||||
GBLayout *_currentLayout;
|
||||
GBHorizontalLayout *_horizontalLayout;
|
||||
GBVerticalLayout *_verticalLayout;
|
||||
UIImageView *_backgroundView;
|
||||
UIImageView *_dpadView;
|
||||
UIImageView *_aButtonView;
|
||||
UIImageView *_bButtonView;
|
||||
UIImageView *_startButtonView;
|
||||
UIImageView *_selectButtonView;
|
||||
GBView *_gbView;
|
||||
GB_gameboy_t _gb;
|
||||
GBViewController *self = (__bridge GBViewController *)GB_get_user_data(gb);
|
||||
[self loadBootROM:type];
|
||||
}
|
||||
|
||||
static void vblank(GB_gameboy_t *gb, GB_vblank_type_t type)
|
||||
{
|
||||
GBViewController *self = (__bridge GBViewController *)GB_get_user_data(gb);
|
||||
[self vblankWithType:type];
|
||||
}
|
||||
|
||||
static void consoleLog(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
|
||||
{
|
||||
static NSString *buffer = @"";
|
||||
buffer = [buffer stringByAppendingString:@(string)];
|
||||
if ([buffer containsString:@"\n"]) {
|
||||
NSLog(@"%@", buffer);
|
||||
buffer = @"";
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return (r << 0) | (g << 8) | (b << 16) | 0xFF000000;
|
||||
}
|
||||
|
||||
static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
|
||||
{
|
||||
GBViewController *self = (__bridge GBViewController *)GB_get_user_data(gb);
|
||||
[self gotNewSample:sample];
|
||||
}
|
||||
|
||||
static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
||||
{
|
||||
GBViewController *self = (__bridge GBViewController *)GB_get_user_data(gb);
|
||||
[self rumbleChanged:amp];
|
||||
}
|
||||
|
||||
- (void)initGameBoy
|
||||
{
|
||||
GB_init(&_gb, GB_MODEL_CGB_E);
|
||||
GB_set_user_data(&_gb, (__bridge void *)(self));
|
||||
GB_set_boot_rom_load_callback(&_gb, (GB_boot_rom_load_callback_t)loadBootROM);
|
||||
GB_set_vblank_callback(&_gb, (GB_vblank_callback_t) vblank);
|
||||
GB_set_log_callback(&_gb, (GB_log_callback_t) consoleLog);
|
||||
GB_set_color_correction_mode(&_gb, (GB_color_correction_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBColorCorrection"]);
|
||||
GB_set_light_temperature(&_gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBLightTemperature"]);
|
||||
GB_set_interference_volume(&_gb, [[NSUserDefaults standardUserDefaults] doubleForKey:@"GBInterferenceVolume"]);
|
||||
GB_set_border_mode(&_gb, GB_BORDER_NEVER);
|
||||
[self updatePalette];
|
||||
GB_set_rgb_encode_callback(&_gb, rgbEncode);
|
||||
GB_set_highpass_filter_mode(&_gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
|
||||
GB_set_rtc_mode(&_gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRTCMode"]);
|
||||
GB_apu_set_sample_callback(&_gb, audioCallback);
|
||||
GB_set_rumble_callback(&_gb, rumbleCallback);
|
||||
[self updateRumbleMode];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
|
@ -54,7 +122,7 @@ static void positionView(UIImageView *view, CGPoint position)
|
|||
_selectButtonView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"button2"]];
|
||||
_gbView = [[GBViewMetal alloc] initWithFrame:CGRectZero];
|
||||
|
||||
GB_init(&_gb, GB_MODEL_CGB_E);
|
||||
[self initGameBoy];
|
||||
_gbView.gb = &_gb;
|
||||
[_gbView screenSizeChanged];
|
||||
|
||||
|
@ -68,9 +136,21 @@ static void positionView(UIImageView *view, CGPoint position)
|
|||
[_backgroundView addSubview:_selectButtonView];
|
||||
[_backgroundView addSubview:_gbView];
|
||||
|
||||
_audioLock = [[NSCondition alloc] init];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application
|
||||
{
|
||||
[self start];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application
|
||||
{
|
||||
[self stop];
|
||||
}
|
||||
|
||||
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
|
||||
{
|
||||
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
|
||||
|
@ -96,7 +176,6 @@ static void positionView(UIImageView *view, CGPoint position)
|
|||
screenFrame.size.height /= [UIScreen mainScreen].scale;
|
||||
|
||||
_gbView.frame = screenFrame;
|
||||
memset(_gbView.pixels, rand(), 160 * 144 * 4);
|
||||
[_gbView flip];
|
||||
}
|
||||
|
||||
|
@ -109,4 +188,176 @@ static void positionView(UIImageView *view, CGPoint position)
|
|||
return true;
|
||||
}
|
||||
|
||||
- (void)preRun
|
||||
{
|
||||
GB_set_pixels_output(&_gb, _gbView.pixels);
|
||||
GB_set_sample_rate(&_gb, 96000);
|
||||
_audioClient = [[GBAudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, GB_sample_t *buffer) {
|
||||
[_audioLock lock];
|
||||
|
||||
if (_audioBufferPosition < nFrames) {
|
||||
_audioBufferNeeded = nFrames;
|
||||
[_audioLock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.125]];
|
||||
}
|
||||
|
||||
if (_stopping) {
|
||||
memset(buffer, 0, nFrames * sizeof(*buffer));
|
||||
[_audioLock unlock];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_audioBufferPosition < nFrames) {
|
||||
// Not enough audio
|
||||
memset(buffer, 0, (nFrames - _audioBufferPosition) * sizeof(*buffer));
|
||||
memcpy(buffer, _audioBuffer, _audioBufferPosition * sizeof(*buffer));
|
||||
_audioBufferPosition = 0;
|
||||
}
|
||||
else if (_audioBufferPosition < nFrames + 4800) {
|
||||
memcpy(buffer, _audioBuffer, nFrames * sizeof(*buffer));
|
||||
memmove(_audioBuffer, _audioBuffer + nFrames, (_audioBufferPosition - nFrames) * sizeof(*buffer));
|
||||
_audioBufferPosition = _audioBufferPosition - nFrames;
|
||||
}
|
||||
else {
|
||||
memcpy(buffer, _audioBuffer + (_audioBufferPosition - nFrames), nFrames * sizeof(*buffer));
|
||||
_audioBufferPosition = 0;
|
||||
}
|
||||
[_audioLock unlock];
|
||||
} andSampleRate:96000];
|
||||
|
||||
[_audioClient start];
|
||||
}
|
||||
|
||||
- (void)run
|
||||
{
|
||||
[self preRun];
|
||||
while (_running) {
|
||||
GB_run(&_gb);
|
||||
}
|
||||
[self postRun];
|
||||
_stopping = false;
|
||||
}
|
||||
|
||||
- (void)postRun
|
||||
{
|
||||
[_audioLock lock];
|
||||
memset(_audioBuffer, 0, (_audioBufferSize - _audioBufferPosition) * sizeof(*_audioBuffer));
|
||||
_audioBufferPosition = _audioBufferNeeded;
|
||||
[_audioLock signal];
|
||||
[_audioLock unlock];
|
||||
[_audioClient stop];
|
||||
_audioClient = nil;
|
||||
|
||||
// Todo
|
||||
//GB_save_battery(&gb, self.savPath.UTF8String);
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
if (_running) return;
|
||||
_running = true;
|
||||
[[[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil] start];
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
if (!_running) return;
|
||||
[_audioLock lock];
|
||||
_stopping = true;
|
||||
[_audioLock signal];
|
||||
[_audioLock unlock];
|
||||
_running = false;
|
||||
while (_stopping) {
|
||||
[_audioLock lock];
|
||||
[_audioLock signal];
|
||||
[_audioLock unlock];
|
||||
}
|
||||
}
|
||||
- (void)loadBootROM: (GB_boot_rom_t)type
|
||||
{
|
||||
static NSString *const names[] = {
|
||||
[GB_BOOT_ROM_DMG_0] = @"dmg0_boot",
|
||||
[GB_BOOT_ROM_DMG] = @"dmg_boot",
|
||||
[GB_BOOT_ROM_MGB] = @"mgb_boot",
|
||||
[GB_BOOT_ROM_SGB] = @"sgb_boot",
|
||||
[GB_BOOT_ROM_SGB2] = @"sgb2_boot",
|
||||
[GB_BOOT_ROM_CGB_0] = @"cgb0_boot",
|
||||
[GB_BOOT_ROM_CGB] = @"cgb_boot",
|
||||
[GB_BOOT_ROM_AGB] = @"agb_boot",
|
||||
};
|
||||
GB_load_boot_rom(&_gb, [[[NSBundle mainBundle] pathForResource:names[type] ofType:@"bin"] UTF8String]);
|
||||
}
|
||||
|
||||
- (void)vblankWithType:(GB_vblank_type_t)type
|
||||
{
|
||||
if (type != GB_VBLANK_TYPE_REPEAT) {
|
||||
[_gbView flip];
|
||||
GB_set_pixels_output(&_gb, _gbView.pixels);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)gotNewSample:(GB_sample_t *)sample
|
||||
{
|
||||
[_audioLock lock];
|
||||
if (_audioClient.isPlaying) {
|
||||
if (_audioBufferPosition == _audioBufferSize) {
|
||||
if (_audioBufferSize >= 0x4000) {
|
||||
_audioBufferPosition = 0;
|
||||
[_audioLock unlock];
|
||||
return;
|
||||
}
|
||||
|
||||
if (_audioBufferSize == 0) {
|
||||
_audioBufferSize = 512;
|
||||
}
|
||||
else {
|
||||
_audioBufferSize += _audioBufferSize >> 2;
|
||||
}
|
||||
_audioBuffer = realloc(_audioBuffer, sizeof(*sample) * _audioBufferSize);
|
||||
}
|
||||
_audioBuffer[_audioBufferPosition++] = *sample;
|
||||
}
|
||||
if (_audioBufferPosition == _audioBufferNeeded) {
|
||||
[_audioLock signal];
|
||||
_audioBufferNeeded = 0;
|
||||
}
|
||||
[_audioLock unlock];
|
||||
}
|
||||
|
||||
- (void)rumbleChanged:(double)amp
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
- (void)updateRumbleMode
|
||||
{
|
||||
GB_set_rumble_mode(&_gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRumbleMode"]);
|
||||
}
|
||||
|
||||
- (const GB_palette_t *)userPalette
|
||||
{
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
switch ([defaults integerForKey:@"GBColorPalette"]) {
|
||||
case 1: return &GB_PALETTE_DMG;
|
||||
case 2: return &GB_PALETTE_MGB;
|
||||
case 3: return &GB_PALETTE_GBL;
|
||||
default: return &GB_PALETTE_GREY;
|
||||
case -1: {
|
||||
static GB_palette_t customPalette;
|
||||
NSArray *colors = [defaults dictionaryForKey:@"GBThemes"][[defaults stringForKey:@"GBCurrentTheme"]][@"Colors"];
|
||||
if (colors.count == 5) {
|
||||
unsigned i = 0;
|
||||
for (NSNumber *color in colors) {
|
||||
uint32_t c = [color unsignedIntValue];
|
||||
customPalette.colors[i++] = (struct GB_color_s) {c, c >> 8, c >> 16};
|
||||
}
|
||||
}
|
||||
return &customPalette;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updatePalette
|
||||
{
|
||||
GB_set_palette(&_gb, [self userPalette]);
|
||||
}
|
||||
@end
|
||||
|
|
78
iOS/main.m
78
iOS/main.m
|
@ -1,7 +1,85 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import "GBViewController.h"
|
||||
#include <Core/gb.h>
|
||||
#include "GBView.h"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
||||
@"GBFilter": @"NearestNeighbor",
|
||||
@"GBColorCorrection": @(GB_COLOR_CORRECTION_MODERN_BALANCED),
|
||||
@"GBHighpassFilter": @(GB_HIGHPASS_REMOVE_DC_OFFSET),
|
||||
@"GBFrameBlendingMode": @(GB_FRAME_BLENDING_MODE_ACCURATE),
|
||||
|
||||
@"GBDMGModel": @(GB_MODEL_DMG_B),
|
||||
@"GBCGBModel": @(GB_MODEL_CGB_E),
|
||||
@"GBAGBModel": @(GB_MODEL_AGB_A),
|
||||
@"GBSGBModel": @(GB_MODEL_SGB2),
|
||||
@"GBRumbleMode": @(GB_RUMBLE_CARTRIDGE_ONLY),
|
||||
|
||||
@"GBVolume": @(1.0),
|
||||
|
||||
// Default themes
|
||||
@"GBThemes": @{
|
||||
@"Desert": @{
|
||||
@"BrightnessBias": @0.0,
|
||||
@"Colors": @[@0xff302f3e, @0xff576674, @0xff839ba4, @0xffb1d0d2, @0xffb7d7d8],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.10087773904382469,
|
||||
@"HueBiasStrength": @0.062142056772908363,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
@"Evening": @{
|
||||
@"BrightnessBias": @-0.10168700106441975,
|
||||
@"Colors": @[@0xff362601, @0xff695518, @0xff899853, @0xffa6e4ae, @0xffa9eebb],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.60027079191058874,
|
||||
@"HueBiasStrength": @0.33816297305747867,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
@"Fog": @{
|
||||
@"BrightnessBias": @0.0,
|
||||
@"Colors": @[@0xff373c34, @0xff737256, @0xff9da386, @0xffc3d2bf, @0xffc7d8c6],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.55750435756972117,
|
||||
@"HueBiasStrength": @0.18424738545816732,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
@"Magic Eggplant": @{
|
||||
@"BrightnessBias": @0.0,
|
||||
@"Colors": @[@0xff3c2136, @0xff942e84, @0xffc7699d, @0xfff1e4b0, @0xfff6f9b2],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.87717878486055778,
|
||||
@"HueBiasStrength": @0.65018052788844627,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
@"Radioactive Pea": @{
|
||||
@"BrightnessBias": @-0.48079556772908372,
|
||||
@"Colors": @[@0xff215200, @0xff1f7306, @0xff169e34, @0xff03ceb8, @0xff00d4d1],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.3795131972111554,
|
||||
@"HueBiasStrength": @0.34337649402390436,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
@"Seaweed": @{
|
||||
@"BrightnessBias": @-0.28532744023904377,
|
||||
@"Colors": @[@0xff3f0015, @0xff426532, @0xff58a778, @0xff95e0df, @0xffa0e7ee],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.2694067480079681,
|
||||
@"HueBiasStrength": @0.51565612549800799,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
@"Twilight": @{
|
||||
@"BrightnessBias": @-0.091789093625498031,
|
||||
@"Colors": @[@0xff3f0015, @0xff461286, @0xff6254bd, @0xff97d3e9, @0xffa0e7ee],
|
||||
@"DisabledLCDColor": @YES,
|
||||
@"HueBias": @0.0,
|
||||
@"HueBiasStrength": @0.49710532868525897,
|
||||
@"Manual": @NO,
|
||||
},
|
||||
},
|
||||
}];
|
||||
}
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([GBViewController class]));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue