I'm working on a 80x25 terminal that splits the screen into two. The top half gets the first buffer, the bottom half gets the second buffer. I'm concerned about readability and accessibility. For accessibility, what I'm trying to do is hide as much of the implementation as possible so that other parts of the code don't have to rely on this class as much. For example, I'm able to plug in a third party printf implementation and only have to change it at one place, where it outputs the buffer. Therefore 90% of the class should be private. The user only uses printf for outputting, unless they need to change the buffer or color.For readability, after adding the logic to handle two buffers, I noticed an explosion in buffers[current_buffer]. I want to be able to reduce this. As well as that, I've found myself making typos such as typing 'rows' instead of 'columns' and so on. By tackling readability, annoying bugs like this hopefully can be eliminated.Should I make terminal a global object?Terminal.hppextern Terminal terminal;Terminal.cpp// Define terminal in one translation unit.Terminal terminal;Example usage:main.cppprintf(Buffer 1.\n);// Zero-indexed.terminal.setbuffer(1);printf(Buffer 2.\n);printf.cpp// Printf doesn't need to care about terminal's internals. Just copy// to buffer and output result to screen.void printf(const char* fmt, ...){    va_list args;    int i;    va_start(args, fmt);    i = vsprintf(terminal.getcurrentbuffer().buffer, fmt, args);    va_end(args);    terminal.getcurrentbuffer().buffer[i] = '\0';    terminal.puts(terminal.getcurrentbuffer().buffer);}How should I handle access to the current buffer and setting it?terminal.getcurrentbuffer().buffer is an eyesore and a handful to type. But I don't want to actually return a char* instead of a Buffer&. Secondly, terminal.puts(terminal.getcurrentbuffer().buffer); seems rather redundant, but currently I just write directly to video memory. Terminal.hpp    // Zero-indexed. So 0 == buffer1, 1 == buffer2, etc.    void setbuffer(int new_buffer);    Buffer& getcurrentbuffer();private:    Buffer buffers[2];    int current_buffer;Terminal.cpp// Assumes that the user provided a non-zero index.void Terminal::setbuffer(int new_buffer){    current_buffer = new_buffer;}Terminal::Buffer& Terminal::getcurrentbuffer(){    return buffers[current_buffer];}What is a way to improve readability for loops like this?void Terminal::scroll(){    uint16_t blank = make_vgaentry(' ', buffers[current_buffer].color    );    if (buffers[current_buffer].ypos >= buffers[current_buffer].rows) {        for (int i =   buffers[current_buffer].ybase                     * buffers[current_buffer].columns;                  i <  (buffers[current_buffer].ybase + buffers[current_buffer].rows - 1)                      * buffers[current_buffer].columns; i++)        {            video_buffer[i] = video_buffer[i + buffers[current_buffer].columns];        }        for (int i =  (buffers[current_buffer].ybase + buffers[current_buffer].rows - 1)                      * buffers[current_buffer].columns;                  i <  (buffers[current_buffer].ybase                      + buffers[current_buffer].rows) * buffers[current_buffer].columns; i++)        {            video_buffer[i] = blank;        }        buffers[current_buffer].ypos = buffers[current_buffer].rows - 1;    }}I considered using a local variable copy but that might be expensive(?) I also considered making function stubs that return common calculations, like x + y * columns and such.Full code dump.Terminal.hpp#ifndef TERMINAL_HPP#define TERMINAL_HPP#include <common.hpp> // stddef.h, stdint.hclass Terminal{    struct Buffer    {        char buffer[1024];        int xpos;        int ypos;        int ybase; // What line the buffer starts at        constexpr static int columns = 80;        // This is how many lines each buffer gets.        constexpr static int rows = 12;        // Combined foreground and background color.        uint8_t color;    };public:    enum class Color    {        Black        = 0,        Blue         = 1,        Green        = 2,        Cyan         = 3,        Red          = 4,        Magenta      = 5,        Brown        = 6,        LightGrey    = 7,        DarkGrey     = 8,        LightBlue    = 9,        LightGreen   = 10,        LightCyan    = 11,        LightRed     = 12,        LightMagenta = 13,        LightBrown   = 14,        White        = 15,    };    Terminal();    ~Terminal();    // Clears entire screen and puts separate in middle. Doesn't clear    // invidiual buffer.    void clrscr();    void clrbuf(int buffer);    void putch(uint8_t c);    void puts(const char* s);    // Zero-indexed. So 0 == buffer1, 1 == buffer2, etc.    void setbuffer(int new_buffer);    Buffer& getcurrentbuffer();private:    volatile uint16_t* video_buffer;    Buffer buffers[2];    int current_buffer;    uint8_t make_color(Color fg, Color bg);    uint16_t make_vgaentry(uint8_t c, uint8_t color);    void scroll();    void movecsr();};extern Terminal terminal;#endifTerminal.cpp#include <terminal.hpp>#include <system.hpp> // outb, inb#include <common.hpp>#include <cstring.hpp> // C library string functions// Define terminal in one translation unit.Terminal terminal;// Constructors.Terminal::Terminal()    : video_buffer(reinterpret_cast<volatile uint16_t*>(0xb8000)),      current_buffer(0){    buffers[0].color = make_color(Color::White, Color::Black);    buffers[1].color = make_color(Color::LightGreen, Color::Green);    // Put buffer2's starting point after the separator.    buffers[1].ybase = 13;    clrscr();    clrbuf(0);    clrbuf(1);    terminal.setbuffer(current_buffer);}Terminal::~Terminal(){}// Public functions.void Terminal::clrscr(){    for (int i = 0; i < buffers[current_buffer].rows * buffers[current_buffer].columns; ++i)    {        video_buffer[i] = make_vgaentry(' ', buffers[current_buffer].color);    }    // http://www.asciitable.com/    // Set middle of screen with separator.    for (int i = 0; i < buffers[current_buffer].columns; ++i)    {        video_buffer[i + 12 * buffers[current_buffer].columns]                = make_vgaentry(205, buffers[current_buffer].color);    }}void Terminal::clrbuf(int buffer){    for (int i = buffers[buffer].ybase * buffers[buffer].columns;              i <  (buffers[buffer].ybase + buffers[buffer].rows)                  * buffers[buffer].columns; ++i)    {        video_buffer[i] = make_vgaentry(' ', buffers[buffer].color);    }}void Terminal::putch(uint8_t c){    if (c == '\n')    {        buffers[current_buffer].xpos = 0;        buffers[current_buffer].ypos++;    } else {        video_buffer[   buffers[current_buffer].xpos                      + (buffers[current_buffer].ybase + buffers[current_buffer].ypos)                     *  buffers[current_buffer].columns]                        = make_vgaentry(c, buffers[current_buffer].color);        buffers[current_buffer].xpos++;    }    if (buffers[current_buffer].xpos >= buffers[current_buffer].columns)    {        buffers[current_buffer].xpos = 0;        buffers[current_buffer].ypos++;    }    scroll();    movecsr();}void Terminal::puts(const char* s){    size_t len = strlen((char*) s);    for (size_t i = 0; i < len; ++i)    {        putch(s[i]);    }}void Terminal::setbuffer(int new_buffer){    current_buffer = new_buffer;}Terminal::Buffer& Terminal::getcurrentbuffer(){    return buffers[current_buffer];}// Private functions./*+------------------+-----------------+|    Attribute     |    Character    |+------------------+-----------------+| 7 6 5 4 3 2 1 0  | 7 6 5 4 3 2 1 0 || B BG    FG       | CP              |+------------------+-----------------+B  - BlinkBG - Background ColorFG - Foreground ColorCP - CodepointAlthough bits 4-7 may make up the background color.*/uint8_t Terminal::make_color(Color fg, Color bg){    return static_cast<uint8_t>(fg) |            static_cast<uint8_t>(bg) << 4;}uint16_t Terminal::make_vgaentry(uint8_t c, uint8_t color){    uint16_t c16 = c;    uint16_t color16 = color;    return c16 | color16 << 8;}// https://github.com/stupaq/sos-kernel/blob/master/kernel/lib/monitor.c#L40// We add ybase to each place where ypos is required, so that the correct// position is calculated for the respective buffer.void Terminal::scroll(){    uint16_t blank = make_vgaentry(' ', buffers[current_buffer].color    );    if (buffers[current_buffer].ypos >= buffers[current_buffer].rows) {        for (int i =   buffers[current_buffer].ybase                     * buffers[current_buffer].columns;                  i <  (buffers[current_buffer].ybase + buffers[current_buffer].rows - 1)                      * buffers[current_buffer].columns; i++)        {            video_buffer[i] = video_buffer[i + buffers[current_buffer].columns];        }        for (int i =  (buffers[current_buffer].ybase + buffers[current_buffer].rows - 1)                      * buffers[current_buffer].columns;                  i <  (buffers[current_buffer].ybase                      + buffers[current_buffer].rows) * buffers[current_buffer].columns; i++)        {            video_buffer[i] = blank;        }        buffers[current_buffer].ypos = buffers[current_buffer].rows - 1;    }}void Terminal::movecsr() {    // The screen is 80 characters wide...    uint16_t cursorLocation =  (buffers[current_buffer].ybase + buffers[current_buffer].ypos)                               * buffers[current_buffer].columns + buffers[current_buffer].xpos;    outb(0x3D4, 14); // Tell the VGA board we are setting the high cursor byte.    outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte.    outb(0x3D4, 15); // Tell the VGA board we are setting the low cursor byte.    outb(0x3D5, cursorLocation); // Send the low cursor byte.}
Console class that handles multiple buffers
c++;classes;console