From 03a867f43e69ccdb2c765495618a232ecbd0efd0 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Mon, 10 Aug 2026 05:13:53 +0200 Subject: [PATCH] boot: enumerate VBE modes, so a resolution can be typed Option 4 asks for a width and a height and then goes looking for a mode that matches, instead of picking from three hardcoded numbers. AX=4F00 returns a controller info block whose VideoModePtr is a far pointer to a 0xFFFF-terminated list of mode numbers; each is asked what it actually is with AX=4F01, and the first that reports the requested size at 32bpp wins. That is the fix for the table, not an addition to it -- the table was wrong the first time it was written (0x146 was "1600x1200" and is 320x200x8 here), and a table can only ever describe machines somebody happened to test. 800x600 stays the floor: the console, the splash and Schcom all assume at least that, and smaller would be a different set of assumptions rather than a smaller number. Below it, or a size the BIOS cannot do, says so and keeps 800x600 rather than failing to boot. Scratch buffers at 0x31000 and 0x31200, clear of the mode info block at 0x30000 and everything else in that page -- the flags at +0x100, the e820 map at +0x208 and the RSDP slot at +0x600. Verified over serial: 1280x800 comes up at 1280x800, which is in no table anywhere and is the point; 640x480 is refused as below the minimum; 3000x2000 reports no such mode. Presets 1-3 and the no-key timeout are unchanged. bootcheck 108 [ok]. stage2 is 20932 of 24576 bytes. Co-Authored-By: Claude Opus 5 --- boot/bios/stage2.S | 180 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/boot/bios/stage2.S b/boot/bios/stage2.S index cdeae7a..3f98b66 100644 --- a/boot/bios/stage2.S +++ b/boot/bios/stage2.S @@ -202,6 +202,133 @@ anykey: call serial_getc ret + +# Echo AL to both the screen and the serial line. Clobbers nothing. +echo_char: + pushw %ax + pushw %bx + movb $0x0E, %ah + movw $0x0007, %bx + int $0x10 + popw %bx + popw %ax + call serial_putc + ret + +# Read a decimal number, echoing as it goes. Returns it in AX. +# Ends on Enter; anything that is not a digit is ignored rather than aborting, +# so a stray keystroke does not silently truncate the number. +read_num: + pushw %bx + pushw %cx + pushw %dx + xorw %bx, %bx +.Lrn_loop: + call anykey + jnc .Lrn_loop + cmpb $0x0D, %al # Enter (both the keyboard and a serial line) + je .Lrn_done + cmpb $0x0A, %al + je .Lrn_done + cmpb $'0', %al + jb .Lrn_loop + cmpb $'9', %al + ja .Lrn_loop + call echo_char + subb $'0', %al + movzbw %al, %cx + movw %bx, %ax + movw $10, %dx + mulw %dx # DX:AX = AX * 10 + addw %cx, %ax + movw %ax, %bx + jmp .Lrn_loop +.Lrn_done: + movw %bx, %ax + popw %dx + popw %cx + popw %bx + ret + +# Find a VBE mode matching reqw x reqh at 32bpp. Returns CX = mode, or 0. +# +# This asks the BIOS for its own list rather than carrying a table of numbers. +# The table was wrong the first time it was written -- 0x146 was "1600x1200" and +# is 320x200x8 on this VBE -- and a table can only ever describe the machines +# somebody happened to test. AX=4F00 returns a controller info block whose +# VideoModePtr is a far pointer to a 0xFFFF-terminated list of mode numbers; +# each one is then asked what it actually is. +# +# Scratch buffers live at 0x31000 and 0x31200, clear of the mode info block at +# 0x30000, the flags at +0x100, the e820 map at +0x208 and the RSDP slot at +# +0x600. +find_mode: + pushw %ax + pushw %bx + pushw %dx + pushw %si + pushw %di + + movw $0x3100, %ax + movw %ax, %es + xorw %di, %di + movw $0x4F00, %ax + int $0x10 + cmpw $0x004F, %ax + jne .Lfm_none + + # VideoModePtr: offset at +14, segment at +16. + movw %es:14(%di), %si + movw %es:16(%di), %ax + movw %ax, %fs + +.Lfm_next: + movw %fs:(%si), %cx + addw $2, %si + cmpw $0xFFFF, %cx + je .Lfm_none + + # CX and SI have to survive the BIOS call; VBE only promises AX back. + pushw %cx + pushw %si + movw $0x3120, %ax + movw %ax, %es + xorw %di, %di + movw $0x4F01, %ax + int $0x10 + popw %si + popw %cx + cmpw $0x004F, %ax + jne .Lfm_next + + movw $0x3120, %ax + movw %ax, %es + xorw %di, %di + cmpb $32, %es:25(%di) # bits per pixel + jne .Lfm_next + movw %es:18(%di), %ax # width + cmpw reqw, %ax + jne .Lfm_next + movw %es:20(%di), %ax # height + cmpw reqh, %ax + jne .Lfm_next + jmp .Lfm_done # CX is the answer + +.Lfm_none: + xorw %cx, %cx +.Lfm_done: + popw %di + popw %si + popw %dx + popw %bx + popw %ax + ret + +reqw: + .word 0 +reqh: + .word 0 + # Offer a resolution choice, briefly. # # In real mode because this is the only place it can be: setting a VBE mode is @@ -254,6 +381,8 @@ res_menu: .Lrpick: call anykey jnc .Lrpick + cmpb $'4', %al + je .Lrcustom cmpb $'1', %al jb .Lrpick cmpb $'3', %al @@ -278,6 +407,47 @@ res_menu: call print16 ret +# Type a size. Enumerated rather than looked up, so anything the BIOS can +# actually do is available -- not just the three that happen to be listed. +.Lrcustom: + call echo_char + movw $reswidth, %si + call print16 + call read_num + movw %ax, reqw + movw $resheight, %si + call print16 + call read_num + movw %ax, reqh + + movw $resok, %si + call print16 + + # 800x600 is the floor the system is built around: the console, the splash + # and Schcom all assume at least that much, and a smaller mode would be a + # different set of assumptions rather than a smaller number. + cmpw $800, reqw + jb .Lrcsmall + cmpw $600, reqh + jb .Lrcsmall + + call find_mode + testw %cx, %cx + jz .Lrcnone + ret + +.Lrcsmall: + movw $resmin, %si + call print16 + movw $0x0143, %cx + ret + +.Lrcnone: + movw $resnone, %si + call print16 + movw $0x0143, %cx + 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. @@ -301,9 +471,17 @@ resprompt: restimeout: .asciz "keeping 800x600\r\n" resmenu: - .asciz "\r\n 1) 800x600 2) 1024x768 3) 1280x1024\r\n choice: " + .asciz "\r\n 1) 800x600 2) 1024x768 3) 1280x1024 4) type a size\r\n choice: " resok: .asciz "\r\n" +reswidth: + .asciz " width: " +resheight: + .asciz "\r\n height: " +resmin: + .asciz " below the 800x600 minimum, keeping 800x600\r\n" +resnone: + .asciz " no such 32bpp mode, keeping 800x600\r\n" bootmsg: .asciz "SchOS booting...\r\n"