1866 lines
47 KiB
NASM
1866 lines
47 KiB
NASM
|
|
%if 0
|
|
|
|
Create a boot image file (default: 1440 KiB diskette) in NASM
|
|
2019, by C. Masloch
|
|
|
|
Usage of the works is permitted provided that this
|
|
instrument is retained with the works, so that any entity
|
|
that uses the works is notified of this instrument.
|
|
|
|
DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
|
|
|
|
%endif
|
|
|
|
%if 0
|
|
|
|
_PAYLOADFILE must be defined to a list of names and/or keywords.
|
|
The filenames and keywords are separated by commas.
|
|
|
|
Filenames are accepted as tokens or strings. A plain filename that
|
|
does not belong to a preceeding keyword is taken as a file to add
|
|
to the image in the image's current directory. The entire name is
|
|
interpreted as a source pathname, passed to the incbin directive
|
|
in full, which in turn searches NASM's working directory and the
|
|
directories specified to NASM with -I switches. The last component
|
|
of the name, after the last slash or backslash, is converted into
|
|
allcaps and taken as the target filename.
|
|
|
|
Keywords are accepted only as (non-string) tokens. The available
|
|
keywords are:
|
|
|
|
::empty Ignored. Can be used to create an image without files.
|
|
|
|
::chdir Change into subdirectory. May be followed by a ..
|
|
(dotdot) keyword to back out of a directory.
|
|
Otherwise, the filename of the subdirectory to
|
|
create (relative to the current directory),
|
|
as a string, is expected in the next list item.
|
|
|
|
::fragment Followed by number of fragments, followed by length
|
|
(in number of clusters) of each fragment. Note that
|
|
if the file is shorter than the sum of fragment
|
|
lengths plus 1 cluster then it will have NUL bytes
|
|
appended so as to make sure the last cluster contains
|
|
one byte, and all prior clusters are full.
|
|
|
|
::fragmentsame Followed by number of fragments, followed by one value
|
|
that specifies the length of all the fragments.
|
|
|
|
::nextfragment Places next fragment of a fragmented file. At the
|
|
end of the image all remaining fragments are placed.
|
|
(The exact order that fragments appear in is not yet
|
|
fixed; only the first fragment is certain.)
|
|
|
|
::nextfragments Followed by number of fragments to place. Places the
|
|
next fragments of fragmented files. The number is
|
|
allowed to be higher than the amount of fragments
|
|
that remain to be placed; this is handled the same
|
|
as if that exact amount was specified.
|
|
|
|
::rename Followed by source pathname, then a target filename.
|
|
|
|
::fill Followed by length, byte value, target filename.
|
|
|
|
::endfill Followed by length, byte value, target filename.
|
|
Filename is written to the directory as usual.
|
|
The FAT chain is allocated after all other
|
|
non-::endfill files however. Additionally, if the
|
|
byte value is zero, the chain is allocated after
|
|
all non-zero-value ::endfill files. If _FULL=0
|
|
then the zero-value ::endfill data will not be
|
|
written at all, only the FAT entries are allocated.
|
|
If the value is given as 256 this is treated as
|
|
zero but not handled specifically to make use of
|
|
_FILL=0 like it is for the literal value 0.
|
|
|
|
::attrib Followed by attribute byte value. Sets what
|
|
attributes to use for the following directory
|
|
entries. ATTR_DIRECTORY and ATTR_VOLLABEL are
|
|
invalid to use for this. The _ATTRIB def is
|
|
used to set the initial default.
|
|
The valid values are: ATTR_READONLY,
|
|
ATTR_HIDDEN, ATTR_SYSTEM, ATTR_ARCHIVE, and
|
|
any combination of those.
|
|
|
|
::date Followed by numeric date value, eg 20110308.
|
|
The _FILEDATE def is used to set the initial
|
|
default. If _USE_FILE_DATETIME is set, this
|
|
defaults to NASM's __DATE_NUM__, else it
|
|
defaults to 1980_00_00.
|
|
|
|
::time Followed by numeric time value, eg 113842.
|
|
The _FILETIME def is used to set the initial
|
|
default. If _USE_FILE_DATETIME is set, this
|
|
defaults to NASM's __TIME_NUM__, else it
|
|
defaults to 0.
|
|
|
|
::setdirdatetime Special case: Set root directory
|
|
datetime to current setting, instead
|
|
of using default _FILEDATE _FILETIME.
|
|
(Only affects subdir dotdot entries.)
|
|
|
|
File and directory names are checked for duplicates. If a
|
|
string like ::chdir,'foo',::chdir,..,::chdir,'foo' is used,
|
|
the subdirectory will be detected as a duplicate. That is,
|
|
it is not valid to create/enter the same subdirectory twice.
|
|
|
|
The order of the files' directory entries and cluster chains in
|
|
the file system is determined by the order they were specified
|
|
in the list. Particularly, in FAT12 and FAT16 images, the first
|
|
two files specified will be placed consecutively at the start
|
|
of the data area, which may be necessary for some loaders.
|
|
As this is not known to be required by any FAT32 loaders,
|
|
the FAT32 root directory counts as the first file.
|
|
|
|
Files and directories are always written into a single run of
|
|
clusters each, that is, there is no fragmentation.
|
|
|
|
Some things are not supported yet:
|
|
|
|
* A volume label directory entry.
|
|
|
|
* Automatic choice of a volume serial number.
|
|
|
|
* Long file names.
|
|
|
|
* Boot sector loaders that are not exactly 512 bytes long.
|
|
|
|
* Automatic calculation of some parameters such as SPF.
|
|
|
|
|
|
A directory is always terminated with one or more directory
|
|
entries that are filled with all-zeros.
|
|
|
|
If _BOOTPATCHFILE is specified (not the empty quoted string),
|
|
then it is used with incbin to extract the boot loader's
|
|
jump instruction (3 bytes) and the boot code that's to be
|
|
placed after the BPBN (size depends on FAT type). The (E)BPB,
|
|
including BPBN, is then created by this script to match the
|
|
filesystem that is being written.
|
|
|
|
Else, if _BOOTFILE is specified (not the empty quoted string),
|
|
it is expected that the (E)BPB of that boot sector matches
|
|
the file system parameters specified to the bootimg.asm file.
|
|
If _BOOTFILE equals "" then a proper (E)BPB is created,
|
|
along with a default loader (which aborts with an error).
|
|
If _BOOTJUMPFILE and _BOOTCODEFILE are specified but
|
|
_BOOTFILE is not then these files are used to initialise the
|
|
first 512 bytes of the boot sector(s), excepting the (E)BPB.
|
|
The size of the jump file has to match 3 bytes or 11 bytes.
|
|
The size of the code file has to match the amount of bytes
|
|
remaining after the BPBN fields to fill up 512 bytes, or two
|
|
bytes less (for an 0AA55h signature), or four bytes less
|
|
(for an 0AA55_0000h signature).
|
|
|
|
If the FAT type is FAT32 and _FSINFO is set, then the def
|
|
_BOOTINFOFILE can be specified. If it is defined to the quoted
|
|
string "::bootpatchfile" then the FSIBOOT loader is extracted
|
|
from the file specified in _BOOTPATCHFILE, for 484 bytes that
|
|
start at offset 512 in the file. It is expected that the first
|
|
4 bytes of this area contain the FSINFO signature "RRaA".
|
|
|
|
Otherwise, if a file is specified as _BOOTINFOFILE (not the
|
|
empty quoted string) that file must be exactly 484 bytes long.
|
|
The first four bytes should again contain the FSINFO signature
|
|
reading "RRaA".
|
|
|
|
Otherwise the 480-bytes FSIBOOT area is zero-filled.
|
|
|
|
|
|
=== Examples
|
|
|
|
Empty default (90mm 1440 KiB FAT12 diskette) image:
|
|
|
|
nasm bootimg.asm -I ../lmacros/ -o bootimg.img -D_PAYLOADFILE=::empty
|
|
|
|
|
|
A FAT16 image with some fragmented data files,
|
|
and automatic calculation of a large enougn or
|
|
too large FAT:
|
|
|
|
nasm a.asm -o a.bin
|
|
nasm a.asm -o b.bin -DCONTENT="'b'"
|
|
nasm a.asm -o minus.bin -DCONTENT=-1
|
|
nasm bootimg.asm -o bootimg.img -I ../lmacros/ \
|
|
-D_BPE=16 -D_SPC=1 -D_SPI=$(( 1024 * 2 * 32 )) \
|
|
-D_SPF=$(( (1024 * 2 * 32 / 1 * 2 + 511) / 512 )) \
|
|
-D_NUMROOT=512 -D_ALIGNDATA \
|
|
-D_PAYLOADFILE=::fragmentsame,15,1,a.bin,\
|
|
::fragmentsame,7,1,b.bin,\
|
|
::nextfragments,1024,minus.bin
|
|
|
|
|
|
lDOS boot tests can also specify a custom _PAYLOADFILE def:
|
|
|
|
./test.sh diskette -D_PAYLOADFILE="::fragmentsame,3,1,testwrit.sys,\
|
|
result.txt,::nextfragment,::chdir,dir,::nextfragment,::rename,result.txt,result2"
|
|
|
|
|
|
A FAT32 image in an MBR partition, with a FAT32+FSIBOOT loader
|
|
read at build time, and some files loaded into it:
|
|
|
|
nasm bootimg.asm \
|
|
-D_WARN_DEFAULT_OFF=1 -D_WARN_TOOMANYFAT=0 -D_WARN_ALIGNDATA=0 \
|
|
-D_MBR -D_ALIGNDATA -D_CHS_HEADS=8 -D_CHS_SECTORS=8 \
|
|
-D_MBR_PART_TYPE=ptFAT32 \
|
|
-D_BPE=32 -D_SPC=1 -D_SPI=69632 -D_SPF=544 -D_NUMROOT=224 \
|
|
-o hdimage.img -l hdimage.lst \
|
|
-D_PAYLOADFILE=testwrit.sys,result.txt,::chdir,dir \
|
|
-D_BOOTPATCHFILE="'boot32tw.bin'" -D_UNIT=80h \
|
|
-I ../lmacros/ \
|
|
-D_BOOTINFOFILE=::bootpatchfile -D_MBRPATCHFILE=oldmbr.bin
|
|
|
|
|
|
A FAT32 image with auto-calculated FAT size, depicting some
|
|
uses of attributes and filetimes, wrapped into a dosemu2
|
|
image of an MBR-partitioned unit:
|
|
|
|
nasm -D_ATTRIB=ATTR_SYSTEM -I ../lmacros/ \
|
|
bootimg.asm -D_BPS=512 \
|
|
-D_PAYLOADFILE=::empty,::rename,"'test.dat'",test.bin,\
|
|
::time,030826,::setdirdatetime,::time,012638,\
|
|
::attrib,ATTR_HIDDEN+ATTR_READONLY+ATTR_SYSTEM,\
|
|
"'test.dat'",::chdir,testdir,"'test.dat'" \
|
|
-D_BPE=32 -D_SPI="(34 * 2 * 1024)" \
|
|
-D_SPF="((_SPI / _SPC * _BPE / 8 + _BPS - 1) / _BPS)" \
|
|
-D_SPC=1 -D_CHS_HEADS=8 -D_CHS_SECTORS=8 \
|
|
-D_MBR -D_MBR_DOSEMU_IMAGE_HEADER \
|
|
-D_BOOTPATCHFILE="'boot32tw.bin'" -D_BOOTINFOFILE="'::bootpatchfile'" \
|
|
-D_USE_FILE_DATETIME=1 -o part.img && mdir -i part.img@@80s -a ::.
|
|
|
|
%endif
|
|
|
|
|
|
%include "lmacros3.mac"
|
|
|
|
defaulting
|
|
sectalign off
|
|
|
|
numdef SPI, 2880 ; sectors per image
|
|
numdef BPS, 512 ; bytes per sector
|
|
numdef SPC, 1 ; sectors per cluster
|
|
numdef SPF, 9 ; sectors per FAT
|
|
numdef BPE, 12 ; bits per entry
|
|
numdef FSINFO, 1 ; (only if FAT32) include an FSINFO sector
|
|
numdef BACKUP, 1 ; (only if FAT32) include a boot sector backup
|
|
numdef FSI_INIT_FREE, 1 ; initialise FSINFO amount free clusters
|
|
numdef NUMFATS, 2 ; number of FATs
|
|
numdef NUMROOT, 224 ; number of root directory entries
|
|
%assign _SP512 (512 + _BPS - 1) / _BPS
|
|
; (512 + 32 - 1) / 32 = 16
|
|
; (512 + 64 - 1) / 64 = 8
|
|
; (512 + 512 - 1) / 512 = 1
|
|
; (512 + 1024 - 1) / 1024 = 1
|
|
numdef NUMRESERVED, _SP512
|
|
; number of reserved sectors
|
|
%if _BPE == 32 && (_FSINFO || _BACKUP)
|
|
numdef NUMRESERVED, _SP512 * 16
|
|
; number of reserved sectors
|
|
%endif
|
|
numdef ALIGNDATA, 0, 16 ; align data to this sector boundary
|
|
; (done by increasing _NUMRESERVED)
|
|
numdef FILLROOT, 1
|
|
numdef WARN_TOOMANYFAT, 1
|
|
numdef WARN_ALIGNDATA, 1
|
|
numdef WARN_FILLROOT, 1
|
|
numdef WARN_SMALL32, 1
|
|
numdef WARN_DEFAULT_OFF, 0
|
|
%if _WARN_DEFAULT_OFF
|
|
numdef WARN_TOOMANYFAT, 0
|
|
numdef WARN_ALIGNDATA, 0
|
|
numdef WARN_FILLROOT, 0
|
|
numdef WARN_SMALL32, 0
|
|
%endif
|
|
numdef ERROR_SMALL32, 1
|
|
numdef MEDIAID, 0F0h ; media ID
|
|
numdef EOF, 15 ; suffix of EOF entry marker
|
|
|
|
numdef UNIT, 0 ; load unit in BPB
|
|
numdef CHS_SECTORS, 18 ; CHS geometry field for sectors
|
|
numdef CHS_HEADS, 2 ; CHS geometry field for heads
|
|
numdef HIDDEN, 0 ; number of hidden sectors
|
|
|
|
strdef BOOTPATCHFILE, ""
|
|
strdef BOOTFILE, ""
|
|
strdef BOOTJUMPFILE, ""
|
|
strdef BOOTCODEFILE, ""
|
|
strdef BOOTINFOFILE, ""
|
|
gendef PAYLOADFILE, ""
|
|
gendef MAP, ""
|
|
|
|
strdef OEM_NAME, " lDOS"
|
|
strdef OEM_NAME_FILL, '_'
|
|
strdef DEFAULT_LABEL, "NO NAME"
|
|
numdef VOLUMEID, 0
|
|
numdef ATTRIB, 0
|
|
numdef FILEDATE, 1980_00_00
|
|
numdef FILETIME, 0
|
|
numdef USE_FILE_DATETIME, 0
|
|
%if _USE_FILE_DATETIME
|
|
numdef FILEDATE, __DATE_NUM__
|
|
numdef FILETIME, __TIME_NUM__
|
|
%endif
|
|
|
|
numdef MBR, 0
|
|
strdef MBRPATCHFILE, ""
|
|
strdef MBRCODEFILE, ""
|
|
gendef MBR_PART_TYPE, fat %+ _BPE
|
|
numdef MBR_DOSEMU_IMAGE_HEADER, 0
|
|
numdef MBR_GAP_SIZE_SECTORS, 0, 2048
|
|
numdef MBR_GAP_SIZE_CYLINDERS, 1
|
|
numdef MBR_ADD_GAP_TO_HIDDEN, 1
|
|
numdef MBR_ADD_GAP_TO_ALIGN, 1
|
|
|
|
|
|
numdef FULL, 1
|
|
numdef ZEROBYTES_RESB, 1
|
|
numdef ZEROBYTES_MAX, 4000_0000h
|
|
; Using the resb trick to zero takes about 1/40th
|
|
; the time of using times (for a 512 MiB image).
|
|
%if _ZEROBYTES_RESB
|
|
%imacro zerobytes 1.nolist
|
|
%if %1 < 0
|
|
%error Negative count given to zerobytes
|
|
%else
|
|
[warning -zeroing]
|
|
[warning -other]
|
|
%rep (%1) / (_ZEROBYTES_MAX)
|
|
resb (_ZEROBYTES_MAX)
|
|
%endrep
|
|
resb (%1) % (_ZEROBYTES_MAX)
|
|
[warning *other]
|
|
[warning *zeroing]
|
|
%endif
|
|
%endmacro
|
|
%else
|
|
%imacro zerobytes 1.nolist
|
|
%if %1 < 0
|
|
%error Negative count given to zerobytes
|
|
%else
|
|
%rep (%1) / (_ZEROBYTES_MAX)
|
|
times (_ZEROBYTES_MAX) db 0
|
|
%endrep
|
|
times (%1) % (_ZEROBYTES_MAX) db 0
|
|
%endif
|
|
%endmacro
|
|
%endif
|
|
|
|
|
|
numdef FILLBYTES_MAX, 4000_0000h
|
|
%imacro fillbytes 2.nolist
|
|
%ifn %2
|
|
zerobytes %1
|
|
%else
|
|
%rep (%1) / (_FILLBYTES_MAX)
|
|
times (_FILLBYTES_MAX) db %2
|
|
%endrep
|
|
times (%1) % (_FILLBYTES_MAX) db %2
|
|
%endif
|
|
%endmacro
|
|
|
|
|
|
%imacro zerobytes_if_full 1.nolist
|
|
%if _FULL
|
|
zerobytes %1
|
|
%endif
|
|
%endmacro
|
|
|
|
|
|
ptEmpty: equ 0
|
|
ptFAT12: equ 1
|
|
ptFAT16_16BIT_CHS: equ 4
|
|
ptExtendedCHS: equ 5
|
|
ptFAT16_CHS: equ 6
|
|
ptFAT32_CHS: equ 0Bh
|
|
ptFAT32: equ 0Ch
|
|
ptFAT16: equ 0Eh
|
|
ptExtended: equ 0Fh
|
|
ptLinux: equ 83h
|
|
ptExtendedLinux: equ 85h
|
|
|
|
%if _MBR
|
|
%if _MBR_GAP_SIZE_SECTORS == 0
|
|
%if _MBR_GAP_SIZE_CYLINDERS == 0
|
|
%error Invalid gap size specified
|
|
%else
|
|
%assign _MBR_GAP_SIZE_SECTORS _MBR_GAP_SIZE_CYLINDERS * _CHS_HEADS * _CHS_SECTORS
|
|
%endif
|
|
%endif
|
|
%if _MBR_ADD_GAP_TO_HIDDEN
|
|
%assign _HIDDEN _HIDDEN + _MBR_GAP_SIZE_SECTORS
|
|
%endif
|
|
%ifidni _MBR_PART_TYPE, fat12
|
|
%assign _MBR_PART_TYPE ptFAT12
|
|
%elifidni _MBR_PART_TYPE, fat16_16bit_chs
|
|
%assign _MBR_PART_TYPE ptFAT16_16BIT_CHS
|
|
%elifidni _MBR_PART_TYPE, fat16_chs
|
|
%assign _MBR_PART_TYPE ptFAT16_CHS
|
|
%elifidni _MBR_PART_TYPE, fat32_chs
|
|
%assign _MBR_PART_TYPE ptFAT32_CHS
|
|
%elifidni _MBR_PART_TYPE, fat32
|
|
%assign _MBR_PART_TYPE ptFAT32
|
|
%elifidni _MBR_PART_TYPE, fat16
|
|
%assign _MBR_PART_TYPE ptFAT16
|
|
%endif
|
|
%assign _MBR_PART_TYPE _MBR_PART_TYPE
|
|
%ifnnum _MBR_PART_TYPE
|
|
%error Invalid partition type specified
|
|
%endif
|
|
%if _MBR_PART_TYPE == 0
|
|
%error Invalid partition type specified
|
|
%endif
|
|
%endif
|
|
|
|
%ifnidn _MAP, ""
|
|
[map all _MAP]
|
|
%endif
|
|
|
|
%ifidn _PAYLOADFILE, ""
|
|
%error No payload files specified!
|
|
%endif
|
|
|
|
|
|
%if _BPE == 12
|
|
%elif _BPE == 16
|
|
%elif _BPE == 32
|
|
%else
|
|
%error Invalid BPE (_BPE)
|
|
%endif
|
|
|
|
%if _BPE != 32
|
|
%if _FILLROOT
|
|
%assign ii (_NUMROOT + _BPS / 32 - 1) & ~(_BPS / 32 - 1)
|
|
%if _WARN_FILLROOT && ii != _NUMROOT
|
|
%warning Expanding root to ii entries to fill last sector
|
|
%endif
|
|
%assign _NUMROOT ii
|
|
%endif
|
|
%assign ROOTSECTORS (_NUMROOT * 32 + _BPS - 1) / _BPS
|
|
%else
|
|
%assign ROOTSECTORS 0
|
|
%endif
|
|
%assign FATSECTORS _NUMFATS * _SPF
|
|
|
|
%if _ALIGNDATA
|
|
%assign ii 0
|
|
%if _MBR && _MBR_ADD_GAP_TO_ALIGN
|
|
%assign ii ii + _MBR_GAP_SIZE_SECTORS
|
|
%endif
|
|
%assign ii ii + _NUMRESERVED + ROOTSECTORS + FATSECTORS
|
|
%assign ii (ii + (_ALIGNDATA)) % (_ALIGNDATA)
|
|
%assign ii ((_ALIGNDATA) - ii) % (_ALIGNDATA)
|
|
%if ii
|
|
%if _WARN_ALIGNDATA
|
|
%warning Adding ii reserved sectors for %[_ALIGNDATA]-sector alignment
|
|
%endif
|
|
%endif
|
|
%assign _NUMRESERVED _NUMRESERVED + ii
|
|
%endif
|
|
|
|
%assign DATASECTORS (_SPI - _NUMRESERVED - FATSECTORS - ROOTSECTORS)
|
|
%assign DATACLUSTERS (DATASECTORS / _SPC)
|
|
%assign NUMENTRIES _SPF * _BPS * 8 / _BPE
|
|
%assign NUMSECTORS _SPF
|
|
%assign NUMNEEDED (DATACLUSTERS + 2)
|
|
%assign SECNEEDED (NUMNEEDED * _BPE / 8 + _BPS - 1) / _BPS
|
|
%if NUMENTRIES < NUMNEEDED
|
|
%error Too few FAT sectors specified \
|
|
(NUMENTRIES entries (NUMSECTORS sectors), NUMNEEDED needed (SECNEEDED sectors))
|
|
%elif NUMENTRIES >= (NUMNEEDED + _BPS * 8 / _BPE)
|
|
%if _WARN_TOOMANYFAT
|
|
%warning Too many FAT sectors specified \
|
|
(NUMENTRIES entries (NUMSECTORS sectors), NUMNEEDED needed (SECNEEDED sectors))
|
|
%endif
|
|
%endif
|
|
|
|
%macro detectionerror 1-2.nolist 0
|
|
%assign len 8
|
|
%assign ii 0
|
|
%assign sum 0
|
|
%define string ""
|
|
%rep len
|
|
%assign shift ((len - 1 - ii) * 4)
|
|
%assign digit (DATACLUSTERS >> shift) & 15
|
|
%assign sum sum + digit
|
|
%if sum
|
|
%substr digit "0123456789ABCDEF" digit + 1
|
|
%strcat string string,digit
|
|
%endif
|
|
%assign ii ii + 1
|
|
%endrep
|
|
%ifn sum
|
|
%define string "0"
|
|
%endif
|
|
%deftok string string
|
|
%if %2 && !_ERROR_SMALL32
|
|
%if _WARN_SMALL32
|
|
%warning FAT would be detected %1 (%[DATACLUSTERS] = %[string]h clusters)
|
|
%endif
|
|
%else
|
|
%error FAT would be detected %1 (%[DATACLUSTERS] = %[string]h clusters)
|
|
%endif
|
|
%endmacro
|
|
|
|
%if (DATACLUSTERS + 2) > 0FFF0h
|
|
%if _BPE == 12 || _BPE == 16
|
|
detectionerror as FAT32
|
|
%endif
|
|
%elif (DATACLUSTERS + 2) > 0FF0h
|
|
%if _BPE == 12
|
|
detectionerror as FAT16
|
|
%endif
|
|
%endif
|
|
%if (DATACLUSTERS + 2) < 1000h
|
|
%if _BPE == 16
|
|
detectionerror as FAT12
|
|
%endif
|
|
%if _BPE == 32
|
|
detectionerror as FAT12, 1
|
|
%endif
|
|
%elif (DATACLUSTERS + 2) < 10000h
|
|
%if _BPE == 32
|
|
detectionerror as FAT16, 1
|
|
%endif
|
|
%endif
|
|
|
|
%if _EOF < 8 || _EOF > 15
|
|
%error Invalid EOF value (_EOF)
|
|
%endif
|
|
|
|
%if _BPE != 32
|
|
%assign MAXENTRY (1 << _BPE) - 1
|
|
%else
|
|
%assign MAXENTRY (1 << 28) - 1
|
|
%endif
|
|
%assign EOFSTARTENTRY MAXENTRY - 7
|
|
%assign EOFENDENTRY MAXENTRY
|
|
%assign BADENTRY MAXENTRY - 8
|
|
%assign EOFENTRY MAXENTRY - 15 + _EOF
|
|
|
|
|
|
struc PARTINFO_CHS_TUPLE
|
|
pictHead: resb 1
|
|
pictSectorLow6:
|
|
pictCylinderHigh2: resb 1
|
|
pictCylinderLow8: resb 1
|
|
endstruc
|
|
|
|
%macro chs_tuple 1.nolist
|
|
%assign %%sector (%1) % _CHS_SECTORS
|
|
%assign %%i (%1) / _CHS_SECTORS
|
|
%assign %%head %%i % _CHS_HEADS
|
|
%assign %%cylinder %%i / _CHS_HEADS
|
|
%if %%cylinder >= 1024
|
|
%assign %%cylinder 1023
|
|
%endif
|
|
istruc PARTINFO_CHS_TUPLE
|
|
at pictHead, db %%head
|
|
at pictSectorLow6
|
|
at pictCylinderHigh2, db (%%sector + 1) | ((%%cylinder >> (8 - 6)) & 0C0h)
|
|
at pictCylinderLow8, db (%%cylinder & 0FFh)
|
|
iend
|
|
%endmacro
|
|
|
|
struc PARTINFO
|
|
piBoot: resb 1
|
|
piStartCHS: resb 3 ; PARTINFO_CHS_TUPLE
|
|
piType: resb 1
|
|
piEndCHS: resb 3 ; PARTINFO_CHS_TUPLE
|
|
piStart: resd 1
|
|
piLength: resd 1
|
|
endstruc
|
|
|
|
struc BS
|
|
bsJump: resb 3
|
|
bsOEM: resb 8
|
|
bsBPB:
|
|
endstruc
|
|
|
|
struc EBPB ; BPB sec
|
|
bpbBytesPerSector: resw 1 ; offset 00h 0Bh
|
|
bpbSectorsPerCluster: resb 1 ; offset 02h 0Dh
|
|
bpbReservedSectors: resw 1 ; offset 03h 0Eh
|
|
bpbNumFATs: resb 1 ; offset 05h 10h
|
|
bpbNumRootDirEnts: resw 1 ; offset 06h 11h -- 0 for FAT32
|
|
bpbTotalSectors: resw 1 ; offset 08h 13h
|
|
bpbMediaID: resb 1 ; offset 0Ah 15h
|
|
bpbSectorsPerFAT: resw 1 ; offset 0Bh 16h -- 0 for FAT32
|
|
bpbCHSSectors: resw 1 ; offset 0Dh 18h
|
|
bpbCHSHeads: resw 1 ; offset 0Fh 1Ah
|
|
bpbHiddenSectors: resd 1 ; offset 11h 1Ch
|
|
bpbTotalSectorsLarge: resd 1 ; offset 15h 20h
|
|
bpbNew: ; offset 19h 24h
|
|
|
|
ebpbSectorsPerFATLarge: resd 1 ; offset 19h 24h
|
|
ebpbFSFlags: resw 1 ; offset 1Dh 28h
|
|
ebpbFSVersion: resw 1 ; offset 1Fh 2Ah
|
|
ebpbRootCluster: resd 1 ; offset 21h 2Ch
|
|
ebpbFSINFOSector: resw 1 ; offset 25h 30h
|
|
ebpbBackupSector: resw 1 ; offset 27h 32h
|
|
ebpbReserved: resb 12 ; offset 29h 34h
|
|
ebpbNew: ; offset 35h 40h
|
|
endstruc
|
|
|
|
struc BPBN ; ofs B16 S16 B32 S32
|
|
bpbnBootUnit: resb 1 ; 00h 19h 24h 35h 40h
|
|
resb 1 ; 01h 1Ah 25h 36h 41h
|
|
bpbnExtBPBSignature: resb 1 ; 02h 1Bh 26h 37h 42h -- 29h for valid BPBN
|
|
bpbnSerialNumber: resd 1 ; 03h 1Ch 27h 38h 43h
|
|
bpbnVolumeLabel: resb 11 ; 07h 20h 2Bh 3Ch 47h
|
|
bpbnFilesystemID: resb 8 ; 12h 2Bh 36h 47h 52h
|
|
endstruc ; 1Ah 33h 3Eh 4Fh 5Ah
|
|
|
|
struc DIRENTRY
|
|
deName: resb 8
|
|
deExt: resb 3
|
|
deAttrib: resb 1
|
|
resb 8
|
|
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
|
|
|
|
struc DOSEMU_IMAGE_HEADER
|
|
; This structure is parsed when determining the geometry for
|
|
; a hard disk image. It is documented in the dosemu2 sources at:
|
|
; https://github.com/dosemu2/dosemu2/blob/f41c0f267fec/src/include/disks.h#L132
|
|
dihSig: resb 7 ; "DOSEMU",0 or 0Eh,"DEXE"
|
|
dihCHSHeads: resd 1 ; (note the misalignment for these)
|
|
dihCHSSectors: resd 1
|
|
dihCHSCylinders:resd 1
|
|
dihHeaderSize: resd 1
|
|
dihDummy: resb 1
|
|
dihDEXEFlags: resd 1
|
|
endstruc
|
|
dih_our_size: equ 8192 ; (16 * 512)
|
|
|
|
%imacro checkattrib 0
|
|
%if _ATTRIB & (ATTR_VOLLABEL | ATTR_DIRECTORY)
|
|
%error Invalid attribute: _ATTRIB
|
|
%endif
|
|
%endmacro
|
|
checkattrib
|
|
|
|
%imacro convertdatetime 0
|
|
%push
|
|
%assign %$year _FILEDATE / 1_00_00
|
|
%assign %$month _FILEDATE / 1_00 % 100
|
|
%assign %$day _FILEDATE % 100
|
|
%if %$year < 1980
|
|
%assign _FATFILEDATE 0 << 9 | 1 << 5 | 1
|
|
%elif (%$year - 1980) >= 128
|
|
%assign _FATFILEDATE 127 << 9 | 12 << 5 | 31
|
|
%else
|
|
%assign _FATFILEDATE (%$year - 1980) << 9 | %$month << 5 | %$day
|
|
%endif
|
|
%assign %$hour _FILETIME / 1_00_00
|
|
%assign %$minute _FILETIME / 1_00 % 100
|
|
%assign %$second _FILETIME % 100
|
|
%assign _FATFILETIME %$hour << 11 | %$minute << 5 | %$second >> 1
|
|
%pop
|
|
%endmacro
|
|
convertdatetime
|
|
|
|
|
|
%define STARTSFOLLOWS start=0
|
|
|
|
%if _MBR
|
|
CYLINDERS equ (_MBR_GAP_SIZE_SECTORS + _SPI + _CHS_HEADS * _CHS_SECTORS - 1) \
|
|
/ (_CHS_HEADS * _CHS_SECTORS)
|
|
|
|
|
|
%if _MBR_DOSEMU_IMAGE_HEADER
|
|
addsection dosemu_image_header, STARTSFOLLOWS
|
|
%define STARTSFOLLOWS follows=dosemu_image_header
|
|
istruc DOSEMU_IMAGE_HEADER
|
|
at dihSig, asciz "DOSEMU"
|
|
at dihCHSHeads, dd _CHS_HEADS
|
|
at dihCHSSectors, dd _CHS_SECTORS
|
|
at dihCHSCylinders, dd CYLINDERS
|
|
at dihHeaderSize, dd dih_our_size
|
|
iend
|
|
times dih_our_size - ($ - $$) db 0
|
|
%endif
|
|
|
|
|
|
addsection mbr, STARTSFOLLOWS vstart=7C00h
|
|
%define STARTSFOLLOWS follows=mbr
|
|
|
|
mbr_start:
|
|
%ifnidn _MBRPATCHFILE, ""
|
|
incbin _MBRPATCHFILE, 0, 512 - 2 - 4 * 16
|
|
%elifnidn _MBRCODEFILE, ""
|
|
incbin _MBRCODEFILE
|
|
%else
|
|
cli
|
|
cld
|
|
xor ax, ax
|
|
mov es, ax
|
|
mov ds, ax
|
|
mov ss, ax
|
|
mov sp, 7C00h
|
|
sti
|
|
mov si, mbr_message
|
|
@@:
|
|
lodsb
|
|
test al, al
|
|
jz @F
|
|
mov ah, 0Eh
|
|
mov bh, [462h]
|
|
mov bl, 7
|
|
int 10h
|
|
jmp @B
|
|
|
|
@@:
|
|
xor ax, ax
|
|
int 13h
|
|
xor ax, ax
|
|
int 16h
|
|
int 19h
|
|
|
|
mbr_message:
|
|
db "Unable to boot, MBR loader not written.",13,10
|
|
db 13,10
|
|
db "Press any key to reboot.",13,10
|
|
db 0
|
|
%endif
|
|
|
|
times (512 - 2 - 4 * 16) - ($ - $$) db 0
|
|
|
|
%assign TOTAL_MBR_SECTORS CYLINDERS * _CHS_HEADS * _CHS_SECTORS
|
|
mbr_partition_table:
|
|
istruc PARTINFO
|
|
at piBoot, db 80h ; active primary partition
|
|
at piStartCHS
|
|
chs_tuple _MBR_GAP_SIZE_SECTORS
|
|
at piType, db _MBR_PART_TYPE
|
|
at piEndCHS
|
|
chs_tuple (TOTAL_MBR_SECTORS - 1)
|
|
at piStart, dd _MBR_GAP_SIZE_SECTORS
|
|
at piLength, dd TOTAL_MBR_SECTORS - _MBR_GAP_SIZE_SECTORS
|
|
iend
|
|
|
|
times (512 - 2) - ($ - $$) db 0
|
|
mbr_signature:
|
|
dw 0AA55h
|
|
align _BPS, db 0
|
|
|
|
|
|
addsection gap, STARTSFOLLOWS
|
|
%define STARTSFOLLOWS follows=gap
|
|
|
|
zerobytes (_MBR_GAP_SIZE_SECTORS - 1) * _BPS
|
|
%endif
|
|
|
|
|
|
%imacro emit_boot 2
|
|
addsection %1_boot, STARTSFOLLOWS vstart=7C00h
|
|
%define STARTSFOLLOWS follows=%1_boot
|
|
|
|
%1_boot_start:
|
|
%ifnidn _BOOTPATCHFILE, ""
|
|
%assign USEBOOTFILE 0
|
|
%elifnidn _BOOTFILE, ""
|
|
%assign USEBOOTFILE 1
|
|
%else
|
|
%assign USEBOOTFILE 0
|
|
%endif
|
|
|
|
%if USEBOOTFILE
|
|
incbin _BOOTFILE
|
|
%else
|
|
%ifnidn _BOOTPATCHFILE, ""
|
|
incbin _BOOTPATCHFILE, 0, 3
|
|
%elifnidn _BOOTJUMPFILE, ""
|
|
incbin _BOOTJUMPFILE
|
|
%else
|
|
jmp strict short %1_boot_after_bpb
|
|
nop
|
|
%endif
|
|
%if ($ - $$) == bsOEM
|
|
%1_oem_id: ; offset 03h (03)
|
|
fill 8,_OEM_NAME_FILL,db _OEM_NAME
|
|
%endif
|
|
%if ($ - $$) != bsBPB
|
|
%error Invalid boot jump file length, must be 11 or 3
|
|
%endif
|
|
%1_bytes_per_sector: ; offset 0Bh (11)
|
|
dw _BPS
|
|
%1_sectors_per_cluster: ; offset 0Dh (13)
|
|
db _SPC & 255
|
|
%1_num_reserved_sectors:; offset 0Eh (14)
|
|
dw _NUMRESERVED
|
|
%1_num_fats: ; offset 10h (16)
|
|
db _NUMFATS
|
|
%1_num_root_dir_ents: ; offset 11h (17)
|
|
%if _BPE != 32
|
|
dw _NUMROOT
|
|
%else
|
|
dw 0
|
|
%endif
|
|
%1_total_sectors: ; offset 13h (19)
|
|
%if _SPI < 1_0000h
|
|
dw _SPI
|
|
%else
|
|
dw 0
|
|
%endif
|
|
%1_media_id: ; offset 15h (21)
|
|
db _MEDIAID
|
|
%1_sectors_per_fat: ; offset 16h (22)
|
|
%if _BPE != 32
|
|
dw _SPF
|
|
%else
|
|
dw 0
|
|
%endif
|
|
%1_sectors_per_track: ; offset 18h (24)
|
|
dw _CHS_SECTORS
|
|
%1_heads: ; offset 1Ah (26)
|
|
dw _CHS_HEADS
|
|
%1_hidden_sectors: ; offset 1Ch (28)
|
|
dd _HIDDEN
|
|
%1_total_sectors_large: ; offset 20h (32)
|
|
%if _SPI >= 1_0000h
|
|
dd _SPI
|
|
%else
|
|
dd 0
|
|
%endif
|
|
|
|
; Extended BPB ; offset 24h (36)
|
|
|
|
%if _BPE == 32
|
|
%1_sectors_per_fat_large:
|
|
; offset 24h (36)
|
|
dd _SPF
|
|
%1_fsflags: ; offset 28h (40)
|
|
dw 0
|
|
%1_fsversion: ; offset 2Ah (42)
|
|
dw 0
|
|
%1_root_cluster: ; offset 2Ch (44)
|
|
dd rootcluster
|
|
%1_fsinfo_sector: ; offset 30h (48)
|
|
%if _FSINFO
|
|
dw (%1_fsinfo - %1_boot_start) / _BPS
|
|
%else
|
|
dw 0
|
|
%endif
|
|
%1_backup_sector: ; offset 32h (50)
|
|
%if _BACKUP
|
|
dw (_boot_end_2 - _boot_start) / _BPS
|
|
%else
|
|
dw 0
|
|
%endif
|
|
|
|
times 12 db 0 ; offset 34h (52) reserved
|
|
|
|
; Extended BPB ; offset 40h (64)
|
|
%endif
|
|
|
|
%1_boot_unit: db _UNIT
|
|
db 0
|
|
%1_ext_bpb_signature: db 29h
|
|
%1_serial_number: dd _VOLUMEID
|
|
%1_volume_label: fill 11,32,db _DEFAULT_LABEL
|
|
%1_filesystem_identifier:
|
|
%if _BPE == 12
|
|
fill 8,32,db "FAT12"
|
|
%elif _BPE == 16
|
|
fill 8,32,db "FAT16"
|
|
%elif _BPE == 32
|
|
fill 8,32,db "FAT32"
|
|
%else
|
|
%error Invalid BPE
|
|
%endif
|
|
|
|
|
|
%1_boot_after_bpb:
|
|
%ifnidn _BOOTPATCHFILE, ""
|
|
incbin _BOOTPATCHFILE, ($ - $$), 512 - 2 - ($ - $$)
|
|
%elifnidn _BOOTCODEFILE, ""
|
|
incbin _BOOTCODEFILE
|
|
%else
|
|
cli
|
|
cld
|
|
xor ax, ax
|
|
mov es, ax
|
|
mov ds, ax
|
|
mov ss, ax
|
|
mov sp, 7C00h
|
|
sti
|
|
mov si, %1_boot_message
|
|
@@:
|
|
lodsb
|
|
test al, al
|
|
jz @F
|
|
mov ah, 0Eh
|
|
mov bh, [462h]
|
|
mov bl, 7
|
|
int 10h
|
|
jmp @B
|
|
|
|
@@:
|
|
xor ax, ax
|
|
int 13h
|
|
xor ax, ax
|
|
int 16h
|
|
int 19h
|
|
|
|
%1_boot_message:
|
|
db "Unable to boot, loader not written.",13,10
|
|
db 13,10
|
|
db "Press any key to reboot.",13,10
|
|
db 0
|
|
|
|
times 508 - ($ - $$) db 0
|
|
%endif
|
|
%endif
|
|
%if ($ - $$) == 508
|
|
dw 0
|
|
%endif
|
|
%if ($ - $$) == 510
|
|
dw 0AA55h
|
|
%endif
|
|
%1_boot_end:
|
|
|
|
%if (%1_boot_end - %1_boot_start) != 512
|
|
%ifnidn _BOOTFILE, ""
|
|
%error Boot file has wrong size, must be 512, 510, or 508
|
|
%elifnidn _BOOTCODEFILE, ""
|
|
%assign numberexpected1 512 - (%1_boot_after_bpb - $$)
|
|
%assign numberexpected2 numberexpected1 - 2
|
|
%assign numberexpected3 numberexpected1 - 4
|
|
%error Boot code file has wrong size, must be numberexpected1, numberexpected2, or numberexpected3
|
|
%else
|
|
%error Internal error, default boot sector loader has wrong size
|
|
%endif
|
|
%endif
|
|
%if _BPS > 512
|
|
_fill _BPS - 4, 0, %1_boot_start
|
|
dd 0AA55_0000h
|
|
%endif
|
|
|
|
%if _BPE == 32 && _FSINFO
|
|
%1_fsinfo:
|
|
istruc FSINFO
|
|
%ifnidn _BOOTINFOFILE, ""
|
|
%ifidni _BOOTINFOFILE, "::bootpatchfile"
|
|
incbin _BOOTPATCHFILE, 512, 484
|
|
%else
|
|
incbin _BOOTINFOFILE
|
|
%endif
|
|
%else
|
|
at FSINFO.signature1
|
|
dd "RRaA"
|
|
at FSINFO.reserved1
|
|
_fill 480 + 4, 0, %1_fsinfo
|
|
%endif
|
|
%if $ - %1_fsinfo != FSINFO.signature2
|
|
%error Boot info file has wrong size
|
|
%endif
|
|
at FSINFO.signature2
|
|
dd "rrAa"
|
|
at FSINFO.numberfree
|
|
%if _FSI_INIT_FREE && %2
|
|
dd DATACLUSTERS - USEDCLUSTERS
|
|
%else
|
|
dd -1 ; number of free clusters
|
|
%endif
|
|
at FSINFO.nextfree
|
|
dd -1 ; first free cluster
|
|
at FSINFO.reserved2
|
|
times 3 dd 0
|
|
at FSINFO.signature3
|
|
dd 0_AA55_0000h
|
|
iend
|
|
%if _BPS > 512
|
|
times (_BPS - 512 - 4) db 0
|
|
dd 0_AA55_0000h
|
|
%endif
|
|
%endif
|
|
|
|
%if _BACKUP && _BPE == 32
|
|
%if _BPS < 512
|
|
zerobytes (6 * 512) - ($ - $$)
|
|
%else
|
|
zerobytes (6 * _BPS) - ($ - $$)
|
|
%endif
|
|
%endif
|
|
%1_boot_end_2:
|
|
%assign bootused bootused + ($ - $$)
|
|
|
|
%endmacro
|
|
|
|
|
|
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
|
|
|
|
%assign bootused 0
|
|
emit_boot ,1
|
|
%if _BACKUP && _BPE == 32
|
|
emit_boot backup,0
|
|
%endif
|
|
zerobytes (_NUMRESERVED * _BPS) - bootused
|
|
|
|
|
|
addsection fat, STARTSFOLLOWS align=_BPS
|
|
|
|
|
|
; %1 = name for the equ to receive the start cluster,
|
|
; _start_cluster will be appended
|
|
; also name for the equ from which to read end entry,
|
|
; _chain_end will be appended
|
|
; %2 = how many clusters/entries to allocate
|
|
; (if zero, %1_start_cluster equ will be zero)
|
|
%macro addfatchain 2.nolist
|
|
%xdefine FATCHAINS FATCHAINS,%1,%2
|
|
%endmacro
|
|
%define FATCHAINS secondentry,1
|
|
secondentry_chain_end equ EOFENTRY
|
|
|
|
|
|
addsection fixed_root, follows=fat align=_BPS
|
|
|
|
|
|
; %1 = directory name ("" if is root)
|
|
; %2 = data section name to use
|
|
; %3 = data section name of dir to write to (none if is root)
|
|
; %$parentcluster = data cluster number of parent dir (0 if root is parent)
|
|
; %$parentattrib = attrib of parent dir (0 if root is parent)
|
|
; %$parentfiletime = file time of parent dir
|
|
; %$parentfiledate = file date of parent dir
|
|
%macro incdir 3.nolist
|
|
usesection %2
|
|
%2 %+ _start:
|
|
%ifnidni %1, ""
|
|
istruc DIRENTRY
|
|
at deName, fill 8,32,db "."
|
|
at deExt, fill 3,32,db 32
|
|
at deAttrib, db ATTR_DIRECTORY | _ATTRIB
|
|
at deClusterHigh, dw %%dir_start_cluster >> 16
|
|
at deTime, dw _FATFILETIME
|
|
at deDate, dw _FATFILEDATE
|
|
at deClusterLow, dw %%dir_start_cluster & 0FFFFh
|
|
at deSize, dd 0
|
|
iend
|
|
istruc DIRENTRY
|
|
at deName, fill 8,32,db ".."
|
|
at deExt, fill 3,32,db 32
|
|
at deAttrib, db ATTR_DIRECTORY | %$$parentattrib
|
|
at deClusterHigh, dw %$$parentcluster >> 16
|
|
at deTime, dw %$$parentfiletime
|
|
at deDate, dw %$$parentfiledate
|
|
at deClusterLow, dw %$$parentcluster & 0FFFFh
|
|
at deSize, dd 0
|
|
iend
|
|
%endif
|
|
|
|
%%dir_chain_end equ EOFENTRY
|
|
%xdefine CHAINLIST CHAINLIST,%%dir
|
|
|
|
%ifnidni %3, none
|
|
|
|
%define string %1
|
|
%ifnstr string
|
|
%defstr string string
|
|
%endif
|
|
parsename %1, 1
|
|
|
|
usesection %3
|
|
istruc DIRENTRY
|
|
at deName, fill 8,32,db %$$name
|
|
at deExt, fill 3,32,db %$$ext
|
|
at deAttrib, db ATTR_DIRECTORY | _ATTRIB
|
|
at deClusterHigh, dw %%dir_start_cluster >> 16
|
|
at deTime, dw _FATFILETIME
|
|
at deDate, dw _FATFILEDATE
|
|
at deClusterLow, dw %%dir_start_cluster & 0FFFFh
|
|
at deSize, dd 0
|
|
iend
|
|
%else
|
|
rootcluster equ %%dir_start_cluster
|
|
%endif
|
|
%2 %+ _start_cluster equ %%dir_start_cluster
|
|
%endmacro
|
|
|
|
|
|
; %1 = source pathname
|
|
; %2 = offset behind data already read
|
|
; %3 = fixed, 0
|
|
; %4 = size of fragment in amount clusters, nonzero
|
|
; %5 only defined if more fragments to follow
|
|
;
|
|
; Recreates the FRAGMENTLIST smacro by defining
|
|
; it to 0 then appending %5 and subsequent
|
|
; parameters, if any.
|
|
; Sets FRAGMENTOFFSET to the next offset after
|
|
; the data possibly read in by this mmacro.
|
|
; If the file is shorter, this mmacro will fill
|
|
; the space with appended NUL bytes to use up
|
|
; the specified amount clusters.
|
|
%macro incbinpart 4-*.nolist
|
|
%%start:
|
|
incbin %1, %2, %4 * _BPS * _SPC
|
|
%assign FRAGMENTOFFSET %2 + %4 * _BPS * _SPC
|
|
%define FRAGMENTLIST 0
|
|
%rotate 4
|
|
%rep %0 - 4
|
|
%xdefine FRAGMENTLIST FRAGMENTLIST, %1
|
|
%rotate 1
|
|
%endrep
|
|
%if ($ - %%start) < %4 * _BPS * _SPC
|
|
%assign TAIL %4 * _BPS * _SPC - ($ - %%start)
|
|
%warning File %1 is shorter than expected, appending TAIL NUL bytes
|
|
times TAIL db 0
|
|
%endif
|
|
%endmacro
|
|
|
|
|
|
%macro nextfragment 6-*.nolist
|
|
usesection %1
|
|
%1 %+ _start:
|
|
%3_chain_end equ %%fragment_start_cluster
|
|
; equate prior fragment's end entry to start of this fragment
|
|
%xdefine %%sourcename %4 ; source pathname to read from
|
|
%xdefine %%offset %5 ; offset to -> after already read data
|
|
%xdefine %%oldfragmentlist FRAGMENTLIST
|
|
; if we have one preserve the new FRAGMENTLIST
|
|
%xdefine FRAGMENTLIST %6 ; set up our remaining as the current list
|
|
|
|
; In this mmacro we eat the next fragment to allocate.
|
|
; %1 = section name, %2 = "none" token, %3 = fragment
|
|
; identifier for prior fragment (macro-local name),
|
|
; %4 = pathname, %5 = file offset, %6 = FRAGMENTLIST
|
|
; (which starts with 0 and may have embedded commas).
|
|
; So %7 and up are the parameters of the subsequent
|
|
; fragment, if any, in groups of four.
|
|
%define NEXTFRAGMENTS none ; reset the list to a none token
|
|
%rotate 6 ; rotate past the section name, none,
|
|
; and all four of our current fragment
|
|
%assign COUNTER 1
|
|
%rep %0 - 6 ; repeat for all remaining parameters
|
|
%if COUNTER % 4
|
|
%xdefine NEXTFRAGMENTS NEXTFRAGMENTS, %1
|
|
; append this parameter
|
|
%else
|
|
%xdefine NEXTFRAGMENTS NEXTFRAGMENTS, {%1}
|
|
; this is a FRAGMENTLIST, so we make
|
|
; sure to pass along curly braces
|
|
%endif
|
|
%rotate 1 ; next parameter
|
|
%assign COUNTER COUNTER + 1
|
|
%endrep ; note: rotated 6 + %0 - 6 times equals
|
|
; %0 times, all back to the beginning
|
|
|
|
%ifidn FRAGMENTLIST, 0 ; if no further fragment
|
|
%%start:
|
|
incbin %%sourcename, %%offset ; include remaining data
|
|
%%fragment_chain_end equ EOFENTRY
|
|
; tell FAT cnain allocation to end after this
|
|
%if ($ - %%start) == 0
|
|
%warning File %%sourcename is shorter than expected, appending a NUL byte
|
|
db 0 ; for proper FAT chain size mustn't be zero
|
|
%endif
|
|
%3_next_size equ ($ - %1 %+ _start)
|
|
; communicate to prior fragment what its
|
|
; next size (ie, this fragment's) is
|
|
%else
|
|
incbinpart %%sourcename, %%offset, FRAGMENTLIST
|
|
; incbin a part, specified by this mmacro's
|
|
; %4, and re-create FRAGMENTLIST
|
|
%xdefine NEXTFRAGMENTS \
|
|
NEXTFRAGMENTS, %%fragment, %%sourcename, FRAGMENTOFFSET, \
|
|
{FRAGMENTLIST} ; remember at the end of NEXTFRAGMENTS list
|
|
%define FRAGMENTLIST 0 ; reset FRAGMENTLIST
|
|
%3_next_size equ ($ - %1 %+ _start) + %%fragment_next_size
|
|
; communicate to prior fragment what its
|
|
; next size (ie, this fragment's) is
|
|
; this includes the size of the fragment
|
|
; that's subsequent to this one, so it
|
|
; includes this fragment and all the
|
|
; fragments that are still to come.
|
|
%endif
|
|
|
|
%xdefine CHAINLIST CHAINLIST,%%fragment
|
|
; remember base name for this fragment's chain
|
|
|
|
%xdefine FRAGMENTLIST %%oldfragmentlist
|
|
; preserve current FRAGMENTLIST for new file
|
|
%endmacro
|
|
|
|
|
|
; %1 = source filename, may include a directory
|
|
; (the directory is used to locate
|
|
; the file on the host system)
|
|
; %2 = target filename, may include a directory
|
|
; (directory is stripped, base name is
|
|
; used in all-caps to store to the image)
|
|
; %3 = data section name to use
|
|
; %4 = data section name of directory to write to
|
|
; %5 = nonzero if filling data
|
|
; %6 = size of data to fill
|
|
; %7 = value to fill
|
|
; isrename = nonzero if ::rename
|
|
; isfill = nonzero if ::fill
|
|
%macro incfile 4-7.nolist 0
|
|
usesection %3
|
|
%3 %+ _start:
|
|
%if %5
|
|
fillbytes %6, %7
|
|
%%fragment_chain_end equ EOFENTRY
|
|
%%filesize equ ($ - %3 %+ _start)
|
|
%else
|
|
%define string %1
|
|
%ifnstr string
|
|
%defstr string string
|
|
%endif
|
|
%xdefine %%sourcename string
|
|
%ifidn FRAGMENTLIST, 0
|
|
incbin %%sourcename
|
|
%%fragment_chain_end equ EOFENTRY
|
|
%%filesize equ ($ - %3 %+ _start)
|
|
%else
|
|
incbinpart %%sourcename, 0, FRAGMENTLIST
|
|
%xdefine NEXTFRAGMENTS \
|
|
NEXTFRAGMENTS, %%fragment, %%sourcename, FRAGMENTOFFSET, \
|
|
{FRAGMENTLIST}
|
|
%define FRAGMENTLIST 0
|
|
%%filesize equ ($ - %3 %+ _start) + %%fragment_next_size
|
|
%endif
|
|
%endif
|
|
|
|
%xdefine CHAINLIST CHAINLIST,%%fragment
|
|
|
|
%define string %2
|
|
%ifnstr string
|
|
%defstr string string
|
|
%endif
|
|
parsename %2, !!(isrename || isfill)
|
|
|
|
usesection %4
|
|
istruc DIRENTRY
|
|
at deName, fill 8,32,db %$$name
|
|
at deExt, fill 3,32,db %$$ext
|
|
at deAttrib, db _ATTRIB
|
|
at deClusterHigh, dw %%fragment_start_cluster >> 16
|
|
at deTime, dw _FATFILETIME
|
|
at deDate, dw _FATFILEDATE
|
|
at deClusterLow, dw %%fragment_start_cluster & 0FFFFh
|
|
at deSize, dd %%filesize
|
|
iend
|
|
%endmacro
|
|
|
|
|
|
; %1 = filename
|
|
; %2 = data section name of directory to write to
|
|
; %3 = fill length
|
|
; %4 = fill value
|
|
%imacro incendfill 4.nolist
|
|
%if %4
|
|
%if %4 == 256
|
|
%xdefine LISTENDFILL LISTENDFILL, %%file, %3, 0
|
|
%else
|
|
%xdefine LISTENDFILL LISTENDFILL, %%file, %3, %4
|
|
%endif
|
|
%else
|
|
%xdefine LISTENDFILLZEROS LISTENDFILLZEROS, %%file, %3, %4
|
|
%endif
|
|
|
|
%define string %1
|
|
%ifnstr string
|
|
%defstr string string
|
|
%endif
|
|
parsename %1, 1
|
|
|
|
usesection %2
|
|
istruc DIRENTRY
|
|
at deName, fill 8,32,db %$$name
|
|
at deExt, fill 3,32,db %$$ext
|
|
at deAttrib, db _ATTRIB
|
|
at deClusterHigh, dw %%file_start_cluster >> 16
|
|
at deTime, dw _FATFILETIME
|
|
at deDate, dw _FATFILEDATE
|
|
at deClusterLow, dw %%file_start_cluster & 0FFFFh
|
|
at deSize, dd %3
|
|
iend
|
|
%endmacro
|
|
|
|
|
|
; %1 = filename, with directory as on host side
|
|
; string = stringified filename, still with host path
|
|
; %2 = if slash disallowed in pathname
|
|
; returns result in %$name, %$ext, and %$namelist
|
|
%macro parsename 1-2.nolist 0
|
|
%strlen length string
|
|
%assign ii 1
|
|
%rep length
|
|
%substr cc string length - ii + 1
|
|
%ifidn cc,"/"
|
|
%if %2
|
|
%if ischdir
|
|
%error Subsubdirectory not supported, use ::chdir twice
|
|
%elif isrename
|
|
%error Subdirectory not supported in ::rename target
|
|
%elif isfill
|
|
%error Subdirectory not supported in ::fill target
|
|
%else
|
|
%error Subdirectory not supported
|
|
%endif
|
|
%endif
|
|
%substr string string length -ii + 2, -1
|
|
%exitrep
|
|
%endif
|
|
%ifidn cc,"\"
|
|
%if %2
|
|
%if ischdir
|
|
%error Subsubdirectory not supported, use ::chdir twice
|
|
%elif isrename
|
|
%error Subdirectory not supported in ::rename target
|
|
%elif isfill
|
|
%error Subdirectory not supported in ::fill target
|
|
%else
|
|
%error Subdirectory not supported
|
|
%endif
|
|
%endif
|
|
%substr string string length -ii + 2, -1
|
|
%exitrep
|
|
%endif
|
|
%assign ii ii + 1
|
|
%endrep
|
|
%strlen length string
|
|
%assign ii 1
|
|
%define %$name ""
|
|
%define %$ext ""
|
|
%assign dotyet 0
|
|
%rep length
|
|
%substr cc string ii
|
|
%assign ii ii + 1
|
|
%if cc >= 'a' && cc <= 'z'
|
|
%substr cc "ABCDEFGHIJKLMNOPQRSTUVWXYZ" (cc - 'a' + 1)
|
|
%endif
|
|
%ifn dotyet
|
|
%ifidn cc,"."
|
|
%assign dotyet 1
|
|
%else
|
|
%strlen ll %$name
|
|
%if ll >= 8
|
|
%error Too long name part in %1
|
|
%exitrep
|
|
%endif
|
|
checkchar %1,cc
|
|
%strcat %$name %$name,cc
|
|
%endif
|
|
%else
|
|
%strlen ll %$ext
|
|
%if ll >= 3
|
|
%error Too long ext part in %1
|
|
%exitrep
|
|
%else
|
|
checkchar %1,cc,"."
|
|
%strcat %$ext %$ext,cc
|
|
%endif
|
|
%endif
|
|
%endrep
|
|
%ifidn %$name,""
|
|
%error Invalid empty name part in %1
|
|
%endif
|
|
|
|
addnametonamelist %$namelist
|
|
%endmacro
|
|
|
|
%macro checkchar 2-3.nolist 0
|
|
%if %2 <= ' ' || %2 >= 128 || \
|
|
%2 == '/' || %2 == '\' || \
|
|
%2 == '"' || %2 == %3
|
|
%error Invalid character (%2) in name (%1)
|
|
%endif
|
|
%endmacro
|
|
|
|
; %$name = (string) filename part, 1 to 8 characters, all caps
|
|
; %$ext = (string) file extension part, 0 to 3 characters, all caps
|
|
; %3 = first saved name part in name list
|
|
; %4 = first saved extension part in name list
|
|
; %5, %6 = next filename in name list
|
|
; %$namelist = name list (pairs of name parts, extension parts)
|
|
%macro addnametonamelist 2-*.nolist
|
|
%ifnidn %1, ""
|
|
%error Expected first list entry to be empty
|
|
%elifnidn %2, ""
|
|
%error Expected first list entry to be empty
|
|
%elif %0 & 1
|
|
%error Expected list to contain even number of entries
|
|
%endif
|
|
%rotate 2
|
|
%rep (%0 - 2) / 2
|
|
%ifidn %1, %$name
|
|
%ifidn %2, %$ext
|
|
%error Duplicate filename %$name %+ . %+ %$ext in directory %$dirname
|
|
%exitrep
|
|
%endif
|
|
%endif
|
|
%rotate 2
|
|
%endrep
|
|
%xdefine %$namelist %$namelist, %$name, %$ext
|
|
%endmacro
|
|
|
|
|
|
%macro setup_a_section 0.nolist
|
|
addsection data %+ filescount, follows= %+ ff vstart=0
|
|
%xdefine ff data %+ filescount
|
|
%assign filescount filescount + 1
|
|
%endmacro
|
|
|
|
|
|
%define DIRLIST none
|
|
%define CHAINLIST none
|
|
%define FRAGMENTLIST 0
|
|
%define NEXTFRAGMENTS none
|
|
%define LISTENDFILL none, none, none
|
|
%define LISTENDFILLZEROS none, none, none
|
|
%macro multiincfile 1-*.nolist
|
|
%assign filescount 0
|
|
%define ff fixed_root
|
|
%assign chainindex 0
|
|
%assign ischdir 0
|
|
%assign isfragment 0
|
|
%assign isfragmentsame 0
|
|
%assign isnextfragments 0
|
|
%assign isrename 0
|
|
%assign isfill 0
|
|
%assign isattrib 0
|
|
%assign isdatetime 0
|
|
%assign isdate 0
|
|
%assign istime 0
|
|
%push ROOT
|
|
%define %$namelist "", ""
|
|
%define %$dirname "/"
|
|
%if _BPE == 32
|
|
setup_a_section ; add a section for root directory
|
|
incdir "", data %+ chainindex, none
|
|
%xdefine nextdir data %+ chainindex
|
|
%xdefine DIRLIST DIRLIST,nextdir
|
|
%xdefine %$parent nextdir
|
|
; %xdefine %$parentcluster %$parent %+ _start_cluster
|
|
%define %$parentcluster 0
|
|
%assign chainindex chainindex + 1
|
|
%else
|
|
%define %$parent fixed_root
|
|
%define %$parentcluster 0
|
|
%endif
|
|
%assign %$parentattrib 0
|
|
%assign %$parentfiletime _FATFILETIME
|
|
%assign %$parentfiledate _FATFILEDATE
|
|
%rep %0
|
|
%if ischdir
|
|
%ifidni %1, ..
|
|
%ifctx ROOT
|
|
%error Cannot back out over root
|
|
%else
|
|
%pop
|
|
%endif
|
|
%else
|
|
setup_a_section ; add a section for a directory
|
|
incdir %1, data %+ chainindex, %$parent
|
|
%push SUB
|
|
%define %$namelist "", ""
|
|
%strcat %$dirname %$$dirname, %$$name, ".", %$$ext, "/"
|
|
%xdefine nextdir data %+ chainindex
|
|
%xdefine DIRLIST DIRLIST,nextdir
|
|
%xdefine %$parent nextdir
|
|
%xdefine %$parentcluster %$parent %+ _start_cluster
|
|
%assign %$parentattrib _ATTRIB
|
|
%assign %$parentfiletime _FATFILETIME
|
|
%assign %$parentfiledate _FATFILEDATE
|
|
%assign chainindex chainindex + 1
|
|
%endif
|
|
%assign ischdir 0
|
|
%elif isfragment == -1
|
|
%assign isfragment %1
|
|
%elif isfragment == -2
|
|
%assign isfragmentsame %1
|
|
%assign isfragment 0
|
|
%elif isfragment
|
|
%ifn %1
|
|
%error Zero clusters fragment is invalid
|
|
%endif
|
|
%assign ONEFRAGMENT %1
|
|
%xdefine FRAGMENTLIST FRAGMENTLIST, ONEFRAGMENT
|
|
%assign isfragment isfragment - 1
|
|
%elif isfragmentsame
|
|
%ifn %1
|
|
%error Zero clusters fragment is invalid
|
|
%endif
|
|
%assign ONEFRAGMENT %1
|
|
%rep isfragmentsame
|
|
%xdefine FRAGMENTLIST FRAGMENTLIST, ONEFRAGMENT
|
|
%endrep
|
|
%assign isfragmentsame 0
|
|
%elif isnextfragments
|
|
%rep %1
|
|
%ifidn NEXTFRAGMENTS, none
|
|
%exitrep
|
|
%else
|
|
setup_a_section ; add a section for next data fragment
|
|
nextfragment data %+ chainindex, NEXTFRAGMENTS
|
|
%assign chainindex chainindex + 1
|
|
%endif
|
|
%endrep
|
|
%assign isnextfragments 0
|
|
%elif isrename == 1
|
|
%xdefine renamesource %1
|
|
%assign isrename 2
|
|
%elif isrename == 2
|
|
setup_a_section ; add a section for a file
|
|
incfile renamesource, %1, data %+ chainindex, %$parent
|
|
%assign chainindex chainindex + 1
|
|
%assign isrename 0
|
|
%elif isfill == 1 || isfill == 4
|
|
%xdefine filllength %1
|
|
%assign isfill isfill + 1
|
|
%elif isfill == 2 || isfill == 5
|
|
%xdefine fillvalue %1
|
|
%assign isfill isfill + 1
|
|
%elif isfill == 3
|
|
setup_a_section ; add a section for a file
|
|
incfile "", %1, data %+ chainindex, %$parent, 1, filllength, fillvalue
|
|
%assign chainindex chainindex + 1
|
|
%assign isfill 0
|
|
%elif isfill == 6
|
|
incendfill %1, %$parent, filllength, fillvalue
|
|
%assign isfill 0
|
|
%elif isattrib
|
|
%assign _ATTRIB %1
|
|
checkattrib
|
|
%assign isattrib 0
|
|
%elif isdatetime == 1
|
|
%assign _FILEDATE %1
|
|
%assign isdatetime 2
|
|
%elif isdatetime == 2
|
|
%assign _FILETIME %1
|
|
convertdatetime
|
|
%assign isdatetime 0
|
|
%elif isdate
|
|
%assign _FILEDATE %1
|
|
convertdatetime
|
|
%assign isdate 0
|
|
%elif istime
|
|
%assign _FILETIME %1
|
|
convertdatetime
|
|
%assign istime 0
|
|
%elifidni %1, ::chdir
|
|
%assign ischdir 1
|
|
%elifidni %1, ::fragment
|
|
%assign isfragment -1
|
|
%elifidni %1, ::fragmentsame
|
|
%assign isfragment -2
|
|
%elifidni %1, ::nextfragment
|
|
%ifidn NEXTFRAGMENTS, none
|
|
%error No next fragments to write
|
|
%endif
|
|
setup_a_section ; add a section for next data fragment
|
|
nextfragment data %+ chainindex, NEXTFRAGMENTS
|
|
%assign chainindex chainindex + 1
|
|
%elifidni %1, ::nextfragments
|
|
%assign isnextfragments 1
|
|
%elifidni %1, ::rename
|
|
%assign isrename 1
|
|
%elifidni %1, ::fill
|
|
%assign isfill 1
|
|
%elifidni %1, ::endfill
|
|
%assign isfill 4
|
|
%elifidni %1, ::attrib
|
|
%assign isattrib 1
|
|
%elifidni %1, ::datetime
|
|
%assign isdatetime 1
|
|
%elifidni %1, ::date
|
|
%assign isdate 1
|
|
%elifidni %1, ::time
|
|
%assign istime 1
|
|
%elifidni %1, ::setdirdatetime
|
|
%assign %$parentfiletime _FATFILETIME
|
|
%assign %$parentfiledate _FATFILEDATE
|
|
%elifnidni %1, ::empty
|
|
%ifidn %1, ""
|
|
%error Invalid filename
|
|
%elifempty %1
|
|
%error Invalid filename
|
|
%else
|
|
setup_a_section ; add a section for a file
|
|
incfile %1, %1, data %+ chainindex, %$parent
|
|
%assign chainindex chainindex + 1
|
|
%endif
|
|
%endif
|
|
%rotate 1
|
|
%endrep
|
|
%if ischdir
|
|
%error No directory to create specified
|
|
%endif
|
|
%if isfragment
|
|
%error No fragment list specified
|
|
%endif
|
|
%if isfragmentsame
|
|
%error No fragment size specified
|
|
%endif
|
|
%if isnextfragments
|
|
%error No amount of fragments specified
|
|
%endif
|
|
%if isrename == 1
|
|
%error No rename source specified
|
|
%endif
|
|
%if isrename == 2
|
|
%error No rename target specified
|
|
%endif
|
|
%if isfill == 1 || isfill == 4
|
|
%error No fill length specified
|
|
%endif
|
|
%if isfill == 2 || isfill == 5
|
|
%error No fill value specified
|
|
%endif
|
|
%if isfill == 3 || isfill == 6
|
|
%error No fill target specified
|
|
%endif
|
|
%if isattrib
|
|
%error No attributes value specified
|
|
%endif
|
|
%if isdatetime == 1
|
|
%error No datetime date value specified
|
|
%endif
|
|
%if isdatetime == 2
|
|
%error No datetime time value specified
|
|
%endif
|
|
%if isdate
|
|
%error No date value specified
|
|
%endif
|
|
%if istime
|
|
%error No time value specified
|
|
%endif
|
|
%rep 1024
|
|
%ifctx ROOT
|
|
%exitrep
|
|
%else
|
|
%pop
|
|
%endif
|
|
%endrep
|
|
%pop
|
|
%endmacro
|
|
multiincfile _PAYLOADFILE
|
|
|
|
%rep 1024
|
|
%ifidn NEXTFRAGMENTS, none
|
|
%exitrep
|
|
%else
|
|
setup_a_section ; add a section for next data fragment
|
|
nextfragment data %+ chainindex, NEXTFRAGMENTS
|
|
%assign chainindex chainindex + 1
|
|
%endif
|
|
%endrep
|
|
|
|
%macro extenddirectories 1-*.nolist
|
|
%ifnidni %1, none
|
|
%error Expected a none entry to start dir list
|
|
%endif
|
|
%rotate 1
|
|
%rep %0 - 1
|
|
usesection %1
|
|
times 32 db 0
|
|
; Insure at least one entry. Makes things
|
|
; easier to handle for the loader too.
|
|
; (Can depend on a zero-filled entry
|
|
; that terminates the directory.)
|
|
%rotate 1
|
|
%endrep
|
|
%endmacro
|
|
|
|
extenddirectories DIRLIST
|
|
|
|
|
|
%macro allocatechains 1-*.nolist
|
|
%ifnidni %1, none
|
|
%error Expected a none entry to start chain list
|
|
%endif
|
|
%rotate 1
|
|
%assign ii 0
|
|
%rep %0 - 1
|
|
usesection data %+ ii
|
|
align _BPS * _SPC, db 0
|
|
%assign clusters ($ - data %+ ii %+ _start) / (_BPS * _SPC)
|
|
addfatchain %1,clusters
|
|
%assign ii ii + 1
|
|
%rotate 1
|
|
%endrep
|
|
%endmacro
|
|
|
|
%macro allocateendfill 3-*.nolist
|
|
%ifnidni %1, none
|
|
%error Expected a none entry to start chain list
|
|
%endif
|
|
%if %0 % 3
|
|
%error Expected an amount of chain list entries divisible by 3
|
|
%endif
|
|
; ii as left from allocatechains
|
|
%rep (%0 - 3) / 3
|
|
%rotate 3
|
|
setup_a_section
|
|
usesection data %+ ii
|
|
data %+ ii %+ _start:
|
|
%if isendfillzeros
|
|
; zerobytes_if_full (%2 + _BPS * _SPC - 1) & ~(_BPS * _SPC - 1)
|
|
zerobytes_if_full %2
|
|
%else
|
|
fillbytes %2, %3
|
|
%endif
|
|
align _BPS * _SPC, db 0
|
|
%assign clusters (%2 + _BPS * _SPC - 1) / (_BPS * _SPC)
|
|
%assign data_used data_used + (clusters * _BPS * _SPC)
|
|
addfatchain %1,clusters
|
|
%1_chain_end equ EOFENTRY
|
|
%assign ii ii + 1
|
|
%endrep
|
|
%endmacro
|
|
|
|
allocatechains CHAINLIST
|
|
|
|
%assign data_used 0
|
|
%assign ii 0
|
|
%rep filescount
|
|
usesection data %+ ii
|
|
align _BPS * _SPC, db 0
|
|
data %+ ii %+ _end:
|
|
%assign data_used data_used \
|
|
+ (data %+ ii %+ _end - data %+ ii %+ _start)
|
|
%assign ii ii + 1
|
|
%endrep
|
|
|
|
%assign isendfillzeros 0
|
|
allocateendfill LISTENDFILL
|
|
%assign isendfillzeros 1
|
|
allocateendfill LISTENDFILLZEROS
|
|
addsection empty, follows= %+ ff vstart=0
|
|
|
|
|
|
usesection fixed_root
|
|
%if _BPE != 32
|
|
times 32 db 0
|
|
%endif
|
|
zerobytes ROOTSECTORS * _BPS - ($ - $$)
|
|
|
|
|
|
USEDCLUSTERS equ data_used / (_BPS * _SPC)
|
|
usesection empty
|
|
%if data_used > (DATASECTORS * _BPS)
|
|
%error Too much data used
|
|
%endif
|
|
zerobytes_if_full DATASECTORS * _BPS - data_used
|
|
|
|
|
|
%if _MBR
|
|
addsection partition_end_padding, follows=empty
|
|
zerobytes_if_full (CYLINDERS * _CHS_HEADS * _CHS_SECTORS \
|
|
- _MBR_GAP_SIZE_SECTORS - _SPI) \
|
|
* _BPS
|
|
%endif
|
|
|
|
|
|
%if _BPE == 12
|
|
%macro dumpfatentry 1.nolist
|
|
%ifempty EVENENTRY
|
|
%xdefine EVENENTRY %1
|
|
%else
|
|
db (EVENENTRY) & 0FFh
|
|
db ((EVENENTRY) >> 8) \
|
|
| (((%1) & 0Fh) << 4)
|
|
db ((%1) >> 4)
|
|
%define EVENENTRY
|
|
%endif
|
|
%endmacro
|
|
%elif _BPE == 16
|
|
%define dumpfatentry dw
|
|
%elif _BPE == 32
|
|
%define dumpfatentry dd
|
|
%else
|
|
%error Invalid bpe
|
|
%endif
|
|
|
|
%macro expandfatchains 2-*.nolist
|
|
%define EVENENTRY
|
|
dumpfatentry (_MEDIAID | (MAXENTRY - 15))
|
|
; first entry has media ID byte
|
|
%assign ii 1 ; first entry is at index 0,
|
|
; next is index 1
|
|
%rep %0 / 2 ; for each pair
|
|
%if %2
|
|
%1_start_cluster equ ii ; equ for chain start cluster
|
|
%rep %2 - 1 ; as many as are non-end entries
|
|
%assign ii ii + 1
|
|
dumpfatentry ii ; link to next cluster
|
|
%endrep
|
|
%assign ii ii + 1
|
|
dumpfatentry %1_chain_end ; end of contiguous chain entry
|
|
%else
|
|
%1_start_cluster equ 0 ; empty chain, start cluster is 0
|
|
%endif
|
|
%rotate 2 ; rotate to next pair
|
|
%endrep
|
|
%ifnempty EVENENTRY
|
|
; dumpfatentry 0
|
|
dw EVENENTRY
|
|
%if 0
|
|
|
|
Edge case result with dumpfatentry 0 here:
|
|
|
|
bootimg$ nasm bootimg.asm \
|
|
-D_PAYLOADFILE=::endfill,"(_SPI - 1 - 1 - 1) * 512",0,zeros.bin \
|
|
-I ../lmacros/ -D_FULL=0 -D_BPE=12 -D_SPI=342 -D_SPF="1" \
|
|
-D_NUMFATS=1 -D_NUMROOT=16
|
|
bootimg.asm:1201: error: Negative count given to zerobytes
|
|
bootimg.asm:1205: error: Internal error in FAT filling
|
|
|
|
Correct behaviour with dw EVENENTRY:
|
|
|
|
bootimg$ nasm bootimg.asm \
|
|
-D_PAYLOADFILE=::endfill,"(_SPI - 1 - 1 - 1) * 512",0,zeros.bin \
|
|
-I ../lmacros/ -D_FULL=0 -D_BPE=12 -D_SPI=342 -D_SPF="1" \
|
|
-D_NUMFATS=1 -D_NUMROOT=16
|
|
|
|
Correctly rejecting too small FAT:
|
|
|
|
bootimg$ time nasm bootimg.asm \
|
|
-D_PAYLOADFILE=::endfill,"(_SPI - 1 - 1 - 1) * 512",0,zeros.bin \
|
|
-I ../lmacros/ -D_FULL=0 -D_BPE=12 -D_SPI=343 -D_SPF="1" \
|
|
-D_NUMFATS=1 -D_NUMROOT=16
|
|
bootimg.asm:272: error: Too few FAT sectors specified (341 entries, 342 needed)
|
|
bootimg.asm:1221: error: Negative count given to zerobytes
|
|
bootimg.asm:1225: error: Internal error in FAT filling
|
|
|
|
%endif
|
|
%endif
|
|
%endmacro
|
|
|
|
usesection fat
|
|
%assign jj 0
|
|
%rep _NUMFATS
|
|
fat%[jj]:
|
|
expandfatchains FATCHAINS
|
|
zerobytes _SPF * _BPS - ($ - fat%[jj])
|
|
%assign jj jj+1
|
|
%endrep
|
|
%if FATSECTORS * _BPS - ($ - $$)
|
|
%error Internal error in FAT filling
|
|
%endif
|