Initial public commit

This commit is contained in:
Lior Halphon 2016-03-30 23:07:55 +03:00
commit f1e9623371
42 changed files with 7947 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

BIN
BootROMs/SameboyLogo.1bpp Normal file

Binary file not shown.

BIN
BootROMs/SameboyLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

1087
BootROMs/cgb_boot.asm Normal file

File diff suppressed because it is too large Load Diff

141
BootROMs/dmg_boot.asm Normal file
View File

@ -0,0 +1,141 @@
; Sameboy CGB bootstrap ROM
; Todo: use friendly names for HW registers instead of magic numbers
SECTION "BootCode", ROM0[$0]
Start:
; Init stack pointer
ld sp, $fffe
; Clear memory VRAM
ld hl, $8000
.clearVRAMLoop
ldi [hl], a
bit 5, h
jr z, .clearVRAMLoop
; Init Audio
ld a, $80
ldh [$26], a
ldh [$11], a
ld a, $f3
ldh [$12], a
ldh [$25], a
ld a, $77
ldh [$24], a
; Init BG palette
ld a, $fc
ldh [$47], a
; Load logo from ROM.
; A nibble represents a 4-pixels line, 2 bytes represent a 4x4 tile, scaled to 8x8.
; Tiles are ordered left to right, top to bottom.
ld de, $104 ; Logo start
ld hl, $8010 ; This is where we load the tiles in VRAM
.loadLogoLoop
ld a, [de] ; Read 2 rows
ld b, a
call DoubleBitsAndWriteRow
call DoubleBitsAndWriteRow
inc de
ld a, e
xor $34 ; End of logo
jr nz, .loadLogoLoop
; Load trademark symbol
ld de, TrademarkSymbol
ld c,$08
.loadTrademarkSymbolLoop:
ld a,[de]
inc de
ldi [hl],a
inc hl
dec c
jr nz, .loadTrademarkSymbolLoop
; Set up tilemap
ld a,$19 ; Trademark symbol
ld [$9910], a ; ... put in the superscript position
ld hl,$992f ; Bottom right corner of the logo
ld c,$c ; Tiles in a logo row
.tilemapLoop
dec a
jr z, .tilemapDone
ldd [hl], a
dec c
jr nz, .tilemapLoop
ld l,$0f ; Jump to top row
jr .tilemapLoop
.tilemapDone
; Turn on LCD
ld a, $91
ldh [$40], a
; Wait ~0.75 seconds
ld b, 45
call WaitBFrames
; Play first sound
ld a, $83
call PlaySound
ld b, 15
call WaitBFrames
; Play second sound
ld a, $c1
call PlaySound
; Wait ~2.5 seconds
ld b, 150
call WaitBFrames
; Boot the game
jp BootGame
DoubleBitsAndWriteRow:
; Double the most significant 4 bits, b is shifted by 4
ld a, 4
ld c, 0
.doubleCurrentBit
sla b
push af
rl c
pop af
rl c
dec a
jr nz, .doubleCurrentBit
ld a, c
; Write as two rows
ldi [hl], a
inc hl
ldi [hl], a
inc hl
ret
WaitFrame:
ldh a, [$44]
cp $90
jr nz, WaitFrame
ret
WaitBFrames:
call WaitFrame
dec b
jr nz, WaitBFrames
ret
PlaySound:
ldh [$13], a
ld a, $87
ldh [$14], a
ret
TrademarkSymbol:
db $3c,$42,$b9,$a5,$b9,$a5,$42,$3c
SECTION "BootGame", ROM0[$fc]
BootGame:
ld a, 1
ldh [$50], a

7
Cocoa/AppDelegate.h Normal file
View File

@ -0,0 +1,7 @@
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@end

17
Cocoa/AppDelegate.m Normal file
View File

@ -0,0 +1,17 @@
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
@end

BIN
Cocoa/AppIcon.icns Normal file

Binary file not shown.

11
Cocoa/AudioClient.h Normal file
View File

@ -0,0 +1,11 @@
#import <Foundation/Foundation.h>
@interface AudioClient : NSObject
@property (strong) void (^renderBlock)(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer);
@property (readonly) UInt32 rate;
@property (readonly, getter=isPlaying) bool playing;
-(void) start;
-(void) stop;
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer)) block
andSampleRate:(UInt32) rate;
@end

