mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-29 12:50:28 +00:00
Add enough of the DMA subsystem to trip over in PPI world.
This commit is contained in:
parent
119c83eb18
commit
55f466f2fa
@ -13,6 +13,8 @@
|
|||||||
#include "../../InstructionSets/x86/Instruction.hpp"
|
#include "../../InstructionSets/x86/Instruction.hpp"
|
||||||
#include "../../InstructionSets/x86/Perform.hpp"
|
#include "../../InstructionSets/x86/Perform.hpp"
|
||||||
|
|
||||||
|
#include "../../Numeric/RegisterSizes.hpp"
|
||||||
|
|
||||||
#include "../ScanProducer.hpp"
|
#include "../ScanProducer.hpp"
|
||||||
#include "../TimedMachine.hpp"
|
#include "../TimedMachine.hpp"
|
||||||
|
|
||||||
@ -20,6 +22,71 @@
|
|||||||
|
|
||||||
namespace PCCompatible {
|
namespace PCCompatible {
|
||||||
|
|
||||||
|
class DMA {
|
||||||
|
public:
|
||||||
|
void flip_flop_reset() {
|
||||||
|
next_access_low = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mask_reset() {
|
||||||
|
// TODO: set all mask bits off.
|
||||||
|
}
|
||||||
|
|
||||||
|
void master_reset() {
|
||||||
|
flip_flop_reset();
|
||||||
|
// TODO: clear status, set all mask bits on.
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int address>
|
||||||
|
void write(uint8_t value) {
|
||||||
|
constexpr int channel = (address >> 1) & 3;
|
||||||
|
constexpr bool is_count = address & 1;
|
||||||
|
|
||||||
|
next_access_low ^= true;
|
||||||
|
if(next_access_low) {
|
||||||
|
if constexpr (is_count) {
|
||||||
|
channels_[channel].count.halves.high = value;
|
||||||
|
} else {
|
||||||
|
channels_[channel].address.halves.high = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (is_count) {
|
||||||
|
channels_[channel].count.halves.low = value;
|
||||||
|
} else {
|
||||||
|
channels_[channel].address.halves.low = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int address>
|
||||||
|
uint8_t read() {
|
||||||
|
constexpr int channel = (address >> 1) & 3;
|
||||||
|
constexpr bool is_count = address & 1;
|
||||||
|
|
||||||
|
next_access_low ^= true;
|
||||||
|
if(next_access_low) {
|
||||||
|
if constexpr (is_count) {
|
||||||
|
return channels_[channel].count.halves.high;
|
||||||
|
} else {
|
||||||
|
return channels_[channel].address.halves.high;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (is_count) {
|
||||||
|
return channels_[channel].count.halves.low;
|
||||||
|
} else {
|
||||||
|
return channels_[channel].address.halves.low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool next_access_low = true;
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
CPU::RegisterPair16 address, count;
|
||||||
|
} channels_[4];
|
||||||
|
};
|
||||||
|
|
||||||
template <bool is_8254>
|
template <bool is_8254>
|
||||||
class PIT {
|
class PIT {
|
||||||
public:
|
public:
|
||||||
@ -46,7 +113,6 @@ class PIT {
|
|||||||
printf("Set mode on %d\n", channel_id);
|
printf("Set mode on %d\n", channel_id);
|
||||||
|
|
||||||
Channel &channel = channels_[channel_id];
|
Channel &channel = channels_[channel_id];
|
||||||
channel.next_write_high = false;
|
|
||||||
switch((value >> 4) & 3) {
|
switch((value >> 4) & 3) {
|
||||||
default:
|
default:
|
||||||
channel.latch_value();
|
channel.latch_value();
|
||||||
@ -57,7 +123,7 @@ class PIT {
|
|||||||
case 3: channel.latch_mode = LatchMode::LowHigh; break;
|
case 3: channel.latch_mode = LatchMode::LowHigh; break;
|
||||||
}
|
}
|
||||||
channel.is_bcd = value & 1;
|
channel.is_bcd = value & 1;
|
||||||
channel.next_write_high = false;
|
channel.next_access_high = false;
|
||||||
|
|
||||||
const auto operating_mode = (value >> 1) & 7;
|
const auto operating_mode = (value >> 1) & 7;
|
||||||
switch(operating_mode) {
|
switch(operating_mode) {
|
||||||
@ -127,7 +193,7 @@ class PIT {
|
|||||||
uint16_t latch = 0;
|
uint16_t latch = 0;
|
||||||
bool output = false;
|
bool output = false;
|
||||||
|
|
||||||
bool next_write_high = false;
|
bool next_access_high = false;
|
||||||
|
|
||||||
void latch_value() {
|
void latch_value() {
|
||||||
latch = counter;
|
latch = counter;
|
||||||
@ -169,17 +235,16 @@ class PIT {
|
|||||||
reload = (reload & 0xff00) | value;
|
reload = (reload & 0xff00) | value;
|
||||||
break;
|
break;
|
||||||
case LatchMode::HighOnly:
|
case LatchMode::HighOnly:
|
||||||
reload = (reload & 0x00ff) | (value << 8);
|
reload = uint16_t((reload & 0x00ff) | (value << 8));
|
||||||
break;
|
break;
|
||||||
case LatchMode::LowHigh:
|
case LatchMode::LowHigh:
|
||||||
if(!next_write_high) {
|
next_access_high ^= true;
|
||||||
|
if(next_access_high) {
|
||||||
reload = (reload & 0xff00) | value;
|
reload = (reload & 0xff00) | value;
|
||||||
next_write_high = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
reload = (reload & 0x00ff) | (value << 8);
|
reload = uint16_t((reload & 0x00ff) | (value << 8));
|
||||||
next_write_high = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,8 +264,8 @@ class PIT {
|
|||||||
case LatchMode::HighOnly: return uint8_t(latch >> 8);
|
case LatchMode::HighOnly: return uint8_t(latch >> 8);
|
||||||
default:
|
default:
|
||||||
case LatchMode::LowHigh:
|
case LatchMode::LowHigh:
|
||||||
next_write_high ^= true;
|
next_access_high ^= true;
|
||||||
return next_write_high ? uint8_t(latch) : uint8_t(latch >> 8);
|
return next_access_high ? uint8_t(latch) : uint8_t(latch >> 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,7 +529,7 @@ struct Memory {
|
|||||||
|
|
||||||
class IO {
|
class IO {
|
||||||
public:
|
public:
|
||||||
IO(PIT<false> &pit) : pit_(pit) {}
|
IO(PIT<false> &pit, DMA &dma) : pit_(pit), dma_(dma) {}
|
||||||
|
|
||||||
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) {
|
||||||
@ -481,13 +546,23 @@ class IO {
|
|||||||
printf("TODO: NMIs %s\n", (value & 0x80) ? "masked" : "unmasked");
|
printf("TODO: NMIs %s\n", (value & 0x80) ? "masked" : "unmasked");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x0000: case 0x0001: case 0x0002: case 0x0003:
|
case 0x0000: dma_.write<0>(value); break;
|
||||||
case 0x0004: case 0x0005: case 0x0006: case 0x0007:
|
case 0x0001: dma_.write<1>(value); break;
|
||||||
|
case 0x0002: dma_.write<2>(value); break;
|
||||||
|
case 0x0003: dma_.write<3>(value); break;
|
||||||
|
case 0x0004: dma_.write<4>(value); break;
|
||||||
|
case 0x0005: dma_.write<5>(value); break;
|
||||||
|
case 0x0006: dma_.write<6>(value); break;
|
||||||
|
case 0x0007: dma_.write<7>(value); break;
|
||||||
|
|
||||||
case 0x0008: case 0x0009: case 0x000a: case 0x000b:
|
case 0x0008: case 0x0009: case 0x000a: case 0x000b:
|
||||||
case 0x000c: case 0x000d: case 0x000e: case 0x000f:
|
case 0x000c: case 0x000f:
|
||||||
printf("TODO: DMA write of %02x at %04x\n", value, port);
|
printf("TODO: DMA write of %02x at %04x\n", value, port);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0x000d: dma_.master_reset(); break;
|
||||||
|
case 0x000e: dma_.mask_reset(); break;
|
||||||
|
|
||||||
case 0x0060: case 0x0061: case 0x0062: case 0x0063:
|
case 0x0060: case 0x0061: case 0x0062: case 0x0063:
|
||||||
case 0x0064: case 0x0065: case 0x0066: case 0x0067:
|
case 0x0064: case 0x0065: case 0x0066: case 0x0067:
|
||||||
case 0x0068: case 0x0069: case 0x006a: case 0x006b:
|
case 0x0068: case 0x0069: case 0x006a: case 0x006b:
|
||||||
@ -529,6 +604,15 @@ class IO {
|
|||||||
printf("Unhandled in: %04x\n", port);
|
printf("Unhandled in: %04x\n", port);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 0x0000: return dma_.read<0>();
|
||||||
|
case 0x0001: return dma_.read<1>();
|
||||||
|
case 0x0002: return dma_.read<2>();
|
||||||
|
case 0x0003: return dma_.read<3>();
|
||||||
|
case 0x0004: return dma_.read<4>();
|
||||||
|
case 0x0005: return dma_.read<5>();
|
||||||
|
case 0x0006: return dma_.read<6>();
|
||||||
|
case 0x0007: return dma_.read<7>();
|
||||||
|
|
||||||
case 0x0040: return pit_.read<0>();
|
case 0x0040: return pit_.read<0>();
|
||||||
case 0x0041: return pit_.read<1>();
|
case 0x0041: return pit_.read<1>();
|
||||||
case 0x0042: return pit_.read<2>();
|
case 0x0042: return pit_.read<2>();
|
||||||
@ -545,6 +629,7 @@ class IO {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
PIT<false> &pit_;
|
PIT<false> &pit_;
|
||||||
|
DMA &dma_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FlowController {
|
class FlowController {
|
||||||
@ -596,7 +681,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
|
||||||
) : context(pit_) {
|
) : context(pit_, dma_) {
|
||||||
// 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.
|
||||||
@ -655,13 +740,14 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
PIT<false> pit_;
|
PIT<false> pit_;
|
||||||
|
DMA dma_;
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
Context(PIT<false> &pit) :
|
Context(PIT<false> &pit, DMA &dma) :
|
||||||
segments(registers),
|
segments(registers),
|
||||||
memory(registers, segments),
|
memory(registers, segments),
|
||||||
flow_controller(registers, segments),
|
flow_controller(registers, segments),
|
||||||
io(pit)
|
io(pit, dma)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user