mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
The Enterprise is now an Activity::Source; also sketches out the owner of Dave's timed interrupt logic.
This commit is contained in:
parent
b698056f78
commit
2f53b105bb
@ -10,6 +10,8 @@
|
||||
|
||||
using namespace Enterprise::Dave;
|
||||
|
||||
// MARK: - Audio generator
|
||||
|
||||
Audio::Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
|
||||
audio_queue_(audio_queue) {}
|
||||
|
||||
@ -188,3 +190,20 @@ void Audio::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interrupt source
|
||||
|
||||
uint8_t TimedInterruptSource::get_new_interrupts() {
|
||||
const uint8_t result = interrupts_;
|
||||
interrupts_ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TimedInterruptSource::write(uint16_t address, uint8_t value) {
|
||||
(void)address;
|
||||
(void)value;
|
||||
}
|
||||
|
||||
void TimedInterruptSource::run_for(Cycles cycles) {
|
||||
(void)cycles;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
||||
#include "../../Numeric/LFSR.hpp"
|
||||
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
||||
@ -18,9 +19,14 @@
|
||||
namespace Enterprise {
|
||||
namespace Dave {
|
||||
|
||||
enum class Interrupt: uint8_t {
|
||||
VariableFrequency = 0x02,
|
||||
OneHz = 0x08,
|
||||
Nick = 0x20,
|
||||
};
|
||||
|
||||
/*!
|
||||
Models a subset of Dave's behaviour; memory mapping and interrupt status
|
||||
is integrated into the main Enterprise machine.
|
||||
Models the audio-production subset of Dave's behaviour.
|
||||
*/
|
||||
class Audio: public Outputs::Speaker::SampleSource {
|
||||
public:
|
||||
@ -105,6 +111,24 @@ class Audio: public Outputs::Speaker::SampleSource {
|
||||
uint8_t poly_state_[4];
|
||||
};
|
||||
|
||||
/*!
|
||||
Provides Dave's timed interrupts — those that are provided at 1 kHz,
|
||||
50 Hz or according to the rate of tone generators 0 or 1, plus the fixed
|
||||
1 Hz interrupt.
|
||||
|
||||
*/
|
||||
class TimedInterruptSource {
|
||||
public:
|
||||
void write(uint16_t address, uint8_t value);
|
||||
|
||||
uint8_t get_new_interrupts();
|
||||
|
||||
void run_for(Cycles);
|
||||
|
||||
private:
|
||||
uint8_t interrupts_ = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,3 +70,9 @@ void EXDos::set_motor_on(bool on) {
|
||||
// writing state, so plenty of work to do in general here.
|
||||
get_drive().set_motor_on(on);
|
||||
}
|
||||
|
||||
void EXDos::set_activity_observer(Activity::Observer *observer) {
|
||||
for_all_drives([observer] (Storage::Disk::Drive &drive, size_t index) {
|
||||
drive.set_activity_observer(observer, "Drive " + std::to_string(index+1), true);
|
||||
});
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ class EXDos final : public WD::WD1770 {
|
||||
void set_control_register(uint8_t control);
|
||||
uint8_t get_control_register();
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
bool disk_did_change_ = false;
|
||||
|
||||
|
@ -62,6 +62,7 @@ namespace Enterprise {
|
||||
*/
|
||||
|
||||
template <bool has_disk_controller> class ConcreteMachine:
|
||||
public Activity::Source,
|
||||
public CPU::Z80::BusHandler,
|
||||
public Machine,
|
||||
public MachineTypes::AudioProducer,
|
||||
@ -86,8 +87,8 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
min_ram_slot_(min_ram_slot(target)),
|
||||
z80_(*this),
|
||||
nick_(ram_.end() - 65536),
|
||||
dave_(audio_queue_),
|
||||
speaker_(dave_) {
|
||||
dave_audio_(audio_queue_),
|
||||
speaker_(dave_audio_) {
|
||||
// Request a clock of 4Mhz; this'll be mapped upwards for Nick and Dave elsewhere.
|
||||
set_clock_rate(4'000'000);
|
||||
|
||||
@ -226,7 +227,7 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
const auto nick = nick_.last_valid();
|
||||
const bool nick_interrupt_line = nick->get_interrupt_line();
|
||||
if(nick_interrupt_line && !previous_nick_interrupt_line_) {
|
||||
set_interrupt(Interrupt::Nick, nick_.last_sequence_point_overrun());
|
||||
set_interrupts(uint8_t(Dave::Interrupt::Nick), nick_.last_sequence_point_overrun());
|
||||
}
|
||||
previous_nick_interrupt_line_ = nick_interrupt_line;
|
||||
}
|
||||
@ -306,6 +307,9 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
const HalfCycles full_length = cycle.length + penalty;
|
||||
time_since_audio_update_ += full_length;
|
||||
advance_nick(full_length);
|
||||
if(dave_timer_ += full_length) {
|
||||
set_interrupts(dave_timer_.last_valid()->get_new_interrupts(), dave_timer_.last_sequence_point_overrun());
|
||||
}
|
||||
|
||||
// The WD/etc runs at a nominal 8Mhz.
|
||||
if constexpr (has_disk_controller) {
|
||||
@ -387,7 +391,8 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
case 0xa8: case 0xa9: case 0xaa: case 0xab:
|
||||
case 0xac: case 0xad: case 0xae: case 0xaf:
|
||||
update_audio();
|
||||
dave_.write(address, *cycle.value);
|
||||
dave_audio_.write(address, *cycle.value);
|
||||
dave_timer_->write(address, *cycle.value);
|
||||
break;
|
||||
|
||||
case 0xb4:
|
||||
@ -442,12 +447,6 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
audio_queue_.perform();
|
||||
}
|
||||
|
||||
inline void update_audio() {
|
||||
// TODO: divide by only 8, letting Dave divide itself by a further 2 or 3
|
||||
// as per its own register.
|
||||
speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(16)));
|
||||
}
|
||||
|
||||
private:
|
||||
// MARK: - Memory layout
|
||||
std::array<uint8_t, 256 * 1024> ram_{};
|
||||
@ -560,12 +559,9 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Interrupts
|
||||
enum class Interrupt: uint8_t {
|
||||
Nick = 0x20
|
||||
};
|
||||
|
||||
uint8_t interrupt_mask_ = 0x00, interrupt_state_ = 0x00;
|
||||
void set_interrupt(Interrupt mask, HalfCycles offset = HalfCycles(0)) {
|
||||
void set_interrupts(uint8_t mask, HalfCycles offset = HalfCycles(0)) {
|
||||
interrupt_state_ |= uint8_t(mask);
|
||||
update_interrupts(offset);
|
||||
}
|
||||
@ -580,12 +576,27 @@ template <bool has_disk_controller> class ConcreteMachine:
|
||||
// Cf. timing guesses above.
|
||||
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
Dave::Audio dave_;
|
||||
Dave::Audio dave_audio_;
|
||||
Outputs::Speaker::LowpassSpeaker<Dave::Audio> speaker_;
|
||||
HalfCycles time_since_audio_update_;
|
||||
|
||||
// The following two should both use the same divider.
|
||||
JustInTimeActor<Dave::TimedInterruptSource, HalfCycles, 1, 16> dave_timer_;
|
||||
inline void update_audio() {
|
||||
// TODO: divide by only 8, letting Dave divide itself by a further 2 or 3
|
||||
// as per its own register.
|
||||
speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(16)));
|
||||
}
|
||||
|
||||
// MARK: - EXDos card.
|
||||
EXDos exdos_;
|
||||
|
||||
// MARK: - Activity Source
|
||||
void set_activity_observer(Activity::Observer *observer) final {
|
||||
if constexpr (has_disk_controller) {
|
||||
exdos_.set_activity_observer(observer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user