115
Cocoa/AudioClient.m Normal file
View File

@ -0,0 +1,115 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "AudioClient.h"
static OSStatus render(
AudioClient *self,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
// This is a mono tone generator so we only need the first buffer
const int channel = 0;
SInt16 *buffer = (SInt16 *)ioData->mBuffers[channel].mData;
self.renderBlock(self.rate, inNumberFrames, buffer);
return noErr;
}
@implementation AudioClient
{
AudioComponentInstance audioUnit;
}
-(id) initWithRendererBlock:(void (^)(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer)) block
andSampleRate:(UInt32) rate
{
if(!(self = [super init]))
{
return nil;
}
// Configure the search parameters to find the default playback output unit
// (called the kAudioUnitSubType_RemoteIO on iOS but
// kAudioUnitSubType_DefaultOutput on Mac OS X)
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;
// Get the default playback output unit
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
NSAssert(defaultOutput, @"Can't find default output");
// Create a new unit based on this that we'll use for output
OSErr err = AudioComponentInstanceNew(defaultOutput, &audioUnit);
NSAssert1(audioUnit, @"Error creating unit: %hd", err);
// Set our tone rendering function on the unit
AURenderCallbackStruct input;
input.inputProc = (void*)render;
input.inputProcRefCon = (__bridge void * _Nullable)(self);
err = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
NSAssert1(err == noErr, @"Error setting callback: %hd", err);
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = rate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
streamFormat.mBytesPerPacket = 2;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = 2;
streamFormat.mChannelsPerFrame = 1;
streamFormat.mBitsPerChannel = 2 * 8;
err = AudioUnitSetProperty (audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
NSAssert1(err == noErr, @"Error setting stream format: %hd", err);
err = AudioUnitInitialize(audioUnit);
NSAssert1(err == noErr, @"Error initializing unit: %hd", err);
self.renderBlock = block;
_rate = rate;
return self;
}
-(void) start
{
OSErr err = AudioOutputUnitStart(audioUnit);
NSAssert1(err == noErr, @"Error starting unit: %hd", err);
_playing = YES;
}
-(void) stop
{
AudioOutputUnitStop(audioUnit);
_playing = NO;
}
-(void) dealloc {
[self stop];
AudioUnitUninitialize(audioUnit);
AudioComponentInstanceDispose(audioUnit);
}
@end

BIN
Cocoa/Cartridge.icns Normal file

Binary file not shown.

BIN
Cocoa/ColorCartridge.icns Normal file

Binary file not shown.

12
Cocoa/Document.h Normal file
View File

@ -0,0 +1,12 @@
#import <Cocoa/Cocoa.h>
#include "GBView.h"
@interface Document : NSDocument <NSWindowDelegate>
@property (strong) IBOutlet GBView *view;
@property (strong) IBOutlet NSTextView *consoleOutput;
@property (strong) IBOutlet NSPanel *consoleWindow;
@property (strong) IBOutlet NSTextField *consoleInput;
@end

366
Cocoa/Document.m Normal file
View File

@ -0,0 +1,366 @@
#include <CoreAudio/CoreAudio.h>
#include "AudioClient.h"
#import "Document.h"
#include "gb.h"
@interface Document ()
{
/* NSTextViews freeze the entire app if they're modified too often and too quickly.
We use this bool to tune down the write speed. Let me know if there's a more
reasonable alternative to this. */
unsigned long pendingLogLines;
bool tooMuchLogs;
}
@property AudioClient *audioClient;
- (void) vblank;
- (void) log: (const char *) log withAttributes: (gb_log_attributes) attributes;
- (const char *) getDebuggerInput;
@end
static void vblank(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
[self vblank];
}
static void consoleLog(GB_gameboy_t *gb, const char *string, gb_log_attributes attributes)
{
Document *self = (__bridge Document *)(gb->user_data);
[self log:string withAttributes: attributes];
}
static char *consoleInput(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
return strdup([self getDebuggerInput]);
}
static uint32_t rgbEncode(GB_gameboy_t *gb, unsigned char r, unsigned char g, unsigned char b)
{
return (r << 24) | (g << 16) | (b << 8);
}
@implementation Document
{
GB_gameboy_t gb;
volatile bool running;
volatile bool stopping;
NSConditionLock *has_debugger_input;
NSMutableArray *debugger_input_queue;
bool is_inited;
}
- (instancetype)init {
self = [super init];
if (self) {
has_debugger_input = [[NSConditionLock alloc] initWithCondition:0];
debugger_input_queue = [[NSMutableArray alloc] init];
[self initCGB];
}
return self;
}
- (void) initDMG
{
gb_init(&gb);
gb_load_bios(&gb, [[[NSBundle mainBundle] pathForResource:@"dmg_boot" ofType:@"bin"] UTF8String]);
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_rgb_encode_callback(&gb, rgbEncode);
gb.user_data = (__bridge void *)(self);
}
- (void) initCGB
{
gb_init_cgb(&gb);
gb_load_bios(&gb, [[[NSBundle mainBundle] pathForResource:@"cgb_boot" ofType:@"bin"] UTF8String]);
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_rgb_encode_callback(&gb, rgbEncode);
gb.user_data = (__bridge void *)(self);
}
- (void) vblank
{
[self.view flip];
gb_set_pixels_output(&gb, self.view.pixels);
}
- (void) run
{
running = true;
gb_set_pixels_output(&gb, self.view.pixels);
self.view.gb = &gb;
gb_set_sample_rate(&gb, 96000);
self.audioClient = [[AudioClient alloc] initWithRendererBlock:^(UInt32 sampleRate, UInt32 nFrames, SInt16 *buffer) {
//apu_render(&gb, sampleRate, nFrames, buffer);
apu_copy_buffer(&gb, buffer, nFrames);
} andSampleRate:96000];
[self.audioClient start];
while (running) {
gb_run(&gb);
}
[self.audioClient stop];
gb_save_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
stopping = false;
}
- (void) start
{
if (running) return;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self run];
});
}
- (void) stop
{
if (!running) return;
if (gb.debug_stopped) {
gb.debug_stopped = false;
[self consoleInput:nil];
}
stopping = true;
running = false;
while (stopping);
}
- (IBAction)reset:(id)sender
{
bool was_cgb = gb.is_cgb;
[self stop];
gb_free(&gb);
is_inited = false;
if (([sender tag] == 0 && was_cgb) || [sender tag] == 2) {
[self initCGB];
}
else {
[self initDMG];
}
[self readFromFile:self.fileName ofType:@"gb"];
[self start];
}
- (IBAction)togglePause:(id)sender
{
if (running) {
[self stop];
}
else {
[self start];
}
}
- (void)dealloc
{
gb_free(&gb);
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
[super windowControllerDidLoadNib:aController];
self.consoleOutput.textContainerInset = NSMakeSize(4, 4);
[self.view becomeFirstResponder];
[self start];
}
+ (BOOL)autosavesInPlace {
return YES;
}
- (NSString *)windowNibName {
// Override returning the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
return @"Document";
}
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)type
{
if (is_inited++) {
return YES;
}
gb_load_rom(&gb, [fileName UTF8String]);
gb_load_battery(&gb, [[[fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
return YES;
}
- (void)close
{
[self stop];
[self.consoleWindow close];
[super close];
}
- (IBAction) interrupt:(id)sender
{
[self log:"^C\n"];
gb.debug_stopped = true;
[self.consoleInput becomeFirstResponder];
}
- (IBAction)mute:(id)sender
{
if (self.audioClient.isPlaying) {
[self.audioClient stop];
}
else {
[self.audioClient start];
}
}
- (IBAction)toggleBlend:(id)sender
{
self.view.shouldBlendFrameWithPrevious ^= YES;
}
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem
{
if([anItem action] == @selector(mute:)) {
[(NSMenuItem*)anItem setState:!self.audioClient.isPlaying];
}
if ([anItem action] == @selector(togglePause:)) {
[(NSMenuItem*)anItem setState:!running];
}
if ([anItem action] == @selector(reset:) && anItem.tag != 0) {
[(NSMenuItem*)anItem setState:(anItem.tag == 1 && !gb.is_cgb) || (anItem.tag == 2 && gb.is_cgb)];
}
if([anItem action] == @selector(toggleBlend:)) {
[(NSMenuItem*)anItem setState:self.view.shouldBlendFrameWithPrevious];
}
return [super validateUserInterfaceItem:anItem];
}
- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)newFrame
{
NSRect rect = window.contentView.frame;
int titlebarSize = window.contentView.superview.frame.size.height - rect.size.height;
int step = 160 / [[window screen] backingScaleFactor];
rect.size.width = floor(rect.size.width / step) * step + step;
rect.size.height = rect.size.width / 10 * 9 + titlebarSize;
if (rect.size.width > newFrame.size.width) {
rect.size.width = 160;
rect.size.height = 144 + titlebarSize;
}
else if (rect.size.height > newFrame.size.height) {
rect.size.width = 160;
rect.size.height = 144 + titlebarSize;
}
rect.origin = window.frame.origin;
rect.origin.y -= rect.size.height - window.frame.size.height;
return rect;
}
- (void) log: (const char *) string withAttributes: (gb_log_attributes) attributes
{
if (pendingLogLines > 128) {
/* The ROM causes so many errors in such a short time, and we can't handle it. */
tooMuchLogs = true;
return;
}
pendingLogLines++;
NSString *nsstring = @(string); // For ref-counting
dispatch_async(dispatch_get_main_queue(), ^{
NSFont *font = [NSFont userFixedPitchFontOfSize:12];
NSUnderlineStyle underline = NSUnderlineStyleNone;
if (attributes & GB_LOG_BOLD) {
font = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:NSBoldFontMask];
}
if (attributes & GB_LOG_UNDERLINE_MASK) {
underline = (attributes & GB_LOG_UNDERLINE_MASK) == GB_LOG_DASHED_UNDERLINE? NSUnderlinePatternDot | NSUnderlineStyleSingle : NSUnderlineStyleSingle;
}
NSMutableParagraphStyle *paragraph_style = [[NSMutableParagraphStyle alloc] init];
[paragraph_style setLineSpacing:2];
NSAttributedString *attributed =
[[NSAttributedString alloc] initWithString:nsstring
attributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: [NSColor whiteColor],
NSUnderlineStyleAttributeName: @(underline),
NSParagraphStyleAttributeName: paragraph_style}];
[self.consoleOutput.textStorage appendAttributedString:attributed];
if (pendingLogLines == 1) {
if (tooMuchLogs) {
tooMuchLogs = false;
[self log:"[...]\n"];
}
[self.consoleOutput scrollToEndOfDocument:nil];
[self.consoleWindow orderBack:nil];
}
pendingLogLines--;
});
}
- (IBAction)showConsoleWindow:(id)sender
{
[self.consoleWindow orderBack:nil];
}
- (IBAction)consoleInput:(NSTextField *)sender {
NSString *line = [sender stringValue];
if (!line) {
line = @"";
}
[self log:[line UTF8String]];
[self log:"\n"];
[has_debugger_input lock];
[debugger_input_queue addObject:line];
[has_debugger_input unlockWithCondition:1];
[sender setStringValue:@""];
}
- (const char *) getDebuggerInput
{
[self log:">"];
[has_debugger_input lockWhenCondition:1];
NSString *input = [debugger_input_queue firstObject];
[debugger_input_queue removeObjectAtIndex:0];
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
return [input UTF8String];
}
- (IBAction)saveState:(id)sender
{
bool was_running = running;
if (!gb.debug_stopped) {
[self stop];
}
gb_save_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]);
if (was_running) {
[self start];
}
}
- (IBAction)loadState:(id)sender
{
bool was_running = running;
if (!gb.debug_stopped) {
[self stop];
}
gb_load_state(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:[NSString stringWithFormat:@"s%ld", (long)[sender tag] ]] UTF8String]);
if (was_running) {
[self start];
}
}
- (IBAction)clearConsole:(id)sender
{
[self.consoleOutput setString:@""];
}
- (void)log:(const char *)log
{
[self log:log withAttributes:0];
}
@end

