mirror of
https://github.com/TomHarte/CLK.git
synced 2024-07-07 23:29:06 +00:00
Takes a further step towards real timing.
This commit is contained in:
parent
c1df4d1c0b
commit
1502c4530e
@ -61,27 +61,38 @@ class ConcreteMachine:
|
|||||||
// MARK: - MC68000::BusHandler.
|
// MARK: - MC68000::BusHandler.
|
||||||
using Microcycle = CPU::MC68000::Microcycle;
|
using Microcycle = CPU::MC68000::Microcycle;
|
||||||
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
|
||||||
// Intended 1-2 step here is:
|
|
||||||
//
|
//
|
||||||
// (1) determine when this CPU access will be scheduled;
|
// TODO: clean up below.
|
||||||
// (2) do all the other actions prior to this CPU access being scheduled.
|
|
||||||
//
|
//
|
||||||
// (or at least enqueue them, JIT-wise).
|
// Probably: let the Chipset own the CIAs, killing the need to pass hsyncs/vsyncs
|
||||||
|
// and CIA interrupt lines back and forth. Then the two run_fors can return, at most,
|
||||||
|
// an interrupt level and a duration. Possibly give the chipset a reference to the
|
||||||
|
// 68k so it can set IPL directly?
|
||||||
|
|
||||||
// Advance time.
|
|
||||||
|
// Do a quick advance check for Chip RAM access; add a suitable delay if required.
|
||||||
|
Chipset::Changes net_changes;
|
||||||
|
HalfCycles access_delay;
|
||||||
|
if(cycle.operation & Microcycle::NewAddress && *cycle.address < 0x20'0000) {
|
||||||
|
// TODO: shouldn't delay if the overlay bit is set?
|
||||||
|
net_changes = chipset_.run_until_cpu_slot();
|
||||||
|
access_delay = net_changes.duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute total length.
|
||||||
|
const HalfCycles total_length = cycle.length + access_delay;
|
||||||
|
|
||||||
// The CIAs are on the E clock.
|
// The CIAs are on the E clock.
|
||||||
// TODO: so they probably should be behind VPA?
|
cia_divider_ += total_length;
|
||||||
cia_divider_ += cycle.length;
|
|
||||||
const HalfCycles e_clocks = cia_divider_.divide(HalfCycles(20));
|
const HalfCycles e_clocks = cia_divider_.divide(HalfCycles(20));
|
||||||
if(e_clocks > HalfCycles(0)) {
|
if(e_clocks > HalfCycles(0)) {
|
||||||
cia_a_.run_for(e_clocks);
|
cia_a_.run_for(e_clocks);
|
||||||
cia_b_.run_for(e_clocks);
|
cia_b_.run_for(e_clocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto changes = chipset_.run_for(cycle.length);
|
net_changes += chipset_.run_for(total_length);
|
||||||
cia_a_.advance_tod(changes.vsyncs);
|
cia_a_.advance_tod(net_changes.vsyncs);
|
||||||
cia_b_.advance_tod(changes.hsyncs);
|
cia_b_.advance_tod(net_changes.hsyncs);
|
||||||
|
|
||||||
chipset_.set_cia_interrupts(cia_a_.get_interrupt_line(), cia_b_.get_interrupt_line());
|
chipset_.set_cia_interrupts(cia_a_.get_interrupt_line(), cia_b_.get_interrupt_line());
|
||||||
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
|
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
|
||||||
@ -95,11 +106,11 @@ class ConcreteMachine:
|
|||||||
// Autovector interrupts.
|
// Autovector interrupts.
|
||||||
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
if(cycle.operation & Microcycle::InterruptAcknowledge) {
|
||||||
mc68000_.set_is_peripheral_address(true);
|
mc68000_.set_is_peripheral_address(true);
|
||||||
return HalfCycles(0);
|
return access_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing if no address is exposed.
|
// Do nothing if no address is exposed.
|
||||||
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);
|
if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return access_delay;
|
||||||
|
|
||||||
// TODO: interrupt acknowledgement.
|
// TODO: interrupt acknowledgement.
|
||||||
|
|
||||||
@ -186,7 +197,7 @@ class ConcreteMachine:
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return HalfCycles(0);
|
return access_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -53,12 +53,11 @@ Chipset::Changes Chipset::run_until_cpu_slot() {
|
|||||||
|
|
||||||
void Chipset::set_cia_interrupts(bool cia_a, bool cia_b) {
|
void Chipset::set_cia_interrupts(bool cia_a, bool cia_b) {
|
||||||
// TODO: are these really latched, or are they active live?
|
// TODO: are these really latched, or are they active live?
|
||||||
if(cia_a || cia_b) {
|
interrupt_requests_ &= ~(InterruptFlag::IOPortsAndTimers | InterruptFlag::External);
|
||||||
interrupt_requests_ |=
|
interrupt_requests_ |=
|
||||||
(cia_a ? InterruptFlag::IOPortsAndTimers : 0) |
|
(cia_a ? InterruptFlag::IOPortsAndTimers : 0) |
|
||||||
(cia_b ? InterruptFlag::External : 0);
|
(cia_b ? InterruptFlag::External : 0);
|
||||||
update_interrupts();
|
update_interrupts();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chipset::Copper::advance(uint16_t position) {
|
bool Chipset::Copper::advance(uint16_t position) {
|
||||||
@ -220,11 +219,6 @@ template <int cycle> void Chipset::output() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <int cycle, bool stop_if_cpu> bool Chipset::perform_cycle() {
|
template <int cycle, bool stop_if_cpu> bool Chipset::perform_cycle() {
|
||||||
// TODO: actual CPU scheduling.
|
|
||||||
if constexpr (stop_if_cpu) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr (cycle & 1) {
|
if constexpr (cycle & 1) {
|
||||||
// Odd slot priority is:
|
// Odd slot priority is:
|
||||||
//
|
//
|
||||||
@ -249,7 +243,7 @@ template <int cycle, bool stop_if_cpu> bool Chipset::perform_cycle() {
|
|||||||
// 4. CPU.
|
// 4. CPU.
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool stop_on_cpu> int Chipset::advance_slots(int first_slot, int last_slot) {
|
template <bool stop_on_cpu> int Chipset::advance_slots(int first_slot, int last_slot) {
|
||||||
@ -303,15 +297,24 @@ template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
|
|||||||
const int end_slot = (line_cycle_ + line_pixels) >> 2;
|
const int end_slot = (line_cycle_ + line_pixels) >> 2;
|
||||||
const int actual_slots = advance_slots<stop_on_cpu>(start_slot, end_slot);
|
const int actual_slots = advance_slots<stop_on_cpu>(start_slot, end_slot);
|
||||||
|
|
||||||
if(actual_slots >= 0) {
|
if(stop_on_cpu && actual_slots >= 0) {
|
||||||
// TODO: abbreviate run, prior to adding to totals below.
|
// Run until the end of the named slot.
|
||||||
assert(false);
|
if(actual_slots) {
|
||||||
|
const int actual_line_pixels =
|
||||||
|
(4 - (line_cycle_ & 3)) + ((actual_slots - 1) << 2);
|
||||||
|
line_cycle_ += actual_line_pixels;
|
||||||
|
changes.duration += HalfCycles(actual_line_pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just ensure an exit.
|
||||||
|
pixels_remaining = 0;
|
||||||
|
} else {
|
||||||
|
line_cycle_ += line_pixels;
|
||||||
|
changes.duration += HalfCycles(line_pixels);
|
||||||
|
pixels_remaining -= line_pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
line_cycle_ += line_pixels;
|
// Advance intraline counter and pcoossibly ripple upwards into
|
||||||
pixels_remaining -= line_pixels;
|
|
||||||
|
|
||||||
// Advance intraline counter and possibly ripple upwards into
|
|
||||||
// lines and fields.
|
// lines and fields.
|
||||||
if(line_cycle_ == (line_length_ * 4)) {
|
if(line_cycle_ == (line_length_ * 4)) {
|
||||||
++changes.hsyncs;
|
++changes.hsyncs;
|
||||||
@ -334,7 +337,6 @@ template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changes.interrupt_level = interrupt_level_;
|
changes.interrupt_level = interrupt_level_;
|
||||||
changes.duration = length;
|
|
||||||
return changes;
|
return changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +391,8 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
|
|||||||
cycle.set_value16(position);
|
cycle.set_value16(position);
|
||||||
} break;
|
} break;
|
||||||
case Read(0x006): {
|
case Read(0x006): {
|
||||||
const uint16_t position = uint16_t(((line_cycle_ << 6) & 0xff00) | (y_ & 0x00ff));
|
// const uint16_t position = uint16_t(((line_cycle_ << 6) & 0xff00) | (y_ & 0x00ff));
|
||||||
|
const uint16_t position = 0xd1ef; // TODO: !!!
|
||||||
LOG("Read position low " << PADHEX(4) << position);
|
LOG("Read position low " << PADHEX(4) << position);
|
||||||
cycle.set_value16(position);
|
cycle.set_value16(position);
|
||||||
} break;
|
} break;
|
||||||
|
@ -23,15 +23,18 @@ class Chipset {
|
|||||||
public:
|
public:
|
||||||
Chipset(uint16_t *ram, size_t size);
|
Chipset(uint16_t *ram, size_t size);
|
||||||
|
|
||||||
/// @returns The duration from now until the beginning of the next
|
|
||||||
/// available CPU slot for accessing chip memory.
|
|
||||||
HalfCycles time_until_cpu_slot();
|
|
||||||
|
|
||||||
struct Changes {
|
struct Changes {
|
||||||
int hsyncs = 0;
|
int hsyncs = 0;
|
||||||
int vsyncs = 0;
|
int vsyncs = 0;
|
||||||
int interrupt_level = 0;
|
int interrupt_level = 0;
|
||||||
HalfCycles duration;
|
HalfCycles duration;
|
||||||
|
|
||||||
|
Changes &operator += (const Changes &rhs) {
|
||||||
|
hsyncs += rhs.hsyncs;
|
||||||
|
vsyncs += rhs.vsyncs;
|
||||||
|
duration += rhs.duration;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Advances the stated amount of time.
|
/// Advances the stated amount of time.
|
||||||
@ -68,7 +71,7 @@ class Chipset {
|
|||||||
|
|
||||||
// MARK: - Scheduler.
|
// MARK: - Scheduler.
|
||||||
|
|
||||||
template <bool stop_on_cpu> Changes run(HalfCycles duration = HalfCycles());
|
template <bool stop_on_cpu> Changes run(HalfCycles duration = HalfCycles::max());
|
||||||
template <bool stop_on_cpu> int advance_slots(int, int);
|
template <bool stop_on_cpu> int advance_slots(int, int);
|
||||||
template <int cycle, bool stop_if_cpu> bool perform_cycle();
|
template <int cycle, bool stop_if_cpu> bool perform_cycle();
|
||||||
template <int cycle> void output();
|
template <int cycle> void output();
|
||||||
|
Loading…
Reference in New Issue
Block a user