1; 2; Copyright 2007, Dengg David, david-d@gmx.at. All rights reserved. 3; Copyright 2008, Michael Pfeiffer, laplace@users.sourceforge.net. All rights reserved. 4; Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. 5; Copyright 2011, Axel Dörfler, axeld@pinc-software.de. 6; Distributed under the terms of the MIT License. 7 8 9%assign USE_TEST_MENU 0 10 11%assign BOOT_BLOCK_START_ADDRESS 0x7c00 12 13%assign MBR_SIGNATURE 0xAA55 14 15; BIOS calls 16 17%assign BIOS_VIDEO_SERVICES 0x10 18%assign BIOS_DISK_SERVICES 0x13 19%assign BIOS_KEYBOARD_SERVICES 0x16 20%assign BIOS_REBOOT 0x19 ; dl - boot drive number 21%assign BIOS_TIME_SERVICES 0x1A 22 23; video services 24%assign SET_VIDEO_MODE 0x00 ; al - mode 25 26%assign SET_CURSOR_SHAPE 0x01 ; ch - starting scan line (5 bits) 27 ; cl - ending scan line (5 bits) 28 29%assign SET_CURSOR 0x02 ; dl - column 30 ; dh - row 31 ; bh - page 32 33 34%assign GET_CURSOR 0x03 ; bh - page 35 ; -> dl - column 36 ; dh - row 37 ; Cursor shape: 38 ; ch - starting scan line 39 ; cl - ending scan line 40 41%assign SCROLL_UP 0x06 ; al - lines (0: clear screen) 42 ; bh - attribute 43 ; ch - upper line 44 ; cl - left column 45 ; dh - lower line 46 ; dl - right column 47 48%assign WRITE_CHAR 0x09 ; al - char 49 ; bh - page 50 ; bl - attribute 51 ; cx - count 52 53;%assign WRITE_CHAR 0x0e ; al - char 54 ; bh - page 55 ; bl - foreground color (graphics mode only) 56 57; disk services 58%assign READ_DISK_SECTORS 0x02 ; dl - drive 59 ; es:bx - buffer 60 ; dh - head (0 - 15) 61 ; ch - track 7:0 (0 - 1023) 62 ; cl - track 9:8, 63 ; sector (1 - 17) 64 ; al - sector count 65 ; -> al - sectors read 66%assign READ_DRIVE_PARAMETERS 0x08 ; dl - drive 67 ; -> cl - max cylinder 9:8 68 ; - sectors per track 69 ; ch - max cylinder 7:0 70 ; dh - max head 71 ; dl - number of drives (?) 72%assign CHECK_DISK_EXTENSIONS_PRESENT 0x41 ; bx - 0x55aa 73 ; dl - drive 74 ; -> success: carry clear 75 ; ah - extension version 76 ; bx - 0xaa55 77 ; cx - support bit mask 78 ; -> error: carry set 79%assign EXTENDED_READ 0x42 ; dl - drive 80 ; ds:si - address packet 81 ; -> success: carry clear 82 ; -> error: carry set 83 84%assign FIXED_DISK_SUPPORT 0x1 ; flag indicating fixed disk 85 ; extension command subset 86 87; keyboard services 88%assign READ_CHAR 0x00 ; -> al - ASCII char 89 ; ah - scan code 90 91%assign PROBE_CHAR 0x01 ; -> zf = 0 92 ; al - ASCII char 93 ; ah - scan code 94 95%assign GET_MODIFIER_KEYS 0x02 ;-> al - modifier key bitmask 96 97; timer services 98%assign READ_CLOCK 0x00 ; -> cx - high word 99 ; dx - low word 100 ; one tick = 1/18.2s 101 102%assign TICKS_PER_SECOND 19 103 104; video modes 105%assign GRAPHIC_MODE_80x25 0x12 ; 640 x 480 graphic mode 106 107%assign TEXT_COLUMNS 80 ; Number of columns 108%assign TEXT_ROWS 25 ; Number of rows 109 110; Colors 111%assign BLACK 0 112%assign BLUE 1 113%assign GREEN 2 114%assign CYAN 3 115%assign RED 4 116%assign MAGENTA 5 117%assign BROWN 6 118%assign LIGHT_GRAY 7 119%assign DARK_GRAY 8 120%assign LIGHT_BLUE 9 121%assign LIGHT_GREEN 10 122%assign LIGHT_CYAN 11 123%assign LIGHT_RED 12 124%assign LIGHT_MAGENTA 13 125%assign YELLOW 14 126%assign WHITE 15 127 128%assign BRIGHT_COLOR_MASK 8 129 130; Characters 131%assign TRIANGLE_TO_RIGHT 16 132%assign TRIANGLE_TO_LEFT 17 133 134; Key codes 135%assign KEY_DOWN 0x50 136%assign KEY_UP 0x48 137%assign KEY_PAGE_DOWN 0x51 138%assign KEY_PAGE_UP 0x49 139%assign KEY_HOME 0x47 140%assign KEY_END 0x4f 141%assign KEY_RETURN 0x1C 142 143; Modifier key bitmasks 144%assign MODIFIER_RIGHT_SHIFT_KEY 0x01 145%assign MODIFIER_LEFT_SHIFT_KEY 0x02 146%assign MODIFIER_CONTROL_KEY 0x04 147%assign MODIFIER_ALT_KEY 0x08 148%assign MODIFIER_SCROLL_LOCK_KEY 0x10 149%assign MODIFIER_NUM_LOCK_KEY 0x20 150%assign MODIFIER_CAPS_LOCK_KEY 0x40 151%assign MODIFIER_INSERT_KEY 0x80 152 153; String constants with their length 154%define TITLE 'Haiku Boot Manager' 155%strlen TITLE_LENGTH TITLE 156%define SELECT_OS_MESSAGE 'Select an OS from the menu' 157%strlen SELECT_OS_MESSAGE_LENGTH SELECT_OS_MESSAGE 158 159; 16 bit code 160SECTION .text 161BITS 16 162 163 164; nicer way to get the size of a structure 165%define sizeof(s) s %+ _size 166 167; using a structure in a another structure definition 168%macro nstruc 1-2 1 169 resb sizeof(%1) * %2 170%endmacro 171 172; Variables on stack 173struc Locals 174 selection resw 1 175 firstLine resb 2 ; low byte used only 176 timeoutTicks resd 1 177 cursorX resb 1 178 cursorY resb 1 179 cursorShape resw 1 180 biosDrive resb 1 181endstruc 182 183cursorPosition equ cursorX 184 185%macro DEBUG_PAUSE 0 186 push ax 187 mov ah, READ_CHAR 188 int BIOS_KEYBOARD_SERVICES 189 pop ax 190%endmacro 191 192%macro CLEAR_SCREEN 0 193 mov ah, SCROLL_UP 194 xor al, al 195 mov bh, WHITE 196 xor cx, cx 197 mov dx, (TEXT_ROWS-1) * 0x100 + (TEXT_COLUMNS-1) 198 int BIOS_VIDEO_SERVICES 199%endmacro 200 201; Prints a null terminated string 202; bl ... color 203; si ... offset to string 204%macro PRINT_STRING 0 205 push ax 206 push bx 207 push cx 208 push dx 209 xor bh, bh ; write on page 0 210 jmp .loop_condition 211.loop: 212 mov dx, [bp + cursorPosition] 213 mov ah, SET_CURSOR 214 int BIOS_VIDEO_SERVICES 215 216 inc byte [bp + cursorX] 217 218 mov cx, 1 219 mov ah, WRITE_CHAR 220 int BIOS_VIDEO_SERVICES 221.loop_condition: 222 lodsb 223 cmp al, 0 224 jnz .loop 225 pop dx 226 pop cx 227 pop bx 228 pop ax 229 ret 230%endmacro 231 232; 64 bit value 233struc quadword 234 .lower resd 1 235 .upper resd 1 236endstruc 237 238; address packet as required by the EXTENDED_READ BIOS call 239struc AddressPacket 240 .packet_size resb 1 241 .reserved1 resb 1 242 .block_count resb 1 243 .reserved2 resb 1 244 .buffer resd 1 245 .offset nstruc quadword 246endstruc 247 248struc BootLoaderAddress 249 .device resb 1 ; hard drive number 250 .offset nstruc quadword ; LBA of start start sector 251endstruc 252 253; use code available in stage 1 254%define printstr printStringStage1 255 256stage1: 257 mov ax, 0x07c0 ; BOOT_BLOCK_START_ADDRESS / 16 258 mov ds, ax ; Setup segment registers 259 mov es, ax 260 mov ss, ax 261 262 mov sp, 0xFFFF - sizeof(Locals) ; Make stack empty 263 mov bp, sp 264 265 mov [bp + biosDrive], dl ; Store boot drive 266 cld ; String operations increment index 267 ; registers 268 CLEAR_SCREEN 269 call hideCursor 270 271 mov bh, 0 ; Text output on page 0 272 273 ; Print title centered at row 2 274 mov dx, 1 * 0x100 + (40 - TITLE_LENGTH / 2) 275 mov [bp + cursorPosition], dx 276 277 mov si, kTitle 278 mov bl, WHITE 279 call printstr 280 281 ; Print message centered at second last row 282 mov dx, (TEXT_ROWS-2) * 0x100 + (40 - SELECT_OS_MESSAGE_LENGTH / 2) 283 mov [bp + cursorPosition], dx 284 285 mov bl, LIGHT_GRAY 286 mov si, kSelectOSMessage 287 call printstr 288 289 ; Chain load rest of boot loader 290 mov ah, EXTENDED_READ ; Load 3 more sectors 291 mov dl, [bp + biosDrive] 292 mov si, nextStageDAP 293 int BIOS_DISK_SERVICES 294 jc .error ; I/O error 295 jmp stage2 ; Continue in loaded stage 2 296 297.error: 298 call showCursor 299 mov si, kError 300 mov bl, RED 301 call printstr 302 303 mov ah, READ_CHAR 304 int BIOS_KEYBOARD_SERVICES 305 306 mov dl, [bp + biosDrive] 307 int BIOS_REBOOT 308 309printStringStage1: 310 PRINT_STRING 311 312hideCursor: 313 mov ah, GET_CURSOR 314 int BIOS_VIDEO_SERVICES 315 mov [bp + cursorShape], cx 316 317 mov ah, SET_CURSOR_SHAPE 318 mov cx, 0x2000 319 int BIOS_VIDEO_SERVICES 320 ret 321 322showCursor: 323 mov cx, [bp + cursorShape] 324 mov ah, SET_CURSOR_SHAPE 325 int BIOS_VIDEO_SERVICES 326 ret 327 328nextStageDAP: 329 istruc AddressPacket 330 at AddressPacket.packet_size, db 0x10 331 at AddressPacket.block_count, db 0x03 332 at AddressPacket.buffer, dw 0x0200, 0x07c0 333 at AddressPacket.offset, dw 1 334 iend 335 336kTitle: 337 db TITLE, 0x00 338kSelectOSMessage: 339 db SELECT_OS_MESSAGE, 0x00 340kError: 341 db 'Error loading sectors!', 0x00 342 343kStage1UnusedSpace equ 440 - ($-$$) 344 ; Fill the missing space to reach byte 440 345 times kStage1UnusedSpace db 'B' 346 347kDiskSignature: 348 dw 0, 0 349kReserved: 350 dw 0 351kPartitionTable: 352 times 64 db 0 353 354kMBRSignature: 355 ; Magic marker "AA55" (to identify a valid boot record) 356 dw MBR_SIGNATURE 357 358; ====================================================================== 359; ======================= SECOND SECTOR ================================ 360; ====================================================================== 361 362; Use code available in stage 2 363%define printstr printStringStage2 364 365%assign TIMEOUT_OFF 0xffff 366 367 368stage2: 369 mov ax, [defaultItem] ; Select default item 370 mov [bp + selection], ax 371 372 mov ax, TICKS_PER_SECOND ; Calculate timeout ticks 373 mul word [timeout] 374 mov bx, dx 375 push ax 376 377 mov ah, READ_CLOCK 378 int BIOS_TIME_SERVICES 379 380 pop ax ; Add current ticks 381 add ax, dx 382 adc bx, cx 383 mov [bp + timeoutTicks], ax 384 mov [bp + timeoutTicks + 2], bx 385 386 mov al, [listItemCount] ; Calculate start row for menu 387 shr al, 1 388 mov bl, TEXT_ROWS / 2 389 sub bl, al ; y = TEXT_ROWS / 2 - number of items / 2 390 mov [bp + firstLine], bl 391 392 mov ah, GET_MODIFIER_KEYS ; Disable timeout if ALT key is pressed 393 int BIOS_KEYBOARD_SERVICES 394 and al, MODIFIER_ALT_KEY 395 jz showMenu 396 mov word [timeout], TIMEOUT_OFF 397 398showMenu: 399 call printMenu 400 401 cmp word [timeout], TIMEOUT_OFF 402 je inputLoop 403 404timeoutLoop: 405 mov ah, PROBE_CHAR 406 int BIOS_KEYBOARD_SERVICES 407 jnz inputLoop ; cancel timeout if key is pressed 408 call isTimeoutReached 409 jnc timeoutLoop 410 jmp bootSelectedPartition 411 412isTimeoutReached: 413 mov ah, READ_CLOCK 414 int BIOS_TIME_SERVICES 415 cmp cx, [bp + timeoutTicks + 2] 416 jb .returnFalse 417 ja .returnTrue 418 cmp dx, [bp + timeoutTicks] 419 ja .returnTrue 420.returnFalse: 421 clc 422 ret 423.returnTrue: 424 stc 425 ret 426 427; ================== Wait for a key and do something with it ================== 428mainLoop: 429 call printMenu 430 431inputLoop: 432 mov ah, READ_CHAR 433 int BIOS_KEYBOARD_SERVICES ; AL = ASCII Code, AH = Scancode 434 435 cmp ah, KEY_DOWN 436 je selectNextPartition 437 438 cmp ah, KEY_PAGE_DOWN 439 je selectLastPartition 440 cmp ah, KEY_END 441 je selectLastPartition 442 443 cmp ah, KEY_UP 444 je selectPreviousPartition 445 446 cmp ah, KEY_PAGE_UP 447 je selectFirstPartition 448 cmp ah, KEY_HOME 449 je selectFirstPartition 450 451 cmp ah, KEY_RETURN 452 jne inputLoop 453 jmp bootSelectedPartition 454 455selectNextPartition: 456 mov ax, [bp + selection] 457 inc ax 458 cmp ax, [listItemCount] 459 jne .done ; At end of list? 460 xor ax, ax ; Then jump to first entry 461.done: 462 mov [bp + selection], ax 463 jmp mainLoop 464 465selectLastPartition: 466 mov ax, [listItemCount] 467 dec ax 468 mov [bp + selection], ax 469 jmp mainLoop 470 471selectPreviousPartition: 472 mov ax, [bp + selection] 473 or ax, ax 474 jnz .done ; At top of list? 475 mov ax, [listItemCount] ; Then jump to last entry 476.done: 477 dec ax 478 mov [bp + selection], ax 479 jmp mainLoop 480 481selectFirstPartition: 482 xor ax, ax 483 mov [bp + selection], ax 484 jmp mainLoop 485 486 487; ======================= Print the OS list ============================ 488printMenu: 489 mov al, [bp + firstLine] 490 mov [bp + cursorY], al 491 492 mov si, list ; Start at top of list 493 xor cx, cx ; The index of the current item 494 495.loop: 496 lodsb ; String length incl. 0-terminator 497 add al, 3 ; center menu item 498 shr al, 1 ; x = TEXT_COLUMNS / 2 - length / 2 499 mov dl, TEXT_COLUMNS / 2 500 sub dl, al 501 mov [bp + cursorX], dl 502 503 mov al, TRIANGLE_TO_RIGHT 504 call updateMarker 505 inc byte [bp + cursorX] 506 507 mov di, cx 508 and di, 3 509 mov bl, [kColorTable + di] ; Text color 510 511 cmp cx, [bp + selection] 512 jne .print ; Selected item reached? 513 xor bl, BRIGHT_COLOR_MASK ; Highlight it 514 515.print: 516 call printstr 517 add si, sizeof(BootLoaderAddress) 518 519 add byte [bp + cursorX], 1 520 mov al, TRIANGLE_TO_LEFT 521 call updateMarker 522 523 inc byte [bp + cursorY] 524 inc cx 525 526 cmp cx, [listItemCount] 527 jne .loop 528 ret 529 530updateMarker: 531 cmp cx, [bp + selection] 532 je .print 533 mov al, ' ' ; Clear marker 534.print: 535 mov bl, WHITE 536 jmp printChar ; return from subroutine 537 538 539; ========================== Chainload ========================== 540 541bootSelectedPartition: 542 543 call showCursor 544 545 call getSelectedBootLoaderAddress 546 lodsb ; Set boot drive 547 mov dl, al 548 549 mov di, bootSectorDAP+AddressPacket.offset ; Copy start sector 550 mov cx, 4 ; It is stored in a quad word 551.copy_start_sector: 552 lodsw 553 stosw 554 loop .copy_start_sector 555 556 mov ah, EXTENDED_READ ; Now read start sector from HD 557 mov si, bootSectorDAP 558 int BIOS_DISK_SERVICES 559 mov si, kReadError 560 jc printAndHalt ; Failed to read sector 561 562 mov ax, [kMBRSignature] 563 cmp ax, MBR_SIGNATURE 564 mov si, kNoBootablePartitionError 565 jne printAndHalt ; Missing signature 566 567 CLEAR_SCREEN 568 569 ; Print "Loading <name>" at top of screen 570 mov word [bp + cursorPosition], 0 571 mov si, kLoadingMessage 572 mov bl, LIGHT_GRAY 573 call printstr 574 575 inc byte [bp + cursorX] 576 call getSelectedMenuItem 577 inc si ; Skip string length byte 578 call printstr 579 580 mov dx, 0x100 581 xor bh, bh 582 mov ah, SET_CURSOR 583 int BIOS_VIDEO_SERVICES 584 585 call getSelectedBootLoaderAddress 586 mov dl, [si] ; drive number in dl 587 588 jmp $$ ; Start loaded boot loader 589 590 591printAndHalt: 592 mov dx, (TEXT_ROWS-4) * 0x100 + (TEXT_COLUMNS / 3) 593 mov [bp + cursorPosition], dx 594 595 mov bx, 0x0F ; Page number and foreground color 596 call printstr 597 mov ah, READ_CHAR 598 int BIOS_KEYBOARD_SERVICES 599 mov dl, [bp + biosDrive] 600 int BIOS_REBOOT 601 602; Output: 603; si address of selected menu item 604; Trashes: 605; ax, cx 606getSelectedMenuItem: 607 mov si, list ; Search address of start sector 608 ; of the selected item. 609 mov cx, [bp + selection] 610 inc cx ; Number of required iterations 611 612 xor ah, ah ; The high-byte of the string length 613 ; see loop body 614 jmp .entry 615 616.loop: 617 lodsb ; Length of menu item name 618 add si, ax ; Skip name to BootLoaderAddess 619 add si, sizeof(BootLoaderAddress) 620 621.entry: 622 loop .loop 623 ret 624 625getSelectedBootLoaderAddress: 626 call getSelectedMenuItem 627 lodsb 628 xor ah, ah 629 add si, ax ; Skip name 630 mov dl, [si] 631 test dl, 0 ; if drive is 0, use boot drive 632 jz .takeOverBootDrive 633 ret 634.takeOverBootDrive: 635 mov dl, [bp + biosDrive] 636 mov [si], dl 637 ret 638 639printStringStage2: 640 PRINT_STRING 641 642; al ... ASCII character 643; bl ... color 644printChar: 645 push ax 646 push bx 647 push cx 648 push dx 649 650 xor bh, bh ; Write on page 0 651 652 mov dx, [bp + cursorPosition] 653 mov ah, SET_CURSOR 654 int BIOS_VIDEO_SERVICES 655 656 inc byte [bp + cursorX] 657 658 mov cx, 1 659 mov ah, WRITE_CHAR 660 int BIOS_VIDEO_SERVICES 661 662 pop dx 663 pop cx 664 pop bx 665 pop ax 666 ret 667 668; ================================ DATA =========================== 669 670bootSectorDAP: 671 istruc AddressPacket 672 at AddressPacket.packet_size, db 0x10 673 at AddressPacket.block_count, db 0x01 674 at AddressPacket.buffer, dw 0x0000, 0x07c0 675 iend 676 677kColorTable: 678 db BLUE, RED, GREEN, CYAN 679kReadError: 680 db 'Error loading sectors', 0x00 681kNoBootablePartitionError: 682 db 'Not a bootable partition', 0x00 683kLoadingMessage: 684 db 'Loading', 0x00 685 686 687listItemCount: 688defaultItem equ listItemCount + 2 689timeout equ defaultItem + 2 690list equ timeout + 2 691 692; dw number of entries 693; dw the default entry 694; dw the timeout (-1 for none) 695; entry: 696; db size of partition name 0-terminated string 697; db 0-terminated string with partition name 698; db hard drive number 699; quadword start sector 700 701%if USE_TEST_MENU 702 dw 0x06 703 704 dw 2 705 706 dw 5 707 708 db 0x06 709 db 'HAIKU', 0 710 db 0x80 711 dw 1, 0, 0, 0 712 713 db 0x08 714 db 'FreeBSD', 0 715 db 0x80 716 dw 0x003F, 0, 0, 0 717 718 db 0x04 719 db 'DOS', 0 720 db 0x80 721 dw 0x003E, 0, 0, 0 722 723 db 0x06 724 db 'LINUX', 0 725 db 0x80 726 dw 0x003F, 0, 0, 0 727 728 db 0x08 729 db 'BeOS R5', 0 730 db 0x80 731 dw 0x003F, 0, 0, 0 732 733 db 0x07 734 db 'OpenBSD', 0 735 db 0x80 736 dw 0xAAAA, 0, 0, 0 737 738 dw kStage1UnusedSpace 739%endif 740 741