Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

bootstrap16 - real mode FAT12/16/32 bootloader

406 views
Skip to first unread message

Mike Gonta

unread,
May 17, 2013, 8:03:23 PM5/17/13
to
* Original code by Mike Gonta, Public Domain 2013
* 16 bit real mode Intel syntax assembly language
* CPU 8086
* four formats supported
- FAT12 1.44Mb floppy disk / flash drive
- FAT12 hard drive / flash drive
- FAT16 hard drive / flash drive
- FAT32 hard drive / flash drive
* handles 512 byte, 1K, 2K, and 4K sector sizes
* same code (boot16.inc) for NASM and FASM
* NASM (boot16.nsm) and FASM (boot16.fsm) headers
* same binary with NASM (-O1) and FASM
* same binary code for FAT12/16/32 only the BPB is different
* loads a plain english "8.3" ("kernel.bin") flat binary file
from the root directory to 0x1000:0
* includes 32Mb FAT32 image

http://mikegonta.com/bootstrap16.zip


Mike Gonta
look and see - many look but few see

http://mikegonta.com



Rod Pemberton

unread,
May 18, 2013, 4:06:05 AM5/18/13
to
"Mike Gonta" <mike...@gmail.com> wrote in message
news:kn6ggb$68p$1...@speranza.aioe.org...
Mike,

I'm curious. How did you manage to test the 1K, 2K, and 4K sector
sizes? Did you use physical devices that _only_ supported those
sizes? Where did you get them? Or, were these actually 512 byte
devices being used as if they are 1/2/4KB?


Rod Pemberton



James Harris

unread,
May 18, 2013, 6:53:53 AM5/18/13
to
On May 18, 9:06 am, "Rod Pemberton" <do_not_h...@notemailnotq.cpm>
wrote:
> "Mike Gonta" <mikego...@gmail.com> wrote in message
> news:kn6ggb$68p$1...@speranza.aioe.org...

>
> > * Original code by Mike Gonta, Public Domain 2013
> > * 16 bit real mode Intel syntax assembly language
> > * CPU 8086
> > * four formats supported
> >   - FAT12 1.44Mb floppy disk / flash drive
> >   - FAT12 hard drive / flash drive
> >   - FAT16 hard drive / flash drive
> >   - FAT32 hard drive / flash drive
> > * handles 512 byte, 1K, 2K, and 4K sector sizes
> > * same code (boot16.inc) for NASM and FASM
> > * NASM (boot16.nsm) and FASM (boot16.fsm) headers
> > * same binary with NASM (-O1) and FASM
> > * same binary code for FAT12/16/32 only the BPB is different
> > * loads a plain english "8.3" ("kernel.bin") flat binary file
> >   from the root directory to 0x1000:0
> > * includes 32Mb FAT32 image
>
> >http://mikegonta.com/bootstrap16.zip

...

> I'm curious.  How did you manage to test the 1K, 2K, and 4K sector
> sizes?  Did you use physical devices that _only_ supported those
> sizes?  Where did you get them?  Or, were these actually 512 byte
> devices being used as if they are 1/2/4KB?

I read that and assumed Mike meant cluster sizes. Possibly the maths
over 4k would lead to a numeric overflow which is why the max is
stated as 4k but that is just a guess.

I haven't built the code but it looks like it will occupy more than
512 bytes. If so it won't fit in a floppy bootsector or a hard disk
VBR. If the "times" instruction at the end is anything to go by I
suspect this code will take three or four sectors. It is feasible to
write it over a blank floppy or partition but I'm curious as to what
would be needed to write it to a disk which has an existing
filesystem.

There's some interesting code in there, especially the implementation
of setjmp/longjmp!

There are also some interesting defines to distinguish FD and HD
versions of FAT12 and I see Mike has been careful to distinguish
between the different fat types using the formula in fatgen103.

James
Message has been deleted

Mike Gonta

unread,
May 18, 2013, 6:08:44 PM5/18/13
to
"Rod Pemberton" wrote:
> I'm curious. How did you manage to test the 1K, 2K, and 4K sector
> sizes? Did you use physical devices that _only_ supported those
> sizes? Where did you get them? Or, were these actually 512 byte
> devices being used as if they are 1/2/4KB?

