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

16-bit real mode executable formats

146 views
Skip to first unread message

James Harris

unread,
Jan 27, 2013, 3:23:26 PM1/27/13
to
There don't seem to be many options for x86 16-bit real mode
executable formats!

Can you add any to the list below or would you recommend a particular
format? It is for a piece of code that will run in x86 real mode at
system boot time. The amount of code will likely be more than 64k so
it may need to use far calls and returns. I am not sure at the moment
whether I will need more than 64k of data. Probably not but if I do
then I will need to use far pointers.

I couldn't find a 16-bit executable format for Unix. (There must be
one for early Minix but I haven't found it yet and even if/when I do
it may be hard to find a compiler/linker which will target it.) It
seems Microsoft defined a number of formats.

* com - flat binary with a 256-byte header
* sys - flat binary without a header in the memory image but there is
a header on disk that does not get loaded into memory
* exe - a single name for a number of different types. See

http://www.wotsit.org/list.asp?fc=5

AIUI com and sys only support very small (64k) code/data combination.
Otherwise sys would probably be OK.

Some 16-bit exe formats are listed at

http://wiki.osdev.org/A.out

Of course, I'll need a compiler or assembler. Nasm is great if I end
up writing in assembly language but maybe there are better options.
Any recommendations for a C compiler or for an assembler?

If anyone else is interested there are some useful comments at

http://alexfru.narod.ru/

I just had a thought that I could possibly use an object format rather
than an executable format. Umm, not sure what options that leads
to....

James

s_dub...@yahoo.com

unread,
Jan 28, 2013, 12:36:29 AM1/28/13
to
On Jan 27, 2:23 pm, James Harris <james.harri...@gmail.com> wrote:
> There don't seem to be many options for x86 16-bit real mode
> executable formats!
>
> Can you add any to the list below or would you recommend a particular
> format? It is for a piece of code that will run in x86 real mode at
> system boot time. The amount of code will likely be more than 64k so
> it may need to use far calls and returns. I am not sure at the moment
> whether I will need more than 64k of data. Probably not but if I do
> then I will need to use far pointers.
>

There's dri's .cmd executable format, to add to the list, but whatever
you choose, do you intend to implement a loader program for it?

Anyway, http://www.seasip.demon.co.uk/Cpm/cmdfile.html

