我正在制作操作系统,或者至少在尝试,但我偶然发现了一个问题。当试图迭代字符串以转换为char打印到屏幕时,返回的char似乎是空的!(实际上我是操作系统开发的新手(;以下是代码片段:
int offset = 0;
void clear_screen() {
unsigned char * video = 0xB8000;
for(int i = 0; i < 2000; i+=2){
video[i] = ' ';
}
}
void printc(char c) {
unsigned char * video = 0xB8000;
video[offset] = c;
video[offset+1] = 0x03;
offset += 2;
}
void print(unsigned char *string) {
char * sus = ' ';
uint32 i = 0;
printc('|');
sus[0] = 'a';
printc(sus[0]); //this prints "a" correctly
string[i] = 'c';
while (string[i] != ' ') {
printc(string[i]); //this while loop is only called once
i++; //it prints " " only once and exits
}
printc('|');
}
int bootup(void)
{
clear_screen();
// printc('h');
// printc('e');
// printc('l'); /* These work */
// printc('l');
// printc('o');
print("hello"); //this doesn't
return 1;
}
它打印的输出:
|a |
提前感谢!!
编辑
新的打印功能
void print(unsigned char *string) {
uint32 i = 0;
printc('|');
while (string[i] != ' ') {
printc('i'); //not printed
printc(string[i]);
i++;
}
printc('|');
}
仍然不起作用
编辑2根据@lundin的建议更新了代码
int offset = 0;
void clear_screen() {
unsigned char * video = (unsigned char *)0xB8000;
for(int i = 0; i < 2000; i+=2){
video[i] = ' ';
}
}
void printc(char c) {
unsigned char * video = (unsigned char *)0xB8000;
video[offset] = c;
video[offset+1] = 0x03;
offset += 2;
}
void print(const char *string) {
int i = 0;
printc('|');
while (string[i] != ' ') {
printc('i');
printc(string[i]);
i++;
}
printc('|');
}
int bootup(void)
{
clear_screen();
// printc('h');
// printc('e');
// printc('l');
// printc('l');
// printc('o');
print("hello");
return 1;
}
堆栈:
init_lm:
mov ax, 0x10
mov fs, ax ;other segments are ignored
mov gs, ax
mov rbp, 0x90000 ;set up stack
mov rsp, rbp
;Load kernel from disk
xor ebx, ebx ;upper 2 bytes above bh in ebx is for cylinder = 0x0
mov bl, 0x2 ;read from 2nd sectors
mov bh, 0x0 ;head
mov ch, 1 ;read 1 sector
mov rdi, KERNEL_ADDRESS
call ata_chs_read
jmp KERNEL_ADDRESS
jmp $
在继续之前,我建议阅读基于文本的UI上的OSDev wiki页面。
虽然这可能在一定程度上超出了问题的范围,但我强烈建议您不要手动将字符/属性值作为unsigned char
来处理,而应该为这些对声明一个struct
类型:
struct TextCell {
volatile unsigned char ch;
volatile uint8_t attribute;
};
(实际上,通过为属性的各个前景、背景和装饰组件使用位字段,您可以对其进行更精细的处理,但这可能会超前。(
从那里你可以将文本缓冲区定义为一个常量指针:
const struct TextCell* text_buffer = (TextCell *)0xB8000;
您可以进一步定义
const uint16_t MAXH = 80, MAXV = 25;
uint16_t currv = 0, currh = 0;
struct TextCell* text_cursor = text_buffer;
void advance_cursor() {
text_cursor++;
if (currh < MAXH) {
currh++;
}
else {
currh = 0;
if (currv < MAXV) {
currv++;
}
else {
/* handle scrolling */
}
}
}
void gotoxy(uint16_t x, uint16_t y) {
uint16_t new_pos = x * y;
if (new_pos > (MAXV * MAXH)) {
text_cursor = text_buffer + (MAXV * MAXH);
currh = MAXH;
currv = MAXV;
}
else {
text_cursor += new_pos;
currh = x;
currv = y;
}
这将导致对您的代码进行以下修改:
void kprintc(char c, uint8_t attrib) {
text_cursor->ch = c;
text_cursor->attribute = attrib;
advance_cursor();
}
void kprint(const char *string, uint8_t attribs) {
int i;
for (i = 0; string[i] != ' '; i++) {
kprintc(string[i], attribs);
}
}
void clear_screen() {
for(int i = 0; i < (MAXH * MAXV); i++) {
kprintc(' ', 0);
}
}
int bootup(void) {
clear_screen();
// kprintc('h', 0x03);
// kprintc('e', 0x03);
// kprintc('l', 0x03);
// kprintc('l', 0x03);
// kprintc('o', 0x03);
kprint("hello", 0x03);
return 1;
}
那么,我为什么要建议所有这些额外的东西呢?因为用这种方式调试要容易得多,主要是因为它能更好地划分问题,更有效地构建数据(或者在这种情况下,视频文本缓冲区(。此外,在项目中的某个时刻,您最终需要做这样的事情,所以如果这对现在有帮助,您还不如现在就做。
如果我在这件事上越界了,请告诉我。
您的程序有未定义的行为,因为它包含多行无效的C。您将收到关于这些行的编译器消息。
unsigned char * video = 0xB8000;
等不是有效的C,您需要显式强制转换"来自整数的指针/来自不带强制转换的指针的整数";问题- 类似地,
char * sus = ' ';
也是无效的C。您试图将指针分配给单个字符,这是没有意义的。字符串处理初学者常见问题解答:C编程中常见的字符串处理陷阱。它还解决了内存分配的基本问题 sus[0] = 'a';
等等,因为sus
没有指向有效内存,所以这里有大量未定义的行为- 如果你真的试图访问物理内存地址,这不是正确的方法。你需要volatile限定指针。请参阅如何从固件访问硬件寄存器?(在您的情况下,它可能不是一个寄存器,但该链接中的所有内容仍然适用——如何使用十六进制常量等。(
- 编辑:
void print(unsigned char *string)
。。。string[i] = 'c';
也是错误的。首先,您传递的是一个char*
,它不一定与unsigned char*
兼容。那么,您不应该从一个名为print
的函数内部修改传递的字符串,这是没有意义的。这应该是const char* string
以防止此类错误。目前,您正在向该函数传递一个字符串文字,然后尝试修改它——这是未定义的行为,因为字符串文字是只读的
假设是gcc或clang,如果您希望阻止编译器用无效的C代码生成可执行文件,请查看学习C的初学者建议使用哪些编译器选项?在您的情况下,您可能还需要上面提到的-ffreestanding
选项。
char * sus = ' ';
尚未检查更多。。。但这会为sus
分配一个空指针,很可能不是您想要做的