858934f152
Prior to this commit the first cluster specified from the last compared directory entry would be used if the file is not found. Fix the wrong (boot.asm) or missing (oemboot.asm) jump to the error handler so that on file not found an error is displayed. Reference: https://sourceforge.net/p/freedos/bugs/97/ "Bugs in FAT12/16 boot sector loaders if file not found" This commit fixes the most crucial bug of this 2012-09-27 report.
573 lines
20 KiB
NASM
573 lines
20 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
|
|
; |LBA PKT |
|
|
; |--------| 1FE0:7BC0
|
|
; |--------| 1FE0:7BA0
|
|
; |BS STACK|
|
|
; |--------|
|
|
; |4KBRDBUF| used to avoid crossing 64KB DMA boundary
|
|
; |--------| 1FE0:63A0
|
|
; | |
|
|
; |--------| 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
|
|
;verify one and only one of ISFAT12 or ISFAT16 is defined
|
|
%ifdef ISFAT12
|
|
%ifdef ISFAT16
|
|
%error Must select one FS
|
|
%endif
|
|
%elifndef ISFAT16
|
|
%error Must select one FS
|
|
%endif
|
|
|
|
|
|
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
|
|
|
|
;-----------------------------------------------------------------------
|
|
|
|
times 36h - ($ - $$) db 0
|
|
; The filesystem ID is used by lDOS's instsect (by ecm)
|
|
; by default to validate that the filesystem matches.
|
|
%ifdef ISFAT12
|
|
%define FATFS "FAT12"
|
|
%elifdef ISFAT16
|
|
%define FATFS "FAT16"
|
|
%endif
|
|
db FATFS
|
|
times 3Eh - ($ - $$) db 32
|
|
|
|
; using bp-Entry+loadseg_xxx generates smaller code than using just
|
|
; loadseg_xxx, where bp is initialized to Entry, so bp-Entry equals 0
|
|
%define loadsegoff_60 bp-Entry+loadseg_off
|
|
%define loadseg_60 bp-Entry+loadseg_seg
|
|
|
|
%define LBA_PACKET bp-0x40
|
|
%define LBA_SIZE word [LBA_PACKET] ; size of packet, should be 10h
|
|
%define LBA_SECNUM word [LBA_PACKET+2] ; number of sectors to read
|
|
%define LBA_OFF LBA_PACKET+4 ; buffer to read/write to
|
|
%define LBA_SEG LBA_PACKET+6
|
|
%define LBA_SECTOR_0 word [LBA_PACKET+8 ] ; LBA starting sector #
|
|
%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 READBUF 0x63A0 ; max 4KB buffer (min 2KB stack), == stacktop-0x1800
|
|
%define READADDR_OFF BP-0x60-0x1804 ; pointer within user buffer
|
|
%define READADDR_SEG BP-0x60-0x1802
|
|
|
|
%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, BASE
|
|
|
|
|
|
; 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
|
|
|
|
loadseg_off dw 0
|
|
loadseg_seg dw LOADSEG
|
|
|
|
cont:
|
|
mov ds, ax
|
|
mov ss, ax
|
|
lea sp, [bp-0x60]
|
|
sti
|
|
;
|
|
; Note: some BIOS implementations may not correctly pass drive number
|
|
; in DL, however we work around this in SYS.COM by NOP'ing out the use of DL
|
|
; (formerly we checked for [drive]==0xff; update sys.c if code moves)
|
|
;
|
|
mov [drive], dl ; rely on BIOS drive number in DL
|
|
|
|
mov LBA_SIZE, 10h
|
|
mov LBA_SECNUM,1 ; initialise LBA packet constants
|
|
mov word [LBA_SEG],ds
|
|
mov word [LBA_OFF],READBUF
|
|
|
|
|
|
; GETDRIVEPARMS: Calculate start of some disk areas.
|
|
;
|
|
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
|
|
push ax
|
|
|
|
add si, ax
|
|
adc di, byte 0 ; DI:SI = first data sector
|
|
|
|
mov [data_start], si
|
|
mov [data_start+2], di
|
|
|
|
|
|
; 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]
|
|
pop di ; mov di, word [RootDirSecs]
|
|
les bx, [loadsegoff_60] ; es:bx = 60:0
|
|
call readDisk
|
|
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
|
|
|
|
jmp boot_error ; fail if not found
|
|
ffDone:
|
|
push ax ; store first cluster number
|
|
|
|
|
|
; 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 2.5 KB (655360 / 512 * 2 = 2560).
|
|
;
|
|
; Call with: AX = first cluster in chain
|
|
|
|
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
|
|
|
|
; 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
|
|
mov cl, 4
|
|
shr ax, cl
|
|
|
|
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
|
|
|
|
|
|
; 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 ; EOF?
|
|
jne load_next ; no, continue
|
|
mov bl,dl ; drive (left from readDisk)
|
|
jmp far [loadsegoff_60] ; yes, pass control to kernel
|
|
|
|
load_next: 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
|
|
jmp short cluster_next
|
|
|
|
; shows text after the call to this function.
|
|
|
|
show.do_show:
|
|
mov ah, 0Eh ; show character
|
|
int 10h ; via "TTY" mode
|
|
show: pop si
|
|
lodsb ; get character
|
|
push si ; stack up potential return address
|
|
cmp al, 0 ; end of string?
|
|
jne .do_show ; until done
|
|
ret
|
|
|
|
boot_error: call show
|
|
; db "Error! Hit a key to reboot.",0
|
|
db "Error!",0
|
|
|
|
xor ah,ah
|
|
int 0x13 ; reset floppy
|
|
int 0x16 ; wait for a key
|
|
int 0x19 ; reboot the machine
|
|
|
|
|
|
; 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
|
|
;
|
|
; 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 [READADDR_SEG], es
|
|
mov word [READADDR_OFF], bx
|
|
|
|
call show
|
|
db ".",0
|
|
read_next:
|
|
|
|
;******************** LBA_READ *******************************
|
|
|
|
; check for LBA support
|
|
|
|
mov ah,041h ;
|
|
mov bx,055aah ;
|
|
mov dl, [drive]
|
|
|
|
; NOTE: sys must be updated if location changes!!!
|
|
%ifdef ISFAT12
|
|
%define LBA_TEST_OFFSET 179h
|
|
%elifdef ISFAT16
|
|
%define LBA_TEST_OFFSET 176h
|
|
%else
|
|
%define LBA_TEST_OFFSET 0
|
|
; Just a placeholder, so the proper error message
|
|
; will be shown when assembling without either
|
|
; of the ISFATx defines.
|
|
%endif
|
|
%if ($ - Entry) != LBA_TEST_OFFSET
|
|
%error Must update constant offset (LBA_TEST_OFFSET) to test dl,dl here and in sys.c for FATFS
|
|
%endif
|
|
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 ; bx is 0 if extended 13h mode supported
|
|
mov LBA_SECTOR_48,bx
|
|
|
|
mov ah,042h
|
|
jmp short do_int13_read
|
|
|
|
|
|
|
|
read_normal_BIOS:
|
|
|
|
;******************** END OF LBA_READ ************************
|
|
mov cx,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
|
|
;
|
|
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)
|
|
or cl, ah ; merge sector into cylinder
|
|
inc cx ; make sector 1-based (1-63)
|
|
|
|
les bx,[LBA_OFF]
|
|
mov ax, 0x0201
|
|
do_int13_read:
|
|
mov dl, [drive]
|
|
int 0x13
|
|
jc boot_error ; exit on error
|
|
|
|
mov ax, word [bsBytesPerSec]
|
|
|
|
push di
|
|
mov si,READBUF ; copy read in sector data to
|
|
les di,[READADDR_OFF] ; user provided buffer
|
|
mov cx, ax
|
|
; shr cx, 1 ; convert bytes to word count
|
|
; rep movsw
|
|
rep movsb
|
|
pop di
|
|
|
|
; div byte[LBA_PACKET] ; luckily 16 !!
|
|
mov cl, 4
|
|
shr ax, cl ; adjust segment pointer by increasing
|
|
add word [READADDR_SEG], ax ; by paragraphs read in (per sector)
|
|
|
|
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 ; continue
|
|
|
|
les bx, [READADDR_OFF]
|
|
; clear carry: unnecessary since adc clears it
|
|
pop si
|
|
ret
|
|
|
|
times 0x01f1-$+$$ db 0
|
|
|
|
filename db "KERNEL SYS",0,0
|
|
|
|
sign dw 0xAA55
|
|
|
|
%ifdef DBGPRNNUM
|
|
; DEBUG print hex digit routines
|
|
PrintLowNibble: ; Prints low nibble of AL, AX is destroyed
|
|
and AL, 0Fh ; ignore upper nibble
|
|
cmp AL, 09h ; if greater than 9, then don't base on '0', base on 'A'
|
|
jbe .printme
|
|
add AL, 7 ; convert to character A-F
|
|
.printme:
|
|
add AL, '0' ; convert to character 0-9
|
|
mov AH,0x0E ; show character
|
|
int 0x10 ; via "TTY" mode
|
|
retn
|
|
PrintAL: ; Prints AL, AX is preserved
|
|
push AX ; store value so we can process a nibble at a time
|
|
shr AL, 4 ; move upper nibble into lower nibble
|
|
call PrintLowNibble
|
|
pop AX ; restore for other nibble
|
|
push AX ; but save so we can restore original AX
|
|
call PrintLowNibble
|
|
pop AX ; restore for other nibble
|
|
retn
|
|
PrintNumber: ; Prints (in Hex) value in AX, AX is preserved
|
|
xchg AH, AL ; high byte 1st
|
|
call PrintAL
|
|
xchg AH, AL ; now low byte
|
|
call PrintAL
|
|
retn
|
|
%endif
|
|
|