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

C 16 bit compiler

1,290 views
Skip to first unread message

Jack Smith

unread,
Sep 28, 2010, 2:01:36 AM9/28/10
to
hi!

Do you know about a compiler C which can compile into 16 bit? i've
tried watcom, but it's difficult to use and i need that 16 bit binary
to be an ELF, not an OMF, besides ELF is rudimentary in wlink.

The more general question,
suppose you have to write a bootloader (only stage1) in C and in real
mode, what compiler will you use?

ah, yeah..unfortunately.. gcc is not suitable for this,
because .code("asm16gcc"); directive makes the compiler to insert a
lot of 66 and 67 stuff in the code which makes the binary too large
for my purpose, moreover someone says that it will not work either, do
you confirm this?.

any help is appreciated..

Egdares Futch

unread,
Sep 28, 2010, 11:00:37 PM9/28/10
to
Hi, the XV6 academic operating system from MIT
(http://pdos.csail.mit.edu/6.828/2009/xv6-book/index.html) has a
bootloader that builds in ELF format, using real-mode x86 code. Check
it out.

Regards,

Egdares Futch

On Tuesday, September 28, 2010, Jack Smith <ilikeq...@katamail.com> wrote:
> Do you know about a compiler C which can compile into 16 bit? ...

Nils M Holm

unread,
Sep 29, 2010, 12:45:03 AM9/29/10
to
Jack Smith <ilikeq...@katamail.com> wrote:
> The more general question,
> suppose you have to write a bootloader (only stage1) in C and in real
> mode, what compiler will you use?

I would probably use TurboC (TCC) 2.x or Bruce's C compiler (BCC).
TCC is, of course, commercial, but as far as I know it has been made
freely available at some time. BCC is free software.

--
Nils M Holm | http://t3x.org

BGB / cr88192

unread,
Sep 29, 2010, 2:50:03 AM9/29/10
to
"Jack Smith" <ilikeq...@katamail.com> wrote in message

> Do you know about a compiler C which can compile into 16 bit? i've
> tried watcom, but it's difficult to use and i need that 16 bit binary
> to be an ELF, not an OMF, besides ELF is rudimentary in wlink.
>

16-bit C compilers... I am not sure, but I will list off what comes to
mind...

AFAIK, 16-bit ELF is itself non-standard (I doubt a C compiler will be found
which can produce this format natively), and it is likely better that one
just live with the OMF, or link into a raw binary image or similar.

besides Watcom, old TurboC or similar compilers exist (still produce OMF
objects though). dunno the specifics of getting ahold of older TurboC
though, not looked into this.

IIRC, there is also Minix, which IIRC had a compiler available which could
produce 16-bit a a.out variant or similar (I really don't remember if it had
source or not, but I think it did, but I could be wrong).

IIRC, lcc also had a 16-bit nasm target (lcc was available in source form,
and various people customized it for various targets).


> The more general question, suppose you have to write a bootloader
> (only stage1) in C and in real mode, what compiler will you use?

a lot depends on how exactly you want to do it, what tools one has
available, ...


my thoughts:
for a bootloader one is better off skipping C.
a bootloader is small and cramped enough that usually the only really good
option is ASM, but OTOH, it is typically small enough that ASM is managable
for the task.

typically, if C is used, it is usually for the "second stage".

say, typical boot process:
first stage gets the second stage and kernel loaded into memory;
second stage may be ASM or C;
the second stage will typically copy or decompress the kernel to a higher
memory address, setup protected mode, ...

likely the second stage could be most easily linked into a raw binary or a
".com" file (which is basically a raw binary file with a load and start
address of 0x100), however, some special care will be needed to produce a
standalone file (naively using a DOS compiler would produce a file which
assumes DOS to exist).

MZ-EXE is also possible, but MZ requires fixups' prior to use (could be an
issue for a bootloader).


one can also append the files together, which could save loading multiple
files (if one is using raw images, but could pose a few added issues, so
loading multiple files into memory is a little simpler). also possible
though is to only use the bootloader to load the second-stage, which is
itself responsible for loading any other files (say, kernel image and any
startup modules).

admittedly, in my case (back when I was into OS's), I wrote the second stage
in ASM as well (as I also lacked a decent 16-bit C compiler).

unless one is intending to have a lot of general-purpose logic code in
realmode though, it is unclear what real advantage there is to using C here
(to be worth the hassle of getting ahold of/using a 16-bit C compiler). a
lot of low-level OS tasks are not particularly well suited to an HLL like C
(since a lot of it tends to be messing around with the BIOS and with the CPU
state). this is especially true if the loader logic is done partly/mostly in
"big real" mode, which is unlikely to really be supported by compilers
anyways, but big-real is IME one of the most convinient ways to handle mixed
real-mode and protected-mode logic (since one has full access to the memory
map, ...).


a possible option is to use MZ-EXE + PE/COFF, but MZ-EXE (for the real-mode
portion) would require segment fixups. if used, this would then work vaguely
similarly to a DPMI loader, where the 16-bit portion first gains control
first and then sets up and jumps into the 32-bit portion of the image. in
this case, the bootloader would effectively function similarly to a sort of
micro-DOS or similar.


back when I wrote an OS (early 2000's), I was using PE/COFF for binaries,
but this was because I was building on Windows and so PE/COFF was the most
convinient format (one just supplies all their own library code). at the
time, I was using only static linking though (my loader didn't support
DLL's, or loading at non-default addresses, for that matter...).

back then, both my bootloader and second stage were written in ASM though
(where the second stage continued being used after the kernel was loaded and
after switching to protected-mode, where it mostly served as a real-mode
interface for using the BIOS and similar, and contained most of the logic
for switching between realmode and pmode).


even now, I would probably still use PE/COFF as a matter of personal
preference (the format is a little ugly, but it is fairly easy to work with
IME). between PE/COFF and ELF I am not sure which is easier to do a loader
for though (not personally written an ELF loader, so I am not sure).

more recently for other reasons (an x86 emulator/interpreter), I did
implement a full PE/COFF loader with DLL support (it was not particularly
complicated).


> ah, yeah..unfortunately.. gcc is not suitable for this,
> because .code("asm16gcc"); directive makes the compiler to insert a
> lot of 66 and 67 stuff in the code which makes the binary too large
> for my purpose, moreover someone says that it will not work either, do
> you confirm this?.
>
> any help is appreciated..
>

can't really comment much on this...


sorry, I really don't know if any of this is much help...

maybe check 'alt.os.development', 'alt.lang.asm', or 'comp.lang.asm.x86',
since the people on these groups are much more into this and are more likely
to be able to provide helpful answers...


or such...

George Neuner

unread,
Sep 29, 2010, 4:31:09 PM9/29/10
to
On Mon, 27 Sep 2010 23:01:36 -0700 (PDT), Jack Smith
<ilikeq...@katamail.com> wrote:

>Do you know about a compiler C which can compile into 16 bit?

16-bit what?

>i've tried watcom, but it's difficult to use and i need that 16 bit binary
>to be an ELF, not an OMF, besides ELF is rudimentary in wlink.

There are plenty of 16-bit x86 compilers around. Have you tried
Google?

>suppose you have to write a bootloader (only stage1) in C and in real
>mode, what compiler will you use?

A first stage boot loader can only be up to one device block long.
Because of this it would usually be written in assembler. It's
possible to write one in C, but you have to be *EXTREMELY* careful
about using any library functions. Still it would be difficult to get
a useful C program to fit into a 512 byte floppy sector.

Moreover, the second stage loader is usually packaged together with
the first stage loader, so if you want to use the same compiler for
both you'll need one suitable for the second stage as well.

And you can't put a boot loader in ELF, it has to be a flat
non-segmented format like A.OUT or COM.

I have no experience with freeware 16/32-bit compilers. I've used
Microsoft, Watcom (the commercial version), Lattice and Borland (which
is now freely available). Any of them would do fine.


>ah, yeah..unfortunately.. gcc is not suitable for this,
>because .code("asm16gcc"); directive makes the compiler to insert a
>lot of 66 and 67 stuff in the code which makes the binary too large
>for my purpose,

If you don't know what those codes mean, you probably shouldn't be
doing whatever you are doing.

>moreover someone says that it will not work either, do you confirm this?

No. Whether the code will work depends on what chip it will be run on
- which you haven't specified.

GCC is nominally a 32-bit compiler which makes no guarantees of
compatibility with smaller chips. Regardless of quality or length of
service, the GCC code generators for 16-bit chips are all considered
to be experimental.

>any help is appreciated..

You appear to need a lot of help and I question whether you are ready
to take on this project - whatever it is. I suggest you spend some
quality time learning more about it before you proceed.

George

Arargh...@arargh.com

unread,
Sep 30, 2010, 4:42:04 AM9/30/10
to
On Wed, 29 Sep 2010 16:31:09 -0400, George Neuner
<gneu...@comcast.net> wrote:

<snip>


>A first stage boot loader can only be up to one device block long.

Not really. While the BIOS or partition table loader will only load 1
block (of usually 512 bytes), there is absolutely no reason that the
first block can't just go and load as many more blocks as it wants to.
I have done that, usually in the partition table loader.

And, take a look at the boot sector for MS DOS 7.1 (The version that
hides under W98.)

>Because of this it would usually be written in assembler. It's
>possible to write one in C, but you have to be *EXTREMELY* careful
>about using any library functions. Still it would be difficult to get
>a useful C program to fit into a 512 byte floppy sector.

Very true. :-)

<snip>
--
BCET Basic Compiler Page: http://www.arargh.com/basic/index.html

Jack Smith

unread,
Oct 1, 2010, 11:19:53 AM10/1/10
to
thanks to all for replies!

writing the whole stuff in 512byte was just a challenge with myself,
but in the end i've figured out writing a bootloader 16bit in C is
hopeless.. :)

thanks to who posted the link to XV6, very useful and a very nice
project!

George Neuner

unread,
Oct 1, 2010, 11:41:31 AM10/1/10
to
On Wed, 29 Sep 2010 06:45:03 +0200, "Nils M Holm" <n...@t3x.org> wrote:

>I would probably use TurboC (TCC) 2.x or Bruce's C compiler (BCC).
>TCC is, of course, commercial, but as far as I know it has been made
>freely available at some time. BCC is free software.

Just for clarification: the open source Tiny C Compiler is known as
"TCC". Versions of Borland's Turbo C and Turbo C++ are now available
free from various sites.

George

http://bellard.org/tcc/
http://www.brothersoft.com/turbo-c-182798.html

George Neuner

unread,
Oct 3, 2010, 1:17:49 AM10/3/10
to
On Thu, 30 Sep 2010 03:42:04 -0500, Arargh...@Arargh.com wrote:

>On Wed, 29 Sep 2010 16:31:09 -0400, George Neuner
><gneu...@comcast.net> wrote:
>
><snip>
>>A first stage boot loader can only be up to one device block long.
>
>Not really. While the BIOS or partition table loader will only load 1
>block (of usually 512 bytes), there is absolutely no reason that the
>first block can't just go and load as many more blocks as it wants to.
>I have done that, usually in the partition table loader.

Yes, the boot block can load additional blocks - and usually does
because the OS/program startup loader is typically linked into the
same executable. But you can't use a meta-executable format like ELF
- it needs to be a flat, non-segmented format.

>>Because of this it would usually be written in assembler. It's
>>possible to write one in C, but you have to be *EXTREMELY* careful
>>about using any library functions. Still it would be difficult to get
>>a useful C program to fit into a 512 byte floppy sector.
>Very true. :-)

I want to make sure there's no confusion due to different people
numbering the boot stages differently. I order them based on disk
device boot as Unix does, from zero as :

0 : device boot block
1 : file system boot block
2 : program/OS kernel

For memory devices like Flash and SSD, there may be no file system and
all the bootstrap work is done from the device boot block. In that
case, stages 0/1 are synonymous.

Regardless, if there is a file system, it can't be used until, at
least, stage 2. Stage 0/1 essentially are limited to BIOS functions
and can reference only raw device blocks. That, along with block size
limitations on many devices, is the reason that these stages are
normally written in assembler.

It is perfectly possible to write block loaders in C, but there's
little point to doing so because they are so simple and because few
(if any) C compilers have any BIOS specific functions - usually just
some generic API for invoking software interrupts/traps ... meaning
that you are writing what is effectively assembler anyway and, in
addition, working without access to most of the C standard library.

George

BGB / cr88192

unread,
Oct 6, 2010, 2:29:43 PM10/6/10
to
"George Neuner" <gneu...@comcast.net> wrote in message

> On Thu, 30 Sep 2010 03:42:04 -0500, Arargh...@Arargh.com wrote:
>
>>On Wed, 29 Sep 2010 16:31:09 -0400, George Neuner
>><gneu...@comcast.net> wrote:
>>
>><snip>
>>>A first stage boot loader can only be up to one device block long.
>>
>>Not really. While the BIOS or partition table loader will only load 1
>>block (of usually 512 bytes), there is absolutely no reason that the
>>first block can't just go and load as many more blocks as it wants to.
>>I have done that, usually in the partition table loader.
>
> Yes, the boot block can load additional blocks - and usually does
> because the OS/program startup loader is typically linked into the
> same executable. But you can't use a meta-executable format like ELF
> - it needs to be a flat, non-segmented format.

granted, this whole matter may be more topical to alt.os.development or
similar, but oh well...


yes, however:
there may be restrictions depending on the filesystem in use as well.

for example, many filesystems by default only provide some space in the
first sector (512 minus BPB FAT/NTFS), or maybe 1kB (EXT2/...), in which to
fit the entire bootloader.

hence, usually then, the first thing one has to do is get the rest of the
loader loaded, which is usually by reading it in from the filesystem.
granted, yes, it is possible that one could lay it out in memory as a linear
chunk (say, the first 512 bytes are simply the bootloader, and everything
after is the first/second stage, so the goal is to locate this additional
code and load it directly after the bootloader).

back when I did this though, I did the bootloader and second stage as
separate components though.


even in the MBR, newer bootloaders/bootmanagers may have to contend with
GUID partition tables or similar, rather than use the old trick of simply
placing their data directly after the MBR sector (although, it can still
work, as 'track 0' is a fairly large space and only a small amount is likely
needed for GPT, unless of course the partitioner no longer honors the "begin
and end partitions on a track" tradition).

then again, usually where one finds GPT one also finds EFI, so it may not
actually matter that much.


>>>Because of this it would usually be written in assembler. It's
>>>possible to write one in C, but you have to be *EXTREMELY* careful
>>>about using any library functions. Still it would be difficult to get
>>>a useful C program to fit into a 512 byte floppy sector.
>>Very true. :-)
>
> I want to make sure there's no confusion due to different people
> numbering the boot stages differently. I order them based on disk
> device boot as Unix does, from zero as :
>
> 0 : device boot block
> 1 : file system boot block
> 2 : program/OS kernel
>
> For memory devices like Flash and SSD, there may be no file system and
> all the bootstrap work is done from the device boot block. In that
> case, stages 0/1 are synonymous.
>
> Regardless, if there is a file system, it can't be used until, at
> least, stage 2. Stage 0/1 essentially are limited to BIOS functions
> and can reference only raw device blocks. That, along with block size
> limitations on many devices, is the reason that these stages are
> normally written in assembler.

I was using the notion:
first-stage=bootloader (from filesystem bootsector);
second-stage=OS loader (comes from filesystem, usual goal is to load/setup
the kernel itself).

