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_RETURN 0x1C 138 139; Modifier key bitmasks 140%assign MODIFIER_RIGHT_SHIFT_KEY 0x01 141%assign MODIFIER_LEFT_SHIFT_KEY 0x02 142%assign MODIFIER_CONTROL_KEY 0x04 143%assign MODIFIER_ALT_KEY 0x08 144%assign MODIFIER_SCROLL_LOCK_KEY 0x10 145%assign MODIFIER_NUM_LOCK_KEY 0x20 146%assign MODIFIER_CAPS_LOCK_KEY 0x40 147%assign MODIFIER_INSERT_KEY 0x80 148 149; String constants with their length 150%define TITLE 'Haiku Boot Manager' 151%strlen TITLE_LENGTH TITLE 152%define SELECT_OS_MESSAGE 'Select an OS from the menu' 153%strlen SELECT_OS_MESSAGE_LENGTH SELECT_OS_MESSAGE 154 155; 16 bit code 156SECTION .text 157BITS 16 158 159 160; nicer way to get the size of a structure 161%define sizeof(s) s %+ _size 162 163; using a structure in a another structure definition 164%macro nstruc 1-2 1 165 resb sizeof(%1) * %2 166%endmacro 167 168; Variables on stack 169struc Locals 170 selection resw 1 171 firstLine resb 2 ; low byte used only 172 timeoutTicks resd 1 173 cursorX resb 1 174 cursorY resb 1 175 cursorShape resw 1 176 biosDrive resb 1 177endstruc 178 179cursorPosition equ cursorX 180 181%macro DEBUG_PAUSE 0 182 push ax 183 mov ah, READ_CHAR 184 int BIOS_KEYBOARD_SERVICES 185 pop ax 186%endmacro 187 188%macro CLEAR_SCREEN 0 189 mov ah, SCROLL_UP 190 xor al, al 191 mov bh, WHITE 192 xor cx, cx 193 mov dx, (TEXT_ROWS-1) * 0x100 + (TEXT_COLUMNS-1) 194 int BIOS_VIDEO_SERVICES 195%endmacro 196 197; Prints a null terminated string 198; bl ... color 199; si ... offset to string 200%macro PRINT_STRING 0 201 push ax 202 push bx 203 push cx 204 push dx 205 xor bh, bh ; write on page 0 206 jmp .loop_condition 207.loop: 208 mov dx, [bp + cursorPosition] 209 mov ah, SET_CURSOR 210 int BIOS_VIDEO_SERVICES 211 212 inc byte [bp + cursorX] 213 214 mov cx, 1 215 mov ah, WRITE_CHAR 216 int BIOS_VIDEO_SERVICES 217.loop_condition: 218 lodsb 219 cmp al, 0 220 jnz .loop 221 pop dx 222 pop cx 223 pop bx 224 pop ax 225 ret 226%endmacro 227 228; 64 bit value 229struc quadword 230 .lower resd 1 231 .upper resd 1 232endstruc 233 234; address packet as required by the EXTENDED_READ BIOS call 235struc AddressPacket 236 .packet_size resb 1 237 .reserved1 resb 1 238 .block_count resb 1 239 .reserved2 resb 1 240 .buffer resd 1 241 .offset nstruc quadword 242endstruc 243 244struc BootLoaderAddress 245 .device resb 1 ; hard drive number 246 .offset nstruc quadword ; LBA of start start sector 247endstruc 248 249; use code available in stage 1 250%define printstr printStringStage1 251 252stage1: 253 mov ax, 0x07c0 ; BOOT_BLOCK_START_ADDRESS / 16 254 mov ds, ax ; Setup segment registers 255 mov es, ax 256 mov ss, ax 257 258 mov sp, 0xFFFF - sizeof(Locals) ; Make stack empty 259 mov bp, sp 260 261 mov [bp + biosDrive], dl ; Store boot drive 262 cld ; String operations increment index 263 ; registers 264 CLEAR_SCREEN 265 call hideCursor 266 267 mov bh, 0 ; Text output on page 0 268 269 ; Print title centered at row 2 270 mov dx, 1 * 0x100 + (40 - TITLE_LENGTH / 2) 271 mov [bp + cursorPosition], dx 272 273 mov si, kTitle 274 mov bl, WHITE 275 call printstr 276 277 ; Print message centered at second last row 278 mov dx, (TEXT_ROWS-2) * 0x100 + (40 - SELECT_OS_MESSAGE_LENGTH / 2) 279 mov [bp + cursorPosition], dx 280 281 mov bl, LIGHT_GRAY 282 mov si, kSelectOSMessage 283 call printstr 284 285 ; Chain load rest of boot loader 286 mov ah, EXTENDED_READ ; Load 3 more sectors 287 mov dl, [bp + biosDrive] 288 mov si, nextStageDAP 289 int BIOS_DISK_SERVICES 290 jc .error ; I/O error 291 jmp stage2 ; Continue in loaded stage 2 292 293.error: 294 call showCursor 295 mov si, kError 296 mov bl, RED 297 call printstr 298 299 mov ah, READ_CHAR 300 int BIOS_KEYBOARD_SERVICES 301 302 mov dl, [bp + biosDrive] 303 int BIOS_REBOOT 304 305printStringStage1: 306 PRINT_STRING 307 308hideCursor: 309 mov ah, GET_CURSOR 310 int BIOS_VIDEO_SERVICES 311 mov [bp + cursorShape], cx 312 313 mov ah, SET_CURSOR_SHAPE 314 mov cx, 0x2000 315 int BIOS_VIDEO_SERVICES 316 ret 317 318showCursor: 319 mov cx, [bp + cursorShape] 320 mov ah, SET_CURSOR_SHAPE 321 int BIOS_VIDEO_SERVICES 322 ret 323 324nextStageDAP: 325 istruc AddressPacket 326 at AddressPacket.packet_size, db 0x10 327 at AddressPacket.block_count, db 0x03 328 at AddressPacket.buffer, dw 0x0200, 0x07c0 329 at AddressPacket.offset, dw 1 330 iend 331 332kTitle: 333 db TITLE, 0x00 334kSelectOSMessage: 335 db SELECT_OS_MESSAGE, 0x00 336kError: 337 db 'Error loading sectors!', 0x00 338 339kStage1UnusedSpace equ 440 - ($-$$) 340 ; Fill the missing space to reach byte 440 341 times kStage1UnusedSpace db 'B' 342 343kDiskSignature: 344 dw 0, 0 345kReserved: 346 dw 0 347kPartitionTable: 348 times 64 db 0 349 350kMBRSignature: 351 ; Magic marker "AA55" (to identify a valid boot record) 352 dw MBR_SIGNATURE 353 354; ====================================================================== 355; ======================= SECOND SECTOR ================================ 356; ====================================================================== 357 358; Use code available in stage 2 359%define printstr printStringStage2 360 361%assign TIMEOUT_OFF 0xffff 362 363 364stage2: 365 mov ax, [defaultItem] ; Select default item 366 mov [bp + selection], ax 367 368 mov ax, TICKS_PER_SECOND ; Calculate timeout ticks 369 mul word [timeout] 370 mov bx, dx 371 push ax 372 373 mov ah, READ_CLOCK 374 int BIOS_TIME_SERVICES 375 376 pop ax ; Add current ticks 377 add ax, dx 378 adc bx, cx 379 mov [bp + timeoutTicks], ax 380 mov [bp + timeoutTicks + 2], bx 381 382 mov al, [listItemCount] ; Calculate start row for menu 383 shr al, 1 384 mov bl, TEXT_ROWS / 2 385 sub bl, al ; y = TEXT_ROWS / 2 - number of items / 2 386 mov [bp + firstLine], bl 387 388 mov ah, GET_MODIFIER_KEYS ; Disable timeout if ALT key is pressed 389 int BIOS_KEYBOARD_SERVICES 390 and al, MODIFIER_ALT_KEY 391 jz showMenu 392 mov word [timeout], TIMEOUT_OFF 393 394showMenu: 395 call printMenu 396 397 cmp word [timeout], TIMEOUT_OFF 398 je inputLoop 399 400timeoutLoop: 401 mov ah, PROBE_CHAR 402 int BIOS_KEYBOARD_SERVICES 403 jnz inputLoop ; cancel timeout if key is pressed 404 call isTimeoutReached 405 jnc timeoutLoop 406 jmp bootSelectedPartition 407 408isTimeoutReached: 409 mov ah, READ_CLOCK 410 int BIOS_TIME_SERVICES 411 cmp cx, [bp + timeoutTicks + 2] 412 jb .returnFalse 413 ja .returnTrue 414 cmp dx, [bp + timeoutTicks] 415 ja .returnTrue 416.returnFalse: 417 clc 418 ret 419.returnTrue: 420 stc 421 ret 422 423; ================== Wait for a key and do something with it ================== 424mainLoop: 425 call printMenu 426 427inputLoop: 428 mov ah, READ_CHAR 429 int BIOS_KEYBOARD_SERVICES ; AL = ASCII Code, AH = Scancode 430 431 cmp ah, KEY_DOWN 432 je selectNextPartition 433 434 cmp ah, KEY_UP 435 je selectPreviousPartition 436 437 cmp ah, KEY_RETURN 438 jne inputLoop 439 jmp bootSelectedPartition 440 441selectNextPartition: 442 mov ax, [bp + selection] 443 inc ax 444 cmp ax, [listItemCount] 445 jne .done ; At end of list? 446 xor ax, ax ; Then jump to first entry 447.done: 448 mov [bp + selection], ax 449 jmp mainLoop 450 451selectPreviousPartition: 452 mov ax, [bp + selection] 453 or ax, ax 454 jnz .done ; At top of list? 455 mov ax, [listItemCount] ; Then jump to last entry 456.done: 457 dec ax 458 mov [bp + selection], ax 459 jmp mainLoop 460 461 462; ======================= Print the OS list ============================ 463printMenu: 464 mov al, [bp + firstLine] 465 mov [bp + cursorY], al 466 467 mov si, list ; Start at top of list 468 xor cx, cx ; The index of the current item 469 470.loop: 471 lodsb ; String length incl. 0-terminator 472 add al, 3 ; center menu item 473 shr al, 1 ; x = TEXT_COLUMNS / 2 - length / 2 474 mov dl, TEXT_COLUMNS / 2 475 sub dl, al 476 mov [bp + cursorX], dl 477 478 mov al, TRIANGLE_TO_RIGHT 479 call updateMarker 480 inc byte [bp + cursorX] 481 482 mov di, cx 483 and di, 3 484 mov bl, [kColorTable + di] ; Text color 485 486 cmp cx, [bp + selection] 487 jne .print ; Selected item reached? 488 xor bl, BRIGHT_COLOR_MASK ; Highlight it 489 490.print: 491 call printstr 492 add si, sizeof(BootLoaderAddress) 493 494 add byte [bp + cursorX], 1 495 mov al, TRIANGLE_TO_LEFT 496 call updateMarker 497 498 inc byte [bp + cursorY] 499 inc cx 500 501 cmp cx, [listItemCount] 502 jne .loop 503 ret 504 505updateMarker: 506 cmp cx, [bp + selection] 507 je .print 508 mov al, ' ' ; Clear marker 509.print: 510 mov bl, WHITE 511 jmp printChar ; return from subroutine 512 513 514; ========================== Chainload ========================== 515 516bootSelectedPartition: 517 518 call showCursor 519 520 call getSelectedBootLoaderAddress 521 lodsb ; Set boot drive 522 mov dl, al 523 524 mov di, bootSectorDAP+AddressPacket.offset ; Copy start sector 525 mov cx, 4 ; It is stored in a quad word 526.copy_start_sector: 527 lodsw 528 stosw 529 loop .copy_start_sector 530 531 mov ah, EXTENDED_READ ; Now read start sector from HD 532 mov si, bootSectorDAP 533 int BIOS_DISK_SERVICES 534 mov si, kReadError 535 jc printAndHalt ; Failed to read sector 536 537 mov ax, [kMBRSignature] 538 cmp ax, MBR_SIGNATURE 539 mov si, kNoBootablePartitionError 540 jne printAndHalt ; Missing signature 541 542 CLEAR_SCREEN 543 544 ; Print "Loading <name>" at top of screen 545 mov word [bp + cursorPosition], 0 546 mov si, kLoadingMessage 547 mov bl, LIGHT_GRAY 548 call printstr 549 550 inc byte [bp + cursorX] 551 call getSelectedMenuItem 552 inc si ; Skip string length byte 553 call printstr 554 555 mov dx, 0x100 556 xor bh, bh 557 mov ah, SET_CURSOR 558 int BIOS_VIDEO_SERVICES 559 560 call getSelectedBootLoaderAddress 561 mov dl, [si] ; drive number in dl 562 563 jmp $$ ; Start loaded boot loader 564 565 566printAndHalt: 567 mov dx, (TEXT_ROWS-4) * 0x100 + (TEXT_COLUMNS / 3) 568 mov [bp + cursorPosition], dx 569 570 mov bx, 0x0F ; Page number and foreground color 571 call printstr 572 mov ah, READ_CHAR 573 int BIOS_KEYBOARD_SERVICES 574 mov dl, [bp + biosDrive] 575 int BIOS_REBOOT 576 577; Output: 578; si address of selected menu item 579; Trashes: 580; ax, cx 581getSelectedMenuItem: 582 mov si, list ; Search address of start sector 583 ; of the selected item. 584 mov cx, [bp + selection] 585 inc cx ; Number of required iterations 586 587 xor ah, ah ; The high-byte of the string length 588 ; see loop body 589 jmp .entry 590 591.loop: 592 lodsb ; Length of menu item name 593 add si, ax ; Skip name to BootLoaderAddess 594 add si, sizeof(BootLoaderAddress) 595 596.entry: 597 loop .loop 598 ret 599 600getSelectedBootLoaderAddress: 601 call getSelectedMenuItem 602 lodsb 603 xor ah, ah 604 add si, ax ; Skip name 605 mov dl, [si] 606 test dl, 0 ; if drive is 0, use boot drive 607 jz .takeOverBootDrive 608 ret 609.takeOverBootDrive: 610 mov dl, [bp + biosDrive] 611 mov [si], dl 612 ret 613 614printStringStage2: 615 PRINT_STRING 616 617; al ... ASCII character 618; bl ... color 619printChar: 620 push ax 621 push bx 622 push cx 623 push dx 624 625 xor bh, bh ; Write on page 0 626 627 mov dx, [bp + cursorPosition] 628 mov ah, SET_CURSOR 629 int BIOS_VIDEO_SERVICES 630 631 inc byte [bp + cursorX] 632 633 mov cx, 1 634 mov ah, WRITE_CHAR 635 int BIOS_VIDEO_SERVICES 636 637 pop dx 638 pop cx 639 pop bx 640 pop ax 641 ret 642 643; ================================ DATA =========================== 644 645bootSectorDAP: 646 istruc AddressPacket 647 at AddressPacket.packet_size, db 0x10 648 at AddressPacket.block_count, db 0x01 649 at AddressPacket.buffer, dw 0x0000, 0x07c0 650 iend 651 652kColorTable: 653 db BLUE, RED, GREEN, CYAN 654kReadError: 655 db 'Error loading sectors', 0x00 656kNoBootablePartitionError: 657 db 'Not a bootable partition', 0x00 658kLoadingMessage: 659 db 'Loading', 0x00 660 661 662listItemCount: 663defaultItem equ listItemCount + 2 664timeout equ defaultItem + 2 665list equ timeout + 2 666 667; dw number of entries 668; dw the default entry 669; dw the timeout (-1 for none) 670; entry: 671; db size of partition name 0-terminated string 672; db 0-terminated string with partition name 673; db hard drive number 674; quadword start sector 675 676%if USE_TEST_MENU 677 dw 0x06 678 679 dw 2 680 681 dw 5 682 683 db 0x06 684 db 'HAIKU', 0 685 db 0x80 686 dw 1, 0, 0, 0 687 688 db 0x08 689 db 'FreeBSD', 0 690 db 0x80 691 dw 0x003F, 0, 0, 0 692 693 db 0x04 694 db 'DOS', 0 695 db 0x80 696 dw 0x003E, 0, 0, 0 697 698 db 0x06 699 db 'LINUX', 0 700 db 0x80 701 dw 0x003F, 0, 0, 0 702 703 db 0x08 704 db 'BeOS R5', 0 705 db 0x80 706 dw 0x003F, 0, 0, 0 707 708 db 0x07 709 db 'OpenBSD', 0 710 db 0x80 711 dw 0xAAAA, 0, 0, 0 712 713 dw kStage1UnusedSpace 714%endif 715 716