     1                                  ; This is an LBA-enabled FreeDOS FAT32 boot sector (single sector!).
     2                                  ; You can use and copy source code and binaries under the terms of the
     3                                  ; GNU Public License (GPL), version 2 or newer. See www.gnu.org for more.
     4                                  
     5                                  ; Based on earlier work by FreeDOS kernel hackers, modified heavily by
     6                                  ; Eric Auer and Jon Gentle in 7 / 2003.
     7                                  ;
     8                                  ; Features: Uses LBA and calculates all variables from BPB/EBPB data,
     9                                  ; thus making partition move / resize / image-restore easier. FreeDOS
    10                                  ; can boot from FAT32 partitions which start > 8 GB boundary with this
    11                                  ; boot sector. Disk geometry knowledge is not needed for booting.
    12                                  ;
    13                                  ; Windows uses 2-3 sectors for booting (sector stage, statistics sector,
    14                                  ; filesystem stage). Only using 1 sector for FreeDOS makes multi-booting
    15                                  ; of FreeDOS and Windows on the same filesystem easier.
    16                                  ;
    17                                  ; Requirements: LBA BIOS and 386 or better CPU. Use the older CHS-only
    18                                  ; boot sector if you want FAT32 on really old PCs (problems: you cannot
    19                                  ; boot from > 8 GB boundary, cannot move / resize / ... without applying
    20                                  ; SYS again if you use the CHS-only FAT32 boot sector).
    21                                  ;
    22                                  ; FAT12 / FAT16 hints: Use the older CHS-only boot sector unless you
    23                                  ; have to boot from > 8 GB. The LBA-and-CHS FAT12 / FAT16 boot sector
    24                                  ; needs applying SYS again after move / resize / ... a variant of that
    25                                  ; boot sector without CHS support but with better move / resize / ...
    26                                  ; support would be good for use on LBA harddisks.
    27                                  
    28                                  
    29                                  ; Memory layout for the FreeDOS FAT32 single stage boot process:
    30                                  
    31                                  ;	...
    32                                  ;	|-------| 1FE0:7E00
    33                                  ;	|BOOTSEC|
    34                                  ;	|RELOC.	|
    35                                  ;	|-------| 1FE0:7C00
    36                                  ;	...
    37                                  ;	|-------| 2000:0200
    38                                  ;	|  FAT  | (only 1 sector buffered)
    39                                  ;	|-------| 2000:0000
    40                                  ;	...
    41                                  ;	|-------| 0000:7E00
    42                                  ;	|BOOTSEC| overwritten by the kernel, so the
    43                                  ;	|ORIGIN | bootsector relocates itself up...
    44                                  ;	|-------| 0000:7C00
    45                                  ;	...
    46                                  ;	|-------|
    47                                  ;	|KERNEL	| maximum size 134k (overwrites bootsec origin)
    48                                  ;	|LOADED	| (holds 1 sector directory buffer before kernel load)
    49                                  ;	|-------| 0060:0000
    50                                  ;	...
    51                                  
    52                                  segment	.text
    53                                  
    54                                  		org	0x7c00		; this is a boot sector
    55                                  
    56 00000000 EB58                    Entry:		jmp	short real_start
    57 00000002 90                      		nop
    58                                  
    59                                  ;	bp is initialized to 7c00h
    60                                  ; %define bsOemName	bp+0x03	; OEM label (8)
    61                                  %define bsBytesPerSec	bp+0x0b ; bytes/sector (dw)
    62                                  %define bsSecPerClust	bp+0x0d	; sectors/allocation unit (db)
    63                                  %define bsResSectors	bp+0x0e	; # reserved sectors (dw)
    64                                  %define bsFATs		bp+0x10	; # of fats (db)
    65                                  ; %define bsRootDirEnts	bp+0x11	; # of root dir entries (dw, 0 for FAT32)
    66                                  			; (FAT32 has root dir in a cluster chain)
    67                                  ; %define bsSectors	bp+0x13	; # sectors total in image (dw, 0 for FAT32)
    68                                  			; (if 0 use nSectorHuge even if FAT16)
    69                                  ; %define bsMedia	bp+0x15	; media descriptor: fd=2side9sec, etc... (db)
    70                                  ; %define sectPerFat	bp+0x16	; # sectors in a fat (dw, 0 for FAT32)
    71                                  			; (FAT32 always uses xsectPerFat)
    72                                  %define sectPerTrack	bp+0x18	; # sectors/track
    73                                  ; %define nHeads	bp+0x1a	; # heads (dw)
    74                                  %define nHidden		bp+0x1c	; # hidden sectors (dd)
    75                                  ; %define nSectorHuge	bp+0x20	; # sectors if > 65536 (dd)
    76                                  %define xsectPerFat	bp+0x24	; Sectors/Fat (dd)
    77                                  			; +0x28 dw flags (for fat mirroring)
    78                                  			; +0x2a dw filesystem version (usually 0)
    79                                  %define xrootClst	bp+0x2c	; Starting cluster of root directory (dd)
    80                                  			; +0x30 dw -1 or sector number of fs.-info sector
    81                                  			; +0x32 dw -1 or sector number of boot sector backup
    82                                  			; (+0x34 .. +0x3f reserved)
    83                                  %define drive		bp+0x40	; Drive number
    84                                  %define loadsegoff_60	bp+loadseg_off-Entry
    85                                  
    86                                  %define LOADSEG		0x0060
    87                                  
    88                                  %define FATSEG		0x2000
    89                                  
    90                                  %define	fat_secshift	fat_afterss-1	; each fat sector describes 2^??
    91                                  					; clusters (db) (selfmodifying)
    92                                  %define fat_sector	bp+0x44		; last accessed FAT sector (dd)
    93                                  					; (overwriting unused bytes)
    94                                  %define fat_start	bp+0x48		; first FAT sector (dd)
    95                                  					; (overwriting unused bytes)
    96                                  %define data_start	bp+0x4c		; first data sector (dd)
    97                                  					; (overwriting unused bytes)
    98                                  
    99 00000003 00<rep 4Fh>             		times   52h - ($ - $$) db 0
   100                                  		; The filesystem ID is used by lDOS's instsect (by ecm)
   101                                  		;  by default to validate that the filesystem matches.
   102 00000052 4641543332              		db "FAT32"
   103 00000057 20<rep 3h>              		times   5Ah - ($ - $$) db 32
   104                                  		; not used: [0x42] = byte 0x29 (ext boot param flag)
   105                                  		; [0x43] = dword serial
   106                                  		; [0x47] = label (padded with 00, 11 bytes)
   107                                  		; [0x52] = "FAT32",32,32,32 (not used by Windows)
   108                                  		; ([0x5a] is where FreeDOS parts start)
   109                                  
   110                                  ;-----------------------------------------------------------------------
   111                                  ; ENTRY
   112                                  ;-----------------------------------------------------------------------
   113                                  
   114 0000005A FC                      real_start:	cld
   115 0000005B FA                      		cli
   116 0000005C 29C0                    		sub	ax, ax
   117 0000005E 8ED8                    		mov	ds, ax
   118 00000060 BD007C                  		mov	bp, 0x7c00
   119                                  
   120 00000063 B8E01F                  		mov	ax, 0x1FE0
   121 00000066 8EC0                    		mov	es, ax
   122 00000068 89EE                    		mov	si, bp
   123 0000006A 89EF                    		mov	di, bp
   124 0000006C B90001                  		mov	cx, 0x0100
   125 0000006F F3A5                    		rep	movsw		; move boot code to the 0x1FE0:0x0000
   126 00000071 EA[7A00]E01F            		jmp	word 0x1FE0:cont
   127                                  
   128 00000076 00006000                loadseg_off	dw	0, LOADSEG
   129                                  
   130                                  ; -------------
   131                                  
   132 0000007A 8ED8                    cont:		mov	ds, ax
   133 0000007C 8ED0                    		mov	ss, ax		; stack and BP-relative moves up, too
   134 0000007E 8D66E0                                  lea     sp, [bp-0x20]
   135 00000081 FB                      		sti
   136 00000082 885640                  		mov	[drive], dl	; BIOS passes drive number in DL
   137                                  
   138                                  %ifndef QUIET
   139 00000085 BE[C601]                		mov	si, msg_LoadFreeDOS
   140 00000088 E8F900                  		call	print		; modifies AX BX SI
   141                                  %else ; ensure code after this still at same location
   142                                  		times 6 nop
   143                                  %endif
   144                                  
   145                                  
   146                                  ; -------------
   147                                  
   148                                  ;	CALCPARAMS: figure out where FAT and DATA area starts
   149                                  ;	(modifies EAX EDX, sets fat_start and data_start variables)
   150                                  
   151 0000008B 6631C0                  calc_params:	xor	eax, eax
   152 0000008E 66894644                		mov	[fat_sector], eax	; init buffer status
   153                                  
   154                                  		; first, find fat_start:
   155 00000092 8B460E                  		mov	ax, [bsResSectors]	; no movzx eax, word... needed
   156 00000095 6603461C                		add	eax, [nHidden]
   157 00000099 66894648                		mov 	[fat_start], eax	; first FAT sector
   158 0000009D 6689464C                		mov	[data_start], eax	; (only first part of value)
   159                                  
   160                                  		; next, find data_start:
   161 000000A1 668B4610                		mov	eax, [bsFATs]		; no movzx ... byte needed:
   162                                  		; the 2 dw after the bsFATs db are 0 by FAT32 definition :-).
   163 000000A5 66F76E24                		imul	dword [xsectPerFat]	; (also changes edx)
   164 000000A9 6601464C                		add	[data_start], eax	; first DATA sector
   165                                  						; (adding in RAM is shorter!)
   166                                  
   167                                  		; finally, find fat_secshift:
   168 000000AD B80002                  		mov	ax, 512	; default sector size (means default shift)
   169                                  				; shift = log2(secSize) - log2(fatEntrySize)
   170                                  ;---		mov	cl, 9-2	; shift is 7 for 512 bytes per sector
   171 000000B0 3B460B                  fatss_scan:	cmp	ax, [bsBytesPerSec]
   172 000000B3 7408                    		jz	fatss_found
   173 000000B5 01C0                    		add	ax,ax
   174                                  ;---		inc	cx
   175 000000B7 FF06[3601]              		inc	word [fat_secshift]	;XXX	; initially 9-2 (byte!)
   176 000000BB EBF3                    		jmp 	short fatss_scan	; try other sector sizes
   177                                  fatss_found:
   178                                  ;---		mov	[fat_secshift], cl
   179                                  
   180                                  ; -------------
   181                                  
   182                                  ; FINDFILE:	Searches for the file in the root directory.
   183                                  ; Returns:	EAX = first cluster of file
   184                                  
   185 000000BD 668B462C                		mov	eax, [xrootClst]	; root dir cluster
   186                                  
   187 000000C1 6650                    ff_next_clust:	push	eax			; save cluster
   188 000000C3 E89600                  		call	convert_cluster
   189 000000C6 724F                    		jc	boot_error		; EOC encountered
   190                                  		; EDX is clust/sector, EAX is sector
   191                                  				
   192 000000C8 C45E76                  ff_next_sector:	les	bx, [loadsegoff_60]	; load to loadseg:0
   193 000000CB E8BC00                  		call	readDisk
   194                                  ;---		push	eax			; save sector
   195                                  
   196                                  ;---		xor	ax, ax		; first dir. entry in this sector
   197 000000CE 31FF                    		xor	di, di			;XXX
   198                                  
   199                                  		; Search for KERNEL.SYS file name, and find start cluster.
   200 000000D0 B90B00                  ff_next_entry:	mov	cx, 11
   201 000000D3 BE[F101]                		mov	si, filename
   202                                  ;---		mov	di, ax
   203 000000D6 F3A6                    		repe	cmpsb
   204 000000D8 7415                    		jz	ff_done		; note that di now is at dirent+11
   205                                  
   206                                  ;---		add	ax, 0x20		; next directory entry
   207                                  ;---		cmp 	ax, [bsBytesPerSec]	; end of sector reached?
   208 000000DA 83C720                  		add	di, byte 0x20		;XXX
   209 000000DD 83E7E0                  		and	di, byte -0x20 ; 0xffe0	;XXX
   210 000000E0 3B7E0B                  		cmp	di, [bsBytesPerSec]	;XXX
   211 000000E3 75EB                    		jnz	ff_next_entry
   212                                  
   213                                  ;---		pop	eax		; restore sector
   214 000000E5 4A                      		dec 	dx		; next sector in cluster
   215 000000E6 75E0                    		jnz	ff_next_sector
   216                                  
   217 000000E8 6658                    ff_walk_fat:	pop	eax			; restore current cluster
   218 000000EA E83600                  		call	next_cluster		; find next cluster
   219 000000ED EBD2                    		jmp	ff_next_clust
   220                                  
   221 000000EF 26FF7509                ff_done:	push	word [es:di+0x14-11]	; get cluster number HI
   222 000000F3 26FF750F                		push	word [es:di+0x1A-11]	; get cluster number LO
   223 000000F7 6658                    		pop	eax			; convert to 32bit
   224                                  
   225 000000F9 29DB                    		sub	bx, bx			; ES points to LOADSEG
   226                                  						; (kernel -> ES:BX)
   227                                  
   228                                  ; -------------
   229                                  
   230 000000FB 6650                    read_kernel:	push	eax
   231 000000FD E85C00                  		call	convert_cluster
   232 00000100 720D                    		jc	boot_success		; EOC encountered - done
   233                                  		; EDX is sectors in cluster, EAX is sector
   234                                  
   235 00000102 E88500                  rk_in_cluster:	call	readDisk
   236 00000105 4A                      		dec	dx
   237 00000106 75FA                    		jnz	rk_in_cluster		; loop over sect. in cluster
   238                                  
   239 00000108 6658                    rk_walk_fat:	pop	eax
   240 0000010A E81600                  		call	next_cluster
   241 0000010D EBEC                    		jmp	read_kernel
   242                                  		
   243                                  ;-----------------------------------------------------------------------
   244                                  
   245                                  boot_success:
   246 0000010F 8A5640                  		mov dl, [drive]			; for Enhanced DR-DOS load
   247 00000112 88D3                    		mov bl, dl			; for FreeDOS load
   248 00000114 FF6E76                  		jmp	far [loadsegoff_60]
   249                                  
   250                                  ;-----------------------------------------------------------------------
   251                                  
   252 00000117 BE[EE01]                boot_error:	mov	si, msg_BootError
   253 0000011A E86700                  		call	print			; modifies AX BX SI
   254                                  
   255 0000011D 30E4                    wait_key:	xor	ah,ah
   256 0000011F CD16                    		int	0x16			; wait for a key
   257 00000121 CD19                    reboot:		int	0x19			; reboot the machine
   258                                  
   259                                  ;-----------------------------------------------------------------------
   260                                  
   261                                  ; given a cluster number, find the number of the next cluster in
   262                                  ; the FAT chain. Needs fat_secshift and fat_start.
   263                                  ; input:	EAX - cluster
   264                                  ; output:	EAX - next cluster
   265                                  
   266 00000123 06                      next_cluster:	push	es
   267 00000124 57                      		push	di
   268 00000125 53                      		push	bx
   269                                  
   270 00000126 89C7                    		mov	di, ax
   271 00000128 C1E702                  		shl	di, 2			; 32bit FAT
   272                                  
   273 0000012B 50                      		push	ax
   274 0000012C 8B460B                  		mov	ax, [bsBytesPerSec]
   275 0000012F 48                      		dec	ax
   276 00000130 21C7                    		and	di, ax			; mask to sector size
   277 00000132 58                      		pop	ax
   278                                  
   279 00000133 66C1E807                		shr	eax, 7			; e.g. 9-2 for 512 by/sect.
   280                                  fat_afterss:	; selfmodifying code: previous byte is patched!
   281                                  		; (to hold the fat_secshift value)
   282                                  
   283 00000137 66034648                		add	eax, [fat_start]	; absolute sector number now
   284                                  
   285 0000013B BB0020                  		mov	bx, FATSEG
   286 0000013E 8EC3                    		mov	es, bx
   287 00000140 29DB                    		sub	bx, bx
   288                                  
   289 00000142 663B4644                		cmp	eax, [fat_sector]	; already buffered?
   290 00000146 7407                    		jz	cn_buffered
   291 00000148 66894644                		mov	[fat_sector],eax	; number of buffered sector
   292 0000014C E83B00                  		call	readDisk
   293                                  
   294 0000014F 268065030F              cn_buffered:	and	byte [es:di+3],0x0f	; mask out top 4 bits
   295 00000154 26668B05                		mov	eax, [es:di]		; read next cluster number
   296                                  
   297 00000158 5B                      		pop	bx
   298 00000159 5F                      		pop 	di
   299 0000015A 07                      		pop	es
   300 0000015B C3                      		ret
   301                                  
   302                                  
   303                                  ;-----------------------------------------------------------------------
   304                                  
   305                                  ; Convert cluster number to the absolute sector number
   306                                  ; ... or return carry if EndOfChain! Needs data_start.
   307                                  ; input:	EAX - target cluster
   308                                  ; output:	EAX - absolute sector
   309                                  ;		EDX - [bsSectPerClust] (byte)
   310                                  ;		carry clear
   311                                  ;		(if carry set, EAX/EDX unchanged, end of chain)
   312                                  
   313                                  convert_cluster:
   314 0000015C 663DF8FFFF0F            		cmp	eax, 0x0ffffff8	; if end of cluster chain...
   315 00000162 7318                    		jnb	end_of_chain
   316                                  
   317                                  		; sector = (cluster-2) * clustersize + data_start
   318 00000164 6648                    		dec	eax
   319 00000166 6648                    		dec	eax
   320                                  
   321 00000168 660FB6560D              		movzx	edx, byte [bsSecPerClust]
   322 0000016D FECA                                    dec     dl              ; spc - 1
   323 0000016F 42                                      inc     dx              ; spc
   324 00000170 6652                    		push	edx
   325 00000172 66F7E2                  		mul	edx
   326 00000175 665A                    		pop	edx
   327 00000177 6603464C                		add	eax, [data_start]
   328                                  		; here, carry is unset (unless parameters are wrong)
   329 0000017B C3                      		ret
   330                                  
   331 0000017C F9                      end_of_chain:	stc			; indicate EOC by carry
   332 0000017D C3                      		ret
   333                                  
   334                                  ;-----------------------------------------------------------------------
   335                                  
   336                                  ; PRINT - prints string DS:SI
   337                                  ; modifies AX BX SI
   338                                  
   339 0000017E 31DB                    printchar:	xor	bx, bx		; video page 0
   340 00000180 B40E                    		mov	ah, 0x0e	; print it
   341 00000182 CD10                    		int	0x10		; via TTY mode
   342 00000184 AC                      print:		lodsb			; get token
   343 00000185 3C00                    		cmp	al, 0		; end of string?
   344 00000187 75F5                    		jne	printchar	; until done
   345 00000189 C3                      		ret			; return to caller
   346                                  
   347                                  ;-----------------------------------------------------------------------
   348                                  
   349                                  ; Read a sector from disk, using LBA
   350                                  ; input:	EAX - 32-bit DOS sector number
   351                                  ;		ES:BX - destination buffer
   352                                  ;		(will be filled with 1 sector of data)
   353                                  ; output:	ES:BX points one byte after the last byte read.
   354                                  ;		EAX - next sector
   355                                  
   356 0000018A 52                      readDisk:	push	dx
   357 0000018B 56                      		push	si
   358 0000018C 57                      		push	di
   359                                  
   360 0000018D 6650                    read_next:	push	eax	; would ax be enough?
   361 0000018F 89E7                    		mov	di, sp	; remember parameter block end
   362                                  
   363                                  ;---		db	0x66	; operand size override (push dword)
   364 00000191 6A00                    		push	byte 0	;XXX	; other half of the 32 bits at [C]
   365                                  				; (did not trust "o32 push byte 0" opcode)
   366 00000193 6A00                    		push	byte 0	; [C] sector number high 32bit
   367 00000195 6650                    		push	eax	; [8] sector number low 32bit
   368 00000197 06                      		push	es	; [6] buffer segment
   369 00000198 53                      		push	bx	; [4] buffer offset
   370 00000199 6A01                    		push	byte 1	; [2] 1 sector (word)
   371 0000019B 6A10                    		push	byte 16	; [0] size of parameter block (word)
   372 0000019D 89E6                    		mov	si, sp
   373 0000019F 8A5640                  		mov	dl, [drive]
   374 000001A2 B442                    		mov	ah, 42h	; disk read
   375 000001A4 CD13                    		int	0x13	
   376                                  
   377 000001A6 89FC                    		mov	sp, di	; remove parameter block from stack
   378                                  				; (without changing flags!)
   379 000001A8 6658                    		pop	eax	; would ax be enough?
   380                                  
   381 000001AA 7308                    		jnc	read_ok		; jump if no error
   382                                  
   383 000001AC 50                      		push	ax			; !!
   384 000001AD 30E4                    		xor	ah, ah		; else, reset and retry
   385 000001AF CD13                    		int	0x13
   386 000001B1 58                      		pop	ax			; !!
   387 000001B2 EBD9                    		jmp	read_next
   388                                  
   389 000001B4 6640                    read_ok:	inc 	eax			; next sector
   390 000001B6 035E0B                  		add	bx, word [bsBytesPerSec]
   391 000001B9 7307                    		jnc	no_incr_es		; if overflow...
   392                                  
   393 000001BB 8CC2                    		mov	dx, es
   394 000001BD 80C610                  		add	dh, 0x10		; ...add 1000h to ES
   395 000001C0 8EC2                    		mov	es, dx
   396                                  
   397 000001C2 5F                      no_incr_es:	pop	di
   398 000001C3 5E                      		pop 	si
   399 000001C4 5A                      		pop	dx
   400 000001C5 C3                      		ret
   401                                  
   402                                  ;-----------------------------------------------------------------------
   403                                  
   404 000001C6 4C6F6164696E672046-     msg_LoadFreeDOS db "Loading FreeDOS ",0
   404 000001CF 726565444F532000   
   405                                  
   406 000001D7 00<rep 17h>                    times 0x01ee-$+$$ db 0
   407                                  
   408 000001EE 4E6F20                  msg_BootError	db "No "
   409                                  		; currently, only "kernel.sys not found" gives a message,
   410                                  		; but read errors in data or root or fat sectors do not.
   411                                  
   412 000001F1 4B45524E454C202053-     filename	db "KERNEL  SYS"
   412 000001FA 5953               
   413                                  
   414 000001FC 000055AA                sign		dw 0, 0xAA55
   415                                  		; Win9x uses all 4 bytes as magic value here.