It's a 128 byte, 80h, header record, allowing for 8 sections:
CS,DS,ES,SS, plus 4 auxillary, -.bss like. The dw descriptors are
paragraphs, upper 16bits of 20bits where lower 4 bits, nibble, are
always implied as zero. This enforces segment alignment. There are a
number of details I won't go into now, but you can use nasm -f bin to
generate a .cmd image directly by using .text section for the header
record, and macros to fill in the fields from the rest of the section
sizes of .cseg,.dseg,.eseg, .sseg, etc. (I've done this myself). Hmm,
i forget what hll tools maybe useful. There's a small-c for cp/m-86,
but I don't recall if it generated the compact model sections that the
above describe. I messed with it quite abit about a decade ago. In
the end I realized how to use nasm -fbin to manage the startup of
segments through the startup code at the beginning of the binary
itself. Self-locating code, located at 'you are here' CS value, using
that to fill in the rest of the section selectors; DS,ES,SS,FS,GS, as
needed.

Steve

James Harris

unread,
Jan 28, 2013, 4:47:07 AM1/28/13
to
On Jan 28, 5:36 am, "s_dubrov...@yahoo.com" <s_dubrov...@yahoo.com>
wrote:
> On Jan 27, 2:23 pm, James Harris <james.harri...@gmail.com> wrote:
>
> > There don't seem to be many options for x86 16-bit real mode
> > executable formats!
>
> > Can you add any to the list below or would you recommend a particular
> > format? It is for a piece of code that will run in x86 real mode at
> > system boot time. The amount of code will likely be more than 64k so
> > it may need to use far calls and returns. I am not sure at the moment
> > whether I will need more than 64k of data. Probably not but if I do
> > then I will need to use far pointers.
>
> There's dri's .cmd executable format, to add to the list, but whatever
> you choose, do you intend to implement a loader program for it?

Yes, I'll load it myself.

> Anyway, http://www.seasip.demon.co.uk/Cpm/cmdfile.html

Cool - a CPM load module format!

>
> It's a 128 byte, 80h, header record, allowing for 8 sections:
> CS,DS,ES,SS, plus 4 auxillary, -.bss like.  The dw descriptors are
> paragraphs, upper 16bits of 20bits where lower 4 bits, nibble, are
> always implied as zero.  This enforces segment alignment.  There are a
> number of details I won't go into now, but you can use nasm -f bin to
> generate a .cmd image directly by using .text section for the header
> record, and macros to fill in the fields from the rest of the section
> sizes of .cseg,.dseg,.eseg, .sseg, etc. (I've done this myself).

I could set up the segment register values in executable code but you
have raised a good point. If I write in assembly then any kind of
header I am likely to need can be generated as DWs in the source. That
simplifies things a lot. Come back assembly - all is forgiven.

Any overtly real-mode code I wrote in the long-distant past was in
assembly language rather than in an HLL. (As an aside I started using
an assembler called Chasm. Anyone remember that?)

It *would* be helpful to be able to write the program in C so that
there are things like output routines. memory management and debugger
facilities built in. However, as the code has to run without an
operating system I wonder how successful the output of a compiler
would be. New territory for me. I believe Rod loads his OS from DOS
and uses C so he would have some real-mode code but it may rely on the
presence of DOS. All I will have available is the BIOS.

>  Hmm,
> i forget what hll tools maybe useful.  There's a small-c for cp/m-86,
> but I don't recall if it generated the compact model sections that the
> above describe.

Not sure if you mean "compact model" in the Intel memory model sense:

http://en.wikipedia.org/wiki/Intel_Memory_Model

I probably want what they call "medium" or "far" models. A lot of
nonsense all this memory model stuff, wasn't it!

That's the thing. If I write in C then I hope I could leave the
specifics of the calls and returns and much of the segment register
manipulation and data access to the compiler. If I write it in
assembly then I have to deal with those details.

For code the tiny difference in speed between near and far calls will
be utterly unimportant for this application so I can use far calls in
all cases. In itself that is no problem but it would require that the
assembler be able to generate a piece of code longer than 64k in the
selected output format so that might rule out the "flat binary" form.
It looks like I could use far indirect calls (opcode 0xff) which might
help the assembler.

Accessing data could be more of an issue. Depending on the size of
data blocks access to them might mean me having to deal with different
segments. I could check that the CPU was a 386 or above and then use
the so-called unreal mode.

http://wiki.osdev.org/Unreal_Mode

I could even switch to protected mode and back but this is getting
complicated.

Having only ever written small code in real mode it might be
educational to me to have to deal with segment registers the way the
old timers had to do it!

> I messed with it quite abit about a decade ago.  In
> the end I realized how to use nasm -fbin to manage the startup of
> segments through the startup code at the beginning of the binary
> itself.  Self-locating code, located at 'you are here' CS value, using
> that to fill in the rest of the section selectors; DS,ES,SS,FS,GS, as
> needed.

True. If I use an assembler I can see this could be done.

If anyone else at some point in the future has similar requirements
the Nasm manual has some helpful info on how to generate different
types of output file format.

I would still like at least to have the option to use a HLL, though.
Maybe I should look at writing some in an HLL - probably C - and some
in assembly. Before I start I will have more of a look at the various
exe formats. At least they can be produced by many compilers/linkers.

James

Kristoffer

unread,
Jan 28, 2013, 7:32:00 AM1/28/13
to
Try looking at an early version of TurboC or TurboPascal? I bet you can
get a torrent of it.

s_dub...@yahoo.com

unread,
Jan 28, 2013, 2:58:43 PM1/28/13
to
On Jan 28, 3:47 am, James Harris <james.harri...@gmail.com> wrote:
> On Jan 28, 5:36 am, "s_dubrov...@yahoo.com" <s_dubrov...@yahoo.com>
> wrote:
>
> > On Jan 27, 2:23 pm, James Harris <james.harri...@gmail.com> wrote:
>
> > > There don't seem to be many options for x86 16-bit real mode
> > > executable formats!
>
> > > Can you add any to the list below or would you recommend a particular
> > > format? It is for a piece of code that will run in x86 real mode at
> > > system boot time. The amount of code will likely be more than 64k so
> > > it may need to use far calls and returns. I am not sure at the moment
> > > whether I will need more than 64k of data. Probably not but if I do
> > > then I will need to use far pointers.
>
> > There's dri's .cmd executable format, to add to the list, but whatever
> > you choose, do you intend to implement a loader program for it?
>
> Yes, I'll load it myself.
>
> > Anyway,http://www.seasip.demon.co.uk/Cpm/cmdfile.html
>
> Cool - a CPM load module format!
>
>
>
> > It's a 128 byte, 80h, header record, allowing for 8 sections:
> > CS,DS,ES,SS, plus 4 auxillary, -.bss like.  The dw descriptors are
> > paragraphs, upper 16bits of 20bits where lower 4 bits, nibble, are
> > always implied as zero.  This enforces segment alignment.  There are a
> > number of details I won't go into now, but you can use nasm -f bin to
> > generate a .cmd image directly by using .text section for the header
> > record, and macros to fill in the fields from the rest of the section
> > sizes of .cseg,.dseg,.eseg, .sseg, etc. (I've done this myself).
>
> I could set up the segment register values in executable code but you
> have raised a good point. If I write in assembly then any kind of
> header I am likely to need can be generated as DWs in the source. That
> simplifies things a lot. Come back assembly - all is forgiven.
>
> Any overtly real-mode code I wrote in the long-distant past was in
> assembly language rather than in an HLL. (As an aside I started using
> an assembler called Chasm. Anyone remember that?)
>
> It *would* be helpful to be able to write the program in C so that
> there are things like output routines. memory management and debugger
> facilities built in. However, as the code has to run without an

There's the rub, and you've started another thread on it already.
I'll add that there is the C interface to consider, the ABI, how
parameters are passed, what registers are required to do that, etc. -
you have to buy into that, it becomes a part of your OS design.

> operating system I wonder how successful the output of a compiler
> would be. New territory for me. I believe Rod loads his OS from DOS
> and uses C so he would have some real-mode code but it may rely on the
> presence of DOS. All I will have available is the BIOS.
>
> >  Hmm,
> > i forget what hll tools maybe useful.  There's a small-c for cp/m-86,
> > but I don't recall if it generated the compact model sections that the
> > above describe.
>
> Not sure if you mean "compact model" in the Intel memory model sense:

No, I meant it in the DRI sense, it's their definition,
specification. It is a multi-segment model, DS & SS are in separate
memory segments for example, and separate from the ES memory segment,
so string operations (which use ES:offset as a destination) require
care, or knowledge of that.

>
>  http://en.wikipedia.org/wiki/Intel_Memory_Model
>
> I probably want what they call "medium" or "far" models. A lot of
> nonsense all this memory model stuff, wasn't it!
>

Well, the article is no help, they're really describing _compiler_
memory models. And there is some differences between vendors on what
those definitions are for their tools.

> That's the thing. If I write in C then I hope I could leave the
> specifics of the calls and returns and much of the segment register
> manipulation and data access to the compiler. If I write it in
> assembly then I have to deal with those details.
>

What is the over-arching goal? Don't take your eyes off of that.

>
> For code the tiny difference in speed between near and far calls will
> be utterly unimportant for this application so I can use far calls in
> all cases. In itself that is no problem but it would require that the
> assembler be able to generate a piece of code longer than 64k in the
> selected output format so that might rule out the "flat binary" form.

Not at all. Assemblers can generate >64k.

> It looks like I could use far indirect calls (opcode 0xff) which might
> help the assembler.
>

Speed is the wrong target, consider complexity. You probably don't
require far calls at all. You can bundle generic functions into
modules, and enter those thru an Int xxh, -just an idea. I've done it
both ways in assembler. Looking to a HLL, good luck, you are
constricted to its strictures.

The subject of OS design is a jigsaw puzzle, to be sure. For user's
and utilities, a HLL is (let's say) required. The choice of HLL tools
and development platform affect the range of choices for your OS
design.

Steve

Alexei A. Frounze

unread,
Jan 30, 2013, 6:51:14 AM1/30/13
to
The regular DOS MZ .EXE is good enough and simple enough to parse/
relocate (some 50 lines of assembly code will do the trick). You can
generate said .EXEs with 16-bit Turbo C/C++ (and its linker TLINK),
Open Watcom C/C++ (+WLINK), 16-bit Microsoft's LINK (if you find it
somewhere) and probably some other compilers and linkers. I'm using a
combination of NASM + Turbo C/TLINK + Open Watcom/WLINK. I can run
Turbo C/TLINK in DosBox and OW/WLINK runs directly on Windows as does
NASM. OW and NASM should come with Linux binaries as well, but I've
only tried NASM on Linux and can't speak for OW.

Alex

James Harris

unread,
Jan 30, 2013, 10:54:27 AM1/30/13
to
On Jan 30, 11:51 am, "Alexei A. Frounze" <alexfrun...@gmail.com>
wrote:
> On Jan 27, 12:23 pm, James Harris <james.harri...@gmail.com> wrote:
>
>
>
> > There don't seem to be many options for x86 16-bit real mode
> > executable formats!
...

> The regular DOS MZ .EXE is good enough and simple enough to parse/
> relocate (some 50 lines of assembly code will do the trick).

Agreed. After much searching around MZ seems to be the way to go.
These are the best two docs I found on the format.

http://www.tavi.co.uk/phobos/exeformat.html
http://www.delorie.com/djgpp/doc/exe/

> You can
> generate said .EXEs with 16-bit Turbo C/C++ (and its linker TLINK),
> Open Watcom C/C++ (+WLINK), 16-bit Microsoft's LINK (if you find it
> somewhere) and probably some other compilers and linkers. I'm using a
> combination of NASM + Turbo C/TLINK + Open Watcom/WLINK. I can run
> Turbo C/TLINK in DosBox and OW/WLINK runs directly on Windows as does
> NASM. OW and NASM should come with Linux binaries as well, but I've
> only tried NASM on Linux and can't speak for OW.

I have been trying Open Watcom's C. So far it has taken me many (too
many) hours trying to persuade Open Watcom (OW) to produce a Hello
World MZ executable but I have finally got at least something
generated. This may be due to my lack of experience with it but I
found the installation software to be buggy and the help files to be
out of date. Rather than there being a paucity of documentation for OW
there seems to be too many documents. It is hard to know which to look
at.

In particular Open Watcom kept generating 32-bit code and I could not
force the Windows version of it to create a DOS 16-bit MZ exe file. In
the end I got it to work by installing the DOS-only version configured
so the kind of file I wanted was the only output option it had.
Otherwise even it kept generating (or at least kept complaining about)
a 32-bit object code file. The DOS version seems to generate output
file names always in upper case which is a bit annoying but not
important.

Having said all that, now it is working Open Watcom does look in
theory like a good choice. I have just set up (in a VM) a DOS
environment in which to run the 8086 code. The idea is that I can test
the code in DOS and as long as it only accesses the BIOS and the
hardware (and does not make any DOS calls or rely on the PSP) then it
should work on bare metal.

If you managed to get Windows OW to generate 16-bit MZ executables do
you remember any tricks as to how you did it?

I'll keep in mind the other options you mention in case OW doesn't
play ball in some way.

James

Alexei A. Frounze

unread,
Jan 31, 2013, 5:40:20 AM1/31/13
to
On Jan 30, 7:54 am, James Harris <james.harri...@gmail.com> wrote:
> On Jan 30, 11:51 am, "Alexei A. Frounze" <alexfrun...@gmail.com>
> wrote:> On Jan 27, 12:23 pm, James Harris <james.harri...@gmail.com> wrote:
>
> > > There don't seem to be many options for x86 16-bit real mode
> > > executable formats!
>
> ...
>
> > The regular DOS MZ .EXE is good enough and simple enough to parse/
> > relocate (some 50 lines of assembly code will do the trick).
>
> Agreed. After much searching around MZ seems to be the way to go.
> These are the best two docs I found on the format.
>
>  http://www.tavi.co.uk/phobos/exeformat.html
>  http://www.delorie.com/djgpp/doc/exe/

Here: http://alexfru.narod.ru/os/fat/fat.html

Inside http://alexfru.narod.ru/os/fat/fat-2006-12-03.zip there are 2
versions of a bootloader for FAT12 and FAT16 (_boot
\BOOT12.ASM,BOOT16.ASM) that load and execute either a DOS .COM or a
DOS .EXE.

This is all the code that's needed:
;;;;;;;;;;;;;;;;;;;
;; Type checking ;;
;;;;;;;;;;;;;;;;;;;

push byte ImageLoadSeg
pop ds
mov ax, ds ; ax=ds=seg the program is
loaded to

cmp word [0], 5A4Dh ; "MZ" signature?

je RelocateEXE ; yes, it's an EXE program

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Setup and Run COM program ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

sub ax, 10h ; "org 100h" stuff :)
mov es, ax
mov ds, ax
mov ss, ax
xor sp, sp
push es
push word 100h
jmp short Run

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Relocate, setup and run EXE program ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

