1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-04 18:29:40 +00:00

Takes a further step towards real timing.

This commit is contained in:
Thomas Harte 2021-08-08 21:52:28 -04:00
parent c1df4d1c0b
commit 1502c4530e
3 changed files with 56 additions and 39 deletions

View File

@ -61,27 +61,38 @@ class ConcreteMachine:
// MARK: - MC68000::BusHandler.
using Microcycle = CPU::MC68000::Microcycle;
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;
// (2) do all the other actions prior to this CPU access being scheduled.
// TODO: clean up below.
//
// (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.
// TODO: so they probably should be behind VPA?
cia_divider_ += cycle.length;
cia_divider_ += total_length;
const HalfCycles e_clocks = cia_divider_.divide(HalfCycles(20));
if(e_clocks > HalfCycles(0)) {
cia_a_.run_for(e_clocks);
cia_b_.run_for(e_clocks);
}
const auto changes = chipset_.run_for(cycle.length);
cia_a_.advance_tod(changes.vsyncs);
cia_b_.advance_tod(changes.hsyncs);
net_changes += chipset_.run_for(total_length);
cia_a_.advance_tod(net_changes.vsyncs);
cia_b_.advance_tod(net_changes.hsyncs);
chipset_.set_cia_interrupts(cia_a_.get_interrupt_line(), cia_b_.get_interrupt_line());
mc68000_.set_interrupt_level(chipset_.get_interrupt_level());
@ -95,11 +106,11 @@ class ConcreteMachine:
// Autovector interrupts.
if(cycle.operation & Microcycle::InterruptAcknowledge) {
mc68000_.set_is_peripheral_address(true);
return HalfCycles(0);
return access_delay;
}
// 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.
@ -186,7 +197,7 @@ class ConcreteMachine:
// }
}
return HalfCycles(0);
return access_delay;
}
private:

View File

@ -53,12 +53,11 @@ Chipset::Changes Chipset::run_until_cpu_slot() {
void Chipset::set_cia_interrupts(bool cia_a, bool cia_b) {
// TODO: are these really latched, or are they active live?
if(cia_a || cia_b) {
interrupt_requests_ |=
(cia_a ? InterruptFlag::IOPortsAndTimers : 0) |
(cia_b ? InterruptFlag::External : 0);
update_interrupts();
}
interrupt_requests_ &= ~(InterruptFlag::IOPortsAndTimers | InterruptFlag::External);
interrupt_requests_ |=
(cia_a ? InterruptFlag::IOPortsAndTimers : 0) |
(cia_b ? InterruptFlag::External : 0);
update_interrupts();
}
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() {
// TODO: actual CPU scheduling.
if constexpr (stop_if_cpu) {
return true;
}
if constexpr (cycle & 1) {
// Odd slot priority is:
//
@ -249,7 +243,7 @@ template <int cycle, bool stop_if_cpu> bool Chipset::perform_cycle() {
// 4. CPU.
}
return false;
return true;
}
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 actual_slots = advance_slots<stop_on_cpu>(start_slot, end_slot);
if(actual_slots >= 0) {
// TODO: abbreviate run, prior to adding to totals below.
assert(false);
if(stop_on_cpu && actual_slots >= 0) {
// Run until the end of the named slot.
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;
pixels_remaining -= line_pixels;
// Advance intraline counter and possibly ripple upwards into
// Advance intraline counter and pcoossibly ripple upwards into
// lines and fields.
if(line_cycle_ == (line_length_ * 4)) {
++changes.hsyncs;
@ -334,7 +337,6 @@ template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
}
changes.interrupt_level = interrupt_level_;
changes.duration = length;
return changes;
}
@ -389,7 +391,8 @@ void Chipset::perform(const CPU::MC68000::Microcycle &cycle) {
cycle.set_value16(position);
} break;
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);
cycle.set_value16(position);
} break;

View File

@ -23,15 +23,18 @@ class Chipset {
public:
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 {
int hsyncs = 0;
int vsyncs = 0;
int interrupt_level = 0;
HalfCycles duration;
Changes &operator += (const Changes &rhs) {
hsyncs += rhs.hsyncs;
vsyncs += rhs.vsyncs;
duration += rhs.duration;
return *this;
}
};
/// Advances the stated amount of time.
@ -68,7 +71,7 @@ class Chipset {
// 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 <int cycle, bool stop_if_cpu> bool perform_cycle();
template <int cycle> void output();