Departments


We Have Mail


Errata

In last month's CUJ (August 1993), we inadvertently omitted the main code listing for the article on pages 23-24, "Clipping Polygons with Sutherland-Hodgman's Algorithm," by Thomas Rvsz. The listing includes definitions for the classes ClipEdge and LinearEdge, which Rvsz described in his article. We have included the listing in this issue (p. 135). We apologize for the inconvenience this omission may have caused our readers.

Dear CUJ,

I enjoyed very much the article "SSX - Stack Swap eXecutive" in the March 93 issue of CUJ. It coincided very nicely with my desire to implement a small "multi-tasking" executive, just for the fun of it. My initial idea was to use some form of "callback dispatching" similar to the X11 system, primarily because I was scared of what pre-emptive scheduling would do to pending DOS operations. Your SSX does not resolve this fear, but the elegant simplicity of the SSX solution has spurred me on to work around the DOS single-thread problem. Maybe I will publish my results in CUJ. Who knows?

I was most impressed with the level of portability achieved in SSX, which brings me to the reason for this letter, i.e. to nit-pick about two minor holes in the code that detract from portability. The first is when ssx_stop is called, and the second is when a task deletes itself.

In both of these cases, the stack currently being used by the executing code is released to the heap via free. However, the executing code then proceeds to call other functions, which by definition will modify the stack. This is a risky operation, as the stack has been returned to the heap.

All the implementations of malloc and free I have seen would not blink an eye in the above situation, assuming the current stack-pointer is some distance from the top and bottom of the stack. However, if the stack-pointer is near the top or bottom and the heap package uses the released storage for its own purposes (for example, to keep a chain of free blocks), then a corrupt heap soon results.

The only solutions I can think of all involve having a special stack that is always switched to for task deletes (or ssx_stop). The best candidate is probably the stack that was used to invoke ssx_run. I have managed to do this for ssx_stop by moving all the logic to after the longjmp has completed. The task delete is proving more difficult, so as they say in the text books it is "left as an exercise for the reader!"

Thanks for the magazine, and thanks for an interesting article.

David Brown
dcb@atb.ch

Dear Editor,

I have had two bugs reported by readers from our article, "SSX—Stack Swap eXecutive" (Tom Green and Dennis Cronin, CUJ, March 1993.). The first bug is a design problem that was introduced when we ported SSX to MS-DOS. In sx_stop and ssx_task_create, stacks allocated for tasks may be freed. The stack that the executive is running on is freed in some cases. This means that the program's current stack space is in memory that has been freed. Although this works in most cases, it certainly cannot be guaranteed to work.

Dennis and I discussed this feature and he suggested that the executive should not be involved in allocating and freeing stacks for tasks. After the reported bug, I now agree with him. In our original version, ssx_task_create was called with the address and size of the stack space, so the executive was not involved in allocating and freeing stacks. This is probably the best approach.

The second bug involves the current task pointer when the switch_lock is enabled. In Listing 1 on page 38 there are the following lines:

oldtcb = t_current
t_current = np;
/* check and see if scheduling *
 * is disabled                 */
if(switch_lock == 0)
    stack_swap(&oldtcp-->stack_ptr
             ,&np-->stack_ptr);
These should be changed to:

oldtcb = t_current
t_current = np;
/* check and see if scheduling *
 * is disabled                 */
if(switch_lock == 0){
    oldtcb = t_current
    t_current = np;
    stack_swap (&oldtcp-->stack_ptr
               ,&np-->stack_ptr);
}
The old code would set t_current prematurely and, if switch_lock was on, t_current would point to the wrong task.

Thanks,

Tom Green
tomg@cd.com

P.J. Plauger:

I've been using Borland C++ w/ Application Frameworks to generate on-screen graphics charts. I'd like to get hardcopy of these on my HP IIIP laser printer. There doesn't seem to be much information available on how one goes about doing this. In the November 1992 issue of The C Users Journal there was an article by Lowell Smith on plotting. He showed some source code which gave me an idea of what must be done to obtain hardcopy. My HP technical reference document also added some detail. It looks like I need to (1) send some escape sequences to the printer, (2) send a stream of data, and (3) send some more escape sequences.

