首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在VESA中绘制像素?

如何在VESA中绘制像素?
EN

Stack Overflow用户
提问于 2022-07-24 01:13:34
回答 1查看 124关注 0票数 1

我正在尝试将我的kernel.cpp转换成使用VESA,尽管我得到的只是一系列奇怪的错误。在教程之后,我使用以下代码使VESA的引导加载程序运行得相当顺利:

代码语言:javascript
复制
mov ax, 0x4F02   ; set VBE mode
mov bx, 0x4118   ; VBE mode number
int 0x10         ; call VBE BIOS
cmp ax, 0x004F   ; test for error
jne error

;Get video mode info
mov ax, 4f01h
mov cx, 105h
mov di, modeInfo 
int 10h

然而,当我尝试从kernel.cpp绘制像素时,问题就出现了。无论我在代码中更改了什么,我都会遇到一个错误(可能我还会添加一些令人惊讶的变化)。我试着从引导器(来自这里的代码)中绘制像素,它工作得很好,所以kernel.cpp中的问题就出现了。我遇到的一个常见错误是屏幕顶部出现了一条随机颜色,如这里所见。在这里,我将保留该文件和我的GitHub (用于其余代码):

kernel.cpp:

代码语言:javascript
复制
//VIDEO MODE IN 1024x768x32bpp
typedef unsigned char uint8_t;
typedef unsigned char u8;
typedef unsigned short uint16_t;
typedef unsigned int u32;
typedef u32 size_t;
#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768
#define BPP 32
#define SCREEN_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT)
#define FPS 30
#define PIT_HERTZ 1193131.666
#define CLOCK_HIT (int)(PIT_HERTZ/FPS)
#define KEY_LEFT 0x4B
#define KEY_UP 0x48
#define KEY_RIGHT 0x4D
#define KEY_DOWN 0x50

static u32 *BUFFER = (u32 *) 0xA0000;

// double buffers
u32 _sbuffers[2][SCREEN_SIZE];
u32 _sback = 0;

#define CURRENT (_sbuffers[_sback])
#define SWAP() (_sback = 1 - _sback)

#define screen_buffer() (_sbuffers[_sback])

// #define screen_set(_p, _x, _y)\
//     (_sbuffers[_sback][((_y)/* * pitch */ + ((_x) * (BPP/8)))]=(_p))

//u32 pixel_offset = y * pitch + (x * (bpp/8)) + framebuffer;

static inline void outb(uint16_t port, uint8_t val)
{
    asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
}

