FreeDOS/boot/boot.asm
Bart Oldeman 9a3da2b737 Version 2.7 luchezar, tomehlert, ericauer 2003/8/5
boot.asm modified to support non-512 byte sectors (e.g. 1.2 MB NEC diskettes)
  512 byte sector size check removed (although Eric's boot32ea.asm does require
    512-byte sectors, all LBA FAT32 volumes have 512-byte sectors anyway).
  boot.asm cleaned up and modified to support Metakern (by Tom, Eric and Lucho)
  bsDriveNumber updated by SYS even #ifndef STORE_BOOT_INFO (nothing in common)

Version 2.7beta, Luchezar Georgiev, 1 August 2003
  o  Now supports and includes the following new boot sectors:
       1) the combined CHS+LBA FAT12/FAT16 boot sector by Tom Ehlert (29 July)
       2) the CHS-only FAT32 boot sector -- as patched by Jon Gentle (08 July)
       3) the LBA-only FAT32 boot sector by Eric Auer and Jon Gentle (19 July)
  o  The old combined (CHS+LBA), no-CALCPARAMS, no-resizable, non-Ghostable,
     non-standard FAT32 boot sector and STORE_BOOT_INFO is no longer used.
  o  dump_sector() showed ASCII debug printout for the old sector only - fixed.
  o  Put a boot sector only if sector size = 512 (may it not be so in Japan?!).
     If non-512 byte sectors encountered, report to the fd-kernel mailing list.
  o  Now puts boot sector before copying kernel/shell so it works in WinNT now.
  o  check_space() now implemented at last (required significant modifications)
  o  New file system (FAT type) detection method according to a MS White Paper.
  o  Now BOOTONLY works even without a preceding boot sector image file name.
  o  Some minor code cleanups.

Bart: some cleanups to the above; replaced 386 code for CHS FAT32 by 8088 code.
use boot32lb.asm instead of boot32ea.asm


git-svn-id: https://svn.code.sf.net/p/freedos/svn/kernel/trunk@652 6ac86273-5f31-0410-b378-82cca8765d1b
2003-08-08 15:01:15 +00:00

528 lines
17 KiB
NASM

