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

odd*_*der 1 x86 gcc qemu inline-assembly osdev

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

#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);
}
Run Code Online (Sandbox Code Playgroud)

和文件 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));
}
Run Code Online (Sandbox Code Playgroud)

使用该功能前光标有时闪烁下划线有时不出现而使用该功能后没有光标出现

这是运行的主要功能

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

我尝试使用此功能

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
}
Run Code Online (Sandbox Code Playgroud)

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

编辑 我试图修复错误inb并且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 char value, unsigned short int port)
{
        asm volatile ("outb %%al, $0"::"Nd"(value), "a"(port));
}
Run Code Online (Sandbox Code Playgroud)

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

编辑 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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

Mic*_*tch 6

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;
}
Run Code Online (Sandbox Code Playgroud)

它的几个问题:

  • It seems you have the parameters to inb reversed. See the instruction set reference for inb. Remember that in AT&T syntax (that you are using in your GNU Assembler code) the operands are reversed. The instruction set reference shows them in Intel format.
  • The port number is either specified as an immediate 8 bit value or passed in the DX register. The proper constraint for specifying the DX register or an immediate 8 bit value for inb/outb is Nd. See my Stackoverflow answer here for an explanation of the constraint Nd.
  • The destination that the value read is returned in is either AL/AX/EAX so a constraint =rm on the output that says an available register or memory address is incorrect. It should be =a in your case.

Your code should be something like:

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

Your assembler template for outb is incorrect:

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

A couple problems with it:

  • The port number is either specified as an immediate 8 bit value or passed in the DX register. The proper constraint for specifying the DX register or an immediate 8 bit value for inb/outb is Nd. See my Stackoverflow answer here for an explanation of the constraint Nd.
  • The value to output on the port has to be specified in AL/AX/EAX so a constraint rm on the value that says an available register or memory address is incorrect. It should be a in your case. See the instruction set reference for outb

The code should probably look something like:

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

Enabling and Disabling the Cursor

I had to look up the VGA registers about the cursor and found this document on the cursor start register which says:

   Cursor Start Register (Index 0Ah)

-------------------------------------------------
|  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0  |
-------------------------------------------------
|     |     | CD  |     Cursor Scan Line Start  |
-------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

CD -- Cursor Disable

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

0 -- Cursor Enabled

1 -- Cursor Disabled

Cursor Scan Line Start

An important thing is that the cursor is disabled when the bit 5 is set. In your github setcursor function you do this:

outb(curstart | 0x20, 0x3D5);
Run Code Online (Sandbox Code Playgroud)

curstart | 0x20 sets bit 5 (0x20 = 0b00100000). If you want to clear bit 5 and enable the cursor, then you bitwise NEGATE(~) the bitmask and bitwise AND (&) that with curstart. It should look like this:

outb(curstart & ~0x20, 0x3D5);
Run Code Online (Sandbox Code Playgroud)

VGA Function Bugs

Once you have the cursor properly enabled it will render the cursor in the foreground color (attribute) for the particular video location it is currently over. One thing I noticed is that your clc routine does this:

vga_deref_80x24(VGAx,VGAy) = \
        vga_encode_80x24(' ',BgColor,BgColor);
Run Code Online (Sandbox Code Playgroud)

The thing to observe is that you set the attribute for the foreground and background colors to BgColor . If you set the bgcolor to black before calling clc it will flash a black underline cursor on a black background rendering it invisible on any screen location. For the cursor to be visible it must be on a screen location where the foreground and background are different colors. One way to see if this works is to change the code to:

vga_deref_80x24(VGAx,VGAy) = \
        vga_encode_80x24(' ',BgColor,ForeColor);
Run Code Online (Sandbox Code Playgroud)

I think it is a bug that you are clearing it with encoding vga_encode_80x24(' ',BgColor,BgColor); I think you mean to use vga_encode_80x24(' ',BgColor,ForeColor);

Now in your kmain function you need to set a ForeColor and BgColor before calling clc and they both must be different color to make the cursor visible. You have this code:

setbgcolor(BLACK);
clc();
setforecolor(BLUE);
Run Code Online (Sandbox Code Playgroud)

It should now be:

setbgcolor(BLACK);
setforecolor(BLUE);
clc();
Run Code Online (Sandbox Code Playgroud)

Now if the cursor is rendered anywhere on an unwritten location on the screen it will flash BLUE underline on BLACK background.

This should solve your cursor problem. However, I noticed that you also use encode vga_encode_80x24(' ',BgColor,BgColor); in your VGA scrolldown and terminal_control functions. I think this is a bug as well, and I think you should use encode vga_encode_80x24(' ',BgColor,ForeColor); instead. You do seem to set it properly in terminal_write.

If you want to change the color of the cursor at any location you could write a function that changes the foreground attribute under the cursor location without changing the background color. Make sure the two attributes (Foreground and background color) are different for the cursor to be visible. If you wish to hide the cursor you can set foreground and background color the same color for the screen location the cursor is currently at.

  • 这篇文章非常有帮助——谢谢。(我的特殊问题是,当我滚动显示屏时,我将最后一行归零,将颜色设置为黑底黑字 - 即不可见 - 因此光标也是如此。) (2认同)