1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

PIT output now reaches the PIC.

This commit is contained in:
Thomas Harte 2023-11-20 22:36:05 -05:00
parent f0e2ef5e28
commit 695282b838
2 changed files with 74 additions and 32 deletions

View File

@ -84,8 +84,13 @@ class PIC {
return 0; return 0;
} }
template <int vector> template <int input>
void raise() { void apply_edge(bool final_level) {
const uint8_t input_mask = 1 << input;
if(mask_ & input_mask) {
return;
}
printf("PIC: Unmasked input %d switches to level %d\n", input, final_level);
} }
private: private:
@ -103,6 +108,30 @@ class PIC {
} config_; } config_;
}; };
class PITObserver {
public:
PITObserver(PIC &pic) : pic_(pic) {}
template <int channel>
void update_output(bool new_level) {
switch(channel) {
default: break;
case 0: pic_.apply_edge<0>(new_level); break;
}
}
private:
PIC &pic_;
// TODO:
//
// channel 0 is connected to IRQ 0;
// channel 1 is used for DRAM refresh (presumably connected to DMA?);
// channel 2 is gated by a PPI output and feeds into the speaker.
};
using PIT = i8237<false, PITObserver>;
class i8255PortHandler : public Intel::i8255::PortHandler { class i8255PortHandler : public Intel::i8255::PortHandler {
// Likely to be helpful: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol // Likely to be helpful: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
public: public:
@ -452,7 +481,7 @@ struct Memory {
class IO { class IO {
public: public:
IO(PIT<false> &pit, DMA &dma, PPI &ppi, PIC &pic) : pit_(pit), dma_(dma), ppi_(ppi), pic_(pic) {} IO(PIT &pit, DMA &dma, PPI &ppi, PIC &pic) : pit_(pit), dma_(dma), ppi_(ppi), pic_(pic) {}
template <typename IntT> void out(uint16_t port, IntT value) { template <typename IntT> void out(uint16_t port, IntT value) {
switch(port) { switch(port) {
@ -555,7 +584,7 @@ class IO {
} }
private: private:
PIT<false> &pit_; PIT &pit_;
DMA &dma_; DMA &dma_;
PPI &ppi_; PPI &ppi_;
PIC &pic_; PIC &pic_;
@ -610,7 +639,7 @@ class ConcreteMachine:
ConcreteMachine( ConcreteMachine(
[[maybe_unused]] const Analyser::Static::Target &target, [[maybe_unused]] const Analyser::Static::Target &target,
const ROMMachine::ROMFetcher &rom_fetcher const ROMMachine::ROMFetcher &rom_fetcher
) : ppi_(ppi_handler_), context(pit_, dma_, ppi_, pic_) { ) : pit_observer_(pic_), pit_(pit_observer_), ppi_(ppi_handler_), context(pit_, dma_, ppi_, pic_) {
// Use clock rate as a MIPS count; keeping it as a multiple or divisor of the PIT frequency is easy. // Use clock rate as a MIPS count; keeping it as a multiple or divisor of the PIT frequency is easy.
static constexpr int pit_frequency = 1'193'182; static constexpr int pit_frequency = 1'193'182;
set_clock_rate(double(pit_frequency) * double(PitMultiplier) / double(PitDivisor)); // i.e. almost 0.4 MIPS for an XT. set_clock_rate(double(pit_frequency) * double(PitMultiplier) / double(PitDivisor)); // i.e. almost 0.4 MIPS for an XT.
@ -668,14 +697,17 @@ class ConcreteMachine:
} }
private: private:
PIT<false> pit_;
DMA dma_;
i8255PortHandler ppi_handler_;
PPI ppi_;
PIC pic_; PIC pic_;
DMA dma_;
PITObserver pit_observer_;
i8255PortHandler ppi_handler_;
PIT pit_;
PPI ppi_;
struct Context { struct Context {
Context(PIT<false> &pit, DMA &dma, PPI &ppi, PIC &pic) : Context(PIT &pit, DMA &dma, PPI &ppi, PIC &pic) :
segments(registers), segments(registers),
memory(registers, segments), memory(registers, segments),
flow_controller(registers, segments), flow_controller(registers, segments),

View File

@ -11,15 +11,17 @@
namespace PCCompatible { namespace PCCompatible {
template <bool is_8254> template <bool is_8254, typename PITObserver>
class PIT { class i8237 {
public: public:
i8237(PITObserver &observer) : observer_(observer) {}
template <int channel> uint8_t read() { template <int channel> uint8_t read() {
return channels_[channel].read(); return channels_[channel].read();
} }
template <int channel> void write(uint8_t value) { template <int channel> void write(uint8_t value) {
channels_[channel].write(value); channels_[channel].template write<channel>(observer_, value);
} }
void set_mode(uint8_t value) { void set_mode(uint8_t value) {
@ -29,20 +31,27 @@ class PIT {
read_back_ = is_8254; read_back_ = is_8254;
return; return;
} }
channels_[channel_id].set_mode(value); switch(channel_id) {
case 0: channels_[0].template set_mode<0>(observer_, value); break;
case 1: channels_[1].template set_mode<1>(observer_, value); break;
case 2: channels_[2].template set_mode<2>(observer_, value); break;
}
} }
void run_for(Cycles cycles) { void run_for(Cycles cycles) {
// TODO: be intelligent enough to take ticks outside the loop when appropriate. // TODO: be intelligent enough to take ticks outside the loop when appropriate.
auto ticks = cycles.as<int>(); auto ticks = cycles.as<int>();
while(ticks--) { while(ticks--) {
channels_[0].advance(1); channels_[0].template advance<0>(observer_, 1);
channels_[1].advance(1); channels_[1].template advance<1>(observer_, 1);
channels_[2].advance(1); channels_[2].template advance<2>(observer_, 1);
} }
} }
private: private:
// The target for output changes.
PITObserver &observer_;
// Supported only on 8254s. // Supported only on 8254s.
bool read_back_ = false; bool read_back_ = false;
@ -80,7 +89,8 @@ class PIT {
latch = counter; latch = counter;
} }
void set_mode(uint8_t value) { template <int channel>
void set_mode(PITObserver &observer, uint8_t value) {
switch((value >> 4) & 3) { switch((value >> 4) & 3) {
default: default:
latch_value(); latch_value();
@ -108,19 +118,20 @@ class PIT {
case OperatingMode::InterruptOnTerminalCount: case OperatingMode::InterruptOnTerminalCount:
case OperatingMode::HardwareRetriggerableOneShot: case OperatingMode::HardwareRetriggerableOneShot:
set_output(false); set_output<channel>(observer, false);
awaiting_reload = true; awaiting_reload = true;
break; break;
case OperatingMode::RateGenerator: case OperatingMode::RateGenerator:
case OperatingMode::SquareWaveGenerator: case OperatingMode::SquareWaveGenerator:
set_output(true); set_output<channel>(observer, true);
awaiting_reload = true; awaiting_reload = true;
break; break;
} }
} }
void advance(int ticks) { template <int channel>
void advance(PITObserver &observer, int ticks) {
if(gated || awaiting_reload) return; if(gated || awaiting_reload) return;
// TODO: BCD mode is completely ignored below. Possibly not too important. // TODO: BCD mode is completely ignored below. Possibly not too important.
@ -128,7 +139,7 @@ class PIT {
case OperatingMode::InterruptOnTerminalCount: case OperatingMode::InterruptOnTerminalCount:
case OperatingMode::HardwareRetriggerableOneShot: case OperatingMode::HardwareRetriggerableOneShot:
// Output goes permanently high upon a tick from 1 to 0; reload value is not reused. // Output goes permanently high upon a tick from 1 to 0; reload value is not reused.
set_output(output | (counter <= ticks)); set_output<channel>(observer, output | (counter <= ticks));
counter -= ticks; counter -= ticks;
break; break;
@ -138,7 +149,7 @@ class PIT {
// If there's a step from 1 to 0 within the next batch of ticks, // If there's a step from 1 to 0 within the next batch of ticks,
// toggle output and apply a reload. // toggle output and apply a reload.
if(counter && ticks >= counter) { if(counter && ticks >= counter) {
set_output(output ^ true); set_output<channel>(observer, output ^ true);
ticks -= counter; ticks -= counter;
const uint16_t reload_mask = output ? 0xffff : 0xfffe; const uint16_t reload_mask = output ? 0xffff : 0xfffe;
@ -155,7 +166,7 @@ class PIT {
// Check for a step from 2 to 1 within the next batch of ticks, which would cause output // Check for a step from 2 to 1 within the next batch of ticks, which would cause output
// to go high. // to go high.
if(counter > 1 && ticks >= counter - 1) { if(counter > 1 && ticks >= counter - 1) {
set_output(true); set_output<channel>(observer, true);
ticks -= counter - 1; ticks -= counter - 1;
counter = 1; counter = 1;
continue; continue;
@ -163,7 +174,7 @@ class PIT {
// If there is a step from 1 to 0, reload and set output back to low. // If there is a step from 1 to 0, reload and set output back to low.
if(counter && ticks >= counter) { if(counter && ticks >= counter) {
set_output(false); set_output<channel>(observer, false);
ticks -= counter; ticks -= counter;
counter = reload; counter = reload;
continue; continue;
@ -180,7 +191,8 @@ class PIT {
} }
} }
void write(uint8_t value) { template <int channel>
void write([[maybe_unused]] PITObserver &observer, uint8_t value) {
switch(latch_mode) { switch(latch_mode) {
case LatchMode::LowOnly: case LatchMode::LowOnly:
reload = (reload & 0xff00) | value; reload = (reload & 0xff00) | value;
@ -225,22 +237,20 @@ class PIT {
} }
} }
void set_output(bool level) { template <int channel>
void set_output(PITObserver &observer, bool level) {
if(output == level) { if(output == level) {
return; return;
} }
// TODO: how should time be notified?
observer.template update_output<channel>(level);
output = level; output = level;
// TODO: notify _someone_.
} }
} channels_[3]; } channels_[3];
// TODO: // TODO:
// //
// channel 0 is connected to IRQ 0;
// channel 1 is used for DRAM refresh;
// channel 2 is gated by a PPI output and feeds into the speaker.
//
// RateGenerator: output goes high if gated. // RateGenerator: output goes high if gated.
}; };