Refactored and reformatted.

Select default item at start.
Added timeout handling.
Don't switch to graphics mode, stay in text mode instead. Graphics mode led to display issues with lilo.
Reverted order of text color like it is in original bootman.
Fixed bugs.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24933 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Pfeiffer
2008-04-12 08:19:25 +00:00
parent 1b56fe8db5
commit 86ad7d1c71
+478 -293
View File
@@ -1,376 +1,561 @@
; ;
; Copyright 2007, Dengg David, [email protected]. All rights reserved. ; Copyright 2007, Dengg David, [email protected]. All rights reserved.
; Copyright 2008, Michael Pfeiffer, [email protected]. All rights reserved.
; Copyright 2005, Ingo Weinhold, [email protected].
; Distributed under the terms of the MIT License. ; Distributed under the terms of the MIT License.
;
; Steps to create BootLoader.h
; 1. nasm -f bin bootman.S -o bootman.bin
; 2. cc MakeArray.cpp -o make_array -lstdc++
; 3. ./make_array kBootLoader bootman.bin > BootLoader.h
; or
; nasm -f bin bootman.S -o bootman.bin && ./make_array kBootLoader bootman.bin > BootLoader.h
%assign USE_TEST_MENU 0
%assign BOOT_BLOCK_START_ADDRESS 0x7c00
%assign MBR_SIGNATURE 0xAA55
; BIOS calls
%assign BIOS_VIDEO_SERVICES 0x10 %assign BIOS_VIDEO_SERVICES 0x10
%assign BIOS_DISK_SERVICES 0x13
%assign BIOS_KEYBOARD_SERVICES 0x16 %assign BIOS_KEYBOARD_SERVICES 0x16
%assign WRITE_CHAR 0x0e ; al - char %assign BIOS_REBOOT 0x19
%assign BIOS_TIME_SERVICES 0x1A
; video services
%assign SET_VIDEO_MODE 0x00 ; al - mode
%assign SET_CURSOR 0x02 ; dl - column
; dh - row
; bh - page
%assign GET_CURSOR 0x03 ; bh - page
; -> dl - column
; dh - row
; Cursor shape:
; ch - starting scan line
; cl - ending scan line
%assign SCROLL_UP 0x06 ; al - lines (0: clear screen)
; bh - attribute
; ch - upper line
; cl - left column
; dh - lower line
; dl - right column
%assign WRITE_CHAR 0x09 ; al - char
; bh - page
; bl - attribute
; cx - count
;%assign WRITE_CHAR 0x0e ; al - char
; bh - page ; bh - page
; bl - foreground color (graphics mode only) ; bl - foreground color (graphics mode only)
[bits 16] ; Real Mode yeah ; disk services
start: %assign READ_DISK_SECTORS 0x02 ; dl - drive
; ======================= SCREEN SETUP ================================= ; es:bx - buffer
mov ax, 0x07C0 ; Segment Location 0x07C0 ; dh - head (0 - 15)
mov ds, ax ; Set Data Segment to 0x07C0 ; ch - track 7:0 (0 - 1023)
mov es, ax ; Set Extra Segment to 0x07C0 ; cl - track 9:8,
mov ss, ax ; Set Stack Segment to 0x07C0 ; sector (1 - 17)
mov sp, 0xFFFF ; al - sector count
; -> al - sectors read
%assign READ_DRIVE_PARAMETERS 0x08 ; dl - drive
; -> cl - max cylinder 9:8
; - sectors per track
; ch - max cylinder 7:0
; dh - max head
; dl - number of drives (?)
%assign CHECK_DISK_EXTENSIONS_PRESENT 0x41 ; bx - 0x55aa
; dl - drive
; -> success: carry clear
; ah - extension version
; bx - 0xaa55
; cx - support bit mask
; -> error: carry set
%assign EXTENDED_READ 0x42 ; dl - drive
; ds:si - address packet
; -> success: carry clear
; -> error: carry set
mov ah, 0x00 ; Set Screen Resolution to 640x480 %assign FIXED_DISK_SUPPORT 0x1 ; flag indicating fixed disk
mov al, 0x12 ; extension command subset
mov bx, 0x0
mov cx, 0x1
int 0x10
mov ah, 0x02 ; Set cursor position ; keyboard services
mov dx, 0x0120 %assign READ_CHAR 0x00 ; -> al - ASCII char
int 0x10 ; ah - scan code
%assign PROBE_CHAR 0x01 ; -> zf = 0
; al - ASCII char
; ah - scan code
; timer services
%assign READ_CLOCK 0x00 ; -> cx - high word
; dx - low word
; one tick = 1/18.2s
%assign TICKS_PER_SECOND 19
; video modes
%assign GRAPHIC_MODE_80x25 0x12 ; 640 x 480 graphic mode
%assign TEXT_COLUMNS 80 ; Number of columns
%assign TEXT_ROWS 25 ; Number of rows
; Colors
%assign BLACK 0
%assign BLUE 1
%assign GREEN 2
%assign CYAN 3
%assign RED 4
%assign MAGENTA 5
%assign BROWN 6
%assign LIGHT_GRAY 7
%assign DARK_GRAY 8
%assign LIGHT_BLUE 9
%assign LIGHT_GREEN 10
%assign LIGHT_CYAN 11
%assign LIGHT_RED 12
%assign LIGHT_MAGENTA 13
%assign YELLOW 14
%assign WHITE 15
%assign BRIGHT_COLOR_MASK 8
mov si, signation ; Print "Haiku Bootmanager" ; Key codes
call printstr %assign KEY_DOWN 0x50
%assign KEY_UP 0x48
%assign KEY_RETURN 0x1C
; String constants with their length
%define TITLE 'Haiku Boot Manager'
%strlen TITLE_LENGTH TITLE
%define SELECT_OS_MESSAGE 'Select an OS from the menu'
%strlen SELECT_OS_MESSAGE_LENGTH SELECT_OS_MESSAGE
; 16 bit code
SECTION
BITS 16
mov ah, 0x02 ; Set cursor position ; nicer way to get the size of a structure
mov dx, 0x1B1B %define sizeof(s) s %+ _size
int 0x10
mov si, select_os ; Print "Select Os..." ; using a structure in a another structure definition
call printstr %macro nstruc 1-2 1
resb sizeof(%1) * %2
%endmacro
mov ah, 0 %macro DEBUG_PAUSE 0
int 0x16 push ax
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
pop ax
%endmacro
; ======================= LOAD 3 MORE SECTORS INTO MEM ================= %macro clearScreen 0
load_from_disk: ; load 3 more sectors into mem mov ah, SCROLL_UP
mov ah, 0x42 xor al, al
mov dl, 0x80 mov bh, WHITE
mov si, DAP xor cx, cx
int 0x13 mov dx, (TEXT_ROWS-1) * 0x100 + (TEXT_COLUMNS-1)
mov si, error int BIOS_VIDEO_SERVICES
jc printstr %endmacro
jmp secsec ; Jump into the second sector
ende: ; Prints a null terminated string
mov ah, 0x0e ; si ... offset to string
mov al, 'F' %macro printString 0
mov bx, 0x2 push cx
mov cx, 0x1
int 0x10
jmp $ ; Final Loopy. Never reached
%if 0
printstr: ; Poor mans printf
lodsb
mov ah, 0x0E
mov bx, 0x0F
mov cx, 0x01
int 0x10
cmp al, 0x00
jnz printstr
ret
%else
printstr:
; page and foreground color
jmp .loop_condition jmp .loop_condition
.loop .loop
mov ah, WRITE_CHAR
mov bx, 0x0F
mov cx, 1 mov cx, 1
mov ah, WRITE_CHAR
int BIOS_VIDEO_SERVICES
mov ah, GET_CURSOR
int BIOS_VIDEO_SERVICES
inc dl
mov ah, SET_CURSOR
int BIOS_VIDEO_SERVICES int BIOS_VIDEO_SERVICES
.loop_condition .loop_condition
lodsb lodsb
cmp al, 0 cmp al, 0
jnz .loop jnz .loop
pop cx
ret ret
%endif %endmacro
DAP: ; 64 bit value
db 0x10 ; Size of DAP struc quadword
db 0x00 ; unused (zero) .lower resd 1
db 0x03 ; number of sectors to read .upper resd 1
db 0x00 ; unused (zero) endstruc
dw 0x0200 ; segment:offset pointer to buffer
dw 0x07C0 ; segment:offset pointer to buffer
dw 0x0001 ; LBA address
dw 0x0000
dw 0x0000
dw 0x0000
WinDAP: ; address packet as required by the EXTENDED_READ BIOS call
db 0x0B ; Size of DAP struc AddressPacket
db 0x00 ; unused (zero) .packet_size resb 1
db 0x01 ; number of sectors to read .reserved1 resb 1
db 0x00 ; unused (zero) .block_count resb 1
dw 0x0000 ; segment:offset pointer to buffer .reserved2 resb 1
dw 0x07C0 ; segment:offset pointer to buffer .buffer resd 1
dw 0x003F ; LBA address .offset nstruc quadword
dw 0x0000 endstruc
; use code available in stage 1
%define printstr printStringStage1
signation: stage1:
db 'Haiku Bootmanager', 0x00 mov ax, 0x07C0 ; BOOT_BLOCK_START_ADDRESS / 16
select_os: mov ds, ax ; Setup segment registers
db 'Select an OS from the menu', 0x00 mov es, ax
mov ss, ax
times 510 -($-$$) db 'B' ; Fill the missing space to reach 510 byte and set the mov sp, 0xFFFF ; Make stack empty
dw 0xAA55 ; magic marker "AA55" (to identify a valid bootrecord)
cld ; String operations increment index registers
clearScreen
mov bh, 0 ; Text output on page 0
; Print title centered at row 2
mov ah, SET_CURSOR
mov dx, 1 * 0x100 + (40 - TITLE_LENGTH / 2)
int BIOS_VIDEO_SERVICES
mov si, kTitle
mov bl, WHITE
call printstr
; Print message centered at second last row
mov ah, SET_CURSOR
mov dx, (TEXT_ROWS-2) * 0x100 + (40 - SELECT_OS_MESSAGE_LENGTH / 2)
mov bl, LIGHT_GRAY
int BIOS_VIDEO_SERVICES
mov si, kSelectOSMessage
call printstr
; Chain load rest of boot loader
mov ah, EXTENDED_READ ; Load 3 more sectors
mov dl, 0x80 ; First HDD
mov si, nextStageDAP
int BIOS_DISK_SERVICES
jc .error ; I/O error
jmp stage2 ; Continue in loaded stage 2
.error:
mov si, kError
mov bl, RED
call printstr
.halt
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
hlt
jmp .halt
printStringStage1:
printString
nextStageDAP:
istruc AddressPacket
at AddressPacket.packet_size, db 0x10
at AddressPacket.block_count, db 0x03
at AddressPacket.buffer, dw 0x0200, 0x07c0
at AddressPacket.offset, dw 1
iend
kTitle:
db TITLE, 0x00
kSelectOSMessage:
db SELECT_OS_MESSAGE, 0x00
kError:
db 'Error loading sectors!', 0x00
; Fill the missing space to reach 510 byte
times 510 -($-$$) db 'B'
signature:
; Magic marker "AA55" (to identify a valid boot record)
dw MBR_SIGNATURE
; ====================================================================== ; ======================================================================
; ======================= SECOND SECTOR ================================ ; ======================= SECOND SECTOR ================================
; ====================================================================== ; ======================================================================
secsec: ; Use code available in stage 1
; ======================= Center list ================================== %define printstr printStringStage2
xor bx,bx ; Get ready for the Division
xor ax, ax ; Get ready for the Division
mov al, [list_items]
mov bl, 2 ; Divide by two
div bl ; Divide
mov bl, 12 %assign TIMEOUT_OFF 0xffff
sub bl, al ; Substract from 12 (screen is 80x25)
mov dh, bl
mov [first_position], dx
; ======================= Print the OS list ============================
print_list:
mov dx, [first_position]
mov [position], dx
mov ah, 0x02 ; Set cursor position stage2:
int 0x10 mov ax, [defaultItem] ; Select default item
mov si, list ; Load the List mov [selection], ax
xor cx,cx ; Load listitems to CX
mov byte [colour], 0x0000 ; Reset Colour
print_list_loop:
lodsb ; Get lenght of string from list into al
; Get the Text to be in the middle (40 - lenght/2)
xor bx,bx ; Get ready for the Division
mov ah, 0x00
mov bl, 2 ; Divide by two
div bl ; Divide
mov bl, 40 mov ax, TICKS_PER_SECOND ; Calculate timeout ticks
sub bl, al ; Substract from 40 (screen is 80x25) mul word [timeout]
mov al, bl mov bx, dx
mov dx, [position] ; Get last position push ax
inc dh ; Next line
mov dl, al ; Move it to the place where the row should be for the interrupt (DL)
mov ah, 0x02 ; Set cursor position mov ah, READ_CLOCK
mov [position], dx int BIOS_TIME_SERVICES
int 0x10
mov dx, [selection] pop ax ; Add current ticks
mov byte bl, [colour] ; Load Colour add ax, dx
and bx, 0x03 ; Just adc bx, cx
inc bl ; Colours ... 1-4 mov [timeoutTicks], ax
cmp cx, [selection] mov [timeoutTicks + 2], bx
jnz no_highlight
xor bx, 0x08 ; Highlight selected item mov al, [listItemCount] ; Calculate start row for menu
no_highlight: shr al, 1
mov byte [colour], bl ; Store Colour mov bl, TEXT_ROWS / 2
call print_list_item sub bl, al ; y = TEXT_ROWS / 2 - number of items / 2
inc cx mov [firstLine], bl
cmp cx, [list_items]
jne print_list_loop call printMenu
cmp word [timeout], TIMEOUT_OFF
je inputLoop
timeoutLoop:
mov ah, PROBE_CHAR
int BIOS_KEYBOARD_SERVICES
jnz inputLoop ; cancel timeout if key is pressed
call isTimeoutReached
jnc timeoutLoop
jmp bootSelectedPartition
isTimeoutReached:
mov ah, READ_CLOCK
int BIOS_TIME_SERVICES
cmp cx, [timeoutTicks + 2]
jb .returnFalse
ja .returnTrue
cmp dx, [timeoutTicks]
ja .returnTrue
.returnFalse:
clc
ret
.returnTrue:
stc
ret
; ================== Wait for a key and do something with it ================== ; ================== Wait for a key and do something with it ==================
mainLoop:
call printMenu
main_loopy: inputLoop:
mov ah, 0x02 ; Set cursor position mov ah, READ_CHAR
mov dx, 0x181C int BIOS_KEYBOARD_SERVICES ; AL = ASCII Code, AH = Scancode
int 0x10
mov ah, 0x00 ; Keyboard Read: wait for key cmp ah, KEY_DOWN
int 0x16 ; AL = ASCII Code, AH = Scancode je selectNextPartition
cmp ah, 0x50
jz key_down ; Was it key_down, key_up... ?
cmp ah, 0x48
jz key_up
cmp ah, 0x1C
jz key_enter
jmp main_loopy cmp ah, KEY_UP
je selectPreviousPartition
key_down: cmp ah, KEY_RETURN
je bootSelectedPartition
jmp inputLoop
selectNextPartition:
mov ax, [selection] mov ax, [selection]
mov bx, [list_items]
dec bx
cmp ax, bx
je key_down_no ; Are we at the bottom of the list?
inc ax inc ax
cmp ax, [listItemCount]
jne .done ; At end of list?
xor ax, ax ; Then jump to first entry
.done:
mov [selection], ax mov [selection], ax
key_down_no: jmp mainLoop
jmp print_list
key_up: selectPreviousPartition:
mov ax, [selection] mov ax, [selection]
mov bx, [list_items] or ax, ax
cmp ax, 0 jnz .done ; At top of list?
je key_down_no ; Are we at the top of the list? mov ax, [listItemCount] ; Then jump to last entry
.done:
dec ax dec ax
mov [selection], ax mov [selection], ax
key_up_no: jmp mainLoop
jmp print_list
; ======================= Print the OS list ============================
printMenu:
mov al, [firstLine]
mov [currentLine], al
mov si, list ; Start at top of list
xor cx, cx ; The index of the current item
.loop:
lodsb ; String length incl. 0-terminator
dec al ; center menu item
shr al, 1 ; x = TEXT_COLUMNS / 2 - length / 2
mov dl, TEXT_COLUMNS / 2
sub dl, al
mov dh, [currentLine]
mov ah, SET_CURSOR
int BIOS_VIDEO_SERVICES
mov di, cx
and di, 3
mov bl, [kColorTable + di] ; Text color
cmp cx, [selection]
jne .print ; Selected item reached?
xor bl, BRIGHT_COLOR_MASK ; Highlight it
.print:
call printstr
add si, sizeof(quadword) ; Skip start sector quad word
inc byte [currentLine]
inc cx
cmp cx, [listItemCount]
jne .loop
mov ah, SET_CURSOR ; Set cursor position
mov dx, (TEXT_ROWS-4) * 0x100 + (TEXT_COLUMNS / 3)
mov dx, 0x181C
int BIOS_VIDEO_SERVICES
ret
; ========================== Chainload ========================== ; ========================== Chainload ==========================
key_enter: bootSelectedPartition:
mov si, list ; Search address of start sector
; of the selected item.
mov cx, [selection] mov cx, [selection]
mov si, list inc cx ; Number of required iterations
inc cx
load_LBA:
lodsb
cmp al, 0x80
jne load_LBA
loop load_LBA
xor ah, ah ; The high-byte of the string length
; see loop body
jmp .search_loop_entry
.search_loop:
add si, sizeof(quadword) ; Skip to begin of next menu item
.search_loop_entry:
lodsb ; Length of menu item name
add si, ax ; Skip name to address of start sector
loop .search_loop
mov di, bootSectorDAP+AddressPacket.offset ; Copy start sector
mov cx, 4 ; It is stored in a quad word
.copy_start_sector
lodsw lodsw
mov [LBA_selectionA], ax stosw
lodsw loop .copy_start_sector
mov [LBA_selectionB], ax
mov si, ChainLoad_DAP ; Load the List mov ah, EXTENDED_READ ; Now read start sector from HD
mov ax, [LBA_selectionA] mov dl, 0x80 ; overwriting our first stage
mov word [si+8], ax mov si, bootSectorDAP
mov ax, [LBA_selectionB] int BIOS_DISK_SERVICES
mov word [si+10], ax mov si, kReadError
jc printAndHalt ; Failed to read sector
mov ah, 0x42 mov ax, [signature]
mov dl, 0x80 cmp ax, MBR_SIGNATURE
mov si, ChainLoad_DAP mov si, kNoBootablePartitionError
int 0x13 jne printAndHalt ; Missing signature
mov si, error_lfd
jc print_debug
mov si, 0x0 jmp $$ ; Start loaded boot loader
xor ax, ax
mov si, error_nb
mov byte al, [0x01FE]
cmp al, 0x55
jne print_debug
mov byte al, [0x01FF]
cmp al, 0xAA
jne print_debug
mov si, yeah ; Yeah
jmp print_debug
; ====================== THE printMenu function ================
printStringStage2:
printString
fin: printAndHalt:
mov ah, 0x0e mov bx, 0x0F ; Page number and foreground color
mov al, 'F' call printstr
mov bx, 0x2 .halt
mov cx, 0x1 mov ah, READ_CHAR
int 0x10 int BIOS_KEYBOARD_SERVICES
jmp $ hlt
jmp .halt
; ====================== THE print_list function ================
print_list_item:
print_list_item_loop:
lodsb
cmp al,0x80
jz print_list_item_end
mov ah, 0x0E
int 0x10
jmp print_list_item_loop
print_list_item_end:
lodsw
lodsw
ret
print_debug: ; Poor mans printf again
lodsb
mov ah, 0x0E
mov bx, 0x0F
mov cx, 0x01
int 0x10
cmp al, 0x00
jnz print_debug
jmp $
; ================================ DATA =========================== ; ================================ DATA ===========================
error_lfd:
db 'Error loading Sectors', 0x00
error_nb:
db 'Not a bootable partition', 0x00
LBA_selectionA:
dw 0x0000
LBA_selectionB:
dw 0x0000
selection: selection:
dw 0x0000 dw 0x0000
position: firstLine:
dw 0x091D db 0
lenght: currentLine:
dw 0x0000 db 0
colour: timeoutTicks:
dw 0x0000 dw 0, 0
first_position:
dw 0x091F bootSectorDAP:
nobots: istruc AddressPacket
db 'Boot sector signature not found', 0x00 at AddressPacket.packet_size, db 0x10
error: at AddressPacket.block_count, db 0x01
at AddressPacket.buffer, dw 0x0000, 0x07c0
iend
kColorTable:
db BLUE, RED, CYAN, GREEN
kReadError:
db 'Error loading sectors', 0x00 db 'Error loading sectors', 0x00
yeah: kNoBootablePartitionError:
db 'Not a bootable partition', 0x00
kBootMessage:
db 'Boot!', 0x00 db 'Boot!', 0x00
listItemCount:
defaultItem equ listItemCount + 2
timeout equ defaultItem + 2
list equ timeout + 2
list_items: ; dw number of entries
; dw the default entry
; dw the timeout (-1 for none)
; entry:
; db size of partition name 0-terminated string
; db 0-terminated string with partition name
; quadword start sector
%if USE_TEST_MENU
dw 0x06 dw 0x06
list:
db 0x05 dw 2
db 'HAIKU' , 0x80
dw 0x003E dw 5
dw 0x0000
db 0x06
db 'HAIKU', 0
dw 1, 0, 0, 0
db 0x08
db 'FreeBSD', 0
dw 0x003F, 0, 0, 0
db 0x04
db 'DOS', 0
dw 0x003E, 0, 0, 0
db 0x06
db 'LINUX', 0
dw 0x003F, 0, 0, 0
db 0x08
db 'BeOS R5', 0
dw 0x003F, 0, 0, 0
db 0x07 db 0x07
db 'FreeBSD' , 0x80 db 'OpenBSD', 0
dw 0x003F dw 0xAAAA, 0, 0, 0
dw 0x0000 %endif
db 0x03
db 'DOS' , 0x80
dw 0x003E
dw 0x0000
db 0x05
db 'LINUX' , 0x80
dw 0x003F
dw 0x0000
db 0x07
db 'BeOS R5' , 0x80
dw 0x003F
dw 0x0000
db 0x07
db 'OpenBSD' , 0x80
dw 0xAAAA
dw 0x0000
ChainLoad_DAP:
db 0x10 ; Size of DAP
db 0x00 ; unused (zero)
db 0x01 ; number of sectors to read
db 0x00 ; unused (zero)
dw 0x0000 ; segment:offset pointer to buffer
dw 0x07C0 ; segment:offset pointer to buffer
dw 0x003F ; LBA address
dw 0x0000
dw 0x0000
dw 0x0000