;
; File:
; boot.asm
; Description:
; DOS-C boot
;
; Copyright (c) 1997;
; Svante Frey
; All Rights Reserved
;
; This file is part of DOS-C.
;
; DOS-C is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version
; 2, or (at your option) any later version.
;
; DOS-C is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
; the GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with DOS-C; see the file COPYING. If not,
; write to the Free Software Foundation, 675 Mass Ave,
; Cambridge, MA 02139, USA.
;
;
; +--------+ 1FE0:7E00
; |BOOT SEC|
; |RELOCATE|
; |--------| 1FE0:7C00
; | |
; |--------| 1FE0:3000
; | CLUSTER|
; | LIST |
; |--------| 1FE0:2000
; | |
; |--------| 0000:7E00
; |BOOT SEC| overwritten by max 128k FAT buffer
; |ORIGIN | and later by max 134k loaded kernel
; |--------| 0000:7C00
; | |
; |--------|
; |KERNEL | also used as max 128k FAT buffer
; |LOADED | before kernel loading starts
; |--------| 0060:0000
; | |
; +--------+
;%define ISFAT12 1
;%define ISFAT16 1
segment .text
%define BASE 0x7c00
org BASE
Entry: jmp short real_start
nop
; bp is initialized to 7c00h
%define bsOemName bp+0x03 ; OEM label
%define bsBytesPerSec bp+0x0b ; bytes/sector
%define bsSecPerClust bp+0x0d ; sectors/allocation unit
%define bsResSectors bp+0x0e ; # reserved sectors
%define bsFATs bp+0x10 ; # of fats
%define bsRootDirEnts bp+0x11 ; # of root dir entries
%define bsSectors bp+0x13 ; # sectors total in image
%define bsMedia bp+0x15 ; media descrip: fd=2side9sec, etc...
%define sectPerFat bp+0x16 ; # sectors in a fat
%define sectPerTrack bp+0x18 ; # sectors/track
%define nHeads bp+0x1a ; # heads
%define nHidden bp+0x1c ; # hidden sectors
%define nSectorHuge bp+0x20 ; # sectors if > 65536
%define drive bp+0x24 ; drive number
%define extBoot bp+0x26 ; extended boot signature
%define volid bp+0x27
%define vollabel bp+0x2b
%define filesys bp+0x36
%define LOADSEG 0x0060
%define FATBUF 0x2000 ; offset of temporary buffer for FAT
; chain
; Some extra variables
;%define StoreSI bp+3h ;temp store
; To save space, functions that are just called once are
; implemented as macros instead. Four bytes are saved by
; avoiding the call / ret instructions.
; GETDRIVEPARMS: Calculate start of some disk areas.
;
%macro GETDRIVEPARMS 0
mov si, word [nHidden]
mov di, word [nHidden+2]
add si, word [bsResSectors]
adc di, byte 0 ; DI:SI = first FAT sector
mov word [fat_start], si
mov word [fat_start+2], di
mov al, [bsFATs]
cbw
mul word [sectPerFat] ; DX:AX = total number of FAT sectors
add si, ax
adc di, dx ; DI:SI = first root directory sector
mov word [root_dir_start], si
mov word [root_dir_start+2], di
; Calculate how many sectors the root directory occupies.
mov bx, [bsBytesPerSec]
mov cl, 5 ; divide BX by 32
shr bx, cl ; BX = directory entries per sector
mov ax, [bsRootDirEnts]
xor dx, dx
div bx
mov word [RootDirSecs], ax ; AX = sectors per root directory
add si, ax
adc di, byte 0 ; DI:SI = first data sector
mov [data_start], si
mov [data_start+2], di
%endmacro
;-----------------------------------------------------------------------
times 0x3E-$+$$ db 0
%define loadsegoff_60 bp+0x3E
dw 0
%define loadseg_60 bp+0x40
dw LOADSEG
;%define LBA_PACKET bp+0x42
; db 10h ; size of packet
; db 0 ; const
; dw 1 ; number of sectors to read
%define LBA_PACKET bp-0x40
%define LBA_SIZE word [LBA_PACKET]
%define LBA_SECNUM word [LBA_PACKET+2]
%define LBA_OFF LBA_PACKET+4
%define LBA_SEG LBA_PACKET+6
%define LBA_SECTOR_0 word [LBA_PACKET+8 ]
%define LBA_SECTOR_16 word [LBA_PACKET+10]
%define LBA_SECTOR_32 word [LBA_PACKET+12]
%define LBA_SECTOR_48 word [LBA_PACKET+14]
%define PARAMS LBA_PACKET+0x10
%define RootDirSecs PARAMS+0x0 ; # of sectors root dir uses
%define fat_start PARAMS+0x2 ; first FAT sector
%define root_dir_start PARAMS+0x6 ; first root directory sector
%define data_start PARAMS+0x0a ; first data sector
;-----------------------------------------------------------------------
; ENTRY
;-----------------------------------------------------------------------
real_start:
cli
cld
xor ax, ax
mov ds, ax
mov bp, 0x7c00
; a reset should not be needed here
; int 0x13 ; reset drive
; int 0x12 ; get memory available in AX
; mov ax, 0x01e0
; mov cl, 6 ; move boot sector to higher memory
; shl ax, cl
; sub ax, 0x07e0
mov ax, 0x1FE0
mov es, ax
mov si, bp
mov di, bp
mov cx, 0x0100
rep movsw
jmp word 0x1FE0:cont
cont:
mov ds, ax
mov ss, ax
lea sp, [bp-0x60]
sti
;
; Some BIOS don't pass drive number in DL, so don't use it if [drive] is known
;
cmp byte [drive], 0xff ; impossible number written by SYS
jne dont_use_dl ; was SYS drive: other than A or B?
mov [drive], dl ; yes, rely on BIOS drive number in DL
dont_use_dl: ; no, rely on [drive] written by SYS
mov LBA_SIZE, 10h
mov LBA_SECNUM,1 ; initialise LBA packet constants
call print
db "FreeDOS",0
GETDRIVEPARMS
; FINDFILE: Searches for the file in the root directory.
;
; Returns:
; AX = first cluster of file
; First, read the whole root directory
; into the temporary buffer.
mov ax, word [root_dir_start]
mov dx, word [root_dir_start+2]
mov di, word [RootDirSecs]
les bx, [loadsegoff_60] ; es:bx = 60:0
call readDisk
jc jmp_boot_error
les di, [loadsegoff_60] ; es:di = 60:0
; Search for KERNEL.SYS file name, and find start cluster.
next_entry: mov cx, 11
mov si, filename
push di
repe cmpsb
pop di
mov ax, [es:di+0x1A]; get cluster number from directory entry
je ffDone
add di, byte 0x20 ; go to next directory entry
cmp byte [es:di], 0 ; if the first byte of the name is 0,
jnz next_entry ; there is no more files in the directory
jc boot_error ; fail if not found
ffDone:
push ax ; store first cluster number
; call print
; db " FAT",0
; GETFATCHAIN:
;
; Reads the FAT chain and stores it in a temporary buffer in the first
; 64 kb. The FAT chain is stored an array of 16-bit cluster numbers,
; ending with 0.
;
; The file must fit in conventional memory, so it can't be larger than
; 640 kb. The sector size must be at least 512 bytes, so the FAT chain
; can't be larger than around 3 kb.
;
; Call with: AX = first cluster in chain
; Load the complete FAT into memory. The FAT can't be larger
; than 128 kb, so it should fit in the temporary buffer.
les bx, [loadsegoff_60] ; es:bx=60:0
mov di, [sectPerFat]
mov ax, word [fat_start]
mov dx, word [fat_start+2]
call readDisk
pop ax ; restore first cluster number
jmp_boot_error: jc boot_error
; Set ES:DI to the temporary storage for the FAT chain.
push ds
pop es
mov ds, [loadseg_60]
mov di, FATBUF
next_clust: stosw ; store cluster number
mov si, ax ; SI = cluster number
%ifdef ISFAT12
; This is a FAT-12 disk.
fat_12: add si, si ; multiply cluster number by 3...
add si, ax
shr si, 1 ; ...and divide by 2
lodsw
; If the cluster number was even, the cluster value is now in
; bits 0-11 of AX. If the cluster number was odd, the cluster
; value is in bits 4-15, and must be shifted right 4 bits. If
; the number was odd, CF was set in the last shift instruction.
jnc fat_even
div word[LBA_PACKET]; luckily 16 !! -- divide the cluster number
fat_even: and ah, 0x0f ; mask off the highest 4 bits
cmp ax, 0x0ff8 ; check for EOF
jb next_clust ; continue if not EOF
%endif
%ifdef ISFAT16
; This is a FAT-16 disk. The maximal size of a 16-bit FAT
; is 128 kb, so it may not fit within a single 64 kb segment.
fat_16: mov dx, [loadseg_60]
add si, si ; multiply cluster number by two
jnc first_half ; if overflow...
add dh, 0x10 ; ...add 64 kb to segment value
first_half: mov ds, dx ; DS:SI = pointer to next cluster
lodsw ; AX = next cluster
cmp ax, 0xfff8 ; >= FFF8 = 16-bit EOF
jb next_clust ; continue if not EOF
%endif
finished: ; Mark end of FAT chain with 0, so we have a single
; EOF marker for both FAT-12 and FAT-16 systems.
xor ax, ax
stosw
push cs
pop ds
;call print
;db " Kernel",0 ; "KERNEL"
; loadFile: Loads the file into memory, one cluster at a time.
les bx, [loadsegoff_60] ; set ES:BX to load address 60:0
mov si, FATBUF ; set DS:SI to the FAT chain
cluster_next: lodsw ; AX = next cluster to read
or ax, ax ; if EOF...
je boot_success ; ...boot was successful
dec ax ; cluster numbers start with 2
dec ax
mov di, word [bsSecPerClust]
and di, 0xff ; DI = sectors per cluster
mul di
add ax, [data_start]
adc dx, [data_start+2] ; DX:AX = first sector to read
call readDisk
jnc cluster_next
boot_error: call print
db " err",0
xor ah,ah
int 0x16 ; wait for a key
int 0x19 ; reboot the machine
boot_success:
;call print
;db " GO! ",0
mov bl, [drive]
jmp far [loadsegoff_60]
; prints text after call to this function.
print_1char:
xor bx, bx ; video page 0
mov ah, 0x0E ; else print it
int 0x10 ; via TTY mode
print: pop si ; this is the first character
print1: lodsb ; get token
push si ; stack up potential return address
cmp al, 0 ; end of string?
jne print_1char ; until done
ret ; and jump to it
; readDisk: Reads a number of sectors into memory.
;
; Call with: DX:AX = 32-bit DOS sector number
; DI = number of sectors to read
; ES:BX = destination buffer
; ES must be 64k aligned (1000h, 2000h etc).
;
; Returns: CF set on error
; ES:BX points one byte after the last byte read.
readDisk: push si
mov LBA_SECTOR_0,ax
mov LBA_SECTOR_16,dx
mov word [LBA_SEG],es
mov word [LBA_OFF],bx
read_next:
;******************** LBA_READ *******************************
; check for LBA support
mov ah,041h ;
mov bx,055aah ;
mov dl, [drive]
test dl,dl ; don't use LBA addressing on A:
jz read_normal_BIOS ; might be a (buggy)
; CDROM-BOOT floppy emulation
int 0x13
jc read_normal_BIOS
shr cx,1 ; CX must have 1 bit set
sbb bx,0aa55h - 1 ; tests for carry (from shr) too!
jne read_normal_BIOS
; OK, drive seems to support LBA addressing
lea si,[LBA_PACKET]
; setup LBA disk block
mov LBA_SECTOR_32,bx
mov LBA_SECTOR_48,bx
mov ah,042h
jmp short do_int13_read
read_normal_BIOS:
;******************** END OF LBA_READ ************************
mov ax,LBA_SECTOR_0
mov dx,LBA_SECTOR_16
;
; translate sector number to BIOS parameters
;
;
; abs = sector offset in track
; + head * sectPerTrack offset in cylinder
; + track * sectPerTrack * nHeads offset in platter
;
xchg ax, cx
mov al, [sectPerTrack]
mul byte [nHeads]
xchg ax, cx
; cx = nHeads * sectPerTrack <= 255*63
; dx:ax = abs
div cx
; ax = track, dx = sector + head * sectPertrack
xchg ax, dx
; dx = track, ax = sector + head * sectPertrack
div byte [sectPerTrack]
; dx = track, al = head, ah = sector
mov cx, dx
; cx = track, al = head, ah = sector
; the following manipulations are necessary in order to
; properly place parameters into registers.
; ch = cylinder number low 8 bits
; cl = 7-6: cylinder high two bits
; 5-0: sector
mov dh, al ; save head into dh for bios
xchg ch, cl ; set cyl no low 8 bits
ror cl, 1 ; move track high bits into
ror cl, 1 ; bits 7-6 (assumes top = 0)
mov al, byte [sectPerTrack]
sub al, ah ; al has # of sectors left
inc ah ; sector offset from 1
or cl, ah ; merge sector into cylinder
les bx,[LBA_OFF]
mov ax, 0x0201
do_int13_read:
mov dl, [drive]
int 0x13
read_finished:
jnc read_ok ; jump if no error
xor ah, ah ; else, reset floppy
int 0x13
read_next_chained:
jmp short read_next ; read the same sector again
read_ok:
mov ax, word [bsBytesPerSec]
div byte[LBA_PACKET] ; luckily 16 !!
add word [LBA_SEG], ax
add LBA_SECTOR_0, byte 1
adc LBA_SECTOR_16, byte 0 ; DX:AX = next sector to read
dec di ; if there is anything left to read,
jnz read_next_chained ; continue
mov es,word [LBA_SEG]
; clear carry: unnecessary since adc clears it
pop si
ret
filename db "KERNEL SYS"
times 0x01fe-$+$$ db 0
sign dw 0xAA55