assemblyvirtual-machinebootosdevfloppy

How to make a bootable floppy disc image?


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?


Solution

  • 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 ]
    $