My specific questions are;

1. How do I re-direct the graphics routines to a file instead of the screen?

2. How do I transfer the data from that file to the printer?

3. Do I need any special drivers?

4. Is there a good reference on this subject?

Thanks,

Jim LeBar
6962 Almondine Drive
Garden Grove, Ca 92645
(714) 896-3513

Lowell Smith replies:

Mr. James LeBar, Greetings:

This is in response to your E mail request to P.J. Plauger, which was forwarded to me by The C Users Journal.

If the graphics you develop in Borland C++ are drawn in while on a black background you can use the function hpltit given at the end of the SPLOT.C file. First open the file hpfile. This can, of course, be assigned to different hard disk file names at various points in the program. Then simply call that function while your graphic is on the screen. The graphic will be saved in hpfile in PCL format. To transmit the graphic to the printer after the run is complete, type:

C> copy <filename> lpt1 /b
The /b (for binary copy) is necessary to assure copying the entire file. Text ASCII files use ctl-Z (ASCII 026d) as an end-of-file indicator, and that character may occur in the middle of a binary file.

I'll briefly discuss the operation of hpltit and then present an alternative for use with plots with foreground color other than white and/or background color other than black.

In VGA hi-res color mode, the pixels in the image are stored in row-major order starting at coordinate (0, 0) at the upper left corner of the screen. Beginning at location A0000:0000 in memory, there are 80 bytes (640 bits) for row 0, followed by 80 bytes for row 1, etc. The bytes can be read from memory using peek (segment, byte). The code sets segment at the memory location corresponding to the beginning of the row and byte at the offset of the byte in the row.

HP control sequences stored in the array bcn [] are used to reset the printer, position the printer cursor at the upper left of the image on the page, initiate graphics mode, start each row of graphics output, initiate the transfer of 80 bytes for each row, end graphics mode, and finally to reset the printer (which automatically ejects a page). Note that the cursor position is relative to the upper left margin — normally 0.5 inch from the top and left edges of the page.

The HP control sequences are defined in the LaserJet Printer Technical reference manual for your printer. The code was developed using the LaserJet II, which supports PCL level 4. I believe the LaserJet IIIp supports (most of) level 5, so you should have no problems.