109
Cocoa/Document.xib Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="Document">
<connections>
<outlet property="consoleInput" destination="l22-S8-uji" id="Heu-am-YgB"/>
<outlet property="consoleOutput" destination="doS-dM-hnl" id="Gn5-ju-Wb0"/>
<outlet property="consoleWindow" destination="21F-Ah-yHX" id="eQ4-ug-LsT"/>
<outlet property="view" destination="uqf-pe-VAF" id="HMP-rf-Yqk"/>
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="133" y="235" width="160" height="144"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<value key="minSize" type="size" width="160" height="144"/>
<view key="contentView" id="gIp-Ho-8D9">
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<openGLView colorSize="5bit_RGB_8bit_Alpha" useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" id="uqf-pe-VAF" customClass="GBView">
<rect key="frame" x="0.0" y="0.0" width="160" height="144"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</openGLView>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="jvM-lC-fKb"/>
</connections>
<point key="canvasLocation" x="293" y="347"/>
</window>
<window title="Debug Console" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="21F-Ah-yHX" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES" utility="YES" HUD="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="272" y="172" width="320" height="240"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<value key="minSize" type="size" width="320" height="240"/>
<view key="contentView" id="dCP-E5-7Fi">
<rect key="frame" x="0.0" y="0.0" width="320" height="240"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="oTo-zx-o6N">
<rect key="frame" x="0.0" y="25" width="320" height="216"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="EQe-Ad-L7S">
<rect key="frame" x="0.0" y="0.0" width="320" height="216"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView editable="NO" drawsBackground="NO" importsGraphics="NO" richText="NO" baseWritingDirection="leftToRight" findStyle="bar" verticallyResizable="YES" allowsNonContiguousLayout="YES" id="doS-dM-hnl">
<rect key="frame" x="0.0" y="0.0" width="320" height="216"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.14901960784313725" green="0.14901960784313725" blue="0.14901960784313725" alpha="1" colorSpace="calibratedRGB"/>
<size key="minSize" width="320" height="216"/>
<size key="maxSize" width="463" height="10000000"/>
<color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<size key="minSize" width="320" height="216"/>
<size key="maxSize" width="463" height="10000000"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textView>
</subviews>
<color key="backgroundColor" red="0.16470588235294117" green="0.16470588235294117" blue="0.16470588235294117" alpha="1" colorSpace="calibratedRGB"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="3fZ-tl-Zi7">
<rect key="frame" x="-100" y="-100" width="87" height="18"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="cwi-6E-rbh">
<rect key="frame" x="304" y="0.0" width="16" height="216"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField focusRingType="none" verticalHuggingPriority="750" mirrorLayoutDirectionWhenInternationalizing="never" id="l22-S8-uji">
<rect key="frame" x="0.0" y="0.0" width="320" height="24"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" state="on" focusRingType="none" id="p3j-nS-44f">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" red="0.16470588235294117" green="0.16470588235294117" blue="0.16470588235294117" alpha="1" colorSpace="calibratedRGB"/>
<allowedInputSourceLocales>
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
</allowedInputSourceLocales>
</textFieldCell>
<connections>
<action selector="consoleInput:" target="-2" id="ylQ-vw-ARS"/>
</connections>
</textField>
<box verticalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" id="960-dL-7ZY">
<rect key="frame" x="0.0" y="23" width="320" height="5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
</subviews>
</view>
<point key="canvasLocation" x="348" y="-29"/>
</window>
</objects>
</document>

