Porting Borland Turbo C source code to GNU gcc
Running a ported program
Running a ported program remotely
Supported Turbo C entities
Handling of integer datatypes
Text screen resizing
Graphical display characters in text mode
Alternative graphics-mode functions
No explanation of the exact functionality of the various TurboC library elements is offered here, except for noting differences between Borland's version and my version. As Pablo Vidal points out on his UConio web page, if you don't know how to use these functions already, then you won't need the TurboC library, since you shouldn't be using it for new program development anyway. Consult Borland's Turbo C Reference Guide as needed.
But if you are porting or supporting a Turbo C program that somebody else has created, and don't have access to Borland's printed materials yourself, I'd offer the following advice: Download the free Turbo C development environment that Borland has kindly provided on their "museum" website. (Requires you to register, but registering is free.) The IDE has context-sensitive help, and you can find out what most functions do by typing the name of the thing (such as clrscr) into the source-code editor and then hitting ctrl-F1. Of course, this requires you to have some means of running an MS-DOS program.
Alternately, the online help for Borland's C++Builder program actually provides much fuller documentation of Turbo C library functions (even though it doesn't support a lot of them), and while C++Builder itself is not free, Borland allows you to download the Win32-based helpfile for free from their website . After you download it, the particular helpfile you want is the one which Borland calls the DOS Reference. Sadly, there's no guarantee that this documentation corresponds exactly to the functionality provided in Turbo C.
gcc [-O0] ... your switches ... -lTurboC -lm -lncurses -L/usr/X11R6/lib -lX11 -lpthreadIn FreeBSD, you'll have to say "-lc_r" rather than "-lpthread". Note that the actual directories containing header files and libraries of the X-window system may vary, so you may need to alter the "-L" switch or add a "-I" switch to access the appropriate X files.orgcc -DDoNotFixIntegers ... your switches ... -lTurboCu -lm -lncurses -L/usr/X11R6/lib -lX11 -lpthread
The command-line switch "-DDoNotFixIntegers" is optionally used to turn off TurboC's integer datatype conversion macros and (as you may notice) uses a slightly different version of the library. In the past, some GNU "functions" actually implemented as macros have failed to compile with optimization level higher than -O0, when integer datatype conversion is turned on. That's why one of the examples includes -O0 and the other does not. However, this precaution is probably no longer necessary, and you needn't use -O0 unless experiencing problems.
You can see samples of both types of compilation in the provided Makefile. If there are complaints from the compiler, refer to the trouble-shooting section below.
The switch "-lm" is required by only a couple of functions, but there's no harm in using it for everything. The switch "-lncurses" is needed only if you are using the Turbo C functions prototyped in conio.h, while the switches "-L/usr/X11R6/lib -lX11 -lpthread" are needed only if using the functions prototyped in graphics.h. You'll want to remove these switches if they're not used, to enhance your ability to compile the ported program on more platforms. For example, neither ncurses nor Xlib are typically present in a Mac OS X system (though they can be installed with some effort), and compilation of your ported program will therefore typically fail if these compiler switches are included.
Although I'm not entirely sure why you'd want to do it, and though I haven't tried it myself, I think you can probably disable the graphics.h functions in the TurboC library, and replace them with those in the GRX 2D graphics library. In the TurboC library Makefile, remove the compiler switch "-DWITH_X" before compiling the TurboC library. Then install GRX and follow the instructions in GRX's readme.bgi file. I'd be happy to hear from anyone with feedback (pro/con/howto) on this topic, and will post those comments if they seem to be of general interest.
If your code is linked to ncurses or X, as described in the previous section, you'll also have to conform to their licenses, namely the MIT license and the X-Consortium license respectively. These licenses are highly non-restrictive, and require merely that you include their copyright notices. Both licenses can be viewed at the Free Software Foundation's website.
While text-based programs ported with the TurboC library run to some extent in almost any *nix text terminal, it is highly advisable to run the ported program under the X-window system, explicitly using xterm. I'd recommend invoking your program as follows, either from a command line or as a desktop icon:
xterm +sb -tn linux -e PortedProgram PortedProgramOptions ...(See "man xterm".) The "+sb" command-line switch removes scrollbars from the xterm console. The "-tn linux" command-line switch insures that the xterm color settings, function-key interpretation, etc., match what's used in Linux (where I've been developing the TurboC library). This is useful, for example, if the ported program is being run in FreeBSD, where xterm's default settings aren't necessarily the same as in Linux.
The deficiencies which you may encounter if xterm is not used are numerous, but other kinds of text consoles (even a real text console without the X-window system) may prove to be satisfactory for your purposes. You'll simply have to try it and see, if you're so inclined.
Graphical capabilities, on the other hand, are supported only under the X-window system, and require availability of an X server. In general, this means that the ported program can be run on any *nix box except bare-bone ones (like firewalls) or quirky ones (like Mac OS X) that don't provide X.
xterm +sb -tn linuxActually, there is some difficulty correctly sizing the screen when ssh is used for the remote login, though telnet and rlogin seem to work properly in the cases I've tried. What happens with ssh is that if the xterm window is manually resized, it doesn't automatically resize itself until a key is pressed. I imagine that the desire to remotely run a ported Turbo C program is rare, so this is unlikely to pose a problem for many people.
telnet/rlogin/ssh/etc. (to connect to host)
PortedProgram PortedProgramOptions ...
To a certain extent, graphically-based programs can also be run remotely, because the TurboC library's graphical capabilites are based on the X-window system. However, exploiting this capability is hardly a user-friendly process. The following steps are necessary:
Putting it all together, the complete sequence of steps for running the program remotely might become something like this:
- You must set up the X-server at the user's location to accept input from the remote computer hosting the ported TurboC program. This is done with the command-line program xhost, and you need to know the ip address of the remote computer:
The DISPLAY environment variable must be properly initialized on the remote computer to indicate the user's computer. Typically this is done automatically when you log in (with telnet/rlogin/ssh ), but not necessarily. So after logging in, but before running the ported program, you might need to set up DISPLAY. In the bash shell, this would look something like this:
xterm +sb -tn linuxUnfortunately, firewalls and/or network-address-translation may wreak havoc with this scheme, and you may find that the graphics capabilities of the ported Turbo C programs simply don't work remotely. Text-only ported programs are fortunately less affected (i.e., not affected) by these problems.
PortedProgram PortedProgramOptions ...
Anyway, the table below isn't a complete list of all the Turbo C
entities you can use, but simply those that the TurboC library treats
I'm sure there are Turbo C functions that need special treatment which
simply not encountered yet in my porting activities, and so aren't
|Turbo C Entity||Entity Type||Supported||Notes|
|__libTurboC__||constant||n/a||Provided by the TurboC library, but not normally present in actual Turbo C. Can be used with conditionaly compilation to insure that any changes you make to your code in porting to libTurboC are discarded if the program is recompiled later in actual Turbo C. :-)|
|alloc.h||header file||Yes||This is supported in the sense that a file of this name is provided. However, not all of the functions are necessarily supported.|
|bios.h||header file||Yes||This is supported in the sense that a file of this name is provided. However, not all of the functions are necessarily supported.|
|biosprint||function||Yes||Thanks to Igor Bujna! At the moment, this one relies on Linux specifically (not FreeBSD or other Unices).|
|BypassResizeXterm||variable||n/a||This is not present in real Turbo C. Normally, the TurboC library will attempt to physically resize the text console when you call textmode . However, this does not work unless you are running xterm. If you set BypassResizeXterm to a non-zero value before calling textmode-- say, on the basis of some environment variable setting -- then the attempt to resize the window is bypassed. However, the window must still be resized by some other mechanism or else the program may segfault.|
|clock||function||Yes||This is already a standard *nix function, but the existing *nix function is not suitable for Turbo C. Instead the existing function is rename clockUnix and a new clock (clockTurbo) function is created. The CLK_TCK constant may also pre-exist in *nix, but it may (or may not) be assigned a different value than needed for clock . If TurboC.h or any other header files from the TurboC library are included, CLK_TCK will automatically be reassigned (within that source file) to an appropriate value. However, it may not be the same value for CLK_TCK used in true Turbo C (variously, 18 or 1000). For example, in my experiments it is equal to 100, but there's no guarantee that it won't be 1000000 on some system. This means that the output of clock must always be assigned to a variable of type clock_t , because it may overflow an int or unsigned. clock and CLK_TCK are supposed to be prototyped in time.h -- and are -- but won't have the behavior expected in TurboC unless a TurboC library header file is included also. In other words, if time.h is included but no TurboC header file is included, clock will behave incorrectly but there will be no errors or warnings. Finally, clock is supposed to measure the time since program startup, but in TurboC it actually measures the time since clock itself was first called by the program.|
|COLS||variable||n/a||This is not present in true Turbo C. It is an ncurses variable that tells the current number of text columns in the display console.|
|conio.h||header file||Yes||Most conio.h functions are supported. Currently, only cscanf is omitted. Support is through ncurses.|
|ConioResizeCallback||function||n/a||This is not present in true Turbo C. It is a function which the TurboC library calls when it has detected that the user has resized the console. By default, it does nothing, but you can override this behavior by providing your own version of this function (which will be preferentially chosen at linktime over the version already within the TurboC library). You can do anything you like with this function, but one possible use is to refresh all of the text data on the screen. Use the LINES and COLS variables to determine the current screen height and width.|
|cscanf||function||No||It may seem naively that this function is a simple
cgets with sscanf. However, on closer inspection
isn't. I don't presently see how to implement it without
with the guts of the scanf function family (which I have no
Workaround: For the present time, I'd suggest writing cscanf out of your code in favor of cgets and sscanf . Actually, I've seen Borland docs that recommend this also.
|struct date||data type||Yes||Thanks to Igor Bujna.|
|delay||function||Yes||(Igor Bujna provided this function but I haven't used it, because his version relies on ncurses. Thanks but sorry, Igor!) The function parameter has arbitrarily been changed from 16-bit to 32-bit.|
|dir.h||header file||Yes||This is supported in the sense that a file of this name is provided. However, not all of the functions are necessarily supported.|
|variable||Yes||I provide variables of this name, which you can manipulated as desired, but they don't affect any conio functionality. (It seems to me that variables of both names have been provided in different versions of Turbo C, but they behave the same.)|
|dostounix||function||Yes||Contributed by Igor Bujna.|
|far||type modifier||Yes||Requires TurboC.h. There's no need for "far pointers" with a 32-bit compiler, so this is simply an empty macro.|
|farcalloc||function||Yes||This is the same as calloc.|
|farcoreleft||function||Yes||This is supposed to determine how much space remains on the heap. There's no way to do this, since the heap size is not limited in any practical sense. Hence, it is a macro that simply always has the value 512K.|
|farfree||function||Yes||This is the same as free.|
|farmalloc||function||Yes||This is the same as malloc.|
|Requires TurboC.h. This is not a function for which I provide any code. It's simply a standard library function.|
|struct ffblk||data type||Yes|
|findfirst||function||Yes||While substantially a functional replacement for the Turbo C
function of the same name, it is also subtly different in a number of
|findlast||function||n/a||This is not a function provided by true Turbo C, but is
provided to correct a problem in the libTurboC emulation. The
"normal" use of
findfirst/findnext, as defined by me, is that the entire
list is read, and then terminates when findnext cannot locate
more files. If the functions are not used in this way -- in other
if findnext never locates the last file -- then
has no way to know that it should free the memory which has been
With each "abnormal" use of findfirst/findnext this
will accumulate. The symptom is that eventuallyfindfirst/findnext
will not be able to locate files even though they exist. This
can be fixed by explicitly adding a call to findlast after
use of findnext, but before deallocating its struct ffblk
. The prototype is:
void findlast (struct ffblk *);
|findnext||function||Yes||See the notes for findfirst.|
|floodfill||function||TBD||I may prove to be too darned lazy for this. However, there's a GRX function that does this. If anyone wants to adapt it ...|
|fnkeys.h||header file||n/a||This is a file I've provided, but there is not any equivalent Turbo C header file. This header simply provides symbolic names for all of the special-function keys recognized by MS-DOS. Unfortunately, these are not all supported by ncurses. See the discussion below.|
|getarccoords||function||Yes||Requires the "-lm" compiler switch.|
|getaspectratio||function||Yes||There is a function of this name, but at this point it acts as though the pixels are always square.|
|getch||function||Yes||There is an ncurses function of the same name, but somewhat
different functionality. In any source file using conio.h, the
conio version of getch applies below the #include
"conio.h", while the ncurses version of getch applies
above it. The ncurses functionality continues to be available,
however, as a new function getchNcurses
Please note that getch will work in textmode or in graphics mode, but will not presently work otherwise. Conversely, getchar will work only when not in textmode . (getchar will work in graphics mode if textmode has not been previously called, but will not echo to graphics screen).
|getdate||function||Yes||Contributed by Igor Bujna. In some newer system libraries, there actually is a function called getdate, which works differently and conflicts with this function. That function, if it exists, will be accessible in TurboC programs as getdateSystem.|
|getdefaultpalette||function||Yes||The true Turbo C function returns a pointer to a struct palettetype , whereas the TurboC library function returns a pointer to const struct palettetype.|
|getdrivername||function||Yes||This functions returns a const char *, whereas the true Turbo C function returns char *.|
|getftime||function||Yes||I initially believed that this function did not work, because the linker could not find the fstat function on which it is based. Later, this problem mysteriously disappeared. I mention this just FYI.|
|getimage||function||Yes||Borland's documentation does not
specify the format of the buffer into which the image is placed, beyond
fact that the first two words contain the width and height, in
pixels. If you use this function with those restrictions in mind,
you'll be okay.
On the other hand ... I think it's very likely that programs containing this function might exploit knowledge of the graphics controller in the PC, and thus may (improperly if cleverly) directly manipulate date within the output graphics buffer. Realize that no effort has been made to duplicate these undocumented buffer formats. If your program depends on the size or format of the graphical area of the output buffer, I guarantee that it will not work!
As a corollary, be sure to allocate image buffers dynamically using imagesize, per Borland's docs, with malloc or calloc. I suspect that many existing programs statically allocate the buffers, on the incorrect notion that the buffer format is known.
Finally, you should not expect to be able to save the image buffer as a file and to reload it later.
|getmaxmode||function||Yes||See the comments for getmoderange.|
|getmodename||function||Yes||Since the TurboC library does not limit the graphics modes to those of a particular "graphics driver" as Borland Turbo C does, and since there are many more graphics modes, it is not practical to make the name strings identical to those reported in an actual Turbo C program.|
|getmoderange||function||Yes||Since the TurboC library rejects the concept of a "graphics driver", every graphics mode is allowed for every driver. Furthermore, while various graphics modes are available under the same symbolic constant names as in true Turbo C (such as VGAHI, VGALO, etc.), the symbolic constants don't have the same numeric values as in true Turbo C, and many more modes are available than are assigned symbolic constants. (To understand this, refer to the graph.h file.) The upshot of all this is that getmoderange returns a huge range of allowable modes, in comparison to true Turbo C. At this writing, the range is from 0 to approximately 1439, whereas true Turbo C topped out at about 5; this maximum mode presently provides a 1280x1024 screen with 256 colors and 4 pages. Whether this presents a problem remains to be seen.|
|getpixel||function||Yes||In true Turbo C, we are guaranteed that the on-screen color corresponds to an actual color in our palette. In the TurboC library, this isn't true. The closest-match paletted color is returned. If there is more than one match, the first one encountered is returned.|
|gettext||function||Yes||The format of the text buffer created is the same as in true
The same bytes are produced, and they are stored in the same order.
11/02/03 and later: There is a function called gettext in the internationalization library (libintl). To avoid a conflict, the internationalization library's function will be accessible instead by the name gettextIntl.
|gettime||function||Yes||There are two versions of this, one by me, and one by Igor Bujna (called gettime_d). I'm not aware of the pros and cons of the two.|
|graphics.h||header file||Yes||At present, most graphics.h functionality is supported in one
another except text output, which is next on the to-do list.
Use of graphics requires the X-window system.
|_graphfreemem||function||Yes||This function is not used in any way by the TurboC library -- or hopefully, by the user code -- but has nevertheless been provided.|
|_graphgetmem||function||Yes||See the comments for _graphfreemem.|
|huge||type modifier||Yes||Requires TurboC.h. There's no need for "huge pointers" with a 32-bit compiler, so this is simply an empty macro.|
|imagesize||function||Yes||Important: see the warnings for the getimage function above.|
|initgraph||function||Yes||Causes a separate graphics window to open. (The parent text-based window remains visible.) The location of this graphics window (i.e., locally or remotely) is dependent on the value of the DISPLAY environment variable. Typically (but not always) this variable is set automatically for you by the system, and so you usually don't have to worry about it. The graphdriver and pathtodriver input-parameters of initgraph are (almost) completely ignored, though the constants used as names for the various drivers are available for your use (DETECT, CGA, MCGA, EGA, etc.). As far as the graphmode input-parameter is concerned, every mode applicable to any graphics driver is accepted. All graphics modes through Borland C++ 5.0 are supported: CGAC0, CGAC1, CGAC2, CGAC3, CGAHI, MCGAC0, MCGAC1, MCGAC2, MCGAC3, MCGAMED, MCGAHI, EGALO, EGAHI, EGA64LO, EGA64HI, EGAMONOHI, HERCMONOHI, ATT400C0, ATT400C1, ATT400C2, ATT400C3, ATT400MED, ATT400HI, VGALO, VGAMED, VGAHI, PC3270HI, IBM8514HI, IBM8514LO.|
|installuserdriver||function||Yes (sort of)||Graphics drivers as such are not used by the TurboC library (which allows every defined graphics mode to be used by every "driver" anyway). This function, therefore, does nothing. It always returns a value of IBM8514.|
|LINES||variable||n/a||This is not present in true Turbo C. It is an ncurses variable that tells the current number of text rows in the display console.|
|mkdir||function||Yes||A function of the same name is provided by GNU gcc, but behaves somewhat differently in the Linux environment. The version provided by TurboC corrects that behavior.|
|outtext||function||Yes||See the comments for outtextxy.|
|outtextxy||function||Yes||See the comments for settextstyle.
Borland seems to perform clipping on text by not displaying any characters that aren't wholly within the viewport. The TurboC library performs clipping on text by not displaying any pixels of the characters that are outside of the viewport. In other words, the TurboC library may display partial characters in some cases, whereas true Turbo C will not do so.
|putimage||function||Yes||Important: see the warnings for the getimage function above.|
|puttext||function||Yes||See the notes for gettext.|
|registerbgidriver||function||Yes (sort of)||This function serves no purpose in the TurboC library, since there is no need to load separate graphics "drivers". Hence, this function actually does nothing. It always returns a value of IBM8514, which probably doesn't correspond to what you might have wanted. Therefore, it is probably best to write this function out of your code.|
|restorecrtmode||function||Yes||Note that Borland's documentation does not specify whether the resulting text-mode screen is cleared or not. With the TurboC library, it will not be cleared.|
|sector||function||Yes||Requires the "-lm" compiler switch.|
|setaspectratio||function||Yes||There's a function of this name, but it does nothing, on the assumption that the pixels are always square.|
|setbkcolor||function||Yes||See the comment for setpalette below.|
|_setcursortype||function||Yes||This function was not present in Turbo C 2.0, but appeared in some later versions. The cursor type, as in Turbo C, is one of the following: _NOCURSOR, _NORMALCURSOR, _SOLIDCURSOR. However, ncurses does not provide these choices as such; it provides only "invisible", "visible", and "very visible", with the differences between these being dependent on the target platform. For example, on my iMac Linux box, both _NORMALCURSOR and _SOLIDCURSOR appear as indistinguishable block cursors. In other words, your mileage may vary.|
|setftime||function||No||The closest *nix function corresponding to this that I can
utimes, which unfortunately requires knowing the filename.
the other hand, setftime requires the handle of an
There's no clean way I know to determine a filename given a file
I don't see any clean way to implement this without intercepting all
open and fopen calls.
Workaround: Rewrite your code to use utimes rather than setftime.
Note: Actually, there is a way of writing such a function without trapping fopen, but it's so unclean that I'd hesitate to do it. I'd be happy to receive someone else's code for it, though. Given the file handle, the fstat function can reveal the file's "inode". There's no way to do a reverse lookup and determine a filename given an inode, but a search of the entire file-system (using the find function from the command line, with the appropriate switches) can give a list of the filenames of all files with a given inode. The search can be made relatively efficient by first searching just the current directory, and then searching the home directory only on failure, and then searching the entire file-system only on failure. (Unfortunately, the inode numbers are not necessarily unique. They are only unique within a given file-system, and if multiple file-systems are mounted, they could each have files with the same inode.) Finally, after find has determined the filename, the utimes functions could be used. Ick!
|setgraphbufsize||function||Yes ... and no||Yes there is a function of this name. No, it doesn't do anything, though it does return the correct values. I don't allow you to alter the size of any memory buffers used internally by the TurboC library.|
|setpalette||function||Yes||In true Turbo C, changes to the palette may instantly be seen on the graphics display, because true hardware modes within the graphics controller are affected. With the TurboC library, only future drawing operations are affected, but pixels which have already been drawn are unaffected.|
|setrgbpalette||function||Yes||See the comment for setpalette above.|
|settextjustify||function||Yes||Borland Turbo C 2.x seemingly has a bug in which if the text style is HORIZ_DIR, then LEFT_TEXT is treated as RIGHT_TEXT . This bug has not been duplicated in the TurboC library.|
|settextstyle||function||Yes ... and no||So far (20020608), only the bitmapped font (DEFAULT_FONT)
Eventually, all stroked fonts defined in Borland C++ 5.0 will be supported, supposing that I can find appropriate open-source fonts with which to implement them: TRIPLEX_FONT, SMALL_FONT , SANS_SERIF_FONT, GOTHIC_FONT, SCRIPT_FONT, SIMPLEX_FONT, TRIPLEX_SCR_FONT, COMPLEX_FONT, EUROPEAN_FONT, BOLD_FONT
|setwritemode||function||Yes||Supports not only the modes COPY_PUT and XOR_PUT specified by Borland, but also the modes OR_PUT, AND_PUT, and NOT_PUT (which in true Turbo C were used only for the putimage function).|
|sopen||function||Yes||This function (implemented as a macro) was contributed by Igor Bujna. Never having used it myself, I don't know how closely it matches the Turbo C function. However, O_TEXT is defined identically to O_BINARY, so it clearly doesn't distinguish between text and binary modes.|
|_stklen||global variable||Yes||Requires TurboC.h. In Turbo C, this was used to define the size of the stack. This is pointless in gcc, so a variable of this name is provided, but assigning it a value does nothing.|
|strcmpi||function||Yes||Requires TurboC.h. This is simply a macro that renames the function as strcasecmp.|
|stricmp||function||Yes||Requires TurboC.h. This is simply a macro that renames the function as strcasecmp.|
|strncmpi||function||Yes||Requires TurboC.h. This is simply a macro that renames the function as strncasecmp.|
|const int TcUnicodeMappings||global variable||n/a||This is a resource not available in true Turbo C, but is helpful if you choose to use Xlib functions rather than graphics.h functions to display text on a graphics-mode screen. Except for the usual ASCII printable characters (0x20-0x7E), the character set in normal *nix fonts is not the same as the normal character set in MS-DOS. However, you can fake up this character set if a Unicode font (one with font descriptions ending in 10646-1) is used. The array simply provides a decent Unicode equivalent for MS-DOS characters. Every character is represented, but not every character is necessarily present in every Unicode font. The so-called GNU Unifont supports every character we reference. Note that lots of characters were missing from the original Borland-supplied stroked fonts anyhow.|
|textattr||function||Yes||(See notes for textcolor.)|
|textbackground||function||Yes||(See notes for textcolor.)|
|textcolor||function||Yes||For color control, Borland's color constants are used, and are assigned the same numerical values as in Turbo C: BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE, BLINK. Whether or not you actually get blinking when using BLINK seems to depend on the console being used. For me, it seems to blink with KDE's konsole but not with xterm. Anyhow, your mileage may vary.|
|struct text_info||data type||Yes|
|textmode||function||Yes||All of the Turbo C 2.0 text modes are supported, and I've
added later, up to those defined in Borland C++ 5.5. The same
with the same numerical values are used as in Turbo C. (BW40,
BW80, C80, MONO, C4350, ....)
If you don't explicitly call textmode before beginning to use the various console-i/o operations, there is an implicit call textmode(C80). Unfortunately, even though the initial call to textmode is optional, there is a non-optional call to textmode at the end of the program, not needed by the original Turbo C program but needed by the TurboC library. A mode called EXITMODE not found in Turbo C is provided to allow ncurses to gracefully shutdown. You must call textmode(EXITMODE) prior to exiting your program, or else you risk losing control of the console from within which you're running the program.
TurboC does not actually distinguish between B&W and Color modes; all modes are color modes.
The rationale for sizing of the text console differs somewhat between Turbo C and ncurses, in a way that makes it difficult to reconcile them with perfect accuracy. You'll want to read the section titled "Screen resizing" for an explanation.
|struct time||data type||Yes|
|TurboC.h||header file||n/a||There is no Turbo C header file of this name, but this is sort of a catch-all header I've created to cover everything not directly covered in conio.h and the other actual Turbo C header files. For example, it contains the integer datatype conversion macros and the prototypes for the strupr and strlwr functions (which are handled in Turbo C by the string.h header file, but can't be handled in gcc that way because string.h already exists). This file is automatically included if any of the other header files (conio.h, io.h, dir.h, etc.) are included.|
|ungetch||function||Yes||There is an ncurses function of the same name, but somewhat different functionality. In any source file using conio.h, the conio version of ungetch applies below the #include "conio.h", while the ncurses version of ungetch applies above it. The ncurses functionality continues to be available, however, as a new function ungetchNcurses .|
|unixtodos||functions||Yes||Contributed by Igor Bujna.|
|_wscroll||variable||Yes||This global variable variable was not present in Turbo C 2.0, but is present in some later versions.|
The TurboC library allows you to handle this problem in two separate ways, at your option. The first way, of course, is simply to ignore the problem and to assume that an int is an int is an int. :-) In other words, datatypes can be left unchanged, and it's up to you to figure it out later, if your program doesn't work. By default, though, TurboC redefines the words short, int , unsigned, and long using macros so that they are replaced by GNU datatypes specifically corresponding to the Turbo C datatypes, namely:
#define short int16_t(You may wonder why there's a macro for long, since it's a 32-bit datatype on both Turbo C and gcc. If so, you've fallen into the Turbo-thinking trap! The long datatype is 64-bit for some CPU types supported by gcc.)
#define int int16_t
#define unsigned uint16_t
#define long int32_t
This scheme has the advantage of transparently fixing almost all data declarations in your program. Those declarations which are not fixed properly, such as unsigned long or the use of unsigned to define bitfields in a struct, are broken. This is actually good news, because the compiler gives you an error message and you're allowed to fix the problem, rather than just allowing you to assume everything is swell. The resources you have to fix the problems with are the various explicit datatypes provided by GNU gcc (int8_t,uint8_t, int16_t, uint16_t, int32_t, and uint32_t) and some new datatypes defined by the TurboC library corresponding to the original (non-macro-replaced) GNU datatypes: gchar, gschar , guchar, gshort, gushort, gint, guint, glong, and gulong.
For example, after automatic macro conversion, things like the following will be correct
int i, j, k;but things like this will be wrong:
unsigned int i, j, k; // Change to unsigned or to uint16_t.The automatic conversions are enabled by default, but can be disabled with the "-DDoNotFixIntegers" compiler switch. You have to decide for yourself which is more appropriate. Enabling the automatic conversions means that your program is more likely to work correctly once ported. But it also means that it may be more difficult to maintain afterward because it will be filled with things that look like int but aren't really.
unsigned char c; // Change to uint8_t.
int i; // Okay.
unsigned x:1; // change to guint.
unsigned y:2; // change to guint.
int main (int argc, char *argv) // Change to gint.
Note that if you want to use "-DDoNotFixIntegers" to compile the code you're porting, a different version of the library is used -- libTurboCu.a rather than libTurboC.a. Both library versions are automatically built, for versions 20020319 or later. (For earlier versions, I'm afraid you'll have to build libTurboCu.a yourself by manually changing the Makefile to have the gcc switch -DDoNotFixIntegers).
Here is a tabular summary of how the integer datatype declarations
are handled if converted automatically by the TurboC library:
|Turbo C type||Size||Equivalent
GNU gcc type
|Handling by TurboC|
|signed char||8||signed char||Correct|
|unsigned char||8||uint8_t||Causes compile-time error. Replace manually with uint8_t .|
|unsigned short||16||uint16_t||Causes compile-time error. Replace manually with uint16_t .|
|short int||16||int16_t||Causes compile-time error. Replace manually with short .|
|unsigned short int||16||uint16_t||Causes compile-time error. Replace manually with uint16_t .|
|int bitfields in structs||varies||int||Causes a compile-time error. Replace manually with gint .|
|unsigned int||16||uint16_t||Causes compile-time error. Replace manually with unsigned .|
|unsigned bitfields in structs||varies||unsigned||Causes a compile-time error. Replace manually with guint .|
|unsigned long||32||uint32_t||Causes compile-time error. Replace manually with uint32_t .|
|long int||32||int32_t||Causes compile-time error. Replace manually with long .|
|unsigned long int||32||uint32_t||Causes compile-time error. Replace manually with uint32_t .|
Unfortunately, many function-key sequences available using getch in MS-DOS are not available at all using the ncurses library. (For example, F1 is available, but Ctrl-PgUp is not.) Others may or may not be available, depending on various factors. Consequently, while the TurboC library has mimicked this functionality as best it can -- or rather, as best I can -- you may still be in the position of having to change some of the special-function keys used by your program.
Here's a list of all special keys that I believe are handled by
getch function, along with my observations of how they are
by the TurboC library's getch function. Your
|Special Key||Symbol provided in fnkeys.h||Under xterm||Under KDE Konsole||Under a text-mode login|
|Digits on numeric keypad||n/a (0-9, of course)||Ok||Ok||No! Treated as arrows, PgUp/PgDn.|
|Ctrl-A through Ctrl-Z||C_A-C_Z||Ok||Ok||Ok, except there is no Ctrl-Z.|
|Alt-A through Alt-Z||A_A-A_Z||Ok||Ok, except that Alt-O has a slight delay before registering.||Ok|
|F1 through F10||N_F1-N-F10||Ok||Ok||Ok|
|Shift-F1 though Shift-F10||S_F1-S_F10||Ok, except Shift-F5 terminates program||Ok, except: Shift-F5 terminates program, and F9-F10 don't work.||Ok, except: Shift-F5 terminates program, and F9-F10 don't work.|
|Alt-F1 through Alt-F10||A_F1-A_F10||No! (These are KDE hotkeys.)||No! (These are KDE hotkeys.)||No! (Switches between consoles.)|
|Ctrl-F1 through Ctrl-F10||C_F1-C_F10||No! (These are KDE hotkeys.)||No! (These are KDE hotkeys.)||No!|
|Alt-0 through Alt-9||A_0-A9||Ok||Ok||Ok|
|Arrows||N_UP, N_DOWN, N_LEFT, N_RIGHT||Ok||Ok on standalone arrow keys, but not on numeric keypad.||Ok|
|PgUp, PgDn||N_PGUP, N_PGDN||Ok||Ok on standalone arrow keys, but not on numeric keypad.||Ok|
|Home, End||N_HOME, N_END||No!||Ok on standalone arrow keys, but not on numeric keypad.||No!|
|Ins, Del||N_INS, N_DEL||Ok||Ok on standalone arrow keys, but not on numeric keypad.||Ok|
|Pad-minus, Pad-plus||N_PADPLUS, N_PADMINUS||Ok||No, treated as regular +/-.||No, treated as regular +/-.|
|Ctrl-right, Ctrl-left||C_LEFT, C_RIGHT||Ok on standalone arrow keys, but not on numeric keypad.||No, treated as regular right/left.||No, treated as regular right/left.|
|Ctrl-PgUp, Ctrl-PgDn||C_PGUP, C_PGDN||No!||No, treated as regular PgUp/PgDn.||No, treated as regular PgUp/PgDn.|
|Ctrl-Home, Ctrl-End||C_HOME, C_END||Ok on standalone arrow keys, but not on numeric keypad.||No, treated as regular home/end.||No!|
|Alt-minus, Alt-equals||A_MINUS, A_EQUALS||Ok||Ok||Ok on main keyboard, but not on the numeric keypad.|
|Esc||N_ESC||Ok, though there is a short delay before the keystroke registers.||Ok, though there is a short delay before the keystroke registers.||Ok, though there is a short delay before the keystroke registers.|
I'd be happy to hear from anyone with a clear idea on how make this work more consistently, perhaps by using termcap/terminfo, of which I'm completely ignorant.
The situation is different in ncurses -- at least, if using the X-Window system -- because the size of the text console is entirely under the control of the user. The user can resize the text console at will, and the program has little effective control over the resizing.
This presents a difficulty, because Turbo C programs have naturally been written under the suppositions that the text console size has been set by the program itself, and that the text console is not going to be unexpectedly resized during program execution. Yet, these suppositions are both rather dodgy with code that has been ported using the TurboC library.
There are several possible approaches to the problem:
In the latter case, by the way, if the user manually resizes the text console, then your current window (as determined by the windowor textmode function) will automatically be reduced in size (if necessary) to fit the new console size. However, it will not be correspondingly increased in size of the user later enlarges the text console. To override this behavior, you'll need to provide an alternative to the ResizeTurboC function. You'll need to examine the source code for any further explanation.
What makes the first of these two lines "bad" is that the numerical
code which the compiler creates for 'é' is based on the
character sets and languages installed on the computer doing the
compiling, and is very likely not
0x82. So the first form is almost guaranteed not to work
properly. Even so, calling the second form "good" is stretching a
point, because (as it happens), the TurboC library isn't presently able
to display the character 'é' anyway. True Turbo C is able
to display any of the characters in the IBM PC
set -- i.e. the standard printable 7-bit ASCII characters, plus
characters in the numerical ranges 0-31 and 128-255. However, the
library does not fully support this capability in text mode, because
ncurses library does not. A subset of the IBM PC character
is supported, including linedraw characters. (However, all
characters representing double-lines or combinations of double/single
are represented simply in terms of single lines.)
True Turbo C
(Click here to see the program that generated this output.) As you may notice, non-supported characters are rendered as dots. Actually, this can be changed within the ported program to any other character as follows:
/*If this still isn't good enough for you, you can individually control the mapping of every character. For example:
Add this code prior to the FIRST use of any conio function.
The default character cannot be changed after initialization.
The values used are any integer character code recognized by
ncurses (NOT IBM PC character codes).
extern gint DefaultChar;
DefaultChar = ' '; /* or whatever */
Add this code AFTER the first use of textmode.
It will be overridden if placed BEFORE textmode.
extern gint TranslatedChar;
TranslatedChar[0x9d] = 'Y'; /* support YEN as 'Y'. */
In true Turbo C, graphics-mode screens (as opposed to text-mode screens) are handled by something called the Borland Graphics Interface, or BGI for short. The BGI is an API containing 80+ functions. Note that the graphics functions provided with the TurboC library are somewhat less portable than the remainder of the library, in that the X-window system is required. If you need graphics functions that can operate without X, I'd suggest reading the next section.
Some of the more important points of departure from true Turbo C are these:
For example, there is a 2D graphics library called the GRX library ( by Csaba Biegl, Michael Goffioul, and Hartmut Schirmer). The GRX website does not advertise (or even mention) Turbo C compatibility. However, it contains quite a few functions of the same name (and, apparently, the same functionality) as Turbo C library functions. To find out more, download the library using the link above. After unpacking the tarball, you'll find the Borland replacement functions in the src/bgi directory, and information about them in the doc/readme.bgi file.
Disabling the graphics functions present in the TurboC library involves these steps:
To make it easier to deal with this problem, I've provided a number of functions not originally present in Turbo C that can be used to convert endian types where required. Unfortunately, it's still up to you to figure out that there's a problem and to add these function calls to the program.
There are two separate groups of functions, those which convert the
type of an integer value already stored in memory, and those which
data on-the-fly whilst reading/writing a file. All of the
require the TurboC.h header file.
|void FixLittle16 (uint16_t *);||Performs an in-place conversion of a 16-bit integer value stored in memory from little-endian format to the natural CPU endian format, or vice-versa . Using this function twice in succession gets back whatever you started with. There's no harm using this function if the CPU happens to be little-endian, since the value is passed through unchanged in that case.|
|void FixLittle32 (uint32_t *);||Same as FixLittle16, but 32-bit instead.|
|void FixBig16 (uint16_t *);||Same as FixLittle16, but big-endian instead.|
|void FixBig32 (uint32_t *);||Same as FixLittle16, but 32-bit big-endian instead.|
|int ReadLittle16 (FILE *fp, uint16_t *Value);||Reads a 16-bit little-endian integer from a file, and delivers it in the natural endian-format of the CPU. Returns 0 on success, non-zero on failure.|
|int ReadBig16 (FILE *fp, uint16_t *Value);||Same as ReadLittle16, but big-endian instead.|
|int ReadLittle32 (FILE *fp, uint32_t *Value);||Same as ReadLittle16, but 32-bit instead.|
|int ReadBig32 (FILE *fp, uint32_t *Value);||Same as ReadLittle16, but 32-bit big-endian instead.|
|int WriteLittle16 (FILE *fp, uint16_t Value);||Takes a 16-bit integer value in the natural endian format of the CPU, and writes it to a file as a 16-bit little-endian integer. Returns 0 on success and non-zero on failure.|
|int WriteBig16 (FILE *fp, uint16_t Value);||Same as WriteLittle16, but big-endian instead.|
|int WriteLittle32 (FILE *fp, uint32_t Value);||Same as WriteLittle16, but 32-bit instead.|
|int WriteBig32 (FILE *fp, uint32_t Value);||Same as WriteLittle16, but 32-bit big-endian instead.|
#include <conio.h>This program almost compiles with GNU gcc (and the TurboC library), and almost works as-is, except for a couple of points:
time_t t, newt;
int i = 0, x, y;
cprintf ("Hit any key to continue: ");
x = wherex ();
y = wherey ();
// Wait for a keypress, flashing the message
// "I'm waiting!" on and off once per second.
// After any key is hit, display "Hello, world!"
while (!kbhit ())
if (t != newt)
t = newt;
if (0 == (i & 1))
cprintf ("I\'m waiting!");
gotoxy (x, y);
cprintf ("\r\nHello, world!\r\n");
gcc -o Hello Hello.c -lTurboC -lncursesCompiling without warnings is actually a little unusual, because there are quite a few things that can cause non-fatal warning messages. See the trouble-shooting section for more detail.
xterm +sb -e Hello
Summary or illumination of trouble-shooting items covered earlier