diff options
| -rw-r--r-- | arch/x86_64/include/x86_64/device_io/port_io.hpp | 94 | ||||
| -rw-r--r-- | arch/x86_64/include/x86_64/vga/crtc.hpp | 8 |
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 { |
