mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 01:31:42 +00:00
Experiments with a JustInTimeActor in the Master System.
This commit is contained in:
parent
5149f290d0
commit
a43ada82b2
@ -150,7 +150,7 @@ template <class T> class WrappedInt {
|
||||
is reset to zero.
|
||||
*/
|
||||
template <typename Target> forceinline Target flush() {
|
||||
const Target result(length_);
|
||||
const Target result = T(length_);
|
||||
length_ = 0;
|
||||
return result;
|
||||
}
|
||||
@ -176,7 +176,7 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
forceinline constexpr HalfCycles(int 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_int() * 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.
|
||||
@ -184,17 +184,20 @@ class HalfCycles: public WrappedInt<HalfCycles> {
|
||||
return Cycles(length_ >> 1);
|
||||
}
|
||||
|
||||
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
|
||||
template <typename Cycles> forceinline Cycles flush() {
|
||||
const Cycles result(length_ >> 1);
|
||||
length_ &= 1;
|
||||
/*!
|
||||
Flushes the value in @c this. The current value is returned, and the internal value
|
||||
is reset to zero.
|
||||
*/
|
||||
template <typename Target> forceinline Target flush() {
|
||||
const Target result(length_);
|
||||
length_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Flushes the half cycles in @c this, returning the number stored and setting this total to zero.
|
||||
forceinline HalfCycles flush() {
|
||||
HalfCycles result(length_);
|
||||
length_ = 0;
|
||||
/// Flushes the whole cycles in @c this, subtracting that many from the total stored here.
|
||||
template <> forceinline Cycles flush<Cycles>() {
|
||||
const Cycles result(length_ >> 1);
|
||||
length_ &= 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -27,20 +27,27 @@ template <class T, class LocalTimeScale, class TargetTimeScale = LocalTimeScale>
|
||||
template<typename... Args> JustInTimeActor(Args&&... args) : object_(std::forward<Args>(args)...) {}
|
||||
|
||||
/// Adds time to the actor.
|
||||
inline void operator += (const TimeScale &rhs) {
|
||||
inline void operator += (const LocalTimeScale &rhs) {
|
||||
time_since_update_ += rhs;
|
||||
is_flushed_ = false;
|
||||
}
|
||||
|
||||
/// Flushes all accumulated time and returns a pointer to the included object.
|
||||
inline T *operator->() {
|
||||
object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
||||
flush();
|
||||
return &object_;
|
||||
}
|
||||
|
||||
/// Flushes all accumulated time.
|
||||
inline void flush() {
|
||||
if(!is_flushed_) object_.run_for(time_since_update_.template flush<TargetTimeScale>());
|
||||
is_flushed_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
T object_;
|
||||
LocalTimeScale time_since_update_;
|
||||
|
||||
bool is_flushed_ = true;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -38,7 +38,7 @@ template <typename T> void MOS6522<T>::set_register(int address, uint8_t value)
|
||||
// Store locally and communicate outwards.
|
||||
registers_.output[1] = value;
|
||||
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
bus_handler_.set_port_output(Port::B, value, registers_.data_direction[1]);
|
||||
|
||||
registers_.interrupt_flags &= ~(InterruptFlag::CB1ActiveEdge | ((registers_.peripheral_control&0x20) ? 0 : InterruptFlag::CB2ActiveEdge));
|
||||
@ -48,7 +48,7 @@ template <typename T> void MOS6522<T>::set_register(int address, uint8_t value)
|
||||
case 0x1: // Write Port A.
|
||||
registers_.output[0] = value;
|
||||
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
bus_handler_.set_port_output(Port::A, value, registers_.data_direction[0]);
|
||||
|
||||
if(handshake_modes_[1] != HandshakeMode::None) {
|
||||
@ -205,7 +205,7 @@ template <typename T> uint8_t MOS6522<T>::get_register(int address) {
|
||||
}
|
||||
|
||||
template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t output_mask, uint8_t output) {
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
const uint8_t input = bus_handler_.get_port_input(port);
|
||||
return (input & ~output_mask) | (output & output_mask);
|
||||
}
|
||||
@ -220,7 +220,7 @@ template <typename T> void MOS6522<T>::reevaluate_interrupts() {
|
||||
if(new_interrupt_status != last_posted_interrupt_status_) {
|
||||
last_posted_interrupt_status_ = new_interrupt_status;
|
||||
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
bus_handler_.set_interrupt_status(new_interrupt_status);
|
||||
}
|
||||
}
|
||||
@ -338,7 +338,7 @@ template <typename T> void MOS6522<T>::do_phase1() {
|
||||
// Determine whether to toggle PB7.
|
||||
if(registers_.auxiliary_control&0x80) {
|
||||
registers_.output[1] ^= 0x80;
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
bus_handler_.set_port_output(Port::B, registers_.output[1], registers_.data_direction[1]);
|
||||
}
|
||||
}
|
||||
@ -369,7 +369,7 @@ template <typename T> void MOS6522<T>::run_for(const HalfCycles half_cycles) {
|
||||
}
|
||||
|
||||
template <typename T> void MOS6522<T>::flush() {
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
bus_handler_.flush();
|
||||
}
|
||||
|
||||
@ -422,7 +422,7 @@ template <typename T> void MOS6522<T>::set_control_line_output(Port port, Line l
|
||||
control_outputs_[port].lines[line] = value;
|
||||
|
||||
if(value != LineState::Input) {
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush());
|
||||
bus_handler_.run_for(time_since_bus_handler_call_.flush<HalfCycles>());
|
||||
bus_handler_.set_control_line_output(port, line, value != LineState::Off);
|
||||
}
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
|
||||
private:
|
||||
void update_video() {
|
||||
video_.run_for(time_since_video_update_.flush());
|
||||
video_.run_for(time_since_video_update_.flush<HalfCycles>());
|
||||
time_until_video_event_ = video_.get_next_sequence_point();
|
||||
}
|
||||
|
||||
|
@ -397,7 +397,7 @@ class ConcreteMachine:
|
||||
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider)));
|
||||
}
|
||||
inline void update_video() {
|
||||
vdp_.run_for(time_since_vdp_update_.flush());
|
||||
vdp_.run_for(time_since_vdp_update_.flush<HalfCycles>());
|
||||
}
|
||||
|
||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||
|
@ -489,7 +489,7 @@ class ConcreteMachine:
|
||||
*cycle.value = read_pointers_[address >> 13][address & 8191];
|
||||
} else {
|
||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush<HalfCycles>());
|
||||
*cycle.value = memory_slots_[slot_hit].handler->read(address);
|
||||
}
|
||||
break;
|
||||
@ -500,7 +500,7 @@ class ConcreteMachine:
|
||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||
if(memory_slots_[slot_hit].handler) {
|
||||
update_audio();
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush<HalfCycles>());
|
||||
memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]);
|
||||
}
|
||||
} break;
|
||||
@ -508,7 +508,7 @@ class ConcreteMachine:
|
||||
case CPU::Z80::PartialMachineCycle::Input:
|
||||
switch(address & 0xff) {
|
||||
case 0x98: case 0x99:
|
||||
vdp_.run_for(time_since_vdp_update_.flush());
|
||||
vdp_.run_for(time_since_vdp_update_.flush<HalfCycles>());
|
||||
*cycle.value = vdp_.get_register(address);
|
||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
||||
@ -536,7 +536,7 @@ class ConcreteMachine:
|
||||
const int port = address & 0xff;
|
||||
switch(port) {
|
||||
case 0x98: case 0x99:
|
||||
vdp_.run_for(time_since_vdp_update_.flush());
|
||||
vdp_.run_for(time_since_vdp_update_.flush<HalfCycles>());
|
||||
vdp_.set_register(address, *cycle.value);
|
||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
||||
@ -612,7 +612,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
void flush() {
|
||||
vdp_.run_for(time_since_vdp_update_.flush());
|
||||
vdp_.run_for(time_since_vdp_update_.flush<HalfCycles>());
|
||||
update_audio();
|
||||
audio_queue_.perform();
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../KeyboardMachine.hpp"
|
||||
|
||||
#include "../../ClockReceiver/ForceInline.hpp"
|
||||
#include "../../ClockReceiver/JustInTime.hpp"
|
||||
|
||||
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
|
||||
#include "../../Outputs/Log.hpp"
|
||||
@ -168,16 +169,16 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||
vdp_.set_tv_standard(
|
||||
vdp_->set_tv_standard(
|
||||
(region_ == Target::Region::Europe) ?
|
||||
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
|
||||
time_until_debounce_ = vdp_.get_time_until_line(-1);
|
||||
time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||
|
||||
vdp_.set_scan_target(scan_target);
|
||||
vdp_->set_scan_target(scan_target);
|
||||
}
|
||||
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) override {
|
||||
vdp_.set_display_type(display_type);
|
||||
vdp_->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() override {
|
||||
@ -189,7 +190,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
forceinline HalfCycles perform_machine_cycle(const CPU::Z80::PartialMachineCycle &cycle) {
|
||||
time_since_vdp_update_ += cycle.length;
|
||||
vdp_ += cycle.length;
|
||||
time_since_sn76489_update_ += cycle.length;
|
||||
|
||||
if(cycle.is_terminal()) {
|
||||
@ -233,17 +234,15 @@ class ConcreteMachine:
|
||||
*cycle.value = 0xff;
|
||||
break;
|
||||
case 0x40:
|
||||
update_video();
|
||||
*cycle.value = vdp_.get_current_line();
|
||||
*cycle.value = vdp_->get_current_line();
|
||||
break;
|
||||
case 0x41:
|
||||
*cycle.value = vdp_.get_latched_horizontal_counter();
|
||||
*cycle.value = vdp_->get_latched_horizontal_counter();
|
||||
break;
|
||||
case 0x80: case 0x81:
|
||||
update_video();
|
||||
*cycle.value = vdp_.get_register(address);
|
||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
||||
*cycle.value = vdp_->get_register(address);
|
||||
z80_.set_interrupt_line(vdp_->get_interrupt_line());
|
||||
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||
break;
|
||||
case 0xc0: {
|
||||
Joystick *const joypad1 = static_cast<Joystick *>(joysticks_[0].get());
|
||||
@ -284,8 +283,7 @@ class ConcreteMachine:
|
||||
|
||||
// Latch if either TH has newly gone to 1.
|
||||
if((new_ths^previous_ths)&new_ths) {
|
||||
update_video();
|
||||
vdp_.latch_horizontal_counter();
|
||||
vdp_->latch_horizontal_counter();
|
||||
}
|
||||
} break;
|
||||
case 0x40: case 0x41:
|
||||
@ -293,10 +291,9 @@ class ConcreteMachine:
|
||||
sn76489_.set_register(*cycle.value);
|
||||
break;
|
||||
case 0x80: case 0x81:
|
||||
update_video();
|
||||
vdp_.set_register(address, *cycle.value);
|
||||
z80_.set_interrupt_line(vdp_.get_interrupt_line());
|
||||
time_until_interrupt_ = vdp_.get_time_until_interrupt();
|
||||
vdp_->set_register(address, *cycle.value);
|
||||
z80_.set_interrupt_line(vdp_->get_interrupt_line());
|
||||
time_until_interrupt_ = vdp_->get_time_until_interrupt();
|
||||
break;
|
||||
case 0xc0:
|
||||
LOG("TODO: [output] I/O port A/N; " << int(*cycle.value));
|
||||
@ -331,15 +328,14 @@ class ConcreteMachine:
|
||||
time_until_debounce_ -= cycle.length;
|
||||
if(time_until_debounce_ <= HalfCycles(0)) {
|
||||
z80_.set_non_maskable_interrupt_line(pause_is_pressed_);
|
||||
update_video();
|
||||
time_until_debounce_ = vdp_.get_time_until_line(-1);
|
||||
time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||
}
|
||||
|
||||
return HalfCycles(0);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
update_video();
|
||||
vdp_.flush();
|
||||
update_audio();
|
||||
audio_queue_.perform();
|
||||
}
|
||||
@ -412,16 +408,13 @@ class ConcreteMachine:
|
||||
inline void update_audio() {
|
||||
speaker_.run_for(audio_queue_, time_since_sn76489_update_.divide_cycles(Cycles(sn76489_divider)));
|
||||
}
|
||||
inline void update_video() {
|
||||
vdp_.run_for(time_since_vdp_update_.flush());
|
||||
}
|
||||
|
||||
using Target = Analyser::Static::Sega::Target;
|
||||
Target::Model model_;
|
||||
Target::Region region_;
|
||||
Target::PagingScheme paging_scheme_;
|
||||
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
|
||||
TI::TMS::TMS9918 vdp_;
|
||||
JustInTimeActor<TI::TMS::TMS9918, HalfCycles> vdp_;
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
TI::SN76489 sn76489_;
|
||||
@ -431,7 +424,6 @@ class ConcreteMachine:
|
||||
Inputs::Keyboard keyboard_;
|
||||
bool reset_is_pressed_ = false, pause_is_pressed_ = false;
|
||||
|
||||
HalfCycles time_since_vdp_update_;
|
||||
HalfCycles time_since_sn76489_update_;
|
||||
HalfCycles time_until_interrupt_;
|
||||
HalfCycles time_until_debounce_;
|
||||
|
Loading…
Reference in New Issue
Block a user