RelocateEXE:

add ax, [08h] ; ax = image base
mov cx, [06h] ; cx = reloc items
mov bx, [18h] ; bx = reloc table pointer

jcxz RelocationDone

ReloCycle:
mov di, [bx] ; di = item ofs
mov dx, [bx+2] ; dx = item seg (rel)
add dx, ax ; dx = item seg (abs)

push ds
mov ds, dx ; ds = dx
add [di], ax ; fixup
pop ds

add bx, 4 ; point to next entry
loop ReloCycle

RelocationDone:

mov bx, ax
add bx, [0Eh]
mov ss, bx ; ss for EXE
mov sp, [10h] ; sp for EXE

add ax, [16h] ; cs
push ax
push word [14h] ; ip
Run:
mov dl, [cs:bsDriveNumber] ; let program know boot drive

; set the magic number so the program knows who has loaded it:
mov si, 16381 ; prime number 2**14-3
mov di, 32749 ; prime number 2**15-19
mov bp, 65521 ; prime number 2**16-15

retf
Here: http://alexfru.narod.ru/os/c16/c16.html
You will need this: http://alexfru.narod.ru/os/c16/C16SDKWatcom.zip
Run mk_misc.bat and then mk_test.bat. They should compile testc.c
+testa.asm into into a .COM and an .EXE.

> I'll keep in mind the other options you mention in case OW doesn't
> play ball in some way.
>
> James

Alex

Rod Pemberton

unread,
Feb 4, 2013, 2:41:15 AM2/4/13
to
"James Harris" <james.h...@gmail.com> wrote in message
news:99ac5e88-8b60-4510...@j2g2000yqj.googlegroups.com...

...

> I have been trying Open Watcom's C. So far it has
> taken me many (too many) hours trying to persuade
> Open Watcom (OW) to produce a Hello World MZ
> executable but I have finally got at least something
> generated. This may be due to my lack of experience
> with it but I found the installation software to be buggy
> and the help files to be out of date. Rather than there
> being a paucity of documentation for OW there seems
> to be too many documents. It is hard to know which to
> look at.

...

> In particular Open Watcom kept generating 32-bit
> code and I could not force the Windows version of
> it to create a DOS 16-bit MZ exe file. In the end I got
> it to work by installing the DOS-only version
> configured so the kind of file I wanted was the only output
> option it had. Otherwise even it kept generating (or at least
> kept complaining about) a 32-bit object code file. The DOS
> version seems to generate output file names always in upper
> case which is a bit annoying but not important.


I'm using OpenWatcom v1.3.

I don't know they've done to it since then.


To generate a 16-bit RM (real-mode) DOS .exe, I do:

wcl/l=dos code.c

To generate a 32-bit PM (protected-mode) DOS DPMI .exe, I do:

wcl386/l=dos4g code.c

That produces 32-bit DPMI code and binds the executable with
DOS4GW variant of the DOS4G DPMI host.

FYI, DPMI = Dos Protected Mode Interface

The common compile flags are:

-otexan
-wx

You can also manually compile and link:

wcc code.c
wcc file.c
wlink sys dos file program.exe file code.obj,file.obj

Don't "quote" me on that wlink line being correct...

wlink can specify the system, e.g., "dos" or "dos4g", etc. with
"sys" or it can specify the file type with "format", e.g., "elf",
etc. OpenWatcom supports many system and file types. That
should be in the linker documentation, somewhere ...

BTW, DOS .com's don't have a header.


Rod Pemberton



Rod Pemberton

unread,
Feb 4, 2013, 2:41:43 AM2/4/13
to
"James Harris" <james.h...@gmail.com> wrote in message
news:a31b4f3b-8425-4b14...@l9g2000yqp.googlegroups.com...

...

> It *would* be helpful to be able to write the program
> in C so that there are things like output routines. memory
> management and debugger facilities built in.

Memory management will or should be part of your before
the "OS has been loaded" code. It's likely some of the memory
that code or your bootloader uses will need to be preserved and
passed to the OS.

Anyway, memory management is not part of the C language proper.
It is part of the C libraries, either explicitly via malloc() or
sbrk() or mmap() or implicitly via fwrite(), e.g., appending to
end-of-file, or tmpfile(), i.e., usually implemented in memory.
Unfortunately, they are all OS dependent. But, you can declare
fixed-size arrays, etc., within the C language proper to allocate
space. Or, you can declare procedure local variables to allocate
space on the stack (if it has one), e.g., like alloca(). Exiting
the procedure "free's" the space.

> However, as the code has to run without an operating system
> I wonder how successful the output of a compiler would be.
> New territory for me. I believe Rod loads his OS from DOS
> and uses C so he would have some real-mode code but it may
> rely on the presence of DOS. All I will have available is the
> BIOS.

