我正在尝试在C 中编写一个内核(OS内核),并且我遇到了一个非常奇怪的问题(我敢肯定这很明显为其他任何人都做错了,我只是为了我的一生找不到什么错)
我正在使用C 类代表VGA控制台(BIOS应在地址0xB8000加载),可以在OS启动序列中访问以进行调试输出。
(有关更多信息,请阅读: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("]ttKernel gained control of the CPU.n");
//kernelTerm.print("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123465789n");
}
}
(目前,输出已评论,但使用正确的位置使用正确的字符和颜色,它可以按预期进行评论)
)这是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:)
// CATStDOGS 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;
}
}
支持马车返回和新线的支持可能还不完整,我想在测试这些错误之前先摆脱此错误,Altough Newlines似乎正常工作。
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
,以避免编译器优化"无用"记忆?
编译器不了解0xB8000
地址的写作值在C 语言定义之外具有外部效果,通过使用volatile
,您给出编译器提示,它不是常规
btw作为装配程序员,我很高兴看到这样的嵌套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程序员的旧习惯,可以编写最小的代码,并避免使用连续工作的情况下乘以乘法和多个计数器内存块,行/列的逻辑分离对于当前任务并不重要。