                     TURBO DEBUGGER FOR WINDOWS
                     Assembler-level debugging

This file contains information about Assembler-level debugging.  The
contents of the file are as follows:

1.  When source debugging isn't enough
2.  The CPU window
3.  The Code Pane
4.  The Register and Flags panes
5.  The Selector pane
6.  The Data pane
7.  The Stack pane
8.  The assembler
9.  The Dump window
10. The Registers window
11. Assembler-specific bugs
12. Inline assembler tips
13. Inline assembler keywords

The material in this file is for programmers who are familiar with
programming the 80x86 processor family in assembler. You don't need
to use the information in this chapter to debug your programs, but
there are certain problems that might be easier to find using
techniques discussed in this chapter.

We explain when you might want to use assembler-level debugging and
describe the CPU window with its built-in disassembler and
assembler. You then learn how to examine and modify raw hex data
bytes, how to peruse the function calling stack, how to examine and
modify the CPU registers, and finally how to examine and modify the
CPU flags.



===================================================================
1. When source debugging isn't enough
===================================================================

When you're debugging a program, most of the time you refer to data
and code at the source level; you refer to symbol names exactly as
you typed them in your source code, and you proceed through your
program by executing pieces of source code.

Sometimes, however, you can gain insight into a problem by looking
at the exact instructions that the compiler generated, the contents
of the CPU registers, and the contents of the stack. To do this,
you need to be familiar with both the 80x86 family of processors
and with how the compiler turns your source code into machine
instructions. Because many excellent books are available about the
internal workings of the CPU, we won't go into that in detail here.
You can quickly learn how the compiler turns your source code into
machine instructions by looking at the instructions generated for
each line of source code.

C++ lets you write lines of source code that perform many actions
at once, and TDW lets you step one source line at a time, not one
expression at a time. However, you sometimes want to know the
result of executing a small piece of one source line. By stepping
through your program one machine instruction at a time, you can
examine intermediate results, although it does require some effort
to figure out how the compiler translated your source statements
into machine code.



===================================================================
2. The CPU window
===================================================================

The CPU window shows you the entire state of the CPU. You can
examine

o examine and change the bits and bytes that make up your program's
  code and data

o access the contents of any area of memory referenced by a
  selector

o use the built-in assembler in the Code pane to patch your program
  temporarily by entering instructions exactly as you would type
  assembler source statements

o access the underlying bytes of any data structure, display them
  in a number of formats, and change them

Open a CPU window by choosing View|CPU from the menu bar. Depending
on what you're viewing in the current window, the new CPU window
comes up positioned at the appropriate code, data, or stack
location, thus providing a convenient method for taking a "low-
level" look at the code, data, or stack location your cursor is
currently on.

The following table shows where your cursor will be positioned when
you choose the CPU command:


___________________________________________________________________

  Current window        CPU pane             Position
___________________________________________________________________

  Stack window          Stack                Current SS:SP
  Module window         Code                 Current CS:IP
  Variable window       Data/Code            Address of item
  Watches window        Data/Code            Address of item
  Inspector window      Data/Code            Address of item
  Breakpoint            Code                 Breakpoint address
    (if not global)
____________________________________________________________________


The CPU window has six panes. To go from one pane to the next,
press Tab or Shift-Tab, or click the pane with your mouse. The line
at the top of the CPU window shows what processor type you have
(8086, 80286, 80386, or 80486).

o The top left pane (Code pane) shows the disassembled program code
  intermixed with the source lines.

o The second top pane (Register pane) shows the contents of the CPU
  registers.

o The top right pane (Flags pane) shows the state of the eight CPU
  flags.

o The middle left pane (Selector pane--below the Code pane) shows
  all Windows selectors and indicates the general contents of each.

o The bottom left pane (Data pane--below the Selector pane) shows a
  raw hex dump of any area of memory you choose.

o The bottom right pane (Stack pane) shows the contents of the
  stack.

In the Code pane, an arrow shows the current program location
(CS:IP). In the Stack pane, an arrow shows the current stack
pointer (SS:SP).

If the highlighted instruction in the Code pane references a memory
location, the memory address and its current contents are displayed
on the top line of the CPU window. This lets you see both where an
instruction operand points in memory and the value that is about to
be read or written over.

As with all windows and panes, pressing Alt-F10 pops up the pane's
local menu. If control-key shortcuts are enabled, pressing the Ctrl
key with the highlighted letter of the desired local menu command
executes the command.

In the Code, Data, and Stack panes, you can press Ctrl-LeftArrow
and Ctrl-RightArrow to shift the starting display address of the
pane by 1 byte up or down. Pressing these keys is easier than using
the Goto command if you just want to adjust the display slightly.



===================================================================
3. The Code pane
===================================================================

This pane shows the disassembled instructions at an address that
you choose. An arrow shows the current program location (CS:IP).

There are two ways of choosing an address:

o Use the local menu Goto, Origin, Follow, Caller, or Previous
  command.

o Position on a code selector in the Selector pane, then choose
  Examine to display the contents of the selector in the Code pane.

The left part of each disassembled line shows the address of the
instruction. The address is displayed either as a hex segment and
offset, or with the segment value replaced with the CS register
name if the segment value is the same as the current CS register.
If the window is wide enough (zoomed or resized), the bytes that
make up the instruction are displayed. The disassembled instruction
appears to the right.

If the highlighted instruction in the Code pane references a memory
location, the memory address and its current contents are displayed
on the top line of the CPU window. This feature lets you see both
where an instruction operand points in memory and the value that is
about to be read or written over.


The disassembler
================

The Code pane automatically disassembles and displays your program
instructions. If an address corresponds to either a global symbol,
static symbol, or a line number, the line before the disassembled
instruction displays the symbol if the Mixed display mode is set to
Yes. Also, if there is a line of source code that corresponds to
the symbol address, it is displayed after the symbol.

