Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract CallbackBridge from Document. #327

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
139 changes: 18 additions & 121 deletions Cocoa/Document.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <CoreAudio/CoreAudio.h>
#include <Core/gb.h>
#include "GBAudioClient.h"
#include "GBCallbackBridge.h"
#include "Document.h"
#include "AppDelegate.h"
#include "HexFiend/HexFiend.h"
Expand All @@ -22,9 +23,9 @@
MODEL_SGB,
};

@interface Document ()
@interface Document () <GBCallbackBridgeDelegate>
{

NSMutableAttributedString *pending_console_output;
NSRecursiveLock *console_output_lock;
NSTimer *console_output_timer;
Expand Down Expand Up @@ -73,82 +74,19 @@ @interface Document ()
Document *slave;
signed linkOffset;
bool linkCableBit;

GBCallbackBridge *callbackBridge;
}

@property GBAudioClient *audioClient;
- (void) vblank;
- (void) log: (const char *) log withAttributes: (GB_log_attributes) attributes;
- (char *) getDebuggerInput;
- (char *) getAsyncDebuggerInput;
- (void) cameraRequestUpdate;
- (uint8_t) cameraGetPixelAtX:(uint8_t)x andY:(uint8_t)y;
- (void) printImage:(uint32_t *)image height:(unsigned) height
topMargin:(unsigned) topMargin bottomMargin: (unsigned) bottomMargin
exposure:(unsigned) exposure;
- (void) gotNewSample:(GB_sample_t *)sample;
- (void) rumbleChanged:(double)amp;
- (void) loadBootROM:(GB_boot_rom_t)type;
- (void)linkCableBitStart:(bool)bit;
- (bool)linkCableBitEnd;
- (void)infraredStateChanged:(bool)state;

@end

static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self loadBootROM: type];
}

static void vblank(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self vblank];
}

static void consoleLog(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self log:string withAttributes: attributes];
}

static char *consoleInput(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
return [self getDebuggerInput];
}

static char *asyncConsoleInput(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
char *ret = [self getAsyncDebuggerInput];
return ret;
}

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 cameraRequestUpdate(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self cameraRequestUpdate];
}

static uint8_t cameraGetPixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
return [self cameraGetPixelAtX:x andY:y];
}

static void printImage(GB_gameboy_t *gb, uint32_t *image, uint8_t height,
uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self printImage:image height:height topMargin:top_margin bottomMargin:bottom_margin exposure:exposure];
}

static void setWorkboyTime(GB_gameboy_t *gb, time_t t)
{
[[NSUserDefaults standardUserDefaults] setInteger:time(NULL) - t forKey:@"GBWorkboyTimeOffset"];
Expand All @@ -159,37 +97,6 @@ static time_t getWorkboyTime(GB_gameboy_t *gb)
return time(NULL) - [[NSUserDefaults standardUserDefaults] integerForKey:@"GBWorkboyTimeOffset"];
}

static void audioCallback(GB_gameboy_t *gb, GB_sample_t *sample)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self gotNewSample:sample];
}

static void rumbleCallback(GB_gameboy_t *gb, double amp)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self rumbleChanged:amp];
}


static void linkCableBitStart(GB_gameboy_t *gb, bool bit_to_send)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self linkCableBitStart:bit_to_send];
}

static bool linkCableBitEnd(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
return [self linkCableBitEnd];
}

static void infraredStateChanged(GB_gameboy_t *gb, bool on)
{
Document *self = (__bridge Document *)GB_get_user_data(gb);
[self infraredStateChanged:on];
}


@implementation Document
{
Expand Down Expand Up @@ -279,25 +186,17 @@ - (void) updateRumbleMode
- (void) initCommon
{
GB_init(&gb, [self internalModel]);
GB_set_user_data(&gb, (__bridge void *)(self));
GB_set_boot_rom_load_callback(&gb, (GB_boot_rom_load_callback_t)boot_rom_load);
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);

callbackBridge = [[GBCallbackBridge alloc] initWithGameboy:&gb delegate:self];

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_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBBorderMode"]);
[self updatePalette];
GB_set_rgb_encode_callback(&gb, rgbEncode);
GB_set_camera_get_pixel_callback(&gb, cameraGetPixel);
GB_set_camera_update_request_callback(&gb, cameraRequestUpdate);
GB_set_highpass_filter_mode(&gb, (GB_highpass_mode_t) [[NSUserDefaults standardUserDefaults] integerForKey:@"GBHighpassFilter"]);
GB_set_rewind_length(&gb, [[NSUserDefaults standardUserDefaults] integerForKey:@"GBRewindLength"]);
GB_apu_set_sample_callback(&gb, audioCallback);
GB_set_rumble_callback(&gb, rumbleCallback);
GB_set_infrared_callback(&gb, infraredStateChanged);
[self updateRumbleMode];
}

Expand Down Expand Up @@ -1103,7 +1002,7 @@ - (void) updateSideView
[console_output_lock unlock];
}

- (char *) getDebuggerInput
- (NSString *) getDebuggerInput
{
[self updateSideView];
[self log:">"];
Expand All @@ -1121,12 +1020,12 @@ - (char *) getDebuggerInput
}
});
if ((id) input == [NSNull null]) {
return NULL;
return nil;
}
return strdup([input UTF8String]);
return input;
}

