xaizek / tos (License: GPLv3 only) (since 2018-12-07)
This is an alternative version of sources presented as part of Write Your Own OS video tutorial by Viktor Engelmann.
<root> / src / drv / VGA.cpp (8f0fcd03d26d5d533d560362faa2fa746b24e4b1) (4,532B) (mode 100644) [raw]
#include "VGA.hpp"

#include <cstdint>

using namespace drv;

VGA::VGA()
    : miscPort(0x3c2),
      crtcIndexPort(0x3d4),
      crtcDataPort(0x3d5),
      sequencerIndexPort(0x3c4),
      sequencerDataPort(0x3c5),
      graphicsControllerIndexPort(0x3ce),
      graphicsControllerDataPort(0x3cf),
      attrControllerIndexPort(0x3c0),
      attrControllerReadPort(0x3c1),
      attrControllerWritePort(0x3c0),
      attrControllerResetPort(0x3da)
{
}

void
VGA::writeRegisters(std::uint8_t *registers)
{
    // Misc.
    miscPort.write(*(registers++));

    // Sequencer.
    for (std::uint8_t i = 0; i < 5; ++i) {
        sequencerIndexPort.write(i);
        sequencerDataPort.write(*(registers++));
    }

    // Cathode ray tube controller.
    crtcIndexPort.write(0x03);
    crtcDataPort.write(crtcDataPort.read() | 0x80);
    crtcIndexPort.write(0x11);
    crtcDataPort.write(crtcDataPort.read() & ~0x80);

    registers[0x03] = registers[0x03] | 0x80;
    registers[0x11] = registers[0x11] & ~0x80;

    for (std::uint8_t i = 0; i < 25; ++i) {
        crtcIndexPort.write(i);
        crtcDataPort.write(*(registers++));
    }

    // Graphics controller.
    for (std::uint8_t i = 0; i < 9; ++i) {
        graphicsControllerIndexPort.write(i);
        graphicsControllerDataPort.write(*(registers++));
    }

    // Attribute controller.
    for (std::uint8_t i = 0; i < 21; ++i) {
        attrControllerResetPort.read();
        attrControllerIndexPort.write(i);
        attrControllerWritePort.write(*(registers++));
    }

    attrControllerResetPort.read();
    attrControllerIndexPort.write(0x20);
}

bool
VGA::supportsMode(const Mode &mode)
{
    return mode.width == 320 && mode.height == 200 && mode.colorDepth == 8;
}

bool
VGA::setMode(const Mode &mode)
{
    if (!supportsMode(mode)) {
        return false;
    }

    std::uint8_t mode_320x200x256[] = {
        // MISC
        0x63,
        // SEQ
        0x03, 0x01, 0x0f, 0x00, 0x0e,
        // CRTC
        0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f,
        0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x9c, 0x0e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3,
        0xff,
        // GC
        0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f,
        0xff,
        // AC
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0x41, 0x00, 0x0f, 0x00, 0x00
    };

    writeRegisters(mode_320x200x256);

    currentMode = mode;
    return true;
}

std::uint8_t *
VGA::getFrameBufferSegment()
{
    graphicsControllerIndexPort.write(0x06);
    std::uint8_t segmentNumber = graphicsControllerDataPort.read() & (3 << 2);
    switch (segmentNumber) {
        default:
        case 0 << 2: return reinterpret_cast<std::uint8_t *>(0x00000);
        case 1 << 2: return reinterpret_cast<std::uint8_t *>(0xa0000);
        case 2 << 2: return reinterpret_cast<std::uint8_t *>(0xb0000);
        case 3 << 2: return reinterpret_cast<std::uint8_t *>(0xb8000);
    }
}

void
VGA::beginDrawing()
{
    for (int i = 0; i < currentMode.width*currentMode.height; ++i) {
        buffer[i] = 0;
    }
}

void
VGA::endDrawing()
{
    std::uint8_t *pixelAddress = getFrameBufferSegment();
    for (int i = 0; i < currentMode.width*currentMode.height; ++i) {
        *pixelAddress++ = buffer[i];
    }
}

void
VGA::putPixel(Position pos, Color color)
{
    putPixel(pos, getColorIndex(color));
}

void
VGA::fillRectangle(Position pos, Dimentions size, Color color)
{
    for (int y = pos.y; y < pos.y + size.h; ++y) {
        for (int x = pos.x; x < pos.x + size.w; ++x) {
            putPixel(drv::Position { x, y }, color);
        }
    }
}

std::uint8_t
VGA::getColorIndex(Color color)
{
    if (color.r == 0x00 && color.g == 0x00 && color.b == 0x00) {
        return 0x00; // black
    }
    if (color.r == 0x00 && color.g == 0x00 && color.b == 0xa8) {
        return 0x01; // blue
    }
    if (color.r == 0x00 && color.g == 0xa8 && color.b == 0x00) {
        return 0x02; // green
    }
    if (color.r == 0xa8 && color.g == 0x00 && color.b == 0x00) {
        return 0x04; // red
    }
    if (color.r == 0xff && color.g == 0xff && color.b == 0xff) {
        return 0x3f; // white
    }
    return 0x00;
}

void
VGA::putPixel(Position pos, std::uint8_t colorIndex)
{
    if (pos.x < 0 || pos.x >= currentMode.width ||
        pos.y < 0 || pos.y >= currentMode.height) {
        return;
    }

    std::uint8_t *pixelAddress = buffer + currentMode.width*pos.y + pos.x;
    *pixelAddress = colorIndex;
}
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/tos

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/tos

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master