Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C language courses for corporations. He is the author of C Language for Programmers and All On C, and is a member on the ANSI C committee. He also does custom C programming for communications, graphics, and image databases. His address is 4201 University Dr., Suite 102, Durham, NC 27707.
You may fax questions for Ken to (919) 493-4390. When you hear the answering message, press the * button on your telephone. Ken also receives email at kpugh@dukeac.ac.duke.edu (Internet) or dukeac!kpugh (UUCP).
Q
I am writing a program using Microsoft C v5.1 that connects to a mainframe session to execute an on-line mainframe application. I used the High Level Language Application Programming Interface (HLLAPI) to do the terminal emulation.
One of the options that I am trying to incorporate into this program is the ability to toggle a debug switch on or off that will print certain information to an audit file. My intention is to use a function key for this purpose. When I send a string of keystrokes to the mainframe session using HLLAPI, these characters are placed in the keyboard buffer. If I press a function key, this is also placed in the keyboard buffer and sent with the other characters to the mainframe session, thus messing up the mainframe command in addition to losing the function key before the program finally gets around to testing to see if the function key has been pressed.
In IBM Compiler BASIC, the ON KEY (x) command would handle this situation, presumably by doing an interrupt whenever the appropriate function key is pressed. I have not been able to find a similar prewritten function in C. Therefore, I have been attempting to learn how to code an interrupt, but have not been able to find a really good book that explains them well. From the bits and pieces I have been able to find, I think that I need to change interrupt 9 or 15 or 16, but can't figure out which one or how. What I need is an example of how to code this interrupt and a good book on the subject (one that uses C code for examples, not assembler, if possible).
Mike Drew
Woodridge, ILA
You could write a TSR that intercepts the keyboard interrupt, tests the key, and either sets a debug flag or passes it on. The Blaise toolkit provides an easy way of creating a TSR, although there is a bit more overhead than what you require for your one operation. That's the simple approach, with no assembler required.
Microsoft provides an "interrupt" modifier for a function type which allows it to be a handler for an interrupt. It receives as parameters the values of the register when the interrupt was called. To handle an interrupt you:
1. create a function to perform the desired service with the interrupt type modifier.
2. call_dos_getvect() to get the current interrupt handler's address
3. call_dos_setvect() to set the interrupt location to your function's address
4. call_chain_intr() to chain to the previous interrupt's address.
For your keyboard routine, you want to connect to the key I/O interrupt 0x16. The characters typed on the keyboard are handled by the keyboard interrupt routines (0x9) and are placed in a circular buffer. When the key I/O routine is called by a user program (such as by kbhit() or getch()), it checks the keyboard buffer for the next character to return.
Using far pointers, your function can look at this circular buffer and eliminate your hot key, if it is in there. The offset of the head of the buffer is at 0040:001A, the tail offset is at 0040:001C, and the offset of the end is at 0040:001C. The offsets are relative to segment 0040. Note that these addresses are only good for IBM-PC compatibles. The IBM Technical Reference Manual gives the full information, but unfortunately it is documented in comments to assembly code.
Q
I am currently using Turbo C v2.0 with MS-DOS v2.2 on my Zenith PC. However, I also own an older KayPro portable equipped with a Z-80 processor and running CPM 2.2. At the present time finances preclude replacing the KayPro with an MS-DOS battery powered laptop.
As a result, I am searching for a CPM/Z-80 C compiler for the KayPro, that would allow me to develop C code on the PC and then recompile and run it on the KayPro. In addition I am not willing to pay a large price for the CPM compiler because of its memory and speed limits. If you have information regarding CPM/Z-80 C compilers, I would appreciate your response.
Ronald L. Nave
Horsham, PAA
I used to use the Manx Aztec Compiler to perform exactly what you are talking about. In addition, I also ported the C code from the IBM-PC to an Apple II. Although you could use the latest version of the compiler on the PC, it might be better to use an old version that matches the version available for CP/M. (None of the recent ANSI features are available in the CP/M version.) If you use the older version on both machines, you will not be tempted to use some of the more modern features, such as structure assignment. This version may be available from Manx or on the used compiler market.
Q
We are using Microsoft C v5.0. We would like to define a manifest constant to be used both as a number and as part of a message string. For example:
#define Max 10 extern short Value; extern DoMsg(char *Msg) if (Value > Max) DoMsg(<what goes here?>);One way to do this is to itoa() the constant and assemble the message (at runtime), (or printf could be made to do this), but we thought we could get the preprocessor to do this sort of thing. All our attempts pasting, stringizing, etc. have been foiled. For example, use of the stringizer, "#", suppresses macro expansion, so the constant comes through unsubstituted. If we set up a pasting macro, there's no way (that we could find) to paste quotes onto something. The point of all this is to be able to have just one manifest constant to change if the value changes, and have that apply to both value uses and string uses within the module. Help!Josh Cohen
Stuart Downing
Dexter, MI 48130A
You got me on this one. I had assumed that the following would have worked, as you tried, till I read the ANSI standard again.
#define MAX 10 #define STRING_WITH_VALUE(X)"Error message " #X DoMsg(STRING_WITH_VALUE(MAX))The # operator is a new ANSI preprocessor operator that only works as part of a #define with tokens (a function-like macro). It is a "stringizing" operator. It forms a string literal (a set of characters in quotes) from the token that follows. However the token is not processed for replacement. Thus the output of the preprocessor looks like:
DoMsg("Error message " "MAX")and not
DoMsg("Error message " "10")In the first case, the implicit concatenation of string literals in ANSI C yields the following:
DoMsg("Error message MAX")If the output of the preprocessor had been the second case, then you would have gotten exactly what you wanted.Replacement of #define names is not supposed to occur within a string literal. The operation of the # operator appears to be consistent with that philosophy.
Perhaps a reader can solve this problem for us. A solution would certainly be useful. For example, I have been meaning to rewrite quite a bit of code to look something like:
#define WFIRST_NAME 20 #define WLAST_NAME 30 struct s_record { char first_name[WFIRST_NAME]; char last_name[WLAST_NAME]; ... }; struct s_record record; ... #define quote(x) #x printf("\n Record is %" quote(WFIRST_NAME) "s %" quote (WSECOND_NAME) "s", record.first_name, record.last_name);which would yield (if it worked):
printf("\n Record is %20s %30s", record.first_name, record.last_name);One alternative is:
#define MAX_BUFFER 200 /* Or whatever */ char DoMsgBuffer[MAX_BUFFER];/* Local or Global buffer */ #define DoMsgInt(string, integer) \ { \ sscanf(DoMsgBuffer, "%s %d", string, integer); \ DoMsg(DoMsgBuffer); \ }Now instead of calling DoMsg directly, you call;
DoMsgInt("Error message", MAX);This at least eliminates having to change two constants.An uglier alternative uses a separate quote constant with lots of comments about changing both.
#define MAX 10 /* If you change this, change S_MAX to match - or else */ #define S_MAX "10" DoMsg("Error Message" S_MAX);QThis month's C Users Journal (Nov. 89) contains an article by P.J. Plauger describing the print facilities in Standard C. His review of these commands brings back a gripe I have long held about C, which I wish to raise.
I was brought up as a Fortran programmer, and was weaned on its FORMAT statements. Say what you like, Fortran allows a form which is much missed by those of us who have to format complicated output, and that is the "repeat" format construct. Thus in Fortran, a format like 10I3 would replace a lot of writing in Standard C. Even more useful are statements like 4(3I2,2X,4F6.4), which formats 28 variables in 15 characters, and even adds blank spaces where desired.
I am well aware that no two languages are equivalent, but it would seem to me that somebody by now has worked out a scheme to allow multiple format statements of similar types to be written with less difficulty than Standard C allows. What do the gurus say to this?
David Tal
Haifa, IsraelA
I am an ancient Fortran programmer also, or should I say, one who used Fortran in relative antiquity. One of the least desirable aspects of the FORMAT statements was having to count the number of characters (e.g. 8HVALUE IS) for character output. Because this was the syntax for characters, the leading digits on a format specifier (I, X, F) could be used as a repetition count.
Where the language does not feature a particular item you are interested in, you can always come up with your own version.
With the printf format specifier, everything that is not preceded by a % is output as a literal character. You might come up with a scheme such as: "%r%Z" where r is a repetition count (an integer) for the next format specifier. You might even use "%r (x)" where the x represents a string that will be repeated r number of times.
All you need to do is write a routine something like:
char *repeat_format(format) char *format;which you would use in a printf such as:
printf(repeat_format("%5%3d %2(%d ABC)",a,b,c,d,e,f,g,h);and which would print the equivalent of:
printf("%3%3d%3d%3d%3d %d ABC %d ABC",a,b,c,d,e,f,g,h);The routine might be easier if you always required the parenthesis. Otherwise it will have to determine where the end of a simple format specifier is. The function would need an internal buffer, whose address it could return.
Reader Requests
I have a question which concerns especially the IBM PC but maybe you know a solution for my problem.In writing a special data transfer program for an IBM PS and some telecommunications equipment I encountered the following task: Is there any way to determine whether there is a mouse connected to a specific serial COM port? Of course I know how to detect the presence of the mouse driver (Int 33h, function 0). But this doesn't tell me which port is used!
Thank you, and I hope to receive a reply soon.
Michael Wiedmann
West GermanyThat is an interesting problem. The mouse driver that I use does not seem to check for presence of a mouse on the COM port. It assumes that one is there and displays a random position. Anyone have an answer? (KP)
I am a new subscriber to The C Users Journal. I found the article "Pointer Arithmetic at Memory Segment Boundaries" by D. and N. Saks (Vol.7 #7 pp. 27) very informative.
I'm interested in writing applications in C that use expanded memory to store variables and data. I'm also interested in finding information regarding writing applications in C that can run in expanded memory. Can you direct me to sources of such information?
Thank you for your time and continued success with The C Users Journal.
Phil Pistone
Chicago, ILThe Intel/Lotus specifications give the full details on the interface (KP). Anyone have suggestions as to a readable version?
(Ed. Note: You might find what you're after in two articles, both in old Microsoft Systems Journals: "Expanded Memory: Writing Programs That Break the 640K Barrier", M. Hansen, Krueger, B., and Stuecklen, N., March 1987, p. 21; and "Extended Memory Specification 2.x: Taking Advantage of the 80286 Protected Mode", Chip Anderson, July 1989, p. 17.rlw)
Reader Responses:
Here is some information for Jeff Saraiva's questions concerning graphics and the MSC graphics library that was published in your November issue. Sample Programs:I have not seen any extensive programs that use the MSC graphics library. Two good books that I have used are: Advanced Graphics in C and Graphics Programming in C. The authors develop their own graphics library, but the concepts are the same and it should be an easy matter of replacing calls to these libraries to the MSC graphics library routines.
UNIX Tests:
You pretty much named the worthwhile ones.
TIFF Information:
The May 1988 issue of Dr. Dobb's Journal has an excellent article about TIFF starting on page 26. The article has an address to which you can write for the source code and documentation to a TIFF library that reads and writes TIFF files. The code and documentation is free. There are two libraries; one for the MAC and one for the PC. I have used both of these and they work very well. The documentation is very complete.
Ray-tracing Algorithms:
Sorry, can't help you on this one.
Joseph K. Vossen
Duluth, GAPrintscreen:
In your "Q?/A!" column in The C Users Journal, v7n8, you suggested that Roger Glocke kill the "printscreen" key by replacing the vector for INT 5. That is the hard way, and (as you point out) can lead to trouble.
The easy way is to set PrtScr's data byte (at address 0050:0000) to "busy" so the ROM BIOS interrupt handler will ignore the key stroke. The attached listing (Listing 1) illustrates the technique with a small stand-alone utility to set the byte or restore it to "not busy." To use the technique embedded in another program: Initialize PrtScr as a static far pointer as shown, assign -1 to the data byte (*PrtScr) at the beginning of main(), and assign 0 to it before exit (it is safest to use a local function called by atexit() for this).
Turbo C has the MK_FP macro (in dos. h), which could have been used to initialize the far pointer in line 12. However, Mr. Glocke said he uses Microsoft C, which does not provide this macro. The construction shown should work with Microsoft C.
Murray L. Lesser
Yorktown Heights, NYI had mixed emotions about seeing my letter in your column of the November issue of The C Users Journal that arrived just today.
I was pleased because I learned something from your very lucid discussion of the problem raised in my letter. I was a little embarrassed because within a week after I wrote the letter the problem was solved. A discussion with Fred Crigger of Watcom Products, Inc., revealed that the solution was to write some string functions that would accept far pointers to data items and return near pointers to data items, and vice versa.
I wrote the necessary functions, virtually all the string functions in the standard library, and made the appropriate changes in the program. As of this moment everything is working very well and I am rather proud of my efforts.
Incidentally, I have switched completely to Watcom C v7.0 and am delighted with it. It does compile slowly but yields .exe files that are consistently two-thirds the size of those produced by Microsoft. And, the people at Watcom have been very generous with their support.
I do appreciate both your going to the trouble to answer my letter and your column I am always better informed after reading it.
Fred C. McDaniel
Richardson, Texas