mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-20 14:29:11 +00:00
Pull out the PIC, DMA.
This commit is contained in:
parent
a1e118a1ff
commit
375a9f9ff5
83
Machines/PCCompatible/DMA.hpp
Normal file
83
Machines/PCCompatible/DMA.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// DMA.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 21/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef DMA_hpp
|
||||||
|
#define DMA_hpp
|
||||||
|
|
||||||
|
#include "../../Numeric/RegisterSizes.hpp"
|
||||||
|
|
||||||
|
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];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* DMA_hpp */
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include "PCCompatible.hpp"
|
#include "PCCompatible.hpp"
|
||||||
|
|
||||||
|
#include "DMA.hpp"
|
||||||
|
#include "PIC.hpp"
|
||||||
#include "PIT.hpp"
|
#include "PIT.hpp"
|
||||||
|
|
||||||
#include "../../InstructionSets/x86/Decoder.hpp"
|
#include "../../InstructionSets/x86/Decoder.hpp"
|
||||||
@ -27,147 +29,6 @@
|
|||||||
|
|
||||||
namespace PCCompatible {
|
namespace PCCompatible {
|
||||||
|
|
||||||
// Cf. https://helppc.netcore2k.net/hardware/pic
|
|
||||||
class PIC {
|
|
||||||
public:
|
|
||||||
template <int address>
|
|
||||||
void write(uint8_t value) {
|
|
||||||
if(address) {
|
|
||||||
if(config_.word >= 0) {
|
|
||||||
switch(config_.word) {
|
|
||||||
case 0:
|
|
||||||
vector_base_ = value;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
if(config_.has_fourth_word) {
|
|
||||||
// TODO:
|
|
||||||
//
|
|
||||||
// (1) slave mask if this is a master;
|
|
||||||
// (2) master interrupt attachment if this is a slave.
|
|
||||||
}
|
|
||||||
[[fallthrough]];
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
auto_eoi_ = value & 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++config_.word;
|
|
||||||
if(config_.word == (config_.has_fourth_word ? 3 : 2)) {
|
|
||||||
config_.word = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mask_ = value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(value & 0x10) {
|
|
||||||
//
|
|
||||||
// Initialisation Command Word 1.
|
|
||||||
//
|
|
||||||
|
|
||||||
config_.word = 0;
|
|
||||||
config_.has_fourth_word = value & 1;
|
|
||||||
|
|
||||||
if(!config_.has_fourth_word) {
|
|
||||||
auto_eoi_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
single_pic_ = value & 2;
|
|
||||||
four_byte_vectors_ = value & 4;
|
|
||||||
level_triggered_ = value & 8;
|
|
||||||
} else if(value & 0x08) {
|
|
||||||
//
|
|
||||||
// Operation Control Word 3.
|
|
||||||
//
|
|
||||||
|
|
||||||
// b6: 1 => use b5; 0 => ignore.
|
|
||||||
// b5: 1 => set special mask; 0 => clear.
|
|
||||||
// b2: 1 => poll command issued; 0 => not.
|
|
||||||
// b1: 1 => use b0; 0 => ignore.
|
|
||||||
// b0: 1 => read IRR on next read; 0 => read ISR.
|
|
||||||
} else {
|
|
||||||
//
|
|
||||||
// Operation Control Word 2.
|
|
||||||
//
|
|
||||||
|
|
||||||
// b7, b6, b5: EOI type.
|
|
||||||
// b2, b1, b0: interrupt level to acknowledge.
|
|
||||||
if((value >> 5) == 0b001) {
|
|
||||||
// Non-specific EOI.
|
|
||||||
awaiting_eoi_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int address>
|
|
||||||
uint8_t read() {
|
|
||||||
if(address) {
|
|
||||||
return mask_;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int input>
|
|
||||||
void apply_edge(bool final_level) {
|
|
||||||
const uint8_t input_mask = 1 << input;
|
|
||||||
|
|
||||||
// Guess: level triggered means the request can be forwarded only so long as the
|
|
||||||
// relevant input is actually high. Whereas edge triggered implies capturing state.
|
|
||||||
if(level_triggered_) {
|
|
||||||
requests_ &= ~input_mask;
|
|
||||||
}
|
|
||||||
if(final_level) {
|
|
||||||
requests_ |= input_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pending() {
|
|
||||||
// Per the OSDev Wiki, masking is applied after the fact.
|
|
||||||
return !awaiting_eoi_ && (requests_ & ~mask_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int acknowledge() {
|
|
||||||
awaiting_eoi_ = true;
|
|
||||||
|
|
||||||
// TODO: there's bound to be a better solution than this search?
|
|
||||||
// TODO: is this the right priority order?
|
|
||||||
in_service_ = 0x80;
|
|
||||||
int id = 7;
|
|
||||||
while(!(in_service_ & requests_)) {
|
|
||||||
in_service_ >>= 1;
|
|
||||||
--id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(in_service_) {
|
|
||||||
requests_ &= ~in_service_;
|
|
||||||
return vector_base_ + id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spurious interrupt.
|
|
||||||
return vector_base_ + 7;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool single_pic_ = false;
|
|
||||||
bool four_byte_vectors_ = false;
|
|
||||||
bool level_triggered_ = false;
|
|
||||||
bool auto_eoi_ = false;
|
|
||||||
|
|
||||||
uint8_t vector_base_ = 0;
|
|
||||||
uint8_t mask_ = 0;
|
|
||||||
bool awaiting_eoi_ = false;
|
|
||||||
|
|
||||||
uint8_t requests_ = 0;
|
|
||||||
uint8_t in_service_ = 0;
|
|
||||||
|
|
||||||
struct ConfgurationState {
|
|
||||||
int word;
|
|
||||||
bool has_fourth_word;
|
|
||||||
} config_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class PITObserver {
|
class PITObserver {
|
||||||
public:
|
public:
|
||||||
PITObserver(PIC &pic) : pic_(pic) {}
|
PITObserver(PIC &pic) : pic_(pic) {}
|
||||||
@ -237,71 +98,6 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
|||||||
};
|
};
|
||||||
using PPI = Intel::i8255::i8255<i8255PortHandler>;
|
using PPI = Intel::i8255::i8255<i8255PortHandler>;
|
||||||
|
|
||||||
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];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Registers {
|
struct Registers {
|
||||||
public:
|
public:
|
||||||
static constexpr bool is_32bit = false;
|
static constexpr bool is_32bit = false;
|
||||||
|
156
Machines/PCCompatible/PIC.hpp
Normal file
156
Machines/PCCompatible/PIC.hpp
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
//
|
||||||
|
// PIC.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 21/11/2023.
|
||||||
|
// Copyright © 2023 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef PIC_hpp
|
||||||
|
#define PIC_hpp
|
||||||
|
|
||||||
|
namespace PCCompatible {
|
||||||
|
|
||||||
|
// Cf. https://helppc.netcore2k.net/hardware/pic
|
||||||
|
class PIC {
|
||||||
|
public:
|
||||||
|
template <int address>
|
||||||
|
void write(uint8_t value) {
|
||||||
|
if(address) {
|
||||||
|
if(config_.word >= 0) {
|
||||||
|
switch(config_.word) {
|
||||||
|
case 0:
|
||||||
|
vector_base_ = value;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if(config_.has_fourth_word) {
|
||||||
|
// TODO:
|
||||||
|
//
|
||||||
|
// (1) slave mask if this is a master;
|
||||||
|
// (2) master interrupt attachment if this is a slave.
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
auto_eoi_ = value & 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++config_.word;
|
||||||
|
if(config_.word == (config_.has_fourth_word ? 3 : 2)) {
|
||||||
|
config_.word = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mask_ = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(value & 0x10) {
|
||||||
|
//
|
||||||
|
// Initialisation Command Word 1.
|
||||||
|
//
|
||||||
|
|
||||||
|
config_.word = 0;
|
||||||
|
config_.has_fourth_word = value & 1;
|
||||||
|
|
||||||
|
if(!config_.has_fourth_word) {
|
||||||
|
auto_eoi_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
single_pic_ = value & 2;
|
||||||
|
four_byte_vectors_ = value & 4;
|
||||||
|
level_triggered_ = value & 8;
|
||||||
|
} else if(value & 0x08) {
|
||||||
|
//
|
||||||
|
// Operation Control Word 3.
|
||||||
|
//
|
||||||
|
|
||||||
|
// b6: 1 => use b5; 0 => ignore.
|
||||||
|
// b5: 1 => set special mask; 0 => clear.
|
||||||
|
// b2: 1 => poll command issued; 0 => not.
|
||||||
|
// b1: 1 => use b0; 0 => ignore.
|
||||||
|
// b0: 1 => read IRR on next read; 0 => read ISR.
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// Operation Control Word 2.
|
||||||
|
//
|
||||||
|
|
||||||
|
// b7, b6, b5: EOI type.
|
||||||
|
// b2, b1, b0: interrupt level to acknowledge.
|
||||||
|
if((value >> 5) == 0b001) {
|
||||||
|
// Non-specific EOI.
|
||||||
|
awaiting_eoi_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int address>
|
||||||
|
uint8_t read() {
|
||||||
|
if(address) {
|
||||||
|
return mask_;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <int input>
|
||||||
|
void apply_edge(bool final_level) {
|
||||||
|
const uint8_t input_mask = 1 << input;
|
||||||
|
|
||||||
|
// Guess: level triggered means the request can be forwarded only so long as the
|
||||||
|
// relevant input is actually high. Whereas edge triggered implies capturing state.
|
||||||
|
if(level_triggered_) {
|
||||||
|
requests_ &= ~input_mask;
|
||||||
|
}
|
||||||
|
if(final_level) {
|
||||||
|
requests_ |= input_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pending() {
|
||||||
|
// Per the OSDev Wiki, masking is applied after the fact.
|
||||||
|
return !awaiting_eoi_ && (requests_ & ~mask_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int acknowledge() {
|
||||||
|
awaiting_eoi_ = true;
|
||||||
|
|
||||||
|
// TODO: there's bound to be a better solution than this search?
|
||||||
|
// TODO: is this the right priority order?
|
||||||
|
in_service_ = 0x80;
|
||||||
|
int id = 7;
|
||||||
|
while(!(in_service_ & requests_)) {
|
||||||
|
in_service_ >>= 1;
|
||||||
|
--id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(in_service_) {
|
||||||
|
requests_ &= ~in_service_;
|
||||||
|
return vector_base_ + id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spurious interrupt.
|
||||||
|
return vector_base_ + 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool single_pic_ = false;
|
||||||
|
bool four_byte_vectors_ = false;
|
||||||
|
bool level_triggered_ = false;
|
||||||
|
bool auto_eoi_ = false;
|
||||||
|
|
||||||
|
uint8_t vector_base_ = 0;
|
||||||
|
uint8_t mask_ = 0;
|
||||||
|
bool awaiting_eoi_ = false;
|
||||||
|
|
||||||
|
uint8_t requests_ = 0;
|
||||||
|
uint8_t in_service_ = 0;
|
||||||
|
|
||||||
|
struct ConfgurationState {
|
||||||
|
int word;
|
||||||
|
bool has_fourth_word;
|
||||||
|
} config_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PIC_hpp */
|
@ -1143,6 +1143,8 @@
|
|||||||
425739362B051EA800B7D1E4 /* PCCompatible.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCCompatible.hpp; sourceTree = "<group>"; };
|
425739362B051EA800B7D1E4 /* PCCompatible.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCCompatible.hpp; sourceTree = "<group>"; };
|
||||||
425739372B051EA800B7D1E4 /* PCCompatible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCCompatible.cpp; sourceTree = "<group>"; };
|
425739372B051EA800B7D1E4 /* PCCompatible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCCompatible.cpp; sourceTree = "<group>"; };
|
||||||
4267A9C72B0C26FA008A59BB /* PIT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIT.hpp; sourceTree = "<group>"; };
|
4267A9C72B0C26FA008A59BB /* PIT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIT.hpp; sourceTree = "<group>"; };
|
||||||
|
4267A9C82B0D4EC2008A59BB /* PIC.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIC.hpp; sourceTree = "<group>"; };
|
||||||
|
4267A9C92B0D4F17008A59BB /* DMA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMA.hpp; sourceTree = "<group>"; };
|
||||||
4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = "<group>"; };
|
4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = "<group>"; };
|
||||||
428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = "<group>"; };
|
428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = "<group>"; };
|
||||||
428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; };
|
428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = "<group>"; };
|
||||||
@ -2364,6 +2366,8 @@
|
|||||||
425739372B051EA800B7D1E4 /* PCCompatible.cpp */,
|
425739372B051EA800B7D1E4 /* PCCompatible.cpp */,
|
||||||
425739362B051EA800B7D1E4 /* PCCompatible.hpp */,
|
425739362B051EA800B7D1E4 /* PCCompatible.hpp */,
|
||||||
4267A9C72B0C26FA008A59BB /* PIT.hpp */,
|
4267A9C72B0C26FA008A59BB /* PIT.hpp */,
|
||||||
|
4267A9C82B0D4EC2008A59BB /* PIC.hpp */,
|
||||||
|
4267A9C92B0D4F17008A59BB /* DMA.hpp */,
|
||||||
);
|
);
|
||||||
path = PCCompatible;
|
path = PCCompatible;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user