Assembly - How to set graphics mode in UEFI (No VG

2019-06-04 00:53发布

Tools I'm using: nasm, qemu-system-x86_64.

Operating System I'm on: Windows 10.

So I checked out the "Real Mode OS Warning" at https://wiki.osdev.org/Real_Mode_OS_Warning

What the article seems to imply is that everything can be done without using BIOS interrupts whatsoever. I know how to load Long Mode, and so I've done this, but now I am stuck because BIOS interrupts were all I knew until now. I want to do something like set the graphics mode to full-memory-access-mode (might sound familiar if you've seen int 10h / AX = 4F02h / BX = 81FFh), but since I don't want to use something that's deprecated (BIOS), I have been having trouble searching the web for how to set the graphics mode and then access individual pixels in Long Mode only.

Hopefully it turns out to be possible to answer this question on StackOverflow. I have much faith that "it's too complicated" won't show up as an answer, especially since I was just told by OSDev NOT to use deprecated things. Telling someone it's too difficult assumes what they know and what they're able to learn without even knowing who they are. I just need a starting point to find out how to do this.

To give clarification, things that didn't work for me:

Enter graphics mode without interrupts in assembly

This didn't work for me because the answer gives a link to VGA, which I don't want.

Graphics mode in assembly 8086

This didn't work for me because the question does not ask about Long Mode, but rather about VGA graphics in Real Mode.

How to write data to a graphics card without using BIOS?

This didn't work for me because the answer was essentially "it's too complicated, use the deprecated stuff", which is the opposite of what i'm trying to do and quite contradictory to what I was just told on OSDev.

Drawing directly by graphics card on Intel 8086

This didn't work for me because the answer has nothing to do with setting the graphics mode.

A few x86 Assembly language questions

This didn't work for me because the answers do not say how to set the graphics mode in UEFI. They only talk about deprecated things.

1条回答
爷、活的狠高调
2楼-- · 2019-06-04 01:49

Here is sample code that uses UEFI to get and print the available graphics modes on the first graphics device and optionally sets the mode.

I used this reference: http://wiki.phoenix.com/wiki/index.php/EFI_GRAPHICS_OUTPUT_PROTOCOL.

Notes on the implementation:

  • It calls LocateProtocol to obtain a Graphics Output Protocol. I tried using LocateHandle to obtain all the handles that support Graphics Output Protocol. It returned two handles, but OpenProtocol failed. I haven't had a chance to debug the version with LocateHandle. This version using LocateProtocol works.
  • It prints the number of available modes, the current mode, and the characteristics of each mode.
  • The parameter to the function is the mode to set. If it is -1, the mode is not changed. Otherwise it must be between 0 and N - 1, where N is the number of graphics modes supported. The parameter is not checked by this function, but the SetMode function checks it.
  • It uses Sys V x86-64 function calling conventions, except for calls to UEFI functions, which use the UEFI convention.
  • It uses a function called efi_printf, which works just like printf and writes to ConOut using EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
  • It relies on the startup code storing a pointer to the EFI Boot Services Table in a global variable named efi_boot_services.
  • It is written for gas rather than nasm.

Here is sample output:

max mode: 5
mode 1: size 36, ver 0, hor res 800, ver res 600, pixel format 1
frame buffer: b1000000, frame buffer size: 1d4c00
mode 0: size 36, ver 0, hor res 640, ver res 480, pixel format 1
mode 1: size 36, ver 0, hor res 800, ver res 600, pixel format 1
mode 2: size 36, ver 0, hor res 1024, ver res 768, pixel format 1
mode 3: size 36, ver 0, hor res 1280, ver res 1024, pixel format 1
mode 4: size 36, ver 0, hor res 1600, ver res 1200, pixel format 1

I assume you are familiar with UEFI, so I haven't explained how everything works, so let me know if you need more explanation.

            .intel_syntax noprefix

            .section .text
            .align  16
            .globl  gfxmode
    gfxmode:
            push    rbx
            push    rbp
            push    r14
            push    r15
            sub     rsp, 0x38

            mov     ebp, edi                // desired mode

            lea     rcx, EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID[rip]
            xor     edx, edx                // arg 2: unused
            lea     r8, 0x20[rsp]           // arg 3: address of protocol
            mov     rax, efi_boot_services[rip]
            call    0x140[rax]              // locate protocol
            test    rax, rax
            js      2f

            mov     r15, 0x20[rsp]          // graphics output protocol
            mov     r14, 0x18[r15]          // mode

            lea     rdi, trace1[rip]
            mov     esi, [r14]              // max mode
            call    efi_printf

            mov     rdi, 8[r14]             // current mode info
            mov     esi, 4[r14]             // current mode number
            mov     edx, 16[r14]            // current mode info size
            call    print_mode

            lea     rdi, trace3[rip]
            mov     rsi, 24[r14]            // frame buffer addr
            mov     rdx, 32[r14]            // frame buffer size
            call    efi_printf

            xor     ebx, ebx
    1:
            mov     rcx, r15                // arg 1: graphics output protocol
            mov     edx, ebx                // arg 2: mode number
            lea     r8, 0x30[rsp]           // arg 3: &info size
            lea     r9, 0x28[rsp]           // arg 4: &info
            call    0x00[rcx]               // query mode
            test    rax, rax
            js      2f

            mov     rdi, 0x28[rsp]          // mode info
            mov     esi, ebx                // mode number
            mov     edx, 0x30[rsp]          // mode info size
            call    print_mode

            mov     rax, efi_boot_services[rip]
            mov     rcx, 0x28[rsp]          // mode info
            call    0x48[rax]               // free pool

            inc     ebx
            cmp     ebx, [r14]              // max mode
            jb      1b

            xor     eax, eax
            test    ebp, ebp                // new mode
            js      2f
            mov     rcx, r15                // arg 1: graphics output protocol
            mov     edx, ebp                // arg 2: mode number
            call    0x08[rcx]               // set mode

    2:
            add     rsp, 0x38
            pop     r15
            pop     r14
            pop     rbp
            pop     rbx
            ret

            .align  16
    print_mode:
            // rdi: mode info
            // esi: mode number
            // edx: mode size
            mov     ecx, [rdi]              // mode version
            mov     r8d, 4[rdi]             // hor res
            mov     r9d, 8[rdi]             // ver res
            mov     eax, 12[rdi]            // pixel format
            push    rax
            lea     rdi, trace2[rip]
            call    efi_printf
            add     rsp, 8
            ret

    trace1: .asciz  "max mode: %d\n"
    trace2: .asciz  "mode %d: size %d, ver %d, hor res %d, ver res %d, pixel format %d\n"
    trace3: .asciz  "frame buffer: %p, frame buffer size: %llx\n"

            .align  16
    EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID:
            .byte   0xde,0xa9,0x42,0x90,0xdc,0x23,0x38,0x4a
            .byte   0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a
查看更多
登录 后发表回答