Yes, I start from DOS which is 16-bit. I have a bit of special
code as a TSR and an entry interface. The application must be
32-bit DPMI. It's the 32-bit compiled C code that is my OS. The
16-bit part is a loader for the DPMI host and normal DOS .exe
startup. Essentially, I unload the DPMI host which also unloads
DOS, and then return to my 32-bit entry point. I thought my OS
was "free" of DOS, and was working on starting it with Multiboot
via GRUB. But, if you remember, at your prompting, I found a few
functions it's still using. I believe that most are "easy" to
fix, if I ever get back to working on it. A few might be more
complicated. They're integral to the DOS startup methods for DPMI
executables. I didn't use any special compiler flags like those
that GCC has to make the OS independent of the host OS. If
you recall, I'm using two C compilers DJGPP (GCC + custom
DOS C library) and OpenWatcom (v1.3). I'm not sure if OpenWatcom
can produce a stand-alone binary that's OS independent. AIR, OW
can only produce executable and object formats.

> That's the thing. If I write in C then I hope I could leave the
> specifics of the calls and returns and much of the segment
> register manipulation and data access to the compiler. If I
> write it in assembly then I have to deal with those details.

That's an advantage of a high-level language (HLL) compiler.

Real-mode (RM) support is not common anymore.

I think what you'll find is mostly DOS specific compilers like
OpenWatcom, TurboC, or even early versions of LCC (3.4, 3.5) etc.


Rod Pemberton



James Harris

unread,
Feb 6, 2013, 1:25:46 PM2/6/13
to
On Feb 4, 7:41 am, "Rod Pemberton" <do_not_h...@notemailnotz.cnm>
wrote:

...

> I'm using OpenWatcom v1.3.
>
> I don't know they've done to it since then.
>
> To generate a 16-bit RM (real-mode) DOS .exe, I do:
>
>   wcl/l=dos code.c
>
> To generate a 32-bit PM (protected-mode) DOS DPMI .exe, I do:
>
>   wcl386/l=dos4g code.c
>
> That produces 32-bit DPMI code and binds the executable with
> DOS4GW variant of the DOS4G DPMI host.
>
> FYI, DPMI = Dos Protected Mode Interface
>
> The common compile flags are:
>
>  -otexan
>  -wx
>
> You can also manually compile and link:
>
>   wcc code.c
>   wcc file.c
>   wlink sys dos file program.exe file code.obj,file.obj

Thanks for that info. That helped me to get started.

> Don't "quote" me on that wlink line being correct...

Apparently it is:

wlink sys dos name program file code.obj,file.obj

Have you managed with Open Watcom to call C code from assembly or vice
versa? If I start in C - by including a main() routine - the main()
routine works on its own but if I add a call to an assembly routine
somewhere lower down then the main() routine doesn't even seem to
start. There's no error message. It just returns silently without
writing any output.

* If I begin in the assembly side - by using an assembly label ..start
- the linker complains that routines like sprintf_ are undefined even
though the C file has stdio.h included and apparently compiles
properly. The linker also complains that no stack has been defined so
I am guessing that the compiler does some extra work if it sees a
main() routine...?

James

Rod Pemberton

unread,
Feb 7, 2013, 1:14:57 AM2/7/13
to
"James Harris" <james.h...@gmail.com> wrote in message
news:c7693a8b-fd3c-49ea...@j4g2000vby.googlegroups.com...
> On Feb 4, 7:41 am, "Rod Pemberton"
<do_not_h...@notemailnotz.cnm>
> wrote:

...

> Have you managed with Open Watcom to call C code
> from assembly or vice versa?

Yes, I call C code from some NASM assembly.

