文本模式光标不会出现在qemu vga模拟器中

时间:2015-09-26 19:19:42

标签: c assembly qemu osdev vga

我在文本模式下更新光标位置的功能有问题 函数定义和声明是

#include <sys/io.h>
signed int VGAx = 0,VGAy=0;
void setcursor()
{
        uint16_t position = VGAx+VGAy*COLS;
        outb(0x0f, 0x03d4);
        outb((position<<8)>>8,0x03d5);
        outb(0x0e,0x03d4);
        outb(position>>8,0x03d5);
}

和文件sys / io.h

static inline unsigned char inb (unsigned short int port)
{
        unsigned char value;
        asm ("inb %0, %%al":"=rm"(value):"a"(port));
        return value;
}
static inline void outb(unsigned char value, unsigned short int port)
{
        asm volatile ("outb %%al, $0"::"rm"(value), "a"(port));
}

在使用该功能之前,光标有时会闪烁下划线,有时候在使用该功能后没有出现没有光标出现

这是运行的主要功能

#include <vga/vga.h>
int kmain(){
        setcursor()
        setbgcolor(BLACK);
        clc();
        setforecolor(BLUE);
        terminal_write('h');
        setcursor();
        return 0;
}

我尝试使用此功能

void enable_cursor() {
    outb(0x3D4, 0x0A);
    char curstart = inb(0x3D5) & 0x1F; // get cursor scanline start

    outb(0x3D4, 0x0A);
    outb(0x3D5, curstart | 0x20); // set enable bit
}

提供here但我收到此错误 inline asm: operand type mismatch for 'in' 任何帮助表示赞赏

修改 我试图修正错误的inboutb

static inline unsigned char inb (unsigned short int port)
{
        unsigned char value;
        asm volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
        return value;
}
static inline void outb(unsigned char value, unsigned short int port)
{
        asm volatile ("outb %%al, $0"::"Nd"(value), "a"(port));
}

我想这是正确的定义,但仍然没有光标显示

编辑2 我按照给定的答案将io.h文件定义为以下

static inline unsigned char inb (unsigned short int port)
{
        unsigned char value;
        asm volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
        return value;
}
static inline void outb(unsigned char value, unsigned short int port)
{
        asm volatile ("outb %0, %1"::"a"(value), "Nd"(port));
}

我想提一下,我还将enable_cursor();添加到kmain的开头,现在编译时错误已修复,但没有出现光标(这是主要问题)

编辑3 我想指出,如果任何人想要访问问题中没有的代码片段,那么整个代码的版本可以在gihub上获得

1 个答案:

答案 0 :(得分:4)

inb和outb功能错误

inb的此代码不正确:

static inline unsigned char inb (unsigned short int port)
{
        unsigned char value;
        asm ("inb %0, %%al":"=rm"(value):"a"(port));
        return value;
}

它有一些问题:

  • 您似乎有inb的参数被反转。查看inb的{​​{3}}。请记住,在AT&amp; T语法(您在GNU汇编程序代码中使用)中,操作数是相反的。指令集参考以英特尔格式显示它们。
  • 端口号可以指定为立即8位值,也可以在 DX 寄存器中传递。指定DX寄存器或inb / outb的直接8位值的正确约束是Nd。有关约束Nd的解释,请参阅我的Stackoverflow instruction set reference
  • 返回值的目标是 AL / AX / EAX ,因此输出上的约束=rm表示可用的寄存器或内存地址不正确。在你的情况下它应该是=a

您的代码应该是:

static inline unsigned char inb (unsigned short int port)
{
        unsigned char value;
        asm volatile ("inb %1, %0" : "=a"(value) : "Nd"(port));
        return value;
}

outb的汇编程序模板不正确:

static inline void outb(unsigned char value, unsigned short int port)
{
        asm volatile ("outb %%al, $0"::"rm"(value), "a"(port));
}

有几个问题:

  • 端口号可以指定为立即8位值,也可以在 DX 寄存器中传递。指定DX寄存器或inb / outb的直接8位值的正确约束是Nd。有关约束Nd的解释,请参阅我的Stackoverflow answer
  • 必须在 AL / AX / EAX 中指定要在端口上输出的值,因此对可用寄存器或内存地址的值的约束rm不正确。在你的情况下它应该是a。有关outb
  • 的信息,请参见answer

代码应该类似于:

static inline void outb(unsigned char value, unsigned short int port)
{
        asm volatile ("outb %0, %1"::"a"(value), "Nd"(port));
}

启用和禁用光标

我必须查找关于光标的VGA寄存器,并在光标开始寄存器上找到instruction set reference,其中包含:

   Cursor Start Register (Index 0Ah)

-------------------------------------------------
|  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
-------------------------------------------------
|     |     | CD  |     Cursor Scan Line Start  |
-------------------------------------------------
     

CD - 光标禁用

     

此字段控制是否显示文本模式光标。值是:

     
    

0 - 启用光标

         

1 - 光标已禁用

  
     

光标扫描线开始

重要的是,当 bit 5 设置时,光标禁用。在 github setcursor函数中,您可以执行以下操作:

outb(curstart | 0x20, 0x3D5);

curstart | 0x20 设置 第5位(0x20 = 0b00100000)。如果你想清除 第5位启用光标,那么你按位 NEGATE (〜)位掩码和按位 AND (&amp;)与curstart。它应该是这样的:

outb(curstart & ~0x20, 0x3D5);

VGA功能错误

正确启用光标后,它会将光标呈现为当前所在特定视频位置的前景色(属性)。我注意到的一件事是你的clc程序会这样做:

vga_deref_80x24(VGAx,VGAy) = \
        vga_encode_80x24(' ',BgColor,BgColor);

要注意的是,您要将前景色和背景色的属性设置为BgColor。如果在调用bgcolor之前将clc设置为黑色,则会在黑色背景上闪烁黑色下划线光标,使其在任何屏幕位置都不可见。要使光标可见,它必须位于前景和背景为不同颜色的屏幕位置。查看是否有效的一种方法是将代码更改为:

vga_deref_80x24(VGAx,VGAy) = \
        vga_encode_80x24(' ',BgColor,ForeColor);

我认为您使用编码vga_encode_80x24(' ',BgColor,BgColor);清除它是一个错误我认为您的意思是使用vga_encode_80x24(' ',BgColor,ForeColor);

现在在kmain函数中,您需要在调用clc之前设置 ForeColor BgColor ,并且它们的颜色必须不同光标可见。你有这个代码:

setbgcolor(BLACK);
clc();
setforecolor(BLUE);

现在应该是:

setbgcolor(BLACK);
setforecolor(BLUE);
clc();

现在,如果光标在屏幕上未写入的位置上的任何位置呈现,它将在黑色背景上闪烁蓝色下划线。

这应解决您的光标问题。但是,我注意到您还在VGA encode vga_encode_80x24(' ',BgColor,BgColor);scrolldown功能中使用terminal_control。我认为这也是一个错误,我认为你应该使用encode vga_encode_80x24(' ',BgColor,ForeColor);。您似乎确实在terminal_write中正确设置了它。

如果要在任何位置更改光标的颜色,可以编写一个更改光标位置下的前景属性而不更改背景颜色的函数。确保两个属性(前景色和背景色)不同,以使光标可见。如果你想隐藏光标,你可以为光标当前所在的屏幕位置设置相同颜色的前景色和背景色。