The HP control sequences always begin with what they refer to as Ec, which is ASCII 027d or ctl-[.

EcEEc * p600x400YReset printer, position cursor to 600,400 dots (2, 1.3333 in.) relative to upper left margin of page. (Same as EcEEc * p600XEc * p400Y). See discussion on combining escape sequences in Tech. Ref. Manual.

Ec * t150R — Initiate raster graphics at 150 dpi resolution.

Ec * r1AStart a row of graphics output.

Ec * b80W — Transfer 80 bytes of data.

Ec * rBEcEEnd graphics and reset printer.

You can change the initial cursor position and the graphics resolution (75, 100, 150, or 300 dpi) to suit. I chose 150 dpi resolution as a compromise. With 300 dpi you get better resolution, but the maximum image size on the page is 2.133 by 1.6 inches. The PostScript plots created by WOIsplot offer the advantage of output at printer resolution in any scale-e.g., 600 dpi on LaserJet 4M (or 4 with PostScript SIMM). Additional functions would be required to simulate all the Borland C graphics routines, however.

I chose white on black for the graphics displays in splot because I didn't see a need for color when hardcopy output is the main objective. If you must use color, however, the function hpltit2 shown in Listing 1 should do the trick. As explained in Bradley Kliewer's book, EGA/VGA — A Programmer's Reference Guide, Second Edition (McGraw Hill, 1990), the color data are stored using four-bit planes, with bits in the four planes used to define the 16 possible colors in hi-res mode. The bit plane must be selected prior to a memory read. White (color 15) sets bits in all planes, so you get a one bit for a lit pixel regardless of which plane is selected. That is why the direct memory access via peek can be used in hpltit. Borland C provides the function getpixel(x, y) to extract color data for the pixel at the specified location. Note that getpixel returns a nonzero value if and only if the pixel color differs from the background color.

Instead of getting a whole byte via peek at each byte offset th each row, we must determine the value of color for each of the eight bits in the byte. At the beginning of the process for each byte we set the char variable ch to zero. For the i-th pixel, if the color is zero we set the char variable ch1 to zero. If it is nonzero, we set ch1 to 0x80>>i. 0x80 is 10000000b. Thus the leftmost bit remains set for i=0 and the one bit is shifted to the rightmost position for i =7. The statement:

ch = ch1 | ch;

sets the bit in ch at the location where (and if) a bit is set in ch1. After all eight pixels are processed, the byte is transferred to the file as in hpltit.

hpltit2 is rather slow; on my 16 Mhz 386. hpltit processes an image in about one second while hpltit2 takes about 30 seconds. The operation could be speeded up substantially by an assembly-language function developed using the descriptions given in Kliewer's book, but I have not yet attempted that.

I hope this answers your questions. If you have further problems feel free to contact me directly.

Sincerely,

Lowell Smith
(801) 272-3075
Compuserve 73377,501

Dear Messrs. Weinstein and Plauger:

Does Sydney Weinstein know a firstrate book? If so, I'd like to hear about it.

Over the past few years the programming world has been blessed with a number of excellent books. I am happy to believe that not all of these have come to my attention. An armful of titles on almost any topic, including UNIX device drivers, are currently in print. Thus there is little information in a negative review of one book (May '93 CUJ). Given this fact, I suggest that the CUJ Book Review focus exclusively on good and exceptionally good books, and say why they are good.

Sincerely,

Sean Furlong
100 Thayer St., 2H
New York, NY 10040

Your argument is compelling, but I must still demur. I personally appreciate knowing when a book has weaknesses. I might still buy it anyway, but at least I won't be disappointed. — pjp

Dear C Users Journal Subscription Taker:

I was going to let my subscription lapse. The last issue changed my mind. After a long, deep trip into pixel shuffling (Windows), I had been feeling burnt out and disgusted with the direction Microsoft & company is pushing. It was refreshing to read something reinforcing the idea that computers should be used as an aid to, or an extension of, our thought processes. We think in words, not pictures, and each picture requires visual deciphering to determine the words it represents. This deciphering places unnecessary speed bumps in the human thought process. The C Users Journal is the only subscription I receive that appears devoted to the pure beauty of logic. Let children play the games, they will eventually mature and understand the words.

One more thing please: Correct the spelling of my name and company, The name is spelled Gearhart with an R, and the company is BGS not Mbt. Sometimes the words can be confusing also.

Bob Gearhart
121 Washington St.
Trenton, MI 48183-1233

The pure beauty of logic, huh? I was wondering what our acceptance criteria were. — pjp

Dear Dr. Plauger:

I was quite intrigued by the description of the CORDIC algorithm in the November 1992 issue ("The CORDIC method for faster sin and cos calculations"). I was interested enough to translate the C code presented into C++, which I have included with this letter.

Only a few changes were necessary to move the code from C to C++. Most notable, and probably most useful, the C++ version is able to encapsulate the variables which were global in the C version. They are moved inside a cordic class as static member variables, protecting them from accidental modification by routines unaware of them. Moving these variables inside a class structure makes it necessary to develop interface routines, and also requires that a decision be made about how the class will be used.

The route I took was to have one predefined member of the cordic class, cord, which should be used to access the member functions which compute the integer sine and cosine. Other instances of class cordic can be created, and they will work as well as the predefined instance, but there is little advantage to this in the current implementation.

One possibility I did not follow up on in this implementation is providing the ability to instantiate the class with different levels of accuracy — using different bases for the CORDIC algorithm. The complexity of that implementation and its use seemed to go against the straightforwardness which is the main advantage of the CORDIC algorithm.

I hope you can use this in the magazine. Thanks for a wonderful publication, and all the great services connected with the C Users Group.

Sincerely,

Tim Farnum
72 Tryon Park
Rochester, NY 14609
Internet: tfarnum@delphi.com

The code is available on the monthly code disk. — pjp