(It's probably not exactly what you're asking about.
You'll have to read the end of the message.)

> If I start in C - by including a main() routine - the main()
> routine works on its own but if I add a call to an assembly
> routine somewhere lower down then the main() routine
> doesn't even seem to start. There's no error message. It
> just returns silently without writing any output.

Generally, I use inline assembly in C for OW v1.3. For the most
part, I don't use the #pragma aux style of assembly functions
supported by OW. However, both setup a C procedure. I.e., both
are callable in C since they have C procedure names. The C
function's name also act's as a label that assembly can reference
when declared this way.

E.g., I do:

void __declspec(naked) xname(void)
{
_asm {
nop
nop
ret
}
}

IIRC, the "naked" attribute eliminates some or all of the
procedure's prologue and epilogue. I.e., it's a raw assembly
routine that can be called as a function in C as xname(). I think
the "void proc(void)" declaration does much the same for GCC as
the "naked" attribute. I don't think there is an exact equivalent
though for GCC. I was never able to locate an __attribute__()
that did the same.

One issue I have with OW v1.3 is it doesn't support forward
reference labels in _asm{} sections. You have to use backward
references. FYI, in such cases, I split the code into two
functions like above. OW can reference the other C function in
the assembly in the latter _asm{} section as a label.

I'm not 100% sure, but the #pragma aux equivalent of the above
should look something like:

extern void xname(void);
#pragma aux xname = \
"nop" \
"nop" \
"ret" ;
#endif

Note the quotes and backslashes for line continuation and other
syntax stuff. That's without passing parameters ... cringe.

GCC optionally allows inputs, outputs, and a clobber list at the
end of it's inline assembly. OW allows them too, but I'm not very
familiar with it's syntax.

> * If I begin in the assembly side - by using an assembly label
> ..start - the linker complains that routines like sprintf_ are
> undefined even though the C file has stdio.h included and
> apparently compiles properly. The linker also complains
> that no stack has been defined so I am guessing that the
> compiler does some extra work if it sees a main() routine...?

Are you attempting to jump directly into main() via an assembly
routine that's linked in? :-O I sure hope not!

I don't do that, for either of my two start methods, one of which
works, and one of which doesn't (yet). I'll describe them so you
may get an idea or ideas of what need to be done, then get back to
your variation of the issue.

The one that works is like a "trampoline". This the DOS start
method using a special TSR and compiled in C and assembly code.
It starts main(), saves critical registers, calls C's exit(),
"trade secrets" ... , sets up PM, sets up critical registers,
restarts the executable. In this case, the critical registers
needed by OW's compiled code are known in advance. They were
setup by the DPMI host under DOS and passed directly to the C code
runtime. (Critical registers described in the last paragraph.)

The other one that I was working on is a standard bootloader
method. I chose Multiboot via Grub. Multiboot requires a header.
I was attempting to have the header placed into the file without
using linker script or linker options... I was having issues
getting it below 8KB (?). ... I create a normal DOS DPMI
executable, as before, even though much of that functionality
isn't needed for Multiboot. It's convenient. In this case, the
critical registers needed to correctly execute the C code are
*not* known in advance. They must be completed by some
intermediate assembly code prior to jumping to the C code entry.
Also, some of the 32-bit PM setup isn't completed by Grub. Grub
only does a very minimal PM setup. I.e., if you use Grub to start
your OS code, you must setup GDT, IDT, selectors, segment
registers, ESP, etc too, prior to the C code. Even if I get the
location Multiboot header placed correctly, I'm not sure it's
entirely possible to setup the header and intermediate code
without some information from the linking. If I do manage to get
this working, I'll probably "hide" the intermediate assembly code
in inline assembly within a C procedure.

If you're attempting to jump directly into main(), I think you'll
need to do a bunch of standard 32-bit PM startup stuff as well as
setup the critical registers for proper execution of OW's compiled
C. If you're linking this code in, you may actually need to
replace OW's executable startup code or runtime with your own
version... That's why I was avoiding linking in any direct
startup code. That way I can just build a normal executable
supported by the C compiler, without issue. Of course, with me
working on my own toolchain, that's probably not so important
anymore.

If you're not interested in all that, you might try various linker
options or linker scripts. With them, you should be able to
produce ELF executables and set the entry point. ELF should avoid
needing a Multiboot or some other bootloader header. IIRC, Grub
and other bootloaders can load ELF executables directly.
Alternately, if you can live without linking, an object format
might work fine. There are a few obj loaders. Also, you should
be able to call main() from your assembly, as main_ or perhaps
_main ... Note that I said "call" not "jump". main() must return
... Think about that. main() must allow return() and exit() to
return from it. I.e., you might want an infinite loop inside main
and jump into it ... (?) Or, you might want to setup fake return
address on the stack which restarts, halts, or exits the OS if an
errant return() or exit() is used ... If you call main(), you may
still have to handle the linking and executable runtime
replacement issues. Anyway, all that stuff is custom code.

The critical registers for OW (when not using stack calling
convention) are (or where): intruction pointer EIP (your code
entry point), stack pointer ESP, frame pointer EBP, and
descriptors and selectors for code and data segments, with correct
base, limit, priviledge, etc, i.e., load CS, DS, ES, FS, GS, SS.
It's possible other stuff like direction flag or all flags need
setup too.

Sorry, that was fairly long...


Rod Pemberton


japheth

unread,
Feb 7, 2013, 5:53:47 PM2/7/13
to
> versa? If I start in C - by including a main() routine - the main()
> routine works on its own but if I add a call to an assembly routine
> somewhere lower down then the main() routine doesn't even seem to

Here's a sample ( C -> assembly )

---------------------------------------------------------- C part
#include "stdio.h"

extern int asm_proc( int );

int main()
{
printf("asm_proc(1)=%u\n", asm_proc(1) );
return 0;
}
----------------------------------------------------------

---------------------------------------------------------- assembly
part ( Masm )
.286
.model small, c

.code

asm_proc proc a1:word
mov ax, a1
add ax, ax
ret
asm_proc endp

end
----------------------------------------------------------

compiler cmd:

wcc -ecc sample.c

assembler cmd:

ml -c asm_proc.asm

linker cmd:

wlink sys dos file sample.obj, asm_proc.obj


The only thing that is peculiar is the -ecc argument for the compiler.
It tells the 16-bit compiler to use the cdecl calling convention
instead of the "watcom" calling convention ( which is a fastcall
variant ).

James Harris

unread,
Feb 15, 2013, 4:24:14 PM2/15/13
to
On Feb 7, 6:14 am, "Rod Pemberton" <do_not_h...@notemailnotz.cnm>
wrote:
> "James Harris" <james.harri...@gmail.com> wrote in message
>
> news:c7693a8b-fd3c-49ea...@j4g2000vby.googlegroups.com...> On Feb 4, 7:41 am, "Rod Pemberton"
>
> <do_not_h...@notemailnotz.cnm>
>
> > wrote:
>
> ...
>
> > Have you managed with Open Watcom to call C code
> > from assembly or vice versa?
>
> Yes, I call C code from some NASM assembly.
>
> (It's probably not exactly what you're asking about.
> You'll have to read the end of the message.)

You show inline assembly below but didn't you mention that you call C
from Nasm assembly. How do you assemble, compile and link them? Does
your C have a main() function? I really need to omit any main function
but if I try to link without one the linker complains about the lack
of a stack and has other issues. I have tried to add a stack by
linking with

wlink sys dos option stack=4096 file t6.obj

but it still doesn't work. I have set up a stack in a separate module
and tried to link the two together but with no improvement. I still
get messages like

Error! E2028: __STK is an undefined reference
Error! E2028: _big_code_ is an undefined reference
Warning! W1014: stack segment not found
creating a DOS executable
file t6.obj(S:\PROJ\OS\LOAD\HDD\TEST\t6.c): undefined symbol __STK

...

> I'm not 100% sure, but the #pragma aux equivalent of the above
> should look something like:
>
> extern void xname(void);
> #pragma aux xname = \
>         "nop" \
>         "nop" \
>         "ret" ;
> #endif

I tried the pragma aux stuff first. A clever feature but horrible!

>
> Note the quotes and backslashes for line continuation and other
> syntax stuff.  That's without passing parameters ... cringe.

To a C programmer the adjacent strings *not* being concatenated looks
weird doesn't it!

> GCC optionally allows inputs, outputs, and a clobber list at the
> end of it's inline assembly.  OW allows them too, but I'm not very
> familiar with it's syntax.

I had these working but hated it. The 16-bit argument matching rules
were especially awkward. And the C code had become very far from
standard C so I looked around for a way to link with asm instead.

> > * If I begin in the assembly side - by using an assembly label
> > ..start - the linker complains that routines like sprintf_ are
> > undefined even though the C file has stdio.h included and
> > apparently compiles properly. The linker also complains
> > that no stack has been defined so I am guessing that the
> > compiler does some extra work if it sees a main() routine...?
>
> Are you attempting to jump directly into main() via an assembly
> routine that's linked in?  :-O  I sure hope not!

If I include a main() function the compiler adds in a lot of stuff I
don't want. Stuff from crt0.o or something similar, I guess.

Is there any problem with starting in assembly and calling C routines
from there - i.e. with no main() function in sight?

...

> If you're attempting to jump directly into main(), I think you'll
> need to do a bunch of standard 32-bit PM startup stuff as well as

No 32-bit. This is purely 16-bit.

...

> If you're not interested in all that, you might try various linker
> options or linker scripts.  With them, you should be able to
> produce ELF executables and set the entry point.

There is no 16-bit ELF. Elf starts at 32 bits.

...

> Sorry, that was fairly long...

Not at all. Thanks for the info.

James

James Harris

unread,
Feb 15, 2013, 4:34:34 PM2/15/13
to
On Jan 27, 8:23 pm, James Harris <james.harri...@gmail.com> wrote:
> There don't seem to be many options for x86 16-bit real mode
> executable formats!

...

> I couldn't find a 16-bit executable format for Unix. (There must be
> one for early Minix but I haven't found it yet and even if/when I do
> it may be hard to find a compiler/linker which will target it.) It
> seems Microsoft defined a number of formats.
>
> * com - flat binary with a 256-byte header
> * sys - flat binary without a header in the memory image but there is
> a header on disk that does not get loaded into memory
> * exe - a single name for a number of different types. See

I did eventually find a 16-bit Unix format - labelled as minix a.out:

http://faculty.qu.edu.qa/rriley/cmpt507/minix/minix_2a_8out_8h-source.html

It can be produced by bcc (sudo apt-get install bcc), as86 and nasm.
Linking can be done by using ld86.

The format looks great: simple, complete and from the field widths
seems to support large code and data areas. It can generate separate
instruction and data segments and the header appears to be simple to
process.

The slight but important problem is with the compiler. I could not get
bcc to generate 16-bit code larger than 64k and don't know of another
compiler that will.

I might be able to compile modules of up to 64k each and link them
together. I suspect that will need another linker too but that seems
to be an issue of tools. In case anyone else is interested the 16-bit
a.out format itself seems OK.

James

wolfgang kern

unread,
Feb 15, 2013, 5:12:43 PM2/15/13
to

James Harris in discussion about C and 16-bit wrote:

> ...

not quite sure if I understood the question yet :)
methink we had this discussion in the near past already.

Again my oppinion on bootable sectors:

Any embedded OS
or independant bootable game
or any standalone OS apart from Windoze/Loonix-FS
or whatsoever different to this 'Oh So common'-shit
...
will all have one in common, and that's the so called
master boot record which just tells the BIOS to execute
the first 512 bytes of a recognised media on a machine.

so even windoze, loonix and Mac will follow the obove rules
without giving up it's very own behaviour after this.

My KESYS will be 'booted' by BIOS the same way, but then
the following story looks quite different to all the common.

