From 9a299f2fe91554a1b9d9db402391ae757c591ef8 Mon Sep 17 00:00:00 2001 From: Aqua-sama Date: Thu, 11 Feb 2021 16:17:53 +0200 Subject: Add comments to explain CGA ports better --- drivers/cga.cc | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/cga.h | 69 ++++++++++++++++++++++++++++++++++ drivers/makefile | 1 + drivers/ports.h | 47 +++++++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 drivers/cga.cc create mode 100644 drivers/cga.h create mode 100644 drivers/makefile create mode 100644 drivers/ports.h (limited to 'drivers') diff --git a/drivers/cga.cc b/drivers/cga.cc new file mode 100644 index 0000000..79facee --- /dev/null +++ b/drivers/cga.cc @@ -0,0 +1,111 @@ +#include "cga.h" +#include + +static_assert(sizeof(CGA::Entry) == 2); + +/* cga registers, see https://www.lowlevel.eu/wiki/Color_Graphics_Adapter */ +constexpr uint8_t cursor_start = 10; // 0xa +constexpr uint8_t cursor_end = 11; // 0xb +constexpr uint8_t cursor_addr_h = 14; // 0xe +constexpr uint8_t cursor_addr_l = 15; // 0xf + +/* cursor format + * register | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * start | | e | t | scanline | + * end | | scanline | + * e = enable; t = timing + * e t | effect + * 0 0 | no blinking + * 0 1 | hide cursor + * 1 0 | blink normally + * 1 1 | blink fast + */ +constexpr uint8_t cursor_hide = 0b00100000; + +CGA::CGA(Colour fg, Colour bg, uint32_t address) : colour_fg(fg), colour_bg(bg) { + buffer = reinterpret_cast(address); + + // clear buffer + for (size_t y = 0; y < max_rows; y++) { + for (size_t x = 0; x < max_columns; x++) { + const size_t index = y * max_columns + x; + buffer[index].c = ' '; + buffer[index].fg = colour_fg; + buffer[index].bg = colour_bg; + } + } + + enable_cursor(14, 15); + update_cursor(); +} + +void CGA::set_colour(Colour fg, Colour bg) { + colour_fg = fg; + colour_bg = bg; + for (size_t y = 0; y < max_rows; y++) { + for (size_t x = 0; x < max_columns; x++) { + const size_t index = y * max_columns + x; + buffer[index].fg = colour_fg; + buffer[index].bg = colour_bg; + } + } +} + +void CGA::enable_cursor(uint8_t start, uint8_t end) { + p_idx.write(cursor_start); + p_dat.write((p_dat.read() & 0xc0) | start); + + p_idx.write(cursor_end); + p_dat.write((p_dat.read() & 0xe0) | end); +} +void CGA::disable_cursor() { + p_idx.write(cursor_start); + p_dat.write(cursor_hide); +} +void CGA::update_cursor() { + const uint16_t pos = row * max_columns + column; + + p_idx.write(cursor_addr_l); + p_dat.write(static_cast(pos & 0xff)); + + p_idx.write(cursor_addr_h); + p_dat.write(static_cast((pos >> 8) & 0xff)); +} + +void CGA::write(char c) { + switch (c) { + case '\n': + column = 0; + ++row; + break; + default: { + const size_t index = row * max_columns + column; + buffer[index].c = c; + ++column; + } + } + + if (column == max_columns) { + column = 0; + ++row; + } + + if (row == max_rows) { + // scroll up - move rows 1~25 up by one + for (size_t y = 1; y < max_rows; y++) { + const auto prev_y = y - 1; + for (size_t x = 0; x < max_columns; ++x) { + const auto prev = prev_y * max_columns + x; + const auto idx = y * max_columns + x; + buffer[prev] = buffer[idx]; + } + } + --row; + } +} + +void CGA::write(ViewIterator& iter) { + while (iter) { + write(iter.next()); + } +} diff --git a/drivers/cga.h b/drivers/cga.h new file mode 100644 index 0000000..f0ca412 --- /dev/null +++ b/drivers/cga.h @@ -0,0 +1,69 @@ +#pragma once +#include +#include "ports.h" + +/* + * CGA (Colour Graphics Adapter) + * + * useful links: + * https://www.seasip.info/VintagePC/cga.html + * https://www.lowlevel.eu/wiki/Color_Graphics_Adapter + * + * TODO: switching between modes + * TODO: cursor styling + */ + +class CGA : public Console { +public: + /* Hardware text mode colour constants. */ + enum Colour : uint8_t { + CGA_COLOR_BLACK = 0, + CGA_COLOR_BLUE = 1, + CGA_COLOR_GREEN = 2, + CGA_COLOR_CYAN = 3, + CGA_COLOR_RED = 4, + CGA_COLOR_MAGENTA = 5, + CGA_COLOR_BROWN = 6, + CGA_COLOR_LIGHT_GREY = 7, + CGA_COLOR_DARK_GREY = 8, + CGA_COLOR_LIGHT_BLUE = 9, + CGA_COLOR_LIGHT_GREEN = 10, + CGA_COLOR_LIGHT_CYAN = 11, + CGA_COLOR_LIGHT_RED = 12, + CGA_COLOR_LIGHT_MAGENTA = 13, + CGA_COLOR_LIGHT_BROWN = 14, + CGA_COLOR_WHITE = 15, + }; + + CGA(Colour fg = CGA_COLOR_BLACK, Colour bg = CGA_COLOR_LIGHT_GREY, uint32_t address = 0xB8000); + ~CGA() = default; + + void set_colour(Colour fg, Colour bg); + + void enable_cursor(uint8_t start, uint8_t end); + void disable_cursor(); + void update_cursor() override; + + void write(char c) override; + void write(ViewIterator& iter) override; + + + struct Entry { + char c; + Colour fg : 4; + Colour bg : 4; + } __attribute((packed)); + +private: + const size_t max_columns = 80, max_rows = 25; + size_t column = 0, row = 0; + + Colour colour_fg; + Colour colour_bg; + + Entry* buffer; + + // ports + cga_idx_port p_idx; + cga_dat_port p_dat; +}; diff --git a/drivers/makefile b/drivers/makefile new file mode 100644 index 0000000..b76f7c5 --- /dev/null +++ b/drivers/makefile @@ -0,0 +1 @@ +CXX_OBJ += drivers/cga.o diff --git a/drivers/ports.h b/drivers/ports.h new file mode 100644 index 0000000..338c3b8 --- /dev/null +++ b/drivers/ports.h @@ -0,0 +1,47 @@ +#pragma once + +template +concept port_data_t = (is_same::value || is_same::value || is_same::value); + +template +class Port { +public: + [[nodiscard]] T read() { + T result; + + switch (sizeof(T)) { + case 1: + asm volatile("inb %1, %0" : "=a"(result) : "Nd"(port_num)); + break; + case 2: + asm volatile("inw %1, %0" : "=a"(result) : "Nd"(port_num)); + break; + case 4: + asm volatile("inl %1, %0" : "=a"(result) : "Nd"(port_num)); + break; + } + + return result; + } + + void write(T data) { + switch (sizeof(T)) { + case 1: + asm volatile("outb %0, %1" : : "a"(data), "Nd"(port_num)); + break; + case 2: + asm volatile("outw %0, %1" : : "a"(data), "Nd"(port_num)); + break; + case 4: + asm volatile("outl %0, %1" : : "a"(data), "Nd"(port_num)); + break; + } + } +}; + +/* Ports 0x3d0 to 0x3df are reserved for CGA; each port is 1 byte wide */ +typedef Port<0x3d4, uint8_t> cga_idx_port; +typedef Port<0x3d5, uint8_t> cga_dat_port; +// 0x3d8 : mode select register +// 0x3d8 : color control register +// 0x3da : status register -- cgit v1.2.1