all repos — mgba @ e65d12d35e406d6d62aa859611c2feb560d7801f

mGBA Game Boy Advance Emulator

src/platform/openemu/mGBAGameCore.m (view raw)

  1/*
  2 Copyright (c) 2016, Jeffrey Pfau
  3
  4 Redistribution and use in source and binary forms, with or without
  5 modification, are permitted provided that the following conditions are met:
  6     * Redistributions of source code must retain the above copyright
  7       notice, this list of conditions and the following disclaimer.
  8     * Redistributions in binary form must reproduce the above copyright
  9       notice, this list of conditions and the following disclaimer in the
 10       documentation and/or other materials provided with the distribution.
 11
 12 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS''
 13 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 14 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 15 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 16 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 17 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 18 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 19 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 20 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 21 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 22 POSSIBILITY OF SUCH DAMAGE.
 23 */
 24
 25#import "mGBAGameCore.h"
 26
 27#include "util/common.h"
 28
 29#include "gba/cheats.h"
 30#include "gba/renderers/video-software.h"
 31#include "gba/serialize.h"
 32#include "gba/context/context.h"
 33#include "util/circle-buffer.h"
 34#include "util/memory.h"
 35#include "util/vfs.h"
 36
 37#import <OpenEmuBase/OERingBuffer.h>
 38#import "OEGBASystemResponderClient.h"
 39#import <OpenGL/gl.h>
 40
 41#define SAMPLES 1024
 42
 43@interface mGBAGameCore () <OEGBASystemResponderClient>
 44{
 45	struct GBAContext context;
 46	struct GBAVideoSoftwareRenderer renderer;
 47	struct GBACheatDevice cheats;
 48	struct GBACheatSet cheatSet;
 49	uint16_t keys;
 50}
 51@end
 52
 53@implementation mGBAGameCore
 54
 55- (id)init
 56{
 57	if ((self = [super init]))
 58	{
 59		// TODO: Add a log handler
 60		GBAContextInit(&context, 0);
 61		struct GBAOptions opts = {
 62			.useBios = true,
 63			.idleOptimization = IDLE_LOOP_REMOVE
 64		};
 65		GBAConfigLoadDefaults(&context.config, &opts);
 66		GBAVideoSoftwareRendererCreate(&renderer);
 67		renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
 68		renderer.outputBufferStride = 256;
 69		context.renderer = &renderer.d;
 70		GBAAudioResizeBuffer(&context.gba->audio, SAMPLES);
 71		GBACheatDeviceCreate(&cheats);
 72		GBACheatAttachDevice(context.gba, &cheats);
 73		GBACheatSetInit(&cheatSet, "openemu");
 74		GBACheatAddSet(&cheats, &cheatSet);
 75		keys = 0;
 76	}
 77
 78	return self;
 79}
 80
 81- (void)dealloc
 82{
 83	GBAContextDeinit(&context);
 84	GBACheatRemoveSet(&cheats, &cheatSet);
 85	GBACheatDeviceDestroy(&cheats);
 86	GBACheatSetDeinit(&cheatSet);
 87	free(renderer.outputBuffer);
 88
 89	[super dealloc];
 90}
 91
 92#pragma mark - Execution
 93
 94- (BOOL)loadFileAtPath:(NSString *)path error:(NSError **)error
 95{
 96	UNUSED(error);
 97	if (!GBAContextLoadROM(&context, [path UTF8String], true)) {
 98		return NO;
 99	}
100	GBAContextStart(&context);
101	return YES;
102}
103
104- (void)executeFrame
105{
106	GBAContextFrame(&context, keys);
107
108	int16_t samples[SAMPLES * 2];
109	size_t available = 0;
110#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
111	available = blip_samples_avail(context.gba->audio.left);
112	blip_read_samples(context.gba->audio.left, samples, available, true);
113	blip_read_samples(context.gba->audio.right, samples + 1, available, true);
114#else
115#error BLIP_BUF is required for now
116#endif
117	[[self ringBufferAtIndex:0] write:samples maxLength:available * 4];
118}
119
120- (void)resetEmulation
121{
122	ARMReset(context.cpu);
123}
124
125- (void)stopEmulation
126{
127	NSLog(@"Stopping");
128	GBAContextStop(&context);
129	[super stopEmulation];
130}
131
132- (void)setupEmulation
133{
134#if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF
135	blip_set_rates(context.gba->audio.left,  GBA_ARM7TDMI_FREQUENCY, 32768);
136	blip_set_rates(context.gba->audio.right, GBA_ARM7TDMI_FREQUENCY, 32768);
137#endif
138}
139
140#pragma mark - Video
141
142- (OEIntSize)aspectSize
143{
144	return OEIntSizeMake(3, 2);
145}
146
147- (OEIntRect)screenRect
148{
149    return OEIntRectMake(0, 0, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
150}
151
152- (OEIntSize)bufferSize
153{
154    return OEIntSizeMake(256, VIDEO_VERTICAL_PIXELS);
155}
156
157- (const void *)videoBuffer
158{
159	return renderer.outputBuffer;
160}
161
162- (GLenum)pixelFormat
163{
164    return GL_RGBA;
165}
166
167- (GLenum)pixelType
168{
169    return GL_UNSIGNED_INT_8_8_8_8_REV;
170}
171
172- (GLenum)internalPixelFormat
173{
174    return GL_RGB8;
175}
176
177- (NSTimeInterval)frameInterval
178{
179	return GBA_ARM7TDMI_FREQUENCY / (double) VIDEO_TOTAL_LENGTH;
180}
181
182#pragma mark - Audio
183
184- (NSUInteger)channelCount
185{
186    return 2;
187}
188
189- (double)audioSampleRate
190{
191    return 32768;
192}
193
194#pragma mark - Save State
195
196- (NSData *)serializeStateWithError:(NSError **)outError
197{
198	UNUSED(outError);
199	// TODO memory VFile that self-manages memory
200	return nil;
201}
202
203- (BOOL)deserializeState:(NSData *)state withError:(NSError **)outError
204{
205	UNUSED(outError);
206	// TODO VFileFromConstMemory
207	struct VFile* vf = VFileFromMemory((void*) state.bytes, state.length);
208	return GBALoadStateNamed(context.gba, vf, 0);
209}
210
211- (void)saveStateToFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
212{
213	struct VFile* vf = VFileOpen([fileName UTF8String], O_CREAT | O_TRUNC | O_RDWR);
214	block(GBASaveStateNamed(context.gba, vf, 0), nil);
215}
216
217- (void)loadStateFromFileAtPath:(NSString *)fileName completionHandler:(void (^)(BOOL, NSError *))block
218{
219	struct VFile* vf = VFileOpen([fileName UTF8String], O_RDONLY);
220	block(GBALoadStateNamed(context.gba, vf, 0), nil);
221}
222
223#pragma mark - Input
224
225const int GBAMap[] = {
226	GBA_KEY_UP,
227	GBA_KEY_DOWN,
228	GBA_KEY_LEFT,
229	GBA_KEY_RIGHT,
230	GBA_KEY_A,
231	GBA_KEY_B,
232	GBA_KEY_L,
233	GBA_KEY_R,
234	GBA_KEY_START,
235	GBA_KEY_SELECT
236};
237
238- (oneway void)didPushGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player
239{
240	UNUSED(player);
241	keys |= 1 << GBAMap[button];
242}
243
244- (oneway void)didReleaseGBAButton:(OEGBAButton)button forPlayer:(NSUInteger)player
245{
246	UNUSED(player);
247	keys &= ~(1 << GBAMap[button]);
248}
249
250@end
251