【发布时间】:2018-03-03 02:34:21
【问题描述】:
我正在尝试用 C++ 编写一个内核(一个 OS 内核),但遇到了一个非常奇怪的问题(我敢肯定,对于其他人来说,我做错了什么很明显,我只是我一辈子都找不到问题所在)
我正在使用 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->print(' ') 替换this->vgaBuffer[...] = ... 部分时,一切正常。 (如果我这样做,我还需要重置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);
};
【问题讨论】:
-
没有明显的理由说明 7/8 应该不同。你如何定义
VGATerminal::vgaBuffer?您是否将其标记为volatile以避免编译器优化“无用”内存写入?什么是“无限循环”,它在哪些指令上循环以及为什么,您是否在汇编级别检查调试器,发生了什么? -
当然,volatile,非常感谢!
-
记下你是如何设法在没有重要部分(问题来源)的情况下发布问题的。在编程中,数据结构和定义通常比代码更重要,或者至少与代码一样重要。如果您只发布数据定义而不发布代码,我实际上能够更快、更有信心地回答您。多想想你的数据(它们在哪里,什么类型,以及你如何使用它们),代码永远不会超过它,代码只是对数据的操作。
-
感谢您的反馈,下次我提出问题时一定会尝试并记住:)
标签: c++ console operating-system kernel vga