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

text mode cursor doesn't appear in qemu vga emulator

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

#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, [=11=]"::"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, [=14=]"::"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));
}

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

编辑 3 我想指出的是,如果有人想访问问题中不可用的代码片段,可以在 gihub 上获得整个代码的一个版本

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 的参数颠倒了。请参阅 inbinstruction set reference。请记住,在 AT&T 语法(您在 GNU 汇编程序代码中使用的语法)中,操作数是相反的。指令集参考以 Intel 格式显示它们。
  • 端口号指定为立即数 8 位值或在 DX 寄存器中传递。指定 DX 寄存器或 inb/outb 的立即 8 位值的正确约束是 Nd。请参阅我的 Whosebug 此处了解约束 Nd.
  • 的解释
  • 读取的值返回的目的地是 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, [=12=]"::"rm"(value), "a"(port));
}

它有几个问题:

  • 端口号指定为立即数 8 位值或在 DX 寄存器中传递。指定 DX 寄存器或 inb/outb 的立即 8 位值的正确约束是 Nd。请参阅我的 Whosebug 此处了解约束 Nd.
  • 的解释
  • 必须在 AL/AX/EAX 中指定要在端口上输出的值,因此对表示可用寄存器或内存的值的约束 rm地址不正确。在你的情况下应该是 a 。请参阅 instruction set reference 以获得 outb

代码可能类似于:

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

启用和禁用光标

我不得不查找有关光标的 VGA 寄存器,并在 光标启动寄存器 上找到了这个 document,上面写着:

   Cursor Start Register (Index 0Ah)

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

CD -- Cursor Disable

This field controls whether or not the text-mode cursor is displayed. Values are:

0 -- Cursor Enabled

1 -- Cursor Disabled

光标扫描线开始

重要的是当位5设置时,游标是禁用 .在你的 github setcursor 函数中你这样做:

outb(curstart | 0x20, 0x3D5);

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

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

VGA 功能错误

一旦您正确启用了光标,它将以前景色(属性)呈现光标,以显示当前所在的特定视频位置。我注意到的一件事是您的 clc 例程是这样做的:

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

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

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

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

现在在你的 kmain 函数中你需要在调用 clc 之前设置 ForeColorBgColor它们都必须是不同的颜色才能使光标可见。你有这个代码:

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

现在应该是:

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

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

这应该可以解决您的光标问题。但是,我注意到您还在 VGA scrolldownterminal_control 函数中使用了 encode vga_encode_80x24(' ',BgColor,BgColor);。我认为这也是一个错误,我认为您应该改用 encode vga_encode_80x24(' ',BgColor,ForeColor);。您似乎确实在 terminal_write.

中正确设置了它

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

问题出在您的 outb 代码中。还要注意订单端口和值参数。

以下适合我的作品:

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 short int port, unsigned char value)
{
    asm volatile ("outb %b0,%w1": :"a" (value), "Nd" (port));
}

void update_cursor(int x, int y)
{
    uint16_t pos = y * 80 + x;
 
    outb(0x3D4, 0x0F);
    outb(0x3D5, (uint8_t) (pos & 0xFF));
    outb(0x3D4, 0x0E);
    outb(0x3D5, (uint8_t) ((pos >> 8) & 0xFF));
  }