#include "cga.h" #include "../utils/math.h" #include "ioport.h" namespace CGA { // I/O Ports required for hardware cursor constexpr IOPort index_port(0x3d4); ///< Graphic card register selection constexpr IOPort data_port(0x3d5); ///< Read/Write selected register /*! \brief Helper structure for the hardware cursor */ union Cursor { uint16_t value; struct { uint16_t low : 8; uint16_t high : 6; uint16_t : 2; } __attribute__((packed)); } __attribute__((packed)); /*! \brief Register (for index_port) */ enum RegisterIndex { CURSOR_START = 10, ///< Control the cursor (blink timing and start scanline) CURSOR_END = 11, ///< Control the cursors end scanline START_ADDRESS_HIGH = 12, ///< Base offset address for output (high) START_ADDRESS_LOW = 13, ///< Base offset address for output (low) CURSOR_HIGH = 14, ///< Cursor offset address (high) CURSOR_LOW = 15, ///< Cursor offset address (low) }; void setCursor(unsigned abs_x, unsigned abs_y) { // Using the hardware cursor is quite important: // The access to the IO Ports is rather slow and -- if interrupted by // another output -- it will result in a strange word jumble. // Hence this helps in a quite visible manner to understand the // necessity of correct synchronization (and its performance impact). if (abs_x < COLUMNS && abs_y < ROWS) { Cursor pos = {static_cast((abs_y * COLUMNS) + abs_x)}; // Select index 14 / CURSOR_HIGH register index_port.outb(CURSOR_HIGH); // Write high part of value to selected register (cursor high) data_port.outb(pos.high); // Select index 15 / CURSOR_LOW register index_port.outb(CURSOR_LOW); // Write low part of value to selected register (cursor low) data_port.outb(pos.low); } } void getCursor(unsigned& abs_x, unsigned& abs_y) { Cursor pos = {.value = 0}; // Select index 14 / CURSOR_HIGH register index_port.outb(CURSOR_HIGH); // Read high part part of value from selected register (cursor high) pos.high = data_port.inb(); // Select index 15 / CURSOR_LOW register index_port.outb(CURSOR_LOW); // Read low part of value from selected register (cursor low) pos.low = data_port.inb(); // Calculate absolute position abs_x = pos.value % COLUMNS; abs_y = Math::min(pos.value / COLUMNS, ROWS - 1); } void show(unsigned abs_x, unsigned abs_y, char character, Attribute attrib) { // Output only on current page if (abs_x < COLUMNS && abs_y < ROWS) { Cell cell(character, attrib); TEXT_BUFFER_BASE[(abs_y * COLUMNS) + abs_x] = cell; } } }; // namespace CGA