консоль — C ++ пишет в память текстового режима VGA не видны на экране

Я пытаюсь написать ядро ​​(ядро ОС) на C ++, и я столкнулся с действительно странной проблемой (я уверен, что будет совершенно очевидно, что я делаю что-то не так для кого-то еще, я просто для жизни из меня не могу найти что не так)

Я использую класс C ++ для представления консоли VGA (BIOS должен загрузить ее по адресу 0xB8000), к которой можно получить ранний доступ в последовательности загрузки ОС для отладки вывода.

(подробнее читайте: http://wiki.osdev.org/Bare_Bones)

Вот основная функция моего ядра:

#include "Kernel.hpp"
extern "C"{
void kernelMain ()
{
VGATerminal kernelTerm = VGATerminal ();

//kernelTerm.print("[");
//kernelTerm.setTextColour(VGATerminal::LIGHT_GREEN);
//kernelTerm.print("OK");
//kernelTerm.setTextColour(VGATerminal::WHITE);
//kernelTerm.print("]\t\tKernel gained control of the CPU.\n");
//kernelTerm.print("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123465789\n");
}
}

(Прямо сейчас вывод закомментирован, но он работает как положено, используя правильные символы и цвета в нужных местах)

Вот конструктор VGATerminal:

VGATerminal::VGATerminal ()
{
this->row = 0;
this->col = 0;
this->currentColour = generateVGAColour(WHITE, BLACK);
this->vgaBuffer = (uint16*) VGATerminal::VGA_MEMORY;

for (uint16 r = 0; r < VGATerminal::HEIGHT; r++)    // Loop over rows
{
for (uint16 c = 0; c < 8; c++)  // Loop over columns
{
this->vgaBuffer[(r * WIDTH) + c] = generateColouredChar(' ', this->currentColour);
//this->print(' ');
}
}

//this->row = 0;
//this->col = 0;
}

Этот код отправляет мою ОС в бесконечный цикл всякий раз, когда столбцы повторяются после 7 (7 в порядке, 8 отправляет его в бесконечный цикл).
(7 хорошо, я имею в виду ожидаемое поведение очистки первых 7 столбцов экрана (хотя я не могу проверить это, так как до очистки экрана он не заполнен текстом. Текст, который включен) хотя экран стирается правильно)

Цикл начинается где-то в начале ядра (когда я генерирую ISO для своей ОС, он фактически возвращается к началу до того, как GRUB открывается, GRUB открывается, я выбираю свою ОС, и она возвращается к началу).

Когда я заменяю this->vgaBuffer[...] = ... расставаться с this->print(' ') все отлично работает (Если я это сделаю, мне тоже нужно сбросить this->row а также this->colвот почему эти две строки закомментированы в конце)

Вот VGATerminal::print(const char c) функция:

void VGATerminal::print (const char c)
{
switch (c)
{
case '\t':
// First insert 1 space, this will force the terminal to insert atleast this
// (Otherwise, you might get something like this:)
// CATS\tDOGS Becomes CATSDOGS instead of CATS    DOGS
// This happens because after writing CATS, the terminal is at a multiple of 4
// and decides it doesn't need to insert anything anymore
this->vgaBuffer[(this->row * WIDTH) + this->col] = generateColouredChar(' ', this->currentColour);
this->col++;

while ((this->col % 4) != 0)
{
this->vgaBuffer[(this->row * WIDTH) + this->col] = generateColouredChar(' ', this->currentColour);
this->col++;

if (this->col == WIDTH)
{
this->row++;
this->col = 0;
}
}

break;

case '\n':
this->row++;
this->col = 0;
break;

case '\r':
this->col = 0;
break;

default:
this->vgaBuffer[(this->row * WIDTH) + this->col] = generateColouredChar(c, this->currentColour);
this->col++;

if (this->col == WIDTH)
{
this->row++;
this->col = 0;
}

break;
}
}

Поддержка возврата каретки и новых строк может быть неполной, я хотел бы сначала избавиться от этой ошибки, прежде чем тестировать ее, хотя новые строки, кажется, работают правильно.

uint16 generateColouredChar (const char c, uint8 colour) на самом деле функция (в отличие от метода):

uint16 VGATerminal::generateColouredChar (const char c, uint8 colour)
{
return (uint16) colour << 8 | (uint16) c;
}

uint8, uint16, … — это псевдонимы для их соответствующих аналогов (uint8_t, uint16_t, …), созданные в другом заголовке следующим образом:

#include <stdint.h>

using uint8 = uint8_t;
using uint16 = uint16_t;
using uint32 = uint32_t;
using uint64 = uint64_t;

using uchar = uint16;

using int8 = int8_t;
using int16 = int16_t;
using int32 = int32_t;
using int64 = int64_t;

(Я сделал это, потому что это не уменьшает читабельность IMO, но избавляет меня от необходимости вводить этот раздражающий ‘_’)

Редактировать:

Вот полный класс VGATerminal для дальнейшего использования:

class VGATerminal
{
private:
constexpr static uint16 WIDTH = 80;
constexpr static uint16 HEIGHT = 25;
constexpr static int VGA_MEMORY = 0xB8000;

uint16  row;
uint16  col;
uint8   currentColour;
volatile uint16*    vgaBuffer;

public:
/// Colour constants
constexpr static uint8 BLACK = 0;
constexpr static uint8 BLUE = 1;
constexpr static uint8 GREEN = 2;
constexpr static uint8 CYAN = 3;
constexpr static uint8 RED = 4;
constexpr static uint8 MAGENTA = 5;
constexpr static uint8 BROWN = 6;
constexpr static uint8 LIGHT_GREY = 7;
constexpr static uint8 DARK_GREY = 8;
constexpr static uint8 LIGHT_BLUE = 9;
constexpr static uint8 LIGHT_GREEN = 10;
constexpr static uint8 LIGHT_CYAN = 11;
constexpr static uint8 LIGHT_RED = 12;
constexpr static uint8 LIGHT_MAGENTA = 13;
constexpr static uint8 LIGHT_BROWN = 14;
constexpr static uint8 WHITE = 15;

VGATerminal ();

void clear ();

void setTextColour (uint8 colour);

void setBackgroundColour (uint8 colour);

void print (const char c);

void print (const char* str);
};

До комментария Ped7g у меня не было поля vgaBuffer, помеченного как volatile,

Маркировка как volatile (как видно из приведенного выше класса) решил проблему, почтение Ped7g!

-1

Решение

Там нет явных причин (в оригинальном посте не было), почему 7/8 должны отличаться.

Как вы определяете VGATerminal::vgaBuffer? Вы отметили это как volatile избежать компилятора, оптимизирующего «бесполезные» записи в память?

Компилятор не понимает, что значение записи в 0xB8000 адрес имеет внешний эффект вне определения языка C ++, используя volatile вы даете подсказку компилятору, что это не регулярно объем памяти Переменная C ++ write (компилятор может свободно реализовывать переменную C ++ любым способом, даже без реальной записи в память, если код работает так, как определяет язык C ++), но фактическое чтение / запись в память должно выполняться, поскольку это может вызвать внешние эффекты (в случае записи), или значение может быть изменено извне (например, другой код потока, или ОС, или внешним устройством, которое также имеет доступ к памяти).

Кстати, как программист на ассемблере, меня раздражает видеть такое правильное for цикл просто очистить экран:

VGATerminal::VGATerminal ()
{
this->row = 0;
this->col = 0;
this->currentColour = generateVGAColour(WHITE, BLACK);
this->vgaBuffer = (uint16*) VGATerminal::VGA_MEMORY;

const uint16 whiteSpace = generateColouredChar(' ', this->currentColour);
for (unsigned i = 0; i < VGATerminal::HEIGHT*VGATerminal::WIDTH; ++i)
this->vgaBuffer[i] = whiteSpace;
}

Хотя я думаю, что оптимизатор, возможно, изменил вложенные циклы в один цикл после того, как вы использовали VGATerminal::WIDTH для столбцов это просто старая привычка программиста asm писать минимальный код и избегать умножений и множественных счетчиков в тех случаях, когда вы работаете с непрерывным блоком памяти, и логическое разделение строк / столбцов не имеет значения для текущей задачи.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]