I haven't tested those sector sizes. The 1K and 2K didn't catch on (or
don't even exist). The code does not use hardcoded values for sector
size. The boot sector checks the sector size from the BPB (not the BPB
in the code - but the actual values from the formatted media) and if it
is 2K and over (4K is probably the only realistic size) it assumes that
the second stage (LBA 2 and LBA 3) has already been loaded. If not it
loads 2 more sectors (if the sector size is 4K, the 1K 2nd stage will
be located contiguously at the begining of the first of the two sectors.
The loader loads one sector at a time and advances the buffer pointer by
the sector size. It is in this manner that the different sectors sizes
are handled.

Mike Gonta

unread,
May 18, 2013, 6:32:49 PM5/18/13
to
"James Harris" wrote:
> "Rod Pemberton" wrote:
>> I'm curious. How did you manage to test the 1K, 2K, and 4K sector
>> sizes?
> I read that and assumed Mike meant cluster sizes.

No, actual sectors sizes.

> Possibly the maths over 4k would lead to a numeric overflow ...

All required calculations are 32 bit (ax:dx), with helper routines to
handle long multiplication and long division.

> I haven't built the code but it looks like it will occupy more than
> 512 bytes.

It's a two stage bootloader - the second stage is 2 sectors (LBA 2 and
LBA 3)

> It is feasible to write it over a blank floppy or partition but I'm
> curious as to what would be needed to write it to a disk which has an
> existing filesystem.

First, format the media in the desired format.
Then, using any of the 4 images (which can be used with any of the
formats) transfer (using a disk editor or DD) the first 4 sectors
of the image beginning at offset 0x5A of the image to the same offset
(0x5A) of the media. This maintains the BPB formatting information.
Note, for floppy disks, 36 sectors (which contain the file system) are
transfered from and to the same offset (0x5A).

Mike Gonta

unread,
May 19, 2013, 7:20:36 AM5/19/13
to
"Mike Gonta" wrote:
> "Mike Gonta" wrote:
>> * Original code by Mike Gonta, Public Domain 2013 (2013-05-17)
>> * 16 bit real mode Intel syntax assembly language
>> * CPU 8086
>> * four formats supported
>> - FAT12 1.44Mb floppy disk / flash drive
>> - FAT12 hard drive / flash drive
>> - FAT16 hard drive / flash drive
>> - FAT32 hard drive / flash drive
>> * handles 512 byte, 1K, 2K, and 4K sector sizes
>> * same code (boot16.inc) for NASM and FASM
>> * NASM (boot16.nsm) and FASM (boot16.fsm) headers
>> * same binary with NASM (-O1) and FASM
>> * same binary code for FAT12/16/32 only the BPB is different
>> * loads a plain english "8.3" ("kernel.bin") flat binary file
>> from the root directory to 0x1000:0
>> * includes 32Mb FAT32 image
>
> * Original code by Mike Gonta, Public Domain 2013-05-18
> * handles 512 byte, 1K, 2K, and 4K sector sizes _UNTESTED_
> * changed formatting defines
> * added _QUICK_FORMAT define

Original code by Mike Gonta, Public Domain 2013-05-19
* new vesion of setjmp/longjmp
- uses carry flag (assembly language style)
- saves/restores _ALL_ registers except ax (longjmp return value)
- each setjmp/longjmp pair can have its own saved state buffer

<code>
CPU 8086

setjmp: ; ax = save state buffer
push bx
mov bx, ax
mov [bx], di
mov [bx+2], si
mov [bx+4], bp
mov [bx+8], dx
mov [bx+10], cx
mov [bx+12], sp
add WORD [bx+12], 4
mov [bx+14], ss
push bp
mov bp, sp
push WORD [bp+4] ; return address
pop WORD [bx+16]
mov [bx+18], cs
pop bp
pop WORD [bx+6] ; save original bx
clc
ret

longjmp: ; ax = return value, bx = save state buffer
mov sp, [bx+12]
push WORD [bx+18] ; return segment
push WORD [bx+16] ; return address
mov [bx+12], sp ; update sp
mov sp, bx
pop di
pop si
pop bp
pop bx
pop dx
pop cx
pop sp
stc
retf

</code>

example:

<code>
CPU 8086

mov ax, ENV_BUFFER
call setjmp
jnc .1
; error handler goes here
.1:

; ....

mov ax, ERROR_CODE
mov bx, ENV_BUFFER
call longjmp
; restores registers and returns to setjmp

</code>
Message has been deleted
Message has been deleted

Rod Pemberton

unread,
May 19, 2013, 11:36:45 AM5/19/13
to

"Mike Gonta" <mike...@gmail.com> wrote in message
news:knaci2$q20$1...@speranza.aioe.org...
xchg sp,ax
push cs
push ax ; placeholder for ip
push ss ; ss used as flags for retf?
push ax ; sp value
push cx
push dx
push bx
push bp
push si
push di
mov bp,ax
mov bx,[bp] ; return address
mov bp,sp
mov [bp+18],bx
mov sp,ax
clc
ret


Could you do something like that instead? (untested)
[BTW, if it's correct, it's one instruction shorter, but likely
faster ... on an 8086.]

I.e., I was just wondering why you used pop's in longjmp but
didn't use or attempt to use push's in setjmp. Symmetry is good
if you're not completely register blocked.


Rod Pemberton


Mike Gonta

unread,
May 19, 2013, 10:55:57 AM5/19/13
to
bootstrap16 - real mode (CPU 8086) FAT12/16/32 bootloader
Original code by Mike Gonta, Public Domain 2013

http://mikegonta.com/bootstrap16.zip


2013-05-19
* new vesion of setjmp/longjmp
- uses carry flag (assembly language style)
- saves/restores _ALL_ registers except ax (longjmp return value)
- each setjmp/longjmp pair can have its own saved state buffer
* added qemu.bat script to emulate bootstrap16.img
- unzip bootstrap16.zip to your qemu folder, open
the bootstrap16 folder and double click qemu.bat
- works with qemu 32 bit and qemu 64 bit

2013-05-18
* changed formatting defines
* added _QUICK_FORMAT define

2013-05-17 (initial release)
* 16 bit real mode Intel syntax assembly language
* CPU 8086
* four formats supported
- FAT12 1.44Mb floppy disk / flash drive
- FAT12 hard drive / flash drive
- FAT16 hard drive / flash drive
- FAT32 hard drive / flash drive
* handles 1K, 2K, and 4K sector sizes _UNTESTED_
* same code (boot16.inc) for NASM and FASM
* NASM (boot16.nsm) and FASM (boot16.fsm) headers
* same binary with NASM (-O1) and FASM
* same binary code for FAT12/16/32 only the BPB is different
- loads a plain english "8.3" ("kernel.bin") flat binary file
from the root directory to 0x1000:0
* includes FAT32 image

Rod Pemberton

unread,
May 19, 2013, 12:10:40 PM5/19/13
to
"Mike Gonta" <mike...@gmail.com> wrote in message
news:knaci2$q20$1...@speranza.aioe.org...

Wait a minute...

You only pop registers through sp. But, you fill in three more of
them... Yes? You pop all of them off the stack upto sp aka
[bx+12], that leaves ss aka [bx+14], ip aka [bx+16], and cs aka
[bx+18] on the stack. retf pops an ip followed by a segment into
cs. ss is where ip should be. ip is where cs should be.

Sorry, I know I'm sleepy and shouldn't send this. I'm clearly off
by one cell somewhere...


Rod Pemberton



Mike Gonta

unread,
May 19, 2013, 12:18:08 PM5/19/13
to
"Rod Pemberton" wrote:
Hi Rod,

Thanks for the contribution.

> Could you do something like that instead? (untested)

Not in this case. Remember, the save state buffer is
only a small amount of memory inside the code (or data)
space. Pointing the stack pointer here will result in
code (or data corruption) during the first interrupt
handler. I don't want to treat this piece of code as a
critical section by wrapping it in sti/cli.

> I.e., I was just wondering why you used pop's in longjmp but
> didn't use or attempt to use push's in setjmp.

In longjmp the new stack pointer is still pointing to stack
space, so it's safe to do so.

Mike Gonta

unread,
May 19, 2013, 3:18:21 PM5/19/13
to
"Mike Gonta" wrote:
> "Rod Pemberton" wrote:

>> I.e., I was just wondering why you used pop's in longjmp but
>> didn't use or attempt to use push's in setjmp.
>
> In longjmp the new stack pointer is still pointing to stack
> space, so it's safe to do so.

Obviously not. I've corrected the code to eliminate stack pointer
usage on non stack space.

James Harris

unread,
May 20, 2013, 9:05:38 AM5/20/13
to
On May 19, 3:06 pm, "Mike Gonta" <mikego...@gmail.com> wrote:

...

> THIS SHOULD BE:
>
> First, format the media in the desired format.
> Then, using any of the 4 images (which can be used with any of the
> formats) transfer (using a disk editor or DD) the first sector
> of the image beginning at offset 0x5A of the image to the same offset
> (0x5A) of the media.

And bytes 0 to 2?

> Then transfer the 3rd and 4th sectors (skipping the second sector) to
> the 3rd and 4th sectors of the media. (Skipping the second sector LBA 1
> is only required for FAT32 to maintain the the formatted File System
> Information sector - which by default is loacted here).

Unless extra sectors have been reserved (BPB field at offset 14,
BPB_RsvdSecCnt) wouldn't the third and fourth sectors hold part of the
first FAT?

AIUI a FAT volume is formatted as these regions, in order,

1. Reserved space including the boot record
2. FATs (usually two of them)
3. Root directory (fat12 and fat16 only)
4. Data area

If the medium has the default of only one sector reserved then the
FATs would immediately follow, AIUI.

As an aside, to reserve the space your code needs it might be feasible
to increase the reserved sector count on an *existing* volume, i.e.
not have to reformat it first. Any files at the start of the data
section of a Fat disk could be relocated quickly enough as long as
they were not marked as system files. Then the FATs could be shifted
up into that space. The FAT entries would need to be renumbered so
they related to the new positions but that might not be too difficult.

It might even be possible to relocate system files as long as the
renumbering took care to keep all their cluster numbers the same as
they were at the start.

A fair bit of work, though. Code to do that would be fairly long and a
lot would depend on it being absolutely right! On the other hand there
may be some utility out there which will do this already.

James

Mike Gonta

unread,
May 20, 2013, 10:31:57 AM5/20/13
to
"James Harris" wrote:
>"Mike Gonta" wrote:

...

>> THIS SHOULD BE:
>>
>> First, format the media in the desired format.
>> Then, using any of the 4 images (which can be used with any of the
>> formats) transfer (using a disk editor or DD) the first sector
>> of the image beginning at offset 0x5A of the image to the same offset
>> (0x5A) of the media.

> And bytes 0 to 2?

Each image including the FAT32 has a short jmp at the default FAT12/16
jumped to location at offset 0x3E which jumps to offset 0x5A.

>> Then transfer the 3rd and 4th sectors (skipping the second sector) to
>> the 3rd and 4th sectors of the media. (Skipping the second sector LBA 1
>> is only required for FAT32 to maintain the the formatted File System
>> Information sector - which by default is loacted here).

> Unless extra sectors have been reserved (BPB field at offset 14,
> BPB_RsvdSecCnt) wouldn't the third and fourth sectors hold part of the
> first FAT?

Typically the reserved sector count for FAT16/32 formats is at least 32,
(greater with WIN7 and Windows 8), so the bootloader will easily fit
inside without disturbing the file system.

For a floppy disk or flash drive floppy disk format the entire 36 sectors
are transferred completely. The file system configuration is standard.
Windows XP/7/8 won't format a flash drive as FAT12 so the complete image
has to be built and transferred. You can of course transfer any of the
images completely to a flash drive. Windows will see the drive as 32MB
capacity, and function properly.

James Harris

unread,
May 20, 2013, 4:35:28 PM5/20/13
to
On May 20, 3:31 pm, "Mike Gonta" <mikego...@gmail.com> wrote:

...

> >> Then transfer the 3rd and 4th sectors (skipping the second sector) to
> >> the 3rd and 4th sectors of the media. (Skipping the second sector LBA 1
> >> is only required for FAT32 to maintain the the formatted File System
> >> Information sector - which by default is loacted here).

> > Unless extra sectors have been reserved (BPB field at offset 14,
> > BPB_RsvdSecCnt) wouldn't the third and fourth sectors hold part of the
> > first FAT?
>
> Typically the reserved sector count for FAT16/32 formats is at least 32,
> (greater with WIN7 and Windows 8), so the bootloader will easily fit
> inside without disturbing the file system.

It seems that was not always the case on FAT16. For example, I've just
checked a FAT16 volume formatted by MS-DOS 6.20. It has only one
reserved sector.

In fact as that was a partition type 4 volume have just checked and it
applies to partition type 6 as well. Reserved sectors = one.

A copy I have of Microsoft's fatgen103 document says of the reserved
sectors: "For FAT12 and FAT16 volumes, this value should never be
anything other than 1. For FAT32 volumes, this value is typically 32."

The recommendation to set the value to one for FAT12 and FAT16 is
purely for compatibility with non-Microsoft software. Microsoft say
their operating systems will work correctly with other values.
Nevertheless, the fact that they recommend it (or recommended it)
suggests that that's how FAT16 used to be set up.

Do you have any idea *when* the formatting utils started reserving the
extra space on FAT16?

James

wolfgang kern

unread,
May 21, 2013, 3:33:36 PM5/21/13
to

James Harris wrote in part:
...

|Do you have any idea *when* the formatting utils started reserving the
|extra space on FAT16?

Can't tell the when, but I once understood the why.
This wasted sectors on track zero were once the legal hidden space,
meanwhile every OS-variant seem to use this space in different ways.

windoze keep a copy of the MBR somewhere in this range,
while PQmagic use sector 0A (the eleventh) to store bad sectors ?
my kesys once started with LBA_0 and consecutive sectors and still
does for KESYS_SAFE, but cannot do it if any M$-products were installed
after an KESYS_OPEN install because windoze will overwrite a few sectors
on the beginning on any media.
So I had to follow and swallow the toad for leaving the first 63 sectors
to be 'hidden' for whatsoever M$ like to store there.

__
wolfgang


James Harris

unread,
May 23, 2013, 5:30:19 AM5/23/13
to
This seems to relate to the space after the MBR on a hard disk but I
think Mike was talking about space reserved after the boot block of a
FAT volume - such as in a *partition* of a hard disk.

Early versions of FAT16 did not reserve any sectors after the boot
block. Mike mentioned they now do. I wondered when MS formatting utils
started reserving that space. (This was a question purely about FAT16
and not about FAT32. It seems that the extra space has been reserved
on FAT32 for some time.)

James
0 new messages