Global symbols appear simply as the symbol name. Static symbols
appear as the module name, followed by a cross hatch (#) for C or
Assembler or a period (.) for Pascal, followed by the static symbol
name. Line numbers appear as the module name, followed by a cross
hatch (#) for C or Assembler or a period (.) for Pascal, followed
by the decimal line number.

When an immediate operand is displayed, you can infer its size from
the number of digits: A byte immediate has 2 digits, and a word
immediate has 4 digits.

TDW can detect an 8087, 80287, 80387, or 80486 numeric coprocessor
and disassemble those instructions if a floating-point chip or
emulator is present.

The instruction mnemonic RETF indicates that this is a far return
instruction. The normal RET mnemonic indicates a near return.

Where possible, the target of JMP and CALL instructions is
displayed symbolically. If CS:IP is a JMP or conditional jump
instruction, an up-arrow or down-arrow that shows jump direction
will be displayed only if the executing instruction will cause the
jump to occur. Also, memory addresses used by MOV, ADD, and other
instructions display symbolic addresses.


The Code pane local menu
========================

If you don't come up in the Code pane, use Tab or Shift-Tab to get
there. Then press Alt-F10 to bring up the local menu.
 ________________
| Goto...          |
| Origin         |
| Follow         |
| Caller         |
| Previous       |
| Search...        |
| View source    |
| Mixed      Yes |
|________________|
| New cs:ip      |
| Assemble...      |
| I/O            |
|________________|

Goto
----

After choosing this command, you're prompted for the new address to
go to. You can enter addresses that are outside of your program, to
examine code in the BIOS ROM, inside DOS, and in Windows programs.
See Chapter 9 in the debugger manual for complete information on
entering addresses.

The Previous command restores the Code pane to the position it had
before the Goto command was issued.


Origin
------

Positions you at the current program location as indicated by the
CS:IP register pair. This command is useful when you want to return
to where you started.

The Previous command restores the Code pane to the position it had
before the Origin command was issued.


Follow
------

Positions you at the destination address of the currently high-
lighted instruction. The Code pane is repositioned to display the
code at the address where the currently highlighted instruction
will transfer control. For conditional jumps, the address is shown
as if the jump occurred.

This command can be used with the CALL, JMP, conditional jump (JZ,
JNE, LOOP, JCXZ, and so forth) and INT instructions.

The Previous command restores the Code pane to the position it had
before the Follow command was selected.


Caller
------

Positions you at the instruction that called the current interrupt
or subroutine.

This command won't always work. If the interrupt routine or
subroutine has pushed data items onto the stack, sometimes TDW
can't figure out where the routine was called from.

The Previous command restores the Code pane to the position it had
before the Caller command was selected.


Previous
--------

Restores the Code pane position to the address before the last
command that explicitly changed the display address. Using the
arrow keys and PgUp and PgDn does not cause the position to be
remembered.

When you choose Previous, the Code pane position is remembered, so
that repeated use of the Previous command causes the Code pane to
switch back and forth between two addresses.


Search
------

Lets you enter an instruction or byte list to search for. Enter an
instruction exactly as you would with the Assemble command.

Be careful which instructions you try to search for; you should
only search for instructions that don't change the bytes they
assemble to, depending on their location in memory. For example,
searching for the following instructions is no problem:

  PUSH  DX
  POP   [DI+4]
  ADD   AX,100

but searching for the following instructions can cause unpre-
dictable results:

  JE    123
  CALL  MYFUNC
  LOOP  100

You can also enter a byte list instead of an instruction. See
Chapter 9 in the debugger manual for more on entering byte lists.


View Source
-----------

This command opens a Module window to show you the source code that
corresponds to the current disassembled instruction. If there is no
corresponding source code (for example, you're in Windows code, or
there's no debugging information), you just stay in the Code pane.


Mixed
-----

Toggles between the three ways of displaying disassembled instruc-
tions and source code:

No     No source code is displayed, only disassembled instructions.

Yes    Source code lines appear before the first disassembled
       instruction for that source line. The pane is set to this
       display mode if your current module is a high-level language
       source module.

Both   Source code lines replace disassembled lines for those lines
       that have corresponding source code; otherwise, the disassem-
       bled instruction appears. Use this mode when you are debugging
       an assembler module, and you want to see the original source
       code, instead of the corresponding disassembled instruction.
       The pane is set to this display mode if your current module is
       an assembler source module.

New CS:IP
---------

Sets the program location counter (CS:IP registers) to the current-
ly highlighted address. When you rerun your program, execution
starts at this address. This is useful when you want to skip over a
piece of code without executing it.

Use this command with extreme care. If you adjust the CS:IP to a
location where the stack is in a different state than at the
current CS:IP, your program will almost certainly crash. Do not use
this command to set the CS:IP to an address outside the current
routine.


Assemble
--------

Assembles an instruction, replacing the one at the currently
highlighted location. You are prompted for the instruction to
assemble. See the section "The assembler" later in this file for
more details.

You can also invoke this command by simply starting to type the
statement you want to assemble. When you do this, a dialog box
appears exactly as if you had specified Assemble.


I/O
---

Reads or writes a value in the CPU's I/O space and lets you examine
the contents of I/O registers on cards and write to them.

It pops up this menu:
 _____________
| In byte...    |
| Out byte...   |
| Read word...  |
| Write word... |
|_____________|

    In Byte
    -------

    Reads a byte from an I/O port. You are prompted for the I/O port
    whose value you want to examine. Use the Read Word option to read
    from a word-sized I/O port.

    Out Byte
    --------

    Writes a byte to an I/O port. You are prompted for the I/O port to
    write to and the value you want to write. Use the Write Word option
    to write to a word-sized I/O port.

    Read Word
    ---------

    Reads a word from an I/O port. You are prompted for the I/O port
    whose value you want to examine. Use the In Byte option to read
    from a byte-sized I/O port.

    Write Word
    ----------

    Writes a word to an I/O port. You are prompted for the I/O port to
    write to and the value you want to write. Use the Out Byte option
    to write to a byte-sized I/O port.

IN and OUT instructions access the I/O space where peripheral
device controllers (such as serial cards, disk controllers, and
video adapters) reside.

Be careful when you use these commands. Some I/O devices consider
reading their ports to be a significant event that causes the
device to perform some action, such as resetting status bits or
loading a new data byte into the port. You may disrupt the normal
operation of the program you are debugging or the device with
indiscriminate use of these commands.



===================================================================
4. The Register and Flags panes
===================================================================

The Register pane, which is the top pane to the right of the Code
pane, shows the contents of the CPU registers.

The top right pane is the Flags pane, which shows the state of the
eight CPU flags. The following table lists the different flags and
how they are shown in the Flags pane:

________________________________________________

   Letter in pane             Flag name___

      c                         Carry
      z                         Zero
      s                         Sign
      o                         Overflow
      p                         Parity
      a                         Auxiliary carry
      i                         Interrupt enable
      d                         Direction___
________________________________________________

The Register pane local menu
============================

Press Alt-F10 to pop up the Register pane local menu. Or, if
control-key shortcuts are enabled, use the Ctrl key with the first
letter of the desired command to access the command.
 _______________________
| Increment             |
| Decrement             |
| Zero                  |
| Change...               |
| Registers 32-bit   No |
|_______________________|

Increment
---------

Adds 1 to the value in the currently highlighted register. This is
an easy way to make small adjustments in the value of a register to
compensate for "off-by-one" bugs.


Decrement
---------

Subtracts 1 from the value in the currently highlighted register.


Zero
----

Sets the value of the currently highlighted register to 0.

Change
------

Changes the value of the currently highlighted register. You are
prompted for the new value. You can make full use of the expression
evaluator to enter a new value.

You can also invoke this command by simply starting to type the new
value for the register. A dialog box appears exactly as if you had
specified Change.


Registers 32-bit
----------------

On an Intel 80X86 32-bit processor (80386 or later), toggles
between displaying the CPU registers as 16-bit or 32-bit values.
You usually see 16-bit registers, unless you use this command to
set the display to 32-bit registers. You really need to see 32-bit
registers only if you're debugging a program that uses the 32-bit
addressing an arithmetic capabilities of the 386 chip. If you are
debugging a program that uses only 16-bit addressing and
arithmetic, use the 16-bit register display.


The Flags pane local menu
=========================
       ________
      | Toggle |
      |________|

Press Alt-F10 to pop up the Flags pane local menu or, if control-
key shortcuts are enabled, use the Ctrl key with the first letter
of the desired command to access the command.


Toggle
------

Sets the value of the flag to 0 if it was 1, and to 1 if it was 0.
The value 0 corresponds to "clear," and 1 indicates "set." You can
also press Enter or Spacebar to toggle the value of the currently
highlighted flag.



===================================================================
5. The Selector pane
===================================================================

This pane shows a list of protected-mode selectors and indicates
some information about each one.

A selector can be either valid or invalid. If valid, the selector
points to a location in the protected-mode descriptor table
corresponding to a memory address. If invalid, the selector is
unused.

For a valid selector, the pane shows the following:

o if the contents are data or code

o if the selector is loaded (present in memory) or unloaded
  (swapped out to disk)

o the length of the referenced memory area in bytes

o the access mode of the memory area (blank; Read/Write, Up;
  others????)

If the selector references a data segment, there's additional
information on the access rights (Read/Write or Read only) and the
direction the segment expands in memory (Up or Down).


The Selector pane local menu
============================

At the Selector pane, press Alt-F10 to pop up the local menu or, if
control-key shortcuts are enabled, use the Ctrl key with the
highlighted letter of the desired command to access the command.

You can use the local menu of the Selector pane to go to a new
selector (the Selector command) or see the contents of the selector
currently highlighted in the Selector pane (the Examine command).
The contents display in either the Code pane or the Data pane,
depending on their nature.
 ____________
| Selector   |
| Examine...   |
|____________|

Selector
--------

Prompts you to type a selector to display in the pane. You can use
full expression syntax to enter the selector. If you enter a
numeric value, TDW assumes it is decimal unless you use the syntax
of the current language to indicate that the value is hexadecimal.

For example, if the current language were C, you could type the
hexadecimal selector value 7F as 0x7F. For Pascal, you'd type it as
$7F. You could also type the decimal value 127 in order to go to
selector 7F.

Another method of entering the selector value is to display the CPU
window and check the segment register values. If a register holds
the selector you're interested in, you can enter the name of the
register preceded by an underscore (_). For example, you could type
the data segment register as _DS.


Examine
-------

Displays the contents of the memory area referenced by the current
selector and switches focus to the pane where the contents are
displayed. If the selector points to a code segment, the contents
are diplayed in the Code pane. If the contents are data, they're
displayed in the Data pane.



===================================================================
6. The Data pane
===================================================================

This pane shows a raw display of an area of memory you've selected.
The leftmost part of each line shows the address of the data
displayed in that line. The address is displayed either as a hex
segment and offset, or with the segment value replaced with one of
the register names if the segment value is the same as that
register. The Data pane matches registers in the following order:
DS, ES, SS, CS.

Next, the raw display of one or more data items is displayed. The
format of this area depends on the display format selected with the
Display As local menu command. If you choose one of the floating-
point display formats (Comp, Float, Real, Double, Extended), a
single floating-point number is displayed on each line. Byte format
displays 8 bytes per line, Word format displays 4 words per line,
and Long format displays 2 long words per line.

When the data is displayed as bytes, the rightmost part of each
line shows the display characters that correspond to the data bytes
displayed. TDW displays all byte values as their display
equivalents, so don't be surprised if you see graphics characters
displayed to the right of the hex dump area--these are just the
display equivalents of the hex byte values.

If you use the Data pane to examine the contents of the display
memory, the ROM BIOS data area, or the vectors in low memory, you
will see the values that are there when the program being debugged
runs, not the actual values in memory when TDW is running. These
are not the same values that are in these memory areas at the time
you look at them. TDW detects when you're accessing areas of memory
that it uses as well, and it gets the correct data value from where
it stores the user program's copy of these data areas.

There are two ways of choosing an address:

o Use the local menu Goto, Follow, or Previous command.

o Position on a data selector in the Selector pane, then choose
  Examine to display the contents of the selector in the Data pane.


The Data pane local menu
========================

Once you are positioned in the Data pane, press Alt-F10 to pop up
the local menu or, if control-key shortcuts are enabled, use the
Ctrl key with the first letter of the desired command to access it.
 ______________
| Goto...        |
| Search...      |
| Next         |
| Change...      |
| Follow       |
| Previous     |
|______________|
| Display as   |
| Block        |
|______________|

The Data pane local menu lets you go to a new address, search for a
character string, change bytes at the current cursor location,
follow near or far pointer chains, restore a previous address,
change how data appears in the window, and move, change, read, and
write blocks of memory.


Goto
----

Positions you at an address in your data. Enter the new address you
want to go to. You can enter addresses inside DOS, in resident
utilities, or outside your program, which lets you examine data in
the BIOS data area. See Chapter 9 in the debugger manual for a
complete discussion of how to enter addresses.


Search
------

Searches for a character string, starting at the current memory
address as indicated by the cursor position. Enter the byte list to
search for. The search does not wrap around from the end of the
segment to the beginning. See Chapter 9 in the debugger manual for
a complete discussion of byte lists.

Next
----

Searches for the next instance of the byte list you previously
specified with the Search command.


Change
------

Lets you change the bytes at the current cursor location. If you're
over an ASCII display or the format is Byte, you're prompted for a
byte list. Otherwise, you're prompted for an item of the current
display type. See Chapter 9 in the debugger manual for a discussion
of byte lists.

You can also invoke this command by simply starting to type the new
value or values. This brings up a dialog box exactly as if you had
chosen the Change command.


Follow
------

Opens a menu that lets you follow near or far pointer chains.
 ________________________
| Near code              |
| Far code               |
|________________________|
| Offset to data         |
| Segment:offset to data |
| Base segment:0 to data |
|________________________|

    Near Code
    ---------

    This command interprets the word under the cursor in the Data pane
    as an offset into the current code segment as specified by the CS
    register. The Code pane becomes the current pane and is positioned
    to this address.

    Far Code
    --------

    This command interprets the doubleword under the cursor in the Data
    pane as a far address (segment and offset). The Code pane becomes
    the current pane and is positioned to this address.

    Offset to Data
    --------------

    This command lets you follow word (near, offset only) pointer
    chains. The Data pane is set to the offset specified by the word in
    memory at the current cursor location.

    Segment:Offset to Data
    ----------------------

    This command lets you follow long (far, segment, and offset)
    pointer chains. The Data pane is set to the offset specified by the
    two words in memory at the current cursor location.

    Base Segment:0 to Data
    ----------------------

    This command interprets the word under the cursor as a segment
    address and positions the Data pane to the start of that segment.


Previous
--------

Restores the Data pane address to the address before the last
command that explicitly changed the display address. Using the
arrow keys and PgUp and PgDn does not cause the position to be
remembered.

TDW maintains a stack of the last five addresses, so you can
backtrack through multiple uses of the Follow menu or Goto
commands.


Display As
----------

Lets you choose how data appears in the Data pane. You can choose
from any data format used by C++ and assembler. The menu options
are
     __________
    | Byte     |
    | Word     |
    | Long     |
    | Comp     |
    | Float    |
    | Real     |
    | Double   |
    | Extended |
    |__________|

    Byte
    ----

    Sets the Data pane to display as hexadecimal bytes. This
    corresponds to the C++ Byte type.

    Word
    ----

    Sets the Data pane to display as word hexadecimal numbers. The 2-
    byte hexadecimal value is shown. This corresponds to the C int data
    type and the C++ Word type.

    Long
    ----

    Sets the Data pane to display as long hexadecimal integers. The 4-
    byte hex value is shown. This corresponds to the C++ longint type.

    Comp
    ----

    Sets the Data pane to display 8-byte integers. The decimal value of
    the integer is shown. This is the C++ Comp (IEEE) data type.

    Float
    -----

    Sets the Data pane to display as short floating-point numbers. The
    scientific notation floating-point value is shown. This is the same
    as the C++ Single (IEEE) type.

    Real
    ----

    Sets the Data pane to display C++'s 6-byte floating-point numbers.
    The scientific notation floating-point value is shown. This is the
    C++ Real type.

    Double
    ------

    Sets the data pane to display 8-byte floating-point numbers. The
    scientific notation floating-point value is shown. This is the same
    as the C++ Double type.

    Extended
    --------

    Sets the Data pane to display 10-byte floating-point numbers. The
    scientific notation floating-point value is shown. This is the
    internal format used by the 80x87 coprocessor. It also corresponds
    to the C++ Extended (IEEE) type, and the assembler TBYTE type.


Block
-----

Lets you manipulate blocks of memory. You can move, clear and set
memory blocks, and read and write memory blocks to and from disk
files. Block brings up the following menu:
     _________
    | Clear...|
    | Move...   |
    | Set...    |
    | Read...   |
    | Write...  |
    |_________|

    Clear
    -----

    Sets a contiguous block of memory to zero (0). You are prompted for
    the address and the number of bytes to clear.

    Move
    ----

    Copies a block of memory from one address to another. You are
    prompted for the source address, the destination address, and how
    many bytes to copy.

    Set
    ---

    Sets a contiguous block of memory to a specific byte value. You are
    prompted for the address of the block, how many bytes to set, and
    the value to set them to.

    Read
    ----

    Reads all or a portion of a file into a block of memory. You are
    prompted first for the file name to read from, then for the address
    to read it into, and finally for how many bytes to read.

    Write
    -----

    Writes a block of memory to a file. You are prompted first for the
    file name to write to, then for the address of the block to write
    and how many bytes to write.


===================================================================
7. The Stack pane
===================================================================

The Stack pane, in the lower right corner of the CPU window, shows
the contents of the stack. An arrow shows the current stack pointer
(SS:IP).


The Stack pane local menu
=========================

At the Stack pane, press Alt-F10 to pop up the local menu or, if
control-key shortcuts are enabled, use the Ctrl key with the first
letter of the desired command to access the command.
 ___________
| Goto...     |
| Origin    |
| Follow    |
| Previous  |
| Change...   |
|___________|

Goto
----

Positions you at an address in the stack. Enter the new stack
address. If you want, you can enter addresses outside your pro-
gram's stack, although you would usually use the Data pane to
examine arbitrary data outside your program. See Chapter 9 in the
debugger manual for information about how to enter addresses.

The Previous command restores the Stack pane to the position it had
before the Goto command was issued.


Origin
------

Positions you at the current stack location as indicated by the
SS:SP register pair. This command is useful when you want to return
to where you started.

The Previous command restores the Stack pane to the position it had
before the Origin command was issued.


Follow
------

Positions you at the word in the stack pointed to by the currently
highlighted word. This is useful for following stack-frame threads
back to a calling procedure.

The Previous command restores the Stack pane to the position it had
before the Follow command was issued.


Previous
--------

Restores the Stack pane position to the address before the last
command that explicitly changed the display address. Using the
arrow keys and PgUp and PgDn does not cause the position to be
remembered.

Repeated use of the Previous command causes the Stack pane to
switch back and forth between two addresses.


Change
------

Lets you enter a new word value for the currently highlighted stack
word.

You can also invoke this command by simply starting to type the new
value for the highlighted stack item. A dialog box appears, exactly
as if you had specified the Change command.



===================================================================
8. The assembler
===================================================================

If you use the Assemble command in the Code pane local menu, TDW
lets you assemble instructions for the 8086, 80186, 80286, 80386,
and 80486 processors, and also for the 8087, 80287, and 80387
numeric coprocessors.

When you use TDW's built-in assembler to modify your program, the
changes you make are not permanent. If you reload your program Run|
Program Reset, or if you load another program using File|Open,
you'll lose any changes you've made.

Normally you use the assembler to test an idea for fixing your pro-
gram. Once you've verified that the change works, you must change
your source code and recompile and link your program.

The following sections describe the differences between the built-
in assembler and the syntax accepted by Turbo C++'s inline
assembler.


Operand address size overrides
==============================

For the call (CALL), jump (JMP), and conditional jump (JNE, JL, and
so forth) instructions, the assembler automatically generates the
smallest instruction that can reach the destination address. You
can use the NEAR and FAR overrides before the destination address
to assemble the instruction with a specific size. For example,

  CALL FAR XYZ
  JMP  NEAR A1


Memory and immediate operands
-----------------------------

When you use a symbol from your program as an instruction operand,
you must tell the built-in assembler whether you mean the contents
of the symbol or the address of the symbol. If you use just the
symbol name, the assembler treats it as an address, exactly as if
you had used the assembler OFFSET operator before it. If you put
the symbol inside brackets ([ ]), it becomes a memory reference.
For example, if your program contains the data definition

  A    DW 4

then [A] references the area of memory where A is stored.

When you assemble an instruction or evaluate an assembler
expression to refer to the contents of a variable, use the name of
the variable alone or between brackets:

  mov dx,a
  mov ax,[a]

To refer to the address of the variable, use the OFFSET operator:

  mov ax,offset a


Operand data size overrides
===========================

For some instructions, you must specify the operand size using one
of the following expressions before the operand:

  BYTE PTR
  WORD PTR

Here are examples of instructions using these overrides:

  add BYTE PTR[si],10
  mov WORD PTR[bp+10],99

In addition to these size overrides, you can use the following
overrides to assemble 8087/80287/80387/80486 numeric processor
instructions:

  DWORD PTR
  QWORD PTR
  TBYTE PTR

Here are some examples using these overrides:

  fild QWORD PTR[bx]
  stp  TBYTE PTR[bp+4]


String instructions
===================

When you assemble a string instruction, you must include the size
(byte or word) as part of the instruction mnemonic. The assembler
does not accept the form of the string instructions that uses a
sizeless mnemonic with an operand that specifies the size. For
example, use STOSW rather than STOS WORD PTR[di].



===================================================================
9. The Dump window
===================================================================

The Dump window shows you a raw data dump of any area of memory. It
works much like the Data pane in the CPU window, except that, when
zoomed to full size, the Dump Window show twice as much data on a
single line.

See "The Data pane local menu" section earlier in this file for a
description of the contents and local menu for this window.

Typically, you'd use this window when you're debugging an assembler
program at the source level, and you want to take a low-level look
at some data areas. Use View|Dump to open a Dump window or View|
Another|Dump to open multiple views on memory areas.

You can also use this window if you're in an Inspector window, and
you want to look at the raw bytes that make up the object you are
inspecting. Use View|Dump to get a Dump window that's positioned to
the data in the Inspector window.


===================================================================
10. The Registers window
===================================================================

The Registers window shows you the contents of the CPU registers
and flags. It works like a combination of the Registers and Flags
panes in the CPU window. You can shrink the size of your Module
window and put up a Registers window alongside it.


See "The Register pane local menu" and "The Flags pane local menu"
sections earlier in this file for a description of the contents and
local menus for this window.

Use this window when you're debugging an assembler program at the
source level and want to look at the register values.



=========================================
11. Assembler-specific bugs
=========================================

This section, which covers some of the common pitfalls of assembly
language programming, is intended for people who have Turbo Assembler
or use inline assembler in C++ programs. You should refer to the
Turbo Assembler User's Guide for a fuller explanation on these
oft-encountered errors--and tips on how to avoid them.

Forgetting to return to DOS
===========================

In C++, a program ends automatically when there is no more code to
execute, even if no explicit termination command was written into the
program. Not so in assembly language, where only those actions that
you explicitly request are performed. When you run a program that has
no command to return to DOS, execution simply continues right past the
end of the program's code and into whatever code happens to be in the
adjacent memory.


Forgetting a RET instruction
============================

The proper invocation of a subroutine consists of a call to the subroutine
from another section of code, execution of the subroutine, and a return
from the subroutine to the calling code. Remember to insert a RET
instruction in each subroutine, so that the RETurn to the calling code
occurs. When you're typing a program, it's easy to skip a RET and end
up with an error.


Generating the wrong type of return
===================================
The PROC directive has two effects. First, it defines a name by which a
procedure can be called. Second, it controls whether the procedure is a near
or far procedure.

The RET instructions in a procedure should match the type of the procedure,
shouldn't they?

Yes and no. The problem is that it's possible and often desirable to group
several subroutines in the same procedure. Since these subroutines lack an
associated PROC directive, their RET instructions take on the type of the
overall procedure, which is not necessarily the correct type for the
individual subroutines.


Reversing operands
==================

To many people, the order of instruction operands in 8086 assembly language
seems backward (and there is certainly some justification for this
viewpoint). If the line

  mov  ax,bx

meant "move AX to BX," the line would scan smoothly from left to right, and
this is exactly the way in which many microprocessor manufacturers have
designed their assembly languages.

However, Intel took a different approach with 8086 assembly language; for
us, the line means "move BX to AX," and that can sometimes cause confusion.


Forgetting the stack or reserving a too-small stack
===================================================

In most cases, you're treading on thin ice if you don't explicitly allocate
space for a stack. Programs without an allocated stack sometimes run, but
there is no assurance that these programs will run under all circumstances.
DOS programs can have a .STACK directive to reserve space for the stack.
In Turbo C++ for Windows, you can set the stack size by choosing
Options|Compiler. For each program, you should reserve more than enough
space for the deepest stack the program can use.


Calling a subroutine that wipes out registers
=============================================

When you're writing assembler code, it's easy to think of the registers
as local variables, dedicated to the use of the procedure you're working
on at the moment. In particular, there's a tendency to assume that
registers are unchanged by calls to other procedures. It just isn't
so--the registers are global variables, and each procedure can preserve or
destroy any or all registers.


Using the wrong sense for a conditional jump
============================================
The profusion of conditional jumps in assembly language (JE, JNE, JC,
JNC, JA, JB, JG, and so on) allows tremendous flexibility in writing
code--and also makes it easy to select the wrong jump for a given purpose.
Moreover, since condition-handling in assembly language requires at least
two separate lines, one for the comparison and one for the conditional
jump (it requires many more lines for complex conditions), assembly
language condition-handling is less intuitive and more prone to errors than
condition-handling in C++.


Forgetting about REP string overrun
===================================

String instructions have a curious property: After they're executed, the
pointers they use wind up pointing to an address 1 byte away (or 2 bytes
for a word instruction) from the last address processed. This can cause
some confusion with repeated string instructions, especially REP SCAS and
REP CMPS.


Relying on a zero CX to cover a whole segment
=============================================

Any repeated string instruction executed with CX equal to zero does nothing.
Period. This can be convenient in that there's no need to check for the zero
case before executing a repeated string instruction; on the other hand,
there's no way to access every byte in a segment with a byte-sized string
instruction.


Using incorrect direction flag settings
=======================================

When a string instruction is executed, its associated pointer or pointers--
SI or DI or both--increment or decrement. It all depends on the state of the
direction flag.

The direction flag can be cleared with CLD to cause string instructions to
increment (count up) and can be set with STD to cause string instructions to
decrement (count down). Once cleared or set, the direction flag stays in the
same state until either another CLD or STD is executed, or until the flags
are popped from the stack with POPF or IRET. While it's handy to be able to
program the direction flag once and then execute a series of string
instructions that all operate in the same direction, the direction flag can
also be responsible for intermittent and hard-to-find bugs by causing the
behavior of string instructions to depend on code that executed much earlier.


Using the wrong sense for a repeated string comparison
======================================================

The CMPS instruction compares two areas of memory; the SCAS instruction
compares the accumulator to an area of memory.  Prefixed by REPE, either
of these instructions can perform a comparison until either CX becomes
zero or a not-equal comparison occurs. Unfortunately, it's easy to become
confused about which of the REP prefixes does what.


Forgetting about string segment defaults
========================================

Each of the string instructions defaults to using a source segment (if any)
of DS, and a destination segment (if any) of ES. It's easy to forget this
and try to perform, say, a STOSB to the data segment, since that's where
all the data you're processing with non-string instructions normally resides.


Converting incorrectly from byte to word operations
===================================================
In general, it's desirable to use the largest possible data size (usually
word, but dword on an 80386) for a string instruction, since string
instructions with larger data sizes often run faster.

There are a couple of potential pitfalls here. First, the conversion from a
byte count to a word count by a simple

  shr cx,1

loses a byte if CX is odd, since the least-significant bit is shifted out.

Second, make sure you remember SHR divides the byte count by two. Using,
say, STOSW with a byte rather than a word count can wipe out other data
and cause problems of all sorts.


Using multiple prefixes
=======================

String instructions with multiple prefixes are error-prone and should
generally be avoided.


Relying on the operand(s) to a string instruction
=================================================

The optional operand or operands to a string instruction are used for data
sizing and segment overrides only, and do not guarantee that the memory
location referenced is accessed.


Wiping out a register with multiplication
=========================================

Multiplication--whether 8 bit by 8 bit, 16 bit by 16 bit, or 32 bit by 32
bit--always destroys the contents of at least one register other than the
portion of the accumulator used as a source operand.


Forgetting that string instructions alter several registers
===========================================================

The string instructions, MOVS, STOS, LODS, CMPS, and SCAS, can affect several
of the flags and as many as three registers during execution of a single
instruction. When you use string instructions, remember that SI, DI, or
both either increment or decrement (depending on the state of the direction
flag) on each execution of a string instruction. CX is also decremented at
least once, and possibly as far as zero, each time a string instruction with
a REP prefix is used.


Expecting certain instructions to alter the carry flag
======================================================

While some instructions affect registers or flags unexpectedly, other
instructions don't even affect all the flags you might expect them to.


Waiting too long to use flags
=============================

Flags last only until the next instruction that alters them, which is
usually not very long. It's a good practice to act on flags as soon as
possible after they're set, thereby avoiding all sorts of potential bugs.


Confusing memory and immediate operands
=======================================

An assembler program may refer either to the offset of a memory variable or
to the value stored in that memory variable. Unfortunately, assembly language
is neither strict nor intuitive about the ways in which these two types of
references can be made, and as a result, offset and value references to a
memory variable are often confused.


Failing to preserve everything in an interrupt handler
======================================================

Every interrupt handler should explicitly preserve the contents of all
registers. While it is valid to preserve explicitly only those registers
that the handler modifies, it's good insurance just to push all registers
on entry to an interrupt handler and pop all registers on exit.


Forgetting group overrides in operands and data tables
======================================================

Segment groups let you partition data logically into a number of areas
without having to load a segment register every time you want to switch
from one of those logical data areas to another.



=========================================
12. Inline assembler tips
=========================================


Looking at raw hex data
=======================

You can use the Data|Add Watch and Data| Evaluate/Modify commands with
a format modifier to look at raw data dumps. For example, if your
language is Assembler,

  [ES:DI],20m

specifies that you want to look at a raw hex memory dump of the 20 bytes
pointed to by the ES:DI register pair.


Source-level debugging
======================

You can step through your assembler code using a Module window just as
with any of the high-level languages. If you want to see the register
values, you can put a Registers window to the right of the Module window.

Sometimes, you may want to use a CPU window and see your source code as
well.  To do this, open a CPU window and choose the Code pane's Mixed
command until it reads Both. That way you can see both your source code
and machine code bytes. Remember to zoom the CPU window (by pressing F5)
if you want to see the machine code bytes.


Examining and changing registers
================================

The obvious way to change registers is to highlight a register in either
a CPU window or Registers window. A quick way to change a register is to
choose Data|Evaluate/Modify. You can enter an assignment expression that
directly modifies a register's contents. For example,

   SI = 99

loads the SI register with 99.

Likewise, you can examine registers using the same technique. For example,

   Alt-D E AX

shows you the value of the AX register.



=========================================
13. Inline assembler keywords
=========================================

This section lists the instruction mnemonics and other special symbols that
you use when entering instructions with the inline assembler. The keywords
presented here are the same as those used by Turbo Assembler.


8086/80186/80286 instructional mnemonics
_________________________________________
  AAA        INC        LIDT**     REPNZ
  AAD        INSB*      LLDT**     REPZ
  AAM        INSW*      LMSW**     RET
  AAS        INT        LOCK       REFT
  ADC        INTO       LODSB      ROL
  ADD        IRET       LODSW      ROR
  AND        JB         LOOP       SAHF
  ARPL**     JBE        LOOPNZ     SAR
  BOUND*     JCXZ       LOOPZ      SBB
  CALL       JE         LSL**      SCASB
  CLC        JL         LTR**      SCASW
  CLD        JLE        MOV        SGDT**
  CLI        JMP        MOVSB      SHL
  CLTS**     JNB        MOVSW      SHR
  CMC        JNBE       MUL        SLDT**
  CMP        JNE        NEG        SMSW**
  CMPSB      JNLE       NOP        STC
  CMPSW      JNO        NOT        STD
  CWD        JNP        OR         STI
  DAA        JO         OUT        STOSB
  DAS        JP         OUTSB      STOSW
  DEC        JS         OUTSW      STR**
  DIV        LAHF       POP        SUB
  ENTER*     LAR**      POPA*      TEST
  ESC        LDS        POPF       WAIT
  HLT        LEA        PUSH       VERR**
  IDIV       LEAVE      PUSHA*     VERW**
  IMUL       LES        PUSHF      XCHG
  IN         LGDT**     RCL        XLAT
                                   XOR
  ___________________________________________

* Available only when running on the 186 and 286 processor
** Available only when running on the 286 processor


TDW supports all 80386 and 80387 instruction mnemonics and registers:


80386 instruction mnemonics
_________________________________________

  BSF        LSS         SETG        SETS
  BSR        MOVSX       SETL        SHLD
  BT         MOVZX       SETLE       SHRD
  BTC        POPAD       SETNB       CMPSD
  BTR        POPFD       SETNE       STOSD
  BTS        PUSHAD      SETNL       LODSD
  CDQ        PUSHFD      SETNO       MOVSD
  CWDE       SETA        SETNP       SCASD
  IRETD      SETB        SETNS       INSD
  LFS        SETBE       SETO        OUTSD
  LGS        SETE        SETP        JECXZ
__________________________________________

80486 instruction mnemonics
_________________________________________

       BSWAP               INVLPG
       CMPXCHG             WBINVD
       INVD                XADD
_________________________________________

 80386 registers
_________________________________________

       EAX                 EDI
       EBX                 EBP
       ECX                 ESP
       EDX                 FS
       ESI                 GS
_________________________________________

CPU registers
__________________________________________________________________

Byte registers            AH, AL, BH, BL, CH, CL, DH, DL

Word registers            AX, BX, CX, DX, SI, DI, SP, BP, FLAGS

Segment registers         CS, DS, ES, SS

Floating registers        ST, ST(0), ST(1), ST(2), ST(3), ST(4),
                          ST(5), ST(6), ST(7)
___________________________________________________________________

Special keywords
_________________________________________

       WORD PTR            TBYTE PTR
       BYTE PTR            NEAR
       DWORD PTR           FAR
       QWORD PTR           SHORT
_________________________________________

8087/80287 numeric coprocessor instruction mnemonics
____________________________________________________
  FABS       FIADD      FLDL2E     FST
  FADD       FIACOM     FLDL2T     FSTCW
  FADDP      FIACOMP    FLDPI      FSTENV
  FBLD       FIDIV      FLDZ       FSTP
  FBSTP      FIDIVR     FLD1       FSTSW**
  FCHS       FILD       FMUL       FSUB
  FCLEX      FIMUL      FMULP      FSUBP
  FCOM       FINCSTP    FNOP       FSUBR
  FCOMP      FINIT      FNSTS**    FSUBRP
  FCOMPP     FIST       FPATAN     FTST
  FDECSTP    FISTP      FPREM      FWAIT
  FDISI      FISUB      FPTAN      FXAM
  FDIV       FISUBR     FRNDINT    FXCH
  FDIVP      FLD        FRSTOR     FXTRACT
  FDIVR      FLDCWR     FSAVENT    FYL2X
  FDIVRP     FLDENV     FSCALE     FYL2XPI
  FENI       FLDLG2     FSETPM*    F2XM1
  FFREE      FLDLN2     FSQRT
_____________________________________________________

* Available only when running on the 287 numeric coprocessor.
** On the 80287, the fstsw instruction can use the AX register as an operand,
   as well as the normal memory operand.


80387 instruction mnemonics
_________________________________________

       FCOS                FUCOM
       FSIN                FUCOMP
       FPREM1              FUCOMPP
       FSINCOS
_________________________________________

The 80x87 coprocessor chip and emulator
=======================================

This section is for programmers who are familiar with the operation
if the 80x87 math coprocessor.  If your program uses floating-point
numbers, TDW lets you examine and change the state of the numeric
coprocessor or, if the coprocessor is emulated, examine the state of the
software emulator. (Windows permits you only to examine the state of the
emulator, not to change it.)  You don't need to use the capabilities
described in this chapter to debug programs that use floating-point numbers,
although some very subtle bugs may be easier to find.

In this section, we discuss the differences between the 80x87 chip and
the software emulator. We also describe the Numeric Processor window and
show you how to examine and modify the floating-point registers, the status
bits, and the control bits.


The 80x87 chip vs. the emulator
===============================

TDW automatically detects whether your program is using the math chip or the
emulator and adjusts its behavior accordingly.

Note that most programs use either the emulator or the math chip, not both
within the same program. If you have written special assembler code that
uses both, TDW won't be able to show you the status of the math chip; it
reports on the emulator only.


The Numeric Processor window
============================

You create a Numeric Processor window by choosing the View|Numeric Processor
command from the menu bar. The line at the top of the window shows the
current instruction pointer, opcode, and data pointer. The instruction
pointer is both shown as a 20-bit physical address. The data pointer is
either a 16-bit or a 20-bit address, depending on the memory model. You
can convert 20-bit addresses to segment and offset form by using the first
four digits as the segment value and the last digit as the offset value.

For example, if the top line shows IPTR=5A669, you can treat this as the
address 5a66:9 if you want to examine the current data and instruction in
a CPU window. This window has three panes: The left pane (Register pane)
shows the contents of the floating-point registers, the middle pane
(Control pane) shows the control flags, and the right pane (Status pane)
shows the status flags.


The top line shows you the following information about the last floating-
point operation that was executed:

o Emulator indicates that the numeric processor is being emulated. If there
  were a numeric processor, 8087, 80287, 80387, or 80486 would appear instead.

o The IPTR shows the 20-bit physical address from which the last floating-
  point instruction was fetched.

o The OPCODE shows the instruction type that was fetched.

o The OPTR shows the 16-bit or 20-bit physical address of the memory address
  that the instruction referenced, if any.


The Register pane
-----------------

    The 80-bit floating-point registers
    -----------------------------------

    The Register pane shows each of the floating-point registers, ST(0) to
    ST(7), along with its status (valid/zero/special/empty). The contents
    are shown as an 80-bit floating-point number.

    If you've zoomed the Numeric Processor window (by pressing F5) or made
    it wider by using Window|Size/Move, you'll also see the floating-point
    registers displayed as raw hex bytes.


    The Register pane local menu
    ----------------------------
    ___________
   | Zero      |
   | Empty     |
   | Change...   |
   |___________|

    To bring up the Register pane local menu, press Alt-F10, or use the Ctrl
    key with the first letter of the desired command to directly access the
    command.

    Zero
    ----

    Sets the value of the currently highlighted register to zero.

    Empty
    -----

    Sets the value of the currently highlighted register to empty. This is a
    special status that indicates that the register no longer contains valid
    data.

    Change
    ------

    Loads a new value into the currently highlighted register. You are
    prompted for the value to load. You can enter an integer or floating-
    point value, using the current language's expression parser.  The value
    you enter is automatically converted to the 80-bit temporary real format
    used by the numeric coprocessor.

    You can also invoke this command by simply starting to type the new value
    for the floating-point register. A dialog box appears, exactly as if you
    had specified the Change command.


The Control pane
----------------

    The control bits
    ----------------

    The following table lists the different control flags and how they appear
    in the Control pane:
_________________________________________

   Name in pane         Flag description__

      im                Invalid operation mask
      dm                Denormalized operand mask
      zm                Zero divide mask
      om                Overflow mask
      um                Underflow mask
      pm                Precision mask
      iem               Interrupt enable mask (8087 only)
      pc                Precision control
      rc                Rounding control
      ic                Infinity control__


    The Control pane local menu
    ---------------------------
       ________
      | Toggle |
      |________|

    Press Tab to go to the Control pane, then press Alt-F10 to pop up the
    local menu. (Alternatively, you can use the Ctrl key with the first letter
    of the desired command to access it.)

    Toggle
    ------

    Cycles through the values that the currently highlighted control flag
    can be set to. Most flags can only be set or cleared (0 or 1), so this
    command just toggles the flag to the other value. Some other flags have
    more than two values; for those flags, this command increments the flag
    value until the maximum value is reached, and then sets it back to zero.

    You can also toggle the control flag values by highlighting them and
    pressing Enter.


The Status pane
---------------


    The status bits
    ---------------

    The following table lists the different status flags and how they appear
    in the Status pane:
____________________________________

   Name in pane         Flag description__

      ie                Invalid operation
      de                Denormalized operand
      ze                Zero divide
      oe                Overflow
      ue                Underflow
      pe                Precision
      ir                Interrupt request
      cc                Condition code
      st                Stack top pointer_


    The Status pane local menu
    --------------------------
       ________
      | Toggle |
      |________|

    Press Tab to move to the Statuspane, then press Alt-F10 to pop up the
    local menu. (You can also use the Ctrl key with the first letter of the
    desired command to access the command directly.)


    Toggle
    ------

    Cycles through the values that the currently highlighted status flag
    can be set to. Most flags can only be set or cleared (0 or 1), so this
    command just toggles the flag to the other value. Some other flags have
    more than two values; for those flags, this command increments the
    flag value until the maximum value is reached, and then sets it back to
    zero.

    You can also toggle the status flag values by highlighting them and
    pressing Enter.

