Dec 192017
Very good Turbo C debugger. Allows passing of command line parameters. Also allows assembly level debugging.
File TCDEBUG1.ZIP from The Programmer’s Corner in
Category C Source Code
Very good Turbo C debugger. Allows passing of command line parameters. Also allows assembly level debugging.
File Name File Size Zip Size Zip Type
REVLIST 3571 1628 deflated
TCDEBUG.DOC 37917 11951 deflated
TCDEBUG.EXE 74442 43159 deflated

Download File TCDEBUG1.ZIP Here

Contents of the TCDEBUG.DOC file

May 7, 1988

TCdebug Version 1.01

(C) Copyright 1987 by L. David Baldwin.
All Rights Reserved.

Further copyright information given below


TCdebug is an experimental source code debugger for use in debugging Turbo
C (tm) Programs on the IBM-PC (tm). Its goal is to allow source code
debugging in all of Turbo C's memory models and with most of the possible
compile and link options. TCdebug allows you to:

1. View your source code (and, optionally, the assembly language
code) while debugging.
2. Trace the operation of your program by source code lines or by
assembly language instructions.
3. Insert breakpoints in your program by function name, by line
number, or hexadecimal address.
4. Examine (and change) simple global variables by symbolic name.
Local variables may also be examined and changed, but require that
the proper displacement on the stack be known.
5. Set up a Watch window to keep continuous tabs on a variable.
6. Profile your program to find those portions which contribute most
to the execution time.

TCdebug is designed exclusively for debugging Turbo C programs. It should
not be used on programs generated by other compilers.


1. An IBM-PC, AT, or compatible. Screen compatibility is a must.
2. PC-DOS 2.0 or above.
3. Approximately 135k more memory than would normally be required to
run the program to be tested.


TCdebug requires that a Map file for your program be present to supply
information on the program symbols and line number locations. When
compiling and linking with TC, the options can be set with O/C/C/L and
O/L/M/P. With TCC use -M and -y.