- (char *) getAsyncDebuggerInput
- (NSString *) getAsyncDebuggerInput
{
[has_debugger_input lock];
NSString *input = [debugger_input_queue firstObject];
Expand All @@ -1135,9 +1034,9 @@ - (char *) getAsyncDebuggerInput
}
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
if ((id)input == [NSNull null]) {
return NULL;
return nil;
}
return input? strdup([input UTF8String]): NULL;
return input;
}

- (IBAction)saveState:(id)sender
Expand Down Expand Up @@ -1176,7 +1075,7 @@ - (IBAction)clearConsole:(id)sender

- (void)log:(const char *)log
{
[self log:log withAttributes:0];
[self log:@(log) withAttributes:0];
}

- (uint8_t) readMemory:(uint16_t)addr
Expand Down Expand Up @@ -1820,7 +1719,7 @@ - (IBAction)connectPrinter:(id)sender
[self disconnectLinkCable];
[self performAtomicBlock:^{
accessory = GBAccessoryPrinter;
GB_connect_printer(&gb, printImage);
GB_connect_printer(&gb, GBCallbackPrintImage);
}];
}

Expand Down Expand Up @@ -1994,10 +1893,8 @@ - (void)connectLinkCable:(NSMenuItem *)sender
linkOffset = 0;
partner->accessory = GBAccessoryLinkCable;
accessory = GBAccessoryLinkCable;
GB_set_serial_transfer_bit_start_callback(&gb, linkCableBitStart);
GB_set_serial_transfer_bit_start_callback(&partner->gb, linkCableBitStart);
GB_set_serial_transfer_bit_end_callback(&gb, linkCableBitEnd);
GB_set_serial_transfer_bit_end_callback(&partner->gb, linkCableBitEnd);
[callbackBridge enableSerialCallbacks];
[partner->callbackBridge enableSerialCallbacks];
if (wasRunning) {
[self start];
}
Expand Down
96 changes: 96 additions & 0 deletions Cocoa/GBCallbackBridge.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#import <Foundation/Foundation.h>

#include <Core/gb.h>

/** GB_connect_printer can use this callback to invoke the delegate for an already connected GBCallbackBridge. */
void GBCallbackPrintImage(GB_gameboy_t *_Nonnull gb, uint32_t *_Nonnull image, uint8_t height,
uint8_t top_margin, uint8_t bottom_margin, uint8_t exposure);

/**
GBCallbackBridgeDelegate is implemented by objects that intend to receive events from a GB_gameboy_t using a
GBGBCallbackBridge.
*/
@protocol GBCallbackBridgeDelegate <NSObject>
@required

/**
Tells the receiver to load the given boot rom type.

The receiver is expected to invoke GB_load_boot_rom with the appropriate boot rom path for the given type.
*/
- (void)loadBootROM:(GB_boot_rom_t)type;

@optional

/**
Informs the receiver that the PPU has entered vblank mode.

The receiver will typically use this event to swap the pixel buffer using GB_set_pixels_output.
*/
- (void)vblank;

/**
Informs the receiver that a new audio sample was received.

The provided sample should be appended to an audio buffer for playback.

This method is required if a sample rate is provided by GB_set_sample_rate.
*/
- (void)gotNewSample:(nonnull GB_sample_t *)sample;

/**
Informs the receiver that a log message has been generated.

The log message is typically appended to a console.
*/
- (void)log:(nonnull NSString *)log withAttributes:(GB_log_attributes)attributes;

/**
Asks the receiver for the next debugger input.

This is a blocking invocation; until it returns, the emulator will be paused on a background thread.

Returning nil will cause emulation to continue, and this method will not be invoked again until GB_debugger_break is
invoked on the GB_gameboy_t again.
*/
- (nullable NSString *)getDebuggerInput;
- (nullable NSString *)getAsyncDebuggerInput;
- (uint8_t)cameraGetPixelAtX:(uint8_t)x andY:(uint8_t)y;
- (void)cameraRequestUpdate;
- (void)printImage:(nonnull uint32_t *)image
height:(unsigned)height
topMargin:(unsigned)topMargin
bottomMargin:(unsigned)bottomMargin
exposure:(unsigned)exposure;
- (void)rumbleChanged:(double)amp;
- (void)linkCableBitStart:(bool)bit;
- (bool)linkCableBitEnd;
- (void)infraredStateChanged:(bool)state;

@end

/**
A GBCallbackBridge can be used to connect a GB_gameboy_t's callbacks to an Objective-C instance.

An instance of this class is typically owned by the object that conforms to the GBCallbackBridgeDelegate protocol, and
that same object is provided to this instance's init method as the delegate.
*/
__attribute__((objc_subclassing_restricted))
@interface GBCallbackBridge: NSObject

/** Immediately sets user data on gb to self and connects all implemented callbacks to the delegate. */
- (nonnull instancetype)initWithGameboy:(nonnull GB_gameboy_t *)gb
delegate:(nonnull id<GBCallbackBridgeDelegate>)delegate;
- (nonnull instancetype)init NS_UNAVAILABLE;

/** The delegate must implement both linkCableBitStart: and linkCableBitEnd or this method will assert. */
- (void)enableSerialCallbacks;

/**
The object that implements the relevant callbacks.

Setting this value after initialization is not presently supported.
*/
@property (nonatomic, nullable, weak, readonly) id<GBCallbackBridgeDelegate> delegate;

@end
Loading