1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-27 18:55:48 +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;
}
template <int vector>
void raise() {
template <int input>
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:
@ -103,6 +108,30 @@ class PIC {
} 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 {
// Likely to be helpful: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
public:
@ -452,7 +481,7 @@ struct Memory {
class IO {
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) {
switch(port) {
@ -555,7 +584,7 @@ class IO {
}
private:
PIT<false> &pit_;
PIT &pit_;
DMA &dma_;
PPI &ppi_;
PIC &pic_;
@ -610,7 +639,7 @@ class ConcreteMachine:
ConcreteMachine(
[[maybe_unused]] const Analyser::Static::Target &target,
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.
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.
@ -668,14 +697,17 @@ class ConcreteMachine:
}
private:
PIT<false> pit_;
DMA dma_;
i8255PortHandler ppi_handler_;
PPI ppi_;
PIC pic_;
DMA dma_;
PITObserver pit_observer_;
i8255PortHandler ppi_handler_;
PIT pit_;
PPI ppi_;
struct Context {
Context(PIT<false> &pit, DMA &dma, PPI &ppi, PIC &pic) :
Context(PIT &pit, DMA &dma, PPI &ppi, PIC &pic) :
segments(registers),
memory(registers, segments),
flow_controller(registers, segments),

View File

@ -11,15 +11,17 @@
namespace PCCompatible {
template <bool is_8254>
class PIT {
template <bool is_8254, typename PITObserver>
class i8237 {
public:
i8237(PITObserver &observer) : observer_(observer) {}
template <int channel> uint8_t read() {
return channels_[channel].read();
}
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) {
@ -29,20 +31,27 @@ class PIT {
read_back_ = is_8254;
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) {
// TODO: be intelligent enough to take ticks outside the loop when appropriate.
auto ticks = cycles.as<int>();
while(ticks--) {
channels_[0].advance(1);
channels_[1].advance(1);
channels_[2].advance(1);
channels_[0].template advance<0>(observer_, 1);
channels_[1].template advance<1>(observer_, 1);
channels_[2].template advance<2>(observer_, 1);
}
}
private:
// The target for output changes.
PITObserver &observer_;
// Supported only on 8254s.
bool read_back_ = false;
@ -80,7 +89,8 @@ class PIT {
latch = counter;
}
void set_mode(uint8_t value) {
template <int channel>
void set_mode(PITObserver &observer, uint8_t value) {
switch((value >> 4) & 3) {
default:
latch_value();
@ -108,19 +118,20 @@ class PIT {
case OperatingMode::InterruptOnTerminalCount:
case OperatingMode::HardwareRetriggerableOneShot:
set_output(false);
set_output<channel>(observer, false);
awaiting_reload = true;
break;
case OperatingMode::RateGenerator:
case OperatingMode::SquareWaveGenerator:
set_output(true);
set_output<channel>(observer, true);
awaiting_reload = true;
break;
}
}
void advance(int ticks) {
template <int channel>
void advance(PITObserver &observer, int ticks) {
if(gated || awaiting_reload) return;
// TODO: BCD mode is completely ignored below. Possibly not too important.
@ -128,7 +139,7 @@ class PIT {
case OperatingMode::InterruptOnTerminalCount:
case OperatingMode::HardwareRetriggerableOneShot:
// 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;
break;
@ -138,7 +149,7 @@ class PIT {
// If there's a step from 1 to 0 within the next batch of ticks,
// toggle output and apply a reload.
if(counter && ticks >= counter) {
set_output(output ^ true);
set_output<channel>(observer, output ^ true);
ticks -= counter;
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
// to go high.
if(counter > 1 && ticks >= counter - 1) {
set_output(true);
set_output<channel>(observer, true);
ticks -= counter - 1;
counter = 1;
continue;
@ -163,7 +174,7 @@ class PIT {
// If there is a step from 1 to 0, reload and set output back to low.
if(counter && ticks >= counter) {
set_output(false);
set_output<channel>(observer, false);
ticks -= counter;
counter = reload;
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) {
case LatchMode::LowOnly:
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) {
return;
}
// TODO: how should time be notified?
observer.template update_output<channel>(level);
output = level;
// TODO: notify _someone_.
}
} channels_[3];
// 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.
};