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