//+++++++++++This is the troublesome function++++++++++++++++++
void screen_set(u32 color,int x,int y) {
    _sbuffers[_sback][y * SCREEN_WIDTH + (x * (BPP/8))]=color;
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static inline uint8_t inb(uint16_t port)
{
    uint8_t ret;
    asm volatile ( "inb %1, %0"
                   : "=a"(ret)
                   : "Nd"(port) );
    return ret;
}

const unsigned char font[128-32][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
           /*hidden to help length if code...*/
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
};

static inline void *memcpy(void *dst, const void *src, size_t n)
{
    u8 *d = (u8*)dst;
    const u8 *s = (const u8*)src;

    while (n-- > 0) {
        *d++ = *s++;
    }

    return d;
}

void screen_swap() {
    memcpy(BUFFER, CURRENT, SCREEN_SIZE);
    SWAP();
}

unsigned read_pit(void) {
    unsigned count = 0;
 
    // al = channel in bits 6 and 7, remaining bits clear
    outb(0x43,0b0000000);
 
    count = inb(0x40);          // Low byte
    count |= inb(0x40)<<8;      // High byte
 
    return count;
}
 
void draw_char(char c, int x, int y, u32 color)
{
    const unsigned char *glyph = font[(int)c-32];
 
    for(int cy=0;cy<8;cy++){
        for(int cx=0;cx<8;cx++){
            if(((int)glyph[cy]&(1<<cx))==(1<<cx)){
                screen_set(color,x+cx,y+cy);
            }
        } 
    }
}

void draw_string(const char * s, int x, int y, u32 color) {
    int i = 0;
    while(s[i] != false) {
        draw_char(s[i],x+(i*8),y,color);
        i++;
    }
}

void draw_rect(int pos_x, int pos_y, int w, int h, u32 color) {
    for(int y = 0; y<h; y++) {
        for(int x = 0; x<w; x++) {
            screen_set(color,x+pos_x,y+pos_y);
        }
    }
}

static void render(int c0, int c1) {
    //draw_rect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,0x09);
    //draw_string("Hello, reader. This is written text.", 100, 180, 16777215);
    //draw_string("If this is displayed, my code works.", 100+c0, 100+c1, 16777215);
}

extern "C" void main(){
    
    int clock = 0;
    int incC1 = 0;
    int incC0 = 0;
    while(true) {
        uint16_t scancode = (uint16_t) inb(0x60);
        clock++;
        if(read_pit()!= 0 && clock == CLOCK_HIT) {
            if(scancode == KEY_LEFT) {
                incC0--;
            }else if(scancode == KEY_RIGHT) {
                incC0++;
            }
            if(scancode == KEY_DOWN) {
                incC1++;
            }else if(scancode == KEY_UP) {
                incC1--;
            }
            clock = 0;
            render(incC0,incC1);
            screen_swap();
        }
    }

    return;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-24 03:26:01

使用您已经获得的模式信息结构(在真实模式下的modeInfo ),您将需要3条信息:

  • 帧缓冲区的物理地址。请注意(启用了线性帧缓冲区)这将不是0x0A0000,这是对VGA的,并且可能是一个32位的物理地址,比如0xE0000000。这是直接从模式信息结构中的PhysBasePtr字段获取的。
  • 每一行像素的字节数。注意,在每一行的末尾可能有一些未使用的填充,您需要考虑到这一点。这取决于VBE的版本-对于VBE 3.0,您从模式信息结构的LinBytesPerScanLine字段中获取它;对于较早版本的VBE,则从BytesPerScanLine字段获取它。
  • 每像素的字节数。这取决于像素格式和VBE版本。对于古代VBE (VBE 1.0?)您必须根据视频模式数进行猜测;对于VBE 1.2及更高版本,可以从模式信息结构和舍入(如BitsPerPixel )中的BitsPerPixel字段(如bytesPerPixel = (modeInfo->BitsPerPixel + 7) / 8; )中确定,因此“每个像素15位”为每个像素2字节。

有了这3个值之后,计算像素物理地址的公式是:

代码语言:javascript
复制
physical_address = physical_address_of_frame_buffer + y * bytes_per_scan_line + x * bytes_per_pixel

当然,更多的时候,您可以将帧缓冲区映射到虚拟地址空间中,但公式基本上是相同的(仅使用虚拟地址和您选择的virtual_address_of_frame_buffer )。

其他说明:

  • 您的screen_set()没有考虑到每一行像素末尾的可能填充(并且没有正确地舍入)。
  • 您的screen_swap()没有考虑到每一行像素末尾可能的填充。它需要更像for(y = 0; y < vertical_resolution; y++) { address = virtual_address_of_frame_buffer + y * bytes_per_scan_line; memcpy(address, ...单独执行每一行。当没有填充时,您可以(我会)检测,并将其作为单个memcpy()执行当且仅当没有填充。
  • 固定模式号码(例如"0x118 =1024x768,每像素24位“)已在VBE 2.0中停止使用,不应使用(例如,视频模式0x118可以是任何东西,也可以是"640 x 480,每像素8位”,另一些视频模式号可能是"1024 x 768,每像素24位“)。您应该/必须获得可能的模式号的列表,然后搜索实际要确定的模式“1024x768,每个像素24位”。
  • 您不应该假设存在特定的模式。这对于“1024x768,每个像素24位”来说尤其如此,原因有两个:每像素24位(或每个像素3字节)对齐很烦人,因此许多视频卡不支持任何分辨率(并且支持每个像素32位,而是保留8位填充);1024x768分辨率没有很好的标准化(不匹配现代屏幕的纵横比,也没有在"VESA安全模式“标准中定义为所有监视器都支持的模式),因此任何像素格式都可能不支持1024 x 768分辨率。
  • 您还应该检查像素内字段的布局(使用RedMaskSizeRedFieldPosition、..=‘fields 2’> LinRedMaskSizeLinRedFieldPosition,.VBE 3.0的字段)。不能保证(例如)每像素24位是“红色、绿色、蓝色”,而不是“蓝色、绿色、红色”或其他东西。
  • 理想情况下,您的代码将搜索您的代码和视频卡支持的最佳视频模式(以及监视器,但这非常困难);并适应任何分辨率(这很容易--只需使用horizontal_resolutionvertical_resolution无处不在的变量),并支持多种不同的像素格式。支持多种不同像素格式的最简单方法是在RAM中拥有一个缓冲区,它总是使用特定的格式(例如,每个像素32位),然后有多个不同的函数将像素数据从特定格式转换为视频模式想要的任何格式,同时将结果复制到视频卡的帧缓冲区中。
  • 理想情况下(为了性能),您的screen_set()函数不会被任何东西使用。对于行、矩形、字符.,最好只计算左上角像素的地址一次,然后调整地址(例如,用address += bytes_per_pixel;查找右边的下一个像素,或者用address += bytes_per_scan_line;查找下一个像素)。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73095164

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档