1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-10-25 09:27:01 +00:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Thomas Harte
b1e062945e Merge pull request #821 from TomHarte/QtThreading
Qt issue mega ticket
2020-07-27 21:19:12 -04:00
Thomas Harte
3db4a8c312 Attempts to improve vsync deadline estimation.
Also increases probability of bad estimation being discarded.
2020-07-27 21:08:00 -04:00
Thomas Harte
db8e1b0edf Adds feedback on unidentified ROMs. 2020-07-27 20:45:47 -04:00
Thomas Harte
71c3f58c99 Provides user feedback upon improper command-line usage. 2020-07-27 20:40:38 -04:00
Thomas Harte
7c05b1788e Ensures proper thread confinement for updateStatusBarText. 2020-07-27 20:25:52 -04:00
Thomas Harte
a2db6ddea5 Add link to Snap releases. 2020-07-25 23:00:29 -04:00
Thomas Harte
6ad1e3e17e Merge pull request #819 from TomHarte/Warnings
Resolves GCC 7 warnings.
2020-07-24 22:17:50 -04:00
Thomas Harte
e097a841d2 Adds a c++1z fallback for SDL builds, too. 2020-07-24 22:01:22 -04:00
Thomas Harte
fa95a17af5 Resolves receive_bit_count-unused warnings. 2020-07-24 21:59:27 -04:00
Thomas Harte
b961665985 Ensures WOZ2 behaviour even if type_ has an invalid value.
This pleases GCC 7.
2020-07-24 21:56:20 -04:00
Thomas Harte
8af35bc6bb Resolves signed comparison mismatches. 2020-07-24 21:55:33 -04:00
Thomas Harte
9b75287a52 Merge pull request #818 from TomHarte/QtC++1z
Add c++1z as a config option, for older versions of Qt.
2020-07-24 16:34:04 -04:00
Thomas Harte
84d5316aa7 Add c++1z as a config option, for older versions of Qt. 2020-07-24 16:32:59 -04:00
11 changed files with 65 additions and 28 deletions

View File

