mirror of https://github.com/FDOS/kernel.git
1821 lines
47 KiB
NASM
1821 lines
47 KiB
NASM
|
|
%if 0
|
|
|
|
File system boot sector loader code for FAT32
|
|
|
|
Adapted from 2002-11-26 fatboot.zip/fat12.asm,
|
|
released as public domain by Chris Giese
|
|
|
|
Public domain by C. Masloch, 2012
|
|
|
|
%endif
|
|
|
|
|
|
%include "lmacros2.mac"
|
|
|
|
%ifndef _MAP
|
|
%elifempty _MAP
|
|
%else ; defined non-empty, str or non-str
|
|
[map all _MAP]
|
|
%endif
|
|
|
|
defaulting
|
|
|
|
strdef OEM_NAME, " lDOS"
|
|
strdef OEM_NAME_FILL, '_'
|
|
strdef DEFAULT_LABEL, "lDOS"
|
|
numdef VOLUMEID, 0
|
|
strdef FSIBOOTNAME, "FSIBOOT4"
|
|
; used to set experimental name
|
|
; strdef FSIBOOTNAME, "FSIBEX02"
|
|
|
|
strdef LOAD_NAME, "LDOS"
|
|
strdef LOAD_EXT, "COM" ; name of file to load
|
|
numdef LOAD_ADR, 02000h ; where to load
|
|
numdef LOAD_MIN_PARA, paras(1536)
|
|
numdef EXEC_SEG_ADJ, 0 ; how far cs will be from _LOAD_ADR
|
|
numdef EXEC_OFS, 400h ; what value ip will be
|
|
numdef CHECKOFFSET, 1020
|
|
numdef CHECKVALUE, "lD"
|
|
numdef LOAD_DIR_SEG, 0 ; => where to store dir entry (0 if nowhere)
|
|
numdef ADD_SEARCH, 0 ; whether to search second file
|
|
strdef ADD_NAME, ""
|
|
strdef ADD_EXT, "" ; name of second file to search
|
|
numdef ADD_DIR_SEG, 0 ; => where to store dir entry (0 if nowhere)
|
|
|
|
numdef QUERY_GEOMETRY, 1 ; query geometry via 13.08 (for CHS access)
|
|
numdef QUERY_GEOMETRY_DISABLED, 0
|
|
numdef USE_PART_INFO, 1 ; use ds:si-> partition info from MBR, if any
|
|
numdef USE_PART_INFO_DISABLED, 0
|
|
numdef USE_AUTO_UNIT, 1 ; use unit passed from ROM-BIOS in dl
|
|
numdef RPL, 1 ; support RPL and do not overwrite it
|
|
numdef CHS, 1 ; support CHS (if it fits)
|
|
numdef LBA, 1 ; support LBA (if available)
|
|
numdef LBA_33_BIT, 1 ; support 33-bit LBA
|
|
numdef LBA_CHECK_NO_33, 1 ; else: check that LBA doesn't carry
|
|
|
|
numdef RELOCATE, 0 ; relocate the loader to top of memory
|
|
numdef SET_DL_UNIT, 0 ; if to pass unit in dl
|
|
numdef SET_BL_UNIT, 0 ; if to pass unit in bl as well
|
|
numdef SET_AXBX_DATA, 0 ; if to pass first data sector in ax:bx
|
|
numdef SET_DSSI_DPT, 0 ; if to pass DPT address in ds:si
|
|
numdef PUSH_DPT, 0 ; if to push DPT address
|
|
numdef MEMORY_CONTINUE, 1 ; if to just execute when memory full
|
|
numdef SET_SIDI_CLUSTER,0 ; if to pass first load file cluster in si:di
|
|
numdef TURN_OFF_FLOPPY, 0 ; if to turn off floppy motor after loading
|
|
numdef DATASTART_HIDDEN,0 ; if to add hidden sectors to data_start
|
|
numdef LBA_SET_TYPE, 0 ; if to set third byte to LBA partition type
|
|
numdef SET_LOAD_SEG, 1 ; if to set load_seg (word [ss:bp - 6])
|
|
numdef SET_FAT_SEG, 1 ; if to set fat_seg (word [ss:bp - 8])
|
|
numdef SET_CLUSTER, 1 ; if to set first_cluster (dword [ss:bp - 16])
|
|
numdef ZERO_ES, 0 ; if to set es = 0 before jump
|
|
numdef ZERO_DS, 0 ; if to set ds = 0 before jump
|
|
|
|
numdef FIX_SECTOR_SIZE, 0 ; fix sector size (0 = disable, else = sector size)
|
|
numdef FIX_SECTOR_SIZE_SKIP_CHECK, 0 ; don't check sector size
|
|
numdef FIX_CLUSTER_SIZE, 0 ; fix cluster size
|
|
numdef FIX_CLUSTER_SIZE_SKIP_CHECK, 0 ; don't check cluster size
|
|
numdef NO_LIMIT, 0 ; allow using more memory than a boot sector
|
|
; also will not write 0AA55h signature!
|
|
numdef WARN_PART_SIZE, 0
|
|
|
|
numdef LBA_SKIP_CHECK, 1 ; don't use proper LBA extensions check
|
|
numdef LBA_SKIP_CY, 1 ; skip check: set up CY before 13.42
|
|
numdef LBA_SKIP_ANY, 0 ; skip check: try CHS on any error
|
|
incdef _LBA_SKIP_ANY, LBA_SKIP_CY
|
|
numdef LBA_RETRY, 0 ; retry LBA reads one time
|
|
numdef CHS_RETRY, 1 ; retry CHS reads one time
|
|
numdef CHS_RETRY_REPEAT,16 ; retry CHS reads multiple times
|
|
; (value of the def is used as count)
|
|
numdef CHS_RETRY_NORMAL,1 ; do not use aggressive optimisation
|
|
numdef RETRY_RESET, 1 ; call reset disk system 13.00 on retries
|
|
|
|
; Unlike the 1440 KiB diskette image defaults for the FAT12
|
|
; loader we just fill the BPB with zeros by default.
|
|
numdef MEDIAID, 0 ; media ID
|
|
numdef UNIT, 0 ; load unit in BPB
|
|
numdef CHS_SECTORS, 0 ; CHS geometry field for sectors
|
|
numdef CHS_HEADS, 0 ; CHS geometry field for heads
|
|
numdef HIDDEN, 0 ; number of hidden sectors
|
|
numdef SPI, 0 ; sectors per image
|
|
numdef BPS, 0 ; bytes per sector
|
|
numdef SPC, 0 ; sectors per cluster
|
|
numdef SPF, 0 ; sectors per FAT
|
|
numdef SECTOR_FSINFO, 0 ; FSINFO sector
|
|
numdef SECTOR_BACKUP, 0 ; backup boot sector
|
|
numdef CLUSTER_ROOT, 0 ; root directory first cluster
|
|
numdef NUMFATS, 0 ; number of FATs
|
|
numdef NUMRESERVED, 0 ; number of reserved sectors
|
|
|
|
numdef COMPAT_FREEDOS, 0 ; partial FreeDOS load compatibility
|
|
numdef COMPAT_IBM, 0 ; partial IBMDOS load compatibility
|
|
numdef COMPAT_MS7, 0 ; partial MS-DOS 7 load compatibility
|
|
numdef COMPAT_LDOS, 0 ; lDOS load compatibility
|
|
|
|
%if (!!_COMPAT_FREEDOS + !!_COMPAT_IBM + !!_COMPAT_MS7 + !!_COMPAT_LDOS) > 1
|
|
%error At most one set must be selected.
|
|
%endif
|
|
|
|
%if _COMPAT_FREEDOS
|
|
strdef LOAD_NAME, "KERNEL"
|
|
strdef LOAD_EXT, "SYS"
|
|
numdef LOAD_ADR, 00600h
|
|
numdef LOAD_MIN_PARA, paras(512)
|
|
numdef EXEC_SEG_ADJ, 0
|
|
numdef EXEC_OFS, 0
|
|
numdef CHECKVALUE, 0
|
|
|
|
numdef SET_BL_UNIT, 1
|
|
numdef MEMORY_CONTINUE, 0
|
|
numdef RELOCATE, 1
|
|
; The FreeDOS load protocol mandates that the entire file be loaded.
|
|
%endif
|
|
|
|
%if _COMPAT_IBM
|
|
strdef LOAD_NAME, "IBMBIO"
|
|
strdef LOAD_EXT, "COM"
|
|
numdef LOAD_ADR, 00700h
|
|
numdef LOAD_MIN_PARA, paras(512)
|
|
numdef EXEC_SEG_ADJ, 0
|
|
numdef EXEC_OFS, 0
|
|
numdef CHECKVALUE, 0
|
|
numdef LOAD_DIR_SEG, 50h
|
|
numdef ADD_SEARCH, 1
|
|
strdef ADD_NAME, "IBMDOS"
|
|
strdef ADD_EXT, "COM"
|
|
numdef ADD_DIR_SEG, 52h
|
|
; Note: The IBMBIO.COM directory entry must be stored at
|
|
; 0:500h, and the IBMDOS.COM directory entry at 0:520h.
|
|
|
|
numdef SET_DL_UNIT, 1
|
|
numdef MEMORY_CONTINUE, 1
|
|
; 3 sectors * 512 BpS should suffice. We load into 700h--7A00h,
|
|
; ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
|
|
numdef SET_AXBX_DATA, 1
|
|
numdef DATASTART_HIDDEN,1
|
|
numdef SET_DSSI_DPT, 1
|
|
numdef PUSH_DPT, 1
|
|
%endif
|
|
|
|
%if _COMPAT_MS7
|
|
strdef LOAD_NAME, "IO"
|
|
strdef LOAD_EXT, "SYS"
|
|
numdef LOAD_ADR, 00700h
|
|
numdef LOAD_MIN_PARA, paras(1024)
|
|
numdef EXEC_SEG_ADJ, 0
|
|
numdef EXEC_OFS, 200h
|
|
numdef CHECKVALUE, 0
|
|
|
|
numdef SET_DL_UNIT, 1
|
|
numdef SET_DSSI_DPT, 0
|
|
numdef PUSH_DPT, 1
|
|
numdef MEMORY_CONTINUE, 1
|
|
; 4 sectors * 512 BpS should suffice. We load into 700h--7A00h,
|
|
; ie >= 6000h bytes (24 KiB), <= 7300h bytes (28.75 KiB).
|
|
numdef SET_SIDI_CLUSTER,1
|
|
numdef DATASTART_HIDDEN,1
|
|
numdef LBA_SET_TYPE, 1
|
|
%endif
|
|
|
|
%if _COMPAT_LDOS
|
|
strdef LOAD_NAME, "LDOS"
|
|
strdef LOAD_EXT, "COM"
|
|
numdef LOAD_ADR, 02000h
|
|
numdef LOAD_MIN_PARA, paras(1536)
|
|
numdef EXEC_SEG_ADJ, 0
|
|
numdef EXEC_OFS, 400h
|
|
numdef CHECKOFFSET, 1020
|
|
numdef CHECKVALUE, "lD"
|
|
|
|
numdef SET_DL_UNIT, 0
|
|
numdef SET_CLUSTER, 1
|
|
numdef SET_FAT_SEG, 1
|
|
numdef SET_LOAD_SEG, 1
|
|
numdef MEMORY_CONTINUE, 1
|
|
numdef DATASTART_HIDDEN,0
|
|
%endif
|
|
|
|
%if 0
|
|
|
|
Notes about partial load compatibilities
|
|
|
|
* FreeDOS:
|
|
* Relocates to an address other than 27A00h (1FE0h:7C00h)
|
|
* IBMDOS:
|
|
* Does not actually relocate DPT, just provide its address
|
|
* MS-DOS 7:
|
|
* Does not actually relocate DPT, just provide its address
|
|
* Does not contain message table used by loader
|
|
|
|
%endif
|
|
|
|
%if _SET_BL_UNIT && _SET_AXBX_DATA
|
|
%error Cannot select both of these options!
|
|
%endif
|
|
|
|
|
|
%assign LOADLIMIT 0A0000h
|
|
%assign POSITION 07C00h
|
|
|
|
%if _FIX_SECTOR_SIZE
|
|
%assign i 5
|
|
%rep 13-5
|
|
%if (1 << i) != (_FIX_SECTOR_SIZE)
|
|
%assign i i+1
|
|
%endif
|
|
%endrep
|
|
%if (1 << i) != (_FIX_SECTOR_SIZE)
|
|
%error Invalid sector size _FIX_SECTOR_SIZE
|
|
%endif
|
|
%endif
|
|
|
|
%if _FIX_CLUSTER_SIZE
|
|
%if _FIX_CLUSTER_SIZE > 256
|
|
%error Invalid cluster size _FIX_CLUSTER_SIZE
|
|
%endif
|
|
%assign i 0
|
|
%rep 8-0
|
|
%if (1 << i) != (_FIX_CLUSTER_SIZE)
|
|
%assign i i+1
|
|
%endif
|
|
%endrep
|
|
%if (1 << i) != (_FIX_CLUSTER_SIZE)
|
|
%warning Non-power-of-two cluster size _FIX_CLUSTER_SIZE
|
|
%endif
|
|
%endif
|
|
|
|
|
|
%if (_LOAD_ADR & 0Fh)
|
|
%error Load address must be on a paragraph boundary
|
|
%endif
|
|
|
|
%if _LOAD_ADR > LOADLIMIT
|
|
%error Load address must be in LMA
|
|
%elif _LOAD_ADR < 00500h
|
|
%error Load address must not overlap IVT or BDA
|
|
%endif
|
|
%if ! _RELOCATE
|
|
%if _LOAD_ADR > (POSITION-512) && _LOAD_ADR < (POSITION+1024)
|
|
%error Load address must not overlap loader
|
|
%endif
|
|
%endif
|
|
|
|
%if ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS) < 0
|
|
%error Execution address must be in loaded file
|
|
%elif ((_EXEC_SEG_ADJ<<4)+_EXEC_OFS+_LOAD_ADR) > LOADLIMIT
|
|
%error Execution address must be in LMA
|
|
%endif
|
|
|
|
%if (_EXEC_OFS & ~0FFFFh)
|
|
%error Execution offset must fit into 16 bits
|
|
%endif
|
|
|
|
%if (_EXEC_SEG_ADJ > 0FFFFh || _EXEC_SEG_ADJ < -0FFFFh)
|
|
%error Execution segment adjustment must fit into 16 bits
|
|
%endif
|
|
|
|
|
|
%if !_CHS && _QUERY_GEOMETRY
|
|
%warning No CHS support but querying geometry anyway
|
|
%endif
|
|
|
|
%if !_CHS && !_LBA
|
|
%error Either CHS or LBA or both must be enabled
|
|
%endif
|
|
|
|
|
|
%if 0
|
|
|
|
There is some logic inside MS-DOS's hard disk partition initialisation
|
|
code that sets up several requirements for us to fulfil. Otherwise,
|
|
it will not accept the information given in the BPB (using default
|
|
information based on the length as specified by MBR/EPBR instead) or
|
|
make the whole file system inaccessible except for formatting. Both of
|
|
those are undesirable of course. Some/all(?) checks are documented on
|
|
pages 601,602 in "DOS Internals", Geoff Chappell 1994, as follows:
|
|
|
|
* First three bytes contain either "jmp sho xx\ nop" or "jmp ne xx".
|
|
* Media descriptor field >= 0F0h.
|
|
* Bytes per sector field == 512.
|
|
* Sectors per cluster field a power of 2 (1,2,4,8,16,32,64,128).
|
|
* OEM name "version" (last three to four characters)
|
|
* must be "20.?", "10.?" (? = wildcard), but no other with "0.?",
|
|
* otherwise, must be "2.0", or
|
|
* 2nd-to-last,3rd-to-last character codes together > "3.", or
|
|
* those == "3.", last character code > "0"
|
|
|
|
To stay compatible to those, display a warning here if the name
|
|
itself would disqualify our boot sector already.
|
|
|
|
%endif
|
|
|
|
%push
|
|
%strlen %$len _OEM_NAME_FILL
|
|
%if %$len != 1
|
|
%error Specified OEM name fill must be 1 character
|
|
%endif
|
|
%strlen %$len _OEM_NAME
|
|
%define %$nam _OEM_NAME
|
|
%if %$len > 8
|
|
%error Specified OEM name is too long
|
|
%else
|
|
%assign %$warn 0
|
|
%rep 8 - %$len
|
|
%strcat %$nam %$nam,_OEM_NAME_FILL
|
|
%endrep
|
|
%substr %$prefix %$nam 5 ; "wvxyZa.b", get "Z"
|
|
%substr %$major %$nam 6,7 ; "wvxyzA.b", get "A."
|
|
%substr %$minor %$nam 8 ; "wvxyza.B", get "B"
|
|
%if %$major == "0."
|
|
%ifn %$prefix == "1" || %$prefix == "2"
|
|
%assign %$warn 1
|
|
%endif
|
|
%elifn %$major == "2." && %$minor == "0"
|
|
%if %$major < "3."
|
|
%assign %$warn 1
|
|
%elif %$major == "3." && %$minor < "1"
|
|
%assign %$warn 1
|
|
%endif
|
|
%endif
|
|
%if %$warn
|
|
%warning Specified OEM name fails MS-DOS's validation
|
|
%endif
|
|
%endif
|
|
%pop
|
|
|
|
; 512-byte stack (minus the variables).
|
|
ADR_STACK_LOW equ 7C00h - 200h ; 07A00h
|
|
|
|
ADR_FSIBOOT equ end -start+7C00h ; 07E00h
|
|
|
|
%define _AFTER (ADR_FSIBOOT + 512)
|
|
%if _RELOCATE
|
|
; dynamic allocation of FAT buffer
|
|
%else
|
|
%if _LOAD_ADR < 7C00h
|
|
ADR_FATBUF equ _AFTER
|
|
%define _AFTER (ADR_FATBUF + 8192)
|
|
%else
|
|
ADR_FATBUF equ 2000h
|
|
%endif
|
|
%endif
|
|
|
|
%if _ADD_SEARCH
|
|
%if _RELOCATE
|
|
; dynamic allocation of dir buffer
|
|
%elif _LOAD_ADR < 7C00h
|
|
ADR_DIRBUF equ _AFTER
|
|
%define _AFTER (ADR_DIRBUF + 8192)
|
|
%else
|
|
ADR_DIRBUF equ 4000h
|
|
%endif
|
|
%else
|
|
%if _RELOCATE
|
|
ADR_DIRBUF equ _LOAD_ADR ; 00600h
|
|
%elif _LOAD_ADR < 7C00h
|
|
; one-sector directory buffer. Assumes sectors are no larger than 8 KiB
|
|
ADR_DIRBUF equ _AFTER ; 0A000h
|
|
%define _AFTER (ADR_DIRBUF + 8192)
|
|
%else
|
|
ADR_DIRBUF equ 4000h
|
|
%endif
|
|
%endif
|
|
ADR_FREE equ _AFTER ; 08000h
|
|
|
|
%if ! _RELOCATE
|
|
%if ((ADR_FATBUF + 8192 - 1) & ~0FFFFh) != (ADR_FATBUF & ~0FFFFh)
|
|
%warning Possibly crossing 64 KiB boundary while reading FAT
|
|
%endif
|
|
%endif
|
|
|
|
%ifn _RELOCATE && _ADD_SEARCH
|
|
%if ((ADR_DIRBUF + 8192 - 1) & ~0FFFFh) != (ADR_DIRBUF & ~0FFFFh)
|
|
%warning Possibly crossing 64 KiB boundary while reading directory
|
|
%endif
|
|
%endif
|
|
|
|
%if _LOAD_ADR >= ADR_FREE || _RELOCATE
|
|
; If reading on a sector size boundary, no crossing can occur.
|
|
; Check for all possible sector sizes (32 B to 8 KiB). If one
|
|
; of them fails display a warning, including the minimum size.
|
|
%assign SECSIZECHECK 32
|
|
%assign EXITREP 0
|
|
%rep 256
|
|
%ifn EXITREP
|
|
%if _LOAD_ADR & (SECSIZECHECK - 1)
|
|
%warning Possibly crossing 64 KiB boundary while reading file (sector size >= SECSIZECHECK)
|
|
%assign EXITREP 1
|
|
%exitrep
|
|
%endif
|
|
%if SECSIZECHECK == 8192
|
|
%assign EXITREP 1
|
|
%exitrep
|
|
%endif
|
|
%assign SECSIZECHECK SECSIZECHECK * 2
|
|
%endif
|
|
%endrep
|
|
%else
|
|
; If loading below the boot sector, address 1_0000h is never reached.
|
|
%endif
|
|
|
|
|
|
struc FSINFO ; FAT32 FSINFO sector layout
|
|
.signature1: resd 1 ; 41615252h ("RRaA") for valid FSINFO
|
|
.reserved1: ; former unused, initialized to zero by FORMAT
|
|
.fsiboot: resb 480 ; now used for FSIBOOT
|
|
.signature2: resd 1 ; 61417272h ("rrAa") for valid FSINFO
|
|
.numberfree: resd 1 ; FSINFO: number of free clusters or -1
|
|
.nextfree: resd 1 ; FSINFO: first free cluster or -1
|
|
.reserved2: resd 3 ; unused, initialized to zero by FORMAT
|
|
.signature3: resd 1 ; AA550000h for valid FSINFO or FSIBOOT
|
|
endstruc
|
|
|
|
struc FSIBOOTG ; FSIBOOT general layout
|
|
.signature: resq 1 ; 8 bytes that identify the FSIBOOT type
|
|
.fsicode: resb 470 ; 470 bytes FSIBOOT type specific data or code
|
|
.dirsearch: resw 1 ; 1 word -> directory search entrypoint
|
|
endstruc
|
|
|
|
struc DIRENTRY
|
|
deName: resb 8
|
|
deExt: resb 3
|
|
deAttrib: resb 1
|
|
dePlusSize: resb 1
|
|
resb 7
|
|
deClusterHigh: resw 1
|
|
deTime: resw 1
|
|
deDate: resw 1
|
|
deClusterLow: resw 1
|
|
deSize: resd 1
|
|
endstruc
|
|
|
|
ATTR_READONLY equ 1
|
|
ATTR_HIDDEN equ 2
|
|
ATTR_SYSTEM equ 4
|
|
ATTR_VOLLABEL equ 8
|
|
ATTR_DIRECTORY equ 10h
|
|
ATTR_ARCHIVE equ 20h
|
|
|
|
|
|
; use byte-offset addressing from BP for smaller code
|
|
%define VAR(x) ((x) - start) + bp
|
|
|
|
|
|
cpu 8086
|
|
; bootsector loaded at address 07C00h, addressable using 0000h:7C00h
|
|
org POSITION
|
|
start:
|
|
|
|
|
|
%define _LASTVARIABLE start
|
|
%macro nextvariable 2.nolist
|
|
%1 equ (_LASTVARIABLE - %2)
|
|
%define _LASTVARIABLE %1
|
|
%endmacro
|
|
|
|
; Variables
|
|
|
|
; (dword) sector where the first cluster's data starts
|
|
nextvariable data_start, 4
|
|
|
|
; (word) current load segment (points behind last loaded data)
|
|
nextvariable load_seg, 2
|
|
|
|
; (word) segment of FAT buffer
|
|
; for FAT12 this holds the entire FAT
|
|
; for FAT16 this holds the sector given by wo[fat_sector]
|
|
; for FAT32 this holds the sector given by dwo[fat_sector]
|
|
nextvariable fat_seg, 2
|
|
|
|
; (dword for FAT32) currently loaded sector-in-FAT, -1 if none
|
|
nextvariable fat_sector, 4
|
|
|
|
; (dword for FAT32) first cluster of load file
|
|
nextvariable first_cluster, 4
|
|
|
|
ADR_STACK_START equ _LASTVARIABLE -start+POSITION
|
|
|
|
; (word) number of 16-byte paragraphs per sector
|
|
nextvariable para_per_sector, 2
|
|
|
|
; (word) number of 32-byte directory entries per sector
|
|
nextvariable entries_per_sector, 2
|
|
|
|
; (word) segment of last available memory for sector
|
|
nextvariable last_available_sector, 2
|
|
|
|
; (word) actual sectors per cluster
|
|
nextvariable adj_sectors_per_cluster, 2
|
|
|
|
; (word) paragraphs left to read
|
|
nextvariable paras_left, 2
|
|
|
|
lowest_variable equ _LASTVARIABLE
|
|
|
|
|
|
jmp strict short skip_bpb
|
|
%if !_CHS && _LBA_SET_TYPE
|
|
db 0Ch ; LBA-enabled FAT32 FS partition type
|
|
%else
|
|
nop ; default: no LBA
|
|
%endif
|
|
|
|
|
|
; BIOS Parameter Block (BPB)
|
|
;
|
|
; Installation will use the BPB already present in your file system.
|
|
; These values must be initialised when installing the loader.
|
|
|
|
oem_id: ; offset 03h (03) - not used by this code
|
|
fill 8,_OEM_NAME_FILL,db _OEM_NAME
|
|
bytes_per_sector: ; offset 0Bh (11) - refer to _FIX_SECTOR_SIZE !
|
|
dw _BPS
|
|
sectors_per_cluster: ; offset 0Dh (13) - refer to _FIX_CLUSTER_SIZE !
|
|
db _SPC & 255
|
|
fat_start:
|
|
num_reserved_sectors: ; offset 0Eh (14)
|
|
dw _NUMRESERVED
|
|
num_fats: ; offset 10h (16)
|
|
db _NUMFATS
|
|
num_root_dir_ents: ; offset 11h (17)
|
|
dw 0
|
|
total_sectors: ; offset 13h (19) - not used by this code
|
|
%if _SPI < 1_0000h
|
|
dw _SPI
|
|
%else
|
|
dw 0
|
|
%endif
|
|
media_id: ; offset 15h (21) - not used by this code
|
|
db _MEDIAID
|
|
sectors_per_fat: ; offset 16h (22)
|
|
dw 0
|
|
sectors_per_track: ; offset 18h (24)
|
|
dw _CHS_SECTORS
|
|
heads: ; offset 1Ah (26)
|
|
dw _CHS_HEADS
|
|
hidden_sectors: ; offset 1Ch (28)
|
|
dd _HIDDEN
|
|
total_sectors_large: ; offset 20h (32) - not used by this code
|
|
%if _SPI >= 1_0000h
|
|
dd _SPI
|
|
%else
|
|
dd 0
|
|
%endif
|
|
|
|
sectors_per_fat_large: ; offset 24h (36)
|
|
dd _SPF
|
|
fsflags: ; offset 28h (40)
|
|
dw 0
|
|
fsversion: ; offset 2Ah (42)
|
|
dw 0
|
|
root_cluster: ; offset 2Ch (44)
|
|
dd _CLUSTER_ROOT
|
|
fsinfo_sector: ; offset 30h (48)
|
|
dw _SECTOR_FSINFO
|
|
backup_sector: ; offset 32h (50)
|
|
dw _SECTOR_BACKUP
|
|
|
|
times 12 db 0 ; offset 34h (52) reserved
|
|
|
|
; Extended BPB ; offset 40h (64)
|
|
|
|
boot_unit: db _UNIT
|
|
db 0
|
|
ext_bpb_signature: db 29h
|
|
serial_number: dd _VOLUMEID
|
|
volume_label: fill 11,32,db _DEFAULT_LABEL
|
|
filesystem_identifier: fill 8,32,db "FAT32"
|
|
|
|
|
|
; Initialised data
|
|
|
|
align 2
|
|
fsiboot_table: ; this table is used by the FSIBOOT stage
|
|
.error: dw error
|
|
; INP: al = error condition letter
|
|
; ('B' = bad chain / FS error, 'F' = file not found,
|
|
; 'R' = disk read error, 'M' = not enough memory,
|
|
; 'E' = not enough data in file)
|
|
.success: dw load_finish
|
|
; INP: dword [ss:sp] = first cluster
|
|
; Note: The first cluster dword is always filled in
|
|
; by FSIBOOT; the option _SET_SIDI_CLUSTER only
|
|
; affects usage in the primary loader.
|
|
%if _MEMORY_CONTINUE
|
|
.memory_full: dw load_finish_mc
|
|
; INP: al = error condition letter ('M'),
|
|
; dword [ss:sp] = current cluster number,
|
|
; dword [ss:sp + 4] = first cluster, refer to .success
|
|
%else
|
|
.memory_full: dw error
|
|
; refer to previous .memory_full comment
|
|
%endif
|
|
.read_sector: dw read_sector
|
|
; INP: dx:ax = sector number within partition,
|
|
; bx => buffer, (_LBA) ds = ss
|
|
; OUT: dx:ax incremented, bx => incremented,
|
|
; es = input bx, does not return if error occurred
|
|
; CHG: none
|
|
%if _RELOCATE && _ADD_SEARCH
|
|
.dirbuf: dw 8192 >> 4
|
|
; initialised to segment displacement from FAT buffer
|
|
%else
|
|
.dirbuf: dw ADR_DIRBUF>>4
|
|
; => directory sector buffer (one sector)
|
|
%endif
|
|
.writedirentry: dw writedirentry.loaddir
|
|
; INP: es:bx -> found dir entry in dir sector buffer
|
|
; si:di = loaded sector-in-FAT
|
|
; CHG: ax, cx, dx
|
|
; STT: ss:bp -> boot sector
|
|
; ds = ss
|
|
; UP
|
|
; OUT: directory entry copied if so desired
|
|
; (is a no-op if not to copy dir entry)
|
|
.filename: dw .load_name
|
|
; -> name to search
|
|
.minpara: dw _LOAD_MIN_PARA
|
|
.loadseg: dw _LOAD_ADR>>4
|
|
; => where to load
|
|
|
|
.load_name: ; = blank-padded 11-byte filename to search for
|
|
fill 8,32,db _LOAD_NAME
|
|
fill 3,32,db _LOAD_EXT
|
|
|
|
fsiboot_name:
|
|
fill 8, 32, db _FSIBOOTNAME
|
|
|
|
%if _WARN_PART_SIZE
|
|
%assign num $ - start
|
|
%warning BPB + data size is num bytes
|
|
%endif
|
|
|
|
|
|
; Code
|
|
|
|
; Note that this may be entered with cs:ip = 07C0h:0 !
|
|
skip_bpb:
|
|
cli
|
|
cld
|
|
xor cx, cx
|
|
mov bp, start ; magic bytes - checked by instsect
|
|
mov ss, cx
|
|
mov sp, ADR_STACK_START
|
|
%if _USE_AUTO_UNIT
|
|
mov [VAR(boot_unit)], dl; magic bytes - checked by instsect
|
|
%else
|
|
mov dl, [VAR(boot_unit)]; magic bytes - checked by instsect
|
|
%endif
|
|
|
|
; Note: es is left uninitialised here until the first call to
|
|
; read_sector if the below conditional is false.
|
|
%if _USE_PART_INFO ; +19 bytes
|
|
mov es, cx
|
|
; Note: Award Medallion BIOS v6.0 (ASUS MED 2001 ACPI BIOS Revision 1009)
|
|
; loads from a floppy disk drive with ds:si = 0F000h:0A92Dh ->
|
|
; FF FF FF FF 08 00 08 01 FF FF FF FF FF FF FF FF, which was detected
|
|
; as a valid partition table entry by this handling. Therefore, we
|
|
; only accept partition information when booting from a hard disk now.
|
|
|
|
; start of magic byte sequence for instsect
|
|
test dl, dl ; floppy ?
|
|
jns @F ; don't attempt detection -->
|
|
; Check whether an MBR left us partition information.
|
|
; byte[ds:si] bit 7 means active and must be set if valid.
|
|
cmp byte [si], cl ; flags for xx-00h (result is xx), SF = bit 7
|
|
jns @F ; xx < 80h, ie info invalid -->
|
|
; byte[ds:si+4] is the file system type. Check for valid one.
|
|
cmp byte [si+4], cl ; is it zero?
|
|
je @F ; yes, info invalid -->
|
|
; Info valid, trust their hidden sectors over hardcoded.
|
|
; Assume the movsw instructions won't run with si = FFFFh.
|
|
mov di, hidden_sectors ; -> BPB field
|
|
add si, 8 ; -> partition start sector in info
|
|
%if _USE_PART_INFO_DISABLED
|
|
nop
|
|
nop ; size has to match enabled code
|
|
%else
|
|
movsw
|
|
movsw ; overwrite BPB field with value from info
|
|
%endif
|
|
@@:
|
|
; end of magic byte sequence for instsect
|
|
%endif
|
|
mov ds, cx
|
|
sti
|
|
|
|
|
|
%if _QUERY_GEOMETRY ; +27 bytes
|
|
|
|
; start of magic byte sequence for instsect
|
|
; test dl, dl ; floppy?
|
|
; jns @F ; don't attempt query, might fail -->
|
|
; Note that while the original PC BIOS doesn't support this function
|
|
; (for its diskettes), it does properly return the error code 01h.
|
|
; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1)
|
|
mov ah, 08h
|
|
; xor cx, cx ; initialise cl to 0
|
|
; Already from prologue cx = 0.
|
|
stc ; initialise to CY
|
|
%if _QUERY_GEOMETRY_DISABLED
|
|
nop
|
|
nop ; size has to match enabled code
|
|
%else
|
|
int 13h ; query drive geometry
|
|
%endif
|
|
jc @F ; apparently failed -->
|
|
and cx, 3Fh ; get sectors
|
|
jz @F ; invalid (S is 1-based), don't use -->
|
|
mov [VAR(sectors_per_track)], cx
|
|
mov cl, dh ; cx = maximum head number
|
|
inc cx ; cx = number of heads (H is 0-based)
|
|
mov [VAR(heads)], cx
|
|
@@:
|
|
; end of magic byte sequence for instsect
|
|
%endif
|
|
|
|
|
|
; 16-byte paragraphs per sector
|
|
mov ax, [VAR(bytes_per_sector)]
|
|
mov cl, 4
|
|
shr ax, cl
|
|
push ax ; push into word [VAR(para_per_sector)]
|
|
|
|
; 32-byte FAT directory entries per sector
|
|
shr ax, 1 ; /2 = 32-byte entries per sector
|
|
push ax ; push into word [VAR(entries_per_sector)]
|
|
|
|
load_fsiboot:
|
|
cmp ax, 1024 >> 5 ; sector size is at least 1 KiB (large) ?
|
|
jae .loaded ; already loaded as part of the boot sector -->
|
|
|
|
; If this code runs, then ax was below 1024 >> 5,
|
|
; therefore this cwd instruction always zeros dx.
|
|
cwd
|
|
|
|
mov ax, [VAR(fsinfo_sector)]
|
|
; inc ax
|
|
; jz error_fsiboot ; was FFFFh, invalid -->
|
|
; dec ax
|
|
; FFFFh always fails the < num_reserved_sectors check
|
|
test ax, ax
|
|
jz @F ; is 0 ? invalid --> (ZR, NC)
|
|
cmp ax, [VAR(num_reserved_sectors)]
|
|
@@: ; (ZR, NC if ax == 0)
|
|
jae error_fsiboot ; (jump if NC)
|
|
; dx:ax = FSINFO sector (dx = 0 from cwd)
|
|
mov cx, 512
|
|
mov bx, ADR_FSIBOOT >> 4
|
|
@@:
|
|
call read_sector
|
|
sub cx, [VAR(bytes_per_sector)]
|
|
ja @B ; read 512 bytes -->
|
|
|
|
.loaded:
|
|
push ds
|
|
pop es
|
|
mov di, fsiboot.signature
|
|
mov si, fsiboot_name
|
|
mov cx, 4 ; size of fsiboot_name
|
|
repe cmpsw
|
|
jne error_fsiboot
|
|
; Note that now es:di -> fsiboot.start
|
|
|
|
%if _LOAD_ADR >= ADR_FREE || _RELOCATE
|
|
; Get conventional memory size and store it
|
|
int 12h
|
|
mov cl, 6
|
|
shl ax, cl
|
|
%if _RPL ; +33 bytes
|
|
xchg dx, ax
|
|
lds si, [4 * 2Fh]
|
|
add si, 3
|
|
lodsb
|
|
cmp al, 'R'
|
|
jne .no_rpl
|
|
lodsb
|
|
cmp al, 'P'
|
|
jne .no_rpl
|
|
lodsb
|
|
cmp al, 'L'
|
|
jne .no_rpl
|
|
mov ax, 4A06h
|
|
int 2Fh
|
|
.no_rpl:
|
|
push ss
|
|
pop ds
|
|
xchg ax, dx
|
|
%endif
|
|
%if _RELOCATE
|
|
%if _ADD_SEARCH
|
|
sub ax, (20 * 1024 + 8192 + 8192 + (8192-16) + 1024 + 7C00h) >> 4
|
|
%else
|
|
sub ax, (20 * 1024 + 8192 + (8192-16) + 1024 + 7C00h) >> 4
|
|
%endif
|
|
; 20 KiB: reserved for iniload
|
|
; 8 KiB: dir buffer (only if _ADD_SEARCH)
|
|
; 8 KiB: FAT buffer
|
|
; 8 KiB - 16 B: to allow rounding down position of buffers
|
|
; 1 KiB: sector + FSIBOOT
|
|
; 7C00h: stack and to allow addressing with 7C00h in bp
|
|
;
|
|
; Note also that by addressing the stack and sector and FSIBOOT
|
|
; with bp at 7C00h, and insuring this subtraction doesn't
|
|
; underflow, makes sure that we do not overwrite the IVT or
|
|
; BDA. (However, we assume that ax is at least 60h or so.)
|
|
;
|
|
; The FAT buffer segment is masked so that the actual buffer
|
|
; is stored on an 8 KiB boundary. This is to ensure that
|
|
; the buffer doesn't possibly cross a 64 KiB DMA boundary.
|
|
jc error_fsiboot
|
|
cmp ax, (end_after_fsiboot -start+7C00h - ADR_STACK_LOW + 15) >> 4
|
|
; This check is to ensure that the start of the destination
|
|
; for the relocation (stack, sector, FSIBOOT) is
|
|
; above-or-equal the end of the source for the relocation.
|
|
; That is, to avoid overwriting any of the source with the
|
|
; string move instruction (which for simplicity is always UP).
|
|
jb error_fsiboot
|
|
|
|
; With _RELOCATE enabled, the FAT buffer segment
|
|
; is fixed up by the primary loader here before
|
|
; FSIBOOT is executed.
|
|
mov bx, ((8192 - 16) + 1024 + 7C00h)>>4
|
|
; bx is initialised to the value
|
|
; ((8192 - 16) + 1024 + 7C00h)>>4
|
|
; so this is like calculating the following for its value:
|
|
; ((LMA_top - 20 KiB - 8 KiB - 8 KiB{AS} - (8 KiB - 16 B) - 1 KiB - 7C00h) + \
|
|
; ((8 KiB - 16 B) + 1 KiB + 7C00h))>>4
|
|
; == (LMA_top - 20 KiB - 8 KiB - 8 KiB{AS})>>4
|
|
; {AS} = only if _ADD_SEARCH
|
|
add bx, ax
|
|
and bx, ~ ((8192>>4) - 1) ; => FAT sector buffer (one sector)
|
|
%if _ADD_SEARCH
|
|
; .dirbuf is initialised to 8192 >> 4
|
|
add word [VAR(fsiboot_table.dirbuf)], bx
|
|
; => dir sector buffer (one sector)
|
|
%endif
|
|
|
|
push ax
|
|
push di ; -> reloc destination of fsiboot.start
|
|
|
|
mov es, ax ; => destination
|
|
mov si, sp ; ds:si = ss:entries_per_sector - 4
|
|
mov di, si ; es:di -> destination for stack low
|
|
mov cx, (end_after_fsiboot - (entries_per_sector - 4)) >> 1
|
|
; end_after_fsiboot is the top of used memory
|
|
; entries_per_sector is the lowest filled stack frame slot
|
|
; 4 is for the additional slots taken by the return address
|
|
rep movsw ; relocate stack, sector, and FSIBOOT
|
|
mov ss, ax
|
|
mov ds, ax ; relocate these
|
|
add ax, (ADR_STACK_LOW) >> 4 ; (rounding down) => behind available
|
|
|
|
retf ; jump to relocated FSIBOOT
|
|
%else
|
|
sub ax, (20 * 1024) >> 4 ; 20 KiB reserved for iniload
|
|
jc error_fsiboot
|
|
mov bx, ADR_FATBUF >> 4 ; => FAT sector buffer (one sector)
|
|
push es
|
|
push di ; -> fsiboot.start
|
|
retf
|
|
; Do a far return here to ensure that cs is zero.
|
|
%endif
|
|
%elif _LOAD_ADR < ADR_STACK_LOW
|
|
mov ax, (ADR_STACK_LOW >> 4)
|
|
mov bx, ADR_FATBUF >> 4 ; => FAT sector buffer (one sector)
|
|
push es
|
|
push di ; -> fsiboot.start
|
|
retf
|
|
; Do a far return here to ensure that cs is zero.
|
|
%else
|
|
%error Load address within used memory
|
|
%endif
|
|
|
|
|
|
%if _WARN_PART_SIZE
|
|
%assign num $ - skip_bpb
|
|
%warning init size is num bytes
|
|
%endif
|
|
|
|
|
|
finish_start:
|
|
; INP: es:bx -> found dir entry in dir sector buffer
|
|
; si:di = loaded sector-in-FAT
|
|
; CHG: ax, cx, dx
|
|
; STT: ss:bp -> boot sector
|
|
; ds = ss
|
|
; UP
|
|
; OUT: directory entry copied if so desired
|
|
; (is a no-op if not to copy dir entry)
|
|
writedirentry:
|
|
%if _LOAD_DIR_SEG
|
|
.loaddir:
|
|
mov ax, _LOAD_DIR_SEG
|
|
%else
|
|
.loaddir: equ read_sector.retn
|
|
%endif
|
|
%if _LOAD_DIR_SEG || (_ADD_DIR_SEG && _ADD_SEARCH)
|
|
; INP: ax => where to store directory entry
|
|
.ax:
|
|
push ds
|
|
push si
|
|
push di
|
|
push es
|
|
pop ds
|
|
mov si, bx ; ds:si -> directory entry
|
|
mov cx, DIRENTRY_size >> 1
|
|
mov es, ax
|
|
xor di, di ; es:di -> where to store directory entry
|
|
rep movsw ; move to here (one directory entry)
|
|
push ds
|
|
pop es ; es:bx -> dir entry in dir sector buffer
|
|
pop di
|
|
pop si
|
|
pop ds
|
|
retn
|
|
%endif
|
|
|
|
|
|
%if _MEMORY_CONTINUE
|
|
load_finish_mc:
|
|
pop bx
|
|
pop cx
|
|
%endif
|
|
load_finish:
|
|
|
|
%if _ADD_SEARCH
|
|
mov word [VAR(fsiboot_table.filename)], add_name
|
|
call near word [dirsearch_entrypoint]
|
|
%if _ADD_DIR_SEG
|
|
mov ax, _ADD_DIR_SEG
|
|
call writedirentry.ax
|
|
%endif
|
|
%endif
|
|
|
|
%if _TURN_OFF_FLOPPY
|
|
; turn off floppy motor
|
|
mov dx,3F2h
|
|
mov al,0
|
|
out dx,al
|
|
%endif
|
|
|
|
; Set-up registers for and jump to loaded file
|
|
; Already: ss:bp-> boot sector containing BPB
|
|
%if _CHECKVALUE
|
|
CHECKLINEAR equ _LOAD_ADR + _CHECKOFFSET
|
|
%if CHECKLINEAR <= (64 * 1024 - 2) && ! _RELOCATE
|
|
cmp word [CHECKLINEAR], _CHECKVALUE
|
|
%else
|
|
mov ax, CHECKLINEAR >> 4
|
|
mov es, ax
|
|
cmp word [es:CHECKLINEAR & 15], _CHECKVALUE
|
|
%endif
|
|
mov al, 'V' ; check 'V'alue mismatch
|
|
jne error
|
|
%endif
|
|
%if _SET_SIDI_CLUSTER && (_PUSH_DPT || _SET_DSSI_DPT)
|
|
pop cx
|
|
pop dx
|
|
%endif
|
|
%if _PUSH_DPT || _SET_DSSI_DPT
|
|
%ifn _SET_DSSI_DPT ; (implying that only _PUSH_DPT is set)
|
|
%if _RELOCATE
|
|
xor ax, ax
|
|
mov es, ax ; => IVT
|
|
mov di, 1Eh*4
|
|
les si, [es:di] ; -> original (also current) DPT
|
|
push es
|
|
push si ; original (also current) DPT address
|
|
push ax
|
|
push di ; 0000h:0078h (address of 1Eh IVT entry)
|
|
%else
|
|
; If not _RELOCATE, ds = 0000h (=> IVT)
|
|
mov di, 1Eh*4
|
|
les si, [di] ; -> original (also current) DPT
|
|
push es
|
|
push si ; original (also current) DPT address
|
|
; If not _RELOCATE, ss = 0000h (=> IVT)
|
|
push ss
|
|
push di ; 0000h:0078h (address of 1Eh IVT entry)
|
|
%endif
|
|
%else
|
|
%if _RELOCATE
|
|
xor ax, ax
|
|
mov ds, ax ; => IVT
|
|
%endif
|
|
; If not _RELOCATE, ds = 0000h (=> IVT)
|
|
mov di, 1Eh*4
|
|
lds si, [di] ; -> original (also current) DPT
|
|
%if _PUSH_DPT
|
|
push ds
|
|
push si ; original (also current) DPT address
|
|
%if _RELOCATE
|
|
push ax
|
|
push di ; 0000h:0078h (address of 1Eh IVT entry)
|
|
%else
|
|
; If not _RELOCATE, ss = 0000h (=> IVT)
|
|
push ss
|
|
push di ; 0000h:0078h (address of 1Eh IVT entry)
|
|
%endif
|
|
%endif
|
|
%endif
|
|
%endif
|
|
%if _ZERO_ES || _ZERO_DS
|
|
%if _RELOCATE
|
|
%ifn _PUSH_DPT || _SET_DSSI_DPT
|
|
xor ax, ax
|
|
%endif
|
|
%if _ZERO_ES
|
|
mov es, ax
|
|
%endif
|
|
%if _ZERO_DS
|
|
mov ds, ax
|
|
%endif
|
|
%else
|
|
%if _ZERO_ES
|
|
push ss
|
|
pop es
|
|
%endif
|
|
%if _ZERO_DS && _SET_DSSI_DPT
|
|
push ss
|
|
pop ds
|
|
%endif
|
|
%endif
|
|
%endif
|
|
%if _DATASTART_HIDDEN
|
|
mov bx, [VAR(hidden_sectors + 0)]
|
|
mov ax, [VAR(hidden_sectors + 2)]
|
|
add word [VAR(data_start + 0)], bx
|
|
adc word [VAR(data_start + 2)], ax
|
|
%endif
|
|
%if _SET_AXBX_DATA
|
|
mov bx, [VAR(data_start)]
|
|
mov ax, [VAR(data_start+2)]
|
|
%endif
|
|
%if _SET_SIDI_CLUSTER
|
|
%if _SET_DSSI_DPT
|
|
%error Cannot select both of these.
|
|
%endif
|
|
%if _PUSH_DPT || _SET_DSSI_DPT
|
|
mov di, cx
|
|
mov si, dx
|
|
%else
|
|
pop di
|
|
pop si
|
|
%endif
|
|
%endif
|
|
%if _SET_DL_UNIT
|
|
mov dl, [VAR(boot_unit)]; set dl to unit
|
|
%if _SET_BL_UNIT
|
|
mov bl, dl ; set bl to unit, too
|
|
%endif
|
|
%elif _SET_BL_UNIT
|
|
mov bl, [VAR(boot_unit)]; set bl to unit
|
|
%endif
|
|
; ss:bp-> boot sector with BPB
|
|
jmp (_LOAD_ADR>>4)+_EXEC_SEG_ADJ:_EXEC_OFS
|
|
|
|
%if _WARN_PART_SIZE
|
|
%assign num $ - finish_start
|
|
%warning finish size is num bytes
|
|
%endif
|
|
|
|
|
|
error_start:
|
|
|
|
error_fsiboot:
|
|
mov al,'I'
|
|
|
|
db __TEST_IMM16 ; (skip mov)
|
|
read_sector.err:
|
|
mov al, 'R' ; Disk 'R'ead error
|
|
|
|
error:
|
|
%if _RELOCATE
|
|
mov bx, 7
|
|
mov ds, bx
|
|
mov bh, [462h - 70h]
|
|
%else
|
|
mov bh, [462h]
|
|
mov bl, 7
|
|
%endif
|
|
mov ah, 0Eh
|
|
int 10h ; display character
|
|
mov al, 07h
|
|
int 10h ; beep!
|
|
|
|
xor ax, ax ; await key pressed
|
|
int 16h
|
|
|
|
int 19h ; re-start the boot process
|
|
|
|
%if _WARN_PART_SIZE
|
|
%assign num $ - error_start
|
|
%warning error size is num bytes
|
|
%endif
|
|
|
|
|
|
; Read a sector using Int13.02 or Int13.42
|
|
;
|
|
; INP: dx:ax = sector number within partition
|
|
; bx => buffer
|
|
; (_LBA) ds = ss
|
|
; OUT: If unable to read,
|
|
; ! jumps to error instead of returning
|
|
; If sector has been read,
|
|
; dx:ax = next sector number (has been incremented)
|
|
; bx => next buffer (bx = es+word[para_per_sector])
|
|
; es = input bx
|
|
; CHG: -
|
|
read_sector:
|
|
push dx
|
|
push cx
|
|
push ax
|
|
push si
|
|
|
|
mov es, bx ; => buffer
|
|
|
|
; DX:AX==LBA sector number
|
|
; add partition start (= number of hidden sectors)
|
|
add ax,[VAR(hidden_sectors + 0)]
|
|
adc dx,[VAR(hidden_sectors + 2)]
|
|
%if (!_LBA || !_LBA_33_BIT) && _LBA_CHECK_NO_33
|
|
jc .err
|
|
%endif
|
|
%if _LBA ; +70 bytes (with CHS, +63 bytes without CHS)
|
|
%if _LBA_33_BIT
|
|
sbb si, si ; -1 if was CY, 0 else
|
|
neg si ; 1 if was CY, 0 else
|
|
%endif
|
|
xor cx, cx
|
|
push cx ; highest word = 0
|
|
%if _LBA_33_BIT
|
|
push si ; bit 32 = 1 if operating in 33-bit space
|
|
%else
|
|
push cx ; second highest word = 0
|
|
%endif
|
|
push dx
|
|
push ax ; = qword sector number
|
|
push bx
|
|
push cx ; bx => buffer
|
|
inc cx
|
|
push cx ; word number of sectors to read
|
|
mov cl, 10h
|
|
push cx ; word size of disk address packet
|
|
mov si, sp ; ds:si -> disk address packet (on stack)
|
|
|
|
%if _LBA_SKIP_CHECK ; -14 bytes
|
|
mov dl, [VAR(boot_unit)]
|
|
%else
|
|
mov ah, 41h
|
|
mov dl, [VAR(boot_unit)]
|
|
mov bx, 55AAh
|
|
stc
|
|
int 13h ; 13.41.bx=55AA extensions installation check
|
|
jc .no_lba
|
|
cmp bx, 0AA55h
|
|
jne .no_lba
|
|
shr cl, 1 ; support bitmap bit 0
|
|
jnc .no_lba
|
|
%endif
|
|
|
|
%if _LBA_RETRY
|
|
%if _LBA_SKIP_CHECK && _LBA_SKIP_CY
|
|
stc
|
|
%endif
|
|
mov ah, 42h
|
|
int 13h ; 13.42 extensions read
|
|
jnc .lba_done
|
|
|
|
%if _RETRY_RESET
|
|
xor ax, ax
|
|
int 13h ; reset disk
|
|
%endif
|
|
|
|
; have to reset the LBAPACKET's lpCount, as the handler may
|
|
; set it to "the number of blocks successfully transferred".
|
|
; (in any case, the high byte is still zero.)
|
|
mov byte [si + 2], 1
|
|
%endif
|
|
|
|
%if _LBA_SKIP_CHECK && _LBA_SKIP_CY
|
|
stc
|
|
%endif
|
|
mov ah, 42h
|
|
int 13h
|
|
%if _LBA_SKIP_CHECK && _CHS
|
|
%if _LBA_SKIP_ANY
|
|
jc .no_lba
|
|
.err_CY: equ .err
|
|
.err_2: equ .err
|
|
%else
|
|
jnc .lba_done
|
|
cmp ah, 1 ; invalid function?
|
|
je .no_lba ; try CHS instead -->
|
|
.err_CY:
|
|
.err_2:
|
|
jmp .lba_error
|
|
%endif
|
|
%else
|
|
.err_CY:
|
|
jc .lba_error
|
|
.err_2: equ .err
|
|
%endif
|
|
|
|
.lba_done:
|
|
%if _CHS && _LBA_SET_TYPE
|
|
mov byte [bp + 2], 0Ch ; LBA-enabled FAT32 FS partition type
|
|
%endif
|
|
add sp, 10h
|
|
%if _CHS
|
|
jmp short .done
|
|
%endif
|
|
|
|
.lba_error: equ .err
|
|
|
|
%if !_CHS
|
|
.no_lba: equ .err
|
|
%else
|
|
.no_lba:
|
|
add sp, 8
|
|
pop cx ; cx = low word of sector
|
|
pop ax
|
|
pop dx ; dx:ax = middle two words of sector
|
|
; Here dx <= 1 if _LBA_33_BIT, else zero.
|
|
; If dx is nonzero then the CHS calculation
|
|
; should fail. If CHS sectors is equal to 1
|
|
; (very unusual) then the div may fail. Else,
|
|
; we will detect a cylinder > 1023 eventually.
|
|
pop si ; discard highest word of qword
|
|
%endif
|
|
%else
|
|
.err_2: equ .err
|
|
%endif
|
|
|
|
%if _CHS ; +70 bytes
|
|
; dx:ax = LBA sector number, (if _LBA) cx = 0
|
|
; divide by number of sectors per track to get sector number
|
|
; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability
|
|
; Use two-step 32:16 divide to avoid overflow
|
|
%if !_LBA
|
|
xchg cx, ax ; cx = low word of sector, clobbers ax
|
|
xchg ax, dx ; ax = high word of sector, clobbers dx
|
|
xor dx, dx ; dx:ax = high word of sector
|
|
%else
|
|
; from the .no_lba popping we already have:
|
|
; cx = low word of sector
|
|
; dx:ax = high word of sector
|
|
%endif
|
|
div word [VAR(sectors_per_track)]
|
|
xchg cx,ax
|
|
div word [VAR(sectors_per_track)]
|
|
xchg cx,dx
|
|
|
|
; DX:AX=quotient, CX=remainder=sector (S) - 1
|
|
; divide quotient by number of heads
|
|
xchg bx, ax ; bx = low word of quotient, clobbers ax
|
|
xchg ax, dx ; ax = high word of quotient, clobbers dx
|
|
xor dx, dx ; dx = 0
|
|
div word [VAR(heads)]
|
|
; ax = high / heads, dx = high % heads
|
|
xchg bx, ax ; bx = high / heads, ax = low quotient
|
|
div word [VAR(heads)]
|
|
|
|
; bx:ax=quotient=cylinder (C), dx=remainder=head (H)
|
|
; move variables into registers for INT 13h AH=02h
|
|
mov dh, dl ; dh = head
|
|
inc cx ; cl5:0 = sector
|
|
xchg ch, al ; ch = cylinder 7:0, al = 0
|
|
shr ax, 1
|
|
shr ax, 1 ; al7:6 = cylinder 9:8
|
|
; bx has bits set iff it's > 0, indicating a cylinder >= 65536.
|
|
or bl, bh ; collect set bits from bh
|
|
or cl, al ; cl7:6 = cylinder 9:8
|
|
; ah has bits set iff it was >= 4, indicating a cylinder >= 1024.
|
|
or bl, ah ; collect set bits from ah
|
|
mov dl,[VAR(boot_unit)] ; dl = drive
|
|
.nz_err:
|
|
jnz .err_2 ; error if cylinder >= 1024 -->
|
|
; ! bx = 0 (for 13.02 call)
|
|
|
|
; we call INT 13h AH=02h once for each sector. Multi-sector reads
|
|
; may fail if we cross a track or 64K boundary
|
|
%if _CHS_RETRY_REPEAT
|
|
mov si, _CHS_RETRY_REPEAT + 1
|
|
%if _CHS_RETRY_NORMAL && _RETRY_RESET
|
|
db __TEST_IMM16 ; (skip int 13h)
|
|
.loop_chs_retry_repeat:
|
|
int 13h ; reset disk
|
|
%elif _RETRY_RESET
|
|
.loop_chs_retry_repeat:
|
|
xor ax, ax
|
|
int 13h ; reset disk
|
|
%else
|
|
.loop_chs_retry_repeat:
|
|
%endif
|
|
dec si ; another attempt ?
|
|
js .nz_err ; no -->
|
|
mov ax, 0201h
|
|
int 13h ; read one sector
|
|
%if _CHS_RETRY_NORMAL && _RETRY_RESET
|
|
mov ax, bx ; ax = 0
|
|
%endif
|
|
jc .loop_chs_retry_repeat
|
|
; fall through to .done
|
|
%else
|
|
mov ax, 0201h
|
|
%if _CHS_RETRY
|
|
%if _RETRY_RESET
|
|
; In this case we cannot store to the stack and
|
|
; pop the value at the right moment for both
|
|
; cases of the "jnc .done" branch. So use the
|
|
; original code to re-init ax to 0201h.
|
|
int 13h ; read one sector
|
|
jnc .done
|
|
; reset drive
|
|
xor ax, ax
|
|
int 13h
|
|
mov ax, 0201h
|
|
%else
|
|
push ax
|
|
int 13h ; read one sector
|
|
pop ax ; restore ax = 0201h
|
|
jnc .done
|
|
%endif
|
|
%endif
|
|
; try read again
|
|
int 13h
|
|
jc .err_CY
|
|
%endif
|
|
%if ! _LBA
|
|
.err_CY: equ .err
|
|
%endif
|
|
|
|
%endif ; _CHS
|
|
|
|
.done:
|
|
; increment segment
|
|
mov bx, es
|
|
%if _FIX_SECTOR_SIZE
|
|
add bx, _FIX_SECTOR_SIZE >> 4
|
|
%else
|
|
add bx, [VAR(para_per_sector)]
|
|
%endif
|
|
|
|
pop si
|
|
pop ax
|
|
pop cx
|
|
pop dx
|
|
; increment LBA sector number
|
|
inc ax
|
|
jne @F
|
|
inc dx
|
|
@@:
|
|
|
|
.retn:
|
|
retn
|
|
|
|
%if _WARN_PART_SIZE
|
|
%assign num $ - read_sector
|
|
%warning read_sector size is num bytes
|
|
%endif
|
|
|
|
|
|
%if _ADD_SEARCH
|
|
add_name: ; = blank-padded 11-byte filename to search for
|
|
fill 8,32,db _ADD_NAME
|
|
fill 3,32,db _ADD_EXT
|
|
%endif
|
|
|
|
|
|
%if !_NO_LIMIT
|
|
available:
|
|
_fill 508,38,start
|
|
|
|
signatures:
|
|
dw 0
|
|
; 2-byte magic bootsector signature
|
|
dw 0AA55h
|
|
|
|
%assign num signatures-available
|
|
%warning FAT32: num bytes still available.
|
|
%else ; for testing
|
|
align 4
|
|
signatures:
|
|
dw 0
|
|
; 2-byte magic bootsector signature
|
|
dw 0AA55h
|
|
%endif
|
|
|
|
align 16, nop
|
|
end:
|
|
|
|
fsiboot:
|
|
dd "RRaA"
|
|
.signature:
|
|
fill 8, 32, db _FSIBOOTNAME
|
|
|
|
%if ($ - .signature) != 8
|
|
%error Unexpected name size
|
|
%endif
|
|
.start:
|
|
; INP: ax => after last segment to be used for loading
|
|
; bx => FAT buffer (8 KiB)
|
|
; ss:bp -> boot sector, with EBPB
|
|
; dwo [ss:bp - 4] = data_start (uninit)
|
|
; wo [ss:bp - 6] = load_seg (uninit)
|
|
; wo [ss:bp - 8] = fat_seg (uninit)
|
|
; dwo [ss:bp - 12] = fat_sector (uninit)
|
|
; dwo [ss:bp - 16] = first_cluster (uninit)
|
|
; wo [ss:bp - 18] = para_per_sector
|
|
; wo [ss:bp - 20] = entries_per_sector
|
|
; ss:sp = ss:bp - 20
|
|
; (Note: The following stack frame entries are currently
|
|
; only used by FSIBOOT itself, so they may be
|
|
; considered implementation detail instead of
|
|
; part of the FSIBOOT protocol.)
|
|
; wo [ss:bp - 22] = last_available_sector (uninit)
|
|
; wo [ss:bp - 24] = adj_sectors_per_cluster (uninit)
|
|
; wo [ss:bp - 26] = paras_left (uninit)
|
|
; Stack layout has to match!
|
|
; ss = ds
|
|
; ss:bp + ((11 + ebpbNew + BPBN_size + 1) & ~1)
|
|
; = ss:fsiboot_table, refer to its comments
|
|
; cs set to address jump table offsets in fsiboot_table
|
|
; ds set to address fsiboot_table.load_name
|
|
; OUT: Jumps to fsiboot_table.error if error occurs:
|
|
; al = error condition letter
|
|
; Else:
|
|
; data_start (within partition) initialised
|
|
; load_seg => behind last loaded data
|
|
; fat_seg initialised (from bx)
|
|
; fat_sector initialised
|
|
; (-1 initially, loaded sector-in-FAT later)
|
|
; first_cluster initialised
|
|
; last_available_sector initialised
|
|
; (from input ax minus word [para_per_sector])
|
|
; adj_sectors_per_cluster initialised (from BPB)
|
|
; paras_left initialised
|
|
; Jumps to fsiboot_table.success if loaded entirely:
|
|
; dword [ss:sp] = first cluster
|
|
; Jumps to fsiboot_table.memory_full if loaded partially:
|
|
; al = error condition letter ('M')
|
|
; dword [ss:sp] = current cluster number
|
|
; dword [ss:sp + 4] = first cluster
|
|
|
|
mov word [VAR(fat_seg)], bx ; initialise => FAT buffer
|
|
|
|
sub ax, word [VAR(para_per_sector)]
|
|
jc .CY_fsiboot_error_badchain
|
|
push ax ; push into word [VAR(last_available_sector)]
|
|
|
|
mov bx, word [VAR(fsiboot_table.loadseg)]
|
|
mov word [VAR(load_seg)], bx ; initialise => load address
|
|
cmp ax, bx
|
|
jb .CY_fsiboot_error_badchain
|
|
|
|
; adjusted sectors per cluster (store in a word,
|
|
; and decode EDR-DOS's special value 0 meaning 256)
|
|
mov al, [VAR(sectors_per_cluster)]
|
|
dec ax
|
|
mov ah, 0
|
|
inc ax
|
|
push ax ; push into word [VAR(adj_sectors_per_cluster)]
|
|
dec ax ; ! ah = 0
|
|
mov al,[VAR(num_fats)] ; ! ah = 0, hence ax = number of FATs
|
|
push ax
|
|
mul word [VAR(sectors_per_fat_large)]
|
|
; ax = low word SpF*nF
|
|
; dx = high word
|
|
xchg bx, ax
|
|
xchg cx, dx
|
|
; cx:bx = first mul
|
|
pop ax
|
|
mul word [VAR(sectors_per_fat_large + 2)]
|
|
; ax = high word adjust
|
|
; dx = third word
|
|
test dx, dx
|
|
stc
|
|
jnz .CY_fsiboot_error_badchain
|
|
; dx = zero
|
|
xchg dx, ax
|
|
; dx = high word adjust
|
|
; ax = zero
|
|
add dx, cx
|
|
jc .CY_fsiboot_error_badchain
|
|
; dx:bx = result
|
|
xchg ax, bx
|
|
; dx:ax = result
|
|
; bx = zero
|
|
|
|
add ax,[VAR(num_reserved_sectors)]
|
|
adc dx, bx ; bx is zero here
|
|
.CY_fsiboot_error_badchain:
|
|
jc ..@CY_2_fsiboot_error_badchain
|
|
|
|
; first sector of disk data area:
|
|
mov [VAR(data_start)], ax
|
|
mov [VAR(data_start+2)], dx
|
|
|
|
mov di, -1
|
|
mov si, di
|
|
mov word [VAR(fat_sector + 2)], si
|
|
mov word [VAR(fat_sector + 0)], di
|
|
call dirsearch
|
|
|
|
|
|
found_load_file:
|
|
call near word [VAR(fsiboot_table.writedirentry)]
|
|
|
|
; get starting cluster of file
|
|
push word [es:bx + deClusterHigh]
|
|
push word [es:bx + deClusterLow]
|
|
|
|
; check FAT+ size bits
|
|
test byte [es:bx + dePlusSize], 0E7h
|
|
; test whether bits 7-5 and 2-0 NZ
|
|
; https://web.archive.org/web/20150219123449/http://www.fdos.org/kernel/fatplus.txt
|
|
jnz .large_file ; yes, clamp to maximum paras -->
|
|
mov ax, [es:bx + deSize + 2]
|
|
mov bx, [es:bx + deSize]
|
|
; ax:bx = file size (non-FAT+)
|
|
|
|
mov cx, [VAR(bytes_per_sector)]
|
|
dec cx ; BpS - 1
|
|
add bx, cx
|
|
adc ax, 0 ; large ?
|
|
jc .large_file ; yes, clamp to maximum paras -->
|
|
|
|
not cx ; ~ (BpS - 1)
|
|
and bx, cx ; mask to limit to rounded-up sector
|
|
; (this also rounds up paragraphs)
|
|
mov cx, 4
|
|
@@:
|
|
shr ax, 1
|
|
rcr bx, 1
|
|
loop @B
|
|
; ax:bx = size in paragraphs
|
|
; bx = size in paragraphs if < 1_0000h
|
|
test ax, ax ; > 0FFFFh paras ?
|
|
jz @F ; no, take actual size -->
|
|
.large_file:
|
|
mov bx, 0FFFFh ; cx = clamp size to 0FFFFh paras
|
|
@@:
|
|
call check_enough.in_bx ; (CHG ax)
|
|
|
|
pop ax
|
|
pop dx ; dx:ax = first cluster
|
|
|
|
push bx ; push into word [VAR(paras_left)]
|
|
|
|
mov word [VAR(first_cluster + 0)], ax
|
|
mov word [VAR(first_cluster + 2)], dx
|
|
push dx
|
|
push ax ; remember cluster for later
|
|
|
|
call check_clust
|
|
..@CY_2_fsiboot_error_badchain:
|
|
jc ..@CY_3_fsiboot_error_badchain
|
|
|
|
next_load_cluster:
|
|
push dx
|
|
push ax ; preserve cluster number for later
|
|
call clust_to_first_sector
|
|
; dx:ax = first sector of cluster
|
|
; cx = adjusted sectors per cluster
|
|
|
|
; xxx - this will always load an entire cluster (e.g. 64 sectors),
|
|
; even if the file is shorter than this
|
|
@@:
|
|
mov bx, [VAR(load_seg)] ; => where to read next sector
|
|
cmp bx, [VAR(last_available_sector)]
|
|
jbe @F
|
|
call check_enough
|
|
mov al, 'M' ; (! _MEMORY_CONTINUE: error code)
|
|
jmp near word [VAR(fsiboot_table.memory_full)]
|
|
|
|
@@:
|
|
call near word [VAR(fsiboot_table.read_sector)]
|
|
mov [VAR(load_seg)], bx ; => after last read data
|
|
|
|
mov bx, [VAR(para_per_sector)]
|
|
sub word [VAR(paras_left)], bx
|
|
jbe @F ; read enough -->
|
|
|
|
loop @BB
|
|
pop ax
|
|
pop dx
|
|
|
|
call clust_next
|
|
jnc next_load_cluster
|
|
inc ax
|
|
inc ax
|
|
test al, 8 ; set in 0FFF_FFF8h--0FFF_FFFFh,
|
|
; clear in 0, 1, and 0FFF_FFF7h
|
|
jz fsiboot_error_badchain
|
|
db __TEST_IMM16
|
|
@@:
|
|
pop bx
|
|
pop cx
|
|
call check_enough
|
|
jmp near word [VAR(fsiboot_table.success)]
|
|
|
|
|
|
dirsearch:
|
|
mov ax, [VAR(root_cluster)]
|
|
mov dx, [VAR(root_cluster + 2)]
|
|
call check_clust
|
|
..@CY_3_fsiboot_error_badchain:
|
|
jc fsiboot_error_badchain
|
|
|
|
next_root_clust:
|
|
push dx
|
|
push ax
|
|
call clust_to_first_sector
|
|
; dx:ax = first sector of cluster
|
|
; cx = adjusted sectors per cluster
|
|
next_root_sect:
|
|
push cx
|
|
mov cx, [VAR(entries_per_sector)]
|
|
|
|
; Scan root directory for file. We don't bother to check for deleted
|
|
; entries (E5h) or entries that mark the end of the directory (00h).
|
|
mov bx, [VAR(fsiboot_table.dirbuf)]
|
|
call near word [VAR(fsiboot_table.read_sector)]
|
|
|
|
push di
|
|
xor di, di ; es:di-> first entry in this sector
|
|
next_ent:
|
|
test byte [es:di + deAttrib], ATTR_DIRECTORY | ATTR_VOLLABEL
|
|
jnz @F ; directory, label, or LFN entry --> (NZ)
|
|
push si
|
|
push di
|
|
push cx
|
|
mov si, [VAR(fsiboot_table.filename)]
|
|
; ds:si-> name to match
|
|
mov cx, 11 ; length of padded 8.3 FAT filename
|
|
repe cmpsb ; check entry
|
|
pop cx
|
|
pop di
|
|
pop si
|
|
@@: ; ZR = match, NZ = mismatch
|
|
je found_it ; found entry -->
|
|
lea di, [di + DIRENTRY_size]
|
|
|
|
loop next_ent ; count down sector's entries (jumps iff cx >0)
|
|
pop di
|
|
pop cx
|
|
loop next_root_sect
|
|
pop ax
|
|
pop dx
|
|
call clust_next
|
|
jnc next_root_clust
|
|
file_not_found:
|
|
mov al, 'F'
|
|
db __TEST_IMM16
|
|
fsiboot_error_badchain:
|
|
mov al, 'B'
|
|
|
|
fsiboot_error:
|
|
jmp near word [VAR(fsiboot_table.error)]
|
|
|
|
|
|
; INP: dx:ax = cluster - 2 (0-based cluster)
|
|
; OUT: dx:ax = first sector of that cluster
|
|
; cx = adjusted sectors per cluster
|
|
; CHG: bx
|
|
clust_to_first_sector:
|
|
mov cx, word [VAR(adj_sectors_per_cluster)]
|
|
push dx
|
|
mul cx
|
|
xchg bx, ax
|
|
pop ax
|
|
push dx
|
|
mul cx
|
|
test dx, dx
|
|
jnz fsiboot_error_badchain
|
|
xchg dx, ax
|
|
pop ax
|
|
add dx, ax
|
|
jc ..@CY_fsiboot_error_badchain
|
|
xchg ax, bx
|
|
|
|
add ax, [VAR(data_start)]
|
|
adc dx, [VAR(data_start + 2)]
|
|
..@CY_fsiboot_error_badchain:
|
|
jc fsiboot_error_badchain
|
|
; dx:ax = first sector in cluster
|
|
retn
|
|
|
|
|
|
check_enough:
|
|
mov bx, [VAR(load_seg)]
|
|
; => behind last read sector
|
|
sub bx, word [VAR(fsiboot_table.loadseg)]
|
|
.in_bx:
|
|
cmp bx, word [VAR(fsiboot_table.minpara)]
|
|
mov al, 'E'
|
|
jb fsiboot_error
|
|
retn
|
|
|
|
|
|
found_it:
|
|
; es:di -> dir entry in dir sector buffer
|
|
mov bx, di
|
|
pop di ; restore si:di = loaded FAT sector
|
|
pop cx ; (discard sectors per cluster counter)
|
|
pop cx
|
|
pop cx ; (discard current cluster number)
|
|
; es:bx -> dir entry
|
|
retn
|
|
|
|
|
|
; INP: dx:ax = cluster (0-based)
|
|
; si:di = loaded FAT sector, -1 if none
|
|
; OUT: CY if no next cluster
|
|
; NC if next cluster found
|
|
; dx:ax = next cluster value (0-based)
|
|
; si:di = loaded FAT sector
|
|
; CHG: cx, bx, es
|
|
clust_next:
|
|
add ax, 2
|
|
adc dx, 0
|
|
|
|
add ax, ax
|
|
adc dx, dx
|
|
add ax, ax
|
|
adc dx, dx ; * 4 = byte offset into FAT (0--4000_0000h)
|
|
push ax
|
|
xchg ax, dx
|
|
xor dx, dx ; dx:ax = high word
|
|
div word [VAR(bytes_per_sector)]
|
|
xchg bx, ax ; bx = result high word, clobbers ax
|
|
pop ax ; dx = remainder, ax = low word
|
|
div word [VAR(bytes_per_sector)]
|
|
xchg dx, bx ; dx:ax = result, bx = remainder
|
|
; dx:ax = sector offset into FAT (0--200_0000h)
|
|
; bx = byte offset into FAT sector (0--8190)
|
|
cmp dx, si
|
|
jne @F ; read sector
|
|
cmp ax, di
|
|
je @FF ; sector is already buffered
|
|
@@:
|
|
mov si, dx
|
|
mov di, ax
|
|
mov word [VAR(fat_sector + 2)], dx
|
|
mov word [VAR(fat_sector + 0)], ax
|
|
|
|
push bx
|
|
add ax, [VAR(fat_start)]
|
|
adc dx, 0
|
|
mov bx, [VAR(fat_seg)]
|
|
call near word [VAR(fsiboot_table.read_sector)]
|
|
pop bx
|
|
@@:
|
|
mov es, [VAR(fat_seg)]
|
|
mov ax, [es:bx]
|
|
mov dx, [es:bx + 2]
|
|
|
|
; INP: dx:ax = cluster value, 2-based
|
|
; OUT: dx:ax -= 2 (makes it 0-based)
|
|
; CY iff invalid cluster
|
|
check_clust:
|
|
and dh, 0Fh
|
|
sub ax, 2
|
|
sbb dx, 0
|
|
cmp dx, 0FFFh
|
|
jb @F ; CY here means valid ...-
|
|
cmp ax, 0FFF7h - 2
|
|
@@: ; -... or if NC first, CY here also
|
|
cmc ; NC if valid
|
|
retn
|
|
|
|
|
|
fsinfo_available:
|
|
_fill 480 + 4 - 2,38,fsiboot
|
|
|
|
%assign num $-fsinfo_available
|
|
%warning FSINFO: num bytes still available.
|
|
|
|
|
|
; INP: si:di = loaded sector-in-FAT
|
|
; word [fsiboot_table.filename] -> 8.3 filename
|
|
; CHG: ax, cx, dx
|
|
; OUT: si:di updated, if so
|
|
; es:bx -> found directory entry
|
|
; jumps to error handler if an error occurs
|
|
; STT: stack variables as set up by main FSIBOOT entry
|
|
dirsearch_entrypoint:
|
|
dw dirsearch
|
|
|
|
dd "rrAa"
|
|
dd -1 ; number of free clusters
|
|
dd -1 ; first free cluster
|
|
times 3 dd 0 ; FSINFO.reserved2
|
|
dd 0_AA55_0000h ; FSINFO.signature3
|
|
|
|
end_after_fsiboot:
|
|
%if $ - fsiboot != 512
|
|
%error Wrong FSIBOOT layout
|
|
%endif
|