Let's say I have built an assembly program (a basic print), so I have got a .BIN file.
Now I want to make it a bootable floppy disc image (a virtual one), so that when I start an OS (in the vm), it will first execute the assembly instructions from my program which is located on that virtual floppy disc.
How to do that correctly?
You can use my boot sector loaders (ldosboot boot.asm
for FAT12 / FAT16 FS) and my boot image creation script to make a 1440 KiB file system image preloaded with your kernel executable and a loader in the first sector. You'll also need my macro collection. Here's an example shell session to clone the repos and build an image. This is using hg (Mercurial) to load the repos, wget to get a release of my debugger as an example program, Info-ZIP's unzip to unpack it, NASM to assemble the loader and image, and qemu to run everything once assembled.
$ hg clone https://hg.pushbx.org/ecm/lmacros
destination directory: lmacros
requesting all changes
adding changesets
adding manifests
adding file changes
added 143 changesets with 171 changes to 38 files
new changesets 3a982025dd94:323cc150061e
updating to branch default
29 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg clone https://hg.pushbx.org/ecm/ldosboot
destination directory: ldosboot
requesting all changes
adding changesets
adding manifests
adding file changes
added 588 changesets with 650 changes to 15 files (+2 heads)
new changesets 13cf6bb0b5f5:07f4ba0ef8cd
updating to branch default
15 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg clone https://hg.pushbx.org/ecm/bootimg
destination directory: bootimg
requesting all changes
adding changesets
adding manifests
adding file changes
added 88 changesets with 88 changes to 1 files
new changesets 966f8a094eca:fa44558212e7
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ wget https://pushbx.org/ecm/download/ldebug.zip
--2021-11-18 13:05:18-- https://pushbx.org/ecm/download/ldebug.zip
Resolving pushbx.org (pushbx.org)... 2a01:488:66:1000:b01c:1258:0:1, 176.28.18.88
Connecting to pushbx.org (pushbx.org)|2a01:488:66:1000:b01c:1258:0:1|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 7131575 (6.8M) [application/zip]
Saving to: 'ldebug.zip'
ldebug.zip 100%[=====================>] 6.80M --.-KB/s in 0.03s
2021-11-18 13:05:18 (259 MB/s) - 'ldebug.zip' saved [7131575/7131575]
$ unzip ldebug.zip bin/ldebugu.com
Archive: ldebug.zip
inflating: bin/ldebugu.com
$ nasm -I lmacros/ -I ldosboot/ ldosboot/boot.asm -D_COMPAT_FREEDOS -D_QUERY_GEOMETRY=0 -D_LBA=0 -D_USE_PART_INFO=0 -o boot.bin
ldosboot/boot.asm:420: warning: Possibly crossing 64 KiB boundary while reading file (sector size >= 1024) [-w+user]
ldosboot/boot.asm:1851: warning: FAT12: 18 bytes still available. [-w+user]
$ nasm -I lmacros/ -I bootimg/ bootimg/bootimg.asm -D_BOOTFILE="'boot.bin'" -D_PAYLOADFILE="::rename,'bin/ldebugu.com','KERNEL.SYS'" -o diskette.img
$ qemu-system-i386 -fda diskette.img -boot order=a -curses
The documentation for bootimg is in a comment at the top of the main source file. I used the _BOOTFILE
define (set to a twice quoted string so NASM receives a quoted string) to indicate a boot sector file, and the main _PAYLOADFILE
define to specify inclusion of the debugger executable, renaming it to KERNEL.SYS
so that the default name used by the _COMPAT_FREEDOS
loader will find our executable.
Aside from the FreeDOS compatibility selection, boot.asm
needs some additional switches to disable features not needed to boot a 1440 KiB diskette image. This is because the FreeDOS compatible loader needs more space than my (lDOS) default.
Finally, the file that you specify to be loaded (either a default like KERNEL.SYS
or one in a filename specified using the boot.asm
defines _LOAD_NAME
and _LOAD_EXT
) needs to be in the correct format. The FreeDOS load protocol is fairly simple: Your entire file is loaded to address 600h, addressable using the segmented address 60h:0, and cs:ip
are set up exactly as 60h:0. The register bl
receives the ROM-BIOS unit you were booted from and ss:bp
points to a copy of the FS's boot sector somewhere in memory. All other registers, particularly the segment registers, are indeterminate and must be initialised by your code if you want to use them.
Note that the FreeDOS load protocol differs from most other load protocols at this level in that bl
is set to the load unit. Depending on which loader is used dl
may differ from bl
in which case bl
is correct, and dl
incorrect. The load unit can also be found in the boot sector copy pointed to by ss:bp
, either in byte [ss:bp + 40h]
for FAT32 or byte [ss:bp + 24h]
for FAT12 and FAT16.
Reviewing this answer several years later, a few things should be noted:
The new option -D_BOOTPATCHFILE=
can be used instead of -D_BOOTFILE=
. This has the advantage that a different file system layout than the default 1440 KiB FAT12 for a 90mm diskette image can be used. (Even for such images, the boot patch file need not contain the pre-initialised default BPB, which for example the FreeDOS loaders do not.)
The bootimg script will cut the particular bits of the boot patch file that are needed. (In the bdiff this shows up as the default volume label changing to "NO NAME".) You do have to provide the correct loader for the file system type, that is FAT12 (nasm ldosboot/boot.asm
), FAT16 (nasm ldosboot/boot.asm -D_FAT16
), or FAT32 (nasm ldosboot/boot32.asm
). For the FAT32 loader that ships with ldosboot, you also have to specify -D_BOOTINFOFILE='"::bootpatchfile"'
to the bootimg.asm script.
The other bit to consider is if you want to use a bootimg output image as a hard disk partition then you may have to set the media ID byte to 0F8h in order to pass checks in some drivers, eg here in EDR-DOS.
Here's the original and changed bootimg.asm assembly commands, and the bdiff output showing the resulting differences.
$ nasm -I lmacros/ -I bootimg/ bootimg/bootimg.asm -D_BOOTFILE="'boot.bin'" -D_PAYLOADFILE="::rename,'bin/ldebugu.com','KERNEL.SYS'" -o diskette.img
$ nasm -I lmacros/ -I bootimg/ bootimg/bootimg.asm -D_BOOTPATCHFILE="'boot.bin'" -D_PAYLOADFILE="::rename,'bin/ldebugu.com','KERNEL.SYS'" -o patched.img -D_MEDIAID=0F8h
$ bdiff diskette.img patched.img
File: diskette.img
000015: F0 [ F8 ]
00002B: "lDOS " [ "NO NAME" ]
000200: F0 [ F8 ]
001400: F0 [ F8 ]
$