9
Cocoa/GBView.h Normal file
View File

@ -0,0 +1,9 @@
#import <Cocoa/Cocoa.h>
#include "gb.h"
@interface GBView : NSOpenGLView
- (void) flip;
- (uint32_t *) pixels;
@property GB_gameboy_t *gb;
@property BOOL shouldBlendFrameWithPrevious;
@end

163
Cocoa/GBView.m Normal file
View File

@ -0,0 +1,163 @@
#import <Carbon/Carbon.h>
#import <OpenGL/gl.h>
#import "GBView.h"
@implementation GBView
{
uint32_t *image_buffers[3];
unsigned char current_buffer;
}
- (void) _init
{
image_buffers[0] = malloc(160 * 144 * 4);
image_buffers[1] = malloc(160 * 144 * 4);
image_buffers[2] = malloc(160 * 144 * 4);
_shouldBlendFrameWithPrevious = 1;
}
- (unsigned char) numberOfBuffers
{
return _shouldBlendFrameWithPrevious? 3 : 2;
}
- (void)dealloc
{
free(image_buffers[0]);
free(image_buffers[1]);
free(image_buffers[2]);
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
if (!(self = [super initWithCoder:coder]))
{
return self;
}
[self _init];
return self;
}
- (instancetype)initWithFrame:(NSRect)frameRect
{
if (!(self = [super initWithFrame:frameRect]))
{
return self;
}
[self _init];
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
double scale = self.window.backingScaleFactor;
glRasterPos2d(-1, 1);
glPixelZoom(self.bounds.size.width / 160 * scale, self.bounds.size.height / -144 * scale);
glDrawPixels(160, 144, GL_ABGR_EXT, GL_UNSIGNED_BYTE, image_buffers[current_buffer]);
if (_shouldBlendFrameWithPrevious) {
glEnable(GL_BLEND);
glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA);
glBlendColor(1, 1, 1, 0.5);
glDrawPixels(160, 144, GL_ABGR_EXT, GL_UNSIGNED_BYTE, image_buffers[(current_buffer + 2) % self.numberOfBuffers]);
glDisable(GL_BLEND);
}
glFlush();
}
- (void) flip
{
current_buffer = (current_buffer + 1) % self.numberOfBuffers;
[self setNeedsDisplay:YES];
}
- (uint32_t *) pixels
{
return image_buffers[(current_buffer + 1) % self.numberOfBuffers];
}
-(void)keyDown:(NSEvent *)theEvent
{
unsigned short key = theEvent.keyCode;
switch (key) {
case kVK_RightArrow:
_gb->keys[0] = true;
break;
case kVK_LeftArrow:
_gb->keys[1] = true;
break;
case kVK_UpArrow:
_gb->keys[2] = true;
break;
case kVK_DownArrow:
_gb->keys[3] = true;
break;
case kVK_ANSI_X:
_gb->keys[4] = true;
break;
case kVK_ANSI_Z:
_gb->keys[5] = true;
break;
case kVK_Delete:
_gb->keys[6] = true;
break;
case kVK_Return:
_gb->keys[7] = true;
break;
case kVK_Space:
_gb->turbo = true;
break;
default:
[super keyDown:theEvent];
break;
}
}
-(void)keyUp:(NSEvent *)theEvent
{
unsigned short key = theEvent.keyCode;
switch (key) {
case kVK_RightArrow:
_gb->keys[0] = false;
break;
case kVK_LeftArrow:
_gb->keys[1] = false;
break;
case kVK_UpArrow:
_gb->keys[2] = false;
break;
case kVK_DownArrow:
_gb->keys[3] = false;
break;
case kVK_ANSI_X:
_gb->keys[4] = false;
break;
case kVK_ANSI_Z:
_gb->keys[5] = false;
break;
case kVK_Delete:
_gb->keys[6] = false;
break;
case kVK_Return:
_gb->keys[7] = false;
break;
case kVK_Space:
_gb->turbo = false;
break;
default:
[super keyUp:theEvent];
break;
}
}
-(void)reshape
{
double scale = self.window.backingScaleFactor;
glViewport(0, 0, self.bounds.size.width * scale, self.bounds.size.height * scale);
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
@end

71
Cocoa/Info.plist Normal file
View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>14F1509</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>gb</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>Cartridge</string>
<key>CFBundleTypeName</key>
<string>Gameboy Game</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>Document</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>gbc</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>ColorCartridge</string>
<key>CFBundleTypeName</key>
<string>Gameboy Color Game</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>Document</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>SameBoy</string>
<key>CFBundleIconFile</key>
<string>AppIcon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.github.LIJI32.SameBoy</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>SameBoy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>LSMinimumSystemVersion</key>
<string>10.9</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

405
Cocoa/MainMenu.xib Normal file
View File

@ -0,0 +1,405 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8191" systemVersion="14F27" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="SameBoy" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="SameBoy" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Gameboy Emulator" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Gameboy Emulator" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Gameboy Emulator" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="cGb-fc-V1Y">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="rwF-GI-mkw">
<items>
<menuItem title="Undo" keyEquivalent="z" id="0Ff-de-rjb">
<connections>
<action selector="undo:" target="-1" id="XQH-Wy-wlr"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="Pef-QL-e9D">
<connections>
<action selector="redo:" target="-1" id="5DQ-yl-4ds"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gYa-mS-zMS"/>
<menuItem title="Cut" keyEquivalent="x" id="c0j-SN-BK3">
<connections>
<action selector="cut:" target="-1" id="DCn-sI-ibs"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="kRM-zo-IsI">
<connections>
<action selector="copy:" target="-1" id="lgN-ca-tGx"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="tPP-KM-W2x">
<connections>
<action selector="paste:" target="-1" id="zLc-RU-lUk"/>
</connections>
</menuItem>
<menuItem title="Delete" id="CvF-7s-jyR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="zQk-RN-64A"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="tha-Q5-MNs">
<connections>
<action selector="selectAll:" target="-1" id="IfU-4s-7bE"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="Aru-vr-frG"/>
<menuItem title="Find" id="efg-jw-GVP">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="4R6-IU-Jq6">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="tos-1K-NFk">
<connections>
<action selector="performFindPanelAction:" target="-1" id="nTo-u6-2Ne"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="aey-0H-CqY">
<connections>
<action selector="performFindPanelAction:" target="-1" id="DJo-3G-DNV"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="Ex6-6J-WlY">
<connections>
<action selector="performFindPanelAction:" target="-1" id="6Zf-xR-ur5"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="qgQ-0P-lLO">
<connections>
<action selector="performFindPanelAction:" target="-1" id="l2m-8O-aDP"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="Ujj-LE-V19">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="GhX-po-5RK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Emulation" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Emulation" id="HyV-fh-RgO">
<items>
<menuItem title="Reset" keyEquivalent="r" id="p0i-Lt-sTg">
<connections>
<action selector="reset:" target="-1" id="DKW-Bd-h3v"/>
</connections>
</menuItem>
<menuItem title="Pause" keyEquivalent="p" id="4K4-hw-R7Q">
<connections>
<action selector="togglePause:" target="-1" id="osW-wt-QAa"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="QIS-av-Byy"/>
<menuItem title="Save State" id="Hdz-ut-okE">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Save State" id="Mxx-u1-M9D">
<items>
<menuItem title="Slot 1" tag="1" keyEquivalent="1" id="MKg-h9-jfo">
<connections>
<action selector="saveState:" target="-1" id="UZR-bP-ogO"/>
</connections>
</menuItem>
<menuItem title="Slot 2" tag="2" keyEquivalent="2" id="vkn-Zh-eJS">
<connections>
<action selector="saveState:" target="-1" id="Pmj-2O-z6U"/>
</connections>
</menuItem>
<menuItem title="Slot 3" tag="3" keyEquivalent="3" id="9mj-UU-bHY">
<connections>
<action selector="saveState:" target="-1" id="BhO-2h-gyQ"/>
</connections>
</menuItem>
<menuItem title="Slot 4" tag="4" keyEquivalent="4" id="NYY-aj-BHb">
<connections>
<action selector="saveState:" target="-1" id="xlY-3q-JsO"/>
</connections>
</menuItem>
<menuItem title="Slot 5" tag="5" keyEquivalent="5" id="UNN-Yv-1II">
<connections>
<action selector="saveState:" target="-1" id="Kbx-JS-3v5"/>
</connections>
</menuItem>
<menuItem title="Slot 6" tag="6" keyEquivalent="6" id="Io5-NV-GN5">
<connections>
<action selector="saveState:" target="-1" id="SAo-ej-RBG"/>
</connections>
</menuItem>
<menuItem title="Slot 7" tag="7" keyEquivalent="7" id="en2-Uu-Eps">
<connections>
<action selector="saveState:" target="-1" id="MRR-4I-z8l"/>
</connections>
</menuItem>
<menuItem title="Slot 8" tag="8" keyEquivalent="8" id="BHl-sg-rA2">
<connections>
<action selector="saveState:" target="-1" id="WSz-gz-mlZ"/>
</connections>
</menuItem>
<menuItem title="Slot 9" tag="9" keyEquivalent="9" id="vSH-S9-ExZ">
<connections>
<action selector="saveState:" target="-1" id="FOt-UK-jT9"/>
</connections>
</menuItem>
<menuItem title="Slot 10" tag="10" keyEquivalent="0" id="mAB-fq-BJy">
<connections>
<action selector="saveState:" target="-1" id="KQi-wO-F6M"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Load State" id="EXD-SL-cz4">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Load State" id="l9D-Ej-sh2">
<items>
<menuItem title="Slot 1" tag="1" keyEquivalent="1" id="aEJ-6V-7sk">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="rOy-Ve-UUM"/>
</connections>
</menuItem>
<menuItem title="Slot 2" tag="2" keyEquivalent="2" id="EWM-vK-sZm">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="M7f-wx-xt2"/>
</connections>
</menuItem>
<menuItem title="Slot 3" tag="3" keyEquivalent="3" id="YEd-gG-G6p">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="ALD-3X-pJ6"/>
</connections>
</menuItem>
<menuItem title="Slot 4" tag="4" keyEquivalent="4" id="Xgn-pa-LcM">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="I0n-4q-CmW"/>
</connections>
</menuItem>
<menuItem title="Slot 5" tag="5" keyEquivalent="5" id="XIA-qE-emo">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="SAP-0t-CGM"/>
</connections>
</menuItem>
<menuItem title="Slot 6" tag="6" keyEquivalent="6" id="0CQ-w6-dSd">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="CFz-7P-jTJ"/>
</connections>
</menuItem>
<menuItem title="Slot 7" tag="7" keyEquivalent="7" id="sdG-Dc-QNU">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="B49-vL-qN7"/>
</connections>
</menuItem>
<menuItem title="Slot 8" tag="8" keyEquivalent="8" id="pPH-D9-4MJ">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="TZl-ug-0ae"/>
</connections>
</menuItem>
<menuItem title="Slot 9" tag="9" keyEquivalent="9" id="1Uy-yl-ITg">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="Hk5-Pz-VC5"/>
</connections>
</menuItem>
<menuItem title="Slot 10" tag="10" keyEquivalent="0" id="dpk-UF-vN2">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="loadState:" target="-1" id="GEt-4l-90e"/>
</connections>
</menuItem>
</items>
</menu>
<connections>
<action selector="loadState:" target="-1" id="WyW-n5-YHf"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5GS-tt-E0a"/>
<menuItem title="Gameboy" tag="1" id="vc7-yy-ARW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="E4M-QG-ua9"/>
</connections>
</menuItem>
<menuItem title="Gameboy Color" tag="2" id="hdG-Bl-8nJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="reset:" target="-1" id="xAz-cr-0u2"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="DPb-Sh-5tg"/>
<menuItem title="Mute Sound" keyEquivalent="m" id="1UK-8n-QPP">
<connections>
<action selector="mute:" target="-1" id="YE5-mi-Yzd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="YIZ-pz-N4V"/>
<menuItem title="Blend Frames" id="AWj-r8-L6U">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleBlend:" target="-1" id="TjO-ce-UxL"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Developer" id="IwX-DJ-dBk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Developer" id="UVb-cc-at0">
<items>
<menuItem title="Show Console" id="Wse-UY-Y9l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="showConsoleWindow:" target="-1" id="mFf-4i-jLG"/>
</connections>
</menuItem>
<menuItem title="Clear Console" keyEquivalent="k" id="MyO-VS-MKZ">
<connections>
<action selector="clearConsole:" target="-1" id="1UW-8J-Uwl"/>
</connections>
</menuItem>
<menuItem title="Break Debugger" keyEquivalent="c" id="uBD-GY-Doi">
<modifierMask key="keyEquivalentModifierMask" control="YES"/>
<connections>
<action selector="interrupt:" target="-1" id="ZmB-wG-fTs"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Gameboy Emulator Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</objects>
</document>

1
Cocoa/PkgInfo Normal file
View File

@ -0,0 +1 @@
APPL????

5
Cocoa/main.m Normal file
View File

@ -0,0 +1,5 @@
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}

415
Core/apu.c Normal file
View File

@ -0,0 +1,415 @@
#include <stdint.h>
#include <math.h>
#include <string.h>
#include "apu.h"
#include "gb.h"
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
static __attribute__((unused)) int16_t generate_sin(double phase, int16_t amplitude)
{
return (int16_t)(sin(phase) * amplitude);
}
static int16_t generate_square(double phase, int16_t amplitude, double duty)
{
if (fmod(phase, 2 * M_PI) > duty * 2 * M_PI) {
return amplitude;
}
return 0;