From 375a9f9ff5e0c2bf117b67c8e042e4c2b7a0b539 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 21 Nov 2023 15:50:38 -0500 Subject: [PATCH] Pull out the PIC, DMA. --- Machines/PCCompatible/DMA.hpp | 83 +++++++ Machines/PCCompatible/PCCompatible.cpp | 208 +----------------- Machines/PCCompatible/PIC.hpp | 156 +++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 4 + 4 files changed, 245 insertions(+), 206 deletions(-) create mode 100644 Machines/PCCompatible/DMA.hpp create mode 100644 Machines/PCCompatible/PIC.hpp diff --git a/Machines/PCCompatible/DMA.hpp b/Machines/PCCompatible/DMA.hpp new file mode 100644 index 000000000..719bc19f0 --- /dev/null +++ b/Machines/PCCompatible/DMA.hpp @@ -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 + 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 + 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 */ diff --git a/Machines/PCCompatible/PCCompatible.cpp b/Machines/PCCompatible/PCCompatible.cpp index 7ee0a72cf..46eb59e81 100644 --- a/Machines/PCCompatible/PCCompatible.cpp +++ b/Machines/PCCompatible/PCCompatible.cpp @@ -8,6 +8,8 @@ #include "PCCompatible.hpp" +#include "DMA.hpp" +#include "PIC.hpp" #include "PIT.hpp" #include "../../InstructionSets/x86/Decoder.hpp" @@ -27,147 +29,6 @@ namespace PCCompatible { -// Cf. https://helppc.netcore2k.net/hardware/pic -class PIC { - public: - template - 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 - uint8_t read() { - if(address) { - return mask_; - } - return 0; - } - - template - 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 { public: PITObserver(PIC &pic) : pic_(pic) {} @@ -237,71 +98,6 @@ class i8255PortHandler : public Intel::i8255::PortHandler { }; using PPI = Intel::i8255::i8255; -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 - 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 - 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 { public: static constexpr bool is_32bit = false; diff --git a/Machines/PCCompatible/PIC.hpp b/Machines/PCCompatible/PIC.hpp new file mode 100644 index 000000000..c89d4a157 --- /dev/null +++ b/Machines/PCCompatible/PIC.hpp @@ -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 + 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 + uint8_t read() { + if(address) { + return mask_; + } + return 0; + } + + template + 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 */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 45be4a9f8..a1fccf3ee 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1143,6 +1143,8 @@ 425739362B051EA800B7D1E4 /* PCCompatible.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCCompatible.hpp; sourceTree = ""; }; 425739372B051EA800B7D1E4 /* PCCompatible.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCCompatible.cpp; sourceTree = ""; }; 4267A9C72B0C26FA008A59BB /* PIT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIT.hpp; sourceTree = ""; }; + 4267A9C82B0D4EC2008A59BB /* PIC.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PIC.hpp; sourceTree = ""; }; + 4267A9C92B0D4F17008A59BB /* DMA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DMA.hpp; sourceTree = ""; }; 4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = ""; }; 428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = ""; }; 428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = ""; }; @@ -2364,6 +2366,8 @@ 425739372B051EA800B7D1E4 /* PCCompatible.cpp */, 425739362B051EA800B7D1E4 /* PCCompatible.hpp */, 4267A9C72B0C26FA008A59BB /* PIT.hpp */, + 4267A9C82B0D4EC2008A59BB /* PIC.hpp */, + 4267A9C92B0D4F17008A59BB /* DMA.hpp */, ); path = PCCompatible; sourceTree = "";