1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-21 18:37:11 +00:00

Reconnect speaker.

This commit is contained in:
Thomas Harte 2025-03-05 21:51:50 -05:00
parent c7f2805b05
commit 99e0902b74

View File

@ -58,6 +58,49 @@ Log::Logger<Log::Source::PCCompatible> log;
using Target = Analyser::Static::PCCompatible::Target;
struct PCSpeaker {
PCSpeaker() :
toggle(queue),
speaker(toggle) {}
void update() {
speaker.run_for(queue, cycles_since_update);
cycles_since_update = 0;
}
void set_pit(const bool pit_input) {
pit_input_ = pit_input;
set_level();
}
void set_control(const bool pit_mask, const bool level) {
pit_mask_ = pit_mask;
level_ = level;
set_level();
}
void set_level() {
// TODO: I think pit_mask_ actually acts as the gate input to the PIT.
const bool new_output = (!pit_mask_ | pit_input_) & level_;
if(new_output != output_) {
update();
toggle.set_output(new_output);
output_ = new_output;
}
}
Concurrency::AsyncTaskQueue<false> queue;
Audio::Toggle toggle;
Outputs::Speaker::PullLowpass<Audio::Toggle> speaker;
Cycles cycles_since_update = 0;
bool pit_input_ = false;
bool pit_mask_ = false;
bool level_ = false;
bool output_ = false;
};
// Map from members of the VideoAdaptor enum to concrete class types.
template <Target::VideoAdaptor adaptor> struct Adaptor;
template <> struct Adaptor<Target::VideoAdaptor::MDA> { using type = MDA; };
@ -362,7 +405,7 @@ class KeyboardController;
template <Analyser::Static::PCCompatible::Model model>
class KeyboardController<model, typename std::enable_if_t<is_xt(model)>> {
public:
KeyboardController(PIC<model> &pic) : pic_(pic) {}
KeyboardController(PIC<model> &pic, PCSpeaker &) : pic_(pic) {}
// KB Status Port 61h high bits:
//; 01 - normal operation. wait for keypress, when one comes in,
@ -445,7 +488,7 @@ private:
template <Analyser::Static::PCCompatible::Model model>
class KeyboardController<model, typename std::enable_if_t<is_at(model)>> {
public:
KeyboardController(PIC<model> &pic) : pic_(pic) {}
KeyboardController(PIC<model> &pic, PCSpeaker &speaker) : pic_(pic), speaker_(speaker) {}
void run_for([[maybe_unused]] const Cycles cycles) {
}
@ -455,60 +498,40 @@ public:
template <typename IntT>
void write([[maybe_unused]] const uint16_t port, [[maybe_unused]] const IntT value) {
log.error().append("Unimplemented AT keyboard write: %04x to %04x", value, port);
switch(port) {
default:
log.error().append("Unimplemented AT keyboard write: %04x to %04x", value, port);
break;
case 0x0061:
// TODO:
// b7: 1 = reset IRQ 0
// b3: enable channel check
// b2: enable parity check
speaker_.set_control(value & 0x01, value & 0x02);
break;
}
}
template <typename IntT>
IntT read([[maybe_unused]] const uint16_t port) {
log.error().append("Unimplemented AT keyboard read from %04x", port);
switch(port) {
default:
log.error().append("Unimplemented AT keyboard read from %04x", port);
break;
case 0x0061:
refresh_toggle_ ^= 0x10;
log.info().append("AT keyboard: %02x from %04x", refresh_toggle_, port);
return refresh_toggle_;
}
return ~0;
}
private:
PIC<model> &pic_;
};
struct PCSpeaker {
PCSpeaker() :
toggle(queue),
speaker(toggle) {}
void update() {
speaker.run_for(queue, cycles_since_update);
cycles_since_update = 0;
}
void set_pit(const bool pit_input) {
pit_input_ = pit_input;
set_level();
}
void set_control(const bool pit_mask, const bool level) {
pit_mask_ = pit_mask;
level_ = level;
set_level();
}
void set_level() {
// TODO: I think pit_mask_ actually acts as the gate input to the PIT.
const bool new_output = (!pit_mask_ | pit_input_) & level_;
if(new_output != output_) {
update();
toggle.set_output(new_output);
output_ = new_output;
}
}
Concurrency::AsyncTaskQueue<false> queue;
Audio::Toggle toggle;
Outputs::Speaker::PullLowpass<Audio::Toggle> speaker;
Cycles cycles_since_update = 0;
bool pit_input_ = false;
bool pit_mask_ = false;
bool level_ = false;
bool output_ = false;
PCSpeaker &speaker_;
uint8_t refresh_toggle_ = 0;
};
template <Analyser::Static::PCCompatible::Model model>
@ -568,7 +591,7 @@ public:
/// Supplies a hint about the user's display choice. If the high switches haven't been read yet and this is a CGA device,
/// this hint will be used to select between 40- and 80-column default display.
void hint_is_composite(bool composite) {
void hint_is_composite(const bool composite) {
if(high_switches_observed_) {
return;
}
@ -585,7 +608,7 @@ public:
}
}
void set_value(int port, uint8_t value) {
void set_value(const int port, const uint8_t value) {
switch(port) {
case 1:
// b7: 0 => enable keyboard read (and IRQ); 1 => don't;
@ -604,7 +627,7 @@ public:
}
}
uint8_t get_value(int port) {
uint8_t get_value(const int port) {
switch(port) {
case 0:
high_switches_observed_ = true;
@ -997,7 +1020,7 @@ public:
const Analyser::Static::PCCompatible::Target &target,
const ROMMachine::ROMFetcher &rom_fetcher
) :
keyboard_(pic_),
keyboard_(pic_, speaker_),
fdc_(pic_, dma_, DriveCount),
pit_observer_(pic_, speaker_),
ppi_handler_(speaker_, keyboard_, video, DriveCount),
@ -1278,9 +1301,9 @@ public:
private:
static constexpr auto x86_model = processor_model(pc_model);
PCSpeaker speaker_;
PIC<pc_model> pic_;
DMA<pc_model> dma_;
PCSpeaker speaker_;
Video video_;
KeyboardController<pc_model> keyboard_;
@ -1342,7 +1365,7 @@ private:
using namespace PCCompatible;
namespace {
static constexpr bool ForceAT = false;
static constexpr bool ForceAT = true;//false;
template <Target::VideoAdaptor video>
std::unique_ptr<Machine> machine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) {