xref: /haiku/src/apps/bootmanager/bootman.S (revision 529cd177b573aaba391c8adc9c9f5ad76a14bf81)
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