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
+500 -315
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.
;
%assign BIOS_VIDEO_SERVICES 0x10 ; Steps to create BootLoader.h
%assign BIOS_KEYBOARD_SERVICES 0x16 ; 1. nasm -f bin bootman.S -o bootman.bin
%assign WRITE_CHAR 0x0e ; al - char ; 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_DISK_SERVICES 0x13
%assign BIOS_KEYBOARD_SERVICES 0x16
%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 jmp .loop_condition
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
.loop .loop
mov ah, WRITE_CHAR mov cx, 1
mov bx, 0x0F mov ah, WRITE_CHAR
mov cx, 1 int BIOS_VIDEO_SERVICES
int BIOS_VIDEO_SERVICES mov ah, GET_CURSOR
.loop_condition int BIOS_VIDEO_SERVICES
inc dl
mov ah, SET_CURSOR
int BIOS_VIDEO_SERVICES
.loop_condition
lodsb lodsb
cmp al, 0 cmp al, 0
jnz .loop jnz .loop
pop cx
ret ret
%endif %endmacro
DAP:
db 0x10 ; Size of DAP
db 0x00 ; unused (zero)
db 0x03 ; number of sectors to read
db 0x00 ; unused (zero)
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:
db 0x0B ; 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
; 64 bit value
struc quadword
.lower resd 1
.upper resd 1
endstruc
signation: ; address packet as required by the EXTENDED_READ BIOS call
db 'Haiku Bootmanager', 0x00 struc AddressPacket
select_os: .packet_size resb 1
db 'Select an OS from the menu', 0x00 .reserved1 resb 1
.block_count resb 1
.reserved2 resb 1
.buffer resd 1
.offset nstruc quadword
endstruc
; use code available in stage 1
%define printstr printStringStage1
stage1:
mov ax, 0x07C0 ; BOOT_BLOCK_START_ADDRESS / 16
mov ds, ax ; Setup segment registers
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
sub bl, al ; Substract from 40 (screen is 80x25)
mov al, bl
mov dx, [position] ; Get last position
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 ax, TICKS_PER_SECOND ; Calculate timeout ticks
mov [position], dx mul word [timeout]
int 0x10 mov bx, dx
push ax
mov dx, [selection] mov ah, READ_CLOCK
mov byte bl, [colour] ; Load Colour int BIOS_TIME_SERVICES
and bx, 0x03 ; Just
inc bl ; Colours ... 1-4 pop ax ; Add current ticks
cmp cx, [selection] add ax, dx
jnz no_highlight adc bx, cx
xor bx, 0x08 ; Highlight selected item mov [timeoutTicks], ax
no_highlight: mov [timeoutTicks + 2], bx
mov byte [colour], bl ; Store Colour
call print_list_item mov al, [listItemCount] ; Calculate start row for menu
inc cx shr al, 1
cmp cx, [list_items] mov bl, TEXT_ROWS / 2
jne print_list_loop sub bl, al ; y = TEXT_ROWS / 2 - number of items / 2
mov [firstLine], bl
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
inputLoop:
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES ; AL = ASCII Code, AH = Scancode
main_loopy: cmp ah, KEY_DOWN
mov ah, 0x02 ; Set cursor position je selectNextPartition
mov dx, 0x181C
int 0x10 cmp ah, KEY_UP
je selectPreviousPartition
mov ah, 0x00 ; Keyboard Read: wait for key
int 0x16 ; AL = ASCII Code, AH = Scancode cmp ah, KEY_RETURN
cmp ah, 0x50 je bootSelectedPartition
jz key_down ; Was it key_down, key_up... ?
cmp ah, 0x48 jmp inputLoop
jz key_up
cmp ah, 0x1C
jz key_enter
jmp main_loopy selectNextPartition:
mov ax, [selection]
inc ax
cmp ax, [listItemCount]
jne .done ; At end of list?
xor ax, ax ; Then jump to first entry
.done:
mov [selection], ax
jmp mainLoop
selectPreviousPartition:
mov ax, [selection]
or ax, ax
jnz .done ; At top of list?
mov ax, [listItemCount] ; Then jump to last entry
.done:
dec ax
mov [selection], ax
jmp mainLoop
key_down:
mov ax, [selection] ; ======================= Print the OS list ============================
mov bx, [list_items] printMenu:
dec bx mov al, [firstLine]
cmp ax, bx mov [currentLine], al
je key_down_no ; Are we at the bottom of the list?
inc ax mov si, list ; Start at top of list
mov [selection], ax xor cx, cx ; The index of the current item
key_down_no:
jmp print_list .loop:
key_up: lodsb ; String length incl. 0-terminator
mov ax, [selection] dec al ; center menu item
mov bx, [list_items] shr al, 1 ; x = TEXT_COLUMNS / 2 - length / 2
cmp ax, 0 mov dl, TEXT_COLUMNS / 2
je key_down_no ; Are we at the top of the list? sub dl, al
dec ax
mov [selection], ax mov dh, [currentLine]
key_up_no: mov ah, SET_CURSOR
jmp print_list 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 cx, [selection]
mov si, list mov si, list ; Search address of start sector
inc cx ; of the selected item.
load_LBA: mov cx, [selection]
lodsb inc cx ; Number of required iterations
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 ah, EXTENDED_READ ; Now read start sector from HD
mov dl, 0x80 ; overwriting our first stage
mov si, bootSectorDAP
int BIOS_DISK_SERVICES
mov si, kReadError
jc printAndHalt ; Failed to read sector
mov ax, [signature]
cmp ax, MBR_SIGNATURE
mov si, kNoBootablePartitionError
jne printAndHalt ; Missing signature
mov si, ChainLoad_DAP ; Load the List jmp $$ ; Start loaded boot loader
mov ax, [LBA_selectionA]
mov word [si+8], ax
mov ax, [LBA_selectionB]
mov word [si+10], ax
mov ah, 0x42
mov dl, 0x80
mov si, ChainLoad_DAP
int 0x13
mov si, error_lfd
jc print_debug
mov si, 0x0
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 ================
fin: printStringStage2:
mov ah, 0x0e printString
mov al, 'F'
mov bx, 0x2
mov cx, 0x1
int 0x10
jmp $
printAndHalt:
; ====================== THE print_list function ================ mov bx, 0x0F ; Page number and foreground color
call printstr
print_list_item: .halt
print_list_item_loop: mov ah, READ_CHAR
lodsb int BIOS_KEYBOARD_SERVICES
cmp al,0x80 hlt
jz print_list_item_end jmp .halt
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: selection:
db 'Error loading Sectors', 0x00 dw 0x0000
error_nb: firstLine:
db 'Not a bootable partition', 0x00 db 0
LBA_selectionA: currentLine:
dw 0x0000 db 0
LBA_selectionB: timeoutTicks:
dw 0x0000 dw 0, 0
selection:
dw 0x0000 bootSectorDAP:
position: istruc AddressPacket
dw 0x091D at AddressPacket.packet_size, db 0x10
lenght: at AddressPacket.block_count, db 0x01
dw 0x0000 at AddressPacket.buffer, dw 0x0000, 0x07c0
colour: iend
dw 0x0000
first_position: kColorTable:
dw 0x091F db BLUE, RED, CYAN, GREEN
nobots: kReadError:
db 'Boot sector signature not found', 0x00 db 'Error loading sectors', 0x00
error: kNoBootablePartitionError:
db 'Error loading sectors', 0x00 db 'Not a bootable partition', 0x00
yeah: kBootMessage:
db 'Boot!', 0x00 db 'Boot!', 0x00
listItemCount:
defaultItem equ listItemCount + 2
timeout equ defaultItem + 2
list equ timeout + 2
; 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
list_items: %if USE_TEST_MENU
dw 0x06 dw 0x06
list:
db 0x05 dw 2
db 'HAIKU' , 0x80
dw 0x003E dw 5
dw 0x0000
db 0x07 db 0x06
db 'FreeBSD' , 0x80 db 'HAIKU', 0
dw 0x003F dw 1, 0, 0, 0
dw 0x0000
db 0x03 db 0x08
db 'DOS' , 0x80 db 'FreeBSD', 0
dw 0x003E dw 0x003F, 0, 0, 0
dw 0x0000
db 0x05 db 0x04
db 'LINUX' , 0x80 db 'DOS', 0
dw 0x003F dw 0x003E, 0, 0, 0
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
db 0x06
db 'LINUX', 0
dw 0x003F, 0, 0, 0
db 0x08
db 'BeOS R5', 0
dw 0x003F, 0, 0, 0
db 0x07
db 'OpenBSD', 0
dw 0xAAAA, 0, 0, 0
%endif