methink there is a lot of room to boot a game/OS/tools right
after power up anyway. But after execution of the code found
within the first sector of a physical drive we aren't bound
to anything at all. The code found in the first sector of a
volume can lead to whatsoever we (and our clients) may desire.

__
wolfgang



Kristoffer

unread,
Feb 15, 2013, 6:10:20 PM2/15/13
to
Here here (holds glass of scotch up)

Rod Pemberton

unread,
Feb 16, 2013, 8:19:54 AM2/16/13
to
"James Harris" <james.h...@gmail.com> wrote in message
news:c1f489be-3db6-495f...@cd3g2000vbb.googlegroups.com...
> On Feb 7, 6:14 am, "Rod Pemberton"
<do_not_h...@notemailnotz.cnm>
> wrote:
> > "James Harris" <james.harri...@gmail.com> wrote in message
...

It seems I do have an incomplete method for OW startup from Nasm.
This section was written after the rest of the post prior to me
realizing that. Please take into account the fact that some of
the responses in the remainder of the post don't reflect this.

I was working on a Multiboot header, both C and Nasm since I
didn't know what I could make work, , non-working AIR. That might
link Nasm code and C code. Let me go look at that to see what I
was doing ... Okay, I have Nasm assembly linked to OW C code.
It's 32-bit C code though. It's also linked as an ELF... You
said below you couldn't use ELF. Anyway, it's "nasm -f obj"
linked to OW obj's via wlink. For GCC, I was using "nasm -f aout"
linked with LD. You might be able to use "nasm -f obj" with
16-bit OW obj's too... Yeah! It looks like I did manage link to
main(). It's for 32-bits. From what I can tell, it seems I only
got the MB header "working" for DJGPP and not OW... I'll do a
straight 16-bit conversion of relevant Nasm setup:

BITS16
segment _TEXT public align=1 class=code use16
EXTERN _cmain
..start:
GLOBAL start
start:

; ... setup ESP, SS, BP, flags, save Multi-boot data
; ... for 32-bit code ...

call _cmain

; ... code to handle return or exit from _cmain
; Multi-boot header


On my end, changing it to 16-bits, it still assembles fine as
"nasm -f obj". I didn't test linking it to 16-bit obj from OW C
code though... If you're still having problems, let me know if
you'd like me to "play" around with that. I suspect that a .com
executable format can't be generated. That might just leave a
16-bit DOS .exe...


> > > Have you managed with Open Watcom to call
> > > C code from assembly or vice versa?
>
> > Yes, I call C code from some NASM assembly.
>
> You show inline assembly below but didn't you
> mention that you call C from Nasm assembly.

Uh, not exactly...

> How do you assemble, compile and link them?

The assembly code that is linked to the C code is inline assembly
in C, both for OW and DJGPP. There is Nasm assembly, but it's
separate from the C code. It's not linked to the C code. It's
compiled to a DOS .com via NASM -f obj.

Currently, I don't have a method to call the C code directly via
assembly when loading by a bootloader. That's what you're working
on. That's what I was working on with the Multiboot code. I'm
likely to try to find a way for the Multiboot code to jump into
some inline assembly in C... At this point, that's theoretical
and experimental but highly plausible in my mind, i.e., probably
doable. That assembly will complete the OS setup, then call a
naked C routine to start the C code.

My Nasm code is intermediate. It's called *after* the executable
is run. You don't need to worry about that. It's unlikely you'll
do anything like that. It there to eliminate the 32-bit DPMI host
and 16-bit DOS, so my OS has full control. I.e., it's a
"backwards" way to create an OS loader under DOS.


> Does your C have a main() function?

Yes, currently, main() starts normally under my host OS, i.e.,
DOS. That'll have to change. I'll need a way to jump into the OS
image, finish OS setup after transferring from the bootloader, do
C code setup, transfer to the C code, which likely won't be to
main(). It'll likely be to my other special entry point.

> I really need to omit any main function ...

Except for 16-bit .com's, OW seems to only produce executables and
objects by default, not raw binaries. You could try the "stub"
option I mentioned in the other thread with a standard executable
format, or you could try .obj's. Those are ways to remove or not
have the normal startup or entry.

> ... but if I try to link without one the linker
> complains about ...

If you're transferring to code other than main() somehow, you
might leave a "dummy" main in ...

> If I include a main() function the compiler adds in a lot of
> stuff I don't want. Stuff from crt0.o or something similar,
> I guess.

Yes, it does. :-( Other than coding your own crt0.o or stub, I
haven't found a good solution for this. ELF or Multiboot are what
I've been looking at to eliminate some of these issues.

> Is there any problem with starting in assembly and
> calling C routines from there

This should be possible. I just don't fully know how to do so yet
with OW. I've seen examples of people linking their own startup
code to main() for other OSes. You might even find a few examples
on comp.lang.c or news.openwatcom.org. OW just seems to be
"difficult" in this regard. That may just be my ignorance...

> ... - i.e. with no main()
> function in sight?

I'm not sure about not having a main()... IIRC, main() is the
official entry point for the C code per the C specifications.
I.e., non-standard at best to eliminate it.

> > If you're attempting to jump directly into main(), I think
> > you'll need to do a bunch of standard 32-bit PM
> > startup stuff as well as [...]
>
> No 32-bit. This is purely 16-bit.

You'll likely still need some startup. The 16-bit code likely
assumes certain segment registers are set to certain values, etc.
Have you used 'objdump' or 'wdis' etc to look at OW's code?

> > If you're not interested in all that, you might try various
> > linker options or linker scripts. With them, you should
> > be able to produce ELF executables and set the entry point.
>
> There is no 16-bit ELF. Elf starts at 32 bits.

Ah!

16-bit is probably a problem with Windows and NT formats too...
That might eliminate the availability of the "stub" option...


Rod Pemberton




James Harris

unread,
Feb 18, 2013, 4:25:25 AM2/18/13
to
On Feb 16, 1:19 pm, "Rod Pemberton" <do_not_h...@notemailnotz.cnm>
wrote:

...

> > Is there any problem with starting in assembly and
> > calling C routines from there
>
> This should be possible.  I just don't fully know how to do so yet
> with OW.  I've seen examples of people linking their own startup
> code to main() for other OSes.  You might even find a few examples
> on comp.lang.c or news.openwatcom.org.  OW just seems to be
> "difficult" in this regard.  That may just be my ignorance...

On news://news.openwatcom.org/openwatcom.users.c_cpp as you suggested
I got some help and can now link nasm with OW. As desired the nasm
code is executed first and no C main function is needed. Instead, the
assembly code supplies the start symbol.

Here is how it is set up:

The assembly code for an OMF (i.e. .obj) build includes

..start:
mov ax, data ;Set up
mov ds, ax ; the segment
mov es, ax ; registers
call far t6_func_ ;Call the C function
section .data align=16
db "!!AA"
section .stack class=stack align=16
resb 4096 ;Stack size

There is no main function. The entire C code is

int t6_func()
{
return 30;
}

The sources are built with

nasm -fobj -O2 s6.nasm
wcc t6.c -za -ml -wx -q -s -zls
wlink sys dos name t6 file t6.obj, s6.obj

These produce a t6.exe file which is just 84 bytes long. (It is a lot
bigger if the objs t6 and s6 are linked in the opposite order.) As you
can see the test code doesn't produce any output but can be tested
with DOS debug. This shows it starts at ..start and correctly calls
and returns from t6_func_.

S:\proj\os\load\hdd\test>debug t6.exe
-r
AX=0000 BX=FFFF CX=FE54 DX=0000 SP=1010 BP=0000 SI=0000 DI=0000
DS=1A20 ES=1A20 SS=1A32 CS=1A30 IP=0004 NV UP EI PL NZ NA PO NC
1A30:0004 B8321A MOV AX,1A32
-t
AX=1A32 BX=FFFF CX=FE54 DX=0000 SP=1010 BP=0000 SI=0000 DI=0000
DS=1A20 ES=1A20 SS=1A32 CS=1A30 IP=0007 NV UP EI PL NZ NA PO NC
1A30:0007 8ED8 MOV DS,AX

etc.

For reference, the wcc compiler switches have these meanings.

-za disable extensions (i.e., accept only ISO/ANSI C)
-ml large memory model (large code/large data)
-wx set warning level to maximum setting
-q operate quietly
-s remove stack overflow checks
-zls remove automatically inserted symbols

It was -s which removed the C code attempt to call __STK, and -zls
stopped it looking for the _big_code_ label.

>
> > ... - i.e. with no main()
> > function in sight?
>
> I'm not sure about not having a main()...  IIRC, main() is the
> official entry point for the C code per the C specifications.
> I.e., non-standard at best to eliminate it.

According to the Nasm manual the OMF standard is for execution to
begin at a special label called ..start, as you know. It seems to work
without main and the C code looks correct without main. The C code for
t6_func compiles to simply

mov ax, 30
retf

Which is exactly what would be wanted.

> > > If you're attempting to jump directly into main(), I think
> > > you'll need to do a bunch of standard 32-bit PM
> > > startup stuff as well as [...]
>
> > No 32-bit. This is purely 16-bit.
>
> You'll likely still need some startup.  The 16-bit code likely
> assumes certain segment registers are set to certain values, etc.
> Have you used 'objdump' or 'wdis' etc to look at OW's code?

The Nasm manual suggests the startup for .exe files as

..start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,stacktop

I found that under DOS I didn't need to set the stack, only the data
segment.

James

Rod Pemberton

unread,
Feb 18, 2013, 6:39:17 PM2/18/13
to
"James Harris" <james.h...@gmail.com> wrote in message
news:4aca3a54-d660-44d6...@14g2000vbr.googlegroups.com...
> On Feb 16, 1:19 pm, "Rod Pemberton"
<do_not_h...@notemailnotz.cnm>
> wrote:

...

> > > Is there any problem with starting in assembly and
> > > calling C routines from there
>
> > This should be possible. I just don't fully know how to do so
> > yet with OW. I've seen examples of people linking their
> > own startup code to main() for other OSes. You might even find
> > a few examples on comp.lang.c or news.openwatcom.org. OW just
> > seems to be "difficult" in this regard. That may just be my
> > ignorance...
>
> On [o.u.c_cpp] as you suggested I got some help and can now
> link nasm with OW. As desired the nasm code is executed
> first and no C main function is needed. Instead, the assembly
> code supplies the start symbol.
>
> Here is how it is set up:
>
> The assembly code for an OMF (i.e. .obj) build includes
>
> ..start:
> mov ax, data ;Set up
> mov ds, ax ; the segment
> mov es, ax ; registers
> call far t6_func_ ;Call the C function
> section .data align=16
> db "!!AA"
> section .stack class=stack align=16
> resb 4096 ;Stack size
>
> There is no main function. The entire C code is
>
> int t6_func()
> {
> return 30;
> }
>
> The sources are built with
>
> nasm -fobj -O2 s6.nasm
> wcc t6.c -za -ml -wx -q -s -zls
> wlink sys dos name t6 file t6.obj, s6.obj

Ah, well, thanks for posting it.

I seems similar to what I posted, but has a stack section and
doesn't use main().

What specifically removes crt.o? ... the "sys dos" option? the
"..start"?

Or, what causes the NASM code to become the startup code?

What's up with the .data section, e.g., "!!AA"?

> For reference, the wcc compiler switches have these meanings.
>
> -za disable extensions (i.e., accept only ISO/ANSI C)
> -ml large memory model (large code/large data)
> -wx set warning level to maximum setting
> -q operate quietly
> -s remove stack overflow checks
> -zls remove automatically inserted symbols
>
> It was -s which removed the C code attempt to call __STK, and
> -zls stopped it looking for the _big_code_ label.

Ah, I was going to ask about why '-s' ... That seems like
something you'd want to keep, unless it includes I/O code.

'-q' is not supported on the version I use.

> > > ... - i.e. with no main()
> > > function in sight?
>
> > I'm not sure about not having a main()... IIRC, main() is the
> > official entry point for the C code per the C specifications.
> > I.e., non-standard at best to eliminate it.
>
> According to the Nasm manual the OMF standard is for execution
> to begin at a special label called ..start, as you know.

Nope, I didn't know that. I never read about OMF or ELF or much
about linking either... printf() is your "friend". When it
isn't, disassembly is, followed by the Internet... ;-)

Having not worked on my code for quite some time now, I assumed
"start" was an entry point, but I didn't assume that it replaced
crt.o. Most likely, I built it off of some example I found ...
If it does replace crt.o, then what I was working on might need
some changes.

> It seems to work without main and the
> C code looks correct without main.

That's good to know. It's another example to work off of.

> The C code for t6_func compiles to simply
>
> mov ax, 30
> retf
>
> Which is exactly what would be wanted.

Is it? If it returns, then it seems that "!!AA" is executed as
code. Do you need a "jmp $" after "call far t6_func_" above? Or,
perhaps, you want to jump to BIOS init? E.g., far jmp to
FFFF:0000h or F000:E05Bh ?

> The Nasm manual suggests the startup for .exe files as
>
> ..start:
> mov ax,data
> mov ds,ax
> mov ax,stack
> mov ss,ax
> mov sp,stacktop
>
> I found that under DOS I didn't need to set the stack,
> only the data segment.

I thought this was to be "standalone" code not DOS ... ?

BTW, both Nasm 0.98.39 and 2.10.01 complain that t6_func_ is
undefined. Is something missing? E.g., an "EXTERN t6_func_"
line? (Yes.)

So, with the EXTERN the example does do what you said, i.e.,
replace crt.o, except the resulting file is actually still an
.exe, not a .com. That means it has the DOS MZ header, so it's
not "standalone" yet. What are you planning to do to make it
standalone code? Unstub the DOS MZ header? Insert Multi-boot
header? Unstubbing the DOS MZ header results in a file that
starts with the "t6_func_" code, not the "..start" section code
... I.e., you'd have to find a way to locate "..start" and jump
to "..start". Could it be at a fixed offset? I'd guess that's
not the case since that code can increase in size. The example
you posted had no "section .code" directive. Maybe, that would
relocate the "..start" code prior to "t6_func_" code. That didn't
seem to work. Changing the order of the files to be linked works.
However, that inserts alot of space between the "..start" code and
"t6_func_" code.


Rod Pemberton





James Harris

unread,
Feb 19, 2013, 3:14:49 AM2/19/13
to
On Feb 18, 11:39 pm, "Rod Pemberton" <do_not_h...@notemailnotz.cnm>
wrote:

...

> What specifically removes crt.o?  ... the "sys dos" option?  the
> "..start"?

I don't know for sure that crt.o was present but the code that was
present tried to call DOS before the main function got control. It
would have failed on bare metal.

I think it was the "-s" that removed it. Perhaps an initial stack size
check.

> Or, what causes the NASM code to become the startup code?

The ..start label is the key. It is a special symbol used in the OMF
format. I guess that once the linker sees that special name in one OMF
object file then it knows to set that as the entry point.

In OMF the linker will accept only one global ..start symbol. AIUI if
the C code has a main function it too will supply one and prevent the
link process completing.

> What's up with the .data section, e.g., "!!AA"?

That was purely a marker that could be easily recognised in a hex dump
so I could see visually where the data section started.

> > For reference, the wcc compiler switches have these meanings.
>
> >   -za   disable extensions (i.e., accept only ISO/ANSI C)
> >   -ml   large memory model (large code/large data)
> >   -wx   set warning level to maximum setting
> >   -q    operate quietly
> >   -s    remove stack overflow checks
> >   -zls  remove automatically inserted symbols
>
> > It was -s which removed the C code attempt to call __STK, and
> > -zls stopped it looking for the _big_code_ label.
>
> Ah, I was going to ask about why '-s' ...  That seems like
> something you'd want to keep, unless it includes I/O code.

The stack checking code would not work without DOS but it is easy
enough to make a stack that is big enough.

> '-q' is not supported on the version I use.

It was only to cut down wcc's unneeded console output. You know how
wcc and wlink produce a bunch of lines each time they are run? Every
time it is called the version of wcc I have generates exactly the same
lines:

DOS/4GW Protected Mode Run-time Version 1.97
Copyright (c) Rational Systems, Inc. 1990-1994
Open Watcom C16 Optimizing Compiler Version 1.9
Portions Copyright (c) 1984-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
See http://www.openwatcom.org/ for details.

I have since removed "-q" in favour of a set of finds to get rid of
the above. So the batch file which carries out the 'make' process now
compiles like this:

call wcc t6.c -za -ml -wx -s -zls -ecc 2>&1 | ^
find /v "DOS/4GW Protected Mode Run-time" | ^
find /v "Copyright (c) Rational Systems" | ^
find /v "Open Watcom C16 Optimizing Compiler" | ^
find /v "Portions Copyright (c)" | ^
find /v "Source code is available under" | ^
find /v "See http://www.openwatcom.org/" | ^
find /v "Code size:"

I find it makes the compiler output easier on the eye and much easier
to see the genuine messages. YMMV.

...

> > The C code for t6_func compiles to simply
>
> >  mov ax, 30
> >  retf
>
> > Which is exactly what would be wanted.
>
> Is it?  If it returns, then it seems that "!!AA" is executed as
> code.  Do you need a "jmp $" after "call far t6_func_" above?  Or,
> perhaps, you want to jump to BIOS init?  E.g., far jmp to
> FFFF:0000h or F000:E05Bh ?

For this test I only needed to check that the asm called the C code
and that when the C code returned it did so to the same place and with
the stack pointer restored. What the code did after it got back was
irrelevant. I checked the location and that the stack pointer was
restored using debug as shown in the previous post. The !!AA was just
a marker so I could see where the linker had placed the data segment
in the executable file.

> > The Nasm manual suggests the startup for .exe files as
>
> >   ..start:
> >     mov ax,data
> >     mov ds,ax
> >     mov ax,stack
> >     mov ss,ax
> >     mov sp,stacktop
>
> > I found that under DOS I didn't need to set the stack,
> > only the data segment.
>
> I thought this was to be "standalone" code not DOS ... ?

Yes, it is to be run without DOS. It has to be started somehow. The
great thing about DOS is that I can use DOS (and its debug command) to
start and execute the code even before I have implemented an exe
loader. I can almost fully test it under DOS - which is very handy -
and should be able to have the same code then run without DOS, if you
see what I mean.

FWIW, to my surprise the Windows command prompt handles the int 0x10
(console output) calls I have tried. However, under DOS I can also run
int 0x13 calls.

The main thing is that the program doesn't depend on DOS either to
start it or to respond to it. It's OK for it to be started by DOS.
When I later load it myself I can put in place similar start
conditions: basically loaded code and a stack.

> BTW, both Nasm 0.98.39 and 2.10.01 complain that t6_func_ is
> undefined.  Is something missing?  E.g., an "EXTERN t6_func_"
> line?  (Yes.)

You are right. For brevity on Usenet I cut down the asm to what I
thought were the essentials but missed the extern.

There is a lot more I could say and am continuing to make progress
with this. Details are too lengthy to add here. If you like we could
carry on this discussion by email. Or we could start a separate
thread. I know Wolfgang, for one, would love to hear more about 16-bit
code. ;-)

> So, with the EXTERN the example does do what you said, i.e.,
> replace crt.o, except the resulting file is actually still an
> .exe, not a .com.  That means it has the DOS MZ header, so it's
> not "standalone" yet.  What are you planning to do to make it
> standalone code?  Unstub the DOS MZ header?  Insert Multi-boot
> header?

None of those extra steps are needed. An exe is fine for me. I can
load an exe file without needing DOS. Alexei showed how simple it was
to do in an earlier post in this thread. AIUI an exe can be suitably
large so there is plenty of space.

> Unstubbing the DOS MZ header results in a file that
> starts with the "t6_func_" code, not the "..start" section code
> ...  I.e., you'd have to find a way to locate "..start" and jump
> to "..start".  Could it be at a fixed offset?

I think the exe header has a field defining where the code starts -
see offsets 0x14 and 0x16. This is presumably where the location
of ..start gets placed.

> I'd guess that's
> not the case since that code can increase in size.  The example
> you posted had no "section .code" directive.  Maybe, that would
> relocate the "..start" code prior to "t6_func_" code.  That didn't
> seem to work.  Changing the order of the files to be linked works.
> However, that inserts alot of space between the "..start" code and
> "t6_func_" code.

Yes. I think it is to do with the stack definition. If I change the
stack size specification the exe file size changes too. Something to
come back to. I expect the stack can be defined in a better way.

James

Alexei A. Frounze

unread,
Feb 20, 2013, 10:05:14 AM2/20/13
to
> Seehttp://www.openwatcom.org/for details.
>
> I have since removed "-q" in favour of a set of finds to get rid of
> the above. So the batch file which carries out the 'make' process now
> compiles like this:
>
> call wcc t6.c -za -ml -wx -s -zls -ecc 2>&1 | ^
> find /v "DOS/4GW Protected Mode Run-time" | ^
> find /v "Copyright (c) Rational Systems" | ^
> find /v "Open Watcom C16 Optimizing Compiler" | ^
> find /v "Portions Copyright (c)" | ^
> find /v "Source code is available under" | ^
> find /v "Seehttp://www.openwatcom.org/" | ^
I've been meaning to write a lengthy reply, but haven't got around to
doing it.

So... Instead I'll point you again at 2 works of mine, where I think
most of your OW troubles have been resolved:

http://alexfru.narod.ru/os/c16/c16.html
In particular, in C16SDKWatcom.zip from that page you will find a tiny
example of how to make a .COM and an .EXE program without all the
nonsensical OW/DOS code prior to main(). See the start-up code
replacement file, cstart.asm.

http://alexfru.narod.ru/os/fat/fat.html
In Loader-2006-12-03.zip from that page you will find a much bigger
project, FATCMD, with a basic standard library (with file support on
FAT), stack, heap and I don't remember what else it's got. It's
runnable from DOS and directly from my bootsector, the same .EXE.
The start-up replacement code, cstart.wsm, is now more complex to
accommodate DOS, BIOS, stack and heap. I don't recall all details, but
you should figure out how the latter two work. Look up variables
'_stklen' and `_heapbase`.

If you get lost there, ask questions.

Alex
0 new messages