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"