aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-12-12 10:50:40 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-12-12 10:50:40 +0100
commitd89dd14c4492144d54ad755932b3e2c89a17bf74 (patch)
tree13be98b493b9c7a4174c0cc78b92e5691d07c6c4
parentf8c3c91e81f434c26dd84e5047a4420e8fb40534 (diff)
downloadteachos-d89dd14c4492144d54ad755932b3e2c89a17bf74.tar.xz
teachos-d89dd14c4492144d54ad755932b3e2c89a17bf74.zip
x86_64/device_io: split port read/write features
-rw-r--r--arch/x86_64/include/x86_64/device_io/port_io.hpp94
-rw-r--r--arch/x86_64/include/x86_64/vga/crtc.hpp8
2 files changed, 56 insertions, 46 deletions
diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp
index 9f115eb..c3e5271 100644
--- a/arch/x86_64/include/x86_64/device_io/port_io.hpp
+++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp
@@ -21,73 +21,79 @@ namespace teachos::io::x86_64
std::conditional_t<sizeof(ValueType) == 2, std::uint16_t, std::uint32_t>>{});
};
- //! An I/O port of a given size at a given address.
- //!
- //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte to
- //! double-word sized transfers.
- //!
- //! @tparam Address The address (port number) of the I/O port.
- //! @tparam Size The size (in bytes) of the I/O port.
- template<std::uint16_t Address, port_io_type ValueType>
- struct port
+ template<typename Derived>
+ struct port_read
{
- //! The type of data available for reading and writing through this port.
- using value_type = ValueType;
-
- //! The size of the I/O port
- constexpr auto static size = sizeof(value_type);
-
//! Read from the I/O port.
//!
//! @return The data read from the I/O port.
- auto static read() noexcept -> value_type
+ auto static read() noexcept
{
- auto data = value_type{};
- asm volatile((read_code[asm_template_index])
+ auto data = typename Derived::value_type{};
+ asm volatile((code[Derived::size / 2])
: [data] "=m"(data)
- : [port] "i"(Address)
- : "dx", (data_register[asm_template_index]));
+ : [port] "i"(Derived::size)
+ : "dx", (Derived::data_register));
return data;
}
+ private:
+ //! The assembly templates used for reading from an I/O port.
+ constexpr auto static code = std::array{
+ std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"},
+ std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"},
+ std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"},
+ };
+ };
+
+ template<typename Derived>
+ struct port_write
+ {
//! Write data to the I/O port.
//!
//! @param data The data to write to the I/O port.
- auto static write(value_type data) noexcept -> void
+ auto static write(std::same_as<typename Derived::value_type> auto data) noexcept -> void
{
- asm volatile((write_code[asm_template_index])
+ asm volatile((code[Derived::size / 2])
:
- : [port] "i"(Address), [data] "im"(data)
- : "dx", (data_register[asm_template_index]));
+ : [port] "i"(Derived::address), [data] "im"(data)
+ : "dx", (Derived::data_register));
}
private:
- //! The index into the assembly template and register arrays.
- //!
- //! This index is used to select the correct assembly templates for the given operation (read or write), as well as
- //! the clobbered data register, from the relevant template arrays.
- constexpr auto static asm_template_index = size / 2;
-
- //! The assembly templates used for reading from an I/O port.
- constexpr auto static read_code = std::array{
- std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"},
- std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"},
- std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"},
- };
-
//! The assembly templates used for writing to an I/O port.
- constexpr auto static write_code = std::array{
+ constexpr auto static code = std::array{
std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"},
std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"},
std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"},
};
+ };
- //! The registers clobbered by the I/O operation.
- constexpr auto static data_register = std::array{
- std::string_view{"al"},
- std::string_view{"ax"},
- std::string_view{"eax"},
- };
+ //! An I/O port of a given size at a given address.
+ //!
+ //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte
+ //! to double-word sized transfers.
+ //!
+ //! @tparam Address The address (port number) of the I/O port.
+ //! @tparam Size The size (in bytes) of the I/O port.
+ //! @tparam Features The features (readable, writeable)
+ template<std::uint16_t Address, port_io_type ValueType, template<typename> typename... Features>
+ requires(sizeof...(Features) > 0)
+ struct port : Features<port<Address, ValueType, Features...>>...
+ {
+ //! The type of the data of this port.
+ using value_type = ValueType;
+
+ //! The address of this I/O port.
+ constexpr auto static address = Address;
+
+ //! The size of this I/O port.
+ constexpr auto static size = sizeof(value_type);
+
+ //! The register clobbered by the I/O operation.
+ constexpr auto static data_register = size == 1 ? std::string_view{"al"}
+ : size == 2 ? std::string_view{"ax"}
+ : std::string_view{"eax"};
};
} // namespace teachos::io::x86_64
diff --git a/arch/x86_64/include/x86_64/vga/crtc.hpp b/arch/x86_64/include/x86_64/vga/crtc.hpp
index b2c8028..d4b4f51 100644
--- a/arch/x86_64/include/x86_64/vga/crtc.hpp
+++ b/arch/x86_64/include/x86_64/vga/crtc.hpp
@@ -7,15 +7,19 @@
namespace teachos::vga::x86_64::crtc
{
+ namespace io = io::x86_64;
+
/**
* @brief The address port of the CRT Controller.
*/
- using address = io::x86_64::port<0x3d4, std::byte>; // NOLINT(cppcoreguidelines-avoid-magic-numbers)
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
+ using address = io::port<0x3d4, std::byte, io::port_write>;
/**
* @brief The data port of the CRT Controller.
*/
- using data = io::x86_64::port<0x3d5, std::byte>; // NOLINT(cppcoreguidelines-avoid-magic-numbers)
+ // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
+ using data = io::port<0x3d5, std::byte, io::port_read, io::port_write>;
namespace registers
{