2

我是一个尝试在 VBE 中实现简单图形的初学者。我已经编写了以下汇编代码来启动,进入 32 位保护模式,并进入 VBE 模式 0x4117。(有人告诉我,[mode] OR 0x4000 的输出会产生一个带有线性帧缓冲区的模式版本,所以我假设 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

上面调用了“kernel_entry.asm”,如下图:

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

“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);
        }
    }
}

如果我正确加载了线性帧缓冲区,我希望看到渐变。相反,我看到了这个:

这个

一系列盒子,每个盒子里都有一个渐变,它突然切断了。这似乎表明我正在使用存储帧缓冲区而不是线性缓冲区的模式进行编写;梯度走出一个缓冲区,持续数百次迭代,最终到达下一个缓冲区的开始,导致突然移位和“盒子”效应。

我的解释正确吗?我是否正确加载了线性帧缓冲区,如果没有,我该怎么做?

编辑:我已尝试更改unsigned long offset = i * gVbe->y_resolution_ + j;unsigned long offset = i * gVbe->bytes_per_scan_line_ + j,如下面的小丑建议。这产生了以下图像。它同样是四四方方的。在此处输入图像描述

4

1 回答 1

0

我是否正确加载了线性帧缓冲区,如果没有,我该怎么做?

在您的代码中,您只是假设线性帧缓冲区模式可用。您应该检查 ModeInfoBlock.ModeAttributes 位 7 以确定。该位需要打开:

    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

由于此视频模式每个像素使用 2 个字节,因此计算显存中的偏移量需要将 x 坐标翻倍:

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

提示:为什么不使用xy而不是ji;为了清楚...

于 2022-01-15T21:24:38.453 回答