简体   繁体   English

VBE:为什么我的代码不提供线性帧缓冲区?

[英]VBE: why does my code not provide a linear frame buffer?

I am a beginner who is trying to implement simple graphics in VBE.我是一个尝试在 VBE 中实现简单图形的初学者。 I have written the following assembly code to boot, enter 32-bit protected mode, and enter VBE mode 0x4117.我已经编写了以下汇编代码来启动,进入 32 位保护模式,并进入 VBE 模式 0x4117。 (I was told that the output of [mode] OR 0x4000 would produce a version of the mode with a linear frame buffer, so I assumed that 0x0117 OR 0x4000 = 0x4117 should have a linear frame buffer. (有人告诉我,[mode] OR 0x4000 的 output 会产生一个带有线性帧缓冲区的模式版本,所以我假设 0x0117 OR 0x4000 = 0x4117 应该有一个线性帧缓冲区。

[org 0x7c00]            ; Origin is same as addr of MBR.
[bits 16]           

section code
switch:
    mov ax, 0x4f01      ; Querying VBE.
    mov cx, 0x4117      ; We want 0x117 mode graphics.
                ; i.e. 16 bits per pixel, 1024x768 res.
    mov bx, 0x0800      ; Offset for VBE info structure.
    mov es, bx
    mov di, 0x00
    int 0x10        ; Graphics interrupt.

    ; Make the switch to graphics mode.
    mov ax, 0x4f02      ; What VBA service is wanted?
                ; 0x4f02 for actual switching.
    mov bx, 0x4117      
    int 0x10

    ; Zero out registers.
    xor ax, ax
    mov ds, ax
    mov es, ax

    ; Here, we call interrupt 13H to read from hard disk.
    mov bx, 0x1000      ; Location where code is loaded from disk.
    mov ah, 0x02        ; Selects the 13H service, in this case
                ; reading sectors from drive.       
    mov al, 30      ; Num sectors to read from hard disk.
                ; We'll make this larger the bigger our OS gets.
    mov ch, 0x00        ; Where is cylinder?
    mov dh, 0x00        ; Where is head?
    mov cl, 0x02        ; Sector.
    int 0x13        ; Call interrupt corresponding to disk services.

    cli         ; Turn off interrupts.
    lgdt [gdt_descriptor]   ; Load global descriptor table.
    
    mov eax, cr0        
    or eax, 0x1
    mov cr0, eax        ; Make switch.

    jmp code_seg:protected_start

text: db "Jesus said I will rebuild this temple in three days. I could make a compiler in 3 days. - Terry A. Davis",0

[bits 32]
protected_start:
    mov ax, data_seg    ; Loads the data segment start ptr from GDT,
    mov ds, ax      ; and set data segment start in program equal.
    mov ss, ax      ; Set stack segment.
    mov es, ax      ; Set extra segment.
    mov fs, ax      ; Set fs (seg. w/ no specific use).
    mov gs, ax      ; Set gs (seg. w/ no specific use).

    mov ebp, 0x90000    ; Update stack ptr to where it's expected.
    mov esp, ebp
    
    call 0x1000     ; Call kernel code which was loaded into 0x1000.
    jmp $

gdt_begin:
gdt_null_descriptor:        ; Null descriptor. Unclear why this is needed.
    dd 0x00
    dd 0x00
gdt_code_seg:
    dw 0xffff       ; Limit of code segment
    dw 0x00         ; Base of code segment.
    db 0x00         ; Base of code segment (con.).
    db 10011010b        ; Acess byte of form:
                ;    - Present (1) - 1 for valid segment.
                ;    - Privl  (2) - 0 for kernel.
                ;    - S (1) - 1 for code/data segment. 
                ;    - Ex (1) - 1 for code segment.
                ;    - Direction bit (1) - 0 for upward growth.
                ;    - RW (1) - 1 for read/writable.
                ;    - Ac (1) - 0 to indicate not accessed yet.

    db 11001111b        ; Split byte.
                ;    - Upper 4 bits are limit (con.), another 0xf.
                ;    - Lower 4 bits are flags in order of:
                ;        - Gr - 1 for 4KiB page granularity.
                ;        - Sz - 1 for 32-bit protected mode.
                ;    - L - 0, since we aren't in long mode.
                ;        - Reserved bit.

    db 0x00         ; Base of code segment (con.).
gdt_data_seg:
    dw 0xffff       ; Limit of data segment.
    dw 0x00         ; Base of data segment.
    db 0x00         ; Base of data segment (con.).
    db 10010010b        ; Acess byte. 
                ; Same as for code segment but Ex=0 for data seg.
    db 11001111b        ; Split byte, same as for code segment.
    db 0x00         ; Base of code segment (con.).
gdt_end:
gdt_descriptor:
    dw gdt_end - gdt_begin - 1  ; GDT limit.
    dd gdt_begin            ; GDT base.

code_seg equ gdt_code_seg - gdt_begin
data_seg equ gdt_data_seg - gdt_begin

times 510 - ($ - $$) db 0x00    ; Pads file w/ 0s until it reaches 512 bytes.

db 0x55
db 0xaa

The above calls "kernel_entry.asm", shown below:上面调用了“kernel_entry.asm”,如下图:

[bits 32]
START:
    [extern start]
    call start      ; Call kernel func from C file.
    jmp $           ; Infinite loop.

"kernel_entry.asm", in turn, calls my main.c file:反过来,“kernel_entry.asm”调用我的 main.c 文件:

#define PACK_RGB565(r, g, b) \
        (((((r) >> 3) & 0x1f) << 11) | \
         ((((g) >> 2) & 0x3f) << 5) | \
         (((b) >> 3) & 0x1f))

typedef struct VbeInfoBlockStruct {
    unsigned short mode_attribute_;
    unsigned char win_a_attribute_;
    unsigned char win_b_attribute_;
    unsigned short win_granuality_;
    unsigned short win_size_;
    unsigned short win_a_segment_;
    unsigned short win_b_segment_;
    unsigned int win_func_ptr_;
    unsigned short bytes_per_scan_line_;
    unsigned short x_resolution_;
    unsigned short y_resolution_;
    unsigned char char_x_size_;
    unsigned char char_y_size_;
    unsigned char number_of_planes_;
    unsigned char bits_per_pixel_;
    unsigned char number_of_banks_;
    unsigned char memory_model_;
    unsigned char bank_size_;
    unsigned char number_of_image_pages_;
    unsigned char b_reserved_;
    unsigned char red_mask_size_;
    unsigned char red_field_position_;
    unsigned char green_mask_size_;
    unsigned char green_field_position_;
    unsigned char blue_mask_size_;
    unsigned char blue_field_position_;
    unsigned char reserved_mask_size_;
    unsigned char reserved_field_position_;
    unsigned char direct_color_info_;
    unsigned int screen_ptr_;
} VbeInfoBlock;

// VBE Info block will be located at this address at boot time.
#define VBE_INFO_ADDR 0x8000

int start()
{
    VbeInfoBlock *gVbe = (VbeInfoBlock*) VBE_INFO_ADDR;
    for(int i = 0; i < gVbe->y_resolution_; ++i) {
        for(int j = 0; j < gVbe->x_resolution_; ++j) {
            unsigned long offset = i * gVbe->y_resolution_ + j;
            *((unsigned short*) gVbe->screen_ptr_ + offset) = PACK_RGB565(0,i,j);
        }
    }
}

If I had correctly loaded a linear frame buffer, I would expect to see a gradation.如果我正确加载了线性帧缓冲区,我希望看到渐变。 Instead, I see this:相反,我看到了这个:

这个

A series of boxes, each containing a gradation within it that it abruptly cut off.一系列盒子,每个盒子里都有一个渐变,它突然切断了。 This seems to indicate that I'm writing in a mode with banked frame buffers instead of a linear one;这似乎表明我正在使用存储帧缓冲区而不是线性缓冲区的模式进行编写; the gradient goes out one buffer, continued for several hundred iterations, and eventually reaches the start of the next, causing the abrupt shift and the "boxes" effect.梯度走出一个缓冲区,持续数百次迭代,最终到达下一个缓冲区的开始,导致突然移位和“盒子”效应。

Is my interpretation correct?我的解释正确吗? Have I correctly loaded a linear frame buffer, and, if not, how could I do so?我是否正确加载了线性帧缓冲区,如果没有,我该怎么做?

EDIT: I have tried changing unsigned long offset = i * gVbe->y_resolution_ + j;编辑:我试过改变unsigned long offset = i * gVbe->y_resolution_ + j; to unsigned long offset = i * gVbe->bytes_per_scan_line_ + j , as jester suggested below.unsigned long offset = i * gVbe->bytes_per_scan_line_ + j ,如下面的小丑建议。 This produced the following image.这产生了以下图像。 It is similarly boxy.它同样是四四方方的。 在此处输入图像描述

Have I correctly loaded a linear frame buffer, and, if not, how could I do so?我是否正确加载了线性帧缓冲区,如果没有,我该怎么做?

In your code you just assume that the linear frame buffer mode is available.在您的代码中,您只是假设线性帧缓冲区模式可用。 You should inspect the ModeInfoBlock.ModeAttributes bit 7 to know for sure.您应该检查 ModeInfoBlock.ModeAttributes 位 7 以确定。 The bit needs to be ON:该位需要打开:

    mov  ax, 0x4F01      ; Querying VBE.
    mov  cx, 0x0117      ; We want 0x117 mode graphics.
                         ; i.e. 16 bits per pixel, 1024x768 res.
    mov  bx, 0x0800      ; Offset for VBE info structure.
    mov  es, bx
    mov  di, 0x00
    int  0x10
    mov  al, [es:di]
    test al, al
    jns  NoLFB           ; Bit 7 is not set!
                         ; Make the switch to graphics mode.
    mov  ax, 0x4F02      ; What VBA service is wanted?
    mov  bx, 0x4117      
    int  0x10

Since this video mode uses 2 bytes per pixel, the calculation for the offset in the video memory needs to double the x-coordinate:由于此视频模式每个像素使用 2 个字节,因此视频 memory 中的偏移量的计算需要将 x 坐标翻倍:

unsigned long offset = (i * gVbe->bytes_per_scan_line_) + (j * 2)

Tip: Why don't you use x and y instead of j and i ;提示:为什么不使用xy而不是ji for clarity...为了清楚...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM