Columns


Questions & Answers

Modifying IBM Extended Characters

Ken Pugh


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 was a member on the ANSI C committee. He also does custom C programming for communications, graphics, image databases, and hypertext. His address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax questions for Ken to (919) 489-5239. Ken also receives email at kpugh@dukemvs.ac.duke.edu (Internet).

Q

I need help with the following problem. I want to be able to change the appearance, on screen, of one or more of the IBM extended characters (128 to 255). For instance, character number 157 is a symbol for the Japanese yen (x) and I would like to change it to a character which is half white/half black divided diagonally. I believe what I need is a character editor.

In fact, such a character editor (in BASIC) is described in a Tandy publication Graphics and Sound for the Tandy 1000s and PC Compatibles (pg. 164). This is just what I need but I need (want) to do it in C.

I am informed that the extended character set is located in its own area of memory. I know it's possible to copy that section of memory elsewhere and then change the pointers that pointed to the default set so that they now point to the new location. I then want to change character 157 so that when I press ALT-157 it will give me my new shape instead of the YEN symbol. I also want to save this area of memory to disk and bring it back when I need it.

In the book The New Peter Norton Guide to the IBM PC & PS/2 a description is given (pg. 187) on how to do this with ROM BIOS int 10h:

"...to define your own characters you need to use subservice 11H, as follows: Place a table of the bit patterns that define the characters in a buffer. Then call subservice 11H with the address of the table in ES:BP, the number of characters in CX, the ASCII code of the first character in the table in DX, and the number of bytes in each character's bit pattern in BH."

One of my main hang-ups is how to get the table address into ES:BP. My available literature doesn't tell me how. So how do you do it?

I also would appreciate a simple routine for changing the bit pattern of any particular character. Is there an article or book which explains this?

Jack Steiner
Cedar Grove, NJ

A

The routine you are describing is one that works only for EGA and newer video adapters. The original IBM Technical Reference manual only stated how to modify the extended characters (put the address of 1KB table — 128 * 8 bytes each — into location 0X7C) that were used when in graphics mode. Those graphic character patterns are actually in the processor memory. With the EGA, the character patterns you are referring to are stored on the adapter.

Figure 1 shows the interface stated by The EGA Technical Manual for changing the generator characters. The block to load can be 0 to 3, depending on how much storage your EGA card has. With a bit of programming, you can set up two blocks and have 512 different characters.

To load an entire table, set CX to 256, DX to 0, BH to 8 (or whatever the number of scan lines/characters for the screen mode you want to use), and ES:BP to point to the table. To load a single character, simply set CX to 1 and DX to the character value, BH to 8, and ES:BP to point to the table containing the single character.

I assume you are going to be using a compiler that has an int86x or similar function. This function sets up the register according to the values you pass it in a structure, calls the desired interrupt, and then returns the register values in another passed structure. Listing 1 gives an example.

I've used shifts and masks to get to the segment and offset portions of the address. You could use FP_OFF (address) and FP_SEG (address), if you are using Microsoft. Using the shift and mask is the most portable construction I know if you shift a lot between compilers and don't want to bother with making up a header file. Instead of including my custom header file that has its own definitions for segment_of and offset_of, I've written out the equivalent shift and mask operators. This is essentially what FP_OFF (address) and FP_SEG(address) do in Microsoft.

For the benefit of our readers who may want to investigate DOS interrupts, I've included an excerpt (Listing 2) from a quicky program that I wrote that allows you to read a particular cluster on the hard disk, using a DOS interrupt. If the directory structure on a disk is corrupt, you could read through the disk (ala Norton and other programs) and write the data out to the screen or another file. It assumes a particular cluster size and offset, (as befits a quicky program).

Q

Could you please answer me this question: may a strictly ANSI performing C compiler accept old style operators like =-? Actually, we have been searching several days for the reason why one of our programs produced slightly different results on an HP9000/700. Normally, we separate all ='s with spaces but there was one statement

x =-1.0;
(without any spaces!) which was compiled as X = X-1.0.

HP claims an ANSI compiler (for which you have to pay a lot of money to HP) can do so and the user has to separate = and - by spaces.

I would be glad if you would tell me your opinion as far as possible.

Dr.-Ing. Dieter Scharpf
Weinstadt-Endersbach, Germany

A

= - is not a valid ANSI operator. X= -1 consists of three tokens: x, =, and -1. Some compilers give warnings of X= -1 being obsolete, but they still interpret it as x = -1.

Q

I need to trap printer errors, like Out of paper, Printer taken off-line. I guess I need to write an interrupt handler of some sort. So far my searches for skeleton code have been fruitless. I know this has to have been written over and over.

Can you please point me in the direction of code samples, books, articles, anything to help me. This needs to work with simple fprintf statements, normally to PRN, running on DOS. I know what I want to write inside the error handler, I just need to know what to write to let DOS know my handler exists.

Barrie Adsett
San Diego, CA

A

For the benefit of our readers, this answer will be a little more comprehensive than the one item for which you requested information. You didn't mention what compiler you are using. Both Borland and Microsoft have a critical error-handler function. You specify a function to call when a critical error occurs. Note that you will get control regardless of the type of error.

The sample in Listing 3 is Microsoft's version. Borland has a similar function, the only difference is that the parameters that are passed to your_error_handler are slightly different. The prototype for the function that sets up your handler is

