我尝试将我的kernel.cpp
转换为使用VESA,但我得到的只是一系列奇怪的错误。在tutorial之后,我使用以下代码使VESA Graphics的引导加载程序端工作得相当顺利:
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
绘制像素时,问题出现了。(我可以补充一点吗,它的变化范围也很大)。我试着从引导程序中绘制像素(来自here的代码),并且运行得很好,所以这个问题在kernel.cpp
中。我遇到的一个常见错误是在屏幕顶部出现了一条随机的颜色,如here所示。在这里,我将保留该文件和我的GitHub(对于代码的其余部分):
kernel.cpp:
//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;
}
1条答案
按热度按时间wwwo4jvm1#
使用您已经获得的模式信息结构(在真实的模式下为
modeInfo
),您将需要3条信息:PhysBasePtr
字段。LinBytesPerScanLine
字段中获取;而对于旧版本的VBE,则从BytesPerScanLine
字段中获取。BitsPerPixel
字段确定,并且像bytesPerPixel = (modeInfo->BitsPerPixel + 7) / 8;
那样向上舍入,使得“每像素15位”是每像素2字节。有了这3个值后,计算像素物理地址的公式为:
当然,更常见的情况是将帧缓冲区Map到虚拟地址空间,但公式基本相同(只是使用虚拟地址和您选择的
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()
来处理。RedMaskSize
、RedFieldPosition
...字段;对于VBE 3.0,使用LinRedMaskSize
、LinRedFieldPosition
...字段)。无法保证(例如)每个像素的24位是“红、绿色、蓝”,而不是“蓝、绿、红”或其他。horizontal_resolution
和vertical_resolution
等变量),并支持多种不同的像素格式。支持多种不同像素格式的最简单方法是在RAM中设置一个缓冲区,该缓冲区始终使用特定格式(例如,每像素可能为32位)然后使用多个不同的函数将像素数据从特定格式转换为视频模式需要的任何格式,同时将结果复制到视频卡的帧缓冲区。screen_set()
函数不会被任何对象使用。最好先计算左上角像素的地址,然后调整地址(例如,使用address += bytes_per_pixel;
查找右侧的下一个像素,或使用address += bytes_per_scan_line;
查找下方的下一个像素)。