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

View File

@ -1,376 +1,561 @@
;
; Copyright 2007, Dengg David, david-d@gmx.at. All rights reserved.
; Copyright 2008, Michael Pfeiffer, laplace@users.sourceforge.net. All rights reserved.
; Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
; Distributed under the terms of the MIT License.
;
%assign BIOS_VIDEO_SERVICES 0x10
%assign BIOS_KEYBOARD_SERVICES 0x16
%assign WRITE_CHAR 0x0e ; al - char
; 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_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
; bl - foreground color (graphics mode only)
[bits 16] ; Real Mode yeah
start:
; ======================= SCREEN SETUP =================================
mov ax, 0x07C0 ; Segment Location 0x07C0
mov ds, ax ; Set Data Segment to 0x07C0
mov es, ax ; Set Extra Segment to 0x07C0
mov ss, ax ; Set Stack Segment to 0x07C0
mov sp, 0xFFFF
; disk services
%assign READ_DISK_SECTORS 0x02 ; dl - drive
; es:bx - buffer
; dh - head (0 - 15)
; ch - track 7:0 (0 - 1023)
; cl - track 9:8,
; sector (1 - 17)
; 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
mov al, 0x12
mov bx, 0x0
mov cx, 0x1
int 0x10
%assign FIXED_DISK_SUPPORT 0x1 ; flag indicating fixed disk
; extension command subset
mov ah, 0x02 ; Set cursor position
mov dx, 0x0120
int 0x10
; keyboard services
%assign READ_CHAR 0x00 ; -> al - ASCII char
; 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"
call printstr
; Key codes
%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
mov dx, 0x1B1B
int 0x10
; nicer way to get the size of a structure
%define sizeof(s) s %+ _size
mov si, select_os ; Print "Select Os..."
call printstr
; using a structure in a another structure definition
%macro nstruc 1-2 1
resb sizeof(%1) * %2
%endmacro
mov ah, 0
int 0x16
%macro DEBUG_PAUSE 0
push ax
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
pop ax
%endmacro
; ======================= LOAD 3 MORE SECTORS INTO MEM =================
load_from_disk: ; load 3 more sectors into mem
mov ah, 0x42
mov dl, 0x80
mov si, DAP
int 0x13
mov si, error
jc printstr
jmp secsec ; Jump into the second sector
%macro clearScreen 0
mov ah, SCROLL_UP
xor al, al
mov bh, WHITE
xor cx, cx
mov dx, (TEXT_ROWS-1) * 0x100 + (TEXT_COLUMNS-1)
int BIOS_VIDEO_SERVICES
%endmacro
ende:
mov ah, 0x0e
mov al, 'F'
mov bx, 0x2
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
; Prints a null terminated string
; si ... offset to string
%macro printString 0
push cx
jmp .loop_condition
.loop
mov ah, WRITE_CHAR
mov bx, 0x0F
mov cx, 1
int BIOS_VIDEO_SERVICES
.loop_condition
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
.loop_condition
lodsb
cmp al, 0
jnz .loop
cmp al, 0
jnz .loop
pop cx
ret
%endif
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
%endmacro
; 64 bit value
struc quadword
.lower resd 1
.upper resd 1
endstruc
signation:
db 'Haiku Bootmanager', 0x00
select_os:
db 'Select an OS from the menu', 0x00
; address packet as required by the EXTENDED_READ BIOS call
struc AddressPacket
.packet_size resb 1
.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
dw 0xAA55 ; magic marker "AA55" (to identify a valid bootrecord)
mov sp, 0xFFFF ; Make stack empty
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 ================================
; ======================================================================
secsec:
; ======================= Center list ==================================
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
; Use code available in stage 1
%define printstr printStringStage2
mov bl, 12
sub bl, al ; Substract from 12 (screen is 80x25)
mov dh, bl
mov [first_position], dx
%assign TIMEOUT_OFF 0xffff
; ======================= Print the OS list ============================
print_list:
mov dx, [first_position]
mov [position], dx
mov ah, 0x02 ; Set cursor position
int 0x10
mov si, list ; Load the List
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)
stage2:
mov ax, [defaultItem] ; Select default item
mov [selection], ax
mov ah, 0x02 ; Set cursor position
mov [position], dx
int 0x10
mov ax, TICKS_PER_SECOND ; Calculate timeout ticks
mul word [timeout]
mov bx, dx
push ax
mov dx, [selection]
mov byte bl, [colour] ; Load Colour
and bx, 0x03 ; Just
inc bl ; Colours ... 1-4
cmp cx, [selection]
jnz no_highlight
xor bx, 0x08 ; Highlight selected item
no_highlight:
mov byte [colour], bl ; Store Colour
call print_list_item
inc cx
cmp cx, [list_items]
jne print_list_loop
mov ah, READ_CLOCK
int BIOS_TIME_SERVICES
pop ax ; Add current ticks
add ax, dx
adc bx, cx
mov [timeoutTicks], ax
mov [timeoutTicks + 2], bx
mov al, [listItemCount] ; Calculate start row for menu
shr al, 1
mov bl, TEXT_ROWS / 2
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 ==================
mainLoop:
call printMenu
inputLoop:
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES ; AL = ASCII Code, AH = Scancode
main_loopy:
mov ah, 0x02 ; Set cursor position
mov dx, 0x181C
int 0x10
mov ah, 0x00 ; Keyboard Read: wait for key
int 0x16 ; AL = ASCII Code, AH = Scancode
cmp ah, 0x50
jz key_down ; Was it key_down, key_up... ?
cmp ah, 0x48
jz key_up
cmp ah, 0x1C
jz key_enter
cmp ah, KEY_DOWN
je selectNextPartition
cmp ah, KEY_UP
je selectPreviousPartition
cmp ah, KEY_RETURN
je bootSelectedPartition
jmp inputLoop
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]
mov bx, [list_items]
dec bx
cmp ax, bx
je key_down_no ; Are we at the bottom of the list?
inc ax
mov [selection], ax
key_down_no:
jmp print_list
key_up:
mov ax, [selection]
mov bx, [list_items]
cmp ax, 0
je key_down_no ; Are we at the top of the list?
dec ax
mov [selection], ax
key_up_no:
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 ==========================
key_enter:
mov cx, [selection]
mov si, list
inc cx
load_LBA:
lodsb
cmp al, 0x80
jne load_LBA
loop load_LBA
bootSelectedPartition:
mov si, list ; Search address of start sector
; of the selected item.
mov cx, [selection]
inc cx ; Number of required iterations
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
mov [LBA_selectionA], ax
lodsw
mov [LBA_selectionB], ax
stosw
loop .copy_start_sector
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
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
jmp $$ ; Start loaded boot loader
; ====================== THE printMenu function ================
fin:
mov ah, 0x0e
mov al, 'F'
mov bx, 0x2
mov cx, 0x1
int 0x10
jmp $
printStringStage2:
printString
; ====================== 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 $
printAndHalt:
mov bx, 0x0F ; Page number and foreground color
call printstr
.halt
mov ah, READ_CHAR
int BIOS_KEYBOARD_SERVICES
hlt
jmp .halt
; ================================ 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:
dw 0x0000
position:
dw 0x091D
lenght:
dw 0x0000
colour:
dw 0x0000
first_position:
dw 0x091F
nobots:
db 'Boot sector signature not found', 0x00
error:
db 'Error loading sectors', 0x00
yeah:
db 'Boot!', 0x00
selection:
dw 0x0000
firstLine:
db 0
currentLine:
db 0
timeoutTicks:
dw 0, 0
bootSectorDAP:
istruc AddressPacket
at AddressPacket.packet_size, db 0x10
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
kNoBootablePartitionError:
db 'Not a bootable partition', 0x00
kBootMessage:
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:
dw 0x06
list:
db 0x05
db 'HAIKU' , 0x80
dw 0x003E
dw 0x0000
%if USE_TEST_MENU
dw 0x06
dw 2
dw 5
db 0x07
db 'FreeBSD' , 0x80
dw 0x003F
dw 0x0000
db 0x06
db 'HAIKU', 0
dw 1, 0, 0, 0
db 0x03
db 'DOS' , 0x80
dw 0x003E
dw 0x0000
db 0x08
db 'FreeBSD', 0
dw 0x003F, 0, 0, 0
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
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 'OpenBSD', 0
dw 0xAAAA, 0, 0, 0
%endif