mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Implementing counting for a couple of PIT modes.
This commit is contained in:
parent
af885ccf08
commit
05e93f0eb3
@ -24,16 +24,20 @@ template <bool is_8254>
|
||||
class PIT {
|
||||
public:
|
||||
template <int channel> uint8_t read() {
|
||||
return 0;
|
||||
return channels_[channel].read();
|
||||
}
|
||||
|
||||
template <int channel> void write([[maybe_unused]] uint8_t value) {
|
||||
template <int channel> void write(uint8_t value) {
|
||||
printf("Write to %d\n", channel);
|
||||
channels_[channel].write(value);
|
||||
}
|
||||
|
||||
void set_mode(uint8_t value) {
|
||||
const int channel_id = (value >> 6) & 3;
|
||||
if(channel_id == 3) {
|
||||
read_back_ = is_8254;
|
||||
|
||||
// TODO: decode rest of read-back command.
|
||||
return;
|
||||
}
|
||||
|
||||
@ -47,6 +51,7 @@ class PIT {
|
||||
case 2: channel.latch_mode = LatchMode::HighOnly; break;
|
||||
case 3: channel.latch_mode = LatchMode::LowHigh; break;
|
||||
}
|
||||
channel.next_write_high = false;
|
||||
|
||||
const auto operating_mode = (value >> 3) & 7;
|
||||
switch(operating_mode) {
|
||||
@ -54,6 +59,32 @@ class PIT {
|
||||
case 6: channel.mode = OperatingMode::RateGenerator; break;
|
||||
case 7: channel.mode = OperatingMode::SquareWaveGenerator; break;
|
||||
}
|
||||
|
||||
printf("%d switches to mode %d\n", channel_id, int(channel.mode));
|
||||
|
||||
// Set up operating mode.
|
||||
switch(channel.mode) {
|
||||
case OperatingMode::InterruptOnTerminalCount:
|
||||
channel.output = false;
|
||||
channel.awaiting_reload = true;
|
||||
break;
|
||||
|
||||
case OperatingMode::RateGenerator:
|
||||
channel.output = true;
|
||||
channel.awaiting_reload = true;
|
||||
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--) {
|
||||
bool output_changed;
|
||||
output_changed = channels_[0].advance(1);
|
||||
output_changed |= channels_[1].advance(1);
|
||||
output_changed |= channels_[2].advance(1);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -80,8 +111,100 @@ class PIT {
|
||||
OperatingMode mode = OperatingMode::InterruptOnTerminalCount;
|
||||
bool is_bcd = false;
|
||||
|
||||
void latch_value() {}
|
||||
bool gated = false;
|
||||
bool awaiting_reload = true;
|
||||
|
||||
uint16_t counter = 0;
|
||||
uint16_t reload = 0;
|
||||
uint16_t latch = 0;
|
||||
bool output = false;
|
||||
|
||||
bool next_write_high = false;
|
||||
|
||||
void latch_value() {
|
||||
latch = counter;
|
||||
}
|
||||
|
||||
bool advance(int ticks) {
|
||||
if(gated || awaiting_reload) return false;
|
||||
|
||||
// TODO: BCD mode is completely ignored below. Possibly not too important.
|
||||
const bool initial_output = output;
|
||||
switch(mode) {
|
||||
case OperatingMode::InterruptOnTerminalCount:
|
||||
// Output goes permanently high upon a tick from 1 to 0; reload value is not used on wraparound.
|
||||
output |= counter <= ticks;
|
||||
counter -= ticks;
|
||||
break;
|
||||
|
||||
case OperatingMode::RateGenerator:
|
||||
// Output goes low upon a tick from 2 to 1. It goes high again on 1 to 0, and the reload value is used.
|
||||
if(counter <= ticks) {
|
||||
counter = reload - ticks + counter;
|
||||
} else {
|
||||
counter -= ticks;
|
||||
}
|
||||
output = counter != 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO.
|
||||
break;
|
||||
}
|
||||
|
||||
return output != initial_output;
|
||||
}
|
||||
|
||||
void write(uint8_t value) {
|
||||
switch(latch_mode) {
|
||||
case LatchMode::LowOnly:
|
||||
reload = (reload & 0xff00) | value;
|
||||
break;
|
||||
case LatchMode::HighOnly:
|
||||
reload = (reload & 0x00ff) | (value << 8);
|
||||
break;
|
||||
case LatchMode::LowHigh:
|
||||
if(!next_write_high) {
|
||||
reload = (reload & 0xff00) | value;
|
||||
next_write_high = true;
|
||||
return;
|
||||
}
|
||||
|
||||
reload = (reload & 0x00ff) | (value << 8);
|
||||
next_write_high = false;
|
||||
break;
|
||||
}
|
||||
|
||||
awaiting_reload = false;
|
||||
|
||||
switch(mode) {
|
||||
case OperatingMode::InterruptOnTerminalCount:
|
||||
case OperatingMode::RateGenerator:
|
||||
counter = reload;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t read() {
|
||||
switch(latch_mode) {
|
||||
case LatchMode::LowOnly: return uint8_t(latch);
|
||||
case LatchMode::HighOnly: return uint8_t(latch >> 8);
|
||||
default:
|
||||
case LatchMode::LowHigh:
|
||||
next_write_high ^= true;
|
||||
return next_write_high ? uint8_t(latch) : uint8_t(latch >> 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} 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.
|
||||
};
|
||||
|
||||
struct Registers {
|
||||
@ -333,7 +456,7 @@ struct Memory {
|
||||
|
||||
class IO {
|
||||
public:
|
||||
IO(PIT &pit) : pit_(pit) {}
|
||||
IO(PIT<false> &pit) : pit_(pit) {}
|
||||
|
||||
template <typename IntT> void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) {
|
||||
switch(port) {
|
||||
@ -413,7 +536,7 @@ class IO {
|
||||
}
|
||||
|
||||
private:
|
||||
PIT &pit_;
|
||||
PIT<false> &pit_;
|
||||
};
|
||||
|
||||
class FlowController {
|
||||
@ -459,12 +582,16 @@ class ConcreteMachine:
|
||||
public MachineTypes::ScanProducer
|
||||
{
|
||||
public:
|
||||
static constexpr int PitMultiplier = 1;
|
||||
static constexpr int PitDivisor = 3;
|
||||
|
||||
ConcreteMachine(
|
||||
[[maybe_unused]] const Analyser::Static::Target &target,
|
||||
const ROMMachine::ROMFetcher &rom_fetcher
|
||||
) : context(pit_) {
|
||||
// This is actually a MIPS count; try 3 million.
|
||||
set_clock_rate(3'000'000);
|
||||
// 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.
|
||||
|
||||
// Fetch the BIOS. [8088 only, for now]
|
||||
const auto bios = ROM::Name::PCCompatibleGLaBIOS;
|
||||
@ -480,9 +607,12 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - TimedMachine.
|
||||
void run_for([[maybe_unused]] const Cycles cycles) override {
|
||||
void run_for(const Cycles cycles) override {
|
||||
auto instructions = cycles.as_integral();
|
||||
while(instructions--) {
|
||||
// First draft: all hardware runs in lockstep.
|
||||
pit_.run_for(PitDivisor / PitMultiplier);
|
||||
|
||||
// Get the next thing to execute into decoded.
|
||||
if(!context.flow_controller.should_repeat()) {
|
||||
// Decode from the current IP.
|
||||
@ -516,10 +646,10 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
private:
|
||||
PIT pit_;
|
||||
PIT<false> pit_;
|
||||
|
||||
struct Context {
|
||||
Context(PIT &pit) :
|
||||
Context(PIT<false> &pit) :
|
||||
segments(registers),
|
||||
memory(registers, segments),
|
||||
flow_controller(registers, segments),
|
||||
|
Loading…
Reference in New Issue
Block a user