mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-23 18:29:16 +00:00
Expands size of storage in Cycles/HalfCycles; adjusts widely to compensate.
This commit is contained in:
parent
fd02b6fc18
commit
1c154131f9
@ -10,6 +10,7 @@
|
||||
#define ClockReceiver_hpp
|
||||
|
||||
#include "ForceInline.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
/*
|
||||
Informal pattern for all classes that run from a clock cycle:
|
||||
@ -54,7 +55,9 @@
|
||||
*/
|
||||
template <class T> class WrappedInt {
|
||||
public:
|
||||
forceinline constexpr WrappedInt(int l) noexcept : length_(l) {}
|
||||
using IntType = int64_t;
|
||||
|
||||
forceinline constexpr WrappedInt(IntType l) noexcept : length_(l) {}
|
||||
forceinline constexpr WrappedInt() noexcept : length_(0) {}
|
||||
|
||||
forceinline T &operator =(const T &rhs) {
|
||||
@ -133,7 +136,11 @@ template <class T> class WrappedInt {
|
||||
forceinline constexpr bool operator !() const { return !length_; }
|
||||
// bool operator () is not supported because it offers an implicit cast to int, which is prone silently to permit misuse
|
||||
|
||||
forceinline constexpr int as_int() const { return length_; }
|
||||
/// @returns The underlying int, cast to an integral type of your choosing.
|
||||
template<typename Type = IntType> forceinline constexpr Type as() { return Type(length_); }
|
||||
|
||||
/// @returns The underlying int, in its native form.
|
||||
forceinline constexpr IntType as_integral() const { return length_; }
|
||||
|
||||
/*!
|
||||
Severs from @c this the effect of dividing by @c divisor; @c this will end up with
|
||||
@ -161,13 +168,13 @@ template <class T> class WrappedInt {
|
||||
// classes that use this template.
|
||||
|
||||
protected:
|
||||
int length_;
|
||||
IntType length_;
|
||||
};
|
||||
|
||||
/// Describes an integer number of whole cycles: pairs of clock signal transitions.
|
||||
class Cycles: public WrappedInt<Cycles> {
|
||||
public:
|
||||
forceinline constexpr Cycles(int l) noexcept : WrappedInt<Cycles>(l) {}
|
||||
forceinline constexpr Cycles(IntType l) noexcept : WrappedInt<Cycles>(l) {}
|
||||
forceinline constexpr Cycles() noexcept : WrappedInt<Cycles>() {}
|
||||
forceinline constexpr Cycles(const Cycles &cycles) noexcept : WrappedInt<Cycles>(cycles.length_) {}
|
||||
|
||||
@ -187,10 +194,10 @@ class Cycles: public WrappedInt<Cycles> {
|
||||
/// Describes an integer number of half cycles: single clock signal transitions.
|
||||
class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
public:
|
||||
forceinline constexpr HalfCycles(int l) noexcept : WrappedInt<HalfCycles>(l) {}
|
||||
forceinline constexpr HalfCycles(IntType l) noexcept : WrappedInt<HalfCycles>(l) {}
|
||||
forceinline constexpr HalfCycles() noexcept : WrappedInt<HalfCycles>() {}
|
||||
|
||||
forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_int() * 2) {}
|
||||
forceinline constexpr HalfCycles(const Cycles &cycles) noexcept : WrappedInt<HalfCycles>(cycles.as_integral() * 2) {}
|
||||
forceinline constexpr HalfCycles(const HalfCycles &half_cycles) noexcept : WrappedInt<HalfCycles>(half_cycles.length_) {}
|
||||
|
||||
/// @returns The number of whole cycles completely covered by this span of half cycles.
|
||||
|
@ -107,7 +107,7 @@ void WD1770::run_for(const Cycles cycles) {
|
||||
Storage::Disk::Controller::run_for(cycles);
|
||||
|
||||
if(delay_time_) {
|
||||
unsigned int number_of_cycles = static_cast<unsigned int>(cycles.as_int());
|
||||
const auto number_of_cycles = cycles.as_integral();
|
||||
if(delay_time_ <= number_of_cycles) {
|
||||
delay_time_ = 0;
|
||||
posit_event(static_cast<int>(Event1770::Timer));
|
||||
@ -286,7 +286,7 @@ void WD1770::posit_event(int new_event_type) {
|
||||
goto verify;
|
||||
}
|
||||
get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1));
|
||||
unsigned int time_to_wait;
|
||||
Cycles::IntType time_to_wait;
|
||||
switch(command_ & 3) {
|
||||
default:
|
||||
case 0: time_to_wait = 6; break;
|
||||
|
@ -121,7 +121,7 @@ class WD1770: public Storage::Disk::MFMController {
|
||||
void posit_event(int type);
|
||||
int interesting_event_mask_;
|
||||
int resume_point_ = 0;
|
||||
unsigned int delay_time_ = 0;
|
||||
Cycles::IntType delay_time_ = 0;
|
||||
|
||||
// ID buffer
|
||||
uint8_t header_[6];
|
||||
|
@ -346,7 +346,7 @@ template <typename T> void MOS6522<T>::do_phase1() {
|
||||
|
||||
/*! Runs for a specified number of half cycles. */
|
||||
template <typename T> void MOS6522<T>::run_for(const HalfCycles half_cycles) {
|
||||
int number_of_half_cycles = half_cycles.as_int();
|
||||
auto number_of_half_cycles = half_cycles.as_integral();
|
||||
if(!number_of_half_cycles) return;
|
||||
|
||||
if(is_phase2_) {
|
||||
@ -375,7 +375,7 @@ template <typename T> void MOS6522<T>::flush() {
|
||||
|
||||
/*! Runs for a specified number of cycles. */
|
||||
template <typename T> void MOS6522<T>::run_for(const Cycles cycles) {
|
||||
int number_of_cycles = cycles.as_int();
|
||||
auto number_of_cycles = cycles.as_integral();
|
||||
while(number_of_cycles--) {
|
||||
do_phase1();
|
||||
do_phase2();
|
||||
|
@ -107,7 +107,7 @@ template <class T> class MOS6532 {
|
||||
}
|
||||
|
||||
inline void run_for(const Cycles cycles) {
|
||||
unsigned int number_of_cycles = static_cast<unsigned int>(cycles.as_int());
|
||||
unsigned int number_of_cycles = static_cast<unsigned int>(cycles.as_integral());
|
||||
|
||||
// permit counting _to_ zero; counting _through_ zero initiates the other behaviour
|
||||
if(timer_.value >= number_of_cycles) {
|
||||
|
@ -170,7 +170,7 @@ template <class BusHandler> class MOS6560 {
|
||||
// keep track of the amount of time since the speaker was updated; lazy updates are applied
|
||||
cycles_since_speaker_update_ += cycles;
|
||||
|
||||
int number_of_cycles = cycles.as_int();
|
||||
auto number_of_cycles = cycles.as_integral();
|
||||
while(number_of_cycles--) {
|
||||
// keep an old copy of the vertical count because that test is a cycle later than the actual changes
|
||||
int previous_vertical_counter = vertical_counter_;
|
||||
|
@ -111,7 +111,7 @@ template <class T> class CRTC6845 {
|
||||
}
|
||||
|
||||
void run_for(Cycles cycles) {
|
||||
int cyles_remaining = cycles.as_int();
|
||||
auto cyles_remaining = cycles.as_integral();
|
||||
while(cyles_remaining--) {
|
||||
// check for end of visible characters
|
||||
if(character_counter_ == registers_[1]) {
|
||||
|
@ -15,8 +15,8 @@ const HalfCycles ACIA::SameAsTransmit;
|
||||
ACIA::ACIA(HalfCycles transmit_clock_rate, HalfCycles receive_clock_rate) :
|
||||
transmit_clock_rate_(transmit_clock_rate),
|
||||
receive_clock_rate_((receive_clock_rate != SameAsTransmit) ? receive_clock_rate : transmit_clock_rate) {
|
||||
transmit.set_writer_clock_rate(transmit_clock_rate.as_int());
|
||||
request_to_send.set_writer_clock_rate(transmit_clock_rate.as_int());
|
||||
transmit.set_writer_clock_rate(transmit_clock_rate);
|
||||
request_to_send.set_writer_clock_rate(transmit_clock_rate);
|
||||
}
|
||||
|
||||
uint8_t ACIA::read(int address) {
|
||||
@ -84,7 +84,7 @@ void ACIA::write(int address, uint8_t value) {
|
||||
transmit.write(false);
|
||||
break;
|
||||
}
|
||||
receive.set_read_delegate(this, Storage::Time(divider_ * 2, receive_clock_rate_.as_int()));
|
||||
receive.set_read_delegate(this, Storage::Time(divider_ * 2, int(receive_clock_rate_.as_integral())));
|
||||
receive_interrupt_enabled_ = value & 0x80;
|
||||
}
|
||||
}
|
||||
@ -92,27 +92,24 @@ void ACIA::write(int address, uint8_t value) {
|
||||
}
|
||||
|
||||
void ACIA::run_for(HalfCycles length) {
|
||||
// Transmission.
|
||||
const int transmit_advance = length.as_int();
|
||||
|
||||
if(transmit.transmission_data_time_remaining()) {
|
||||
if(transmit.transmission_data_time_remaining() > HalfCycles(0)) {
|
||||
const auto write_data_time_remaining = transmit.write_data_time_remaining();
|
||||
|
||||
// There's at most one further byte available to enqueue, so a single 'if'
|
||||
// rather than a 'while' is correct here. It's the responsibilit of the caller
|
||||
// to ensure run_for lengths are appropriate for longer sequences.
|
||||
if(transmit_advance >= write_data_time_remaining) {
|
||||
if(length >= write_data_time_remaining) {
|
||||
if(next_transmission_ != NoValueMask) {
|
||||
transmit.advance_writer(write_data_time_remaining);
|
||||
consider_transmission();
|
||||
transmit.advance_writer(transmit_advance - write_data_time_remaining);
|
||||
transmit.advance_writer(length - write_data_time_remaining);
|
||||
} else {
|
||||
transmit.advance_writer(transmit_advance);
|
||||
transmit.advance_writer(length);
|
||||
update_clocking_observer();
|
||||
if(transmit_interrupt_enabled_) add_interrupt_cause(TransmitNeedsWrite);
|
||||
}
|
||||
} else {
|
||||
transmit.advance_writer(transmit_advance);
|
||||
transmit.advance_writer(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ void MFP68901::run_for(HalfCycles time) {
|
||||
cycles_left_ += time;
|
||||
|
||||
// TODO: this is the stupidest possible implementation. Improve.
|
||||
int cycles = cycles_left_.flush<Cycles>().as_int();
|
||||
int cycles = int(cycles_left_.flush<Cycles>().as_integral());
|
||||
while(cycles--) {
|
||||
for(int c = 0; c < 4; ++c) {
|
||||
if(timers_[c].mode >= TimerMode::Delay) {
|
||||
|
@ -95,11 +95,11 @@ void i8272::run_for(Cycles cycles) {
|
||||
|
||||
// check for an expired timer
|
||||
if(delay_time_ > 0) {
|
||||
if(cycles.as_int() >= delay_time_) {
|
||||
if(cycles.as_integral() >= delay_time_) {
|
||||
delay_time_ = 0;
|
||||
posit_event(static_cast<int>(Event8272::Timer));
|
||||
} else {
|
||||
delay_time_ -= cycles.as_int();
|
||||
delay_time_ -= cycles.as_integral();
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,8 +108,8 @@ void i8272::run_for(Cycles cycles) {
|
||||
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();
|
||||
int steps = drives_[c].step_rate_counter / (8000 * step_rate_time_);
|
||||
drives_[c].step_rate_counter += cycles.as_integral();
|
||||
auto steps = drives_[c].step_rate_counter / (8000 * step_rate_time_);
|
||||
drives_[c].step_rate_counter %= (8000 * step_rate_time_);
|
||||
while(steps--) {
|
||||
// Perform a step.
|
||||
@ -141,12 +141,12 @@ void i8272::run_for(Cycles cycles) {
|
||||
int head = c&1;
|
||||
|
||||
if(drives_[drive].head_unload_delay[head] > 0) {
|
||||
if(cycles.as_int() >= drives_[drive].head_unload_delay[head]) {
|
||||
if(cycles.as_integral() >= 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();
|
||||
drives_[drive].head_unload_delay[head] -= cycles.as_integral();
|
||||
}
|
||||
timers_left--;
|
||||
if(!timers_left) break;
|
||||
|
@ -73,7 +73,7 @@ class i8272 : public Storage::Disk::MFMController {
|
||||
bool is_access_command_ = false;
|
||||
|
||||
// The counter used for ::Timer events.
|
||||
int delay_time_ = 0;
|
||||
Cycles::IntType delay_time_ = 0;
|
||||
|
||||
// The connected drives.
|
||||
struct Drive {
|
||||
@ -89,12 +89,12 @@ class i8272 : public Storage::Disk::MFMController {
|
||||
bool seek_failed = false;
|
||||
|
||||
// Seeking: transient state.
|
||||
int step_rate_counter = 0;
|
||||
Cycles::IntType step_rate_counter = 0;
|
||||
int steps_taken = 0;
|
||||
int target_head_position = 0; // either an actual number, or -1 to indicate to step until track zero
|
||||
|
||||
// Head state.
|
||||
int head_unload_delay[2] = {0, 0};
|
||||
Cycles::IntType head_unload_delay[2] = {0, 0};
|
||||
bool head_is_loaded[2] = {false, false};
|
||||
|
||||
} drives_[4];
|
||||
|
@ -166,7 +166,7 @@ void TMS9918::run_for(const HalfCycles cycles) {
|
||||
// Convert 456 clocked half cycles per line to 342 internal cycles per line;
|
||||
// the internal clock is 1.5 times the nominal 3.579545 Mhz that I've advertised
|
||||
// for this part. So multiply by three quarters.
|
||||
int int_cycles = (cycles.as_int() * 3) + cycles_error_;
|
||||
int int_cycles = int(cycles.as_integral() * 3) + cycles_error_;
|
||||
cycles_error_ = int_cycles & 3;
|
||||
int_cycles >>= 2;
|
||||
if(!int_cycles) return;
|
||||
|
@ -78,7 +78,7 @@ void DiskII::select_drive(int drive) {
|
||||
void DiskII::run_for(const Cycles cycles) {
|
||||
if(preferred_clocking() == ClockingHint::Preference::None) return;
|
||||
|
||||
int integer_cycles = cycles.as_int();
|
||||
auto integer_cycles = cycles.as_integral();
|
||||
while(integer_cycles--) {
|
||||
const int address = (state_ & 0xf0) | inputs_ | ((shift_register_&0x80) >> 6);
|
||||
if(flux_duration_) {
|
||||
@ -124,7 +124,7 @@ void DiskII::run_for(const Cycles cycles) {
|
||||
// motor switch being flipped and the drive motor actually switching off.
|
||||
// This models that, accepting overrun as a risk.
|
||||
if(motor_off_time_ >= 0) {
|
||||
motor_off_time_ -= cycles.as_int();
|
||||
motor_off_time_ -= cycles.as_integral();
|
||||
if(motor_off_time_ < 0) {
|
||||
set_control(Control::Motor, false);
|
||||
}
|
||||
@ -266,7 +266,7 @@ int DiskII::read_address(int address) {
|
||||
break;
|
||||
case 0xf:
|
||||
if(!(inputs_ & input_mode))
|
||||
drives_[active_drive_].begin_writing(Storage::Time(1, clock_rate_), false);
|
||||
drives_[active_drive_].begin_writing(Storage::Time(1, int(clock_rate_)), false);
|
||||
inputs_ |= input_mode;
|
||||
break;
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class DiskII final:
|
||||
void process_event(const Storage::Disk::Drive::Event &event) override;
|
||||
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference preference) override;
|
||||
|
||||
const int clock_rate_ = 0;
|
||||
const Cycles::IntType clock_rate_ = 0;
|
||||
|
||||
uint8_t state_ = 0;
|
||||
uint8_t inputs_ = 0;
|
||||
@ -109,7 +109,7 @@ class DiskII final:
|
||||
|
||||
int stepper_mask_ = 0;
|
||||
int stepper_position_ = 0;
|
||||
int motor_off_time_ = -1;
|
||||
Cycles::IntType motor_off_time_ = -1;
|
||||
|
||||
bool is_write_protected();
|
||||
std::array<uint8_t, 256> state_machine_;
|
||||
|
@ -233,7 +233,7 @@ void IWM::run_for(const Cycles cycles) {
|
||||
}
|
||||
|
||||
// Activity otherwise depends on mode and motor state.
|
||||
int integer_cycles = cycles.as_int();
|
||||
auto integer_cycles = cycles.as_integral();
|
||||
switch(shift_mode_) {
|
||||
case ShiftMode::Reading: {
|
||||
// Per the IWM patent, column 7, around line 35 onwards: "The expected time
|
||||
@ -241,7 +241,7 @@ void IWM::run_for(const Cycles cycles) {
|
||||
// expected time since the data is not precisely spaced when read due to
|
||||
// variations in drive speed and other external factors". The error_margin
|
||||
// here implements the 'after' part of that contract.
|
||||
const auto error_margin = Cycles(bit_length_.as_int() >> 1);
|
||||
const auto error_margin = Cycles(bit_length_.as_integral() >> 1);
|
||||
|
||||
if(drive_is_rotating_[active_drive_]) {
|
||||
while(integer_cycles--) {
|
||||
@ -254,7 +254,7 @@ void IWM::run_for(const Cycles cycles) {
|
||||
} else {
|
||||
while(cycles_since_shift_ + integer_cycles >= bit_length_ + error_margin) {
|
||||
const auto run_length = bit_length_ + error_margin - cycles_since_shift_;
|
||||
integer_cycles -= run_length.as_int();
|
||||
integer_cycles -= run_length.as_integral();
|
||||
cycles_since_shift_ += run_length;
|
||||
propose_shift(0);
|
||||
}
|
||||
@ -272,7 +272,7 @@ void IWM::run_for(const Cycles cycles) {
|
||||
drives_[active_drive_]->write_bit(shift_register_ & 0x80);
|
||||
shift_register_ <<= 1;
|
||||
|
||||
integer_cycles -= cycles_until_write.as_int();
|
||||
integer_cycles -= cycles_until_write.as_integral();
|
||||
cycles_since_shift_ = Cycles(0);
|
||||
|
||||
--output_bits_remaining_;
|
||||
@ -333,7 +333,7 @@ void IWM::select_shift_mode() {
|
||||
|
||||
// If writing mode just began, set the drive into write mode and cue up the first output byte.
|
||||
if(drives_[active_drive_] && old_shift_mode != ShiftMode::Writing && shift_mode_ == ShiftMode::Writing) {
|
||||
drives_[active_drive_]->begin_writing(Storage::Time(1, clock_rate_ / bit_length_.as_int()), false);
|
||||
drives_[active_drive_]->begin_writing(Storage::Time(1, clock_rate_ / bit_length_.as_integral()), false);
|
||||
shift_register_ = next_output_;
|
||||
write_handshake_ |= 0x80 | 0x40;
|
||||
output_bits_remaining_ = 8;
|
||||
@ -369,7 +369,7 @@ void IWM::propose_shift(uint8_t bit) {
|
||||
// shift in a 1 and start a new window wherever the first found 1 was.
|
||||
//
|
||||
// If no 1s are found, shift in a 0 and don't alter expectations as to window placement.
|
||||
const auto error_margin = Cycles(bit_length_.as_int() >> 1);
|
||||
const auto error_margin = Cycles(bit_length_.as_integral() >> 1);
|
||||
if(bit && cycles_since_shift_ < error_margin) return;
|
||||
|
||||
shift_register_ = uint8_t((shift_register_ << 1) | bit);
|
||||
|
@ -10,18 +10,19 @@
|
||||
|
||||
using namespace Serial;
|
||||
|
||||
void Line::set_writer_clock_rate(int clock_rate) {
|
||||
void Line::set_writer_clock_rate(HalfCycles clock_rate) {
|
||||
clock_rate_ = clock_rate;
|
||||
}
|
||||
|
||||
void Line::advance_writer(int cycles) {
|
||||
if(!cycles) return;
|
||||
void Line::advance_writer(HalfCycles cycles) {
|
||||
if(cycles == HalfCycles(0)) return;
|
||||
|
||||
remaining_delays_ = std::max(remaining_delays_ - cycles, 0);
|
||||
const auto integral_cycles = cycles.as_integral();
|
||||
remaining_delays_ = std::max(remaining_delays_ - integral_cycles, Cycles::IntType(0));
|
||||
if(events_.empty()) {
|
||||
write_cycles_since_delegate_call_ += cycles;
|
||||
write_cycles_since_delegate_call_ += integral_cycles;
|
||||
if(transmission_extra_) {
|
||||
transmission_extra_ -= cycles;
|
||||
transmission_extra_ -= integral_cycles;
|
||||
if(transmission_extra_ <= 0) {
|
||||
transmission_extra_ = 0;
|
||||
update_delegate(level_);
|
||||
@ -29,7 +30,7 @@ void Line::advance_writer(int cycles) {
|
||||
}
|
||||
} else {
|
||||
while(!events_.empty()) {
|
||||
if(events_.front().delay <= cycles) {
|
||||
if(events_.front().delay <= integral_cycles) {
|
||||
cycles -= events_.front().delay;
|
||||
write_cycles_since_delegate_call_ += events_.front().delay;
|
||||
const auto old_level = level_;
|
||||
@ -51,8 +52,8 @@ void Line::advance_writer(int cycles) {
|
||||
transmission_extra_ = minimum_write_cycles_for_read_delegate_bit();
|
||||
}
|
||||
} else {
|
||||
events_.front().delay -= cycles;
|
||||
write_cycles_since_delegate_call_ += cycles;
|
||||
events_.front().delay -= integral_cycles;
|
||||
write_cycles_since_delegate_call_ += integral_cycles;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -69,26 +70,26 @@ void Line::write(bool level) {
|
||||
}
|
||||
}
|
||||
|
||||
void Line::write(int cycles, int count, int levels) {
|
||||
remaining_delays_ += count*cycles;
|
||||
void Line::write(HalfCycles cycles, int count, int levels) {
|
||||
remaining_delays_ += count * cycles.as_integral();
|
||||
|
||||
auto event = events_.size();
|
||||
events_.resize(events_.size() + size_t(count)*2);
|
||||
while(count--) {
|
||||
events_[event].type = Event::Delay;
|
||||
events_[event].delay = cycles;
|
||||
events_[event].delay = int(cycles.as_integral());
|
||||
events_[event+1].type = (levels&1) ? Event::SetHigh : Event::SetLow;
|
||||
levels >>= 1;
|
||||
event += 2;
|
||||
}
|
||||
}
|
||||
|
||||
int Line::write_data_time_remaining() {
|
||||
return remaining_delays_;
|
||||
HalfCycles Line::write_data_time_remaining() {
|
||||
return HalfCycles(remaining_delays_);
|
||||
}
|
||||
|
||||
int Line::transmission_data_time_remaining() {
|
||||
return remaining_delays_ + transmission_extra_;
|
||||
HalfCycles Line::transmission_data_time_remaining() {
|
||||
return HalfCycles(remaining_delays_ + transmission_extra_);
|
||||
}
|
||||
|
||||
void Line::reset_writing() {
|
||||
@ -125,7 +126,7 @@ void Line::update_delegate(bool level) {
|
||||
}
|
||||
|
||||
// Forward as many bits as occur.
|
||||
Storage::Time time_left(cycles_to_forward, clock_rate_);
|
||||
Storage::Time time_left(cycles_to_forward, int(clock_rate_.as_integral()));
|
||||
const int bit = level ? 1 : 0;
|
||||
int bits = 0;
|
||||
while(time_left >= time_left_in_bit_) {
|
||||
@ -141,7 +142,7 @@ void Line::update_delegate(bool level) {
|
||||
time_left_in_bit_ -= time_left;
|
||||
}
|
||||
|
||||
int Line::minimum_write_cycles_for_read_delegate_bit() {
|
||||
Cycles::IntType Line::minimum_write_cycles_for_read_delegate_bit() {
|
||||
if(!read_delegate_) return 0;
|
||||
return 1 + (read_delegate_bit_length_ * static_cast<unsigned int>(clock_rate_)).get<int>();
|
||||
return 1 + (read_delegate_bit_length_ * static_cast<unsigned int>(clock_rate_.as_integral())).get<int>();
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include "../../Storage/Storage.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
|
||||
namespace Serial {
|
||||
|
||||
@ -25,11 +26,11 @@ namespace Serial {
|
||||
*/
|
||||
class Line {
|
||||
public:
|
||||
void set_writer_clock_rate(int clock_rate);
|
||||
void set_writer_clock_rate(HalfCycles clock_rate);
|
||||
|
||||
/// Advances the read position by @c cycles relative to the writer's
|
||||
/// clock rate.
|
||||
void advance_writer(int cycles);
|
||||
void advance_writer(HalfCycles cycles);
|
||||
|
||||
/// Sets the line to @c level.
|
||||
void write(bool level);
|
||||
@ -40,14 +41,14 @@ class Line {
|
||||
/// is scheduled after the final output. The levels to output are
|
||||
/// taken from @c levels which is read from lsb to msb. @c cycles is
|
||||
/// relative to the writer's clock rate.
|
||||
void write(int cycles, int count, int levels);
|
||||
void write(HalfCycles cycles, int count, int levels);
|
||||
|
||||
/// @returns the number of cycles until currently enqueued write data is exhausted.
|
||||
int write_data_time_remaining();
|
||||
HalfCycles write_data_time_remaining();
|
||||
|
||||
/// @returns the number of cycles left until it is guaranteed that a passive reader
|
||||
/// has received all currently-enqueued bits.
|
||||
int transmission_data_time_remaining();
|
||||
HalfCycles transmission_data_time_remaining();
|
||||
|
||||
/// Eliminates all future write states, leaving the output at whatever it is now.
|
||||
void reset_writing();
|
||||
@ -77,10 +78,10 @@ class Line {
|
||||
int delay;
|
||||
};
|
||||
std::vector<Event> events_;
|
||||
int remaining_delays_ = 0;
|
||||
int transmission_extra_ = 0;
|
||||
HalfCycles::IntType remaining_delays_ = 0;
|
||||
HalfCycles::IntType transmission_extra_ = 0;
|
||||
bool level_ = true;
|
||||
int clock_rate_ = 0;
|
||||
HalfCycles clock_rate_ = 0;
|
||||
|
||||
ReadDelegate *read_delegate_ = nullptr;
|
||||
Storage::Time read_delegate_bit_length_, time_left_in_bit_;
|
||||
@ -91,7 +92,7 @@ class Line {
|
||||
} read_delegate_phase_ = ReadDelegatePhase::WaitingForZero;
|
||||
|
||||
void update_delegate(bool level);
|
||||
int minimum_write_cycles_for_read_delegate_bit();
|
||||
HalfCycles::IntType minimum_write_cycles_for_read_delegate_bit();
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -872,7 +872,7 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
|
||||
// TODO (in the player, not here): adapt it to accept an input clock rate and
|
||||
// run_for as HalfCycles
|
||||
if(!tape_player_is_sleeping_) tape_player_.run_for(cycle.length.as_int());
|
||||
if(!tape_player_is_sleeping_) tape_player_.run_for(cycle.length.as_integral());
|
||||
|
||||
// Pump the AY
|
||||
ay_.run_for(cycle.length);
|
||||
@ -1157,7 +1157,7 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
void flush_fdc() {
|
||||
// Clock the FDC, if connected, using a lazy scale by two
|
||||
if(has_fdc && !fdc_is_sleeping_) {
|
||||
fdc_.run_for(Cycles(time_since_fdc_update_.as_int()));
|
||||
fdc_.run_for(Cycles(time_since_fdc_update_.as_integral()));
|
||||
}
|
||||
time_since_fdc_update_ = HalfCycles(0);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ void DiskIICard::perform_bus_operation(Select select, bool is_read, uint16_t add
|
||||
|
||||
void DiskIICard::run_for(Cycles cycles, int stretches) {
|
||||
if(diskii_clocking_preference_ == ClockingHint::Preference::None) return;
|
||||
diskii_.run_for(Cycles(cycles.as_int() * 2));
|
||||
diskii_.run_for(Cycles(cycles.as_integral() * 2));
|
||||
}
|
||||
|
||||
void DiskIICard::set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive) {
|
||||
|
@ -284,7 +284,7 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
// Source: Have an Apple Split by Bob Bishop; http://rich12345.tripod.com/aiivideo/softalk.html
|
||||
|
||||
// Determine column at offset.
|
||||
int mapped_column = column_ + offset.as_int();
|
||||
int mapped_column = column_ + int(offset.as_integral());
|
||||
|
||||
// Map that backwards from the internal pixels-at-start generation to pixels-at-end
|
||||
// (so what was column 0 is now column 25).
|
||||
@ -315,7 +315,7 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
bool get_is_vertical_blank(Cycles offset) {
|
||||
// Map that backwards from the internal pixels-at-start generation to pixels-at-end
|
||||
// (so what was column 0 is now column 25).
|
||||
int mapped_column = column_ + offset.as_int();
|
||||
int mapped_column = column_ + int(offset.as_integral());
|
||||
|
||||
// Map that backwards from the internal pixels-at-start generation to pixels-at-end
|
||||
// (so what was column 0 is now column 25).
|
||||
@ -343,7 +343,7 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
|
||||
static const int first_sync_column = 49; // Also a guess.
|
||||
static const int sync_length = 4; // One of the two likely candidates.
|
||||
|
||||
int int_cycles = cycles.as_int();
|
||||
int int_cycles = int(cycles.as_integral());
|
||||
while(int_cycles) {
|
||||
const int cycles_this_line = std::min(65 - column_, int_cycles);
|
||||
const int ending_column = column_ + cycles_this_line;
|
||||
|
@ -576,7 +576,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
forceinline void advance_time(HalfCycles duration) {
|
||||
time_since_video_update_ += duration;
|
||||
iwm_ += duration;
|
||||
ram_subcycle_ = (ram_subcycle_ + duration.as_int()) & 15;
|
||||
ram_subcycle_ = (ram_subcycle_ + duration.as_integral()) & 15;
|
||||
|
||||
// The VIA runs at one-tenth of the 68000's clock speed, in sync with the E clock.
|
||||
// See: Guide to the Macintosh Hardware Family p149 (PDF p188). Some extra division
|
||||
@ -633,7 +633,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
|
||||
// Consider updating the real-time clock.
|
||||
real_time_clock_ += duration;
|
||||
auto ticks = real_time_clock_.divide_cycles(Cycles(CLOCK_RATE)).as_int();
|
||||
auto ticks = real_time_clock_.divide_cycles(Cycles(CLOCK_RATE)).as_integral();
|
||||
while(ticks--) {
|
||||
clock_.update();
|
||||
// TODO: leave a delay between toggling the input rather than using this coupled hack.
|
||||
@ -750,7 +750,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
void run_for(HalfCycles duration) {
|
||||
// The 6522 enjoys a divide-by-ten, so multiply back up here to make the
|
||||
// divided-by-two clock the audio works on.
|
||||
audio_.time_since_update += HalfCycles(duration.as_int() * 5);
|
||||
audio_.time_since_update += HalfCycles(duration.as_integral() * 5);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
|
@ -47,7 +47,7 @@ void Video::run_for(HalfCycles duration) {
|
||||
// the number of fetches.
|
||||
while(duration > HalfCycles(0)) {
|
||||
const auto pixel_start = frame_position_ % line_length;
|
||||
const int line = (frame_position_ / line_length).as_int();
|
||||
const int line = int((frame_position_ / line_length).as_integral());
|
||||
|
||||
const auto cycles_left_in_line = std::min(line_length - pixel_start, duration);
|
||||
|
||||
@ -62,8 +62,8 @@ void Video::run_for(HalfCycles duration) {
|
||||
//
|
||||
// Then 12 lines of border, 3 of sync, 11 more of border.
|
||||
|
||||
const int first_word = pixel_start.as_int() >> 4;
|
||||
const int final_word = (pixel_start + cycles_left_in_line).as_int() >> 4;
|
||||
const int first_word = int(pixel_start.as_integral()) >> 4;
|
||||
const int final_word = int((pixel_start + cycles_left_in_line).as_integral()) >> 4;
|
||||
|
||||
if(first_word != final_word) {
|
||||
if(line < 342) {
|
||||
@ -153,12 +153,12 @@ void Video::run_for(HalfCycles duration) {
|
||||
}
|
||||
|
||||
bool Video::vsync() {
|
||||
const int line = (frame_position_ / line_length).as_int();
|
||||
const auto line = (frame_position_ / line_length).as_integral();
|
||||
return line >= 353 && line < 356;
|
||||
}
|
||||
|
||||
HalfCycles Video::get_next_sequence_point() {
|
||||
const int line = (frame_position_ / line_length).as_int();
|
||||
const auto line = (frame_position_ / line_length).as_integral();
|
||||
if(line >= 353 && line < 356) {
|
||||
// Currently in vsync, so get time until start of line 357,
|
||||
// when vsync will end.
|
||||
|
@ -69,8 +69,8 @@ class Video {
|
||||
*/
|
||||
bool is_outputting(HalfCycles offset = HalfCycles(0)) {
|
||||
const auto offset_position = frame_position_ + offset % frame_length;
|
||||
const int column = (offset_position % line_length).as_int() >> 4;
|
||||
const int line = (offset_position / line_length).as_int();
|
||||
const int column = int((offset_position % line_length).as_integral()) >> 4;
|
||||
const int line = int((offset_position / line_length).as_integral());
|
||||
return line < 342 && column < 32;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ template<class T> class Cartridge:
|
||||
Adjusts @c confidence_counter according to the results of the most recent run_for.
|
||||
*/
|
||||
void apply_confidence(Analyser::Dynamic::ConfidenceCounter &confidence_counter) {
|
||||
if(cycle_count_.as_int() < 200) return;
|
||||
if(cycle_count_.as_integral() < 200) return;
|
||||
if(horizontal_counter_resets_ > 10)
|
||||
confidence_counter.add_miss();
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class Pitfall2: public BusExtender {
|
||||
|
||||
inline uint8_t update_audio() {
|
||||
const unsigned int clock_divisor = 57;
|
||||
int cycles_to_run_for = cycles_since_audio_update_.divide(clock_divisor).as_int();
|
||||
int cycles_to_run_for = int(cycles_since_audio_update_.divide(clock_divisor).as_integral());
|
||||
|
||||
int table_position = 0;
|
||||
for(int c = 0; c < 3; c++) {
|
||||
|
@ -143,7 +143,7 @@ void TIA::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
}
|
||||
|
||||
void TIA::run_for(const Cycles cycles) {
|
||||
int number_of_cycles = cycles.as_int();
|
||||
int number_of_cycles = int(cycles.as_integral());
|
||||
|
||||
// if part way through a line, definitely perform a partial, at most up to the end of the line
|
||||
if(horizontal_counter_) {
|
||||
@ -176,7 +176,7 @@ void TIA::reset_horizontal_counter() {
|
||||
}
|
||||
|
||||
int TIA::get_cycles_until_horizontal_blank(const Cycles from_offset) {
|
||||
return (cycles_per_line - (horizontal_counter_ + from_offset.as_int()) % cycles_per_line) % cycles_per_line;
|
||||
return (cycles_per_line - (horizontal_counter_ + from_offset.as_integral()) % cycles_per_line) % cycles_per_line;
|
||||
}
|
||||
|
||||
void TIA::set_background_colour(uint8_t colour) {
|
||||
|
@ -64,11 +64,11 @@ class IntelligentKeyboard:
|
||||
}
|
||||
|
||||
ClockingHint::Preference preferred_clocking() final {
|
||||
return output_line_.transmission_data_time_remaining() ? ClockingHint::Preference::RealTime : ClockingHint::Preference::None;
|
||||
return output_line_.transmission_data_time_remaining().as_integral() ? ClockingHint::Preference::RealTime : ClockingHint::Preference::None;
|
||||
}
|
||||
|
||||
void run_for(HalfCycles duration) {
|
||||
output_line_.advance_writer(duration.as_int());
|
||||
output_line_.advance_writer(duration);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -62,7 +62,7 @@ void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
}
|
||||
|
||||
void Video::run_for(HalfCycles duration) {
|
||||
int integer_duration = duration.as_int();
|
||||
int integer_duration = int(duration.as_integral());
|
||||
const auto mode_params = mode_params_for_mode();
|
||||
|
||||
#define Period(lower, upper, type) \
|
||||
|
@ -166,7 +166,7 @@ class ConcreteMachine:
|
||||
|
||||
// for the entire frame, RAM is accessible only on odd cycles; in modes below 4
|
||||
// it's also accessible only outside of the pixel regions
|
||||
cycles += video_output_.get_cycles_until_next_ram_availability(cycles_since_display_update_.as_int() + 1);
|
||||
cycles += video_output_.get_cycles_until_next_ram_availability(int(cycles_since_display_update_.as_integral()) + 1);
|
||||
} else {
|
||||
switch(address & 0xff0f) {
|
||||
case 0xfe00:
|
||||
|
@ -80,7 +80,7 @@ void Tape::run_for(const Cycles cycles) {
|
||||
TapePlayer::run_for(cycles);
|
||||
}
|
||||
} else {
|
||||
output_.cycles_into_pulse += static_cast<unsigned int>(cycles.as_int());
|
||||
output_.cycles_into_pulse += static_cast<unsigned int>(cycles.as_integral());
|
||||
while(output_.cycles_into_pulse > 1664) { // 1664 = the closest you can get to 1200 baud if you're looking for something
|
||||
output_.cycles_into_pulse -= 1664; // that divides the 125,000Hz clock that the sound divider runs off.
|
||||
push_tape_bit(1);
|
||||
|
@ -225,7 +225,7 @@ void VideoOutput::output_pixels(int number_of_cycles) {
|
||||
}
|
||||
|
||||
void VideoOutput::run_for(const Cycles cycles) {
|
||||
int number_of_cycles = cycles.as_int();
|
||||
int number_of_cycles = int(cycles.as_integral());
|
||||
output_position_ = (output_position_ + number_of_cycles) % cycles_per_frame;
|
||||
while(number_of_cycles) {
|
||||
int draw_action_length = screen_map_[screen_map_pointer_].length;
|
||||
|
@ -53,7 +53,7 @@ void DiskROM::run_for(HalfCycles half_cycles) {
|
||||
// Input clock is going to be 7159090/2 Mhz, but the drive controller
|
||||
// needs an 8Mhz clock, so scale up. 8000000/7159090 simplifies to
|
||||
// 800000/715909.
|
||||
controller_cycles_ += 800000 * half_cycles.as_int();
|
||||
controller_cycles_ += 800000 * half_cycles.as_integral();
|
||||
WD::WD1770::run_for(Cycles(static_cast<int>(controller_cycles_ / 715909)));
|
||||
controller_cycles_ %= 715909;
|
||||
}
|
||||
|
@ -599,7 +599,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
if(!tape_player_is_sleeping_)
|
||||
tape_player_.run_for(cycle.length.as_int());
|
||||
tape_player_.run_for(int(cycle.length.as_integral()));
|
||||
|
||||
if(time_until_interrupt_ > 0) {
|
||||
time_until_interrupt_ -= total_length;
|
||||
|
@ -15,7 +15,7 @@ namespace {
|
||||
// by comparing the amount of time this emulator took to show a directory versus a video of
|
||||
// a real Oric. It therefore assumes all other timing measurements were correct on the day
|
||||
// of the test. More work to do, I think.
|
||||
const int head_load_request_counter_target = 7653333;
|
||||
const Cycles::IntType head_load_request_counter_target = 7653333;
|
||||
}
|
||||
|
||||
Microdisc::Microdisc() : WD1770(P1793) {
|
||||
@ -114,7 +114,7 @@ void Microdisc::set_head_load_request(bool head_load) {
|
||||
|
||||
void Microdisc::run_for(const Cycles cycles) {
|
||||
if(head_load_request_counter_ < head_load_request_counter_target) {
|
||||
head_load_request_counter_ += cycles.as_int();
|
||||
head_load_request_counter_ += cycles.as_integral();
|
||||
if(head_load_request_counter_ >= head_load_request_counter_target) set_head_loaded(true);
|
||||
}
|
||||
WD::WD1770::run_for(cycles);
|
||||
|
@ -58,7 +58,7 @@ class Microdisc: public WD::WD1770 {
|
||||
size_t selected_drive_;
|
||||
bool irq_enable_ = false;
|
||||
int paging_flags_ = BASICDisable;
|
||||
int head_load_request_counter_ = -1;
|
||||
Cycles::IntType head_load_request_counter_ = -1;
|
||||
bool head_load_request_ = false;
|
||||
Delegate *delegate_ = nullptr;
|
||||
uint8_t last_control_ = 0;
|
||||
|
@ -94,7 +94,7 @@ void VideoOutput::run_for(const Cycles cycles) {
|
||||
#define clamp(action) \
|
||||
if(cycles_run_for <= number_of_cycles) { action; } else cycles_run_for = number_of_cycles;
|
||||
|
||||
int number_of_cycles = cycles.as_int();
|
||||
int number_of_cycles = int(cycles.as_integral());
|
||||
while(number_of_cycles) {
|
||||
int h_counter = counter_ & 63;
|
||||
int cycles_run_for = 0;
|
||||
|
@ -42,7 +42,7 @@ void Video::flush() {
|
||||
void Video::flush(bool next_sync) {
|
||||
if(sync_) {
|
||||
// If in sync, that takes priority. Output the proper amount of sync.
|
||||
crt_.output_sync(time_since_update_.as_int());
|
||||
crt_.output_sync(int(time_since_update_.as_integral()));
|
||||
} else {
|
||||
// If not presently in sync, then...
|
||||
|
||||
@ -50,8 +50,8 @@ void Video::flush(bool next_sync) {
|
||||
// If there is output data queued, output it either if it's being interrupted by
|
||||
// sync, or if we're past its end anyway. Otherwise let it be.
|
||||
int data_length = static_cast<int>(line_data_pointer_ - line_data_);
|
||||
if(data_length < time_since_update_.as_int() || next_sync) {
|
||||
auto output_length = std::min(data_length, time_since_update_.as_int());
|
||||
if(data_length < int(time_since_update_.as_integral()) || next_sync) {
|
||||
auto output_length = std::min(data_length, int(time_since_update_.as_integral()));
|
||||
crt_.output_data(output_length);
|
||||
line_data_pointer_ = line_data_ = nullptr;
|
||||
time_since_update_ -= HalfCycles(output_length);
|
||||
@ -61,7 +61,7 @@ void Video::flush(bool next_sync) {
|
||||
// Any pending pixels being dealt with, pad with the white level.
|
||||
uint8_t *colour_pointer = static_cast<uint8_t *>(crt_.begin_data(1));
|
||||
if(colour_pointer) *colour_pointer = 0xff;
|
||||
crt_.output_level(time_since_update_.as_int());
|
||||
crt_.output_level(int(time_since_update_.as_integral()));
|
||||
}
|
||||
|
||||
time_since_update_ = 0;
|
||||
|
@ -79,7 +79,7 @@ static CPU::MOS6502::Register registerForRegister(CSTestMachine6502Register reg)
|
||||
}
|
||||
|
||||
- (uint32_t)timestamp {
|
||||
return _processor->get_timestamp().as_int();
|
||||
return uint32_t(_processor->get_timestamp().as_integral());
|
||||
}
|
||||
|
||||
- (void)setIrqLine:(BOOL)irqLine {
|
||||
|
@ -155,7 +155,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
}
|
||||
|
||||
- (int)completedHalfCycles {
|
||||
return _processor->get_timestamp().as_int();
|
||||
return int(_processor->get_timestamp().as_integral());
|
||||
}
|
||||
|
||||
- (void)setNmiLine:(BOOL)nmiLine {
|
||||
@ -216,7 +216,7 @@ static CPU::Z80::Register registerForRegister(CSTestMachineZ80Register reg) {
|
||||
}
|
||||
capture.address = address;
|
||||
capture.value = value;
|
||||
capture.timeStamp = timeStamp.as_int();
|
||||
capture.timeStamp = int(timeStamp.as_integral());
|
||||
|
||||
[_busOperationCaptures addObject:capture];
|
||||
}
|
||||
|
@ -31,14 +31,14 @@
|
||||
int c = 5;
|
||||
bool vsync = _video->vsync();
|
||||
while(c--) {
|
||||
int remaining_time_in_state = _video->get_next_sequence_point().as_int();
|
||||
auto remaining_time_in_state = _video->get_next_sequence_point().as_integral();
|
||||
NSLog(@"Vsync %@ expected for %@ half-cycles", vsync ? @"on" : @"off", @(remaining_time_in_state));
|
||||
while(remaining_time_in_state--) {
|
||||
XCTAssertEqual(vsync, _video->vsync());
|
||||
_video->run_for(HalfCycles(1));
|
||||
|
||||
if(remaining_time_in_state)
|
||||
XCTAssertEqual(remaining_time_in_state, _video->get_next_sequence_point().as_int());
|
||||
XCTAssertEqual(remaining_time_in_state, _video->get_next_sequence_point().as_integral());
|
||||
}
|
||||
vsync ^= true;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@
|
||||
vdp.set_register(1, 0x8a);
|
||||
|
||||
// Get time until interrupt.
|
||||
int time_until_interrupt = vdp.get_time_until_interrupt().as_int() - 1;
|
||||
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
|
||||
|
||||
// Check that an interrupt is now scheduled.
|
||||
NSAssert(time_until_interrupt != -2, @"No interrupt scheduled");
|
||||
@ -74,7 +74,7 @@
|
||||
NSAssert(!vdp.get_interrupt_line(), @"Interrupt wasn't reset by status read");
|
||||
|
||||
// Check interrupt flag isn't set prior to the reported time.
|
||||
time_until_interrupt = vdp.get_time_until_interrupt().as_int() - 1;
|
||||
time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
|
||||
vdp.run_for(HalfCycles(time_until_interrupt));
|
||||
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early [2]");
|
||||
|
||||
@ -101,7 +101,7 @@
|
||||
|
||||
// Clear the pending interrupt and ask about the next one (i.e. the first one).
|
||||
vdp.get_register(1);
|
||||
int time_until_interrupt = vdp.get_time_until_interrupt().as_int() - 1;
|
||||
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
|
||||
|
||||
// Check that an interrupt is now scheduled.
|
||||
NSAssert(time_until_interrupt != -2, @"No interrupt scheduled");
|
||||
@ -135,7 +135,7 @@
|
||||
|
||||
// Now run through an entire frame...
|
||||
int half_cycles = 262*228*2;
|
||||
int last_time_until_interrupt = vdp.get_time_until_interrupt().as_int();
|
||||
auto last_time_until_interrupt = vdp.get_time_until_interrupt().as_integral();
|
||||
while(half_cycles--) {
|
||||
// Validate that an interrupt happened if one was expected, and clear anything that's present.
|
||||
NSAssert(vdp.get_interrupt_line() == (last_time_until_interrupt == 0), @"Unexpected interrupt state change; expected %d but got %d; position %d %d @ %d", (last_time_until_interrupt == 0), vdp.get_interrupt_line(), c, with_eof, half_cycles);
|
||||
@ -144,7 +144,7 @@
|
||||
vdp.run_for(HalfCycles(1));
|
||||
|
||||
// Get the time until interrupt.
|
||||
int time_until_interrupt = vdp.get_time_until_interrupt().as_int();
|
||||
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral();
|
||||
NSAssert(time_until_interrupt != -1, @"No interrupt scheduled; position %d %d @ %d", c, with_eof, half_cycles);
|
||||
NSAssert(time_until_interrupt >= 0, @"Interrupt is scheduled in the past; position %d %d @ %d", c, with_eof, half_cycles);
|
||||
|
||||
@ -160,11 +160,11 @@
|
||||
- (void)testTimeUntilLine {
|
||||
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
||||
|
||||
int time_until_line = vdp.get_time_until_line(-1).as_int();
|
||||
auto time_until_line = vdp.get_time_until_line(-1).as_integral();
|
||||
for(int c = 0; c < 262*228*5; ++c) {
|
||||
vdp.run_for(HalfCycles(1));
|
||||
|
||||
const int time_remaining_until_line = vdp.get_time_until_line(-1).as_int();
|
||||
const auto time_remaining_until_line = vdp.get_time_until_line(-1).as_integral();
|
||||
--time_until_line;
|
||||
if(time_until_line) {
|
||||
NSAssert(time_remaining_until_line == time_until_line, @"Discontinuity found in distance-to-line prediction; expected %d but got %d", time_until_line, time_remaining_until_line);
|
||||
|
@ -128,7 +128,7 @@ class RAM68000: public CPU::MC68000::BusHandler {
|
||||
}
|
||||
|
||||
int get_cycle_count() {
|
||||
return duration_.as_int() >> 1;
|
||||
return int(duration_.as_integral()) >> 1;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -106,7 +106,7 @@ template <typename T> class LowpassSpeaker: public Speaker {
|
||||
void run_for(const Cycles cycles) {
|
||||
if(!delegate_) return;
|
||||
|
||||
std::size_t cycles_remaining = size_t(cycles.as_int());
|
||||
std::size_t cycles_remaining = size_t(cycles.as_integral());
|
||||
if(!cycles_remaining) return;
|
||||
|
||||
FilterParameters filter_parameters;
|
||||
|
@ -941,7 +941,7 @@ template < class T,
|
||||
operation_indices.push_back(target.all_operations.size());
|
||||
for(std::size_t t = 0; t < lengths[c];) {
|
||||
// Skip zero-length bus cycles.
|
||||
if(table[c][t].type == MicroOp::BusOperation && table[c][t].machine_cycle.length.as_int() == 0) {
|
||||
if(table[c][t].type == MicroOp::BusOperation && table[c][t].machine_cycle.length.as_integral() == 0) {
|
||||
t++;
|
||||
continue;
|
||||
}
|
||||
|
@ -13,9 +13,9 @@
|
||||
using namespace Storage::Disk;
|
||||
|
||||
Controller::Controller(Cycles clock_rate) :
|
||||
clock_rate_multiplier_(128000000 / clock_rate.as_int()),
|
||||
clock_rate_(clock_rate.as_int() * clock_rate_multiplier_),
|
||||
empty_drive_(new Drive(clock_rate.as_int(), 1, 1)) {
|
||||
clock_rate_multiplier_(128000000 / clock_rate.as_integral()),
|
||||
clock_rate_(clock_rate.as_integral() * clock_rate_multiplier_),
|
||||
empty_drive_(new Drive(int(clock_rate.as_integral()), 1, 1)) {
|
||||
// seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later
|
||||
Time one(1);
|
||||
set_expected_bit_length(one);
|
||||
@ -48,7 +48,7 @@ void Controller::process_event(const Drive::Event &event) {
|
||||
}
|
||||
|
||||
void Controller::advance(const Cycles cycles) {
|
||||
if(is_reading_) pll_->run_for(Cycles(cycles.as_int() * clock_rate_multiplier_));
|
||||
if(is_reading_) pll_->run_for(Cycles(cycles.as_integral() * clock_rate_multiplier_));
|
||||
}
|
||||
|
||||
void Controller::process_write_completed() {
|
||||
@ -61,7 +61,7 @@ void Controller::set_expected_bit_length(Time bit_length) {
|
||||
bit_length_ = bit_length;
|
||||
bit_length_.simplify();
|
||||
|
||||
Time cycles_per_bit = Storage::Time(clock_rate_) * bit_length;
|
||||
Time cycles_per_bit = Storage::Time(int(clock_rate_)) * bit_length;
|
||||
cycles_per_bit.simplify();
|
||||
|
||||
// this conversion doesn't need to be exact because there's a lot of variation to be taken
|
||||
|
@ -106,8 +106,8 @@ class Controller:
|
||||
|
||||
private:
|
||||
Time bit_length_;
|
||||
int clock_rate_multiplier_ = 1;
|
||||
int clock_rate_ = 1;
|
||||
Cycles::IntType clock_rate_multiplier_ = 1;
|
||||
Cycles::IntType clock_rate_ = 1;
|
||||
|
||||
bool is_reading_ = true;
|
||||
|
||||
|
@ -18,14 +18,14 @@ DigitalPhaseLockedLoop::DigitalPhaseLockedLoop(int clocks_per_bit, std::size_t l
|
||||
clocks_per_bit_(clocks_per_bit) {}
|
||||
|
||||
void DigitalPhaseLockedLoop::run_for(const Cycles cycles) {
|
||||
offset_ += cycles.as_int();
|
||||
phase_ += cycles.as_int();
|
||||
offset_ += cycles.as_integral();
|
||||
phase_ += cycles.as_integral();
|
||||
if(phase_ >= window_length_) {
|
||||
int windows_crossed = phase_ / window_length_;
|
||||
auto windows_crossed = phase_ / window_length_;
|
||||
|
||||
// check whether this triggers any 0s, if anybody cares
|
||||
if(delegate_) {
|
||||
if(window_was_filled_) windows_crossed--;
|
||||
if(window_was_filled_) --windows_crossed;
|
||||
for(int c = 0; c < windows_crossed; c++)
|
||||
delegate_->digital_phase_locked_loop_output_bit(0);
|
||||
}
|
||||
@ -44,16 +44,16 @@ void DigitalPhaseLockedLoop::add_pulse() {
|
||||
}
|
||||
}
|
||||
|
||||
void DigitalPhaseLockedLoop::post_phase_offset(int new_phase, int new_offset) {
|
||||
void DigitalPhaseLockedLoop::post_phase_offset(Cycles::IntType new_phase, Cycles::IntType new_offset) {
|
||||
offset_history_[offset_history_pointer_] = new_offset;
|
||||
offset_history_pointer_ = (offset_history_pointer_ + 1) % offset_history_.size();
|
||||
|
||||
// use an unweighted average of the stored offsets to compute current window size,
|
||||
// bucketing them by rounding to the nearest multiple of the base clocks per bit
|
||||
int total_spacing = 0;
|
||||
int total_divisor = 0;
|
||||
for(int offset : offset_history_) {
|
||||
int multiple = (offset + (clocks_per_bit_ >> 1)) / clocks_per_bit_;
|
||||
Cycles::IntType total_spacing = 0;
|
||||
Cycles::IntType total_divisor = 0;
|
||||
for(auto offset : offset_history_) {
|
||||
auto multiple = (offset + (clocks_per_bit_ >> 1)) / clocks_per_bit_;
|
||||
if(!multiple) continue;
|
||||
total_divisor += multiple;
|
||||
total_spacing += offset;
|
||||
@ -62,7 +62,7 @@ void DigitalPhaseLockedLoop::post_phase_offset(int new_phase, int new_offset) {
|
||||
window_length_ = total_spacing / total_divisor;
|
||||
}
|
||||
|
||||
int error = new_phase - (window_length_ >> 1);
|
||||
auto error = new_phase - (window_length_ >> 1);
|
||||
|
||||
// use a simple spring mechanism as a lowpass filter for phase
|
||||
phase_ -= (error + 1) >> 1;
|
||||
|
@ -52,14 +52,14 @@ class DigitalPhaseLockedLoop {
|
||||
private:
|
||||
Delegate *delegate_ = nullptr;
|
||||
|
||||
void post_phase_offset(int phase, int offset);
|
||||
void post_phase_offset(Cycles::IntType phase, Cycles::IntType offset);
|
||||
|
||||
std::vector<int> offset_history_;
|
||||
std::vector<Cycles::IntType> offset_history_;
|
||||
std::size_t offset_history_pointer_ = 0;
|
||||
int offset_ = 0;
|
||||
Cycles::IntType offset_ = 0;
|
||||
|
||||
int phase_ = 0;
|
||||
int window_length_ = 0;
|
||||
Cycles::IntType phase_ = 0;
|
||||
Cycles::IntType window_length_ = 0;
|
||||
bool window_was_filled_ = false;
|
||||
|
||||
int clocks_per_bit_ = 0;
|
||||
|
@ -94,7 +94,8 @@ int WOZ::get_head_count() {
|
||||
|
||||
long WOZ::file_offset(Track::Address address) {
|
||||
// Calculate table position; if this track is defined to be unformatted, return no track.
|
||||
const int table_position = address.head * (is_3_5_disk_ ? 80 : 160) + (is_3_5_disk_ ? address.position.as_int() : address.position.as_quarter());
|
||||
const int table_position = address.head * (is_3_5_disk_ ? 80 : 160) +
|
||||
(is_3_5_disk_ ? address.position.as_int() : address.position.as_quarter());
|
||||
if(track_map_[table_position] == 0xff) return NoSuchTrack;
|
||||
|
||||
// Seek to the real track.
|
||||
|
@ -173,7 +173,7 @@ void Drive::set_event_delegate(Storage::Disk::Drive::EventDelegate *delegate) {
|
||||
}
|
||||
|
||||
void Drive::advance(const Cycles cycles) {
|
||||
cycles_since_index_hole_ += cycles.as_int();
|
||||
cycles_since_index_hole_ += cycles.as_integral();
|
||||
if(event_delegate_) event_delegate_->advance(cycles);
|
||||
}
|
||||
|
||||
@ -182,20 +182,20 @@ void Drive::run_for(const Cycles cycles) {
|
||||
if(has_disk_) {
|
||||
Time zero(0);
|
||||
|
||||
int number_of_cycles = cycles.as_int();
|
||||
auto number_of_cycles = cycles.as_integral();
|
||||
while(number_of_cycles) {
|
||||
int cycles_until_next_event = static_cast<int>(get_cycles_until_next_event());
|
||||
int cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles);
|
||||
auto cycles_until_next_event = get_cycles_until_next_event();
|
||||
auto cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles);
|
||||
if(!is_reading_ && cycles_until_bits_written_ > zero) {
|
||||
int write_cycles_target = cycles_until_bits_written_.get<int>();
|
||||
if(cycles_until_bits_written_.length % cycles_until_bits_written_.clock_rate) write_cycles_target++;
|
||||
auto write_cycles_target = cycles_until_bits_written_.get<Cycles::IntType>();
|
||||
if(cycles_until_bits_written_.length % cycles_until_bits_written_.clock_rate) ++write_cycles_target;
|
||||
cycles_to_run_for = std::min(cycles_to_run_for, write_cycles_target);
|
||||
}
|
||||
|
||||
number_of_cycles -= cycles_to_run_for;
|
||||
if(!is_reading_) {
|
||||
if(cycles_until_bits_written_ > zero) {
|
||||
Storage::Time cycles_to_run_for_time(cycles_to_run_for);
|
||||
Storage::Time cycles_to_run_for_time(static_cast<int>(cycles_to_run_for));
|
||||
if(cycles_until_bits_written_ <= cycles_to_run_for_time) {
|
||||
if(event_delegate_) event_delegate_->process_write_completed();
|
||||
if(cycles_until_bits_written_ <= cycles_to_run_for_time)
|
||||
@ -345,7 +345,7 @@ void Drive::begin_writing(Time bit_length, bool clamp_to_index_hole) {
|
||||
is_reading_ = false;
|
||||
clamp_writing_to_index_hole_ = clamp_to_index_hole;
|
||||
|
||||
cycles_per_bit_ = Storage::Time(get_input_clock_rate()) * bit_length;
|
||||
cycles_per_bit_ = Storage::Time(int(get_input_clock_rate())) * bit_length;
|
||||
cycles_per_bit_.simplify();
|
||||
|
||||
write_segment_.length_of_a_bit = bit_length / Time(rotational_multiplier_);
|
||||
|
@ -196,7 +196,7 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
|
||||
// A count of time since the index hole was last seen. Which is used to
|
||||
// determine how far the drive is into a full rotation when switching to
|
||||
// a new track.
|
||||
int cycles_since_index_hole_ = 0;
|
||||
Cycles::IntType cycles_since_index_hole_ = 0;
|
||||
|
||||
// The number of cycles that should fall within one revolution at the
|
||||
// current rotation speed.
|
||||
|
@ -11,20 +11,21 @@
|
||||
using namespace SCSI;
|
||||
|
||||
Bus::Bus(HalfCycles clock_rate) {
|
||||
cycles_to_time_ = 1.0 / double(clock_rate.as_int());
|
||||
cycles_to_time_ = 1.0 / double(clock_rate.as_integral());
|
||||
|
||||
// NB: note that the dispatch times below are **ORDERED**
|
||||
// from least to greatest. Each box should contain the number
|
||||
// of whole clock periods it will take to get the the first
|
||||
// discrete moment after the required delay interval has been met.
|
||||
dispatch_times_[0] = 1 + int(CableSkew / cycles_to_time_);
|
||||
dispatch_times_[1] = 1 + int(DeskewDelay / cycles_to_time_);
|
||||
dispatch_times_[2] = 1 + int(BusFreeDelay / cycles_to_time_);
|
||||
dispatch_times_[3] = 1 + int(BusSettleDelay / cycles_to_time_);
|
||||
dispatch_times_[4] = 1 + int(BusClearDelay / cycles_to_time_);
|
||||
dispatch_times_[5] = 1 + int(BusSetDelay / cycles_to_time_);
|
||||
dispatch_times_[6] = 1 + int(ArbitrationDelay / cycles_to_time_);
|
||||
dispatch_times_[7] = 1 + int(ResetHoldTime / cycles_to_time_);
|
||||
using IntType = Cycles::IntType;
|
||||
dispatch_times_[0] = 1 + IntType(CableSkew / cycles_to_time_);
|
||||
dispatch_times_[1] = 1 + IntType(DeskewDelay / cycles_to_time_);
|
||||
dispatch_times_[2] = 1 + IntType(BusFreeDelay / cycles_to_time_);
|
||||
dispatch_times_[3] = 1 + IntType(BusSettleDelay / cycles_to_time_);
|
||||
dispatch_times_[4] = 1 + IntType(BusClearDelay / cycles_to_time_);
|
||||
dispatch_times_[5] = 1 + IntType(BusSetDelay / cycles_to_time_);
|
||||
dispatch_times_[6] = 1 + IntType(ArbitrationDelay / cycles_to_time_);
|
||||
dispatch_times_[7] = 1 + IntType(ResetHoldTime / cycles_to_time_);
|
||||
}
|
||||
|
||||
size_t Bus::add_device() {
|
||||
@ -86,7 +87,7 @@ ClockingHint::Preference Bus::preferred_clocking() {
|
||||
}
|
||||
|
||||
void Bus::update_observers() {
|
||||
const auto time_elapsed = double(time_in_state_.as_int()) * cycles_to_time_;
|
||||
const auto time_elapsed = double(time_in_state_.as_integral()) * cycles_to_time_;
|
||||
for(auto &observer: observers_) {
|
||||
observer->scsi_bus_did_change(this, state_, time_elapsed);
|
||||
}
|
||||
@ -97,7 +98,7 @@ void Bus::run_for(HalfCycles time) {
|
||||
time_in_state_ += time;
|
||||
|
||||
const auto old_index = dispatch_index_;
|
||||
const auto time_as_int = time_in_state_.as_int();
|
||||
const auto time_as_int = time_in_state_.as_integral();
|
||||
while(time_as_int >= dispatch_times_[dispatch_index_] && dispatch_index_ < dispatch_times_.size()) {
|
||||
++dispatch_index_;
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ class Bus: public ClockingHint::Source, public Activity::Source {
|
||||
HalfCycles time_in_state_;
|
||||
double cycles_to_time_ = 1.0;
|
||||
size_t dispatch_index_ = 0;
|
||||
std::array<int, 8> dispatch_times_;
|
||||
std::array<Cycles::IntType, 8> dispatch_times_;
|
||||
|
||||
std::vector<BusState> device_states_;
|
||||
BusState state_ = DefaultBusState;
|
||||
|
@ -15,13 +15,13 @@
|
||||
|
||||
using namespace Storage;
|
||||
|
||||
TimedEventLoop::TimedEventLoop(int input_clock_rate) :
|
||||
TimedEventLoop::TimedEventLoop(Cycles::IntType input_clock_rate) :
|
||||
input_clock_rate_(input_clock_rate) {}
|
||||
|
||||
void TimedEventLoop::run_for(const Cycles cycles) {
|
||||
int remaining_cycles = cycles.as_int();
|
||||
auto remaining_cycles = cycles.as_integral();
|
||||
#ifndef NDEBUG
|
||||
int cycles_advanced = 0;
|
||||
decltype(remaining_cycles) cycles_advanced = 0;
|
||||
#endif
|
||||
|
||||
while(cycles_until_event_ <= remaining_cycles) {
|
||||
@ -42,15 +42,15 @@ void TimedEventLoop::run_for(const Cycles cycles) {
|
||||
advance(remaining_cycles);
|
||||
}
|
||||
|
||||
assert(cycles_advanced == cycles.as_int());
|
||||
assert(cycles_advanced == cycles.as_integral());
|
||||
assert(cycles_until_event_ > 0);
|
||||
}
|
||||
|
||||
int TimedEventLoop::get_cycles_until_next_event() {
|
||||
return std::max(cycles_until_event_, 0);
|
||||
Cycles::IntType TimedEventLoop::get_cycles_until_next_event() {
|
||||
return std::max(cycles_until_event_, Cycles::IntType(0));
|
||||
}
|
||||
|
||||
int TimedEventLoop::get_input_clock_rate() {
|
||||
Cycles::IntType TimedEventLoop::get_input_clock_rate() {
|
||||
return input_clock_rate_;
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ void TimedEventLoop::set_next_event_time_interval(float interval) {
|
||||
|
||||
// So this event will fire in the integral number of cycles from now, putting us at the remainder
|
||||
// number of subcycles
|
||||
const int addition = int(float_interval);
|
||||
const Cycles::IntType addition = Cycles::IntType(float_interval);
|
||||
cycles_until_event_ += addition;
|
||||
subcycles_until_event_ = fmodf(float_interval, 1.0);
|
||||
|
||||
|
@ -42,7 +42,7 @@ namespace Storage {
|
||||
/*!
|
||||
Constructs a timed event loop that will be clocked at @c input_clock_rate.
|
||||
*/
|
||||
TimedEventLoop(int input_clock_rate);
|
||||
TimedEventLoop(Cycles::IntType input_clock_rate);
|
||||
|
||||
/*!
|
||||
Advances the event loop by @c number_of_cycles cycles.
|
||||
@ -52,12 +52,12 @@ namespace Storage {
|
||||
/*!
|
||||
@returns the number of whole cycles remaining until the next event is triggered.
|
||||
*/
|
||||
int get_cycles_until_next_event();
|
||||
Cycles::IntType get_cycles_until_next_event();
|
||||
|
||||
/*!
|
||||
@returns the input clock rate.
|
||||
*/
|
||||
int get_input_clock_rate();
|
||||
Cycles::IntType get_input_clock_rate();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
@ -101,8 +101,8 @@ namespace Storage {
|
||||
Time get_time_into_next_event();
|
||||
|
||||
private:
|
||||
int input_clock_rate_ = 0;
|
||||
int cycles_until_event_ = 0;
|
||||
Cycles::IntType input_clock_rate_ = 0;
|
||||
Cycles::IntType cycles_until_event_ = 0;
|
||||
float subcycles_until_event_ = 0.0;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user