Hello everyone,
I've looked on the forum to an answer to the following question and found nothing. Perhaps I've stated the question improperly. If so, my apologies.
Anyway, here's the question. If I want to call a c function such as printf, etc. in my dos program, how do I do it? Can I do it? I've played around with crt_printf and not had any success.
These functions are easily called in 32 bit MASM, of course, but they must also be callable from 16 bit code too.
I think...
Thanks,
Mark Allyn
You should be able to easily call a 16-bit C library, as was done here:
http://www.masm32.com/board/index.php?topic=10262.msg75164#msg75164
Hello MichaelW,
Thanks for responding and providing some help. In the code you sent you seem to be linking against a 16 bit c library, if I read your .bat file correctly. Is there a corresponding 16 bit c library as part of MASM32? If not, can you suggest where to obtain one?
Second question raised by your response concerns /coff versus /omf files. Out of naivete I have been assuming that I must use /omf switch to get 16 bit code. You don't do this in the .bat file. Am I wrong about /omf as a requirement for 16 bit code?
Perhaps I only need /omf when generating a .com?
Thanks for your help.
Regards,
Mark Allyn
he uses the 16-bit linker :U
COFF nor OMF apply to 16-bit files - they are MZ .EXE or .COM
as for getting the 16-bit files - i am guessing an old C-compiler
Earlier versions of ML output OMF .obj files by default, these are the only ones the 16-bit linker recognises.
Later versions output COFF .obj files by default, so using the appropriate switch won't hurt any.
Even ml10 will output a proper 16-bit object file. Backwards compatibility, all the way to DOS1.
Dave, COFF is restricted to 32/64-bit object files, 16-bit must be OMF for the linker to know.
Hi Mark,
A C library should come with a C compiler. If you do not
already have a compiler, you can get one from the Open
Watcom project or Borland's C compiler was released as a
free download. And there are a number of free compilers
as well, but they may not have the same knowledge base
of users. Using Google, I see Digital Mars and EMX support
DOS as well.
Regards,
Steve N.
Hello MichaelW, Dave, Sinsi, and Steve N.
The MASM 6.1 Programmers Guide is very unclear about what switches need to be set under what circumstances, near as I can tell. From other forum discussions in which Sinsi made contributions, I had picked up the notion that no matter what .MODEL was being specified, in order to get link16 to function and produce EITHER a .com or .exe I had to specify a /omf switch. This conclusion seemed to be consistent with the other conclusion I had drawn that /COFF files were required in order to get the PE header stuff correct. And probably other stuff too.
I broke down and followed Dave's suggestion and bought a used copy of Borland C++ v. 5.02 for 49 bucks. I can only hope that 1. My wife will forgive me, and 2. it works.
Regards,
Mark Allyn
well - i didn't suggest borland - lol
i was thinking an older free ms compiler, actually
if i dig out my old stuff, i have msc version 6 someplace
a good google session would probably have yielded the results you needed
and - before you go spending money, i would have suggested 32-bit code
as for OMF - yes - i forgot that is the OBJ format
version 5.5 is free, and legal
http://edn.embarcadero.com/article/20633
Hello everyone, especially MichaelW,
I copied the code that MichaelW directed me to (see above response from MichaelW). I copied verbatim and attempted to assemble it using his command with exactly the same \c switch. Unfortunately, I get error messages from ML. First, it reports that in his line 36 there is an unresolved symbol: DGROUP. Then there are three symbol type conflicts all involving the printf command. Any idea what might be going on?
Regards,
Mark Allyn
see if you can get an "empty" program to assemble
.MODEL Small
.DATA
;
;
;
.CODE
ASSUME DS:DGROUP
_main PROC FAR
mov ax,@DATA ;@DATA is an alias for DGROUP
mov ds,ax
;
;
;
mov ax,4C00h
int 21h
_main ENDP
END _main
EDIT - added .DATA section :P
Although I have multiple C/C++ compilers from the DOS era, I used Turbo C++ 2.01 because it was (and apparently still is here (http://cc.embarcadero.com/item/25636), but now you must register to download it). The unfortunate thing about Turbo C++ 2.01 CRT is that the printf function does not support floating-point numbers.
Hello Dedndave,
I did as you suggested and got back "undefined symbol : DGROUP" at lines 8 and 12. Thanks for your help and also thanks as well to MichaelW.
I copied your code exactly and use ml /c dedndaveccall.asm as the command.
Regards,
Mark Allyn
Further to Dedndave:
An addendum to my previous post: If I assemble with ml /c /omf /Fl dedndaveccall.asm the assembler does its thing without complaint. If I then do link16 and create an .exe file, the .exe runs.
I thought you'd find this interessin...
Regards,
Mark
here is the batch file i use for 16-bit EXE's (a16.bat)
@echo off
if "x%1"=="x" goto ascusage
if exist %1.asm goto ascasm
:ascusage
echo Usage: asc asmfile
echo "asmfile" = asmfile.asm
goto batchexit
:ascasm
if exist %1.obj del %1.obj
c:\masm32\bin\ml /c /Fl %1.asm >c:\masm32\bin\asmbl.txt
if errorlevel 1 goto showtxt
if exist %1.exe del %1.exe
c:\masm32\bin\Link563 %1.obj; >>c:\masm32\bin\asmbl.txt
:showtxt
if exist %1.obj del %1.obj
type c:\masm32\bin\asmbl.txt
:batchexit
dir %1.*
the masm version is 6.15, although 6.14 and others should work
the /Fl switch creates a listing, and is optional
Link563.exe may be named Link16.exe - if so, modify the batch file accordingly
the batch file is in C:\masm32\bin, which is in the PATH environment variable
i added some test code to the program to display some text
Hi Dedndave
First, thanks for the code and for thinking about this problem. This is an interim report from me. More later.
I copied your modified ("hello") program. I am using v. 6.15 too. Also, my 16-bit linker is link16 (which I believe is a renamed version of link563), the same link16 as shows up in the bin directory for MASM32. As far as I can tell you and I are using exactly the same ml and link commands.
So, coming back to your modified program. When I do ml /c davetest16.asm, I get an error saying "undefined symbol : DGROUP". However, if I write ml /c /omf /Fl davetest16.asm, the assembler is happy. In addition, when I do link16 davetest16.obj, davetest16.exe, the linker is happy too and the code runs and says "hello" just as you would expect. So I am deeply puzzled. I can't see why you wouldn't also get the same error message.
What I haven't done yet is to use your .bat file. I can't see why that should make any difference, but I don't know what else to try.
Again,
Thanks
Mark Allyn
well - it should not hurt anything to use the /omf switch
that is the OBJ file format you want, anyways
however, it makes no sense that it is behaving differently on your machine than it is here
perhaps you have an "ML" environment variable that specifies a set of default switches for masm
to find out, open a console window and type....
set ml
and press enter
if the variable is present, it will tell you the value
if not, it will say something like "variable not defined"
if the coff switch appears - there is your answer :bg
your C compiler installer may have set this variable, so not it may not be a good idea to remove it
there are other ways to work around this problem, like using a batch file SETLOCAL command
adding the following line to the beginning of your 16-bit bath file should disable it
setlocal ml=""
SETLOCAL should affect only the ML variable used during batch execution
(or you can just use /omf for 16-bit batch files - lol)
Hi Dedndave,
I checked my environment variables and ml is not defined or given a value. So... but it was an interesting idea you had and one that I would never have come up with.
I don't mind setting the /omf switch routinely, at any rate, and it solves the problem even though it doesn't explain the problem. I guess I am just compulsive enough to want to know how these things work.
Back to the problem that got us here, namely how to use the C library off Dos. I found a c library in an old Watcom compiler package I hadn't used for a long time. Anyway, when I try to link16 my DOS caller to the library I get a bunch of error messages to the effect that _TEXT : segment size exceeds 64K. I get one of these for each of the c library functions in the file.
Any idea what might be wrong?
Regards,
Mark Allyn
i can't explain that one
the code segment in the asm program may not be named _TEXT, as it was in the old days
if that's the case, do not use ".CODE"
use something like this...
_TEXT SEGMENT WORD PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:DGROUP
;code goes here
_TEXT ENDS
maybe you can send me a library to try
I didn't spend a lot of time on it, but all of my attempts to link with the Open Watcom 1.9 libraries failed, either with an unresolved external error, or if I got past that:
fatal error L1123: _DATA : segment defined both 16- and 32-bit
I tried multiple different libraries, for example those in C:\WATCOM\LIB386\DOS and C:\WATCOM\LIB386\WIN (AFAIK these are Win16 libraries). I have doubts that the libraries are compatible with the Microsoft tools.
Hi DednDAve and MichaelW
I tried 'Dave's suggestion, but got the same error message after swapping out _CODE with _TEXT. Namely, _TEXT : segment size exceeds 64K.
I'm basically where MichaelW is on Watcom. Watcom's documentation on its libraries is pretty good. They created different libs for c functions that depended on the model size specified in the source code. In my case they are all located in c:\watcom\lib286\dos. Watcom named its files clibs.lib, clibm.lib, and clibl.lib. Following MichaelW's suggestion my caller is defined as .MODEL large, c and I have been trying to link with Watcom's clibl.lib.
DednDave: I will send you a copy of clibl.lib later today.
Regards,
Mark
ok - i would have started with a small model program
small - all code fits into a single segment, all data fits into a single segment
for these trivials, large is a bit much - lol
large indicates multiple code and multiple data segments
as i recall, medium indicates a single code segment and multiple data segments
the error sounds something more like some kind of syntax problem
you are somehow not meeting the requirements of the LIB and/or LINK
or, perhaps, you are calling every c function in the lib - lol
Well, I didn't try the libraries in \LIB286\DOS (a 286 is not sexy enough for a young man like me). But now that I have, with a little creative guesswork I was able to get CLIBL.LIB to work. The SYSCALL for the langtype field in the PROTO and PROC directives is to eliminate the leading underscore, and because it also eliminates caller stack cleanup I added a test to verify that the called function is removing the arguments from the stack. And "printf_" would link OK, but it would not display anything. And for the commented statement, I don't know whether the problem is a bad format string or no printf support for floats (tried REAL4 and REAL8).
Edit: Just looked at the listing, and ML is adding code to remove the arguments from the stack, so my impression that ML does this only for the C langtype was wrong. And the library is following the C calling convention, but not the naming convention. And if instead of invoke you push the arguments and do a call, you will need to add code to remove the arguments from the stack on return.
;====================================================================
.model large, c
.386
;====================================================================
cprintf_ PROTO FAR SYSCALL :VARARG
rand_ PROTO FAR SYSCALL
getch_ PROTO FAR SYSCALL
;====================================================================
.stack
.data
fmtd db "%d%c",0
fmth db "%xh%c",0
fmtf db "%f%c",0
r8 REAL8 123.456
.code
;====================================================================
;-------------------------------------------------------------
; This dummy procedure prevents the linker from returning an
; "unresolved external" error. Something in the library code
; is apparently referencing this name, and while the dummy
; procedure will satisfy the linker, if the library were to
; actually call the procedure it would probably fail.
;-------------------------------------------------------------
main_ proc far SYSCALL
ret
main_ endp
.startup
;====================================================================
;----------------------------------------------------------------
; This code is to verify the the library functions do not expect
; caller stack cleanup, as per the C calling convention
;----------------------------------------------------------------
mov dx, sp
invoke cprintf_, ADDR fmtd, dx, 10
mov dx, sp
invoke cprintf_, ADDR fmtd, dx, 10
REPEAT 10
invoke rand_
invoke cprintf_, ADDR fmtd, ax, 10
ENDM
invoke cprintf_, ADDR fmth, 1234h, 10
mov ax, 1234h
invoke cprintf_, ADDR fmth, ax, 10
;invoke cprintf_, ADDR fmtf, r8, 10
invoke getch_
;====================================================================
.exit
end
And I could not get CLIBS.LIB to work even though I changed the model directive to match, the display was corrupted.
Hi,
Not sure this is relevant, but some libraries need to be
initialized before using certain functions. The Microsoft
FORTRAN library needed a start up call of some sort.
That was a somewhat unsuccessful attempt to use
the FPU emulation way back when. You might look at
an assembly listing of a C program to see if there is
something like that required for the Watcom library?
HTH,
Steve N.
i gave a quick browse to the watcom documentation
there appears to be no general init function
however, there are a few specialized init functions for specific things like graphics, allocation, etc
i did find several DOS/ASM source files
http://www.openwatcom.org/index.php/Resources#ASM.2FDOS_source
i would think something in there should get you up and running
1) make sure you are using the correct library for 16-bit DOS
2) the segment order may be a problem - refer to the docs
because you are not using the watcom assembler and linker, you may have to explicitly
open and close the segments in an asm inc file so that the segments are ordered correctly
i can see how this might cause the "_TEXT segment exceeds 64 kb" error
a good reason to start with the small model :bg
http://www.openwatcom.org/index.php/Manuals
the Programmer's Guide and Tools User's Guide look like they contain a lot of good info :P
one more thought....
if i was in this predicament, i might compile a small dos program using watcom's c-compiler
then, tear it apart with exemod, exehdr, debug to see how it went together :bg
i am not a c programmer, nor do i want to become one - lol
but, if you send me a small 16-bit dos exe, i can take it apart :P
Hello SteveN, MichaelW, and DednDave,
While you folks were sending me your good ideas, I threw up my hands on Watcom and went off and downloaded some very old code written in the early 90's by Randall Hyde. I suppose you probably know that Randall H. and UCR developed a library of c-like calls coded in asm and distributed as UCR STDLIB. They had built a library and also included in their distribution the source files. Many familiar faces such as the astoundingly popular printf, but also malloc and others. I could not get my asm source to link to their library. However, I was able to assemble and link to individual obj files built off their source code. The code runs fine. So, in principle it looks like I could build a static lib off their sources. BTW, in order to get my source to link successfully I had to follow MichalW's SYSCALL langtype in the PROTO for the library procs.
Of course, the UCR/Hyde sources are not ANSI C library code. But, it looks like for quick and dirty console I/O from DOS code compiled with ML (and of course the /omf switch) and linked from link16 (AKA link563), the UCR library will work.
I am hoping to receive tomorrow the TURBO C compiler (49 bucks! I didn't get Redskull's note in time...) and arrive at a more elegant solution.
I will also follow up on your recent postings.
Thanks
Mark Allyn
we are familiar with Randy's libraries
and - if you look through the 16-bit forum, you will find many working examples
i am not sure that turbo-c 5.5 will create dos exe's - well, it does not specifically say it will
i did find an older version of turbo-c 2.01 that looks promising, however
http://cc.embarcadero.com/item/25636
A workaround for your problem. (http://www.masm32.com/board/index.php?topic=12724.msg98169#msg98169)
Hi JJ, Dedndave, and Michael W.
I figured you folks were well acquainted with Randy Hyde, but not having seen his name mentioned here I wanted to at least indicate where I got the STDLI B stuff from. And perhaps not everyone reading this thread knows Hyde's work...
I haven't tried out MichaelW's new code yet. But, I did download the TurboC 2.01 files as Dave suggested. As far as trying to understand what WATCOM is doing by writing a small program, I did that. Their makefiles are written in a peculiar syntax and I can't see how they are actually linking in their c libs. Dedndave's idea of tearing the thing apart is interesting but I think I'll try to make Turbo C work first.
I looked at JJ's code. It was highly interesting. I stumbled for a while on what exactly was going on in the line where he loads and unloads the function--couldn't understand what the db instruction was until I reallized he had actually inserted the opcode string! Very cool. Because I'm pretty naive to assembly I had never seen that done.
As for what JJ's workaround does, I'm not sure it does what I'm trying to do. All I was trying to do was simply call ANSI c functions from a DOS .exe. JJ's workaround seems to load and unload 32 bit Windows API's from a DOS .exe. Maybe I'm missing something here.. I guess what he's driving at is that I could load and unload 32 bit C functions from msvcrt.dll/lib. Am I right?
I certainly appreciate all the help you've given me.
Regards to all,
Mark Allyn
well - that is a strange workaround - lol
but it can be done that way
it doen't really accomplish what you are trying to do, though
which is to simply call functions in a 16-bit c library from an asm program
a much better soultion, after all, is to just write 32-bit code :P
Quote from: allynm on April 17, 2011, 10:35:17 PMJJ's workaround seems to load and unload 32 bit Windows API's from a DOS .exe.
Mark,
Yes, it does use 32 bit Windows - from a 120 byte
COM program! I attach a complete example, which works fine on Win XP SP2, adapted from Arkon's code (http://www.ragestorm.net/sample?id=94).
And I agree with DednDave - write 32-bit code, it's much easier :bg
the way those examples are written, i see no advantage in using that method
again, you may as well write a 32-bit program
the same thing could be accomplished by using the INT 21h Exec function to execute a 32-bit program :P
and, what is the advantage in that, anyways ? - lol
Hello Dedndave, MichaelW, and JJ,
OK. So, I got a copy of Borland 2.01 (thanks to Dave), and wrote a small program that pretty much duplicates what MichaleW sent early on in this thread. I just simplified slightly what MichaelW created.
Here it is.
; BOR201.asm
.model large, c
.386
includelib c:\tc\lib\cl.lib
printf PROTO C :VARARG
.stack
.data
fmt1 db "%d", 0
val word 23
.code
main PROC
ret
main ENDP
.startup
invoke printf, ADDR fmt1, val
ret
.exit
END
If I assemble the code with ml /c /omf bor201.asm it assembles. If I link with ..\bin\link16 /map c:\TC\lib\c0l.obj bor201.obj, bor201.exe the link fails with an error message stating that I have too many segments. I haven't seen this message before, and I don't know how to interpret it or fix the problem.
Any suggestions, folks?
Regards,
Mark
JJ and Dedndave,
This is a separate response to your posts regarding JJ's workaround. I agree with both of you that the easiest thing to do is simply write the whole thing in 32 bit form and get on with life. No question about it.
If you don't mind just a bit of personal information to help clarify: I come very late in life to computer programming, especially with microsoft. Many years ago I played around programming MACs in ThinkC, but was interrupted by other life events. I've retired recently and have the time to go back and learn stuff you folks learned long ago---such as, for example, DOS and assembly language. So.... when JJ posted what he posted his contribution was really exceptionally educational and I was fascinated by the ingenuity of the code. Its really great to be able to participate on a site like this where you folks are so willing to share what you have learned by dint of great effort over a long time. Believe me, whatever you post I read carefully and I learn.
Thanks for your patience
Mark Allyn
attach the 2 OBJ files :U
you can peek inside them with a binary editor to see how the segments are named and ordered
for MZ EXE's, the segment names disappear once it has been converted to an EXE
also - you may want to browse the compiler documentation
it should tell you how the segments are created
oh - and....
make sure you use the memory model in the asm file that matches the lib
again - i would start with SMALL !!!
once you get that working, you can try medium or large
if you can write a C program and compile it.....
use a simple C function, like output a single char - not printf
use SMALL model
attach the resulting exe
Quote from: allynm on April 18, 2011, 05:02:01 PMSo.... when JJ posted what he posted his contribution was really exceptionally educational and I was fascinated by the ingenuity of the code.
Mark,
I would feel really flattered if it was
my code :bg
... but I am afraid credits must go to Gil Dabah aka Arkon (http://www.ragestorm.net/contact?user=Arkon)
QuoteIf I link with ..\bin\link16 /map c:\TC\lib\c0l.obj bor201.obj, bor201.exe the link fails
The object module that contains the program entry point needs to be the first one listed, so the order needs to be:
bor201.obj c0l.obj
BTW, for the Watcom library I seem to have the simple stuff working. But so far I have not found any way to enable floating-point support, I cannot get the memory allocation functions to work, and my qsort test hangs. I'll post what I have working later.
Michael,
have a look at the docs (i posted a link above)
there are certain function groups that require initialization
i am sure f.p. is one of them
Mark,
if you do compile the simple program i mentioned above, please include the OBJ, as well as the EXE, if possible
DednDave and MichaelW,
I know I haven't kept up with Dave's suggestions, for which I apologize. I read MichaelW's post and realized where I had gone wrong on the command line, and I made the changes on the link16 order in which files were linked. And the good news is that the program did "successfully" link in the sense that no errors were generated. Moreover, when called, the program does not generate runtime errors either. HOWEVER, it also doesn't output the simple string that I give for printf to print. It skips a line and comes back with a dos prompt.
;bor201.asm
.model large, c
.386
printf PROTO C :VARARG
.stack
.data
fmt2 db "%s", 0
msg db "Hiya, you persistent fool", 0
.code
main PROC
ret
main ENDP
.startup
invoke printf, ADDR fmt2, ADDR msg
ret
.exit
END
I'm probably missing something obvious here, and will ultimately feel stupid, but I can't see what it is. DednDave, I apologize for not pursuing your suggestions yet, but I really thought that we had finally nailed this thing by getting the link16 command properly called.
Also, I looked at Borland 5.0 and it does have the necessary DOS library in it. I have however been using Borland 2.01 for this work.
Regards,
Mark
Hi DednDave, MichaelW, and JJ--
Well, I found my own bug and it was as I thought it would be staring me right in the face. It was the ret instruction at the end of the code. I just threw it in by habit and it caused the woes...
Here is the code now:
.model large, c
.386
printf PROTO C :VARARG
fputchar PROTO C :WORD
.stack
.data
fmt2 db "%s", 0
msg db "Hiya, you persistent fool", 0
.code
main PROC
ret
main ENDP
.startup
invoke printf, ADDR fmt2, ADDR msg
xor ax, ax
mov ax, 'k'
invoke fputchar, ax
.exit
END
For what its worth, I threw in a call to putchar and discovered that in the cl.lib for Borland, putchar doesn't exist. But, fputchar does, and it works just like putchar. Wonder why they wandered off the ANSI ranch...
Regards to all of you and thanks for your very helpful and informative input.
I am going to follow up on DednDave's suggestions .
Regards, Mark
Hi MichaelW,
I haven't caught up yet with your work on Watcom. I will do that shortly. Thanks for continuing to work on this problem.
Mark
I finally overcame all but one of the problems, namely the "Floating-point support not loaded" error when I attempt to display a floating-point value with one of the printf functions. It turns out that normally the Watcom linker links in this support when it detects a reference to a fltused_ symbol that the compiler places in any module that requires floating-point support. After spending hours trying to find a procedure that would provide this support, I decided that I had already spent far too much time on this detail, considering that the missing support apparently affects only the display of a floating-point value with one of the printf functions, and gave up on it. As a workaround, that is included in the test, you can simply convert the value to a string and then display it as such. The Watcom libraries that I used were:
WATCOM\LIB286\NOEMU87.LIB
WATCOM\LIB286\MATH87S.LIB
WATCOM\LIB286\DOS\CLIBS.LIB