boot: pick a resolution with R, instead of hardcoding 800x600
The VBE mode was the literal 0x143 in stage2.S, so 800x600 was the only resolution the system could ever have. Real mode is the only place this can be chosen: setting a mode is INT 10h, stage2main.c already runs in protected mode, and the kernel has no modeset path -- so whatever is picked here is what the framebuffer, the console and Schcom all get. Press R at the prompt within two seconds for 800x600, 1024x768 or 1280x1024. It boots on its own otherwise: a loader that waits for a keypress is a machine that does not come back up after a power cut. The depth is checked before the mode is set, and that is not belt and braces. 0x146 was in the table as 1600x1200x32 and this VBE returns 320x200x8 for it -- a mode the BIOS accepts, so an error return never happens. It boots and then everything above renders garbage, because the console, the fbdev ioctls and the compositor all assume 32bpp. Only numbers verified to come back as the advertised size at 32bpp are listed now, and anything else falls back to 800x600. Verified by driving the monitor's sendkey: 1, 2 and 3 come up at 800x600, 1024x768 and 1280x1024 respectively, all reaching schinit, and no key at all still gives 800x600. bootcheck 108 [ok], schcom-test passes. Typing an arbitrary size is the obvious next step and deliberately not guessed at here: it wants enumerating the BIOS's own mode list (INT 10h AX=4F00) and matching on width and height, which would replace the magic numbers rather than add more of them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
181c420f21
commit
8b52782b75
1 changed files with 123 additions and 2 deletions
|
|
@ -21,16 +21,46 @@ stage2start:
|
|||
# clobber stage2's own tail end. 0x30000 is clear of stage2 (ends well
|
||||
# under 0xA000), the multiboot kernel-search buffer (0x10000-0x14000),
|
||||
# and the kernel's own load address (0x100000).
|
||||
# Which mode. 0x143 is 800x600x32 and stays the default: it is the smallest
|
||||
# the system is meant to run at, and the one every path has been tested
|
||||
# against. Press R at the prompt to pick another.
|
||||
movw $0x0143, %cx
|
||||
call res_menu # may return a different mode in CX
|
||||
|
||||
movw $0x3000, %ax
|
||||
movw %ax, %es
|
||||
xorw %di, %di # ES:DI = 3000:0000 = flat 0x30000
|
||||
movw $0x0143, %cx
|
||||
movw $0x4F01, %ax
|
||||
int $0x10
|
||||
|
||||
# Two ways this goes wrong, and only one of them is an error return.
|
||||
#
|
||||
# A mode number the BIOS does not know fails outright, which is easy. But a
|
||||
# number it *does* know may be nothing like what was asked for: 0x146 was
|
||||
# written here as 1600x1200x32 and this VBE returns 320x200x8 for it. That
|
||||
# boots, and then the framebuffer console and Schcom both render garbage,
|
||||
# because everything above assumes 32bpp. So the depth reported in the mode
|
||||
# info block is checked before the mode is set, not after.
|
||||
cmpw $0x004F, %ax
|
||||
jne res_fallback
|
||||
cmpb $32, %es:25(%di) # mode info +25: bits per pixel
|
||||
je res_ok
|
||||
|
||||
res_fallback:
|
||||
cmpw $0x0143, %cx
|
||||
je e820 # the fallback itself failed; give up on VBE
|
||||
movw $0x0143, %cx
|
||||
xorw %di, %di
|
||||
movw $0x4F01, %ax
|
||||
int $0x10
|
||||
cmpw $0x004F, %ax
|
||||
jne e820
|
||||
movw $0x4143, %bx
|
||||
cmpb $32, %es:25(%di)
|
||||
jne e820
|
||||
|
||||
res_ok:
|
||||
movw %cx, %bx
|
||||
orw $0x4000, %bx # bit 14: linear framebuffer
|
||||
movw $0x4F02, %ax
|
||||
int $0x10
|
||||
|
||||
|
|
@ -99,6 +129,97 @@ print16:
|
|||
.Lp16done:
|
||||
ret
|
||||
|
||||
# Offer a resolution choice, briefly.
|
||||
#
|
||||
# In real mode because this is the only place it can be: setting a VBE mode is
|
||||
# INT 10h, and by the time stage2main.c runs we are in protected mode with no
|
||||
# BIOS left to call. The kernel cannot change it either -- there is no modeset
|
||||
# path, so whatever is chosen here is what the framebuffer, the console and the
|
||||
# compositor all get.
|
||||
#
|
||||
# Two seconds, then it boots on its own. A loader that waits for a keypress is
|
||||
# a machine that does not come back up after a power cut.
|
||||
#
|
||||
# In: CX = default mode. Out: CX = chosen mode. Clobbers AX, BX, DX, SI.
|
||||
res_menu:
|
||||
pushw %cx
|
||||
movw $resprompt, %si
|
||||
call print16
|
||||
|
||||
# Poll for ~2s. INT 1Ah tick count runs at 18.2 Hz, so 36 ticks is two
|
||||
# seconds and needs no calibration against CPU speed -- a delay loop would be
|
||||
# a different length on every machine.
|
||||
movb $0x00, %ah
|
||||
int $0x1A # CX:DX = ticks since midnight
|
||||
movw %dx, %bx # BX = starting tick, low word
|
||||
.Lrwait:
|
||||
movb $0x01, %ah
|
||||
int $0x16 # ZF=0 if a key is waiting
|
||||
jnz .Lrkey
|
||||
movb $0x00, %ah
|
||||
int $0x1A
|
||||
subw %bx, %dx
|
||||
cmpw $36, %dx
|
||||
jb .Lrwait
|
||||
popw %cx # timed out: keep the default
|
||||
ret
|
||||
|
||||
.Lrkey:
|
||||
xorw %ax, %ax
|
||||
int $0x16 # consume it
|
||||
orb $0x20, %al # crude tolower: 'R' and 'r' both land on 'r'
|
||||
cmpb $'r', %al
|
||||
je .Lrshow
|
||||
popw %cx # some other key: boot with the default
|
||||
ret
|
||||
|
||||
.Lrshow:
|
||||
popw %cx # default is no longer needed; a pick replaces it
|
||||
movw $resmenu, %si
|
||||
call print16
|
||||
.Lrpick:
|
||||
xorw %ax, %ax
|
||||
int $0x16
|
||||
cmpb $'1', %al
|
||||
jb .Lrpick
|
||||
cmpb $'3', %al
|
||||
ja .Lrpick
|
||||
|
||||
# Index the table: (key - '1') * 2, because each entry is one 16-bit mode.
|
||||
subb $'1', %al
|
||||
movzbw %al, %bx
|
||||
shlw $1, %bx
|
||||
movw resmodes(%bx), %cx
|
||||
|
||||
movw $resok, %si
|
||||
call print16
|
||||
ret
|
||||
|
||||
# 32-bit colour throughout: the framebuffer console, the fbdev ioctls and
|
||||
# Schcom all assume 32bpp, and a 24bpp mode would be a different renderer
|
||||
# rather than a different number here.
|
||||
#
|
||||
# Numbers are the VESA/Bochs 0x140 block, and only ones actually verified to
|
||||
# come back as the advertised size at 32bpp are listed. 0x146 was in here as
|
||||
# 1600x1200 and returns 320x200x8, which is why the caller now checks the depth
|
||||
# rather than trusting the table.
|
||||
#
|
||||
# Adding a preset means checking it the same way -- boot it and read the
|
||||
# kernel's "VGA:" line. The real fix is to enumerate the BIOS's own mode list
|
||||
# (INT 10h AX=4F00) and match on width/height, which would also allow typing an
|
||||
# arbitrary size; that is the next step here, not a guess at more numbers.
|
||||
resmodes:
|
||||
.word 0x0143 # 800x600 verified
|
||||
.word 0x0144 # 1024x768 verified
|
||||
.word 0x0145 # 1280x1024 verified
|
||||
|
||||
resprompt:
|
||||
.asciz "Press R for resolution... "
|
||||
resmenu:
|
||||
.asciz "\r\n 1) 800x600 2) 1024x768 3) 1280x1024\r\n choice: "
|
||||
resok:
|
||||
.asciz "\r\n"
|
||||
|
||||
bootmsg:
|
||||
.asciz "SchOS booting...\r\n"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue