Newer
Older
tree-os / src / boot / __boot.asm
; bootp.asm  Bootstrap for protected mode image
; Version 1.0, Dec 14, 1997
; Sample code
; by John S. Fine  johnfine@erols.com
; I do not place any restrictions on your use of this source code
; I do not provide any warranty of the correctness of this source code
;_____________________________________________________________________________
;
;   This program should be useable for three purposes:
;
; 1)  Use it unmodified, in the boot sector of a floppy, to load a test image
;     from the hard drive.  In many environments, a floppy boot is the easiest
;     way to have a clean boot for a pmode test.  By getting the image itself
;     from the hard drive, you eliminate most of the delays and restrictions of
;     a floppy boot.
;
; 2)  As a bootstrap tutorial, it demonstrates many of the steps required by
;     a protected mode bootstrap, and many tricks for keeping code size down
;     in a bootstrap.
;
; 3)  As the basis for a protected mode, partition level, bootstrap.  Just
;     remove some of the steps that aren't needed in your environment, and add
;     some error messages for unsupported conditions.
;_____________________________________________________________________________
;
; Documentation:
;
;   1)  Most of the documentation is in comments at the END of this file
;   2)  Some of the documentation is in a separate file:  BOOTP.TXT
;   3)  In the comments within the source code are several uses of {}, each
;       of these indicates a section of documentation at the end of this file
;       which applies to that part of the source code.
;   4)  The major way I reduced code size was to rely on left-over values in
;       registers and/or to initialize registers at points where doing so was
;       cheap.  To make that understandable, there are several lines beginning
;       with either ";>" or ";>>".  These lines describe the register contents
;       at that point in the code.  ";>" indicates that the value is required
;       by the next section of code.  ";>>" indicates that the value passes
;       through the next section unmodified and is needed later.
;_____________________________________________________________________________

	%include "gdt.inc"

	struc	DISK_PARM	;BIOS hard disk parameters
DP_cyl		resw	1
DP_heads	resb	1
		resb	5	;Obsolete
DP_ctrl		resb	1
		resb	5	;Obsolete
DP_sect		resb	1
		resb	1
	endstruc

	struc	PT_ENT		;Partition table entry
BootFlag	resb	1
BeginHead       resb	1
BeginSector     resb	1
BeginCyl        resb	1
SystemID        resb	1
EndHead         resb	1
EndSector       resb	1
EndCyl          resb	1
RelSectorLow    resw	1
RelSectorHigh   resw	1
NumSectorsLow   resw	1
NumSectorsHigh  resw	1
	endstruc

	struc	BB		;Partition Boot block
		resb	0xD	;Things we ignore
BB_clu		resb	1	;Sectors per cluster
BB_res		resw	1	;Reserved sectors
BB_fats		resb	1	;Number of FATs
BB_root		resw	1	;Root directory entries
		resb	3	;Things we ignore
BB_fat		resw	1	;Sectors per fat
	endstruc

max_xfer	equ	0x78	;Maximum number of sectors to transfer at once
				;Must be divisible by 8


SEGMENT	START USE16
	jmp short start			;Standard start of boot sector
	nop
	resb	0x3B			;Skip over parameters (set by format)

start:	
    cli
	lgdt	[cs:gdt]
	mov	al, 0x80
	mov	es, ax

	xor	eax, eax		;Segment and LBN
	mov	ds, ax			;{1B}
	mov	ss, ax
	mov	sp, start

	xor	edi, edi
	inc	di
	call read_sectors

;>  eax = 0
;>  edi = 1
;>  ds  = 0
;>  es  = 80

	mov	si, 0x9BE-PT_ENT_size	;Point at partition table
.10:	add	si, byte PT_ENT_size
	cmp	[si+BootFlag], al	;Active?
	je	.10			;No
	jg	boot_error
	mov	eax, [si+RelSectorLow]	;LBN sector number of partition
	call	read_sectors		;Read partition boot block

;>  eax = LBN of partition
;>  ebx = 0000????
;>  edi = 0000????
;>   ds = 0

	mov	bp, 0x800		;Point at partition boot block
	mov	bx, [bp+BB_res]		;Sectors before first FAT
	add	eax, ebx		;LBN of FAT
	push	eax			;Need to read the FAT later
	movzx	bx, [bp+BB_fats]	;Number of FATs
	imul	bx, [bp+BB_fat]		;Times size of FAT
	add	eax, ebx		;LBN of Root directory
	mov	di, [bp+BB_root]	;Root directory entries
	push	di			; used again later
	dec	di			;Convert to number of sectors
	shr	di, 4			;  16 directory entries per sector
	inc	di
	mov	es, bp			;Read directory at 800:0
	call	read_sectors
	add	eax, edi		;LBN of cluster 2

;>> [sp+2] = LBN of FAT
;>    [sp] = Number of root directory entries
;>>    eax = LBN of cluster 2
;>     edi = 0000????
;>>     bp = 800
;>      ds = 0
;>      es = 800

	pop	bx			;Root directory entries
	xor	di, di			;Point at directory {1C}
.20:	mov	si, file_name		;Name of file we want
	xor	ecx, ecx
	mov	cl, 11
	a32 rep cmpsb			;Found the file?
	je	found			;Yes
	add	cl, 21			;Offset to next directory entry
	add	edi, ecx		;Advance to next entry
	dec	bx			;Loop through all entries
	jnz	.20

	;Couldn't find file in directory
boot_error:
disk_error:	
	mov	ax, 0xE07		;{3}
	int	10h
	jmp short $

;>    [sp] = LBN of FAT
;>     eax = LBN of cluster 2
;>  es:edi = Pointer to end of name within directory entry
;>      bp = 800
;>      ds = 0
;>      es = 800

eof:
	mov	bh, 0x9C
	mov	es, bx			;es = 9C00
	xor	di, di			;{1E} Address of page tables WRT es
	mov	bh, 4096/256		;ebx = 4096
.10:	mov	ch, 4			;cx = 1024
	mov	al, 7
.20:	stosd
	add	eax, ebx
	int	8			;{8}
	loop	.20
	shr	eax, 2			;{4C} (first time only) 4Mb / 4 = 1Mb
	shr	si, 1			;Done just one page?
	jo	.10			;Yes

	cli				;{6}

	mov	eax, 0x9C007		;First page tbl pointer in page dir
	stosd				;{1H}
	mov	ax, (1024-3)*2
	xchg	ax, cx
	rep stosw
	mov	ax, 0xD007		;0FF800000 page tbl pointer
	stosd				;{1F}
	mov	ah, 0xE0		;Page directory self pointer
	stosd				;{1G}
	mov	al, 0
	mov	CR3, eax		;Set up page directory
	mov	eax, CR0		;Turn on paging and protected mode
	or	eax, 0x80000001
	mov	CR0, eax
	mov	cl, flat_data		;Setup ds and es
	push	cx			;{5}
	pop	ds
	mov	es, cx
	jmp dword 8:0xFF800000		;Go
	
	read_sectors:
; Input:
;	EAX = LBN
;	DI  = sector count
;	ES = segment
; Output:
;	BL = low byte of ES
;	EBX high half cleared
;	DL = 0x80
;	EDX high half cleared
;	ESI = 0
; Clobbered:
;	BH, CX, DH

	push	eax
	push	di
	push	es

.10:	push	eax		;LBN

	push	ds
	lds	si, [0x104]	;Hard drive 0 parameters
	cdq			;edx = 0
	movzx	ebx, byte [si+DP_sect]
	div	ebx		;EAX=track ;EDX=sector-1
	mov	cx, dx		;CL=sector-1 ;CH=0
	inc	cx		;CL=Sector number
	xor	dx, dx
	mov	bl, [si+DP_heads]
	pop	ds
	div	ebx

	mov	dh, dl
	mov	dl, 0x80
	xchg	ch, al
	shr	ax, 2
	or	cl, al
	mov	al, max_xfer	;AX = Maximum sectors to xfer
	cmp	ax, di		;Total is larger?
	jb	.20		;Yes: transfer maximum
	xchg	ax, di		;No:  AX=total ;DI=maximum
.20:	mov	ah, 2		;Read
	xor	bx, bx
	int	13h
	jc near	disk_error

	mov	bx, es
	add	bh, max_xfer>>3	;Advance segment
	mov	es, bx

	pop	eax
	add	eax, byte max_xfer
	sub	di, byte max_xfer
	ja	.10

	pop	es
	pop	di
	pop	eax
	xor	esi, esi
	ret	

gdt	start_gdt

flat_code	desc	0, 0xFFBFF, D_CODE+D_READ+D_BIG+D_BIG_LIM

flat_data	desc	0, 0xFFFFF, D_DATA+D_WRITE+D_BIG+D_BIG_LIM

	end_gdt

	resb 0x1FE+$$-$
	db	0x55, 0xAA