mirror of
https://github.com/trudnai/Steve2.git
synced 2024-06-13 09:29:36 +00:00
Game loop is based on display VSYNC - typically 60 Hz
This commit is contained in:
parent
20596f234f
commit
7fa288d84d
|
@ -79,6 +79,8 @@ class ViewController: NSViewController {
|
|||
|
||||
static var current : ViewController? = nil
|
||||
|
||||
var displayLink: CVDisplayLink?
|
||||
|
||||
@IBOutlet weak var textDisplayScroller: NSScrollView!
|
||||
@IBOutlet var textDisplay: NSTextView!
|
||||
@IBOutlet weak var speedometer: NSTextFieldCell!
|
||||
|
@ -1033,15 +1035,15 @@ class ViewController: NSViewController {
|
|||
|
||||
|
||||
var upd = RepeatingTimer(timeInterval: 1)
|
||||
|
||||
func newUpdateTimer( timeInterval : Double ) {
|
||||
upd.kill()
|
||||
upd = RepeatingTimer(timeInterval: timeInterval)
|
||||
upd.eventHandler = {
|
||||
self.Update()
|
||||
}
|
||||
upd.resume()
|
||||
}
|
||||
|
||||
// func newUpdateTimer( timeInterval : Double ) {
|
||||
// upd.kill()
|
||||
// upd = RepeatingTimer(timeInterval: timeInterval)
|
||||
// upd.eventHandler = {
|
||||
// self.Update()
|
||||
// }
|
||||
// upd.resume()
|
||||
// }
|
||||
|
||||
|
||||
// Kelvin Sherlock's fix to avoid uninstalled font problems
|
||||
|
@ -1096,6 +1098,55 @@ class ViewController: NSViewController {
|
|||
// -1.0, -1.0, 0.0,
|
||||
// 1.0, -1.0, 0.0
|
||||
// ]
|
||||
|
||||
// The callback function is called everytime CVDisplayLink says its time to get a new frame.
|
||||
let displayLinkOutputCallback : CVDisplayLinkOutputCallback = { (displayLink: CVDisplayLink, _ inNow: UnsafePointer<CVTimeStamp>, _ inOutputTime: UnsafePointer<CVTimeStamp>, _ flagsIn: CVOptionFlags, _ flagsOut: UnsafeMutablePointer<CVOptionFlags>, _ vController: UnsafeMutableRawPointer?) -> CVReturn in
|
||||
|
||||
/* The displayLinkContext is CVDisplayLink's parameter definition of the view in which we are working.
|
||||
In order to access the methods of a given view we need to specify what kind of view it is as right
|
||||
now the UnsafeMutablePointer<Void> just means we have a pointer to "something". To cast the pointer
|
||||
such that the compiler at runtime can access the methods associated with our SwiftOpenGLView, we use
|
||||
an unsafeBitCast. The definition of which states, "Returns the the bits of x, interpreted as having
|
||||
type U." We may then call any of that view's methods. Here we call drawView() which we draw a
|
||||
frame for rendering. */
|
||||
// unsafeBitCast(displayLinkContext, SwiftOpenGLView.self).renderFrame()
|
||||
|
||||
/// This can be a precise FPS updater so MHz would be dead perfect pitch -- however, it makes things worse
|
||||
// let now : CVTimeStamp = inNow.pointee
|
||||
//// print( "RateScaler:", now.rateScalar )
|
||||
// if (mySelf.last_frame_time != 0) {
|
||||
// let videoTimeDiff = now.videoTime - mySelf.last_frame_time
|
||||
// let currentFPS = Double(now.videoTimeScale) / Double(videoTimeDiff)
|
||||
//
|
||||
// fps = currentFPS
|
||||
// // mySelf.setCPUClockSpeed(freq: MHz_6502)
|
||||
// clk_6502_per_frm = UInt64( MHz_6502 * M / currentFPS )
|
||||
// clk_6502_per_frm_set = clk_6502_per_frm
|
||||
//
|
||||
// }
|
||||
// mySelf.last_frame_time = now.videoTime;
|
||||
|
||||
let mySelf = Unmanaged<ViewController>.fromOpaque(vController!).takeUnretainedValue()
|
||||
mySelf.Update();
|
||||
|
||||
// We are going to assume that everything went well for this mock up, and pass success as the CVReturn
|
||||
return kCVReturnSuccess
|
||||
}
|
||||
|
||||
|
||||
// sets a callback at every screen refresh (normally 60Hz)
|
||||
func setupDisplayLink() {
|
||||
// Grab the a link to the active displays, set the callback defined above, and start the link.
|
||||
/* An alternative to a nested function is a global function or a closure passed as the argument--a local function
|
||||
(i.e. a function defined within the class) is NOT allowed. */
|
||||
// The UnsafeMutablePointer<Void>(unsafeAddressOf(self)) passes a pointer to the instance of our class.
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink)
|
||||
|
||||
let unsafeSelf: UnsafeMutableRawPointer = Unmanaged.passUnretained(self).toOpaque()
|
||||
CVDisplayLinkSetOutputCallback(displayLink!, displayLinkOutputCallback, unsafeSelf)
|
||||
CVDisplayLinkStart(displayLink!)
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
@ -1189,7 +1240,24 @@ class ViewController: NSViewController {
|
|||
// }
|
||||
// upd.resume()
|
||||
|
||||
newUpdateTimer( timeInterval: 1 / Double(fps) )
|
||||
// newUpdateTimer( timeInterval: 1 / Double(fps) )
|
||||
|
||||
|
||||
// let displayLink = CADisplayLink(target: self, selector: #selector(Update))
|
||||
// displayLink.add(to: .current, forMode: .common)
|
||||
|
||||
|
||||
// CVDisplayLinkOutputCallback
|
||||
|
||||
// Grab the a link to the active displays, set the callback defined above, and start the link.
|
||||
/* An alternative to a nested function is a global function or a closure passed as the argument--a local function
|
||||
(i.e. a function defined within the class) is NOT allowed. */
|
||||
// The UnsafeMutablePointer<Void>(unsafeAddressOf(self)) passes a pointer to the instance of our class.
|
||||
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink)
|
||||
let unsafeSelf: UnsafeMutableRawPointer = Unmanaged.passUnretained(self).toOpaque()
|
||||
CVDisplayLinkSetOutputCallback(displayLink!, displayLinkOutputCallback, unsafeSelf)
|
||||
CVDisplayLinkStart(displayLink!)
|
||||
|
||||
|
||||
// #endif
|
||||
|
||||
|
@ -1357,7 +1425,7 @@ class ViewController: NSViewController {
|
|||
// pixelTrail = pow(256, 1 / Double(fps / video_fps_divider / 3) )
|
||||
|
||||
// spkr_buf_size = spkr_sample_rate * 2 / spkr_fps
|
||||
newUpdateTimer( timeInterval: 1 / Double(fps) )
|
||||
// newUpdateTimer( timeInterval: 1 / Double(fps) )
|
||||
setCPUClockSpeed(freq: MHz_6502)
|
||||
|
||||
// TODO: Better way to deal with speaker!!!
|
||||
|
|
|
@ -370,7 +370,7 @@ void m6502_Run() {
|
|||
}
|
||||
|
||||
// TODO: What if we dynamically reduce or increace CPU speed?
|
||||
m6502.clktime += clk_6502_per_frm;
|
||||
m6502.clktime += m6502.clkfrm;
|
||||
|
||||
if( diskAccelerator_count ) {
|
||||
if( --diskAccelerator_count <= 0 ) {
|
||||
|
|
|
@ -186,12 +186,12 @@ extern double * pdl_diffarr;
|
|||
extern double mips;
|
||||
extern double mhz;
|
||||
|
||||
#define DEFAULT_FPS 30U
|
||||
#define DEFAULT_FPS 60U
|
||||
#define DEF_VIDEO_DIV 1U
|
||||
#define DEF_SPKR_DIV 1U
|
||||
|
||||
#define GAME_FPS 180U // 480U
|
||||
#define GAME_VIDEO_DIV (GAME_FPS / 60U)
|
||||
#define GAME_VIDEO_DIV (GAME_FPS / DEFAULT_FPS)
|
||||
|
||||
extern unsigned int video_fps_divider;
|
||||
extern unsigned int fps;
|
||||
|
|
|
@ -84,11 +84,11 @@ int spkr_level_tema = SPKR_LEVEL_ZERO;
|
|||
int spkr_last_level = SPKR_LEVEL_ZERO;
|
||||
|
||||
|
||||
static const int ema_len_sharper = 7;
|
||||
static const int ema_len_sharp = 14;
|
||||
//static const int ema_len_sharper = 7;
|
||||
//static const int ema_len_sharp = 14;
|
||||
static const int ema_len_normal = 18;
|
||||
static const int ema_len_soft = 20;
|
||||
static const int ema_len_supersoft = 40;
|
||||
//static const int ema_len_soft = 20;
|
||||
//static const int ema_len_supersoft = 40;
|
||||
|
||||
int spkr_ema_len = ema_len_normal;
|
||||
|
||||
|
@ -113,6 +113,7 @@ unsigned spkr_clk = 0;
|
|||
|
||||
#define SPKR_BUF_SLOT(n) ( spkr_buf_size * (n) )
|
||||
#define SPKR_SLOT_SIZE(n) ( spkr_buf_size * sizeof(spkr_sample_t) )
|
||||
#define SPKR_SILENT_SLOT 1
|
||||
|
||||
const unsigned spkr_seconds = 1;
|
||||
const unsigned spkr_sample_rate = 192000;
|
||||
|
@ -122,9 +123,9 @@ int spkr_extra_buf = 0; // 26; // 800 / spkr_fps;
|
|||
typedef int16_t spkr_sample_t;
|
||||
const unsigned spkr_buf_size = spkr_seconds * spkr_sample_rate * SPKR_CHANNELS / DEFAULT_FPS; // stereo
|
||||
const unsigned spkr_buf_alloc_size = spkr_buf_size * sizeof(spkr_sample_t);
|
||||
const unsigned sample_buf_array_len = SPKR_BUF_SLOT(BUFFER_COUNT + 1);
|
||||
const unsigned sample_buf_array_len = SPKR_BUF_SLOT(BUFFER_COUNT + SPKR_SILENT_SLOT);
|
||||
spkr_sample_t spkr_sample_buf [ sample_buf_array_len ]; // can store up to 1 sec of sound
|
||||
spkr_sample_t * spkr_samples = spkr_sample_buf + SPKR_BUF_SLOT(1); // keep 1 "empty" frame ahead
|
||||
spkr_sample_t * spkr_samples = spkr_sample_buf + SPKR_BUF_SLOT(SPKR_SILENT_SLOT); // keep 1 "empty" frame ahead
|
||||
unsigned spkr_sample_idx = 0;
|
||||
unsigned spkr_sample_last_idx = 0;
|
||||
unsigned spkr_sample_first_pwm_idx = 0;
|
||||
|
@ -262,7 +263,7 @@ void spkr_init() {
|
|||
alcMakeContextCurrent(ctx);
|
||||
|
||||
// Fill buffer with zeros
|
||||
memset( spkr_sample_buf, 0, SPKR_SLOT_SIZE(BUFFER_COUNT + 1) ); // spkr_buf_alloc_size + spkr_extra_buf * sizeof(spkr_sample_t));
|
||||
memset( spkr_sample_buf, 0, SPKR_SLOT_SIZE(BUFFER_COUNT + SPKR_SILENT_SLOT) ); // spkr_buf_alloc_size + spkr_extra_buf * sizeof(spkr_sample_t));
|
||||
|
||||
// memset(spkr_samples + spkr_sample_idx * sizeof(spkr_sample_t), 0, (sample_buf_array_len - spkr_sample_idx) * sizeof(spkr_sample_t));
|
||||
|
||||
|
@ -725,6 +726,7 @@ void spkr_buffer_with_pause(int buf_len) {
|
|||
switch (state) {
|
||||
case AL_PAUSED:
|
||||
case AL_PLAYING:
|
||||
// it is already playing so we can just simply feed the next sound block
|
||||
alBufferData(spkr_buffers[freeBuffers], AL_FORMAT_STEREO16, spkr_samples, buf_len, spkr_sample_rate);
|
||||
al_check_error();
|
||||
alSourceQueueBuffers(spkr_src[SPKR_SRC_GAME_SFX], 1, &spkr_buffers[freeBuffers]);
|
||||
|
@ -734,7 +736,8 @@ void spkr_buffer_with_pause(int buf_len) {
|
|||
case AL_INITIAL:
|
||||
case AL_STOPPED:
|
||||
default:
|
||||
alBufferData(spkr_buffers[freeBuffers], AL_FORMAT_STEREO16, spkr_sample_buf, buf_len * 2, spkr_sample_rate);
|
||||
// start with a silent sound block so later on we will not roun out of buffer that easy
|
||||
alBufferData(spkr_buffers[freeBuffers], AL_FORMAT_STEREO16, spkr_sample_buf, buf_len * (1 + SPKR_SILENT_SLOT), spkr_sample_rate);
|
||||
al_check_error();
|
||||
alSourceQueueBuffers(spkr_src[SPKR_SRC_GAME_SFX], 1, &spkr_buffers[freeBuffers]);
|
||||
al_check_error();
|
||||
|
@ -991,7 +994,7 @@ void spkr_play_disk_arm() {
|
|||
if ( ( disk_sfx_enabled ) && ( clk_6502_per_frm <= FRAME(iicplus_MHz_6502) ) ) {
|
||||
if ( spkr_play_disk_ioerr_time == 0 ) {
|
||||
spkr_play_sfx( spkr_src[SPKR_SRC_DISK_ARM_SFX], diskarm_sfx, diskarm_sfx_len );
|
||||
spkr_play_disk_arm_time = 2;
|
||||
spkr_play_disk_arm_time = fps / 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1000,7 +1003,7 @@ void spkr_play_disk_arm() {
|
|||
void spkr_play_disk_ioerr() {
|
||||
if ( ( disk_sfx_enabled ) && ( clk_6502_per_frm <= FRAME(iicplus_MHz_6502) ) ) {
|
||||
spkr_playqueue_sfx( spkr_src[SPKR_SRC_DISK_IOERR_SFX], diskioerr_sfx, diskioerr_sfx_len);
|
||||
spkr_play_disk_ioerr_time = 4;
|
||||
spkr_play_disk_ioerr_time = fps / 10; // 4 for 30 FPS, 8 for 60 FPS
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -333,7 +333,7 @@ uint8_t woz_read() {
|
|||
if ( usedBytes ) {
|
||||
// static const int extraForward = 4; // we search for 7 bit high a bit further to speed up disk read...
|
||||
uint64_t bitForward = (clkelpased >> 2) + extraForward;
|
||||
if ( bitForward > 100000000 ) {
|
||||
if ( bitForward > 1000 ) {
|
||||
bitForward = 4;
|
||||
}
|
||||
|
||||
|
@ -359,7 +359,7 @@ uint8_t woz_read() {
|
|||
WOZread.latch = 0;
|
||||
// but we do not want to miss that latch valid nibble...
|
||||
// in other words synchronization is needed because of imperfect cycle calculation
|
||||
if ( bitForward < 18 ) {
|
||||
if ( bitForward < 18 ) { // for 30 Hz FPS 18 is better ) {
|
||||
return latch;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user