mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Onward with carrying this through to the bitter end, this at least results in all the appropriate knowledge and call-ins occuring at the CRT.
This commit is contained in:
parent
09e11469c3
commit
e0d51408e4
@ -25,6 +25,14 @@ Machine::Machine() :
|
||||
_tiaInputValue{0xff, 0xff}
|
||||
{
|
||||
_crt = new Outputs::CRT(228, Outputs::CRT::DisplayType::NTSC60, 1, 2);
|
||||
_crt->set_composite_sampling_function(
|
||||
"float sample(vec2 coordinate, float phase)\n"
|
||||
"{\n"
|
||||
"vec2 c = texture(texID, coordinate).rg;"
|
||||
"float y = 0.1 + c.x * 0.91071428571429;\n"
|
||||
"float aOffset = 6.283185308 * (c.y - 3.0 / 16.0) * 1.14285714285714;\n"
|
||||
"return y + step(0.03125, c.y) * 0.1 * cos(phase - aOffset);\n"
|
||||
"}");
|
||||
memset(_collisions, 0xff, sizeof(_collisions));
|
||||
set_reset_line(true);
|
||||
}
|
||||
@ -147,19 +155,6 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset)
|
||||
pixel[1] = outputColour&0xf0;
|
||||
}
|
||||
|
||||
const char *Machine::get_signal_decoder()
|
||||
{
|
||||
return
|
||||
"float sample(vec2 coordinate, float phase)\n"
|
||||
"{\n"
|
||||
"vec2 c = texture(texID, coordinate).rg;"
|
||||
"float y = 0.1 + c.x * 0.91071428571429;\n"
|
||||
"float aOffset = 6.283185308 * (c.y - 3.0 / 16.0) * 1.14285714285714;\n"
|
||||
"return y + step(0.03125, c.y) * 0.1 * cos(phase - aOffset);\n"
|
||||
"}";
|
||||
}
|
||||
|
||||
|
||||
// in imputing the knowledge that all we're dealing with is the rollover from 159 to 0,
|
||||
// this is faster than the straightforward +1)%160 per profiling
|
||||
#define increment_object_counter(c) _objectCounter[c] = (_objectCounter[c]+1)&~((158-_objectCounter[c]) >> 8)
|
||||
|
@ -10,7 +10,7 @@
|
||||
#define Atari2600_cpp
|
||||
|
||||
#include "../../Processors/6502/CPU6502.hpp"
|
||||
#include "../../Outputs/CRT.hpp"
|
||||
#include "../../Outputs/CRT/CRT.hpp"
|
||||
#include <stdint.h>
|
||||
#include "Atari2600Inputs.h"
|
||||
|
||||
@ -19,7 +19,6 @@ namespace Atari2600 {
|
||||
class Machine: public CPU6502::Processor<Machine> {
|
||||
|
||||
public:
|
||||
|
||||
Machine();
|
||||
~Machine();
|
||||
|
||||
@ -32,8 +31,6 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
|
||||
Outputs::CRT *get_crt() { return _crt; }
|
||||
|
||||
const char *get_signal_decoder();
|
||||
|
||||
private:
|
||||
uint8_t *_rom, *_romPages[4], _ram[128];
|
||||
size_t _rom_size;
|
||||
|
@ -29,6 +29,13 @@ Machine::Machine() :
|
||||
_currentOutputLine(0),
|
||||
_crt(Outputs::CRT(crt_cycles_per_line, Outputs::CRT::DisplayType::PAL50, 1, 1))
|
||||
{
|
||||
_crt.set_rgb_sampling_function(
|
||||
"vec4 sample(vec2 coordinate)\n"
|
||||
"{\n"
|
||||
"float texValue = texture(texID, coordinate).r;\n"
|
||||
"return vec4( step(4.0/256.0, mod(texValue, 8.0/256.0)), step(2.0/256.0, mod(texValue, 4.0/256.0)), step(1.0/256.0, mod(texValue, 2.0/256.0)), 1.0);\n"
|
||||
"}");
|
||||
|
||||
memset(_keyStates, 0, sizeof(_keyStates));
|
||||
memset(_palette, 0xf, sizeof(_palette));
|
||||
_interruptStatus = 0x02;
|
||||
@ -455,7 +462,7 @@ inline void Machine::update_display()
|
||||
}
|
||||
|
||||
|
||||
int pixels_to_output = std::min(_frameCycles - _displayOutputPosition, 104 - line_position);
|
||||
int pixels_to_output = std::min(104 - line_position, cycles_left);
|
||||
_displayOutputPosition += pixels_to_output;
|
||||
// printf("<- %d ->", pixels_to_output);
|
||||
if(_screenMode >= 4)
|
||||
@ -528,7 +535,7 @@ inline void Machine::update_display()
|
||||
|
||||
if(line_position >= 104)
|
||||
{
|
||||
int pixels_to_output = std::min(_frameCycles - _displayOutputPosition, 128 - line_position);
|
||||
int pixels_to_output = std::min(128 - line_position, cycles_left);
|
||||
_crt.output_blank((unsigned int)pixels_to_output * crt_cycles_multiplier);
|
||||
_displayOutputPosition += pixels_to_output;
|
||||
|
||||
@ -556,16 +563,6 @@ inline void Machine::update_display()
|
||||
}
|
||||
}
|
||||
|
||||
const char *Machine::get_signal_decoder()
|
||||
{
|
||||
return
|
||||
"vec4 sample(vec2 coordinate)\n"
|
||||
"{\n"
|
||||
"float texValue = texture(texID, coordinate).r;\n"
|
||||
"return vec4( step(4.0/256.0, mod(texValue, 8.0/256.0)), step(2.0/256.0, mod(texValue, 4.0/256.0)), step(1.0/256.0, mod(texValue, 2.0/256.0)), 1.0);\n"
|
||||
"}";
|
||||
}
|
||||
|
||||
void Machine::set_key_state(Key key, bool isPressed)
|
||||
{
|
||||
if(key == KeyBreak)
|
||||
|
@ -10,7 +10,7 @@
|
||||
#define Electron_hpp
|
||||
|
||||
#include "../../Processors/6502/CPU6502.hpp"
|
||||
#include "../../Outputs/CRT.hpp"
|
||||
#include "../../Outputs/CRT/CRT.hpp"
|
||||
#include "../../Outputs/Speaker.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include <stdint.h>
|
||||
@ -148,7 +148,6 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
|
||||
|
||||
Outputs::CRT *get_crt() { return &_crt; }
|
||||
Outputs::Speaker *get_speaker() { return &_speaker; }
|
||||
const char *get_signal_decoder();
|
||||
|
||||
virtual void tape_did_change_interrupt_status(Tape *tape);
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
|
||||
4B0CCC471C62D1A8001CAC5F /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */; };
|
||||
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */; };
|
||||
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
|
||||
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
|
||||
@ -17,7 +19,6 @@
|
||||
4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; };
|
||||
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; };
|
||||
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; };
|
||||
4B366DFC1B5C165A0026627B /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B366DFA1B5C165A0026627B /* CRT.cpp */; };
|
||||
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE4A1C3B3B0C0093A61B /* CSAtari2600.mm */; };
|
||||
4B55CE4E1C3B3BDA0093A61B /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE4D1C3B3BDA0093A61B /* CSMachine.mm */; };
|
||||
4B55CE541C3B7ABF0093A61B /* CSElectron.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE531C3B7ABF0093A61B /* CSElectron.mm */; };
|
||||
@ -28,6 +29,7 @@
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
|
||||
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; };
|
||||
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
|
||||
4B7BFEFE1C6446EF00089C1C /* CRTFrameBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */; };
|
||||
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; };
|
||||
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
|
||||
4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
|
||||
@ -325,6 +327,10 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
|
||||
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
|
||||
4B0CCC441C62D0B3001CAC5F /* CRTFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRTFrame.h; sourceTree = "<group>"; };
|
||||
4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = "<group>"; };
|
||||
4B0EBFB61C487F2F00A11F35 /* AudioQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioQueue.h; sourceTree = "<group>"; };
|
||||
4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioQueue.m; sourceTree = "<group>"; };
|
||||
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClockSignal-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
@ -337,15 +343,12 @@
|
||||
4B2409531C45AB05004DA684 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = ../../Outputs/Speaker.cpp; sourceTree = "<group>"; };
|
||||
4B2409541C45AB05004DA684 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = ../../Outputs/Speaker.hpp; sourceTree = "<group>"; };
|
||||
4B24095A1C45DF85004DA684 /* Stepper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Stepper.hpp; sourceTree = "<group>"; };
|
||||
4B2632551B631A510082A461 /* CRTFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CRTFrame.h; path = ../../Outputs/CRTFrame.h; sourceTree = "<group>"; };
|
||||
4B2E2D941C399D1200138695 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ElectronDocument.xib; sourceTree = "<group>"; };
|
||||
4B2E2D971C3A06EC00138695 /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = "<group>"; };
|
||||
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Atari2600.hpp; sourceTree = "<group>"; };
|
||||
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = "<group>"; };
|
||||
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = "<group>"; };
|
||||
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = "<group>"; };
|
||||
4B366DFA1B5C165A0026627B /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CRT.cpp; path = ../../Outputs/CRT.cpp; sourceTree = "<group>"; };
|
||||
4B366DFB1B5C165A0026627B /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRT.hpp; path = ../../Outputs/CRT.hpp; sourceTree = "<group>"; };
|
||||
4B55CE491C3B3B0C0093A61B /* CSAtari2600.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSAtari2600.h; sourceTree = "<group>"; };
|
||||
4B55CE4A1C3B3B0C0093A61B /* CSAtari2600.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSAtari2600.mm; sourceTree = "<group>"; };
|
||||
4B55CE4C1C3B3BDA0093A61B /* CSMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSMachine.h; sourceTree = "<group>"; };
|
||||
@ -363,6 +366,7 @@
|
||||
4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapeUEF.cpp; sourceTree = "<group>"; };
|
||||
4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapeUEF.hpp; sourceTree = "<group>"; };
|
||||
4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTFrameBuilder.cpp; sourceTree = "<group>"; };
|
||||
4B92EAC91B7C112B00246143 /* TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingTests.swift; sourceTree = "<group>"; };
|
||||
4BAE587D1C447B7A005B9AF0 /* KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyCodes.h; sourceTree = "<group>"; };
|
||||
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
@ -678,6 +682,19 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
4B0CCC411C62D0B3001CAC5F /* CRT */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */,
|
||||
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */,
|
||||
4B0CCC441C62D0B3001CAC5F /* CRTFrame.h */,
|
||||
4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */,
|
||||
4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */,
|
||||
);
|
||||
name = CRT;
|
||||
path = ../../Outputs/CRT;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B1414561B58879D00E04248 /* 6502 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -730,9 +747,7 @@
|
||||
4B366DFD1B5C165F0026627B /* Outputs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B366DFA1B5C165A0026627B /* CRT.cpp */,
|
||||
4B366DFB1B5C165A0026627B /* CRT.hpp */,
|
||||
4B2632551B631A510082A461 /* CRTFrame.h */,
|
||||
4B0CCC411C62D0B3001CAC5F /* CRT */,
|
||||
4B2409531C45AB05004DA684 /* Speaker.cpp */,
|
||||
4B2409541C45AB05004DA684 /* Speaker.hpp */,
|
||||
);
|
||||
@ -1588,10 +1603,13 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4B55CE541C3B7ABF0093A61B /* CSElectron.mm in Sources */,
|
||||
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
|
||||
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
|
||||
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */,
|
||||
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */,
|
||||
4B0CCC471C62D1A8001CAC5F /* CRTOpenGL.cpp in Sources */,
|
||||
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */,
|
||||
4B7BFEFE1C6446EF00089C1C /* CRTFrameBuilder.cpp in Sources */,
|
||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
|
||||
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
|
||||
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
|
||||
@ -1599,7 +1617,6 @@
|
||||
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
|
||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
|
||||
4B55CE5D1C3B7D6F0093A61B /* CSCathodeRayView.m in Sources */,
|
||||
4B366DFC1B5C165A0026627B /* CRT.cpp in Sources */,
|
||||
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
|
||||
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
|
||||
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */,
|
||||
|
@ -19,7 +19,7 @@ class Atari2600Document: MachineDocument {
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
atari2600.view = openGLView
|
||||
openGLView.frameBounds = CGRectMake(0.1, 0.1, 0.8, 0.8)
|
||||
// openGLView.frameBounds = CGRectMake(0.1, 0.1, 0.8, 0.8)
|
||||
}
|
||||
|
||||
override class func autosavesInPlace() -> Bool {
|
||||
|
@ -61,6 +61,10 @@ class ElectronDocument: MachineDocument {
|
||||
electron.runForNumberOfCycles(numberOfCycles)
|
||||
}
|
||||
|
||||
override func openGLViewDrawView(view: CSCathodeRayView) {
|
||||
electron.drawViewForPixelSize(view.backingSize)
|
||||
}
|
||||
|
||||
// MARK: CSOpenGLViewResponderDelegate
|
||||
override func keyDown(event: NSEvent) {
|
||||
electron.setKey(event.keyCode, isPressed: true)
|
||||
|
@ -46,6 +46,7 @@ class MachineDocument: NSDocument, CSCathodeRayViewDelegate, CSCathodeRayViewRes
|
||||
lastCycleCount = cycleCount
|
||||
}
|
||||
|
||||
func openGLViewDrawView(view: CSCathodeRayView) {}
|
||||
func runForNumberOfCycles(numberOfCycles: Int32) {}
|
||||
|
||||
// MARK: CSOpenGLViewResponderDelegate
|
||||
|
@ -7,13 +7,13 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CRTFrame.h"
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@class CSCathodeRayView;
|
||||
|
||||
@protocol CSCathodeRayViewDelegate
|
||||
- (void)openGLView:(nonnull CSCathodeRayView *)view didUpdateToTime:(CVTimeStamp)time;
|
||||
- (void)openGLViewDrawView:(nonnull CSCathodeRayView *)view;
|
||||
@end
|
||||
|
||||
@protocol CSCathodeRayViewResponderDelegate <NSObject>
|
||||
@ -22,10 +22,6 @@
|
||||
- (void)flagsChanged:(nonnull NSEvent *)newModifiers;
|
||||
@end
|
||||
|
||||
typedef NS_ENUM(NSInteger, CSCathodeRayViewSignalType) {
|
||||
CSCathodeRayViewSignalTypeNTSC,
|
||||
CSCathodeRayViewSignalTypeRGB
|
||||
};
|
||||
|
||||
@interface CSCathodeRayView : NSOpenGLView
|
||||
|
||||
@ -34,11 +30,6 @@ typedef NS_ENUM(NSInteger, CSCathodeRayViewSignalType) {
|
||||
|
||||
- (void)invalidate;
|
||||
|
||||
- (BOOL)pushFrame:(nonnull CRTFrame *)crtFrame;
|
||||
- (void)setSignalDecoder:(nonnull NSString *)decoder type:(CSCathodeRayViewSignalType)type;
|
||||
|
||||
// these are relative to a [0, 1] range in both width and height;
|
||||
// default is .origin = (0, 0), .size = (1, 1)
|
||||
@property (nonatomic, assign) CGRect frameBounds;
|
||||
@property (nonatomic, readonly) CGSize backingSize;
|
||||
|
||||
@end
|
||||
|
@ -10,38 +10,16 @@
|
||||
@import CoreVideo;
|
||||
@import GLKit;
|
||||
#import <OpenGL/gl3.h>
|
||||
#import <OpenGL/gl3ext.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
//#import <OpenGL/gl3ext.h>
|
||||
//#import <libkern/OSAtomic.h>
|
||||
|
||||
|
||||
@implementation CSCathodeRayView {
|
||||
CVDisplayLinkRef displayLink;
|
||||
|
||||
GLuint _vertexShader, _fragmentShader;
|
||||
GLuint _shaderProgram;
|
||||
GLuint _arrayBuffer, _vertexArray;
|
||||
GLint _positionAttribute;
|
||||
GLint _textureCoordinatesAttribute;
|
||||
GLint _lateralAttribute;
|
||||
|
||||
GLint _textureSizeUniform, _windowSizeUniform;
|
||||
GLint _boundsOriginUniform, _boundsSizeUniform;
|
||||
GLint _alphaUniform;
|
||||
|
||||
GLuint _textureName, _shadowMaskTextureName;
|
||||
CRTSize _textureSize;
|
||||
|
||||
CRTFrame *_crtFrame;
|
||||
|
||||
NSString *_signalDecoder;
|
||||
CSCathodeRayViewSignalType _signalType;
|
||||
int32_t _signalDecoderGeneration;
|
||||
int32_t _compiledSignalDecoderGeneration;
|
||||
|
||||
CVDisplayLinkRef _displayLink;
|
||||
CGRect _aspectRatioCorrectedBounds;
|
||||
}
|
||||
|
||||
- (GLuint)textureForImageNamed:(NSString *)name
|
||||
/*- (GLuint)textureForImageNamed:(NSString *)name
|
||||
{
|
||||
NSImage *const image = [NSImage imageNamed:name];
|
||||
NSBitmapImageRep *bitmapRepresentation = [[NSBitmapImageRep alloc] initWithData: [image TIFFRepresentation]];
|
||||
@ -57,7 +35,7 @@
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
return textureName;
|
||||
}
|
||||
}*/
|
||||
|
||||
- (void)prepareOpenGL
|
||||
{
|
||||
@ -66,29 +44,29 @@
|
||||
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
|
||||
// Create a display link capable of being used with all active displays
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
||||
|
||||
// Set the renderer output callback function
|
||||
CVDisplayLinkSetOutputCallback(displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
|
||||
CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
|
||||
|
||||
// Set the display link for the current renderer
|
||||
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
|
||||
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
|
||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
|
||||
|
||||
// install the shadow mask texture as the second texture
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
/* glActiveTexture(GL_TEXTURE1);
|
||||
_shadowMaskTextureName = [self textureForImageNamed:@"ShadowMask"];
|
||||
|
||||
// otherwise, we'll be working on the first texture
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glActiveTexture(GL_TEXTURE0);*/
|
||||
|
||||
// get the shader ready, set the clear colour
|
||||
[self.openGLContext makeCurrentContext];
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
// Activate the display link
|
||||
CVDisplayLinkStart(displayLink);
|
||||
CVDisplayLinkStart(_displayLink);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
@ -103,27 +81,26 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
CVDisplayLinkStop(displayLink);
|
||||
CVDisplayLinkStop(_displayLink);
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Release the display link
|
||||
CVDisplayLinkRelease(displayLink);
|
||||
CVDisplayLinkRelease(_displayLink);
|
||||
|
||||
// Release OpenGL buffers
|
||||
[self.openGLContext makeCurrentContext];
|
||||
glDeleteBuffers(1, &_arrayBuffer);
|
||||
glDeleteVertexArrays(1, &_vertexArray);
|
||||
glDeleteTextures(1, &_textureName);
|
||||
glDeleteTextures(1, &_shadowMaskTextureName);
|
||||
glDeleteProgram(_shaderProgram);
|
||||
// [self.openGLContext makeCurrentContext];
|
||||
// glDeleteBuffers(1, &_arrayBuffer);
|
||||
// glDeleteVertexArrays(1, &_vertexArray);
|
||||
// glDeleteTextures(1, &_textureName);
|
||||
// glDeleteTextures(1, &_shadowMaskTextureName);
|
||||
// glDeleteProgram(_shaderProgram);
|
||||
}
|
||||
|
||||
- (NSPoint)backingViewSize
|
||||
- (CGSize)backingSize
|
||||
{
|
||||
NSPoint backingSize = {.x = self.bounds.size.width, .y = self.bounds.size.height};
|
||||
return [self convertPointToBacking:backingSize];
|
||||
return [self convertSizeToBacking:self.bounds.size];
|
||||
}
|
||||
|
||||
- (void)reshape
|
||||
@ -133,49 +110,49 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
[self.openGLContext makeCurrentContext];
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
|
||||
NSPoint viewSize = [self backingViewSize];
|
||||
glViewport(0, 0, (GLsizei)viewSize.x, (GLsizei)viewSize.y);
|
||||
CGSize viewSize = [self backingSize];
|
||||
glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height);
|
||||
|
||||
[self pushSizeUniforms];
|
||||
// [self pushSizeUniforms];
|
||||
|
||||
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
}
|
||||
|
||||
- (void)setFrameBounds:(CGRect)frameBounds
|
||||
{
|
||||
_frameBounds = frameBounds;
|
||||
|
||||
[self.openGLContext makeCurrentContext];
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
|
||||
[self pushSizeUniforms];
|
||||
|
||||
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
}
|
||||
|
||||
- (void)pushSizeUniforms
|
||||
{
|
||||
if(_shaderProgram)
|
||||
{
|
||||
NSPoint viewSize = [self backingViewSize];
|
||||
if(_windowSizeUniform >= 0)
|
||||
{
|
||||
glUniform2f(_windowSizeUniform, (GLfloat)viewSize.x, (GLfloat)viewSize.y);
|
||||
}
|
||||
|
||||
CGFloat outputAspectRatioMultiplier = (viewSize.x / viewSize.y) / (4.0 / 3.0);
|
||||
|
||||
// NSLog(@"%0.2f v %0.2f", outputAspectRatio, desiredOutputAspectRatio);
|
||||
_aspectRatioCorrectedBounds = _frameBounds;
|
||||
|
||||
CGFloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * _frameBounds.size.width;
|
||||
_aspectRatioCorrectedBounds.origin.x -= bonusWidth * 0.5f * _aspectRatioCorrectedBounds.size.width;
|
||||
_aspectRatioCorrectedBounds.size.width *= outputAspectRatioMultiplier;
|
||||
|
||||
if(_boundsOriginUniform >= 0) glUniform2f(_boundsOriginUniform, (GLfloat)_aspectRatioCorrectedBounds.origin.x, (GLfloat)_aspectRatioCorrectedBounds.origin.y);
|
||||
if(_boundsSizeUniform >= 0) glUniform2f(_boundsSizeUniform, (GLfloat)_aspectRatioCorrectedBounds.size.width, (GLfloat)_aspectRatioCorrectedBounds.size.height);
|
||||
}
|
||||
}
|
||||
//- (void)setFrameBounds:(CGRect)frameBounds
|
||||
//{
|
||||
// _frameBounds = frameBounds;
|
||||
//
|
||||
// [self.openGLContext makeCurrentContext];
|
||||
// CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
//
|
||||
// [self pushSizeUniforms];
|
||||
//
|
||||
// CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
//}
|
||||
//
|
||||
//- (void)pushSizeUniforms
|
||||
//{
|
||||
// if(_shaderProgram)
|
||||
// {
|
||||
// NSPoint viewSize = [self backingViewSize];
|
||||
// if(_windowSizeUniform >= 0)
|
||||
// {
|
||||
// glUniform2f(_windowSizeUniform, (GLfloat)viewSize.x, (GLfloat)viewSize.y);
|
||||
// }
|
||||
//
|
||||
// CGFloat outputAspectRatioMultiplier = (viewSize.x / viewSize.y) / (4.0 / 3.0);
|
||||
//
|
||||
//// NSLog(@"%0.2f v %0.2f", outputAspectRatio, desiredOutputAspectRatio);
|
||||
// _aspectRatioCorrectedBounds = _frameBounds;
|
||||
//
|
||||
// CGFloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * _frameBounds.size.width;
|
||||
// _aspectRatioCorrectedBounds.origin.x -= bonusWidth * 0.5f * _aspectRatioCorrectedBounds.size.width;
|
||||
// _aspectRatioCorrectedBounds.size.width *= outputAspectRatioMultiplier;
|
||||
//
|
||||
// if(_boundsOriginUniform >= 0) glUniform2f(_boundsOriginUniform, (GLfloat)_aspectRatioCorrectedBounds.origin.x, (GLfloat)_aspectRatioCorrectedBounds.origin.y);
|
||||
// if(_boundsSizeUniform >= 0) glUniform2f(_boundsSizeUniform, (GLfloat)_aspectRatioCorrectedBounds.size.width, (GLfloat)_aspectRatioCorrectedBounds.size.height);
|
||||
// }
|
||||
//}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
@ -205,10 +182,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
self.wantsBestResolutionOpenGLSurface = YES;
|
||||
|
||||
// establish default instance variable values
|
||||
self.frameBounds = CGRectMake(0.0, 0.0, 1.0, 1.0);
|
||||
// self.frameBounds = CGRectMake(0.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
- (GLint)formatForDepth:(unsigned int)depth
|
||||
/*- (GLint)formatForDepth:(unsigned int)depth
|
||||
{
|
||||
switch(depth)
|
||||
{
|
||||
@ -489,7 +466,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
}*/
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
@ -501,18 +478,20 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
[self.openGLContext makeCurrentContext];
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
|
||||
while((!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) && _signalDecoder) {
|
||||
_compiledSignalDecoderGeneration = _signalDecoderGeneration;
|
||||
[self prepareShader];
|
||||
}
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (_crtFrame)
|
||||
{
|
||||
if(_textureSizeUniform >= 0) glUniform2f(_textureSizeUniform, _crtFrame->size.width, _crtFrame->size.height);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_crtFrame->number_of_vertices);
|
||||
}
|
||||
[self.delegate openGLViewDrawView:self];
|
||||
// while((!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) && _signalDecoder) {
|
||||
// _compiledSignalDecoderGeneration = _signalDecoderGeneration;
|
||||
// [self prepareShader];
|
||||
// }
|
||||
//
|
||||
// glClear(GL_COLOR_BUFFER_BIT);
|
||||
//
|
||||
// if (_crtFrame)
|
||||
// {
|
||||
// if(_textureSizeUniform >= 0) glUniform2f(_textureSizeUniform, _crtFrame->size.width, _crtFrame->size.height);
|
||||
// glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_crtFrame->number_of_vertices);
|
||||
// }
|
||||
|
||||
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
|
||||
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||
|
@ -19,7 +19,7 @@
|
||||
BOOL _didDecideRegion;
|
||||
}
|
||||
|
||||
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync {
|
||||
/*- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync {
|
||||
if(!_didDecideRegion)
|
||||
{
|
||||
_frameCount++;
|
||||
@ -41,7 +41,7 @@
|
||||
}
|
||||
|
||||
[super crt:crt didEndFrame:frame didDetectVSync:didDetectVSync];
|
||||
}
|
||||
}*/
|
||||
|
||||
- (void)doRunForNumberOfCycles:(int)numberOfCycles {
|
||||
_atari2600.run_for_cycles(numberOfCycles);
|
||||
@ -65,13 +65,4 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setView:(CSCathodeRayView *)view {
|
||||
[super setView:view];
|
||||
[view setSignalDecoder:[NSString stringWithUTF8String:_atari2600.get_signal_decoder()] type:CSCathodeRayViewSignalTypeNTSC];
|
||||
}
|
||||
|
||||
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate{
|
||||
_atari2600.get_crt()->set_delegate(delegate);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -18,4 +18,6 @@
|
||||
|
||||
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
|
||||
|
||||
- (void)drawViewForPixelSize:(CGSize)pixelSize;
|
||||
|
||||
@end
|
||||
|
@ -14,21 +14,9 @@
|
||||
|
||||
@implementation CSElectron {
|
||||
Electron::Machine _electron;
|
||||
|
||||
// NSTimeInterval _periodicStart;
|
||||
// int _numberOfCycles;
|
||||
}
|
||||
|
||||
- (void)doRunForNumberOfCycles:(int)numberOfCycles {
|
||||
/* _numberOfCycles += numberOfCycles;
|
||||
NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];
|
||||
NSTimeInterval difference = timeNow - _periodicStart;
|
||||
if(difference > 1.0)
|
||||
{
|
||||
NSLog(@"cycles: %0.0f", (double)_numberOfCycles / difference);
|
||||
_periodicStart = timeNow;
|
||||
_numberOfCycles = 0;
|
||||
}*/
|
||||
_electron.run_for_cycles(numberOfCycles);
|
||||
}
|
||||
|
||||
@ -44,8 +32,8 @@
|
||||
_electron.set_rom((Electron::ROMSlot)slot, rom.length, (const uint8_t *)rom.bytes);
|
||||
}
|
||||
|
||||
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate {
|
||||
_electron.get_crt()->set_delegate(delegate);
|
||||
- (void)drawViewForPixelSize:(CGSize)pixelSize {
|
||||
_electron.get_crt()->draw_frame((int)pixelSize.width, (int)pixelSize.height);
|
||||
}
|
||||
|
||||
- (BOOL)openUEFAtURL:(NSURL *)URL {
|
||||
@ -67,7 +55,6 @@
|
||||
|
||||
- (void)setView:(CSCathodeRayView *)view {
|
||||
[super setView:view];
|
||||
[view setSignalDecoder:[NSString stringWithUTF8String:_electron.get_signal_decoder()] type:CSCathodeRayViewSignalTypeRGB];
|
||||
}
|
||||
|
||||
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed {
|
||||
|
@ -13,12 +13,10 @@
|
||||
@interface CSMachine (Subclassing)
|
||||
|
||||
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate;
|
||||
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate;
|
||||
|
||||
- (void)doRunForNumberOfCycles:(int)numberOfCycles;
|
||||
- (void)perform:(dispatch_block_t)action;
|
||||
|
||||
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync;
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
|
||||
|
||||
@end
|
||||
|
@ -9,13 +9,6 @@
|
||||
#import "CSMachine.h"
|
||||
#import "CSMachine+Subclassing.h"
|
||||
|
||||
struct CRTDelegate: public Outputs::CRT::Delegate {
|
||||
__weak CSMachine *machine;
|
||||
void crt_did_end_frame(Outputs::CRT *crt, CRTFrame *frame, bool did_detect_vsync) {
|
||||
[machine crt:crt didEndFrame:frame didDetectVSync:did_detect_vsync];
|
||||
}
|
||||
};
|
||||
|
||||
struct SpeakerDelegate: public Outputs::Speaker::Delegate {
|
||||
__weak CSMachine *machine;
|
||||
void speaker_did_complete_samples(Outputs::Speaker *speaker, const int16_t *buffer, int buffer_size) {
|
||||
@ -29,7 +22,6 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
|
||||
};
|
||||
|
||||
@implementation CSMachine {
|
||||
CRTDelegate _crtDelegate;
|
||||
SpeakerDelegate _speakerDelegate;
|
||||
|
||||
dispatch_queue_t _serialDispatchQueue;
|
||||
@ -40,10 +32,6 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
|
||||
dispatch_async(_serialDispatchQueue, action);
|
||||
}
|
||||
|
||||
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync {
|
||||
if([self.view pushFrame:frame]) crt->return_frame();
|
||||
}
|
||||
|
||||
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length {
|
||||
[self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length];
|
||||
}
|
||||
@ -66,16 +54,13 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
|
||||
_serialDispatchQueue = dispatch_queue_create("Machine queue", DISPATCH_QUEUE_SERIAL);
|
||||
_runningLock = [[NSConditionLock alloc] initWithCondition:CSMachineRunningStateStopped];
|
||||
|
||||
_crtDelegate.machine = self;
|
||||
_speakerDelegate.machine = self;
|
||||
[self setCRTDelegate:&_crtDelegate];
|
||||
[self setSpeakerDelegate:&_speakerDelegate sampleRate:44100];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate {}
|
||||
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate {
|
||||
return NO;
|
||||
}
|
||||
|
@ -99,13 +99,14 @@ void CRT::allocate_buffers(unsigned int number, va_list sizes)
|
||||
|
||||
CRT::CRT() :
|
||||
_next_scan(0),
|
||||
_frames_with_delegate(0),
|
||||
_frame_read_pointer(0),
|
||||
_horizontal_counter(0),
|
||||
_sync_capacitor_charge_level(0),
|
||||
_is_receiving_sync(false),
|
||||
_is_in_hsync(false),
|
||||
_is_in_vsync(false),
|
||||
_current_frame(nullptr),
|
||||
_current_frame_mutex(new std::mutex),
|
||||
_rasterPosition({.x = 0, .y = 0})
|
||||
{}
|
||||
|
||||
@ -332,21 +333,24 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
// end of vertical sync: tell the delegate that we finished vertical sync,
|
||||
// releasing all runs back into the common pool
|
||||
case SyncEvent::EndVSync:
|
||||
if(_delegate && _current_frame_builder)
|
||||
if(_current_frame_builder)
|
||||
{
|
||||
_current_frame_builder->complete();
|
||||
_frames_with_delegate++;
|
||||
_delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
|
||||
_current_frame_mutex->lock();
|
||||
_current_frame = &_current_frame_builder->frame;
|
||||
_current_frame_mutex->unlock();
|
||||
// TODO: how to communicate did_detect_vsync? Bring the delegate back?
|
||||
// _delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
|
||||
}
|
||||
|
||||
if(_frames_with_delegate < kCRTNumberOfFrames)
|
||||
// if(_frames_with_delegate < kCRTNumberOfFrames)
|
||||
{
|
||||
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
|
||||
_current_frame_builder = _frame_builders[_frame_read_pointer];
|
||||
_current_frame_builder->reset();
|
||||
}
|
||||
else
|
||||
_current_frame_builder = nullptr;
|
||||
// else
|
||||
// _current_frame_builder = nullptr;
|
||||
|
||||
_is_in_vsync = false;
|
||||
_did_detect_vsync = false;
|
||||
@ -358,18 +362,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
|
||||
}
|
||||
}
|
||||
|
||||
void CRT::return_frame()
|
||||
{
|
||||
_frames_with_delegate--;
|
||||
}
|
||||
|
||||
#pragma mark - delegate
|
||||
|
||||
void CRT::set_delegate(Delegate *delegate)
|
||||
{
|
||||
_delegate = delegate;
|
||||
}
|
||||
|
||||
#pragma mark - stream feeding methods
|
||||
|
||||
void CRT::output_scan()
|
||||
@ -443,82 +435,7 @@ uint8_t *CRT::get_write_target_for_buffer(int buffer)
|
||||
return _current_frame_builder->get_write_target_for_buffer(buffer);
|
||||
}
|
||||
|
||||
#pragma mark - CRTFrame
|
||||
|
||||
CRTFrameBuilder::CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes)
|
||||
{
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
frame.number_of_buffers = number_of_buffers;
|
||||
frame.buffers = new CRTBuffer[number_of_buffers];
|
||||
frame.size_per_vertex = kCRTSizeOfVertex;
|
||||
frame.geometry_mode = CRTGeometryModeTriangles;
|
||||
|
||||
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
||||
{
|
||||
frame.buffers[buffer].depth = va_arg(buffer_sizes, unsigned int);
|
||||
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
CRTFrameBuilder::~CRTFrameBuilder()
|
||||
{
|
||||
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
|
||||
delete[] frame.buffers[buffer].data;
|
||||
delete frame.buffers;
|
||||
}
|
||||
|
||||
void CRTFrameBuilder::reset()
|
||||
{
|
||||
frame.number_of_vertices = 0;
|
||||
_next_write_x_position = _next_write_y_position = 0;
|
||||
frame.dirty_size.width = 0;
|
||||
frame.dirty_size.height = 1;
|
||||
}
|
||||
|
||||
void CRTFrameBuilder::complete()
|
||||
{
|
||||
frame.vertices = &_all_runs[0];
|
||||
}
|
||||
|
||||
uint8_t *CRTFrameBuilder::get_next_run()
|
||||
{
|
||||
const size_t vertices_per_run = 6;
|
||||
|
||||
// get a run from the allocated list, allocating more if we're about to overrun
|
||||
if((frame.number_of_vertices + vertices_per_run) * frame.size_per_vertex >= _all_runs.size())
|
||||
{
|
||||
_all_runs.resize(_all_runs.size() + frame.size_per_vertex * vertices_per_run * 100);
|
||||
}
|
||||
|
||||
uint8_t *next_run = &_all_runs[frame.number_of_vertices * frame.size_per_vertex];
|
||||
frame.number_of_vertices += vertices_per_run;
|
||||
|
||||
return next_run;
|
||||
}
|
||||
|
||||
void CRTFrameBuilder::allocate_write_area(int required_length)
|
||||
{
|
||||
if (_next_write_x_position + required_length > frame.size.width)
|
||||
{
|
||||
_next_write_x_position = 0;
|
||||
_next_write_y_position = (_next_write_y_position+1)&(frame.size.height-1);
|
||||
frame.dirty_size.height++;
|
||||
}
|
||||
|
||||
_write_x_position = _next_write_x_position;
|
||||
_write_y_position = _next_write_y_position;
|
||||
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
|
||||
_next_write_x_position += required_length;
|
||||
frame.dirty_size.width = std::max(frame.dirty_size.width, _next_write_x_position);
|
||||
}
|
||||
|
||||
uint8_t *CRTFrameBuilder::get_write_target_for_buffer(int buffer)
|
||||
{
|
||||
return &frame.buffers[buffer].data[_write_target_pointer * frame.buffers[buffer].depth];
|
||||
}
|
||||
|
||||
char *CRT::get_vertex_shader()
|
||||
{
|
@ -13,6 +13,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include "CRTFrame.h"
|
||||
|
||||
@ -136,12 +137,10 @@ class CRT {
|
||||
*/
|
||||
uint8_t *get_write_target_for_buffer(int buffer);
|
||||
|
||||
// MARK: Binding
|
||||
|
||||
/*! Causes appropriate OpenGL or OpenGL ES calls to be issued in order to draw the current CRT state.
|
||||
The caller is responsible for ensuring that a valid OpenGL context exists for the duration of this call.
|
||||
*/
|
||||
void draw_frame();
|
||||
void draw_frame(int output_width, int output_height);
|
||||
|
||||
/*! Tells the CRT that the next call to draw_frame will occur on a different OpenGL context than
|
||||
the previous.
|
||||
@ -308,12 +307,13 @@ class CRT {
|
||||
|
||||
static const int kCRTNumberOfFrames = 4;
|
||||
|
||||
// the run delegate and the triple buffer
|
||||
// the triple buffer and OpenGL state
|
||||
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
|
||||
CRTFrameBuilder *_current_frame_builder;
|
||||
int _frames_with_delegate;
|
||||
CRTFrame *_current_frame;
|
||||
std::shared_ptr<std::mutex> _current_frame_mutex;
|
||||
int _frame_read_pointer;
|
||||
Delegate *_delegate;
|
||||
void *openGLState;
|
||||
};
|
||||
|
||||
}
|
86
Outputs/CRT/CRTFrameBuilder.cpp
Normal file
86
Outputs/CRT/CRTFrameBuilder.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// CRTFrameBuilder.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/02/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "CRT.hpp"
|
||||
|
||||
using namespace Outputs;
|
||||
|
||||
CRT::CRTFrameBuilder::CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes)
|
||||
{
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
frame.number_of_buffers = number_of_buffers;
|
||||
frame.buffers = new CRTBuffer[number_of_buffers];
|
||||
frame.size_per_vertex = kCRTSizeOfVertex;
|
||||
frame.geometry_mode = CRTGeometryModeTriangles;
|
||||
|
||||
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
||||
{
|
||||
frame.buffers[buffer].depth = va_arg(buffer_sizes, unsigned int);
|
||||
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
CRT::CRTFrameBuilder::~CRTFrameBuilder()
|
||||
{
|
||||
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
|
||||
delete[] frame.buffers[buffer].data;
|
||||
delete frame.buffers;
|
||||
}
|
||||
|
||||
void CRT::CRTFrameBuilder::reset()
|
||||
{
|
||||
frame.number_of_vertices = 0;
|
||||
_next_write_x_position = _next_write_y_position = 0;
|
||||
frame.dirty_size.width = 0;
|
||||
frame.dirty_size.height = 1;
|
||||
}
|
||||
|
||||
void CRT::CRTFrameBuilder::complete()
|
||||
{
|
||||
frame.vertices = &_all_runs[0];
|
||||
}
|
||||
|
||||
uint8_t *CRT::CRTFrameBuilder::get_next_run()
|
||||
{
|
||||
const size_t vertices_per_run = 6;
|
||||
|
||||
// get a run from the allocated list, allocating more if we're about to overrun
|
||||
if((frame.number_of_vertices + vertices_per_run) * frame.size_per_vertex >= _all_runs.size())
|
||||
{
|
||||
_all_runs.resize(_all_runs.size() + frame.size_per_vertex * vertices_per_run * 100);
|
||||
}
|
||||
|
||||
uint8_t *next_run = &_all_runs[frame.number_of_vertices * frame.size_per_vertex];
|
||||
frame.number_of_vertices += vertices_per_run;
|
||||
|
||||
return next_run;
|
||||
}
|
||||
|
||||
void CRT::CRTFrameBuilder::allocate_write_area(int required_length)
|
||||
{
|
||||
if (_next_write_x_position + required_length > frame.size.width)
|
||||
{
|
||||
_next_write_x_position = 0;
|
||||
_next_write_y_position = (_next_write_y_position+1)&(frame.size.height-1);
|
||||
frame.dirty_size.height++;
|
||||
}
|
||||
|
||||
_write_x_position = _next_write_x_position;
|
||||
_write_y_position = _next_write_y_position;
|
||||
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
|
||||
_next_write_x_position += required_length;
|
||||
frame.dirty_size.width = std::max(frame.dirty_size.width, _next_write_x_position);
|
||||
}
|
||||
|
||||
uint8_t *CRT::CRTFrameBuilder::get_write_target_for_buffer(int buffer)
|
||||
{
|
||||
return &frame.buffers[buffer].data[_write_target_pointer * frame.buffers[buffer].depth];
|
||||
}
|
50
Outputs/CRT/CRTOpenGL.cpp
Normal file
50
Outputs/CRT/CRTOpenGL.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// CRTOpenGL.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 03/02/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "CRT.hpp"
|
||||
|
||||
// TODO: figure out correct include paths for other platforms.
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
using namespace Outputs;
|
||||
|
||||
namespace {
|
||||
struct OpenGLState {
|
||||
GLuint _vertexShader, _fragmentShader;
|
||||
GLuint _shaderProgram;
|
||||
GLuint _arrayBuffer, _vertexArray;
|
||||
|
||||
GLint _positionAttribute;
|
||||
GLint _textureCoordinatesAttribute;
|
||||
GLint _lateralAttribute;
|
||||
|
||||
GLint _textureSizeUniform, _windowSizeUniform;
|
||||
GLint _boundsOriginUniform, _boundsSizeUniform;
|
||||
GLint _alphaUniform;
|
||||
|
||||
GLuint _textureName, _shadowMaskTextureName;
|
||||
};
|
||||
}
|
||||
|
||||
void CRT::draw_frame(int output_width, int output_height)
|
||||
{
|
||||
printf("%d %d\n", output_width, output_height);
|
||||
}
|
||||
|
||||
void CRT::set_openGL_context_will_change(bool should_delete_resources)
|
||||
{
|
||||
}
|
||||
|
||||
void CRT::set_composite_sampling_function(const char *shader)
|
||||
{
|
||||
}
|
||||
|
||||
void CRT::set_rgb_sampling_function(const char *shader)
|
||||
{
|
||||
printf("%s\n", shader);
|
||||
}
|
Loading…
Reference in New Issue
Block a user