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