2021-06-22 23:33:41 +00:00
|
|
|
|
//
|
|
|
|
|
// Dave.hpp
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 22/06/2021.
|
|
|
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2024-01-17 04:34:46 +00:00
|
|
|
|
#pragma once
|
2021-06-22 23:33:41 +00:00
|
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
|
2021-06-28 01:02:04 +00:00
|
|
|
|
#include "../../ClockReceiver/ClockReceiver.hpp"
|
2021-06-25 02:21:01 +00:00
|
|
|
|
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
2021-06-22 23:33:41 +00:00
|
|
|
|
#include "../../Numeric/LFSR.hpp"
|
2024-02-09 19:25:40 +00:00
|
|
|
|
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
|
2021-06-22 23:33:41 +00:00
|
|
|
|
|
2023-05-10 21:02:18 +00:00
|
|
|
|
namespace Enterprise::Dave {
|
2021-06-22 23:33:41 +00:00
|
|
|
|
|
2021-06-28 01:02:04 +00:00
|
|
|
|
enum class Interrupt: uint8_t {
|
|
|
|
|
VariableFrequency = 0x02,
|
|
|
|
|
OneHz = 0x08,
|
|
|
|
|
Nick = 0x20,
|
|
|
|
|
};
|
|
|
|
|
|
2021-06-22 23:33:41 +00:00
|
|
|
|
/*!
|
2021-06-28 01:02:04 +00:00
|
|
|
|
Models the audio-production subset of Dave's behaviour.
|
2021-06-22 23:33:41 +00:00
|
|
|
|
*/
|
2024-02-09 19:25:40 +00:00
|
|
|
|
class Audio: public Outputs::Speaker::BufferSource<Audio, true> {
|
2021-06-22 23:33:41 +00:00
|
|
|
|
public:
|
2022-07-16 18:41:04 +00:00
|
|
|
|
Audio(Concurrency::AsyncTaskQueue<false> &audio_queue);
|
2021-06-25 02:21:01 +00:00
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
/// Modifies an register in the audio range; only the low 4 bits are
|
|
|
|
|
/// used for register decoding so it's assumed that the caller has
|
|
|
|
|
/// already identified this write as being to an audio register.
|
2021-06-22 23:33:41 +00:00
|
|
|
|
void write(uint16_t address, uint8_t value);
|
|
|
|
|
|
2021-06-25 02:21:01 +00:00
|
|
|
|
// MARK: - SampleSource.
|
2021-06-25 02:27:02 +00:00
|
|
|
|
void set_sample_volume_range(int16_t range);
|
2024-02-12 15:55:52 +00:00
|
|
|
|
template <Outputs::Speaker::Action action>
|
|
|
|
|
void apply_samples(std::size_t number_of_samples, Outputs::Speaker::StereoSample *target);
|
2021-06-25 02:21:01 +00:00
|
|
|
|
|
2021-06-22 23:33:41 +00:00
|
|
|
|
private:
|
2022-07-16 18:41:04 +00:00
|
|
|
|
Concurrency::AsyncTaskQueue<false> &audio_queue_;
|
2021-06-25 02:21:01 +00:00
|
|
|
|
|
2021-07-04 02:43:20 +00:00
|
|
|
|
// Global divider (i.e. 8MHz/12Mhz switch).
|
|
|
|
|
uint8_t global_divider_;
|
|
|
|
|
uint8_t global_divider_reload_ = 2;
|
|
|
|
|
|
2021-06-27 03:48:53 +00:00
|
|
|
|
// Tone channels.
|
2021-06-25 02:21:01 +00:00
|
|
|
|
struct Channel {
|
|
|
|
|
// User-set values.
|
|
|
|
|
uint16_t reload = 0;
|
|
|
|
|
bool high_pass = false;
|
|
|
|
|
bool ring_modulate = false;
|
|
|
|
|
enum class Distortion {
|
2021-06-27 03:39:59 +00:00
|
|
|
|
None = 0,
|
|
|
|
|
FourBit = 1,
|
|
|
|
|
FiveBit = 2,
|
|
|
|
|
SevenBit = 3,
|
2021-06-25 02:21:01 +00:00
|
|
|
|
} distortion = Distortion::None;
|
|
|
|
|
uint8_t amplitude[2]{};
|
2021-06-27 18:06:49 +00:00
|
|
|
|
bool sync = false;
|
2021-06-25 02:21:01 +00:00
|
|
|
|
|
|
|
|
|
// Current state.
|
|
|
|
|
uint16_t count = 0;
|
2021-06-27 03:39:59 +00:00
|
|
|
|
int output = 0;
|
2021-06-25 02:21:01 +00:00
|
|
|
|
} channels_[3];
|
2021-06-27 18:06:49 +00:00
|
|
|
|
void update_channel(int);
|
2021-06-27 03:48:53 +00:00
|
|
|
|
|
|
|
|
|
// Noise channel.
|
|
|
|
|
struct Noise {
|
|
|
|
|
// User-set values.
|
|
|
|
|
uint8_t amplitude[2]{};
|
|
|
|
|
enum class Frequency {
|
|
|
|
|
DivideByFour,
|
|
|
|
|
ToneChannel0,
|
|
|
|
|
ToneChannel1,
|
|
|
|
|
ToneChannel2,
|
|
|
|
|
} frequency = Frequency::DivideByFour;
|
|
|
|
|
enum class Polynomial {
|
|
|
|
|
SeventeenBit,
|
|
|
|
|
FifteenBit,
|
|
|
|
|
ElevenBit,
|
|
|
|
|
NineBit
|
|
|
|
|
} polynomial = Polynomial::SeventeenBit;
|
|
|
|
|
bool swap_polynomial = false;
|
|
|
|
|
bool low_pass = false;
|
|
|
|
|
bool high_pass = false;
|
|
|
|
|
bool ring_modulate = false;
|
|
|
|
|
|
|
|
|
|
// Current state.
|
2021-06-27 18:06:49 +00:00
|
|
|
|
int count = 0;
|
2021-07-03 02:36:35 +00:00
|
|
|
|
int output = 0;
|
2021-06-27 18:06:49 +00:00
|
|
|
|
bool final_output = false;
|
2021-06-27 03:48:53 +00:00
|
|
|
|
} noise_;
|
2021-06-27 18:06:49 +00:00
|
|
|
|
void update_noise();
|
|
|
|
|
|
|
|
|
|
bool use_direct_output_[2]{};
|
2021-06-27 03:48:53 +00:00
|
|
|
|
|
|
|
|
|
// Global volume, per SampleSource obligations.
|
2021-06-25 02:27:02 +00:00
|
|
|
|
int16_t volume_ = 0;
|
2021-06-22 23:33:41 +00:00
|
|
|
|
|
2021-06-27 03:39:59 +00:00
|
|
|
|
// Polynomials that are always running.
|
2021-06-22 23:33:41 +00:00
|
|
|
|
Numeric::LFSRv<0xc> poly4_;
|
|
|
|
|
Numeric::LFSRv<0x14> poly5_;
|
|
|
|
|
Numeric::LFSRv<0x60> poly7_;
|
2021-06-27 03:39:59 +00:00
|
|
|
|
|
|
|
|
|
// The selectable, noise-related polynomial.
|
2021-06-22 23:33:41 +00:00
|
|
|
|
Numeric::LFSRv<0x110> poly9_;
|
|
|
|
|
Numeric::LFSRv<0x500> poly11_;
|
|
|
|
|
Numeric::LFSRv<0x6000> poly15_;
|
|
|
|
|
Numeric::LFSRv<0x12000> poly17_;
|
2021-06-27 03:39:59 +00:00
|
|
|
|
|
|
|
|
|
// Current state of the active polynomials.
|
|
|
|
|
uint8_t poly_state_[4];
|
2021-06-22 23:33:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-06-28 01:02:04 +00:00
|
|
|
|
/*!
|
|
|
|
|
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:
|
2021-07-03 16:56:56 +00:00
|
|
|
|
/// Modifies an register in the audio range; only the low 4 bits are
|
|
|
|
|
/// used for register decoding so it's assumed that the caller has
|
|
|
|
|
/// already identified this write as being to an audio register.
|
2021-06-28 01:02:04 +00:00
|
|
|
|
void write(uint16_t address, uint8_t value);
|
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
/// Returns a bitmask of interrupts that have become active since
|
|
|
|
|
/// the last time this method was called; flags are as defined in
|
|
|
|
|
/// @c Enterprise::Dave::Interrupt
|
2021-06-28 01:02:04 +00:00
|
|
|
|
uint8_t get_new_interrupts();
|
2021-07-03 16:56:56 +00:00
|
|
|
|
|
|
|
|
|
/// Returns the current high or low states of the inputs that trigger
|
|
|
|
|
/// the interrupts modelled here, as a bit mask compatible with that
|
|
|
|
|
/// exposed by Dave as the register at 0xb4.
|
2021-06-28 02:33:20 +00:00
|
|
|
|
uint8_t get_divider_state();
|
2021-06-28 01:02:04 +00:00
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
/// Advances the interrupt source.
|
2021-06-28 01:02:04 +00:00
|
|
|
|
void run_for(Cycles);
|
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
/// @returns The amount of time from now until the earliest that
|
|
|
|
|
/// @c get_new_interrupts() _might_ have new interrupts to report.
|
2023-09-10 22:00:49 +00:00
|
|
|
|
Cycles next_sequence_point() const;
|
2021-06-28 01:36:55 +00:00
|
|
|
|
|
2021-06-28 01:02:04 +00:00
|
|
|
|
private:
|
2021-06-28 01:47:21 +00:00
|
|
|
|
static constexpr Cycles clock_rate{250000};
|
2021-06-28 02:33:20 +00:00
|
|
|
|
static constexpr Cycles half_clock_rate{125000};
|
2021-06-28 01:47:21 +00:00
|
|
|
|
|
2021-07-04 02:43:20 +00:00
|
|
|
|
// Global divider (i.e. 8MHz/12Mhz switch).
|
2021-07-07 00:12:44 +00:00
|
|
|
|
Cycles global_divider_ = Cycles(2);
|
2021-07-04 02:43:20 +00:00
|
|
|
|
Cycles run_length_;
|
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
// Interrupts that have fired since get_new_interrupts()
|
|
|
|
|
// was last called.
|
|
|
|
|
uint8_t interrupts_ = 0;
|
|
|
|
|
|
|
|
|
|
// A counter for the 1Hz interrupt.
|
2021-07-07 00:12:44 +00:00
|
|
|
|
int two_second_counter_ = 0;
|
2021-06-28 02:33:20 +00:00
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
// A counter specific to the 1kHz and 50Hz timers, if in use.
|
2021-06-28 01:36:55 +00:00
|
|
|
|
enum class InterruptRate {
|
|
|
|
|
OnekHz,
|
|
|
|
|
FiftyHz,
|
|
|
|
|
ToneGenerator0,
|
|
|
|
|
ToneGenerator1,
|
|
|
|
|
} rate_ = InterruptRate::OnekHz;
|
2021-06-29 01:08:41 +00:00
|
|
|
|
bool programmable_level_ = false;
|
2021-06-28 01:36:55 +00:00
|
|
|
|
|
2021-07-03 16:56:56 +00:00
|
|
|
|
// A local duplicate of the counting state of the first two audio
|
|
|
|
|
// channels, maintained in case either of those is used as an
|
|
|
|
|
// interrupt source.
|
2021-06-28 01:36:55 +00:00
|
|
|
|
struct Channel {
|
2021-06-28 03:21:00 +00:00
|
|
|
|
int value = 100, reload = 100;
|
2021-06-28 02:08:38 +00:00
|
|
|
|
bool sync = false;
|
2021-06-28 03:09:11 +00:00
|
|
|
|
bool level = false;
|
2021-06-28 01:36:55 +00:00
|
|
|
|
} channels_[2];
|
2021-06-28 03:09:11 +00:00
|
|
|
|
void update_channel(int c, bool is_linked, int decrement);
|
2021-06-28 01:02:04 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-06-22 23:33:41 +00:00
|
|
|
|
}
|