diff options
| -rw-r--r-- | arch/x86_64/include/x86_64/device_io/port_io.hpp | 155 |
1 files changed, 53 insertions, 102 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 352a4d0..ceabf4a 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 @@ -1,130 +1,81 @@ #ifndef TEACHOS_X86_64_IO_PORT_IO_HPP #define TEACHOS_X86_64_IO_PORT_IO_HPP +#include <array> #include <cstddef> #include <cstdint> +#include <string_view> #include <type_traits> namespace teachos::io::x86_64 { - /** - * @brief An I/O port of a given size at a given address. - * - * @tparam Address The address (port number) of the I/O port. - * @tparam Size The size (in bytes) of the I/O port. - */ + + //! 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, std::size_t Size> + requires(Size == 1 || Size == 2 || Size == 4) struct port { - static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size"); - - /** - * @brief The type of data available for reading and writing through this port. - */ - using io_type = + //! The type of data available for reading and writing through this port. + using value_type = std::conditional_t<Size == 1, std::byte, std::conditional_t<Size == 2, std::uint16_t, std::uint32_t>>; - /** - * @brief Write a byte to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 1) + //! Read from the I/O port. + //! + //! @return The data read from the I/O port. + auto static read() noexcept -> value_type { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%al\n" - "out %%al, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "al"); + auto data = value_type{}; + asm volatile((read_code[asm_template_index]) + : [data] "=m"(data) + : [port] "i"(Address) + : "dx", (data_register[asm_template_index])); + return data; } - /** - * @brief Write a word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 2) + //! 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 { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%ax\n" - "out %%ax, %%dx\n" + asm volatile((write_code[asm_template_index]) : : [port] "i"(Address), [data] "im"(data) - : "dx", "ax"); + : "dx", (data_register[asm_template_index])); } - /** - * @brief Write a double-word to the I/O port. - * - * @param data The data to write to the I/O port. - */ - auto static write(io_type data) -> void - requires(Size == 4) - { - asm volatile("mov %[port], %%dx\n" - "mov %[data], %%eax\n" - "out %%eax, %%dx\n" - : - : [port] "i"(Address), [data] "im"(data) - : "dx", "eax"); - } + 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; - /** - * @brief Read a byte from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 1) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%al\n" - "mov %%al, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "al"); - return data; - } + //! 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]"}, + }; - /** - * @brief Read a word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 2) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%ax\n" - "mov %%ax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "ax"); - return data; - } + //! The assembly templates used for writing to an I/O port. + constexpr auto static write_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"}, + }; - /** - * @brief Read a double-word from the I/O port. - * - * @return The data read from the I/O port. - */ - auto static read() -> io_type - requires(Size == 4) - { - auto data = io_type{}; - asm volatile("mov %[port], %%dx\n" - "in %%dx, %%eax\n" - "mov %%eax, %[data]\n" - : [data] "=m"(data) - : [port] "i"(Address) - : "dx", "eax"); - return data; - } + //! 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"}, + }; }; } // namespace teachos::io::x86_64 |
