我想要一个简单的Hello World!内核启动。每当我打开我的虚拟机时,我都会看到GRUB加载,选择我的操作系统,然后在屏幕上获得一个随机字符,并在三重故障时崩溃。我相信问题在于我的VGA部分。我怀疑问题出在我使用C指针的某个地方。 C和指针都不是强项。具体来说,我怀疑涉及put_char()函数。有一些涉及指针的编译器问题。我究竟做错了什么?我该如何解决这个问题?
感谢您的帮助!
以下是我的VGA部分的代码:
/*
This file will handle dealing with VGA stuff so I can print to the screen.
*/
#include <system.h>
/* this pointer will be set to the VGA access memory address */
unsigned short *text_ptr;
int attrib = 0x0F; // foreground and background color gets set in here.
int csr_x = 0; // cursor x position
int csr_y = 0; // cursor y position
int screen_width = 80; // width of the screen in columns
int screen_height = 25; // height of the screen in rows
void vga_initialize()
{
// set the text pointer to the proper location in memory
text_ptr = (unsigned short *) 0x0B8000;
clear_screen();
}
void set_text_color(unsigned char foreground_color, unsigned char background_color)
{
attrib = (background_color << 4) | (foreground_color & 0x0F);
}
void put_str(char *text)
{
// figure out the length of the string we'll be printing
char *my_text = text; // make a local copy of the pointer so the orinal's not overwritten.
int str_length;
for (str_length = 0; *my_text != '\0'; my_text++) // set the string lenght to 0; if the character at the pointer isn't zero; increment the pointer
{
str_length++; // increment the count
}
// for each character in the string
for (int i = 0; i < str_length; i++)
{
put_char(my_text[i]); // put the character on the screen
}
}
void put_char(char c)
{
unsigned short *index_ptr;
unsigned int my_attrib = attrib << 8;
if (c == 0x08) // backspace
{
if (csr_x != 0)
{
csr_x--;
}
}
else if (c == 0x09) // tab
{
csr_x = (csr_x + 8) & ~(8 - 1); // increment x but only to a point that will make it divisible by 8 (i dunno, lawl)
}
else if (c == '\r') // carriage return, move cursor to beginning of row
{
csr_x = 0;
}
else if (c == '\n') // newline
{
csr_x = 0;
csr_y++;
}
else if (c >= ' ') // any character greater than or equal to space is a printable character
{
// figure out where to put the character
index_ptr = text_ptr + (csr_y * screen_width + csr_x);
// put the character and it's attribute into memory, which will put it on the screen when it refreshes.
*index_ptr = c | my_attrib;
}
scroll();
move_csr();
}
void scroll()
{
unsigned short space_char = 0x20 | (attrib << 8);
// used to calculate the offsets needed when moving data around in the vga buffer memory
int temp_offset;
// screen's full, we need to scroll
if (csr_y >= screen_height)
{
// move everything up
temp_offset = csr_y - screen_height + 1; // offset for calculating the addresses to use in the vga memory
memcpy(text_ptr, text_ptr + temp_offset * screen_width, (screen_height - temp_offset) * screen_width * 2);
// set the chunk of memory that occupies the last line of text to spaces
memsetw(text_ptr + (screen_height - temp_offset) * screen_width, space_char, screen_width);
// adjust the position of the cursor
csr_y = screen_height - 1;
}
}
void move_csr()
{
unsigned char curr_pos = csr_y * screen_width + csr_x;
outportb(0x3D4, 14);
outportb(0x3D5, curr_pos >> 8);
outportb(0x3D4, 15);
outportb(0x3D5, curr_pos);
}
void clear_screen()
{
unsigned short space_char;
// i need to generate the ascii code? that represents a space of the proper background color
space_char = 0x20 | (attrib << 8);
// write spaces to the entire screen
for (int i = 0; i < screen_height; i++)
{
for (int j = 0; j < screen_width; j++)
{
memsetw(text_ptr + i * screen_width, space_char, 1);
}
}
// reset the cursors x and y position
csr_x = 0;
csr_y = 0;
move_csr(); // and reset the cursor in the hardware
}
我一直在关注各种教程,试图创建自己的操作系统。到目前为止,我一直在使用描述here的GNU工具链。我正在使用带有工具链的GNU汇编程序,因为我遵循了指示,并没有考虑将NASM构建到其中。我宁愿学习另一种装配,而不是重做工具链。 :-P我一直在模拟我对Bran's Kernel Development tutorial.的尝试我试图将C代码和GAS代码保存在单独的文件中,因为我不喜欢这些教程如何将所有程序集合到引导加载程序中。我是在Windows 7机器上制作的,但是使用Debian进行编码。我在Windows上安装了Oracle VirtualBox,并使用它来模拟Debian。然后我使用我的开发工具设置Debian,并使用文本编辑器进行编码。
下面是我正在使用的所有代码的链接,崩溃的VirtualBox日志,我在Debian终端中用来构建我的代码的命令的日志文件,目标文件,二进制文件和我制作的iso文件。
http://wikisend.com/download/243118/POS_C3.zip
如果您想自己查看错误,那么您应该能够将pos.iso挂载到虚拟机上并运行它。它应该像宣传的那样崩溃。这时我已经为引导加载程序,内核主函数,GDT和VGA代码制作了代码。我在自己的C文件中有内存和I / O端口函数,GDT的GAS也在它自己的文件中。
答案 0 :(得分:3)
我看到两个问题:
1)您的put_str()
函数访问text
字符串末尾之外的无效内存,这是未定义的行为:
void put_str(char *text)
{
char *my_text = text; // (1)
int str_length;
for (str_length = 0; *my_text != '\0'; my_text++) // (2)
{
str_length++; // increment the count
}
for (int i = 0; i < str_length; i++)
{
put_char(my_text[i]); // (3)
}
}
在这种情况下,您将my_text
分配给(1)处字符串的开头,然后将其前进到(2)处字符串的末尾。然后,您将从(3)处的字符串末尾开始索引它,这将读取内存超出范围。您可能打算在(3)处写text[i]
而不是my_text[i]
,或者您可能忘记在该循环之前重置my_text = text;
(要么产生相同的结果)。您也可以通过调用strlen()
来替换整个第一个循环,但不是那个更简单的解决方案:
void put_str(char *text)
{
while (*text != 0)
{
put_char(*text);
text++;
}
}
这会遍历字符串,寻找NUL终结符,并在每个字符输出时输出。不需要先计算字符串长度,然后再重复字符串。
2)memcpy()
函数内对scroll()
的调用具有未定义的行为,因为源和目标内存范围可能重叠:
memcpy(text_ptr, text_ptr + temp_offset * screen_width, (screen_height - temp_offset) * screen_width * 2);
这可能不会导致您看到的三重错误异常,但它可能导致数据以奇怪的方式被错误地复制,具体取决于标准库中memcpy()
的实现方式。但由于它是未定义的行为,你必须为任何事情做好准备,包括从鼻子里冒出来的恶魔。
简单易用的解决方案是用memmove()
调用替换它,即使源和目标内存范围重叠,它也定义了行为:
memmove(text_ptr, text_ptr + temp_offset * screen_width, (screen_height - temp_offset) * screen_width * 2);