#include "x86_64/vga/text.hpp" #include "kapi/boot.hpp" #include "kapi/cio.hpp" #include "x86_64/boot/boot.hpp" #include "x86_64/boot/ld.hpp" #include "x86_64/vga/crtc.hpp" #include #include #include #include #include #include #include namespace teachos::vga::x86_64::text { namespace { constexpr auto default_buffer_address = std::uintptr_t{0xb8000}; constexpr auto default_buffer_width = 80z; constexpr auto default_buffer_height = 25z; 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_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() -> void { m_position = 0; 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} << bit_cursor_enabled; crtc::address::write(crtc::registers::cursor_start); crtc::data::write(crtc::data::read() | cursor_disable_byte); } [[nodiscard]] auto device::column() const noexcept -> std::ptrdiff_t { return m_position % m_width; } [[nodiscard]] auto device::line() const noexcept -> std::ptrdiff_t { return m_position / m_width; } auto device::handle_special_code_point(char code_point, attribute attribute) -> bool { switch (code_point) { case '\n': newline(); return true; case '\r': m_position -= column(); return true; case '\t': put(" ", attribute); return true; default: return false; } } auto device::put(std::string_view code_points, attribute attribute) -> void { std::ranges::for_each(code_points, [&](auto code_point) -> void { put(code_point, attribute); }); } auto device::put(char code_point, attribute attribute) -> void { m_buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; } auto device::newline() -> void { auto free_glyphs_in_line = m_width - column(); m_position += free_glyphs_in_line; } auto device::scroll(std::size_t nof_lines) -> void { auto scroll_count = std::min(nof_lines, m_height); auto scroll_start = m_buffer.begin() + (scroll_count * m_width); std::ranges::move(scroll_start, m_buffer.end(), m_buffer.begin()); 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) * m_width; } auto device::write(cio::output_stream stream, std::string_view text) -> void { auto attributes = [&] -> attribute { switch (stream) { case cio::output_stream::stderr: return red_on_black; default: return green_on_black; } }(); write(text, attributes); } auto device::write(std::string_view code_points, attribute attribute) -> void { std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); } auto device::write(char code_point, attribute attribute) -> void { if (m_position + 1 > static_cast(m_height * m_width)) { scroll(); } if (!handle_special_code_point(code_point, attribute)) { put(code_point, attribute); } }; } // namespace teachos::vga::x86_64::text