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 {
|
class PIT {
|
||||||
public:
|
public:
|
||||||
template <int channel> uint8_t read() {
|
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) {
|
void set_mode(uint8_t value) {
|
||||||
const int channel_id = (value >> 6) & 3;
|
const int channel_id = (value >> 6) & 3;
|
||||||
if(channel_id == 3) {
|
if(channel_id == 3) {
|
||||||
read_back_ = is_8254;
|
read_back_ = is_8254;
|
||||||
|
|
||||||
|
// TODO: decode rest of read-back command.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +51,7 @@ class PIT {
|
|||||||
case 2: channel.latch_mode = LatchMode::HighOnly; break;
|
case 2: channel.latch_mode = LatchMode::HighOnly; break;
|
||||||
case 3: channel.latch_mode = LatchMode::LowHigh; break;
|
case 3: channel.latch_mode = LatchMode::LowHigh; break;
|
||||||
}
|
}
|
||||||
|
channel.next_write_high = false;
|
||||||
|
|
||||||
const auto operating_mode = (value >> 3) & 7;
|
const auto operating_mode = (value >> 3) & 7;
|
||||||
switch(operating_mode) {
|
switch(operating_mode) {
|
||||||
@ -54,6 +59,32 @@ class PIT {
|
|||||||
case 6: channel.mode = OperatingMode::RateGenerator; break;
|
case 6: channel.mode = OperatingMode::RateGenerator; break;
|
||||||
case 7: channel.mode = OperatingMode::SquareWaveGenerator; 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:
|
private:
|
||||||
@ -80,8 +111,100 @@ class PIT {
|
|||||||
OperatingMode mode = OperatingMode::InterruptOnTerminalCount;
|
OperatingMode mode = OperatingMode::InterruptOnTerminalCount;
|
||||||
bool is_bcd = false;
|
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];
|
} 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 {
|
struct Registers {
|
||||||
@ -333,7 +456,7 @@ struct Memory {
|
|||||||
|
|
||||||
class IO {
|
class IO {
|
||||||
public:
|
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) {
|
template <typename IntT> void out([[maybe_unused]] uint16_t port, [[maybe_unused]] IntT value) {
|
||||||
switch(port) {
|
switch(port) {
|
||||||
@ -413,7 +536,7 @@ class IO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PIT &pit_;
|
PIT<false> &pit_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlowController {
|
class FlowController {
|
||||||
@ -459,12 +582,16 @@ class ConcreteMachine:
|
|||||||
public MachineTypes::ScanProducer
|
public MachineTypes::ScanProducer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static constexpr int PitMultiplier = 1;
|
||||||
|
static constexpr int PitDivisor = 3;
|
||||||
|
|
||||||
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
|
||||||
) : context(pit_) {
|
) : context(pit_) {
|
||||||
// This is actually a MIPS count; try 3 million.
|
// Use clock rate as a MIPS count; keeping it as a multiple or divisor of the PIT frequency is easy.
|
||||||
set_clock_rate(3'000'000);
|
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]
|
// Fetch the BIOS. [8088 only, for now]
|
||||||
const auto bios = ROM::Name::PCCompatibleGLaBIOS;
|
const auto bios = ROM::Name::PCCompatibleGLaBIOS;
|
||||||
@ -480,9 +607,12 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - TimedMachine.
|
// MARK: - TimedMachine.
|
||||||
void run_for([[maybe_unused]] const Cycles cycles) override {
|
void run_for(const Cycles cycles) override {
|
||||||
auto instructions = cycles.as_integral();
|
auto instructions = cycles.as_integral();
|
||||||
while(instructions--) {
|
while(instructions--) {
|
||||||
|
// First draft: all hardware runs in lockstep.
|
||||||
|
pit_.run_for(PitDivisor / PitMultiplier);
|
||||||
|
|
||||||
// Get the next thing to execute into decoded.
|
// Get the next thing to execute into decoded.
|
||||||
if(!context.flow_controller.should_repeat()) {
|
if(!context.flow_controller.should_repeat()) {
|
||||||
// Decode from the current IP.
|
// Decode from the current IP.
|
||||||
@ -516,10 +646,10 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PIT pit_;
|
PIT<false> pit_;
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
Context(PIT &pit) :
|
Context(PIT<false> &pit) :
|
||||||
segments(registers),
|
segments(registers),
|
||||||
memory(registers, segments),
|
memory(registers, segments),
|
||||||
flow_controller(registers, segments),
|
flow_controller(registers, segments),
|
||||||
|
Loading…
Reference in New Issue
Block a user