--- /dev/null
+vesa:
+ ; print message
+ mov ebx, .msg
+ call print_str
+
+ ; get vesa bios info
+ mov eax, dword[.vbe2]
+ mov dword[VESAINFO], eax ; move "VBE2" to start of vesainfo struct
+ mov ax, 0x4F00 ; get VESA BIOS information
+ mov di, VESAINFO ; struct buffer
+ int 0x10
+
+ cmp ax, 0x004F ; check ax for correct magic number
+ jne .fail_getinfo
+
+ mov eax, dword[.vesa]
+ cmp dword[VESAINFO], eax ; check if "VESA" is at start of stuct
+ jne .fail_getinfo
+
+ ; print select message
+ mov ebx, .select_msg
+ call print_str
+
+ ; get segment:offset pointer to video modes into gs:ebx
+ movzx ebx, word[VESAINFO+14]
+ mov ax, word[VESAINFO+16]
+ mov gs, ax
+
+ ; convert modes to own structure
+
+ xor esi, esi ; number of avail modes
+
+.mode_loop:
+ ; get mode info
+ mov cx, [gs:ebx] ; video mode into cx
+ cmp cx, 0xFFFF ; 0xFFFF is terminator, no suitable mode has been found
+ je .mode_done
+ mov ax, 0x4F01 ; get VESA mode information
+ mov di, VESAMODE ; vesa mode info struct buffer
+ int 0x10
+
+ cmp ax, 0x004F ; check ax for correct magic number
+ jne .fail_modeinfo
+
+ mov al, byte[VESAMODE] ; get attributes
+ and al, 0b10000000 ; extract bit 7, indicates linear framebuffer support
+ jz .mode_next
+
+ mov al, byte[VESAMODE+25] ; get bpp (bits per pixel)
+ cmp al, 32
+ jne .mode_next
+
+ push ebx ; print_dec and print_str modify ebx
+
+ mov eax, esi
+ mov ebx, 12
+ mul ebx
+ mov edi, eax
+ add edi, OWNMODE
+
+ mov [edi+10], cx ; copy mode
+
+ ; print selector
+ mov al, '['
+ call print_chr
+
+ mov eax, esi
+ add eax, 'a'
+ call print_chr
+
+ mov al, ']'
+ call print_chr
+
+ mov al, ' '
+ call print_chr
+
+ mov ax, [VESAMODE+16] ; copy pitch
+ mov [edi+0], ax
+
+ movzx eax, word[VESAMODE+18] ; copy width
+ mov [edi+2], ax
+ call print_dec
+
+ mov al, 'x'
+ call print_chr
+
+ movzx eax, word[VESAMODE+20] ; copy height
+ mov [edi+4], ax
+ call print_dec
+ call newline
+
+ mov eax, [VESAMODE+40] ; copy framebuffer
+ mov [edi+6], eax
+
+ pop ebx
+
+ inc esi
+ cmp esi, 'z'-'a' ; only print up to z
+ jg .mode_done
+
+.mode_next:
+ add ebx, 2 ; increase mode pointer
+ jmp .mode_loop ; loop
+
+.mode_done:
+ cmp esi, 0
+ je .fail_nomode
+
+.input:
+ mov ebx, .select_prompt
+ call print_str
+
+ mov ah, 0x00 ; get keypress, blocking
+ int 0x16
+
+ call print_chr ; echo user input
+
+ movzx edi, al ; backup al
+ call newline
+
+ sub edi, 'a'
+ cmp edi, esi
+ jb .valid ; check validity
+
+ mov ebx, .invalid
+ call print_str
+
+ jmp .input
+
+.valid:
+ ; convert selected number to address
+ mov eax, edi
+ mov ebx, 12
+ mul ebx
+ add eax, OWNMODE
+
+ ; copy to final gfx info location
+ mov ebx, [eax+0]
+ mov [GFXINFO+0], ebx
+
+ mov ebx, [eax+4]
+ mov [GFXINFO+4], ebx
+
+ mov bx, [eax+8]
+ mov [GFXINFO+8], bx
+
+ ;mov edi, eax
+ ;mov eax, [edi+6]
+ ;call print_hex
+ ;call newline
+ ;mov eax, edi
+ ;jmp $
+
+ ; set mode
+ mov bx, [eax+10] ; video mode in bx (first 13 bits)
+ or bx, 1 << 14 ; set bit 14: enable linear frame buffer
+ and bx, 0b0111111111111111 ; clear deprecated bit 15
+ mov ax, 0x4F02 ; set VBE mode
+ int 0x10
+
+ ret
+
+.msg: db "setting up vesa", 10, 13, 0
+.vbe2: db "VBE2"
+.vesa: db "VESA"
+.select_msg: db "avaliable video modes:", 10, 13, 0
+.select_prompt: db "select video mode: ", 0
+.invalid: db "invalid input", 10, 13, 0
+
+.fail_getinfo:
+ mov ebx, .fail_getinfo_msg
+ jmp .fail
+
+.fail_modeinfo:
+ mov ebx, .fail_modeinfo_msg
+ jmp .fail
+
+.fail_nomode:
+ mov ebx, .fail_nomode_msg
+ jmp .fail
+
+.fail_getinfo_msg: db "failed getting vesa bios info", 10, 13, 0
+.fail_modeinfo_msg: db "failed getting video mode info", 10, 13, 0
+.fail_nomode_msg: db "no suitable video modes available", 10, 13, 0
+
+.fail:
+ call print_str
+ jmp $