diff --git a/NesKit/NesLayer.m b/NesKit/NesLayer.m index e110c90..d5ec8e0 100644 --- a/NesKit/NesLayer.m +++ b/NesKit/NesLayer.m @@ -14,12 +14,16 @@ - (instancetype)initWithNesSystem:(NesSystem*)system { if(self = [super init]) { self.system = system; + self.contentsGravity = kCAGravityResizeAspect; + _timer = [NSTimer timerWithTimeInterval:1.0/60.0 repeats:YES block:^(NSTimer * _Nonnull timer) { if(self.system) { - self.contents = self.system.frame; + self.contents = (__bridge id _Nullable)(self.system.frame); [self.system stepToNextFrame]; } }]; + + [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode]; } return self; } diff --git a/NesKit/NesSystem.mm b/NesKit/NesSystem.mm index e47b82e..30cebef 100644 --- a/NesKit/NesSystem.mm +++ b/NesKit/NesSystem.mm @@ -28,24 +28,28 @@ size_t frameBufferSize = nes::Ppu::SCREEN_WIDTH * nes::Ppu::SCREEN_HEIGHT * sizeof(nes::Pixel); - _system->setNewFrameCallback([&](auto frameBuffer) { + _system->setNewFrameCallback([frameBufferSize, self](auto frameBuffer) { _runEmulation = NO; CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, (void*)frameBuffer, frameBufferSize, NULL); CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); - _frame = CGImageCreate(nes::Ppu::SCREEN_WIDTH, - nes::Ppu::SCREEN_HEIGHT, - 8, - sizeof(nes::Pixel)*8, - nes::Ppu::SCREEN_WIDTH * sizeof(nes::Pixel), - colorspace, - kCGBitmapByteOrderDefault, - dataProvider, - NULL, - true, - kCGRenderingIntentDefault); + CGImageRef image = CGImageCreate(nes::Ppu::SCREEN_WIDTH, + nes::Ppu::SCREEN_HEIGHT, + 8, + sizeof(nes::Pixel)*8, + nes::Ppu::SCREEN_WIDTH * sizeof(nes::Pixel), + colorspace, + kCGImageAlphaPremultipliedLast, + dataProvider, + NULL, + true, + kCGRenderingIntentDefault); CGDataProviderRelease(dataProvider); CGColorSpaceRelease(colorspace); + + dispatch_async(dispatch_get_main_queue(), ^{ + _frame = image; + }); }); } return self; @@ -54,16 +58,18 @@ - (void)runRom:(NSURL*)url { _system->insertCartridge([url fileSystemRepresentation]); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - [_condition lock]; - while (!_runEmulation) { - [_condition wait]; + while(TRUE) { + [_condition lock]; + while (!_runEmulation) { + [_condition wait]; + } + + while(_runEmulation) { + _system->tick(); + } + + [_condition unlock]; } - - while(_runEmulation) { - _system->tick(); - } - - [_condition unlock]; }); } diff --git a/NesKit/include/NesKit.h b/NesKit/include/NesKit.h index 2088bce..c31b429 100644 --- a/NesKit/include/NesKit.h +++ b/NesKit/include/NesKit.h @@ -9,5 +9,6 @@ #define NesKit_h #import "../NesSystem.h" +#import "../NesLayer.h" #endif /* Header_h */ diff --git a/examples/NesApp/NesApp/ViewController.swift b/examples/NesApp/NesApp/ViewController.swift index 4ae6ebc..0ac3816 100644 --- a/examples/NesApp/NesApp/ViewController.swift +++ b/examples/NesApp/NesApp/ViewController.swift @@ -8,14 +8,65 @@ import UIKit import NesKit +class NesView: UIView { + + let nesLayer: NesLayer + + init(system: NesSystem) { + self.nesLayer = NesLayer(nesSystem: system) + super.init(frame: .zero) + + self.layer.addSublayer(self.nesLayer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + self.nesLayer.frame = self.layer.frame + } +} + class ViewController: UIViewController { - var system = NesSystem() + let system = NesSystem() + lazy var nesView = NesView(system: system) override func viewDidLoad() { super.viewDidLoad() + + view.addSubview(nesView) + + nesView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + nesView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + nesView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + nesView.topAnchor.constraint(equalTo: view.topAnchor), + nesView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + self.openFilePicker() + } } - + func openFilePicker() { + let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.data]) + documentPicker.delegate = self + documentPicker.modalPresentationStyle = .overFullScreen + documentPicker.allowsMultipleSelection = false + present(documentPicker, animated: true) + } +} + +extension ViewController: UIDocumentPickerDelegate { + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { + print("Did pick document at: ", url) + system.runRom(url.path(percentEncoded: false)) + } } diff --git a/src/Cartridge.cpp b/src/Cartridge.cpp index 241adfe..d5e8577 100644 --- a/src/Cartridge.cpp +++ b/src/Cartridge.cpp @@ -28,6 +28,7 @@ namespace nes { _chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize); _mapper = std::make_unique(_header->prgChunks, _header->chrChunks); + _mirroring = _header->flags.mirroring == 0 ? Mirroring::Horizontal : Mirroring::Vertical; } uint8_t Cartridge::readPrg(uint16_t address) { @@ -41,7 +42,7 @@ namespace nes { } Cartridge::Mirroring Cartridge::mirroring() const { - return _header->flags.mirroring == 0 ? Mirroring::Horizontal : Mirroring::Vertical; + return _mirroring; } } diff --git a/src/Cartridge.h b/src/Cartridge.h index 7ef7e95..a6f4542 100644 --- a/src/Cartridge.h +++ b/src/Cartridge.h @@ -53,6 +53,9 @@ namespace nes { RomHeader* _header; std::span _prgRom; std::span _chrRom; + + private: + Mirroring _mirroring; }; }