banan-os/kernel/kernel/Terminal/TextModeTerminal.cpp

147 lines
3.8 KiB
C++

#include <kernel/BootInfo.h>
#include <kernel/IO.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/MMIO.h>
#include <kernel/Terminal/TextModeTerminal.h>
namespace Kernel
{
static constexpr TerminalDriver::Color s_palette[] {
TerminalColor::BLACK,
TerminalColor::BLUE,
TerminalColor::GREEN,
TerminalColor::CYAN,
TerminalColor::RED,
TerminalColor::MAGENTA,
TerminalColor::YELLOW,
TerminalColor::WHITE,
TerminalColor::BRIGHT_BLACK,
TerminalColor::BRIGHT_BLUE,
TerminalColor::BRIGHT_GREEN,
TerminalColor::BRIGHT_CYAN,
TerminalColor::BRIGHT_RED,
TerminalColor::BRIGHT_MAGENTA,
TerminalColor::BRIGHT_YELLOW,
TerminalColor::BRIGHT_WHITE,
};
static constexpr uint8_t color_to_text_mode_color(TerminalDriver::Color color)
{
uint32_t min_diff = BAN::numeric_limits<uint32_t>::max();
uint8_t closest = 0;
static_assert(sizeof(s_palette) / sizeof(*s_palette) == 16);
for (size_t i = 0; i < 16; i++)
{
const auto rdiff = color.red() - s_palette[i].red();
const auto gdiff = color.green() - s_palette[i].green();
const auto bdiff = color.blue() - s_palette[i].blue();
const uint32_t diff = rdiff*rdiff + gdiff*gdiff + bdiff*bdiff;
if (diff >= min_diff)
continue;
min_diff = diff;
closest = i;
}
return closest;
}
BAN::ErrorOr<BAN::RefPtr<TextModeTerminalDriver>> TextModeTerminalDriver::create_from_boot_info()
{
ASSERT(g_boot_info.framebuffer.type == FramebufferInfo::Type::Text);
if (g_boot_info.framebuffer.bpp != 16)
return BAN::Error::from_errno(ENOTSUP);
auto* driver_ptr = new TextModeTerminalDriver(
g_boot_info.framebuffer.address,
g_boot_info.framebuffer.width,
g_boot_info.framebuffer.height,
g_boot_info.framebuffer.pitch
);
if (driver_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto driver = BAN::RefPtr<TextModeTerminalDriver>::adopt(driver_ptr);
TRY(driver->initialize());
return driver;
}
BAN::ErrorOr<void> TextModeTerminalDriver::initialize()
{
const size_t page_count = range_page_count(m_paddr, m_height * m_pitch);
const vaddr_t vaddr = PageTable::kernel().reserve_free_contiguous_pages(page_count, KERNEL_OFFSET);
if (vaddr == 0)
return BAN::Error::from_errno(ENOMEM);
PageTable::kernel().map_range_at(
m_paddr & PAGE_ADDR_MASK,
vaddr,
page_count * PAGE_SIZE,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
PageTable::MemoryType::WriteCombining
);
m_vaddr = vaddr + (m_paddr % PAGE_SIZE);
set_cursor_position(0, 0);
clear(TerminalColor::BLACK);
return {};
}
TextModeTerminalDriver::~TextModeTerminalDriver()
{
if (m_vaddr == 0)
return;
const size_t page_count = range_page_count(m_paddr, m_height * m_pitch);
PageTable::kernel().unmap_range(m_vaddr & PAGE_ADDR_MASK, page_count * PAGE_SIZE);
}
void TextModeTerminalDriver::putchar_at(uint16_t ch, uint32_t x, uint32_t y, Color fg, Color bg)
{
if (x >= m_width || y >= m_height)
return;
if (ch >= 0x100)
ch = '?';
const uint8_t color =
(color_to_text_mode_color(bg) << 4) |
(color_to_text_mode_color(fg) << 0);
MMIO::write16(m_vaddr + y * m_pitch + 2 * x, ch | (color << 8));
}
void TextModeTerminalDriver::clear(Color color)
{
for (uint32_t y = 0; y < m_height; y++)
for (uint32_t x = 0; x < m_width; x++)
putchar_at(' ', x, y, TerminalColor::BRIGHT_WHITE, color);
}
void TextModeTerminalDriver::set_cursor_shown(bool shown)
{
if (shown)
{
IO::outb(0x3D4, 0x0A);
IO::outb(0x3D5, (IO::inb(0x3D5) & 0xC0) | 14);
IO::outb(0x3D4, 0x0B);
IO::outb(0x3D5, (IO::inb(0x3D5) & 0xE0) | 15);
}
else
{
IO::outb(0x3D4, 0x0A);
IO::outb(0x3D5, 0x20);
}
}
void TextModeTerminalDriver::set_cursor_position(uint32_t x, uint32_t y)
{
const uint16_t pos = y * m_width + x;
IO::outb(0x3D4, 0x0F);
IO::outb(0x3D5, pos & 0xFF);
IO::outb(0x3D4, 0x0E);
IO::outb(0x3D5, pos >> 8);
}
}