mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #213 from TomHarte/MinorPalette
Makes a variety of minor performance improvements to the CPC
This commit is contained in:
commit
daeaa4752f
@ -91,6 +91,8 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate, int clock_rate_multipli
|
||||
void i8272::run_for(Cycles cycles) {
|
||||
Storage::Disk::MFMController::run_for(cycles);
|
||||
|
||||
if(is_sleeping_) return;
|
||||
|
||||
// check for an expired timer
|
||||
if(delay_time_ > 0) {
|
||||
if(cycles.as_int() >= delay_time_) {
|
||||
@ -103,6 +105,7 @@ void i8272::run_for(Cycles cycles) {
|
||||
|
||||
// update seek status of any drives presently seeking
|
||||
if(drives_seeking_) {
|
||||
int drives_left = drives_seeking_;
|
||||
for(int c = 0; c < 4; c++) {
|
||||
if(drives_[c].phase == Drive::Seeking) {
|
||||
drives_[c].step_rate_counter += cycles.as_int();
|
||||
@ -122,27 +125,35 @@ void i8272::run_for(Cycles cycles) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
drives_left--;
|
||||
if(!drives_left) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for any head unloads
|
||||
if(head_timers_running_) {
|
||||
for(int c = 0; c < 4; c++) {
|
||||
for(int h = 0; h < 2; h++) {
|
||||
if(drives_[c].head_unload_delay[c] > 0) {
|
||||
if(cycles.as_int() >= drives_[c].head_unload_delay[c]) {
|
||||
drives_[c].head_unload_delay[c] = 0;
|
||||
drives_[c].head_is_loaded[c] = false;
|
||||
head_timers_running_--;
|
||||
if(!head_timers_running_) return;
|
||||
} else {
|
||||
drives_[c].head_unload_delay[c] -= cycles.as_int();
|
||||
}
|
||||
int timers_left = head_timers_running_;
|
||||
for(int c = 0; c < 8; c++) {
|
||||
int drive = (c >> 1);
|
||||
int head = c&1;
|
||||
|
||||
if(drives_[drive].head_unload_delay[head] > 0) {
|
||||
if(cycles.as_int() >= drives_[drive].head_unload_delay[head]) {
|
||||
drives_[drive].head_unload_delay[head] = 0;
|
||||
drives_[drive].head_is_loaded[head] = false;
|
||||
head_timers_running_--;
|
||||
} else {
|
||||
drives_[drive].head_unload_delay[head] -= cycles.as_int();
|
||||
}
|
||||
timers_left--;
|
||||
if(!timers_left) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_sleeping_ = !delay_time_ && !drives_seeking_ && !head_timers_running_;
|
||||
}
|
||||
|
||||
void i8272::set_register(int address, uint8_t value) {
|
||||
@ -187,7 +198,7 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
||||
|
||||
#define MS_TO_CYCLES(x) x * 8000
|
||||
#define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = (int)mask; return; case __LINE__:
|
||||
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = (int)Event8272::Timer; delay_time_ = MS_TO_CYCLES(ms); case __LINE__: if(delay_time_) return;
|
||||
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = (int)Event8272::Timer; delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; case __LINE__: if(delay_time_) return;
|
||||
|
||||
#define PASTE(x, y) x##y
|
||||
#define CONCAT(x, y) PASTE(x, y)
|
||||
@ -245,6 +256,7 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
||||
if(drives_[active_drive_].head_is_loaded[active_head_]) {\
|
||||
if(drives_[active_drive_].head_unload_delay[active_head_] == 0) { \
|
||||
head_timers_running_++; \
|
||||
is_sleeping_ = false; \
|
||||
} \
|
||||
drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\
|
||||
}
|
||||
@ -698,6 +710,7 @@ void i8272::posit_event(int event_type) {
|
||||
// Increment the seeking count if this drive wasn't already seeking.
|
||||
if(drives_[drive].phase != Drive::Seeking) {
|
||||
drives_seeking_++;
|
||||
is_sleeping_ = false;
|
||||
}
|
||||
|
||||
// Set currently seeking, with a step to occur right now (yes, it sounds like jamming these
|
||||
|
@ -102,7 +102,8 @@ class i8272: public Storage::Disk::MFMController {
|
||||
Drive() :
|
||||
head_position(0), phase(NotSeeking),
|
||||
drive(new Storage::Disk::Drive),
|
||||
head_is_loaded{false, false} {};
|
||||
head_is_loaded{false, false},
|
||||
head_unload_delay{0, 0} {};
|
||||
} drives_[4];
|
||||
int drives_seeking_;
|
||||
|
||||
@ -127,6 +128,8 @@ class i8272: public Storage::Disk::MFMController {
|
||||
// Internal registers.
|
||||
uint8_t cylinder_, head_, sector_, size_;
|
||||
|
||||
// Master switch on not performing any work.
|
||||
bool is_sleeping_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -159,7 +159,8 @@ class CRTCBusHandler {
|
||||
mode_(2),
|
||||
next_mode_(2),
|
||||
cycles_into_hsync_(0) {
|
||||
build_mode_tables();
|
||||
establish_palette_hits();
|
||||
build_mode_table();
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -271,6 +272,7 @@ class CRTCBusHandler {
|
||||
case 1: pixel_divider_ = 2; break;
|
||||
case 2: pixel_divider_ = 1; break;
|
||||
}
|
||||
build_mode_table();
|
||||
}
|
||||
|
||||
interrupt_timer_.signal_hsync();
|
||||
@ -339,8 +341,7 @@ class CRTCBusHandler {
|
||||
border_ = mapped_palette_value(colour);
|
||||
} else {
|
||||
palette_[pen_] = mapped_palette_value(colour);
|
||||
// TODO: no need for a full regeneration, of every mode, every time
|
||||
build_mode_tables();
|
||||
patch_mode_table(pen_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,38 +352,129 @@ class CRTCBusHandler {
|
||||
crt_->output_level(length * 16);
|
||||
}
|
||||
|
||||
void build_mode_tables() {
|
||||
#define Mode0Colour0(c) ((c & 0x80) >> 7) | ((c & 0x20) >> 3) | ((c & 0x08) >> 2) | ((c & 0x02) << 2)
|
||||
#define Mode0Colour1(c) ((c & 0x40) >> 6) | ((c & 0x10) >> 2) | ((c & 0x04) >> 1) | ((c & 0x01) << 3)
|
||||
|
||||
#define Mode1Colour0(c) ((c & 0x80) >> 7) | ((c & 0x08) >> 2)
|
||||
#define Mode1Colour1(c) ((c & 0x40) >> 6) | ((c & 0x04) >> 1)
|
||||
#define Mode1Colour2(c) ((c & 0x20) >> 5) | ((c & 0x02) >> 0)
|
||||
#define Mode1Colour3(c) ((c & 0x10) >> 4) | ((c & 0x01) << 1)
|
||||
|
||||
#define Mode3Colour0(c) ((c & 0x80) >> 7) | ((c & 0x08) >> 2)
|
||||
#define Mode3Colour1(c) ((c & 0x40) >> 6) | ((c & 0x04) >> 1)
|
||||
|
||||
void establish_palette_hits() {
|
||||
for(int c = 0; c < 256; c++) {
|
||||
// prepare mode 0
|
||||
uint8_t *mode0_pixels = (uint8_t *)&mode0_output_[c];
|
||||
mode0_pixels[0] = palette_[((c & 0x80) >> 7) | ((c & 0x20) >> 3) | ((c & 0x08) >> 2) | ((c & 0x02) << 2)];
|
||||
mode0_pixels[1] = palette_[((c & 0x40) >> 6) | ((c & 0x10) >> 2) | ((c & 0x04) >> 1) | ((c & 0x01) << 3)];
|
||||
mode0_palette_hits_[Mode0Colour0(c)].push_back((uint8_t)c);
|
||||
mode0_palette_hits_[Mode0Colour1(c)].push_back((uint8_t)c);
|
||||
|
||||
// prepare mode 1
|
||||
uint8_t *mode1_pixels = (uint8_t *)&mode1_output_[c];
|
||||
mode1_pixels[0] = palette_[((c & 0x80) >> 7) | ((c & 0x08) >> 2)];
|
||||
mode1_pixels[1] = palette_[((c & 0x40) >> 6) | ((c & 0x04) >> 1)];
|
||||
mode1_pixels[2] = palette_[((c & 0x20) >> 5) | ((c & 0x02) >> 0)];
|
||||
mode1_pixels[3] = palette_[((c & 0x10) >> 4) | ((c & 0x01) << 1)];
|
||||
mode1_palette_hits_[Mode1Colour0(c)].push_back((uint8_t)c);
|
||||
mode1_palette_hits_[Mode1Colour1(c)].push_back((uint8_t)c);
|
||||
mode1_palette_hits_[Mode1Colour2(c)].push_back((uint8_t)c);
|
||||
mode1_palette_hits_[Mode1Colour3(c)].push_back((uint8_t)c);
|
||||
|
||||
// prepare mode 2
|
||||
uint8_t *mode2_pixels = (uint8_t *)&mode2_output_[c];
|
||||
mode2_pixels[0] = palette_[((c & 0x80) >> 7)];
|
||||
mode2_pixels[1] = palette_[((c & 0x40) >> 6)];
|
||||
mode2_pixels[2] = palette_[((c & 0x20) >> 5)];
|
||||
mode2_pixels[3] = palette_[((c & 0x10) >> 4)];
|
||||
mode2_pixels[4] = palette_[((c & 0x08) >> 3)];
|
||||
mode2_pixels[5] = palette_[((c & 0x04) >> 2)];
|
||||
mode2_pixels[6] = palette_[((c & 0x03) >> 1)];
|
||||
mode2_pixels[7] = palette_[((c & 0x01) >> 0)];
|
||||
|
||||
// prepare mode 3
|
||||
uint8_t *mode3_pixels = (uint8_t *)&mode3_output_[c];
|
||||
mode3_pixels[0] = palette_[((c & 0x80) >> 7) | ((c & 0x08) >> 2)];
|
||||
mode3_pixels[1] = palette_[((c & 0x40) >> 6) | ((c & 0x04) >> 1)];
|
||||
mode3_palette_hits_[Mode3Colour0(c)].push_back((uint8_t)c);
|
||||
mode3_palette_hits_[Mode3Colour1(c)].push_back((uint8_t)c);
|
||||
}
|
||||
}
|
||||
|
||||
void build_mode_table() {
|
||||
switch(mode_) {
|
||||
case 0:
|
||||
// Mode 0: abcdefgh -> [gcea] [hdfb]
|
||||
for(int c = 0; c < 256; c++) {
|
||||
// prepare mode 0
|
||||
uint8_t *mode0_pixels = (uint8_t *)&mode0_output_[c];
|
||||
mode0_pixels[0] = palette_[Mode0Colour0(c)];
|
||||
mode0_pixels[1] = palette_[Mode0Colour1(c)];
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
for(int c = 0; c < 256; c++) {
|
||||
// prepare mode 1
|
||||
uint8_t *mode1_pixels = (uint8_t *)&mode1_output_[c];
|
||||
mode1_pixels[0] = palette_[Mode1Colour0(c)];
|
||||
mode1_pixels[1] = palette_[Mode1Colour1(c)];
|
||||
mode1_pixels[2] = palette_[Mode1Colour2(c)];
|
||||
mode1_pixels[3] = palette_[Mode1Colour3(c)];
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
for(int c = 0; c < 256; c++) {
|
||||
// prepare mode 2
|
||||
uint8_t *mode2_pixels = (uint8_t *)&mode2_output_[c];
|
||||
mode2_pixels[0] = palette_[((c & 0x80) >> 7)];
|
||||
mode2_pixels[1] = palette_[((c & 0x40) >> 6)];
|
||||
mode2_pixels[2] = palette_[((c & 0x20) >> 5)];
|
||||
mode2_pixels[3] = palette_[((c & 0x10) >> 4)];
|
||||
mode2_pixels[4] = palette_[((c & 0x08) >> 3)];
|
||||
mode2_pixels[5] = palette_[((c & 0x04) >> 2)];
|
||||
mode2_pixels[6] = palette_[((c & 0x03) >> 1)];
|
||||
mode2_pixels[7] = palette_[((c & 0x01) >> 0)];
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for(int c = 0; c < 256; c++) {
|
||||
// prepare mode 3
|
||||
uint8_t *mode3_pixels = (uint8_t *)&mode3_output_[c];
|
||||
mode3_pixels[0] = palette_[Mode3Colour0(c)];
|
||||
mode3_pixels[1] = palette_[Mode3Colour1(c)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void patch_mode_table(int pen) {
|
||||
switch(mode_) {
|
||||
case 0: {
|
||||
for(uint8_t c : mode0_palette_hits_[pen]) {
|
||||
uint8_t *mode0_pixels = (uint8_t *)&mode0_output_[c];
|
||||
mode0_pixels[0] = palette_[Mode0Colour0(c)];
|
||||
mode0_pixels[1] = palette_[Mode0Colour1(c)];
|
||||
}
|
||||
} break;
|
||||
case 1:
|
||||
if(pen > 3) return;
|
||||
for(uint8_t c : mode1_palette_hits_[pen]) {
|
||||
uint8_t *mode1_pixels = (uint8_t *)&mode1_output_[c];
|
||||
mode1_pixels[0] = palette_[Mode1Colour0(c)];
|
||||
mode1_pixels[1] = palette_[Mode1Colour1(c)];
|
||||
mode1_pixels[2] = palette_[Mode1Colour2(c)];
|
||||
mode1_pixels[3] = palette_[Mode1Colour3(c)];
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if(pen > 1) return;
|
||||
// Whichever pen this is, there's only one table entry it doesn't touch, so just
|
||||
// rebuild the whole thing.
|
||||
build_mode_table();
|
||||
break;
|
||||
case 3:
|
||||
if(pen > 3) return;
|
||||
// Same argument applies here as to case 1, as the unused bits aren't masked out.
|
||||
for(uint8_t c : mode3_palette_hits_[pen]) {
|
||||
uint8_t *mode3_pixels = (uint8_t *)&mode3_output_[c];
|
||||
mode3_pixels[0] = palette_[Mode3Colour0(c)];
|
||||
mode3_pixels[1] = palette_[Mode3Colour1(c)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef Mode0Colour0
|
||||
#undef Mode0Colour1
|
||||
|
||||
#undef Mode1Colour0
|
||||
#undef Mode1Colour1
|
||||
#undef Mode1Colour2
|
||||
#undef Mode1Colour3
|
||||
|
||||
#undef Mode3Colour0
|
||||
#undef Mode3Colour1
|
||||
|
||||
uint8_t mapped_palette_value(uint8_t colour) {
|
||||
#define COL(r, g, b) (r << 4) | (g << 2) | b
|
||||
static const uint8_t mapping[32] = {
|
||||
@ -417,6 +509,10 @@ class CRTCBusHandler {
|
||||
uint64_t mode2_output_[256];
|
||||
uint16_t mode3_output_[256];
|
||||
|
||||
std::vector<uint8_t> mode0_palette_hits_[16];
|
||||
std::vector<uint8_t> mode1_palette_hits_[4];
|
||||
std::vector<uint8_t> mode3_palette_hits_[4];
|
||||
|
||||
int pen_;
|
||||
uint8_t palette_[16];
|
||||
uint8_t border_;
|
||||
|
@ -107,12 +107,21 @@ class TypeRecipient: public Typer::Delegate {
|
||||
*/
|
||||
void typer_reset(Typer *typer) {
|
||||
clear_all_keys();
|
||||
|
||||
// It's unsafe to deallocate typer right now, since it is the caller, but also it has a small
|
||||
// memory footprint and it's desireable not to imply that the subclass need call it any more.
|
||||
// So shuffle it off into a siding.
|
||||
previous_typer_ = std::move(typer_);
|
||||
typer_ = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual HalfCycles get_typer_delay() { return HalfCycles(0); }
|
||||
virtual HalfCycles get_typer_frequency() { return HalfCycles(0); }
|
||||
std::unique_ptr<Typer> typer_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Typer> previous_typer_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,20 +12,22 @@
|
||||
using namespace Storage::Disk;
|
||||
|
||||
Drive::Drive()
|
||||
: head_position_(0), head_(0) {}
|
||||
: head_position_(0), head_(0), has_disk_(false) {}
|
||||
|
||||
void Drive::set_disk(const std::shared_ptr<Disk> &disk) {
|
||||
disk_ = disk;
|
||||
track_ = nullptr;
|
||||
has_disk_ = !!disk_;
|
||||
}
|
||||
|
||||
void Drive::set_disk_with_track(const std::shared_ptr<Track> &track) {
|
||||
disk_ = nullptr;
|
||||
track_ = track;
|
||||
has_disk_ = !!track_;
|
||||
}
|
||||
|
||||
bool Drive::has_disk() {
|
||||
return (bool)disk_ || (bool)track_;
|
||||
return has_disk_;
|
||||
}
|
||||
|
||||
bool Drive::get_is_track_zero() {
|
||||
|
@ -73,6 +73,7 @@ class Drive {
|
||||
private:
|
||||
std::shared_ptr<Track> track_;
|
||||
std::shared_ptr<Disk> disk_;
|
||||
bool has_disk_;
|
||||
int head_position_;
|
||||
unsigned int head_;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user