cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

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