mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-25 04:29:09 +00:00
Merge pull request #191 from TomHarte/Precache
Introduces more aggressive caching of sectors in the MFM decoder, improves CPC static analysis further
This commit is contained in:
commit
8c33ac71ee
@ -247,6 +247,7 @@ void i8272::posit_event(int event_type) {
|
|||||||
|
|
||||||
// Establishes the drive and head being addressed, and whether in double density mode; populates the internal
|
// Establishes the drive and head being addressed, and whether in double density mode; populates the internal
|
||||||
// cylinder, head, sector and size registers from the command stream.
|
// cylinder, head, sector and size registers from the command stream.
|
||||||
|
status_[0] = status_[1] = status_[2] = 0;
|
||||||
SET_DRIVE_HEAD_MFM();
|
SET_DRIVE_HEAD_MFM();
|
||||||
cylinder_ = command_[2];
|
cylinder_ = command_[2];
|
||||||
head_ = command_[3];
|
head_ = command_[3];
|
||||||
|
@ -9,14 +9,17 @@
|
|||||||
#ifndef Factors_hpp
|
#ifndef Factors_hpp
|
||||||
#define Factors_hpp
|
#define Factors_hpp
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace NumberTheory {
|
namespace NumberTheory {
|
||||||
/*!
|
/*!
|
||||||
@returns The greatest common divisor of @c a and @c b as computed by Euclid's algorithm.
|
@returns The greatest common divisor of @c a and @c b.
|
||||||
*/
|
*/
|
||||||
template<class T> T greatest_common_divisor(T a, T b) {
|
template<class T> T greatest_common_divisor(T a, T b) {
|
||||||
// TODO: replace with the C++17 GCD function, once available.
|
#if __cplusplus > 201402L
|
||||||
|
return std::gcd(a, b);
|
||||||
|
#else
|
||||||
if(a < b) {
|
if(a < b) {
|
||||||
std::swap(a, b);
|
std::swap(a, b);
|
||||||
}
|
}
|
||||||
@ -29,11 +32,12 @@ namespace NumberTheory {
|
|||||||
a = b;
|
a = b;
|
||||||
b = remainder;
|
b = remainder;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@returns The least common multiple of @c a and @c b computed indirectly via Euclid's greatest
|
@returns The least common multiple of @c a and @c b computed indirectly via the greatest
|
||||||
common divisor algorithm.
|
common divisor.
|
||||||
*/
|
*/
|
||||||
template<class T> T least_common_multiple(T a, T b) {
|
template<class T> T least_common_multiple(T a, T b) {
|
||||||
if(a == b) return a;
|
if(a == b) return a;
|
||||||
|
@ -3037,6 +3037,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
@ -3065,6 +3066,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "c++14";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_WARN_ASSIGN_ENUM = YES;
|
CLANG_WARN_ASSIGN_ENUM = YES;
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
@ -29,7 +29,7 @@ GLuint Shader::compile_shader(const std::string &source, GLenum type) {
|
|||||||
GLint logLength;
|
GLint logLength;
|
||||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
|
||||||
if(logLength > 0) {
|
if(logLength > 0) {
|
||||||
GLchar *log = new GLchar[logLength];
|
GLchar *log = new GLchar[(size_t)logLength];
|
||||||
glGetShaderInfoLog(shader, logLength, &logLength, log);
|
glGetShaderInfoLog(shader, logLength, &logLength, log);
|
||||||
printf("Compile log:\n%s\n", log);
|
printf("Compile log:\n%s\n", log);
|
||||||
delete[] log;
|
delete[] log;
|
||||||
@ -66,7 +66,7 @@ Shader::Shader(const std::string &vertex_shader, const std::string &fragment_sha
|
|||||||
GLint logLength;
|
GLint logLength;
|
||||||
glGetProgramiv(shader_program_, GL_INFO_LOG_LENGTH, &logLength);
|
glGetProgramiv(shader_program_, GL_INFO_LOG_LENGTH, &logLength);
|
||||||
if(logLength > 0) {
|
if(logLength > 0) {
|
||||||
GLchar *log = new GLchar[logLength];
|
GLchar *log = new GLchar[(size_t)logLength];
|
||||||
glGetProgramInfoLog(shader_program_, logLength, &logLength, log);
|
glGetProgramInfoLog(shader_program_, logLength, &logLength, log);
|
||||||
printf("Link log:\n%s\n", log);
|
printf("Link log:\n%s\n", log);
|
||||||
delete[] log;
|
delete[] log;
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "StaticAnalyser.hpp"
|
#include "StaticAnalyser.hpp"
|
||||||
|
|
||||||
#include "../../Storage/Disk/Parsers/CPM.hpp"
|
#include "../../Storage/Disk/Parsers/CPM.hpp"
|
||||||
|
#include "../../Storage/Disk/Encodings/MFM.hpp"
|
||||||
|
|
||||||
static bool strcmp_insensitive(const char *a, const char *b) {
|
static bool strcmp_insensitive(const char *a, const char *b) {
|
||||||
if(strlen(a) != strlen(b)) return false;
|
if(strlen(a) != strlen(b)) return false;
|
||||||
@ -37,6 +39,10 @@ static void InspectDataCatalogue(
|
|||||||
size_t last_implicit_suffixed_file = 0;
|
size_t last_implicit_suffixed_file = 0;
|
||||||
|
|
||||||
for(size_t c = 0; c < data_catalogue->files.size(); c++) {
|
for(size_t c = 0; c < data_catalogue->files.size(); c++) {
|
||||||
|
// Files with nothing but spaces in their name can't be loaded by the user, so disregard them.
|
||||||
|
if(data_catalogue->files[c].type == " " && data_catalogue->files[c].name == " ")
|
||||||
|
continue;
|
||||||
|
|
||||||
// Check for whether this is [potentially] BASIC.
|
// Check for whether this is [potentially] BASIC.
|
||||||
if(data_catalogue->files[c].data.size() >= 128 && !((data_catalogue->files[c].data[18] >> 1) & 7)) {
|
if(data_catalogue->files[c].data.size() >= 128 && !((data_catalogue->files[c].data[18] >> 1) & 7)) {
|
||||||
basic_files++;
|
basic_files++;
|
||||||
@ -64,10 +70,17 @@ static void InspectDataCatalogue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void InspectSystemCatalogue(
|
static void InspectSystemCatalogue(
|
||||||
const std::unique_ptr<Storage::Disk::CPM::Catalogue> &data_catalogue,
|
const std::shared_ptr<Storage::Disk::Disk> &disk,
|
||||||
|
const std::unique_ptr<Storage::Disk::CPM::Catalogue> &catalogue,
|
||||||
StaticAnalyser::Target &target) {
|
StaticAnalyser::Target &target) {
|
||||||
// If this is a system disk, then launch it as though it were CP/M.
|
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||||
target.loadingCommand = "|cpm\n";
|
// Check that the boot sector exists.
|
||||||
|
if(parser.get_sector(0, 0, 0x41) != nullptr) {
|
||||||
|
// This is a system disk, then launch it as though it were CP/M.
|
||||||
|
target.loadingCommand = "|cpm\n";
|
||||||
|
} else {
|
||||||
|
InspectDataCatalogue(catalogue, target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StaticAnalyser::AmstradCPC::AddTargets(
|
void StaticAnalyser::AmstradCPC::AddTargets(
|
||||||
@ -114,7 +127,7 @@ void StaticAnalyser::AmstradCPC::AddTargets(
|
|||||||
|
|
||||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), system_format);
|
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target.disks.front(), system_format);
|
||||||
if(system_catalogue) {
|
if(system_catalogue) {
|
||||||
InspectSystemCatalogue(data_catalogue, target);
|
InspectSystemCatalogue(target.disks.front(), data_catalogue, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,6 +266,26 @@ void Parser::seek_to_track(uint8_t track) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Sector> Parser::get_sector(uint8_t head, uint8_t track, uint8_t sector) {
|
std::shared_ptr<Sector> Parser::get_sector(uint8_t head, uint8_t track, uint8_t sector) {
|
||||||
|
// Switch head and track if necessary.
|
||||||
|
if(head_ != head) {
|
||||||
|
drive_->set_head(head);
|
||||||
|
invalidate_track();
|
||||||
|
}
|
||||||
|
seek_to_track(track);
|
||||||
|
int track_index = get_index(head, track, 0);
|
||||||
|
|
||||||
|
// Populate the sector cache if it's not already populated.
|
||||||
|
if(decoded_tracks_.find(track_index) == decoded_tracks_.end()) {
|
||||||
|
std::shared_ptr<Sector> first_sector = get_next_sector();
|
||||||
|
if(first_sector) {
|
||||||
|
while(1) {
|
||||||
|
std::shared_ptr<Sector> next_sector = get_next_sector();
|
||||||
|
if(!next_sector || next_sector->sector == first_sector->sector) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decoded_tracks_.insert(track_index);
|
||||||
|
}
|
||||||
|
|
||||||
// Check cache for sector.
|
// Check cache for sector.
|
||||||
int index = get_index(head, track, sector);
|
int index = get_index(head, track, sector);
|
||||||
auto cached_sector = sectors_by_index_.find(index);
|
auto cached_sector = sectors_by_index_.find(index);
|
||||||
@ -273,14 +293,8 @@ std::shared_ptr<Sector> Parser::get_sector(uint8_t head, uint8_t track, uint8_t
|
|||||||
return cached_sector->second;
|
return cached_sector->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failing that, set the proper head and track, and search for the sector. get_sector automatically
|
// If it wasn't found, it doesn't exist.
|
||||||
// inserts everything found into sectors_by_index_.
|
return nullptr;
|
||||||
if(head_ != head) {
|
|
||||||
drive_->set_head(head);
|
|
||||||
invalidate_track();
|
|
||||||
}
|
|
||||||
seek_to_track(track);
|
|
||||||
return get_sector(sector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> Parser::get_track(uint8_t track) {
|
std::vector<uint8_t> Parser::get_track(uint8_t track) {
|
||||||
|
@ -140,6 +140,7 @@ class Parser: public Storage::Disk::Controller {
|
|||||||
std::vector<uint8_t> get_track();
|
std::vector<uint8_t> get_track();
|
||||||
|
|
||||||
std::map<int, std::shared_ptr<Storage::Encodings::MFM::Sector>> sectors_by_index_;
|
std::map<int, std::shared_ptr<Storage::Encodings::MFM::Sector>> sectors_by_index_;
|
||||||
|
std::set<int> decoded_tracks_;
|
||||||
int get_index(uint8_t head, uint8_t track, uint8_t sector);
|
int get_index(uint8_t head, uint8_t track, uint8_t sector);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,9 +80,8 @@ uint16_t FileHolder::fgetc16be() {
|
|||||||
void FileHolder::ensure_file_is_at_least_length(long length) {
|
void FileHolder::ensure_file_is_at_least_length(long length) {
|
||||||
fseek(file_, 0, SEEK_END);
|
fseek(file_, 0, SEEK_END);
|
||||||
long bytes_to_write = length - ftell(file_);
|
long bytes_to_write = length - ftell(file_);
|
||||||
if(bytes_to_write > 0)
|
if(bytes_to_write > 0) {
|
||||||
{
|
uint8_t *empty = new uint8_t[(size_t)bytes_to_write];
|
||||||
uint8_t *empty = new uint8_t[bytes_to_write];
|
|
||||||
memset(empty, 0, (size_t)bytes_to_write);
|
memset(empty, 0, (size_t)bytes_to_write);
|
||||||
fwrite(empty, sizeof(uint8_t), (size_t)bytes_to_write, file_);
|
fwrite(empty, sizeof(uint8_t), (size_t)bytes_to_write, file_);
|
||||||
delete[] empty;
|
delete[] empty;
|
||||||
|
@ -43,10 +43,12 @@ void TimedEventLoop::set_next_event_time_interval(Time interval) {
|
|||||||
(int64_t)subcycles_until_event_.clock_rate * (int64_t)input_clock_rate_ * (int64_t)interval.length +
|
(int64_t)subcycles_until_event_.clock_rate * (int64_t)input_clock_rate_ * (int64_t)interval.length +
|
||||||
(int64_t)interval.clock_rate * (int64_t)subcycles_until_event_.length;
|
(int64_t)interval.clock_rate * (int64_t)subcycles_until_event_.length;
|
||||||
|
|
||||||
// Simplify now, to prepare for stuffing into possibly 32-bit quantities
|
// Simplify if necessary.
|
||||||
int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator);
|
if(denominator > std::numeric_limits<unsigned int>::max()) {
|
||||||
denominator /= common_divisor;
|
int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator);
|
||||||
numerator /= common_divisor;
|
denominator /= common_divisor;
|
||||||
|
numerator /= common_divisor;
|
||||||
|
}
|
||||||
|
|
||||||
// So this event will fire in the integral number of cycles from now, putting us at the remainder
|
// So this event will fire in the integral number of cycles from now, putting us at the remainder
|
||||||
// number of subcycles
|
// number of subcycles
|
||||||
|
Loading…
Reference in New Issue
Block a user