partly agreed, however, most bootloaders I have seen usually have used the
filesystem to get the kernel loaded (in addition to the secondary loader).
typically though these files are placed in the root directory (commonly,
only the first entries of the root directory may be visible at this point,
and possibly files may be limited to being located in contiguous sectors or
similar, such as to save having to have full logic for dealing with the FAT
or cluster-spans or whatever...).

say, the boot loader looks for the files "LOADER.SYS" and "KERNEL.EXE",
loads them into their initial spots in memory, and jumps to "LOADER.SYS",
which may in turn do other things (in some order...):
start setting up pmode (usually first going into big-real/unreal mode);
copying the kernel to its load address (say, ImageBase in the PE/COFF
extended header or similar);
maybe load in any other relevant bootup files (say, any drivers from a list
of known drivers, ...);
enter pmode (or maybe now longmode) and jump to the kernels' entry point (or
one could set up the stack in the second stage and call into the kernel,
possibly allowing the kernel to 'return' to real mode say to reboot or
whatever, but I am not aware of anyone doing this...).

also reasonable is to only load in the secondary loader by the bootsector,
in which case a directory (say, here called simply SYSTEMROOT) is used to
find, say, "KERNEL.EXE", "BOOTLST.INF", and possibly any secondary modules
(although a multi-module kernel could somewhat complicate the loader, as
then one would need to deal with things like import/export-resolution, image
rebasing, ...).


it should probably all be fairly similar if one uses ELF or MachO or similar
for the kernel as well?...

> It is perfectly possible to write block loaders in C, but there's
> little point to doing so because they are so simple and because few
> (if any) C compilers have any BIOS specific functions - usually just
> some generic API for invoking software interrupts/traps ... meaning
> that you are writing what is effectively assembler anyway and, in
> addition, working without access to most of the C standard library.

agreed, and this is probably the main relevant point:
C makes the thing bigger, and really "brings nothing to the table" over
using just ASM for the task.


even for the main OS loader, which arguably does a good deal more, one is
often still better off using ASM.
reasons would include:
there is no OS available, so no real C library either;
a lot of logic may need to operate in mixed CPU modes, which are not
generally supported by C compilers (I doubt any C compilers are written
specifically for unreal mode);
still lots of interfacing with the BIOS;
...

at first, back when I was doing OS stuff, I first thought I also wanted to
use C for the second stage loader, but really, it worked out perfectly well
writing it all in ASM.

for a much bigger and more complex second-stage, using some C could make
sense, but I am not sure what would be done which would really need it (and
also, little is likely to be done which is actually independent of the
underlying HW, so portable code in a loader is not likely a real goal).
[This is getting rather far afield from compilers, so I'm declaring it
over. Feel free to move it to comp.lang.c for C specific issues or
an OS or machine group. -John]

Walter Bright

unread,
Nov 11, 2010, 3:44:01 AM11/11/10
to
Jack Smith wrote:
> Do you know about a compiler C which can compile into 16 bit?

The Digital Mars C compiler can:
http://www.digitalmars.com/download/freecompiler.html

> i've tried watcom, but it's difficult to use and i need that 16 bit
> binary to be an ELF, not an OMF, besides ELF is rudimentary in wlink.

Why ELF? DMC generates OMF, and the linker will turn it into COM, SYS or EXE files.

---
Walter Bright
Digital Mars free C, C++, D programming language compilers
http://www.digitalmars.com

Marco

unread,
Nov 20, 2010, 10:29:06 AM11/20/10
to
On Nov 11, 1:44 am, Walter Bright <newshou...@digitalmars.com> wrote:
> Jack Smith wrote:
> > Do you know about a compiler C which can compile into 16 bit?
>
> The Digital Mars C compiler
can:http://www.digitalmars.com/download/freecompiler.html
>
> > i've tried watcom, but it's difficult to use and i need that 16 bit
> > binary to be an ELF, not an OMF, besides ELF is rudimentary in wlink.
>
> Why ELF? DMC generates OMF, and the linker will turn it into COM, SYS or EXE
files.

It seems that the OP needs to use the Watcom linker wlink as described
here:
http://wiki.osdev.org/WLink

0 new messages