void _harderr( void (_far *handler)() );
You need to create a function to handle the error and call _harderr to set up that function. When called, it will receive three items. The first is a device error. This contains bits denoting which disk drive (if any) and what possible action can be taken. The bits of dos error are shown in Figure 2 (from MS-DOS Programmer's Reference Manual by Microsoft).

The other two items are the MS-DOS error code and a pointer to the header for the device which caused the error. Your function should call _hardretn or _hardresume, depending on what you want your program to do. Do not use DOS inside your handler to output any messages, since it is not reentrant. Call the BIOS instead.

Q

I'm new to the C programming language. I basically would like to ask you how does one read very large integers into a linked list? I would like to use some code from page 221 of Data Structures Using C by Tenebaum, Langsam, & Augenstein. I would like to be able to add, multiply, divide, and subtract any integer. This code on page 221 gives me a start by setting up the addint routine. I'm sure I could come up with the others I think, but I would like to know how to read very large integers into a linked list. Also, could you tell me, how could I convert a char pointer into a NODEPTR (each node stores five integers instead of one).

Conrad Taylor

A

This question involves a long bit of code that is in a copyright book. The size of the code is too large to reproduce here, so for the benefit of our readers, let me summarize it here. A portion of the header file is shown in Listing 4. The idea behind it is to use a linked list to store a set of long ints which represent a number that is greater than that which can be stored in a single integer.

Taking your second question first, to convert a NODEPTR to a char pointer, simply use the cast operator

 char *pc;
 NODEPTR pn;
 pc = (char *) pn;
In order to discuss your problem with noncopyright code, let me provide another representation of the system you are trying to set up. A linked list may not necessarily be the simplest method that can be applied here. The alternative of a structure that uses the undimensioned array member (a feature of ANSI C) may be simpler. Of course if you can live with some large, but practically unachievable width (say the equivalent of 100 long ints or 2 raised to the 3199th power), then you could use a fixed dimension array member and the code would be simplified.

A general outline of how to proceed is shown in Listing 5. On an aside, this problem is an excellent example of how C++ could simplify the use of such a package. Setting up a class of large ints and overloading the arithmetic operators would greatly ease the user interface to the package.

The same question that you have with initializing a linked list appears with this format. The new_number function only allows the input of a single long int. First, you will need to write a multiply_number function that multiplies two numbers and returns the product. With that function, the process would look like Listing 6.

Note that this routine does not have all the error testing that should be in any code. There is one additional minor bug that a sharp-eyed reader should be able to catch. It doesn't handle negative input numbers. That one remains to keep the code within a reasonable limit of complexity.

Reader Feedback

Dynamic Link Libraries

Your Q?/A column in The C Users Journal of September had a question regarding dynamic link libraries. There are a couple of options for DOS that were not mentioned. An article in Dr. Dobb's Journal, May, 1990 by Gary Syck "Dynamic Link Libraries For DOS" provides a look at building such a system. They also mention compiler supported DLL's. Specifically Jensen & Partners International (JPI) appears to have a compiler(s) that allows use of DLL's under DOS as well as under other environments.

While I have not used either of these items, it would appear that an off-the-shelf solution is available.

C.E. Isdale
Savoy, IL

Thanks for your reply. (KP)

Libraries:

I am writing in response to a letter written by Moses Mawarari Maina, "CUJ 9/91 Q&A". While I admire the writers ambition, I disagree with his approach. I started learning C about three years ago. I have probably purchased over 100 books on C. These books have proved to be an incredible resource when I get jammed, however, my greatest source of knowledge on how to program in C was not obtained from these books.

I purchased my first C library after a year of the usual frustration that goes with learning C. The library was a user interface toolkit. Shortly after using the library's functions I ran into problems compiling. I decided to dump all of the source code and analyze what was really going on. As I started reading the source code I began to notice some of the author's ingenuous programming techniques. It didn't take long for me to realize where the greatest wealth of knowledge on C programming could be obtained. Combined with my library of C reference manuals, the picture came together.

Ever since purchasing that first library I have purchased 12 other libraries, some just to analyze the techniques used by the well seasoned programmers. I have learned everything from shortcuts to excellent documentation techniques from these libraries. I would highly recommend to anyone learning how to program in C to purchase a library, if not to use, at least to analyze.

If the writer still feels guilty about purchasing a library, just look at it as if you were hiring a staff of experienced programmers to take care of the mundane programming while you take care of creating something that someone can use.

Ronald L. Roswell, Jr.
West Palm Beach, FL

I agree with you precisely. I buy libraries because I cannot write everything from scratch. Looking at the way they work will give some ingenious ideas. For example, CTREE from Faircom is an excellent example of how to write portable code. CSCAPE by Liant (formerly Oakland Group) gives some good examples of reasonable function names.

A library with source costing about $200 is the equivalent of two to eight hours of programmer time, depending on salary and overhead. Although there is some learning overhead to use a library, if the same functions had to be designed and coded, the programmer's time would greatly exceed the two to eight hours by a factor of 10 or more. (KP)

Recursive main

Your Q&A column for Oct 91 had a question about recursive main. I can't think of a realistic use for it either (except of course for obfuscated C programs). I think it just falls out of the idea that main is just another function, and there was no reason for the ANSI committee to disallow recursive calls. Interestingly, it is not legal to call main nor to take its address in C++. It is thus recognized in C++ as something special, rather than just another function.

Steve Clamage
TauMetric Corp

Thanks for your comment. (KP)