@@ -63,6 +63,13 @@ class VSyncPredictor {
frame_duration_ = Nanos(1'000'000'000.0f / rate);
}
/*!
@returns The time this class currently believes a whole frame occupies.
*/
Time::Nanos frame_duration() {
return frame_duration_;
}
/*!
Adds a record of how much jitter was experienced in scheduling; these values will be
factored into the @c suggested_draw_time if supplied.
@@ -87,15 +94,13 @@ class VSyncPredictor {
(if those figures are being supplied).
*/
Nanos suggested_draw_time() {
const auto mean = redraw_period_.mean() - timer_jitter_.mean() - vsync_jitter_.mean();
const auto mean = redraw_period_.mean() + timer_jitter_.mean() + vsync_jitter_.mean();
const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_jitter_.variance();
// Permit three standard deviations from the mean, to cover 99.9% of cases.
const auto period = mean - Nanos(3.0f * sqrt(float(variance)));
const auto period = mean + Nanos(3.0f * sqrt(float(variance)));
assert(abs(period) < 10'000'000'000);
return last_vsync_ + period;
return last_vsync_ + frame_duration_ - period;
}
private:
@@ -109,7 +114,6 @@ class VSyncPredictor {
}
void post(Time::Nanos value) {
assert(abs(value) < 10'000'000'000); // 10 seconds is a very liberal maximum.
sum_ -= history_[write_pointer_];
sum_ += value;
history_[write_pointer_] = value;

View File

@@ -316,6 +316,8 @@ void z8530::Channel::write(bool data, uint8_t pointer, uint8_t value) {
}
LOG("Receive bit count: " << receive_bit_count);
(void)receive_bit_count;
/*
b7,b6:
00 = 5 receive bits per character

View File

@@ -1,6 +1,9 @@
QT += core gui multimedia widgets
# Be specific about C++17 but also try the vaguer C++1z for older
# versions of Qt.
CONFIG += c++17
CONFIG += c++1z
# Permit multiple source files in different directories to have the same file name.
CONFIG += object_parallel_to_source

View File

@@ -45,7 +45,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
MainWindow::MainWindow(const QString &fileName) {
init();
launchFile(fileName);
if(!launchFile(fileName)) {
setUIPhase(UIPhase::SelectingMachine);
}
}
void MainWindow::deleteMachine() {
@@ -210,11 +212,17 @@ void MainWindow::insertFile(const QString &fileName) {
mediaTarget->insert_media(media);
}
void MainWindow::launchFile(const QString &fileName) {
bool MainWindow::launchFile(const QString &fileName) {
targets = Analyser::Static::GetTargets(fileName.toStdString());
if(!targets.empty()) {
openFileName = QFileInfo(fileName).fileName();
launchMachine();
return true;
} else {
QMessageBox msgBox;
msgBox.setText("Unable to open file: " + fileName);
msgBox.exec();
return false;
}
}
@@ -707,6 +715,7 @@ void MainWindow::dropEvent(QDropEvent* event) {
bool foundROM = false;
const auto appDataLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString();
QString unusedRoms;
for(const auto &url: event->mimeData()->urls()) {
const char *const name = url.toLocalFile().toUtf8();
FILE *const file = fopen(name, "rb");
@@ -716,6 +725,7 @@ void MainWindow::dropEvent(QDropEvent* event) {
CRC::CRC32 generator;
const uint32_t crc = generator.compute_crc(*contents);
bool wasUsed = false;
for(const auto &rom: missingRoms) {
if(std::find(rom.crc32s.begin(), rom.crc32s.end(), crc) != rom.crc32s.end()) {
foundROM = true;
@@ -731,10 +741,22 @@ void MainWindow::dropEvent(QDropEvent* event) {
FILE *const target = fopen(destination.c_str(), "wb");
fwrite(contents->data(), 1, contents->size(), target);
fclose(target);
wasUsed = true;
}
}
if(!wasUsed) {
if(!unusedRoms.isEmpty()) unusedRoms += ", ";
unusedRoms += url.fileName();
}
}
if(!unusedRoms.isEmpty()) {
QMessageBox msgBox;
msgBox.setText("Couldn't identify ROMs: " + unusedRoms);
msgBox.exec();
}
if(foundROM) launchMachine();
} break;
}
@@ -1338,24 +1360,25 @@ void MainWindow::addActivityObserver() {
}
void MainWindow::register_led(const std::string &name) {
std::lock_guard guard(ledStatusesLock);
ledStatuses[name] = false;
updateStatusBarText();
QMetaObject::invokeMethod(this, "updateStatusBarText");
}
void MainWindow::set_led_status(const std::string &name, bool isLit) {
std::lock_guard guard(ledStatusesLock);
ledStatuses[name] = isLit;
updateStatusBarText(); // Assumption here: Qt's attempt at automatic thread confinement will work here.
QMetaObject::invokeMethod(this, "updateStatusBarText");
}
void MainWindow::updateStatusBarText() {
QString fullText;
bool isFirst = true;
std::lock_guard guard(ledStatusesLock);
for(const auto &pair: ledStatuses) {
if(!isFirst) fullText += " | ";
if(!fullText.isEmpty()) fullText += " | ";
fullText += QString::fromStdString(pair.first);
fullText += " ";
fullText += pair.second ? "" : "";
isFirst = false;
}
statusBar()->showMessage(fullText);
}

View File

@@ -5,6 +5,7 @@
#include <QMainWindow>
#include <memory>
#include <mutex>
#include <optional>
#include "audiobuffer.h"
@@ -80,6 +81,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
private slots:
void startMachine();
void updateStatusBarText();
private:
void start_appleII();
@@ -100,7 +102,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
QAction *insertAction = nullptr;
void insertFile(const QString &fileName);
void launchFile(const QString &fileName);
bool launchFile(const QString &fileName);
void launchTarget(std::unique_ptr<Analyser::Static::Target> &&);
void restoreSelections();
@@ -144,9 +146,11 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void register_led(const std::string &) override;
void set_led_status(const std::string &, bool) override;
std::recursive_mutex ledStatusesLock;
std::map<std::string, bool> ledStatuses;
void addActivityObserver();
void updateStatusBarText();
};
#endif // MAINWINDOW_H

View File

@@ -86,7 +86,7 @@ void ScanTargetWidget::vsync() {
const auto time_now = Time::nanos_now();
requestedRedrawTime = vsyncPredictor.suggested_draw_time();
const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
if(delay_time > 0) {
if(delay_time > 0 && delay_time < vsyncPredictor.frame_duration()) {
QTimer::singleShot(delay_time, this, SLOT(repaint()));
} else {
requestedRedrawTime = 0;

View File

@@ -1,19 +1,19 @@
import glob
import sys
# establish UTF-8 encoding for Python 2
# Establish UTF-8 encoding for Python 2.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf-8')
# create build environment
# Create build environment.
env = Environment()
# determine compiler and linker flags for SDL
# Determine compiler and linker flags for SDL.
env.ParseConfig('sdl2-config --cflags')
env.ParseConfig('sdl2-config --libs')
# gather a list of source files
# Gather a list of source files.
SOURCES = glob.glob('*.cpp')
SOURCES += glob.glob('../../Analyser/Dynamic/*.cpp')
@@ -117,11 +117,11 @@ SOURCES += glob.glob('../../Storage/Tape/*.cpp')
SOURCES += glob.glob('../../Storage/Tape/Formats/*.cpp')
SOURCES += glob.glob('../../Storage/Tape/Parsers/*.cpp')
# add additional compiler flags
env.Append(CCFLAGS = ['--std=c++17', '-Wall', '-O2', '-DNDEBUG'])
# Add additional compiler flags; c++1z is insurance in case c++17 isn't fully implemented.
env.Append(CCFLAGS = ['--std=c++17', '--std=c++1z', '-Wall', '-O2', '-DNDEBUG'])
# add additional libraries to link against
# Add additional libraries to link against.
env.Append(LIBS = ['libz', 'pthread', 'GL'])
# build target
# Build target.
env.Program(target = 'clksignal', source = SOURCES)

View File

@@ -2,7 +2,7 @@
# Clock Signal
Clock Signal ('CLK') is an emulator for tourists that seeks to be invisible. Users directly launch classic software with no emulator or per-emulated-machine learning curve.
[Releases](https://github.com/TomHarte/CLK/releases) are hosted on GitHub.
macOS and source releases are [hosted on GitHub](https://github.com/TomHarte/CLK/releases). For desktop Linux it is also available as a [Snap](https://snapcraft.io/clock-signal).
On the Mac it is a native Cocoa application; under Linux, BSD and other UNIXes and UNIX-alikes it can be built either with Qt or with SDL; the Qt build should be considered preliminary and is currently closely bound to X11 as Qt doesn't abstract game-like keyboard handling.

View File

@@ -166,6 +166,7 @@ std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) {
number_of_bits = std::min(file_.get16le(), uint16_t(6646*8));
break;
default:
case Type::WOZ2: {
// In WOZ 2 an extra level of indirection allows for variable track sizes.
const uint16_t starting_block = file_.get16le();

View File

@@ -247,10 +247,10 @@ void TZX::get_data_block(const DataBlock &data_block) {
void TZX::get_data(const Data &data) {
// Output data.
for(unsigned int c = 0; c < data.data_length; c++) {
for(decltype(data.data_length) c = 0; c < data.data_length; c++) {
uint8_t next_byte = file_.get8();
unsigned int bits = (c != data.data_length-1) ? 8 : data.number_of_bits_in_final_byte;
auto bits = (c != data.data_length-1) ? 8 : data.number_of_bits_in_final_byte;
while(bits--) {
unsigned int pulse_length = (next_byte & 0x80) ? data.length_of_one_bit_pulse : data.length_of_zero_bit_pulse;
next_byte <<= 1;

View File

@@ -77,7 +77,7 @@ class TZX: public PulseQueuedTape {
unsigned int length_of_one_bit_pulse;
unsigned int number_of_bits_in_final_byte;
unsigned int pause_after_block;
long data_length;
uint32_t data_length;
};
struct DataBlock {