Bare metal VGA: optionally dump VESA video mode list at startup

If the user holds down one of the Shift keys at boot time,
the program will dump a list of all usable VESA VBE video
modes on the VGA console.
This commit is contained in:
tkchia 2022-09-22 22:02:53 +00:00
parent 0d594ecd85
commit 8ed7d4df55
2 changed files with 333 additions and 0 deletions

330
libc/vga/rlinit-vesa.S Normal file
View file

@ -0,0 +1,330 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "ape/relocations.h"
#include "libc/macros.internal.h"
#include "libc/runtime/mman.internal.h"
#include "libc/vga/vga.internal.h"
// Routine to activate additional VESA functionality if the user holds
// down a magic key, when booting from bare metal. Currently this just
// dumps a list of all usable VESA VBE video modes on the console.
//
// TODO: allow user to select a video mode and switch to it
// TODO: implement character drawing in graphics modes
//
// @return CF = 0 if we decided to set a new video mode, CF = 1 otherwise
.real
.code16
_rlinit_vesa:
push %es
pushw $0
testb $0b00000011,0x0417 # read keyboard shift state (as
jnz .doit # given by BIOS's IRQ 1); if Shift
# key pressed, activate code below
mov $0x8300,%ax # wait for the magic key for a short
mov $(250000>>16),%cx # period of time...
mov $(250000&0xffff),%dx
push %ss
pop %es
mov %sp,%bx
int $0x15
jc .done
.wait: pause
testb $0b00000011,0x0417
jnz .doit2
cmpb $0,%es:(%bx)
jz .wait
.done: pop %ax
pop %es
stc
ret
.doit2: mov $0x8301,%ax # we got the magic key; cancel the
int $0x15 # wait timer
.doit: call .dump_vesa_modes # we got the magic key; do stuff
push %si
mov $REAL(str.cont),%si # say we are continuing
call .puts
pop %si
mov $0x86,%ah # do a(nother) short few-second wait
mov $(2000000>>16),%cx
mov $(2000000&0xffff),%dx
int $0x15
jmp .done
.endfn _rlinit_vesa,globl,hidden
// Dump a list of all the VESA VBE video modes that are usable on the
// target machine.
.dump_vesa_modes:
push %ds
push %es
push %si
push %di
push %ss # allocate 0x200 bytes on stack
pop %es # for general VESA information
sub $0x200,%sp
mov $0x200/2,%cx
mov %sp,%di
cld
xor %ax,%ax
rep stosw
mov $0x4f00,%ax # get general VESA information
mov %sp,%di
movl $/*"VBE2"*/0x32454256,%es:(%di)
int $0x10
cmp $0x004f,%ax # if this fails, complain
jnz .fail
mov $REAL(str.mode_list_start),%si
call .puts # otherwise start iterating through
lds %es:0xe(%di),%si # the returned video mode list
cld
.iter1: lodsw
inc %ax
jz .done2
dec %ax
call .munge # process mode number
jc .iter1
.iter2: lodsw
inc %ax
jz .nl
dec %ax
call .munge # process another mode number
jc .iter2
.iter3: lodsw
inc %ax
jz .nl
dec %ax
call .munge # process another mode number
jc .iter3
call .putnl
jmp .iter1 # ...and so on
.nl: call .putnl
.done2: add $0x200,%sp # OK, we are finally done
pop %di
pop %si
pop %es
pop %ds
ret
.fail: mov $REAL(str.no_vesa),%si
call .puts
jmp .done2
.endfn .dump_vesa_modes
// Process one video mode number, which should be in ax. Assume that
// es = ss.
//
// @return CF = 0 if video mode is usable, CF = 1 otherwise
.munge: push %ax
push %cx
push %si
push %di
xchg %ax,%si # remember mode number
sub $0x100,%sp # allocate 0x100 stack bytes for
mov $0x100/2,%cx # information on this mode; clear
mov %sp,%di # the bytes
cld
xor %ax,%ax
rep stosw
mov %si,%cx # get information on one mode
mov $0x4f01,%ax
mov %sp,%di
int $0x10
cmp $0x004f,%ax # if error, skip mode
jnz .fail3
mov %es:(%di),%al
mov %al,%cl
and $0b00001011,%al # also skip if mode is unusable, or
cmp $0b00001011,%al # extra information unavailable, or
jnz .fail3 # is monochrome
call .putsp # echo mode information
call .putsp
test $0b00010000,%cl # first, echo mode attributes
setnz %al
imul $'g'-'t',%ax,%ax # - 'g': graphics; 't': text mode
add $'t',%al
shl %cl # - turn 'g' into 'G' if linear
sbb %cl,%cl # frame buffer supported
and $'G'-'g',%cl
add %cl,%al
call .putc
call .putsp
xchg %ax,%si # then output
call .putx # - mode number
mov %es:0x12(%di),%ax # - mode width
call .putd
mov %es:0x14(%di),%ax # - mode height
call .putd
mov %es:0x19(%di),%al # - bits per pixel
call .putdb
clc
.done3: lea 0x100(%esp),%sp
pop %di
pop %si
pop %cx
pop %ax
ret
.fail3: stc
jmp .done3
.endfn .munge
// Output a string via BIOS.
.puts: cld
push %si
0: lodsb
test %al,%al
jz 1f
call .putc
jmp 0b
1: pop %si
ret
.endfn .puts
// Output a 16-bit number in decimal via BIOS.
.putd: push %ax
push %cx
push %dx
push %si
push %ds
push %ss
pop %ds
sub $8,%sp
lea 4(%esp),%si
mov $10,%cx
xor %dx,%dx
div %cx
add $'0'|' '<<8,%dx
mov %dx,(%si)
movb $0,2(%si)
1: mov $' ',%dl
test %ax,%ax
jz 2f
xor %dx,%dx
div %cx
add $'0',%dl
2: dec %si
mov %dl,(%si)
cmp %sp,%si
jnz 1b
call .puts
add $8,%sp
pop %ds
pop %si
pop %dx
pop %cx
pop %ax
ret
.endfn .putd
// Output an 8-bit number in decimal via BIOS.
.putdb: push %ax
mov %al,%ah
cmp $100,%al
jnb 3f
mov $' ',%al
1: call .putc
mov %ah,%al
aam
testb %ah,%ah
jz 5f
add $'0'|'0'<<8,%ax
2: xchg %al,%ah
call .putc
mov %ah,%al
call .putc
pop %ax
ret
3: cmp $200,%al
jnb 4f
sub $100,%ah
mov $'1',%al
jmp 1b
4: sub $200,%ah
mov $'2',%al
jmp 1b
5: add $'0'|' '<<8,%ax
jmp 2b
// Output a number in hexadecimal via BIOS.
.putx: push %ax
push %bx
push %cx
xchg %ax,%bx
mov $'0',%al
call .putc
mov $'x',%al
call .putc
mov $4,%cx
0: rol $4,%bx
mov %bl,%al
and $0b00001111,%al
add $'0',%al
cmp $'9',%al
jna 1f
add $'a'-'9'-1,%al
1: call .putc
loop 0b
pop %cx
pop %bx
pop %ax
.endfn .putx
// fall through
// Output a character via BIOS.
.putsp: mov $' ',%al
.endfn .putsp
// fall through
.putc: push %ax
push %bx
mov $7,%bx
mov $0x0e,%ah
int $0x10
pop %bx
pop %ax
ret
.endfn .putc
.putnl:
mov $'\r',%al
call .putc
mov $'\n',%al
jmp .putc
.endfn .putnl
.previous
str.no_vesa:
.asciz "info: no VESA\r\n"
.endobj str.no_vesa
str.mode_list_start:
.ascii "info: VESA video modes:\r\n"
.ascii " mode# wid ht bpp"
.ascii " mode# wid ht bpp"
.asciz " mode# wid ht bpp\r\n"
.endobj str.mode_list_start
str.cont:
.asciz "info: continuing\r\n"
.endobj str.cont

View file

@ -43,6 +43,8 @@
// @see sys_writev_vga (libc/vga/writev-vga.c)
.section .sort.text.real.init.2,"ax",@progbits
.code16
call _rlinit_vesa
jnc 9f
mov $0x4f03,%ax # get current video mode via VESA
int $0x10
cmp $0x004f,%ax # is VESA a thing here?
@ -76,6 +78,7 @@
xor %bx,%bx
#endif
int $0x10
9:
.previous
.code64
.section .rodata,"a",@progbits