Emulator.mm (8829B)
1/* 2 * Gearboy - Nintendo Game Boy Emulator 3 * Copyright (C) 2012 Ignacio Sanchez 4 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * any later version. 9 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see http://www.gnu.org/licenses/ 17 * 18 */ 19 20#import <GLKit/GLKit.h> 21#import "Emulator.h" 22#include <SDL2/SDL.h> 23#include "inputmanager.h" 24 25const float kMixFrameAlpha = 0.35f; 26const float kGB_Width = 160.0f; 27const float kGB_Height = 144.0f; 28const float kGB_TexWidth = kGB_Width / 256.0f; 29const float kGB_TexHeight = kGB_Height / 256.0f; 30const GLfloat box[] = {0.0f, kGB_Height, 1.0f, kGB_Width,kGB_Height, 1.0f, 0.0f, 0.0f, 1.0f, kGB_Width, 0.0f, 1.0f}; 31const GLfloat tex[] = {0.0f, kGB_TexHeight, kGB_TexWidth, kGB_TexHeight, 0.0f, 0.0f, kGB_TexWidth, 0.0f}; 32 33@implementation Emulator 34 35@synthesize multiplier, retina, iPad; 36 37-(id)init 38{ 39 if (self = [super init]) 40 { 41 firstFrame = YES; 42 saveStatePending = NO; 43 loadStatePending = NO; 44 45#ifdef __APPLE__ 46#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 47 SDL_SetMainReady(); 48#endif 49#endif 50 51 theGearboyCore = new GearboyCore(); 52 theGearboyCore->Init(); 53 54 theSoundQueue = new Sound_Queue(); 55 theSoundQueue->start(44100, 2); 56 57 _audioEnabled = YES; 58 59 GB_Color color1; 60 GB_Color color2; 61 GB_Color color3; 62 GB_Color color4; 63 64 color1.red = 0x87; 65 color1.green = 0x96; 66 color1.blue = 0x03; 67 color2.red = 0x4d; 68 color2.green = 0x6b; 69 color2.blue = 0x03; 70 color3.red = 0x2b; 71 color3.green = 0x55; 72 color3.blue = 0x03; 73 color4.red = 0x14; 74 color4.green = 0x44; 75 color4.blue = 0x03; 76 77 theGearboyCore->SetDMGPalette(color1, color2, color3, color4); 78 79 theInput = new EmulatorInput(self); 80 theInput->Init(); 81 theFrameBuffer = new u16[GAMEBOY_WIDTH * GAMEBOY_HEIGHT]; 82 theTexture = new u16[256 * 256]; 83 84 for (int i = 0; i < (GAMEBOY_WIDTH * GAMEBOY_HEIGHT); ++i) 85 { 86 theFrameBuffer[i] = 0; 87 } 88 89 for (int i = 0; i < (256 * 256); ++i) 90 { 91 theTexture[i] = 0; 92 } 93 } 94 return self; 95} 96 97-(void)dealloc 98{ 99 theGearboyCore->SaveRam(); 100 SafeDeleteArray(theTexture); 101 SafeDeleteArray(theFrameBuffer); 102 SafeDelete(theSoundQueue); 103 SafeDelete(theGearboyCore); 104 [self shutdownGL]; 105} 106 107-(void)shutdownGL 108{ 109 TextureManager::Instance().UnloadAll(); 110 glDeleteTextures(1, &accumulationTexture); 111 glDeleteTextures(1, &GBTexture); 112 glDeleteFramebuffers(1, &accumulationFramebuffer); 113} 114 115-(void)update 116{ 117 if (saveStatePending) 118 { 119 saveStatePending = NO; 120 theGearboyCore->SaveState(1); 121 } 122 else if (loadStatePending) 123 { 124 loadStatePending = NO; 125 theGearboyCore->LoadState(1); 126 } 127 128 InputManager::Instance().Update(); 129 130 int sampleCount = 0; 131 132 theGearboyCore->RunToVBlank(theFrameBuffer, theSampleBufffer, &sampleCount); 133 134 if (_audioEnabled && (sampleCount > 0)) 135 { 136 theSoundQueue->write(theSampleBufffer, sampleCount); 137 } 138 139 for (int y = 0; y < GAMEBOY_HEIGHT; ++y) 140 { 141 int y_256 = y * 256; 142 int y_gb_width = y * GAMEBOY_WIDTH; 143 for (int x = 0; x < GAMEBOY_WIDTH; ++x) 144 { 145 theTexture[y_256 + x] = theFrameBuffer[y_gb_width + x]; 146 } 147 } 148} 149 150-(void)draw 151{ 152 if ((retina || iPad) && !theGearboyCore->GetCartridge()->IsCGB()) 153 { 154 [self renderMixFrames]; 155 } 156 else 157 { 158 [self renderFrame]; 159 } 160} 161 162-(void)initGL 163{ 164 glGenFramebuffers(1, &accumulationFramebuffer); 165 glGenTextures(1, &accumulationTexture); 166 glGenTextures(1, &GBTexture); 167 168 glEnableClientState(GL_VERTEX_ARRAY); 169 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 170 171 glVertexPointer(3, GL_FLOAT, 0, box); 172 glTexCoordPointer(2, GL_FLOAT, 0, tex); 173 174 glClearColor(0.0, 0.0, 0.0, 0.0); 175 176 glEnable(GL_TEXTURE_2D); 177 glBindTexture(GL_TEXTURE_2D, GBTexture); 178 [self setupTextureWithData: (GLvoid*) theTexture]; 179 180 glBindFramebuffer(GL_FRAMEBUFFER, accumulationFramebuffer); 181 glBindTexture(GL_TEXTURE_2D, accumulationTexture); 182 [self setupTextureWithData: NULL]; 183 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, accumulationTexture, 0); 184 185 dotMatrixDMGTexture = TextureManager::Instance().GetTexture("/scanlines_dmg_2x"); 186 dotMatrixCGBTexture = TextureManager::Instance().GetTexture("/scanlines_gbc_2x"); 187} 188 189-(void)renderFrame 190{ 191 glBindTexture(GL_TEXTURE_2D, GBTexture); 192 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (GLvoid*) theTexture); 193 [self renderQuadWithViewportWidth:(80 * multiplier) andHeight:(72 * multiplier) andMirrorY:NO]; 194 195 [self renderDotMatrix]; 196} 197 198-(void)renderMixFrames 199{ 200 glBindFramebuffer(GL_FRAMEBUFFER, accumulationFramebuffer); 201 glBindTexture(GL_TEXTURE_2D, GBTexture); 202 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, (GLvoid*) theTexture); 203 204 float alpha = kMixFrameAlpha; 205 if (firstFrame) 206 { 207 firstFrame = NO; 208 alpha = 1.0f; 209 } 210 211 static bool round_error = false; 212 float round_color = 1.0f - (round_error ? 0.02f : 0.0f); 213 round_error = !round_error; 214 glEnable(GL_BLEND); 215 glColor4f(round_color, round_color, round_color, alpha); 216 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 217 [self renderQuadWithViewportWidth:GAMEBOY_WIDTH andHeight:GAMEBOY_HEIGHT andMirrorY:NO]; 218 glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 219 glDisable(GL_BLEND); 220 221 [self.glview bindDrawable]; 222 223 glClear(GL_COLOR_BUFFER_BIT); 224 glBindTexture(GL_TEXTURE_2D, accumulationTexture); 225 [self renderQuadWithViewportWidth:(80 * multiplier) andHeight:(72 * multiplier) andMirrorY:YES]; 226 227 [self renderDotMatrix]; 228} 229 230-(void)renderDotMatrix 231{ 232 if (IsValidPointer(dotMatrixDMGTexture) && IsValidPointer(dotMatrixCGBTexture)) 233 { 234 glBindTexture(GL_TEXTURE_2D, 235 theGearboyCore->GetCartridge()->IsCGB() ? dotMatrixCGBTexture->GetID() : dotMatrixDMGTexture->GetID()); 236 glEnable(GL_BLEND); 237 if (theGearboyCore->GetCartridge()->IsCGB()) 238 glColor4f(1.0f, 1.0f, 1.0f, 0.15f); 239 else 240 glColor4f(1.0f, 1.0f, 1.0f, 0.30f); 241 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 242 [self renderQuadWithViewportWidth:(80 * multiplier) andHeight:(72 * multiplier) andMirrorY:NO]; 243 glColor4f(1.0f, 1.0f, 1.0f, 1.0f); 244 glDisable(GL_BLEND); 245 } 246} 247 248-(void)setupTextureWithData: (GLvoid*) data 249{ 250 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); 251 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 252 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 253} 254 255-(void)renderQuadWithViewportWidth: (float)viewportWidth andHeight: (float)viewportHeight andMirrorY: (BOOL) mirrorY 256{ 257 glMatrixMode(GL_PROJECTION); 258 glLoadIdentity(); 259 if (mirrorY) 260 glOrthof(0.0f, kGB_Width, 0.0f, kGB_Height, -1.0f, 1.0f); 261 else 262 glOrthof(0.0f, kGB_Width, kGB_Height, 0.0f, -1.0f, 1.0f); 263 glMatrixMode(GL_MODELVIEW); 264 glLoadIdentity(); 265 glViewport(0, 0, viewportWidth, viewportHeight); 266 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 267} 268 269-(void)loadRomWithPath: (NSString *)filePath 270{ 271 theGearboyCore->SaveRam(); 272 theGearboyCore->LoadROM([filePath UTF8String], false); 273 theGearboyCore->LoadRam(); 274 firstFrame = YES; 275} 276 277-(void)keyPressed: (Gameboy_Keys)key 278{ 279 theGearboyCore->KeyPressed(key); 280} 281 282-(void)keyReleased: (Gameboy_Keys)key 283{ 284 theGearboyCore->KeyReleased(key); 285} 286 287-(void)pause 288{ 289 theGearboyCore->Pause(true); 290 _audioEnabled = NO; 291} 292 293-(void)resume 294{ 295 theGearboyCore->Pause(false); 296 _audioEnabled = YES; 297} 298 299-(BOOL)paused 300{ 301 return theGearboyCore->IsPaused(); 302} 303 304-(void)reset 305{ 306 theGearboyCore->SaveRam(); 307 theGearboyCore->ResetROM(false); 308 theGearboyCore->LoadRam(); 309 firstFrame = YES; 310} 311 312-(void)save 313{ 314 theGearboyCore->SaveRam(); 315} 316 317- (void)setAudioEnabled: (BOOL)enabled 318{ 319 _audioEnabled = enabled; 320} 321 322- (void)resetAudio 323{ 324 theSoundQueue->stop(); 325 theSoundQueue->start(44100, 2); 326} 327 328- (void)saveState 329{ 330 saveStatePending = YES; 331} 332 333- (void)loadState 334{ 335 loadStatePending = YES; 336} 337 338@end