diff --git a/boot/bios/stage2.S b/boot/bios/stage2.S index 61df44b..118276a 100644 --- a/boot/bios/stage2.S +++ b/boot/bios/stage2.S @@ -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"