From 67f4809a04999be393e5b10e924bcb0f685c721c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 13 Jan 2026 11:29:09 +0100 Subject: x86_64/vga: split text implementation --- arch/x86_64/include/x86_64/vga/text.hpp | 202 +-------------------- arch/x86_64/include/x86_64/vga/text/attribute.hpp | 30 +++ arch/x86_64/include/x86_64/vga/text/color.hpp | 35 ++++ .../include/x86_64/vga/text/common_attributes.hpp | 37 ++++ arch/x86_64/include/x86_64/vga/text/device.hpp | 108 +++++++++++ arch/x86_64/include/x86_64/vga/text/flags.hpp | 38 ++++ arch/x86_64/src/vga/text.cpp | 64 ++++--- 7 files changed, 292 insertions(+), 222 deletions(-) create mode 100644 arch/x86_64/include/x86_64/vga/text/attribute.hpp create mode 100644 arch/x86_64/include/x86_64/vga/text/color.hpp create mode 100644 arch/x86_64/include/x86_64/vga/text/common_attributes.hpp create mode 100644 arch/x86_64/include/x86_64/vga/text/device.hpp create mode 100644 arch/x86_64/include/x86_64/vga/text/flags.hpp diff --git a/arch/x86_64/include/x86_64/vga/text.hpp b/arch/x86_64/include/x86_64/vga/text.hpp index 64cb2b9..f81ab60 100644 --- a/arch/x86_64/include/x86_64/vga/text.hpp +++ b/arch/x86_64/include/x86_64/vga/text.hpp @@ -1,196 +1,10 @@ -#ifndef TEACHOS_X86_64_VIDEO_VGA_TEXT_HPP -#define TEACHOS_X86_64_VIDEO_VGA_TEXT_HPP - -#include "kapi/cio.hpp" - -#include -#include -#include -#include -#include - -namespace teachos::vga::x86_64::text -{ - /** - * @brief The colors available in the standard VGA text mode. - */ - enum struct color : std::uint8_t - { - black, ///< Equivalent to HTML color \#000000. - blue, ///< Equivalent to HTML color \#0000AA. - green, ///< Equivalent to HTML color \#00AA00. - cyan, ///< Equivalent to HTML color \#00AAAA. - red, ///< Equivalent to HTML color \#AA0000. - purple, ///< Equivalent to HTML color \#AA00AA. - brown, ///< Equivalent to HTML color \#AA5500. - gray, ///< Equivalent to HTML color \#AAAAAA. - }; - - /** - * @brief The foreground color modification flag. - */ - enum struct foreground_flag : bool - { - none, ///< Apply no flag e.g., keep color as is. - intense, ///< Make the color more intense (usually brighter). - }; - - /** - * @brief The background color modification flag. - */ - enum struct background_flag : bool - { - none, ///< Apply no flag e.g., keep color as is. - blink_or_bright, ///< Make the cell blink or more intense, dependent on the VGA configuration. - }; - - /** - * @brief The VGA text mode attribute. - * - * @note In the text mode of VGA, every code point being presented is followed by an attribute description. This - * allows for the modification of how the relevant "cell" is presented. - * - * @see vga::text::foreground_flag - * @see vga::text::background_flag - */ - struct attribute - { - color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. - enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. - color background_color : 3; ///< The background color of the cell. - enum background_flag background_flag : 1; ///< The background color modification flag of the cell. - }; - - static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); - - /** - * @brief Commonly used VGA text mode attributes. - */ - namespace common_attributes - { - /** - * @brief Make the affected cell display with a gray foreground and black background. - */ - [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - /** - * @brief Make the affected cell display with a green foreground and black background. - */ - [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - /** - * @brief Make the affected cell display with a green foreground and black background. - */ - [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - /** - * @brief Make the affected cell display with a white (gray + intense) foreground and red background. - */ - [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, - .foreground_flag = foreground_flag::intense, - .background_color = color::red, - .background_flag = background_flag::none}; - } // namespace common_attributes - - struct device final : teachos::cio::output_device - { - device(); - - /** - * @brief Clear the VGA text mode buffer. - * - * @note This function also resets the text mode buffer pointer. - * - * @param attribute The attribute to "clear" the screen with. - */ - auto clear(attribute attribute = common_attributes::gray_on_black) -> void; - - /** - * @brief Enable or disable the VGA text mode cursor. - * - * @param enabled Whether or not to enable the cursors. - */ - auto cursor(bool enabled) -> void; - - auto write(cio::output_stream stream, std::string_view text) -> void override; - - private: - using glyph = std::pair; - - [[nodiscard]] auto column() const noexcept -> std::ptrdiff_t; - [[nodiscard]] auto line() const noexcept -> std::ptrdiff_t; - - //! Process the semantics of special code points, for example newlines and carriage returns. - //! - //! @param code_point The code point to process. - //! @param attribute The attribute to use when writing to the text buffer. - //! @return @p true iff. the code point was handled, @p false otherwise. - auto handle_special_code_point(char code_point, attribute attribute) -> bool; - - //! Perform the actual output to the buffer. - //! - //! @param code_points The code points to output.. - //! @param attribute The attribute to use when writing to the text buffer. - auto put(std::string_view code_points, attribute attribute) -> void; - - //! Perform the actual output to the buffer. - //! - //! @param code_point The code point to output. - //! @param attribute The attribute to use when writing to the text buffer. - auto put(char code_point, attribute attribute) -> void; - - /** - * @brief Move the cursor to a new line, scrolling the buffer if necessary. - */ - auto newline() -> void; - - //! Scroll the screen by a number of lines. - //! - //! @param nof_lines The number of lines to scroll up. - auto scroll(std::ptrdiff_t nof_lines = 1) -> void; - - /** - * @brief Write a string of code points to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - auto write(std::string_view code_points, attribute attribute) -> void; - - //! Write a single code point to the VGA text buffer. - //! - //! @param code_point A single (8-bit) code point - //! @param attribute The #attribute to apply to the code point. - auto write(char code_point, attribute attribute) -> void; - - /** - * @brief Write a string of code points followed by a newline to the VGA text buffer. - * - * @note This function also updates the text mode buffer pointer. - * - * @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. - * @param attribute The attribute to apply to the written sequence of code points. - * @see vga::text::attribute - */ - auto writeln(std::string_view code_points, attribute attribute) -> void; - - std::span static const buffer; - - std::ptrdiff_t m_position{}; - }; - -} // namespace teachos::vga::x86_64::text +#ifndef TEACHOS_X86_64_VGA_TEXT_HPP +#define TEACHOS_X86_64_VGA_TEXT_HPP + +#include "text/attribute.hpp" // IWYU pragma: export +#include "text/color.hpp" // IWYU pragma: export +#include "text/common_attributes.hpp" // IWYU pragma: export +#include "text/device.hpp" // IWYU pragma: export +#include "text/flags.hpp" // IWYU pragma: export #endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/attribute.hpp b/arch/x86_64/include/x86_64/vga/text/attribute.hpp new file mode 100644 index 0000000..2dce708 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/text/attribute.hpp @@ -0,0 +1,30 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP +#define TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP + +// IWYU pragma: private, include "x86_64/vga/text.hpp" + +#include "x86_64/vga/text/color.hpp" +#include "x86_64/vga/text/flags.hpp" + +namespace teachos::vga::x86_64::text +{ + //! The VGA text mode attribute. + //! + //! @note In the text mode of VGA, every code point being presented is followed by an attribute description. This + //! allows for the modification of how the relevant "cell" is presented. + //! + //! @see text::foreground_flag + //! @see text::background_flag + struct attribute + { + color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. + enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. + color background_color : 3; ///< The background color of the cell. + enum background_flag background_flag : 1; ///< The background color modification flag of the cell. + }; + + static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); + +} // namespace teachos::vga::x86_64::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/color.hpp b/arch/x86_64/include/x86_64/vga/text/color.hpp new file mode 100644 index 0000000..0bdc146 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/text/color.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_COLOR_HPP +#define TEACHOS_X86_64_VGA_TEXT_COLOR_HPP + +// IWYU pragma: private, include "x86_64/vga/text.hpp" + +#include + +namespace teachos::vga::x86_64::text +{ + //! VGA Text Mode standard colors. + //! + //! Every color may be used as a foreground and/or background color, in any combination. + enum struct color : std::uint8_t + { + //! Equivalent to HTML color \#000000. + black, + //! Equivalent to HTML color \#0000AA. + blue, + //! Equivalent to HTML color \#00AA00. + green, + //! Equivalent to HTML color \#00AAAA. + cyan, + //! Equivalent to HTML color \#AA0000. + red, + //! Equivalent to HTML color \#AA00AA. + purple, + //! Equivalent to HTML color \#AA5500. + brown, + //! Equivalent to HTML color \#AAAAAA. + gray, + }; + +} // namespace teachos::vga::x86_64::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/common_attributes.hpp b/arch/x86_64/include/x86_64/vga/text/common_attributes.hpp new file mode 100644 index 0000000..f6204f3 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/text/common_attributes.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP +#define TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP + +// IWYU pragma: private, include "x86_64/vga/text.hpp" + +#include "x86_64/vga/text/attribute.hpp" +#include "x86_64/vga/text/color.hpp" +#include "x86_64/vga/text/flags.hpp" + +namespace teachos::vga::x86_64::text +{ + //! Make the affected cell display with a gray foreground and black background. + [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a green foreground and black background. + [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a green foreground and black background. + [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a white (gray + intense) foreground and red background. + [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::intense, + .background_color = color::red, + .background_flag = background_flag::none}; +} // namespace teachos::vga::x86_64::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/device.hpp b/arch/x86_64/include/x86_64/vga/text/device.hpp new file mode 100644 index 0000000..6cb7a45 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/text/device.hpp @@ -0,0 +1,108 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP +#define TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP + +// IWYU pragma: private, include "x86_64/vga/text.hpp" + +#include "kapi/cio.hpp" + +#include "x86_64/vga/text/attribute.hpp" + +#include +#include +#include +#include + +namespace teachos::vga::x86_64::text +{ + //! A VGA Text Mode device. + //! + //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a + //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute + //! determines the visual style of that cell. + //! + //! @see text::attribute + struct device final : teachos::cio::output_device + { + device(); + + //! Clear the buffer. + //! + //! Clearing the screen ensures the text mode buffer is filled with zeroes, effectively erasing all displayed data + //! and resetting the output position to the start of the buffer. + auto clear() -> void; + + //! Enable or disable the VGA text mode cursor. + //! + //! @param enabled Whether to enable the cursor. + auto cursor(bool enabled) -> void; + + //! @copydoc teachos::cio::output_device + auto write(cio::output_stream stream, std::string_view text) -> void override; + + private: + using cell = std::pair; + + //! Get column number of the current cell. + [[nodiscard]] auto column() const noexcept -> std::ptrdiff_t; + + //! Get the line number of the current cell. + [[nodiscard]] auto line() const noexcept -> std::ptrdiff_t; + + //! Process the semantics of special code points, for example newlines and carriage returns. + //! + //! @param code_point The code point to process. + //! @param attribute The attribute to use when writing to the text buffer. + //! @return @p true iff. the code point was handled, @p false otherwise. + auto handle_special_code_point(char code_point, attribute attribute) -> bool; + + //! Perform the actual output to the buffer. + //! + //! @param code_points The code points to output.. + //! @param attribute The attribute to use when writing to the text buffer. + auto put(std::string_view code_points, attribute attribute) -> void; + + //! Perform the actual output to the buffer. + //! + //! @param code_point The code point to output. + //! @param attribute The attribute to use when writing to the text buffer. + auto put(char code_point, attribute attribute) -> void; + + //! Move the cursor to a new line and scroll the buffer if necessary. + auto newline() -> void; + + //! Scroll the screen. + //! + //! @param nof_lines The number of lines to scroll up. + auto scroll(std::size_t nof_lines = 1) -> void; + + //! Write a string of code points to the text buffer. + //! + //! @note This function also updates the text mode buffer pointer. + //! + //! @param code_points A string of (8-bit) code points to write to the VGA text mode buffer. + //! @param attribute The attribute to apply to the written sequence of code points. + //! @see vga::text::attribute + auto write(std::string_view code_points, attribute attribute) -> void; + + //! Write a single code point to the VGA text buffer. + //! + //! @param code_point A single (8-bit) code point + //! @param attribute The #attribute to apply to the code point. + auto write(char code_point, attribute attribute) -> void; + + //! The width, in cells, of the buffer. + std::size_t m_width{}; + + //! The height, in cells, of the buffer. + std::size_t m_height{}; + + //! The text mode data buffer. + std::span m_buffer; + + //! The position of the next cell to be written to. + std::ptrdiff_t m_position{}; + }; + +} // namespace teachos::vga::x86_64::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/x86_64/vga/text/flags.hpp b/arch/x86_64/include/x86_64/vga/text/flags.hpp new file mode 100644 index 0000000..784b910 --- /dev/null +++ b/arch/x86_64/include/x86_64/vga/text/flags.hpp @@ -0,0 +1,38 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP +#define TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP + +// IWYU pragma: private, include "x86_64/vga/text.hpp" + +namespace teachos::vga::x86_64::text +{ + + //! VGA Text Mode standard background modification flags. + enum struct background_flag : bool + { + //! Do not modify the foreground rendering. + none, + //! Render the background as blinking or intense. + //! + //! Whether this flag is interpreted as 'blink' or 'bright' depends on the currently active configuration of the VGA + //! device. + //! + //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such + //! colors brighter. + blink_or_bright, + }; + + //! VGA Text Mode standard foreground modification flags. + enum struct foreground_flag : bool + { + //! Do not modify the foreground rendering. + none, + //! Render the foreground as intense. + //! + //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such + //! colors brighter. + intense, + }; + +} // namespace teachos::vga::x86_64::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/vga/text.cpp b/arch/x86_64/src/vga/text.cpp index 1249cc3..1ee69f6 100644 --- a/arch/x86_64/src/vga/text.cpp +++ b/arch/x86_64/src/vga/text.cpp @@ -19,30 +19,38 @@ namespace teachos::vga::x86_64::text { namespace { - constexpr auto BUFFER_BASE_ADDRESS = std::uintptr_t{0xb8000}; - constexpr auto DEFAULT_TEXT_BUFFER_WIDTH = 80z; - constexpr auto DEFAULT_TEXT_BUFFER_HEIGHT = 25z; - constexpr auto CURSOR_ENABLED_BIT = 5U; - } // namespace + constexpr auto default_buffer_address = std::uintptr_t{0xb8000}; + constexpr auto default_buffer_width = 80z; + constexpr auto default_buffer_height = 25z; - std::span const device::buffer = - std::span{std::bit_cast(BUFFER_BASE_ADDRESS + - std::bit_cast(&teachos::boot::x86_64::TEACHOS_VMA)), - DEFAULT_TEXT_BUFFER_WIDTH * DEFAULT_TEXT_BUFFER_HEIGHT}; + constexpr auto bit_cursor_enabled = 5U; + + [[maybe_unused]] constexpr auto black_on_black = attribute{.foreground_color = color::black, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + } // namespace device::device() - : m_position{boot::bootstrap_information.vga_buffer_index} - {} + : m_width{default_buffer_width} + , m_height{default_buffer_height} + , m_buffer{std::bit_cast(default_buffer_address + + std::bit_cast(&teachos::boot::x86_64::TEACHOS_VMA)), + m_width * m_height} + , m_position{boot::bootstrap_information.vga_buffer_index} + { + clear(); + } - auto device::clear(attribute attribute) -> void + auto device::clear() -> void { m_position = 0; - std::ranges::fill(buffer, std::pair{' ', std::bit_cast(attribute)}); + std::ranges::fill(m_buffer, std::pair{' ', std::bit_cast(black_on_black)}); } auto device::cursor(bool enabled) -> void { - auto cursor_disable_byte = std::byte{!enabled} << CURSOR_ENABLED_BIT; + auto cursor_disable_byte = std::byte{!enabled} << bit_cursor_enabled; crtc::address::write(crtc::registers::cursor_start); crtc::data::write(crtc::data::read() | cursor_disable_byte); @@ -50,12 +58,12 @@ namespace teachos::vga::x86_64::text [[nodiscard]] auto device::column() const noexcept -> std::ptrdiff_t { - return m_position % DEFAULT_TEXT_BUFFER_WIDTH; + return m_position % m_width; } [[nodiscard]] auto device::line() const noexcept -> std::ptrdiff_t { - return m_position / DEFAULT_TEXT_BUFFER_WIDTH; + return m_position / m_width; } auto device::handle_special_code_point(char code_point, attribute attribute) -> bool @@ -83,26 +91,26 @@ namespace teachos::vga::x86_64::text auto device::put(char code_point, attribute attribute) -> void { - buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; + m_buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; } auto device::newline() -> void { - auto free_glyphs_in_line = DEFAULT_TEXT_BUFFER_WIDTH - column(); + auto free_glyphs_in_line = m_width - column(); m_position += free_glyphs_in_line; } - auto device::scroll(std::ptrdiff_t nof_lines) -> void + auto device::scroll(std::size_t nof_lines) -> void { - auto scroll_count = std::min(nof_lines, DEFAULT_TEXT_BUFFER_HEIGHT); + auto scroll_count = std::min(nof_lines, m_height); - auto scroll_start = buffer.begin() + (scroll_count * DEFAULT_TEXT_BUFFER_WIDTH); - std::ranges::move(scroll_start, buffer.end(), buffer.begin()); + auto scroll_start = m_buffer.begin() + (scroll_count * m_width); + std::ranges::move(scroll_start, m_buffer.end(), m_buffer.begin()); - auto clear_start = buffer.begin() + (DEFAULT_TEXT_BUFFER_HEIGHT - scroll_count) * DEFAULT_TEXT_BUFFER_WIDTH; - std::ranges::fill(clear_start, buffer.end(), glyph{}); + auto clear_start = m_buffer.begin() + (m_height - scroll_count) * m_width; + std::ranges::fill(clear_start, m_buffer.end(), cell{}); - m_position = (line() - scroll_count) * DEFAULT_TEXT_BUFFER_WIDTH; + m_position = (line() - scroll_count) * m_width; } auto device::write(cio::output_stream stream, std::string_view text) -> void @@ -111,9 +119,9 @@ namespace teachos::vga::x86_64::text switch (stream) { case cio::output_stream::stderr: - return common_attributes::red_on_black; + return red_on_black; default: - return common_attributes::green_on_black; + return green_on_black; } }(); write(text, attributes); @@ -126,7 +134,7 @@ namespace teachos::vga::x86_64::text auto device::write(char code_point, attribute attribute) -> void { - if (m_position + 1 > DEFAULT_TEXT_BUFFER_HEIGHT * DEFAULT_TEXT_BUFFER_WIDTH) + if (m_position + 1 > static_cast(m_height * m_width)) { scroll(); } -- cgit v1.2.3