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:
parent
f0e2ef5e28
commit
695282b838
@ -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),
|
||||||
|
@ -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.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user