It is also highly desirable that the program to be tested be compiled with
the 'standard stack frame' option on. Using TC, set this option with
O/C/C/S. With TCC, use -k (The manual says -Y but that's apparently a

When starting a debugging session, the following files should be on the
default drive:

PROG.EXE (or, optionally, .COM for the Tiny Model)
PROG.MAP the map file

Any C source files which will be accessed during the debugging session
should also be on the default drive or on Paths specified in the DOS PATH.


TCdebug may be started by entering the filename of the program to be
debugged and any parameters which it requires on the command line, as:

TCdebug PROG

Here, PROG is the name of the program to be tested with EXE being the
default extension. will be passed on to PROG as its command
line when execution begins. If TCdebug is called without parameters, a
request will be made for them.

When execution begins, TCdebug loads the Map file, the EXE file and reports
some facts about the program. Note should be made of whether pointers are
regarded as Near or Far by default as this will be important when
displaying pointers later.

After "hitting any key", the program is executed to "main()" and the
debugging screen appears. The debugging screen is divided into two parts.
The lower section is the command and data section. The '*' is a prompt for
one of the commands described later. The top part of the screen displays
the program source code text which at this point will be centered at the
first statement in main(). Source code line numbers appear at the left
with the current line (the one to be executed next) being highlighted.

Dividing the two portions of the debugging screen is a line which shows the
name of the source file presently displayed.

Two keys which can be tried at this point are the F9 and F10 keys. The
F9 key toggles into and out of the assembly mode. When in assembly mode,
the assembly language instructions of the program are displayed with the
source code lines interspersed. The F10 key toggles between the debugger
screen and the screen which receives program output.



The following keys may be used to change the text being displayed:

Pg Up
Pg Dn Move the display up or down one page.

^Pg Up
^Pg Dn Move to the start or end of the current file.

Up arrow
Down arrow Scroll up or down one line.

^Left Arrow (with Scroll Lock on)
^Right Arrow (with Scroll Lock on)
Scroll horizontally left and right.

F9 key Toggle in and out of assembly mode.

F10 key Toggle between the debug screen and the screen
of the program under test.

Alt-U Move dividing line up one line.
Alt-D Move dividing line down one line.

Note that the toggling of the Scroll Lock key effects the action of some of
the special keys. The text display may also be changed using the View
command described later.


Commands are entered at the '*' prompt. When entering commands, the
following keys may used to edit the command: (They perform the same
functions as they do in the Turbo C editor.)

Ins, Del, Backspace

The Insert mode (default) is indicated by a slightly fatter cursor. The
cursor is normal in the Overwrite mode.

In addition, a stack of the last 6 commands (those having more than 3
characters) is maintained. The F3 key accesses the stored commands from
the most recent to the oldest and the F5 key accesses them in the reverse
order. These old commands may be used as is or may be modified by editing

Note that commands are not acted upon until the Enter key is hit. (The
cursor does not have to be at the line end to enter the command.)



TCdebug uses one or two letter commands. The command is often followed by
one or more parameters. Parameters are delimited from the command and each
other using spaces. The one exception to this is the format parameter
which is preceded by a comma. The parameters which may be used are:

A C source code symbol representing a function name or variable as
appropriate. The leading underbar added by the compiler has been
stripped off, so symbols are entered exactly as used in the source
program. Case is significant (unless the non case sensitive link
option was selected).

The special symbols, _AX, _AL, _BX, etc. may be used to refer to the
hardware registers.

A special symbol, rtn (or RTN), refers to the return address of the
current function. For this symbol to function reliably, the program
should be compiled with the 'standard stack frame' option on.
Currently, this symbol can only be accessed if the current CS:IP is on
an exact source line number.

V rtn Find out who called this function.
G RTN Execute the remainder of this function, return to caller.

Line numbers are entered as \ as:

In most cases, the part can be dropped. An entry of the


implies line number 345 in the currently displayed source file.
(Currently, the '\' also may be dropped and a simple decimal entry will
do. However, the '\' may be required in future versions.)

Normal numerical entry in TCdebug is decimal with hex entries allowed
by preceding them with '0x' or '$'. However, numerical address
entries must be entered in hex to distinguish them from line numbers.
A takes the following form:



As indicated above, register names are allowed in address
specifications. The _current_ value in the register is used--the
address does not change if the register value later changes. The
segment part of the may be dropped as in:


If no segment is present in the entry, there is an implied segment.
The rules for determining the implied segment are:

1. If the BP register is used, SS is implied.
2. If BX,SI,DI are used (but not BP), DS is implied.
3. If none of the above and a data entry is expected, DS is implied.
4. If a code address is expected, the current CS is implied.

Format parameters are entered as one or two letters preceded by a
comma. Case is significant. The purpose of the parameter is
to inform TCdebug of the size and type of a variable as this
information is not available in the Map file. parameters
correspond to those used in the C 'printf' function. Currently
supported are:


For the p (pointer) format, the default pointer size for the selected
memory model is used. This size is indicated in the information
presented at startup. To override the default, use either N or F as

The c (character) format has been used as a catchall for byte size
items. Display is in quoted character form, decimal, and hex. Change
entries may be made using any of those forms.


G (Go)

G [ [
The Go command starts execution of the program under test. Execution
will continue until a breakpoint is reached or the program terminates.

One or more breakpoints may be entered with the Go command. These
breakpoints are called temporary breakpoints as they are in effect only
until the first stopping point is reached. Any temporary breakpoint
would then have to reentered with the next Go command if desired.



G Start execution with no temporary breakpoints. (There might be
some permanent breakpoints, however.)

G \345 funct1 0x113
Start execution with temporary breakpoints at line number 345, at
the start of function, funct1, and at address CS:0x113. In
addition, there might be other permanent breakpoints in effect.

T (Trace)

T (or F7 key)

In source code mode, execute the code on the current line. Execution
will stop at the next encountered line number. If the current line
contains a function call, the break will be at the start of the

In assembly mode (assembly code is displayed), execute the next assembly
language instruction.

N (Next)

N (or F8 key)

In either the source code or assembly mode, Next will execute the
program, stopping at the next line number.

The difference between the Trace and the Next command is that, if the
current line contains a function call, Next will completely execute the
function whereas Trace will Trace through the function.

P (Permanent breakpoint)

[-]P [] []

The P command is used to specify permanent breakpoints. Unlike temporary
breakpoints, permanent breakpoints remain in effect until removed (using
a '-' preceding the P command).

If no parameter is entered, the breakpoints in effect are listed.

An optional pass count may be included when adding a breakpoint. The
pass count indicates how many times the breakpoint will be passed
before the program is actually interrupted.



P List all permanent breakpoints.
-P Delete all permanent breakpoints.
P moda.c\345 Install a breakpoint at line 345 in file moda.c.
-P funct1 Remove a breakpoint at function funct1.
P 0x34b 10 Install a breakpoint at CS:034B. The break will
occur at the 11th execution of the instruction.

BE, BC (Conditional Breakpoints)

BE , (Break when Equal to value)
BC , (Break when there is a Change)
B (List conditional breakpoints)
-B [] (delete one or more conditionals)

Conditional breakpoints allow TCdebug to interrupt program execution
whenever it finds that a variable has changed or becomes equal to a
specified value. TCdebug checks each one of the conditional breakpoints
whenever it regains control of the program. Normally, when using
conditional breakpoints, the GT (Go in Trace mode) command should be used
to allow the variable checks to be made at each source line.

String variables are compared over the first 10 characters.


BC str,s Break when the string variable, str, changes.
BE *np,i 39 Break when the integer pointed to by np equals 39.
-B2 Delete conditional breakpoint #2.

GT (Go in Trace mode)

GT [ [

The GT command is a special version of the G (Go) command for use with
conditional breakpoints. Both commands have the same syntax. When the
GT command is used, the execution of the program is interrupted briefly
at each source line so that the specified conditions can be checked.

Because of the extra overhead involved with the GT command, program
execution is very much slower than with the G command.

GT \334
Start execution of the program stopping at the first occurrence of:
line 334, reaching a permanent breakpoint, or one of the
conditions specified by a conditional breakpoint.


V (View text)

V []

The View command may be used to change the text being observed in the
text window. If no parameter is entered, the text will be centered
around the current line.

In assembly mode, code for which there is no source may be viewed by
specifying a hex address with the V command.


V initscreens View text at function initscreens.
V \545 View text around line 545.
V 0x1234:0xABCD Display instructions at 1234:ABCD (Assembly mode)

VF (View File)


The View command is for viewing the program source files. The VF
command allows any ASCII file to be displayed.

E (Examine/change variable)

E | ,

The Examine/change command allows the the value of a simple variable to
be displayed and optionally changed. Global variables may be referred to
symbolically. Auto and static variables must be referred to by a
. Pointers may be resolved using one or more '*'s preceding
the symbol or address. Single dimensioned arrays may be examined using
the normal '[..]' to enclose the array index. indicates the
type of display wanted.

After a variable value is displayed in response to the Examine/change
command, a new value may be optionally entered followed by the Enter key.
The Enter key by itself is used if no new value is desired.

String variables are displayed as a string within quotes. When
specifying a change to a string variable, the new string should be
enclosed in double quotes. A null string is specified by two quotes
enclosing nothing.



E flt,g
E dble,lg
E *st,c
E es:bx,u
E bp-0x52, s
E s[22],c
E *pt[3],i the 4th element of the integer array pointed to by pt.

When referring to registers, note that there is a difference between the
register symbols with underbars and those without the underbars, so that:

E ds:bx,x Examine/change the hex integer at the address ds:bx.
E bx,x The same thing (DS is implied).
E _BX,x Examine/change the BX register.

W Watch variable

W | ,
[-]W []

The Watch command opens up a watch window so that the specified variable
is displayed continuously. The value displayed is updated whenever
TCdebug regains control. The syntax for watch variable entry is
identical to that used with the Examine command.

When watch variables are displayed, they are given a number. The number
is useful for selectively deleting them using the -W command.

W var1, u Watch the unsigned variable, var1
W *bp-4,c Watch the char variable pointed to by SS:BP-4
W _AL,c Watch the AL register
-W 1 Delete watch variable #1
-W Delete all watch variables

D (Display Memory)

D | ,


The Display command opens up a three line display window displaying
memory contents at the address given by in the format given
by . This display window may be scrolled using the following
keys when Scroll Lock is toggled on:


Up Arrow
Down Arrow Move up or down one line (Scroll Lock on)

Pg Up
Pg Dn Move up or down two lines (Scroll Lock on)


D ptrs,Fp Display memory at ptrs in far pointer format.
D dbles,lG Display memory at dbles in double format.
D *es:bx,s Display memory as pointed to by es:bx in string or
character format.
-D Close the display window.

DB (Display Bytes)

DB | [, ]

The Display Bytes command produces the customary debug data display
showing memory in both hex bytes and ASCII characters. The display
always starts on an even paragraph with the byte at the specified address

An optional may be entered with the command so that the offset
of subscripted variables may be determined, as:

DB abc[1000],i

will start the display at abc+2000.

X (Translate symbols to addresses or addresses to symbols)


The X command allows the address associated with a symbol to be
determined or vice versa.

R (Register Display)


The Register display command displays the current machine register


RE (Restart program under test)


Reloads a fresh version of the test program and restarts it from the
beginning. Breakpoints and watch variables remain in effect.

WD (Window Divide)

WD [n]

The Window Divide command allows the line dividing the screen to be moved
up or down. n is the desired line position. If n is not entered, the
line position is not changed but the screen is refreshed.

The line dividing the screen may also be moved one line at a time using
Alt-U (Up) or Alt-D (Down).

TA (Tab Size)

TA n

Text displayed in the text window has tabs expanded to eight spaces by
default. The TAb command allows the expansion to be changed.

Q (Quit)


The Quit command ends debugging without running the program under test to
completion. However, the program's exit() routine is executed, so that
files are closed, etc. You will be asked if you wish to restart the
program. (Use F10 to check the program screen at this time if desired.)

DG (DebuG)


This command is used to enter an optional external assembly language
debugger. It is used mainly for debugging TCdebug.



When tracing in assembly mode and returning to source mode, it often
happens that the current address may be between two source lines. In these
cases, the highlight bar will be on the line that is partially executed but
will not extend over the line number. If the current address is at the
start of the line, the highlight will extend over the line number.

It is possible to insert breakpoints in the source code using
geninterrupt(3). These breakpoints cannot be removed by TCdebug but may be
useful for conditional breaks. For instance:

if (i==49) geninterrupt(3);

If you mistakenly Trace into a function you wanted to skip using Next, use
G rtn. Actually, it's best to do a V rtn first to make sure that the 'rtn'
symbol is working correctly.

If, in the assembly mode, you trace into a library routine and can't find
your way out, toggle out of assembly mode (F9). Even if the source code
screen is blank at this time, a single Trace will get you to the next line

Avoid assembly tracing of the 8087 emulation code.

Auto (stack) variables can be Examined or Watched by using the disassembly
listing to determine their position on the stack relative to the BP
register. When doing this, be sure that the BP register is valid. In
particular, the BP register is not valid immediately on entry to the
function. One source line trace must be made (or several assembly
instructions passed) before the BP register is correct.

The Huge memory model has the same sort of problem with the DS register
which changes between modules. In this case, it only effects the
disassembly listing. The data symbols will not be correctly listed until
the DS register has been setup.


When a program runs slow, it's often due to a relatively small portion of

code. The trick is to determine which parts of the program contribute most
to the execution time so that the programmer doesn't waste time refining
the wrong code. TCdebug's profiler provides a way to make that

The profiler divides up a portion of the memory where the program resides
into approximately 128 equal parts (or 'bins'). As the program executes,
it is periodically interrupted using the PC's timer interrupt. At each
interrupt, TCdebug determines the address of the interrupted instruction
and increments a count for the appropriate bin. At the end of the run, the


counts per bin then show the relative time of execution for that part of
the code.


PS (Profiler Setup)


The Profiler Setup command is used to specify which parts of memory are
to be profiled and what the counting rate should be. On issuing the PS
command, the user is asked to answer the following:

1. Starting segment or address (or 'PROGRAM')?
2. Ending segment or address?
3. Counts per second (40-4000)?

The first two questions determine the memory address range where counts
will be recorded. This memory range is divided into approximately 128
equal parts. In some cases, there may fewer than 128 bins created since
the bin sizes must all be equal and are sized in multiples of 16 bytes.
When answering the first two questions, either an address (two hex
numbers separated by a colon) or a segment (hex number without a colon)
may be entered.

An optional response to the first query is the word "PROGRAM". If this
entry is used, TCdebug will set the address limits to that actually used
by the program code.

The counting rate may be set between 40 and 4000 counts per second. A
rate of 1000 is appropriate in most cases.

Note that the Profiler Setup command does not turn the profiler on. The
PO command must be used for that purpose.

PO (Profiler On/Off)

PO 0 (Turn profiler off)
PO 1 (Turn profiler on)
PO (Request profiler status)

When the appropriate point in the program is reached, the profiler is
turned on. Counting then commences with the next 'G' (Go) command and
ceases when the debugger is reentered either at a breakpoint or when the
program terminates. The ability to turn the profiler on and off is
useful to avoid timing unimportant events such as keyboard wait loops.


PR (Profiler Reset)


The Profiler Reset command resets the counts to zero without changing the

PD (Profiler Display)

PD [] (Display profiler results on debugging screen)
PDP [] (Output profiler results to printer)

The Profiler Display command outputs the profiler results in tabular form
either to the screen or to the printer. An optional percentage may be
entered. Bins with count percentages below the value entered will not be
displayed. If no percentage is entered, a default of 0.5% is used as the
cutoff. To see all the counts, enter PD 0 or PDP 0.

Figure 1 shows the display from a profiling run. The 'Total Count' of
765 is the actual number of interruptions to the executing program, and
'Total Count in Specified Interval' of 761 shows that almost all of the
hits occurred in the memory interval specified in the setup (the
"PROGRAM" option was used here). The various column headings are:

The bin number, 0..128.
Address Range
The segment and offset range corresponding to that block. There
are two 'Address Range' columns since it is possible to have a
segment overlap within a block.
Program Loc
The first line number or symbol found in the block. This helps
locate the code for that block.
The count recorded for that block.
% Intvl
The count as a percentage of the 'Count in Specified Interval'.
% Total
The count as a percentage of the 'Total Count'.

The data shown in Figure 1 is a profile of the function that displays one
screen of disassembly listing on an earlier version of TCdebug. The
profiler was turned on at the start of the routine (located between lines
SCREEN.C\377 and 419) and a 'G RTN' issued to time one screenfull. At
1000 counts/sec, it took 0.76 seconds to change the screen. One might
expect that the disassembler (block 64) would be using most of the time
but actually about 80% of the time is spent in blocks 13 and 14. A
further profile run narrowing in on that part of memory showed the
problem to be the line number and symbol look up functions. These are


called for each line displayed to see if a source code line should be


Summarizing the profiler use:

1. Use the PS command to set the address range and the counting rate.
Often the best way to start is to examine all of memory using a
starting segment of 0x0 and an ending segment of 0xFFFF. While this
won't yield any detail (each block is 8k), it will tell how much time
is spent in the program, how much in DOS and how much in ROM bios.

2. Set a breakpoint at the point profiling is to start and execute to
that point. Omit this step if the whole program is being timed.

3. Turn the profiler on.

4. Set a breakpoint where the profiling is to stop, execute the program
to that point, and turn the profiler off. Note that the program must
be executed in real time (the G command)--the profiler does not
operate in any of the trace modes.

5. Display the results.

6. Repeat the above steps narrowing in on the portions of memory where
the action is to get more detail.

A couple of caveats. Because the profiler uses the timer interrupt, it
won't work in sections of code where the interrupts are turned off. If
interrupts are turned off for a fairly long time, counts will tend to
pile up at those instructions where the they're turned back on. Also,
the PC's timer is sped up considerably during profiling. TCdebug
attempts to keep the system clock operating normally, but this can't
always be done exactly. It's probably a good idea to check the system
clock after a profiling session.


The following are current limits (somewhat arbitrarily set).

Permanent and temporary breakpoints are limited to 5 each.
Maximum file size for displayed files = 64k.
Maximum number of line numbers (only those with code count) = 3000.
Maximum number of program modules accommodated = 20.
Maximum number of symbols = 700.
Maximum number of watch variables = 4
Maximum number of conditional breakpoints = 3

A simple screen swap is currently used. This limits debugging to text only


The map file for the Tiny memory model has some peculiarities in data
addresses which affect a number of library data symbols and one or two
source symbols. The problem shows up in the assembly listing where the
affected symbols are not correctly displayed.

The 8087 emulation code turns into a mess when disassembly is attempted.

Currently, non default pointers cannot be resolved by the 'E' or 'W'
commands. That is:

E *st,c

will always resolve st as a far pointer if the default is far.

The map file generated by Turbo C does not correctly handle #include files
which generate code. This shows up as incorrect line number information
for modules using such include files. This problem does not apply to the
normal header include files as they don't generate code.


Documentation and Program (C) Copyright 1987 by L. David Baldwin.
All Rights Reserved.

TCdebug may be copied and distributed freely by individuals to friends and
acquaintances providing that no fee is charged and it is not part of a
package for which a charge is made.

Please do not upload TCdebug to any bulletin board unless you are willing
to keep it updated and report back problems.


IBM-PC is a trademark of International Business Machines
Turbo C is a trademark of Borland International Inc.

Please report all bugs, suggestions, and problems to Dave
Baldwin, CompuServe ID #76327,53.



As mentioned earlier, it is mandatory that the map file generation and line
number options be turned on. The effect of some of the other compiler
options on debugging are discussed below.

Standard Stack Frame
This option is highly desirable as the BP register must be set up in
order for the 'rtn' symbol to work. If this option is not used, any
function which has many auto variables will still have a standard stack
frame, so it is still possible to use 'rtn' in some cases. The safest
method is to do a V rtn or X rtn first to see if the return address makes
sense before committing yourself to a G rtn.

Register Optimization
Borland suggests off. The assembly language listing may be somewhat more
difficult to understand using this option.

Jump Optimization
Same as register optimization.

80186/80286 Code Generation
Off is best here. The disassembler in TCdebug currently does not
understand the new instructions.

Use Register Variables
When this option is on, you may have to figure out which variables the
compiler decided to store in registers.

8087 vs Emulation
If you have a choice, by all means select straight 8087. The emulated
code does not disassemble in any meaningful way.

Non Case Sensitive Link
If this option is selected, symbols will appear in uppercase. Symbol
entries may be made in either case.



With modern compilers, line numbers often do not correspond to the code
generated in the manner that you might expect. Layout of the source code
can help in this respect. While you probably don't want to reformat all
your present code, you may want to change your formatting for code yet to
be written.

The most obvious format change is to only have one source statement per
line. With more than one statement on a line, line numbers can't be used
to set a breakpoint on the second statement and the Trace command will
trace multiple statements.

Statements split between two lines do not cause much trouble.

The 'else' in an 'if' statement causes a difficulty. Consider the
following code:

100 if (...)
101 { something..}
102 else { something else..}

To put a breakpoint on the something else.. one might try a break on
line 102. But the else here actually generates a jump instruction (to line
103) so something else.. is inaccessible except by using a code
hexaddress. A better format is:

100 if (...)
101 { something..}
102 else
103 { something else..}

The 'for' statement also causes problems. The decision code for the 'for'
statement is actually done at the bottom of the loop and often is included
as part of the next line where you would normally think the statement was
completed. The best solution here is to add a blank line after the 'for'
statement loop.

In short, it's best to use plenty of vertical spacing and blank lines.
Also take a peek at the assembly code to see what's going on.


128 Intervals of 28 Paragraphs (448 Bytes) Each
Total Count = 765
Total Count in Specified Interval = 761 ( 99.5%)
Block Address Range Address Range Program Loc Count % Intvl % Total
13 327D:16C0-187F LOADSYM.C\53 127 16.7 16.6
14 327D:1880-1A3F LOADSYM.C\104 483 63.5 63.1
34 327D:3B80-3D3F BREAKS.C\785 1 0.1 0.1
36 327D:3F00-40BF SCREEN.C\139 1 0.1 0.1
37 327D:40C0-427F SCREEN.C\210 17 2.2 2.2
40 327D:4600-47BF SCREEN.C\366 2 0.3 0.3
41 327D:47C0-497F SCREEN.C\404 1 0.1 0.1
52 327D:5B00-5CBF SCREEN.C\1056 15 2.0 2.0
64 327D:7000-71BF UNASDB.C\868 74 9.7 9.7
81 327D:8DC0-8F7F getcwd 1 0.1 0.1
82 327D:8F80-913F intdos 2 0.3 0.3
85 327D:94C0-967F _LoadProg 3 0.4 0.4
86 327D:9680-983F ultoa 29 3.8 3.8
99 327D:AD40-AEFF VPRINTER 2 0.3 0.3
100 327D:AF00-B0BF 1 0.1 0.1
101 327D:B0C0-B27F write 2 0.3 0.3

Figure 1
Profiler Display


 December 19, 2017  Add comments

Leave a Reply