aboutsummaryrefslogtreecommitdiff
path: root/arch/x86_64/src/vga/text.cpp
blob: 1ee69f65964922b52441c86e09b4d0a9b3b0acd4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#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 <algorithm>
#include <bit>
#include <cstddef>
#include <cstdint>
#include <span>
#include <string_view>
#include <utility>

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<device::cell *>(default_buffer_address +
                                               std::bit_cast<std::uintptr_t>(&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<std::byte>(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<std::byte>(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<std::ptrdiff_t>(m_height * m_width))
    {
      scroll();
    }

    if (!handle_special_code_point(code_point, attribute))
    {
      put(code_point, attribute);
    }
  };

}  // namespace teachos::vga::x86_64::text