QLINK is a DOS linker and analysis tool designed to link together MS-DOS compatible .OBJ files. It can replace the MS-DOS LINK.EXE program when producing MS-DOS compatible .EXE and .COM files.
Make a directory (e.g., C:\QLINK), copy the zip file to that directory, and unzip the files:
MD C:\QLINK
CD C:\QLINK
COPY A:\QLINK.ZIP
PKUNZIP QLINK.ZIP
If you'll be running QLINK under Windows 3.1x (see below for Win95 instructions), copy the file WINDPMI.386 to your Windows system directory. For example, if you installed Windows into the directory C:\WINDOWS, copy WINDPMI.386 to C:\WINDOWS\SYSTEM. Then edit your Windows SYSTEM.INI file to insert a line such as the following in the [386ENH] section:
device=windpmi.386
You should first ensure that no other similar line already appears in your SYSTEM.INI file. For example, you might already have a line such as
device=c:\bc4\bin\windpmi.386
If this is the case, do not insert another call to the same driver; you need only one. This VxD does not work with Win9x.
If you'll be running QLINK under Win9x, follow the above procedure using the file W95DPMI.386 instead of WINDPMI.386.
For the most part, just call QLINK instead of LINK or TLINK as appropriate. Borland users should note that a number of Borland specific Object Module Formats (OMFs) are not implemented as yet (I'm waiting for the documentation from Borland). Several MS link switches are not supported as yet (e.g., /PACKC). If there are switches you particularly need which are not supported, let me know. For an explanation of the old linker switches, see your linker manual.
To take advantage of the detailed error processing in QLINK, use the assembler switches which generate types and line numbers. For MASM and TASM these switches are /Zd and /Zi.
The order in which segments appear in the executable file depends on several factors. The first is whether or not the /DOSSEG switch appears explicitly on the command line or implicitly in a OMF record in one of the .OBJ files.
If /DOSSEG is specified, the segment order is as follows:
Otherwise, the segment order is as follows:
There are a number of switches specific to QLINK which are documented in the file QLINK.CFG. These switches control the processing of error messages from QLINK. All error messages begin with either
==> WARN:
or
==> FAIL:
Messages which begin with WARN are warnings and do not halt the linker. Messages which being with FAIL cause the linker to stop immediately and not continue processing the input files.
If an error message is followed by a name such as FIXOVF or GRPEXT0 in parentheses, then that error can be controlled by the switches /I:switch, /W:switch, and /F:switch, where switch is the name in parentheses in the error message.
If you wish to ignore this error (meaning the linker takes a default action and continues processing), use /I:switch. To warn about an error (meaning an error message is displayed, the linker takes a default action, and continues processing), use /W:switch. The default settings for all error messages are described in the file QLINK.CFG.
This same file (QLINK.CFG) is consulted when QLINK begins execution. Any switches found there (including switches such as /MAP, /LINE, etc.) are processed before the command line is parsed. Switches only may be contained in QLINK.CFG, not names of .OBJ files, etc. Even earlier in the process, the environment variable QLINK= is consulted, and it too may contain only switches.
Thus the order of processing of switches is first, those contained in the environment variable QLINK=, then those in the file QLINK.CFG (first in the current directory, and if not found there in the directory from which QLINK is loaded), and finally those found on the command line to QLINK. Switches processed later in the sequence override ones processed earlier.
Occasionally, you want to link together .OBJ modules from different projects which use different naming conventions. For example, in one project code segments are in class CODE and in others they are in class PROG. Previously, you would have to edit the source code, make the changes, and re-compile. With the Name Substitution feature of QLINK, it's a snap.
To substitute names on the fly within the .OBJ file, place the switch /NS before the reference to each .OBJ file whose names are to be substituted. For example, use /NS:PROG-CODE to tell QLINK that the name PROG is to be changed to CODE.
Each substitution is effective for all .OBJ files which appear after it until that substitution (or all substitutions) are halted. Use the form /NS:name to halt substitutions on name; use /NS with no arguments to halt all substitutions.
Note that this means that the occurrences of /NS are sensitive to the position and order in which they appear. Be sure to place occurrences of /NS before the reference to the .OBJ file to which they apply.
To swap two symbols in the same file, use (say) /NS:A-B:B-A.
The substitution is made on all references to the name regardless of context. Thus if you have a file with a segment named PROG and a class named PROG, substituting CODE for PROG changes both references.
The full syntax is
Nameset: (empty)
; Halt substitution on all names
| name
; Halt substitution on this name
| name
'-' name
; Substitute the second name for the first name
Namedef: Nameset
| Namedef
':' Nameset
Switch: '/NS:' Namedef
The keyword /NS may appear in the QLINK environment variable, the QLINK.CFG configuration file, the automatic response file, and the QLINK command line.
| Q: | When I link modules with the MS linker and QLINK, sometimes the executable files are of very different sizes? |
| A: | This can occur if a .LIB is used to resolve external references. Because there is no rule as to the order in which external refs are processed, different ordering of these references mean that there can be different segment boundary alignments which can change the final executable file size. |
In no particular order of importance (nor of expectation of getting done), the following topics are on my list:
Please feel free to add to this list.
Please contact the author via Internet e-mail at
(Bob Smith)
QLINK is © Copyright 1994-2006 Qualitas, Inc. All rights reserved.
5.08 23 March 2006
5.07 2 January 2004
5.06 24 December 2003
5.05 19 June 2003
5.04 22 May 2003
5.03 21 July 2002
5.02 1 July 2002
5.01 26 June 2002
5.00 26 June 2002
1.30 22 June 2002
1.29 18 April 2002
1.28 25 April 2000
1.27 18 April 2000
1.26 10 April 2000
1.25 7 April 2000
1.24 4 April 2000
1.23 30 March 2000
1.22 30 March 2000
1.21 28 March 2000
1.20 24 March 2000
1.19 22 March 2000
1.18 15 March 2000
1.17 2 October 1999
1.16 8 September 1999
1.15 6 September 1999
1.14 3 September 1999
1.13 25 May 1999
1.12 16 May 1999
1.11 25 June 1998
1.10 27 April 1998
1.09 12 March 1998
1.08 12 November 1997
1.07 8 July 1997
1.06 25 June 1995
1.05 8 May 1995
1.04 25 March 1995
1.03 14 February 1995
1.02 18 November 1994
1.01 26 October 1994
1.00 2 October 1994
Use this program on an international keyboard to help the author of 386SWAT support your keyboard.
First try out the program to see how it works (note Space bar to Exit). Next redirect the program's output to a file and type all keys (blindly).
Type every key on your keyboard except for function, arrow, and keypad keys. Type every key in unshifted state as well as with the Shift-, Control-, and Alt-keys. If your keyboard distinguishes between the left and right Shift-, Control-, and/or Alt-keys be sure to test all states for all keys.
After exiting the program, annotate the output file with any comments you consider appropriate. Finally, e-mail the file to the author (bsmith@sudleyplace.com).
For reference, supported international keyboard layouts include
| GR129 | Germany |
| SP172 | Spain |
| BE120 | Belgium |
| CF058 | Canada (French) |
| DK159 | Denmark |
| SU153 | Finland |
| FR120 | France |
| FR189 | France |
| IS197 | Iceland |
| IT142 | Italy |
| LA171 | Latin America |
| NL143 | Netherlands |
| NO155 | Norway |
| PO163 | Portugal |
| SF150 | Switzerland (French) |
| SG150 | Switzerland (German) |
| TR179 | Turkey |
| UK166 | United Kingdom |
| YU234 | Yugoslavia (Latin) |
|
This protected mode debugger provides debugging services to real mode, protected mode, virtual 8086 mode programs, DPMI and VCPI clients, and Win 3.1x and Win95/98 programs under any memory manager while occupying only a few kilobytes of low DOS memory. It supports disassembly of the full 386, 387, 486, Pentium, and Pentium Pro CPU instruction sets through the Pentium MMX & SSE instructions. This debugger is also a Windows Kernel Debugger providing low-level Windows debugging services a la WDEB386, but residing in the same machine, so no COM port hookup is needed. To take advantage of this feature, you must have a separate video adapter (PCI or ISA monochrome) and matching monitor attached to your system. This program started out in early 1988 as an internal tool in Qualitas to help write and debug 386MAX, but has matured into a significant program in itself (the executable is over 400 KB generated from over 3MB of 386 assembly language source code). As it stands now, 386SWAT can debug almost any
Although 386SWAT was written to debug 386MAX, it can also intrude into the PL0 context of other memory managers as well so as to function as a PL0 debugger there. It also can debug programs remotely (over a modem) and can display source code instead of just disassembly instructions, along with many other features. I'd be interested in getting some feedback from you as to how useful 386SWAT is as well as how I can improve it. Questions or comments, please e-mail the author at bsmith@sudleyplace.com, or perhaps you'd like more detailed documentation, or just jump right in and download it. |
================================================ FILE: swat/SWATAFLT.HTM ================================================
Have you ever been puzzled by some CPU fault as to why it occurred, that is, why did the CPU think there was a problem? If so, this feature is for you. Whenever 386SWAT is called on the difficult to figure out CPU faults (TSS Fault, Stack Fault, GP Fault, or Page Fault), the Autofault feature attempts to determine why and to present a short prose description of the cause. The description is displayed in the lower lefthand corner of the screen. Pressing a key causes the message to disappear. To display it again (up to the time another fault occurs), press s-F4.
This feature is implemented for GP, TSS, and Page Faults, although they all could use more tweaking. ================================================ FILE: swat/SWATBMRK.HTM ================================================
At times you need to browse through some code (sometimes it's even your own code) following subroutine calls, conditional jumps, etc. wherever they might go. To make this task easier, use the s-F1 and s-F2 keys. To disassemble at the target of some instruction which transfers control, place the instruction at the top of the disassembly window and press s-F1. For example, say at offset 1234 in the code segment there is a CALL 5678. Place the CALL at the top of the disassembly window and press s-F1. The disassembly window now displays the code at 5678 with an invisible bookmark left at the calling address 1234. Each time s-F1 is used, it leaves a bookmark at the instruction at the top of the disassembly window and disassembles at that instruction's target. To return to the previous bookmark, press s-F2. 386SWAT supports up to 128 nested levels of such disassembly.
As another example, you should be able to disassemble almost any interrupt (say, U .VMI21 which disassembles at VM interrupt 21h), and using the bookmark feature exclusively, go down to the bottom of the chain.
This feature is clever, but not that clever. It can figure out where (say)
JMP Dword Ptr CS:[1234]
is going and disassemble at that target, but it can't figure out the same instruction with a DS override. Nor can it handle effective addresses other than ones with an immediate displacement only (i.e., no registers such as [BX+SI]). ================================================ FILE: swat/SWATCMD.HTM ================================================
A number of arithmetic, bitwise, and logical functions are available.
The precedence of evaluation is similar to that of the C programming language.
Here they are, listed in order of precedence:
| Functions | Type |
|---|---|
| - ~ | Monadic |
| * / | Dyadic |
| + - | Dyadic |
| symbols, .code, .data, etc. | Address expression |
| : | | Dyadic (address construction) |
| ] [ { | Monadic (extraction) |
| >> << | Dyadic (bit shift) |
| < <= >= > | Dyadic (relational) |
| == != | Equality |
| & | Dyadic (bitwise AND) |
| ^ | Dyadic (bitwise XOR) |
| && | Dyadic (logical AND) |
| || | Dyadic (logical OR) |
2a + 3 * {[.data+2|2c / 4 & ffff == 5af && 3 << bl || 21 ^ 2
is evaluated as
(((2a + (3 * ({([.data+2)|(2c / 4)))) & (ffff
== 5af)) && (3 << bl)) || (21 ^ 2)
| BD | Display debug registers (also Alt-F9). |
| BD addr | Set DR breakpoint on instruction fetches at address addr. |
| BD addr LnI | Set DR breakpoint on I/O of length n (n=1 (byte), 2 (word), 4 (dword)) at I/O port addr. |
| BD addr LnR | Set DR breakpoint on read/writes of length n (n=1, 2, 4) at address addr. |
| BD addr LnW | Set DR breakpoint on writes of length n (n=1, 2, 4) at address addr. |
| BDn* | Clear DRn. |
| BDn+ | Enable DRn. |
| BDn- | Disable DRn. |
| E addr [xx ...] | Enter data starting at addr using optional hex bytes xx. |
| DTE expr | Display the Descriptor Table Entry corresponding to the selector in expr. |
| EXIT | Exit to DOS. This command is equivalent to the following command sequence:
R AH=4C
|
| M addr Llen addr | Move data starting at first addr of length len to second addr |
| H exp | Display hex arithmetic result |
| G | Go without stopping (same as ESC) |
| G addr | Goto to instruction at address addr |
| GM bool_exp | Go monitor (single-step until bool_exp is TRUE) -- see Monitor Mode |
| GM | Go monitor using last boolean expression specified with GM |
| MACBASE addr | Set the base address of the MAC chain in case it's different from .DMAC. This is handy when displaying the DOS subsegment chain. |
| PTE addr
PTE val |
Display the Page Directory and Page Table entries which correspond to the address addr or linear address val. |
| SPTE addr/val | Same as PTE command, but also displays the matching PTE in the PTE display screen (F5). |
| R reg[=]exp | Set register reg to exp. Valid registers include all GP and EGP as well as FL, EFL, CRn, DRn, TRn, TR, and LDTR. There are two ways to crash the system via this command: setting CR3 to a bad value, or setting EFL with a bad value for the VM or IOPL flags. The code which sets CR0 forces the Page Enable and Protect Enable bits on (as evidenced by typing R CR0=0), so experiment without fear. Use the pseudo-register names CSIP or CSEIP to set both registers to an address. |
| R reg.str[=]exp | This command also supports bit mask qualifiers on registers. For example, to set the AM bit in CR0, type R CR0.AM=1. See Register Mask Values for a complete list of mask values supported. |
| RC | Clear saved register state so another RS may execute. |
| RR | Restore saved registers. |
| RS | Save registers to restore later via RR. Only one RS may be executed at a time without either restoring the state via RR or clearing the state via RC. |
| S addr1 addr2 tgt
S addr L len tgt |
Search from addr1 to addr2 or from ea for len bytes for target tgt. |
The target may take one of several forms:
* Use a target of the form xx, xxxx, xxxxxxxx where x represents a hex digit to search for bytes, words, or dwords of a specified value. For example, the form S 0:0 L FFFF 10CD searches for all occurrences of the hex bytes CD followed by 10 in the first 64KB of conventional memory. Word and dword searches do not require word or dword alignment of the matching data. The number of digits entered determines the width of the value. Thus you should use leading zeros to pad out a small value to a wider width. For example, 0 and 00 both search for a single byte of zeros, 000 and 0000 both search for a word of zeros, and 00000, 000000, 0000000, and 00000000 all search for a dword of zeros.
* Use a target of the form "search_string" to search for a case sensitive string. For example, S 0:0 L FFFF "386MAX" searches for all occurrences of the string 386MAX in the first 64KB of conventional memory. The ability to search for a case insensitive string will be added in the future.
* Use a target of the form !instr to search for a specific assembler instruction. For example, S 0:0 L FFFF !INT 10 searches for all occurrences of video interrupts in the first 64KB of conventional memory. This target is found by disassembling the code between the start and stop addresses instruction by instruction, thus the alignment of the matching instructions and the starting address is critical. If data appears within that range, some matches may be missed. The command S1 (instead of S) can be used to disassemble the code byte by byte. That is, with the S command, having disassembled an instruction which does not match the specified pattern, the next instruction is searched; with the S1 command, the next byte is searched. The code search text may include one or more question marks as wildcards which match any character in the disassembled instructions. For example, use S 50|0 FFFF !mov e??,cr? to find all moves from a control register to a 32-bit register. Try the forms S 3BC7:100 FFFF !mov [1234] and S 3BC7:100 FFFF !mov ?s:[1234] to find all moves into location [1234] with or without a segment override. To find jumps to a specific location, use the code targets !j? 1234, !j?? 1234, and !j??? 1234. Note that floating point instructions may be disassembled beginning with either F or FN depending upon the presence of a preceding WAIT opcode (9Bh). To be safe, search for both.
* Use a target of the form #pte
to search for a PTE in the Page Tables. For example, S
0 C0000000 # CF4000 searches for the PTE CF4000
from linear 0 through linear C0000000.
The linear addresses are both rounded down to a 4KB boundary. A match at
a particular linear address means that the PTE was found and it covers
the 4KB block at the linear address displayed. When comparing PTEs, the
flag bits are ignored, thus a match might be found when the PTE in the
Page Tables is not present.
| CD [d:][path]
CHDIR [d:][path] |
Change the current directory to path. If no argument, display the current drive/directory. |
| FS | Flush symbol table. |
| LF filename | Load file into browser. |
| LI + | Enable line number display in disassembly screen. |
| LI - | Disable line number display. |
| LI dddd | Go to line dddd (decimal) in file browser. |
| LI dddd+ | Go to line dddd forward from current line. |
| LI dddd- | Go to line dddd back from current line. |
| LS filename | Load symbol file. |
| LS filename exp | Load symbol file and add 16-bit value to all VM segments. |
| PATH d:\dir1[,d:\dir2[,...]] | Set source file search path. |
| PATH+ d:\dira[,d:\dirb[,...]] | Add to source file search path. |
| PS r | Set range of symbol proximity searches to r. |
| PS r g | Set range r and granularity g (1=bytes, 2=words, 4=dwords) of symbol proximity searches. |
| QS addr | Display the symbol which is nearest to (and below) the given address. |
| SB+ | Enable source browser mode. |
| SB- | Disable source browser mode. |
| SB*+ | Enable source browser mode but disregard module names. |
| TS | Force all symbols to be retranslated according to current GDT and LDT. |
| TS sel | Retranslates only for selector/segment sel. |
| TS * ID | Retranslates for all selectors/segments with ID specified. |
| TS sel ID | Retranslates only for selector/segment sel with ID specified. |
| TS *|sel *|IDv|p | Change to specified mode for selector and/or ID specified. |
| TS *|sel *|ID *|v|p nsel | Replace segment/selector and mode for specified selectors and IDs. If * is specified for mode, the mode is left alone. |
| TS *|sel *|ID *|v|p nsel+ | nsel is added to all specified segments. |
| APPKEY | Edit application keystroke buffer. This is primarily useful for remote debugging. If an application is waiting for a keystroke, this feature allows you to send one to the application, as well as to view any that may already be available. |
| CHAT | Enter CHAT mode (also via Ctl-F8). |
| SETCOM port bps | Initialize specified serial port for communications. See SETCOM= profile option for full syntax. |
| SETCOM | Reinitialize the serial port with values last specified by SETCOM or SETCOM=. This is useful when an application has reprogrammed the UART. |
| SETCOM - | Ignore all activity on serial port. Use this if you are done with remote debugging and wish an application to have access to the serial port. When 386SWAT is using the serial port, no serial port interrupts will be visible to virtual mode programs. |
| SETCOM RTS+ | Pull RTS (Request To Send) line high. |
| SETCOM RTS- | Drop RTS (Request To Send) line low. |
| SETCOM DTR+ | Pull DTR (Data Terminal Ready) line high. |
| SETCOM DTR- | Drop DTR (Data Terminal Ready) line low. This is one way to hang up a modem that won't respond to (wait)+++ (wait)ATH(ENTER). |
| REMDBG | Attempt to establish remote debugging session (also via Ctl-F9). See the section below on remote debugging. |
| VMSCOUNT=val | Don't intrude into the GDT/IDT for VCPI debugging until the value in this counter has decremented to zero. This keyword is useful for occasions where the VCPI client shuffles its GDT and IDT around for a while before deciding just where it's going to be. |
| VMSINT=ON|OFF | Enable (ON) or disable (OFF) VCPI debugging. Use this feature in cases where some VCPI programs misbehave when VCPI debugging is enabled. In this case, enable VCPI debugging only as necessary. |
| VMSINT=xx,xx,... | Change the default interrupts intercepted by 386SWAT when debugging VCPI clients. |
| BTF | Display Branch Trace Facility state (ON or OFF). |
| BTF ON|OFF | Turn Branch Trace Facility state ON or OFF. |
| LBR | Display Last Branch/Exception values on the command line. |
| LBR ON|OFF | Turn Last Branch/Exception window display ON or OFF. The four-line window displays the Last Branch From EIP, Last Branch To EIP, Last Exception From EIP, and Last Exception To EIP. Also, the keywords .LBRFR, .LBRTO, .LEXFR, .LEXTO contain the value of the Last Branch/Exception From/To EIP in case these need to be used in command line expressions (e.g., U .LBRFR). |
| IPF [/d] [/s] [/r] expr | If Invalid Page Faults are being trapped by 386SWAT's VxD (see SWATVXD.DOC for more details), use the IPF command to control how these events are to be handled. The optional switch /d tells 386SWAT not to display a message on the mono screen describing this event, /s tells 386SWAT not to stop when this event occurs, /r tells 386SWAT to remove this entry from its local tables, and expr is an expression which evaulates to a linear address corresponding to the Invalid Page Fault. |
| MDB expr | Display the memory which corresponds to the selector expr as a Module Database. |
| SGH [/b|/s|/h|/o] [/c] expr | Search through the Windows Global Heap for values. The expression (expr) entered is interpreted as a base address if /b is specified, size if /s, handle if /h, and owner if /h. If /c is specified, the search continues from the currently displayed entry; otherwise, the search starts at the top of the heap. |
| TDB expr | Display the memory which corresponds to the selector expr as a Task Database. |
| WKD [ON|OFF] | Turn ON or OFF Kernel Debugging. This command cannot be used from within Windows. |
| WKD [QUIET|NOISY] | Disable (QUIET) or Enable (NOISY) reports on Parameter Errors. |
| WKD LOGERROR [ON|OFF] | Turn ON or OFF the INT 01h trap of calls to LogError (). |
| WKD FAULT [ON|OFF|SKIP] | Turn ON, OFF, or SKIP once traps for Faults. |
There are a number of points in memory to which it is common to refer, e.g., the address of the instruction at the top of the disassembly window. These references are made easier by using one of the following shortcuts (all of which can be used anywhere on the command line where an address is expected such as BD .CODE, or BD .DATA L4 W):
| .EA | Effective Address of the first (or only) operand to the instruction at the top of the disassembly window |
| .EA2 | Effective Address of the second operand to the instruction at the top of the disassembly window |
| .GDT | GDT base address (using selector zero) |
| .IDT | IDT base address (using selector zero) |
| .LDT | LDT base address (using selector zero) |
| .TSS | TSS base address (using selector zero) |
| .CMAC | Seg:Off of next C MAC entry -- equivalent to .DATA + 2 + FFFE & [.DATA |
| .CODE | current code display address |
| .CSIP | current cs:[e]ip |
| .DATA | current data display address |
| .DMAC | Seg:0 of first DOS MAC entry |
| .NMAC | Seg:0 of next DOS MAC entry -- equivalent to ((S..DATA)+1+[.DATA+3):0 |
| .LBRFR | EIP of Last Branch From |
| .LBRTO | EIP of Last Branch To |
| .LEXFR | EIP of Exception From |
| .LEXTO | EIP of Exception To |
| .MDB | Base address of the current Module Database (Windows only) |
| .PMIxx | Sel|Off of PM Interrupt xxh |
| .RMIxx | Seg:Off of RM interrupt # xx |
| .TDB | Base address of the current Task Database (Windows only) |
| .VM | Sel|Off of current Windows VM structure |
| .VMIxx | Seg:Off of VM interrupt # xx |
| .VMRET | Return CS|EIP saved in .VMSTK at .VMSTK+50 (DPMI fn 0300) or .VMSTK+150 (emulated INT) |
| .VMSTK | Sel|Off of stack saved in .VM |
| .XBDA | Seg:Off of XBDA; same as ([40:0E):0 |
| .XBDA2 | Seg:Off of 2ndary XBDA; same as ((S..XBDA)+[.XBDA+B4):0 |
| Keyword | Grammar Equivalent | Meaning |
| .RETND | {LaSTK | Near dword |
| .RETNS | [LaSTK | Near word |
| .RETFD | :{LaSTK or |{LaSTK | Far word:dword or word|dword (depending upon the VM bit in the current EFL) |
| .RETFS | :[LaSTK or |[LaSTK | Far word:word or word|word (depending upon the VM bit in the current EFL) |
| .RETN | .RETND or .RETNS | Depending upon the D-bit in CS |
| .RETF | .RETFD or .RETFS | Depending upon the D-bit in CS |
| .IRET | .RETF in VM
.RETFD in PM |
Also allows mode switch from PM to VM by checking VM bit in EFL above return address |
No magic is invoked to extract the return address if data has been pushed onto the stack below the return address, so be sure that LaSTK points to the actual return address.
Also note that the keystrokes A-F and A-N are defined as shortcuts for the commands G .RETF and G .RETN, respectively. ================================================ FILE: swat/SWATDEF.HTM ================================================
trapdiv ; Direct INT 00h
to 386SWAT (Divide Overflow Fault)
trapnmi ; Direct INT 02h
to 386SWAT (Non-maskable interrupt)
trapbound ; Direct INT 05h to 386SWAT
(BOUND Fault)
trapinv ; Direct INT 06h
to 386SWAT (Invalid Opcode Fault)
trapstack ; Direct INT 0Ch to 386SWAT
(Stack Fault)
; debug=caps ; Signal NMI if CapsLock pressed at startup
; debug=ibv ; Use Interrupt mask base Vector
for Ctrl-Alt-PAD5
; debug=int ; Signal INT 03h if CapsLock present
at startup
; debug=pmi ; Signal INT 03h near end of INIT_PROT
; debug=trip ; Use triple fault method to reboot
; debug=vmi ; Signal INT 03h on entry to INIT_VIRT
; debug=wcb1 ; Signal INT 01h on WCB VM->RM
================================================
FILE: swat/SWATDISP.HTM
================================================
The debugger presents its output on various display devices in various contexts. Typically, it uses the primary display, but other options are possible. If you have a secondary display, such as a monochrome adapter/monitor or if you are running on a PCI system with a second PCI VGA adapter, 386SWAT can use those also.
If you have a monochrome adapter in your system, place the keyword MONO in your 386SWAT profile. The debugger determines whether or not your monochrome adapter is always visible or is hidden by a PCI VGA card, even an AGP controller. In either case, 386SWAT ferrets out the adapter and displays its output there. Pressing Alt-F7 switches between the monochrome and color adapters.
If you have a secondary PCI VGA adapter in your system, place the keyword DVGA in your 386SWAT profile. The debugger displays its output on this screen, even if it's hidden by an AGP controller. Pressing Alt-F7 switches between the two PCI VGA adapters.
I learned a bit about PCI programming getting the above to work, so I've gathered together assembler code to demonstrate the techniques used for you to browse. Thanks go to Dominik Behr, Federico Bianchi, Ralf Brown, and others for their helpful suggestions and/or source code. ================================================ FILE: swat/SWATDOC.HTM ================================================
This protected mode debugger when used in conjunction with 386MAX or any other memory manager provides debugging services to protected mode, virtual 8086 mode programs, Windows 3.1x and Win95/98 programs, and DPMI and VCPI clients while occupying only a few kilobytes of conventional memory. It supports disassembly of the full 386, 387, 486, Pentium, Pentium Pro through the Pentium 4 MMX & SSE instruction set. Note that all of this documentation is included in the download file in HTML format.
Installing 386SWAT -- getting it into your system
386SWAT Profile Options -- configuring it
Invoking The Debugger -- bringing it up
Monochrome and Dual PCI VGA Support -- displaying it
Debugging Aids in 386MAX -- in case you're running my favorite memory manager
Debugging Screen and Keystrokes -- what it looks like and what you can type
Disassembly Bookmarks -- a feature for browsing through assembly code
Autofault -- a feature for understanding faults
Windows Debugging -- how it works under Windows
Windows Kernel Debugging -- very low-level debugging
Monitor mode -- conditional tracing
Command Line Actions -- built-in commands
Common Memory References -- dot commands
Register Mask Values -- bit mask modifiers for registers
NDP Register Screen -- Numeric Data Processor display
Remote debugging -- over a COM line
VCPI Program Debugging -- going where no debugger had gone before
Wish List -- it'll never be finished
Version History -- what's been done
Limitations
This program is very preliminary. It lacks many of the features of a
full-blown debugger. On the other hand, it has a few features they don't
have.
Technical Support
Please send your problems, praises, and comments to bsmith@sudleyplace.com ================================================ FILE: swat/SWATGRM.HTM ================================================
// Command lines
APPKEY
BCx opt_addr
x = {<empty>, *, +,
-}
BD opt_addr
BD addr Ln
R n
= {<empty>, 1, 2,
4}
BD addr Ln
W n = {<empty>,
1, 2, 4}
BDnx
x = {*, +,
-} n = {<empty>, 0,
1, 2, 3}
BTF
BTF {ON|OFF}
CHAT
Dxy-
x = {<empty>, b, d,
g, i, t,
t2, t3, v,
w},
y = {<empty>, /n} where
n is the data width
Dxy opt_addr
x = {<empty>, b, d,
g, i, t,
t2, t3, v,
w},
y = {<empty>, /n} where
n is the data width
Dxy addr P
x = {<empty>, b, d,
g, i, t,
t2, t3, v,
w},
y = {<empty>, /n} where n is
the data width
Dxy addr P
exp x =
{<empty>, b, d,
g, i, t,
t2, t3, v,
w},
y = {<empty>, /n} where
n is the data width
DTE exp
E addr
E addr lvallist
EXIT
F addr
L lval lval
F addr L
lval lval P
F addr L
lval lval P
exp
FS
G opt_addr
GM opt_boolexp
H addr
Ix lval
x = {<empty>, b, d,
w}
IMR
INSERT gdtr idtr [cr3 [lapde]]
INSERT * idtr
IPF [/d]
[/s] [/r] exp
IRR
ISR
LBR
LBR {ON|OFF}
LF filename
LI x
x = {+, -}
LI dcon x
x = {<empty>, +, -}
LS filename
x x = {<empty>, lval}
M addr L
lval addr
MACBASE addr
MDB exp
Ox lval lval
x = {<empty>, b, d,
w}
PATHx dirlist
x = {<empty>, +}
PS lval x
x = {<empty>, lval (<256)}
PTE addr
PTE exp
PTE addr P
exp
PTE exp P
exp
QS addr
R reg
[=] exp
R reg.str
[=] exp
REMDBG
RC
RR
RS
Sx addr L
exp atom x = {<empty>,
1}
Sx addr addr
atom x = {<empty>, 1}
S addr addr
! instr
S addr L
exp ! instr
S addr addr
# exp
S addr L
exp # exp
SBx
x = {+, -}
SB*x
x = {+, -}
SETCOM x
x = {<empty>, -}
SETCOM port bps
port = {1, 2,
3, 4}
SETCOM xn
x = {RTS, DTR}
n = {+, -}
SGH [/b|/s|/h|/o|/n]
[/c] exp
SIGINT lval
SPTE addr
SPTE exp
SPTE addr P
exp
SPTE exp
P exp
TDB exp
TOGINT lvallist
TS
TS opt_sel
opt_group x opt_seln
opt_addr
x = {<empty>, *, P,
V} n = {<empty>, +,
-}
Ux-
x = {<empty>, 16, 32}
Ux opt_addr
x = {<empty>, 16, 32}
Ux addr
P
x = {<empty>, 16, 32}
Ux addr
P exp
x = {<empty>, 16, 32}
VMSCOUNT xx
VMSINT {ON|OFF}
VMSINT=xx,xx,...
WKD FAULT [ON|OFF|SKIP]
WKD LOGERROR [ON|OFF]
WKD [ON|OFF]
WKD [QUIET|NOISY]
| lvallist | lval |
| lval lvallist |
| opt_addr | <empty> |
| addr |
| addr | exp | using default segment/selector as per specific command |
| ea |
| opt_boolexp | <empty> |
| exp |
| exp | ( exp ) | |
| mfn | monadic functions | |
| dfn | dyadic functions |
| mfn | atom | |
| [ ea | extract word at effective address | |
| { ea | extract dword ... | |
| O.ea | extract offset from effective address | |
| S.ea | extract segment/selector ... | |
| L.ea | extract linear address ... | |
| P.ea | extract physical address ... | |
| +exp | ||
| -exp | ||
| ~exp |
| lval | atom |
| ( exp ) |
| ea | seg : exp | |
| sel | exp | ||
| .EA | Effective Address #1 (or the only one) | |
| .EA2 | ... #2 | |
| .GDT | GDT base address (using selector zero) | |
| .IDT | IDT ... | |
| .LDT | LDT ... | |
| .TSS | TSS ... | |
| .CMAC | address of next C MAC entry | |
| .CODE | current code display address | |
| .CSIP | address of current cs:[e]ip | |
| .DATA | current data display address | |
| .DMAC | segment of first DOS MAC entry | |
| .NMAC | segment of next DOS MAC entry | |
| .PMIxx | Sel|Off of PM Interrupt xxh | |
| .RMIxx | Seg:Off of RM interrupt xxh | |
| .VM | Sel|Off of current Windows VM structure | |
| .VMIxx | ... VM ... | |
| .VMRET | return cs|eip from Windows VM (|{.vmstk+50 or |{.vmstk+150) | |
| .VMSTK | current ss|esp saved in Windows VM structure (same as |{.vm+40) | |
| .IRET | far word:dword return address on stack allowing a mode switch from PM to VM | |
| .RETN | near word or dword return address on stack | |
| .RETND | near dword return address on stack | |
| .RETNS | near word return address on stack | |
| .RETF | far word:word or word:dword return address on stack | |
| .RETFD | far word:dword return address on stack | |
| .RETFS | far word:word return address on stack | |
| .XBDA | Seg:Off of XBDA; same as ([40:0E):0 | |
| .XBDA2 | Seg:Off of 2ndary XBDA; same as ((S..XBDA)+[.XBDA+B4):0 | |
| dotcmd ? exp | dyadic functions on dot commands (.GDT, .IDT, etc.) where ? is a dyadic function | |
| :[ ea | extract word:word at effective address | |
| :{ ea | extract word:dword at effective address | |
| |[ ea | ... word|word ... | |
| |{ ea | ... word|dword ... | |
| |G ea | ... ... in GDT-format (using selector zero) | |
| |I ea | ... ... in IDT-format (using IDT selector) | |
| |L ea | ... ... in LDT-format (same as GDT-format) | |
| |T ea | ... ... in TSS-format (using CS|EIP) | |
| symbol | effective address of this symbol |
| dotcmd ? exp
|
S.dotcmd : O.dotcmd ? exp | for VM addresses |
| S.dotcmd | O.dotcmd ? exp | for PM addresses
where ? is a dyadic function |
| dfn | atom | |
| lval + exp | addition | |
| lval - exp | subtraction | |
| lval * exp | multiplication | |
| lval / exp | division (with truncation towards zero) | |
| lval & exp | bitwise AND | |
| lval ^ exp | bitwise XOR | |
| lval == exp | is equal (eq) | |
| lval != exp | is not equal (ne) | |
| lval < exp | is less than (lt) | |
| lval <= exp | lt or eq | |
| lval > exp | is greater than (gt) | |
| lval >= exp | gt or eq | |
| lval && exp | logical AND | |
| lval || exp | logical OR |
| Operators | Type |
| - ~ | Monadic |
| * / | Dyadic |
| + - | Dyadic |
| symbols, .code, .data, etc. | Address expression |
| : | | Dyadic (address construction) |
| ] [ { | Monadic (extraction) |
| >> << | Dyadic (bit shift) |
| < <= >= > | Dyadic (relational) |
| == != | Equality |
| & | Dyadic (bitwise AND) |
| ^ | Dyadic (bitwise XOR) |
| && | Dyadic (logical AND) |
| || | Dyadic (logical OR) |
| seg | lval |
| sel | lval |
| opt_sel | <empty> |
| * | |
| seg | |
| sel |
| opt_group | <empty> |
| * | |
| con |
| atom | con |
| reg | |
| .LBRFR | |
| .LBRTO | |
| .LEXFR | |
| .LEXTO |
| reg | GP | AX, AL, AH, BX, ... |
| EGP | EAX, EBX, ECX, ... | |
| CRn | Control registers | |
| DRn | Debug registers | |
| TRn | Test registers | |
| Misc | IP, EIP, FL, EFL, TR, LDTR |
| con | <32-bit hex values> |
| dcon | <16-bit unsigned decimal values> |
| filename | <DOS pathname> |
| dirlist | <List of directory names separated by commas (,)> |
| bps | 55 |
| 110 | |
| 300 | |
| 600 | |
| 1200 | |
| 2400 | |
| 4800 | |
| 9600 | |
| 19200 | |
| 38400 | |
| 76800 | |
| 115200 |
Examples
* To display the successive entries in the DOS memory allocation chain, display one such entry via a DB command. Then type
/D ((S..DATA)+1+[.DATA+3):0
Continuing to press Enter displays the successive MAC entries.
* To display the successive entries in the C memory allocation chain, display one such entry (at the count word) via a DB command. Then type
/D .DATA + 2 + FFFE & [.DATA ================================================ FILE: swat/SWATINST.HTM ================================================
Create a directory on your hard disk (say, C:\386SWAT), copy the ZIP file to that directory, and unzip it there. To unzip 386SWAT, use
PKUNZIP 386SWAT
This debugger can be used in conjunction with any memory manager which supports VCPI, or with just HIMEM.SYS.
To install 386SWAT, place the following line into your CONFIG.SYS file:
Device=d:\path\386SWAT.LOD options
Place the Device= statement for 386SWAT immediately following the Device= for the memory manager (if any), or HIMEM.SYS if no memory manager is being used. d:\path\ represents the drive and path location of 386SWAT and options represents zero or more profile options.
As there are several options you may wish to use with 386SWAT, we recommend that you put all 386SWAT keywords in a profile one keyword per line, and point to the profile with the PRO= option on the 386SWAT.LOD line. For example,
Device=d:\path\386SWAT.LOD
PRO=d:\path\386SWAT.PRO
================================================
FILE: swat/SWATINV.HTM
================================================
Generally, the debugger lies in the background waiting for some catastrophic event to occur.
To bring up the debugger from the keyboard, press Ctl-Alt-Pad5. That is, with the Ctl- and Alt-keys held down, pressing the 5 key on the numeric pad invokes the debugger. This mechanism is useful when your program is stuck somewhere. Remember, the keyboard and the keyboard interrupt must be enabled for this to work. On systems for which the Pad5 key is inconvenient to use (such as some laptops), the sequence Ctl-Alt-SysReq also brings up the debugger.
It is also handy to install an NMI switch for when the system really gets locked up. Be sure to put the keyword TRAPNMI in the 386SWAT profile.
To run a particular program through the debugger, use the SWATRUN utility.
If remote debugging is active (see SETCOM profile option and SETCOM command) the debugger may also be activated by sending SWT! followed by a break signal. This is done by pressing Ctl-6 in the CHAT screen.
The utility SWATCMD (loaded via Device= or from the DOS command line) may be used to pass commands to 386SWAT. Invoking SWATCMD with no options brings up 386SWAT at an INT 03h (assuming TRAPSKIP or TRAPDEBUG is active). ================================================ FILE: swat/SWATMAX.HTM ================================================
The following options are recognized by 386MAX and may be useful in conjunction with 386SWAT. These options should be placed in your 386MAX profile.
To catch even-value stack wraps, use DEBUG=NOWRAP. This situation occurs when an interrupt is encountered with SP = 2 or 4. In this case, the 386 will push the three words onto the stack and wrap to the end of the stack segment leaving SP = FFFC or FFFE. Quite likely, this is an error as most stack segments aren't meant to be 64 KB in length. Technically, this event is not an error as far as the 386 is concerned, but logically it almost always is an error. The default behavior of 386MAX is to emulate the behavior of the CPU and not signal an error; the DEBUG=NOWRAP keyword tells 386MAX to trap this case.
To catch writes into unmapped EMS pages, use DEBUG=EMSWRIT. This option traps with a Page Fault attempts to write into unmapped EMS pages as well as after a Save Page Map call. The latter is presumed to have been done by a memory-resident program prior to mapping in its own pages. To catch reads and/or writes in unmapped EMS pages, use DEBUG=EMSRDWR. To catch an EMS bug in MS-DOS 4.0x, try this latter option with BUFFERS in EMS memory.
To catch unemulated Invalid Opcodes, use DEBUG=I06.
To signal an INT 01h on each DPMI error, use DEBUG=DPMIERR.
To force a new selector on all selector allocates, use DEBUG=DPMINEWSEL. This option is useful if your code has a stale selector, that is, you allocate a selector and tuck it away for later use, but then, before its last reference, you free it and allocate another selector. Without this option you might get the old selector number (the one you just freed). When the stale selector is used, it might not generate an error, but surely it won't do what you expected. ================================================ FILE: swat/SWATMON.HTM ================================================
The GM (go monitor) command takes an expression which will be evaluated as the CPU single-steps (equivalent to Padplus or F11). No display will occur until
1) the monitor expression evaluates TRUE, or
2) 386SWAT is invoked by some other means (GP fault, NMI, Ctrl-Alt-Pad5,
etc.)
Boolean expressions may be constructed using the dyadic functions &&, ||, <, <=, ==, >=, and >. Function precedence is the same as the C language. See Command Line Actions.
For example:
gm ah
will execute until AH is non-zero.
gm [.csip == 21cd && ah!=9
will execute until the current instruction is INT 21 and AH
is any value other than 9 (DOS display string).
gm cx == 0
will execute until CX is 0.
gm
will execute until the last expression specified with gm
is TRUE.
================================================
FILE: swat/SWATNDP.HTM
================================================
This feature is preliminary, but it does allow you to move through the Numeric Data Processor (NDP) registers and settings and change them at will. Lacking is an exact binary to decimal conversion algorithm as well as a decimal to binary conversion algorithm. This screen can be displayed and cleared via Alt-F8. ================================================ FILE: swat/SWATPRO.HTM ================================================
The following profile options are recognized by 386SWAT. Options
may be entered in upper and/or lower case. See the file 386SWAT
for a list you can use already in profile format.
| [section name] | For versions of DOS which support MultiConfig, this option limits profile processing to the matching MultiConfig section in CONFIG.SYS. | ||||||||||||||||||||||||
| ADDRHBITS=n | Bits to use for address hashing (8-12; default 12). See SWATSYM.DOC. | ||||||||||||||||||||||||
| ALTSCR | Display debugging information on the screen other than the current one being used. That is, in a two monitor system, if the current screen uses the color adapter, display debugging data on the monochrome screen and vice versa. This option is valid only if we detect that there are both a color and monochrome adapter in the system. Otherwise, it is ignored. Note that the screens can be swapped via Alt-F7. An alias for this option is /A. | ||||||||||||||||||||||||
| BUCKETS=n | Specify the number of name hashing blocks to allocate (about 1K each). See SWATSYM.DOC. | ||||||||||||||||||||||||
| CMDHIST=nnn | Specify the size of the command history buffer used by command lie recall. Default is 1024. | ||||||||||||||||||||||||
| COLDBOOT | Don't write 1234h to warm boot flag location when rebooting system from within 386SWAT. | ||||||||||||||||||||||||
| DEBUG=TRIP | Use triple fault method of rebooting. Sometimes a system doesn't reboot when using the 8042 method (the default), so here's another way to skin the cat. | ||||||||||||||||||||||||
| DVGA | Use a Dual VGA screen as the secondary monitor. | ||||||||||||||||||||||||
| GPSKIP=key[,key] | If a GP Fault occurs on any of the instructions named in
the list, do not signal this to 386SWAT. This option allows you to trap
GP Faults but filter out ones which may commonly occur but not be of interest.
The key values (GP Skip instructions) supported are
|
||||||||||||||||||||||||
| INTRUDE | Attempt to intrude into another memory manager's PL0 context. If this is successful, 386SWAT appears as a PL0 debugger in the context of the memory manager. This option is now the default. To disable this option, use VCPISWAT. | ||||||||||||||||||||||||
| KEYB=cclay | Use international keyboard whose country code/layout is
cclay. Possible values are
|
||||||||||||||||||||||||
| LCD | Specify that an LCD screen is present (and 386SWAT uses the LCD screen attributes). Does anyone know how to detect this case under program control? I would prefer not to require the user to tell us what the screen is like. | ||||||||||||||||||||||||
| LOADLOW | Tell 386MAX it's not OK to load us into extended memory after INIT_REAL and to relocate our INIT_VIRT code. It's highly unlikely you'll need this option. | ||||||||||||||||||||||||
| LOADSYM=filename [optional] | Load specified symbol file with optional arguments:
|
||||||||||||||||||||||||
| LOGSIZE=nnnnn | Define size of error log in bytes. Default is 4096. | ||||||||||||||||||||||||
| MONO | Use monochrome adapter if present. | ||||||||||||||||||||||||
| NOGD | Because some programs may reset the debug registers which you've carefully setup, 386SWAT automatically sets the Global Debug (GD) bit in DR7 on startup so that we can stop such programs before they can do any harm. In case you don't want 386SWAT to do this, use this keyword. | ||||||||||||||||||||||||
| NORMLIDT | Disable Real Mode LIDT redirection. Device 386SWAT uses a separate IDT to handle nasty bugs which write into the Real Mode IDT at 0:0 such as DOS 6.x does during its transient CONFIG.SYS processing. This is the default state. Use RMLIDT to find this kind of bug. | ||||||||||||||||||||||||
| NOSWAP | Don't restore the previous underlying screen when single-stepping. This option is useful in conjunction with the ALTSCR option. This option specifies the initial state only. It can be toggled via Alt-F6. | ||||||||||||||||||||||||
| NOWINK | Disable Windows Kernel Debugging. | ||||||||||||||||||||||||
| PASSTHROUGH=xx,xx,xx,... | Allow one or more hardware interrupts to be passed through to the previous protected mode handler while 386SWAT is active. Currently, these are limited to 76, 77, 0B, and 0C. THIS OPTION SHOULD NOT BE USED UNLESS NECESSARY. For example, when the IBM PS/2 SCSI adapter (8EFE or 8EFF) is used, staying in 386SWAT for more than 1 or 2 minutes will cause the hard disk to lock on the next disk access. PASSTHROUGH=76 will allow one to stay in 386SWAT indefinitely. | ||||||||||||||||||||||||
| PATH=d:\dir1[,d:\dir2[,...]] | Specify source file search path (see SWATSYM.DOC). | ||||||||||||||||||||||||
| PORTINIT=string | Initialize serial port. String may contain any character
except semicolon, including the following escape sequences:
|
||||||||||||||||||||||||
| PRO=d:\path\filename.ext | Read subsequent command line options from a profile. Just as with 386MAX, as you append more and more options to the 386SWAT command line, you may prefer to collect them all in a 386SWAT profile, one per line. These options may be followed by a semicolon and a comment. This profile is handled exactly the same way as is the corresponding profile for 386MAX. | ||||||||||||||||||||||||
| PROXSRCH=r[,g] | Set range and granularity for proximity searching on symbol addresses. See SWATSYM.DOC. | ||||||||||||||||||||||||
| PS4=xxxx | Periscope 4 hardware debugger board is installed at I/O port xxxx (for reference, the factory setting is 300h). This feature allows 386SWAT to manage the traceback buffer and other features of the Periscope Company's 386 hardware debugger. *NOTE* this feature isn't finished. | ||||||||||||||||||||||||
| RMLIDT | Enable Real Mode LIDT redirection. Device 386SWAT uses a separate IDT to handle nasty bugs which write into the Real Mode IDT at 0:0 such as DOS 6.x does during its transient CONFIG.SYS processing. | ||||||||||||||||||||||||
| SAVESCREEN=nnn | Specify the number of last screens to save. This keyword allows you to control how many screens back Alt-F10 can display. The default is sixteen. Each screen consumes 4000 bytes of storage in extended memory. | ||||||||||||||||||||||||
| SETCOM=port,bps[,itype[,portbase]] | Specify port to use for remote debugging.
|
||||||||||||||||||||||||
| SYMFILTER=text1 [text2 [...]] | Some symbols, especially from Windows programs written in
C, are prefaced with text such as "__imp__",
"_", and the like which adds to the symbol's
length but not understanding. This feature allows you to specify in the
386SWAT profile leading text which is to be stripped from each symbol.
The default settings are SYMFILTER=__imp__ _ Up to 128 characters can be specified in this way. |
||||||||||||||||||||||||
| SYMSIZE=nnnnn | Specify the number of bytes to reserve for the symbol table. The default size is 4096. See SWATSYM.DOC. | ||||||||||||||||||||||||
| TRAPBOUND | Trap BOUND instruction interrupts, ignoring INT 05h. | ||||||||||||||||||||||||
| TRAPDEBUG | Intercept INT 01h/03h at installation time. Normally, 386MAX directs protected mode occurrences of INT 01h/03h to 386SWAT and real mode occurrences to the real IDT handler. This options causes all such interrupts to be handled by 386SWAT. This feature may be toggled via Alt-F1. | ||||||||||||||||||||||||
| TRAPDIV | Trap divide overflow interrupts. | ||||||||||||||||||||||||
| TRAPGENP | Trap General Protection Faults (toggle via Alt-F3). | ||||||||||||||||||||||||
| TRAPINV | Trap Invalid Opcode interrupts (toggle via Ctl-F3). | ||||||||||||||||||||||||
| TRAPNMI | Trap Non-Maskable Interrupts. This option is useful in conjunction with a hardware breakout switch which can be used to invoke the debugger even if all interrupts are disabled (toggle via Alt-F2). | ||||||||||||||||||||||||
| TRAPPAGE | Trap Page Faults (toggle via Alt-F4). | ||||||||||||||||||||||||
| TRAPSEGNP | Trap Segment Not Present Faults. | ||||||||||||||||||||||||
| TRAPSKIP | Trap INT 03h instructions (toggle via Ctl-F2). | ||||||||||||||||||||||||
| TRAPSTACK | Trap Stack Faults (toggle via Ctl-F4). | ||||||||||||||||||||||||
| TRAPSTEP | Trap INT 01h breakpoints (toggle via Ctl-F1). | ||||||||||||||||||||||||
| TRAPTSS | Trap TSS Faults. | ||||||||||||||||||||||||
| VCPISWAT | Do not attempt to intrude into another memory manager's PL0 context. This option disables the default INTRUDE option. | ||||||||||||||||||||||||
| VIDEO=d:\path\filename.ext | Read in/write to video tables. If the specified file exists, it is read in and used as video table information. If the file doesn't exist, it is created. The information in the video table specifies how to switch to particular video modes as well as how to set certain cursor types. Use this option if you wish to bring up 386SWAT on top of graphic applications on single-monitor systems. *NOTE* this option doesn't fully work as yet, so I suggest that you don't use it. | ||||||||||||||||||||||||
| VMSCOUNT=n | Used with VMSINT. Limit number of times 386SWAT inserts itself into a VCPI client's GDT/IDT. This may be useful when debugging VCPI applications that call Enter Protected Mode (AX=DE0C) repeatedly, such as a real-mode int 08h handler that enters protected mode on every clock tick. The correct value may have to be determined by trial and error. | ||||||||||||||||||||||||
| VMSINT[=xx,xx,...] | Trap VCPI Enter Protected Mode switches (AX=DE0C) and blast in sufficient GDT and IDT entries to debug the client application. This option is useful when debugging a VCPI application which does not follow the preliminary VCPI debugger specification. Use this option with care. The argument (if present) limits the intercepted interrupts to the values. When VMSINT is in effect, it may be important to limit the interrupts. For example, some DOS16M apps (such as Lotus 1-2-3 Version 3.0) intercept interrupts but don't set the access rights byte in the IDT (they assume that it's still set for a 286 interrupt gate, as DOS16M setup originally). Thus when 386SWAT blasts its task gate entries into the IDT, subsequent DOS16M intercepts leave those entries marked as a task gate. This debug option limits us to intercepting those faults necessary to catch catastrophic errors, but not everything. Interrupts which may be intercepted are 00, 01, 02, 03, 05, 06, 0A, 0B, 0C, 0D, and 0E. | ||||||||||||||||||||||||
| WKDLS=nnn | Reserve space for nnn WKD load segment entries. |
Two machines may be connected for remote debugging. The connection may be made via a null modem cable connecting the serial ports on both machines, or via modem.
The connection is initiated by using the SETCOM command to initialize the serial port, then pressing Ctl-F9 on each system to attempt to connect. When a connection is established, the "Press M to become master" prompt appears. Only one of the two systems may become master; the other one then becomes the slave. Processing on the slave system then proceeds normally; the master system is now running a special terminal program.
Keystrokes typed in the master terminal program are sent to the remote
system (with some exceptions - see below), and screen output from the remote
386SWAT system appears on the master terminal screen.
Special keys
Ctl-Alt-Del will NOT be sent to
the remote system while in the master terminal screen - it will reboot
the master system. Ctl-F9 invokes
a menu of special options for the master system:
| T | Terminate connection with slave immediately. |
| G | Terminate connection, but have slave go instead of returning to the 386SWAT command prompt. |
| R | Terminate connection, have slave go, but have slave automatically attempt to re-establish connection on next invocation of 386SWAT. |
| B | Terminate connection and reboot slave system. |
| S | Suspend session temporarily. This temporarily exits the terminal program. You may exit 386SWAT. Ctl-F9 again resumes the connection. |
| U | Upload program to remote. |
| D | Download program from remote. |
| Esc | Continue with terminal program |
The following pseudo-records describe the bit masks supported by the
register command where "*" represents reserved
bits with no corresponding name:
| EFL | record | *:13, AC:1, VM:1, RF:1, *:1, NT:1, IOPL:2, OF:1, DF:1, IF:1, TF:1, SF:1, ZF:1, *:1, AF:1, *:1, PF:1, *:1, CF:1 |
| CR0 | record | PG:1, CD:1, NW:1, *:10, AM:1, *:1, WP:1, *:10, NE:1, ET:1, TS:1, EM:1, MP:1, PE:1 |
| PTE | record | FRM:20, PTE_AVL:3, *:2, PTE_D:1, PTE_A:1, PTE_CE:1, PTE_WT:1, PTE_US:1, PTE_RW:1, PTE_P:1 |
| SEL | record | SEL:13, TI:1, PL:2 |
| DR6 | record | *:16, BT:1, BS:1, BD:1, *:9, B3:1, B2:1, B1:1, B0:1 |
| DR7 | record | LEN3:2, RW3:2, LEN2:2, RW2:2, LEN1:2, RW1:2, LEN0:2, RW0:2, *:2, GD:1, *:3, GE:1, LE:1, G3:1, L3:1, G2:1, L2:1, G1:1, L1:1, G0:1, L0:1 |
| TR4 | record | TR4_TAG:21, TR4_WVAL:1, TR4_LRU:3, TR4_RVAL:4, *:3 |
| TR5 | record | *:21, TR5_SSEL:7, TR5_ESEL:2, TR5_CTL:2 |
| TR6 | record | FRM:20, TR6_V:1, TR6_D:1, TR6_DP:1, TR6_U:1, TR6_UP:1, TR6_W:1, TR6_WP:1, *:4, TR6_C:1 |
| TR7 | record | FRM:20,*:7, TR7_HT:1, TR7_REP:2, *:2 |
Debugging Screen
The top of the initial debugging screen consists of a row of the 32-bit general purpose registers, two rows of the segment registers, and one row of CR0, CR2, and extended flags.
The rest of the initial debugging screen displays the instructions to be executed with the stack appearing on the right in one or two columns. If the current instruction references memory, the segment register, offset, and memory value of the reference are displayed on the line separating the registers from the instructions.
The line at the bottom of the screen is used to enter various commands.
The following table summarizes the possibilities. The usual editing keys
are available such as Left, Right, Home, End, Insert, Backspace, and Delete.
Note that a colon (:) is used to separate a segment from an offset (Virtual
8086 Mode) and that a stile (|) is used to
separate a selector from an offset (Protected Mode). The command
line is parsed according to the grammar found in the file 386SWAT.GRM.
Commands entered on the command line are saved in a ring buffer whose length can be changed from the default of 1024 via the profile keyword CMDHIST=nnn.
Previous commands can be retrieved via the keystrokes Alt-<
(previous command) and Alt-> (next command). Pressing
either of these keys repeatedly scrolls through the buffer in the chosen
direction. The keystroke Alt-? displays a history of
(up to 24) commands from which a command can be chosen by scrolling up
or down through the list, or by typing the letter next to the command.
A command may be deleted from this list via the Del key.
Special keystrokes available from the command line include:
| ESC | Continue processing. Equivalent to the Go command. |
| F1 | Display a help screen with submenus. |
| F3 | Display LDT entries. |
| F4 | Display IDT entries. |
| F5 | Display PTE entries. |
| F6 | Display search screen. |
| F7 | Display memory. While this display is active, Ctl-B displays in byte format, Ctl-W, in word format, Ctl-D in dword format, Ctl-V in vector format, Ctl-G in GDT format, Ctl-I in IDT format, and Ctl-T in TSS format. |
| F8 | Display TSS entries. |
| F9 | Display the instruction disassembly screen. |
| F10 | Display screen on entry to debugger. |
| F11 | Single-step the current instruction (see Padplus). This key as well as F12 are useful on systems without a Padplus/Padminus key, or ones for which those keys are difficult to type such as some laptops. |
| F12 | Single-skip the current instruction (see Padminus). |
| s-F1 | Goto the immediate CALL or JMP address of the instruction at the top of the screen and save the address of the current instruction (see Disassembly Bookmarks). |
| s-F2 | Return from the previous s-F1 goto (see Disassembly Bookmarks). |
| s-F3 | Goto the instruction on the top line (see Padstar). |
| s-F4 | Display the last AutoFault message. |
| s-F5 | Display the Real Mode Interrupt Vector Table. |
| s-F10 | Save the current screen into the last screen buffers. |
| a-F1 | Toggle intercept of INTs 01h/03h. The current state appears below the segment/selector register display as 01 or blank, 03 or blank. The default state is controlled by the presence or absence of the TRAPDEBUGkeyword. |
| a-F2 | Toggle intercept of INT 02h. The current state appears below the segment/selector register display as 02 or blank. The default state is controlled by the presence or absence of the TRAPNMIkeyword. |
| a-F3 | Toggle intercept of INT 0Dh. The current state appears below the segment/selector register display as 0D or blank. The default state is controlled by the presence or absence of the TRAPGENPkeyword. |
| a-F4 | Toggle intercept of INT 0Eh. The current state appears below the segment/selector register display as 0E or blank. The default state is controlled by the presence or absence of the TRAPPAGEkeyword. |
| a-F5 | Toggle stack display state between two columns of words and one column of dwords. The tick marks appear every 16 bytes. |
| a-F6 | Toggle screen save state (eliminates screen flicker when single-stepping over instructions which don't write to the screen). The current state appears below the segment/selector register display as SS=ON or SS=OFF. |
| a-F7 | Toggle video base (switch debugging screens in a two-monitor system). |
| a-F8 | Display NDP (floating point) register screen. Use the same keystroke to remove the NDP screen. |
| a-F9 | Display debug register screen. This screen remains active until it is replaced by another screen. That is, you may type on the command line, etc. while the debug register screen is displayed. |
| a-F10 | Display previous debugging screens. This option can be used to compare changes over a single-step or single-skip. Up to sixteen previous screens can be displayed in this manner using the Up and Down arrows. To change from the default value of sixteen, use the keyword SAVESCREEN(see above). |
| a-F11 | Display MMX and SSE2 register screen. |
| a-F | Goto the far return address at SS:SP or SS|eSP (depending upon the current mode). If the current code segment is USE16, the far return address is assumed to be word:word (or word|word); if it's USE32, the format is assumed to be word:dword (or word|dword). This shortcut is equivalent to typing G .RETF at the command line. |
| a-N | Goto the near return address at SS:SP or SS|eSP (depending upon the current mode). If the current code segment is USE16, the near return address is assumed to be word; if it's USE32, the format is assumed to be dword. This shortcut is equivalent to typing G .RETN at the command line. |
| a-< | Retrieve the previous command from the command history buffer. |
| a-> | Retrieve the next command from the command history buffer. |
| a-? | Display the command history buffer. |
| c-F1 | Toggle intercept of INT 01h only. The current state appears below the segment/selector register display as 01 or blank. The default state is controlled by the presence or absence of the TRAPSTEPkeyword. |
| c-F2 | Toggle intercept of INT 03h only. The current state appears below the segment/selector register display as 03 or blank. The default state is controlled by the presence or absence of the TRAPSKIPkeyword. |
| c-F3 | Toggle intercept of INT 06h. The current state appears below the segment/selector register display as 06 or blank. The default state is controlled by the presence or absence of the TRAPINVkeyword. |
| c-F4 | Toggle intercept of INT 0Ch. The current state appears below the segment/selector register display as 0C or blank. The default state is controlled by the presence or absence of the TRAPSTACKkeyword. |
| c-F5 | Display PDE entries. |
| c-F6 | Display symbols. |
| c-F7 | Display file browser. |
| c-F8 | Enter CHAT mode. This allows two connected machines to test the serial port connection. What you type is displayed on the lower screen and sent to the other system; whatever is received is displayed on the top screen. If only garbage characters appear the two machines may not have the same data transfer rate set. |
| c-F9 | Attempt to connect for remote debugging. See the section at the end of this document on remote debugging. |
| c-F10 | Display error log. |
| c-F11 | Single-step INT-like instruction in VM to PM, otherwise just single-step (same as c-Padplus). |
| c-Up | Decrement the location pointer to the previous entry. This change has a different effect depending upon the type of information being displayed. If used in a data display, it moves back one data item (byte, word, dword, etc.). If used in a TSS display, the I/O ports in the I/O bit permission map scroll up. |
| c-Down | Increment the location pointer to the next entry. This change has a different effect depending upon the type of information being displayed. If used in a data display, it moves forward one data item (byte, word, dword, etc.). If used in a TSS display, the I/O ports in the I/O bit permission map scroll down. |
| c-Home | Place the current instruction at the top of the screen. |
| c-B | Display memory in byte (xx) format. |
| c-D | Display memory in dword (xxxxxxxx) format. |
| c-G | Display memory in GDT format. |
| c-I | Display memory in IDT format. |
| c-K | Display Windows Kernel Debugger Structures menu. This menu may be displayed only when running under Windows as a kernel debugger. See WINKDBG.DOC. |
| c-M | Display memory allocation chain entries based at the value assigned to MACBASE. By default, this value is the initial value of .DMAC. |
| c-T | Display memory in TSS format. |
| c-V | Display memory in vector (xxxx:xxxx) format. |
| c-W | Display memory in word (xxxx) format. |
| c-Z | Zap (convert to NOPs) the instruction at the top of the instruction disassembly window. |
| c-ESC | Same as ESC, but if you're on an INT 03h, it skips over it first and then continues. If the current instruction is not an INT 03h, this keystroke behaves identically to ESC. |
| Padplus | Single-step the current instruction (same as F11). |
| c-Padplus | Single-step INT-like instruction in VM to PM, otherwise just single-step (same as c-F11). |
| Padminus | Single-skip the current instruction (same as F12). That is, execute the current instruction and put a breakpoint on the instruction following. This is used to execute but not single-step through a CALL or LOOP instruction. |
| Padstar | Goto the instruction on the top line (same as s-F3). |
| Up | Scroll the screen up one line. This key has the same effect in almost all screen displays. |
| Down | Scroll the screen down one line. This key has the same effect in almost all screen displays. |
| PgUp | Scroll the screen up one page. This key has the same effect in almost all screen displays. |
| PgDown | Scroll the screen down one page. This key has the same effect in almost all screen displays. |
| s-PrtSc | Print the screen. Note that if either the previous application screen (F10) or one of the previous debugging screens (a-F10) is currently displayed, that screen is sent to the printer. The I/O port in the BIOS data area which corresponds to LPT1 is used. |
| Ctl-Alt-Del | Reboot the system. |
This document covers the basics of symbolic debugging support for 386SWAT. The 386SWAT API specifics are documented in VCPIDBG.DOC; here we will describe actual usage of SWAT for symbolic debugging, and the tools needed to load symbols.
Several profile options are new to 386SWAT:
Specify number of address bits to use for address hash table. Values less than 8 and over 12 are ignored.
Address hash table storage in bytes is 2 ^ (ADDRHBITS + 2). The minimum is therefore 1K and the maximum, 16K.
ADDRHBITS defaults to 12. When symbols are present, all possible addresses in the disassembly screen are searched against the address hash table. Possible hits require traversal of the address hash buckets chain, similar to name hash bucket traversal. A "next address bucket" pointer is part of the symbol record. Thus the set of symbol records forms two interleaved linked lists: the address bucket chain and the name bucket chain.
The number of bits specified by ADDRHBITS are taken from a resolved linear address, shifted left 2 bits, and used as an index into the table of dword pointers based at the address contained in SYMNHASH. The address pointed to is an offset from the address in SYMBASE, and points to the beginning symbol record in that address bucket chain. If the value is -1 (FFFFFFFF), there is no such address.
Smaller address hash tables may significantly impact unassembly.
Specify number of 1K blocks to be used for symbol name hashing. Values less than 1 and over 255 are ignored.
To speed symbol name searches, all names are hashed using the following algorithm:
extern unsigned int HASHPRIME;
int hashpjw(char *s)
{
char *p;
unsigned long h=0, g;
for (p=s; *p; p++) {
h = (h <<
4) + (unsigned) tolower (*p);
if (g = (h &
0xf0000000)) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
return ((int) (h % HASHPRIME));
}
HASHPRIME is derived from a table of prime numbers closest to multiples of 256. Here are some examples:
BUCKETS HASHPRIME
1
257
2
509
3
769
254 65027
255 65287
The storage occupied by the name hashing table is HASHPRIMES[BUCKETS-1] * 4, or approximately BUCKETS * 1K.
When a hash value is calculated from a name, that value is shifted left 2 bits and used as an index into the table of dword bucket pointers based at the address contained in SYMNHASH. A bucket pointer may contain -1 (FFFFFFFF) or an offset from the address contained in SYMBASE. In this case, a "bucket" actually points to a symbol record. Next name pointers in each symbol record form a linked list which may be traversed to find an exact name match.
The smaller the BUCKETS value, the longer
these bucket chains will be, thus increasing name search time. This
is only likely to be a performance hit when loading symbols, since 386SWAT
checks for existing symbols by name. Searches for symbols entered
at the command line would not be visibly impacted by longer searches.
LOADSYM Enable file I/O functions within 386SWAT. This is needed for using the file browser, and for source level debugging.
LOADSYM=filename.ssf
Enables file I/O (same as LOADSYM).
Also loads symbols from the specified .SSF file (created by MAPSSF)
at initialization time.
Specify search path for source browser. If not specified, only
the current directory is searched. If a path is specified, the current
directory (.) must be included explicitly. For example, PATH=c:\qui\quilib,c:\qui\inst
will not search the current directory for source files.
Specify how far 386SWAT continues proximity searches and with what granularity. The default is 1 word. The high order byte of PROXSRCH contains the granularity in bytes, and the low order byte contains the number of units to search. The default is 0201h. 0108h would do byte granular searches from x+1 to x+8.
Proximity searches should not visibly impact disassembly performance. A separate check for an empty symbol table ensures that no time is wasted in unnecessary calls to the symbol search routine (which has its own check for empty tables). No proximity searches are done for label display and data display; this only affects values within the disassembly.
The command line option PS
allows these settings to be changed on the fly.
This option sets the number of bytes to be used for the 386SWAT symbol table. The internal format of 386SWAT's symbol table is 12 bytes larger than the symbol table used for the 386SWAT API calls. Each public or line number occupies 23 bytes plus the length of the symbol name.
SYMSIZE is used to calculate a default value for BUCKETS (described below). If BUCKETS is not explicitly defined in the profile, it will be set to min (255, max (1, SYMSIZE / 4096)).
If SYMSIZE is not specified, it will default to 4096 bytes. The default BUCKETS value (if also not specified) will then be 1. See below for BUCKETS storage requirements.
Use SYMSIZE=0 to minimize symbol storage
requirements. This will save 4K over the default.
Any symbol may be used as an effective address. For example,
dd 180|{SYMHASH
will display the symbol address hash table. Symbol display occurs automatically if the symbols are loaded properly. See sections below on SWATRUN and MAPSSF.
Symbols are displayed in the Unassemble (F9) screen. Code addresses may be displayed as public names or line numbers (if line numbers were specified with the linker's /LI option). Addresses within the unassembly are handled as intelligently as possible. For example,
mov ax,[bx+7c39]
may be displayed as
mov ax,[bx+FOOBAR]
if FOOBAR's offset is 7C39 and the default segment/selector (DS in this case) matches the segment/selector specified for the symbol FOOBAR.
386SWAT can now resolve addresses CLOSE TO symbols; a construction commonly seen in C code might be:
mov [FOOBAR],bx
mov [7c3b],es
386SWAT will disassemble this as
mov [FOOBAR],bx
mov [FOOBAR+02],es
Another useful feature would be to turn off case sensitivity when searching
for symbol matches. This is more of an issue when debugging C code.
Again, it could be settable in the profile as well as from the command
line.
The Translate Symbols (TS) command may be used to adjust segment/selector values, as well as to add a constant to symbol offsets, after the symbols are loaded. This is needed for debugging the VxD. The VxD symbols would be loaded with MAPSSF, using a .wsg file to assign selector values and group ID's to the different linker groups. Once SWATVXD has located the offsets of the different VxD groups, they may be translated one at a time.
For example, the VxD's DGROUP is assigned selector 28, group ID 2001, and IGROUP is assigned selector 30, group ID 2002. The .WSG file entries are
2001 P
28 DGROUP
; Comments
2002 P
30 IGROUP
; More comments
We load the .MAP file before entering Windows:
MAPSSF -wVXD.WSG VXD.MAP
We find that the offsets within the Windows flat model segment are
DGROUP 8001BE24
IGROUP 8020397C
The TS command is used to translate both groups:
ts 28 2001 P 28 8001BE24
ts 30 2002 P 30 8020397C
The complete syntax of the TS command is
TS osel ogrp nflag nsel nbase
| osel | Old selector value |
| ogrp | Target group value (unless specified in .WSG file, 0) |
| nflag | P for PM symbols
V for VM symbols |
| nsel | New selector value (in above example, unchanged) |
| nbase | Base value to be added to all symbol offsets |
The Query Symbol (QS) command may be used to display on the command line the name and distance from a given address. For example,
QS .code
might display as
QS .code = VWIN32_Code0001+00005678
This program is called by SWATRUN to load .MAP files on the fly. It may also be used to produce .SSF files for loading at CONFIG.SYS time (not currently supported by SWAT). .SSF files may also be loaded directly by SWATRUN. This is faster than calling MAPSSF every time symbols are loaded.
Besides reading .MAP files, MAPSSF will also process Windows SYM32 files. MAPSSF has been tested with the following .MAP file formats:
- Microsoft LINK
- Microsoft LINK386 flat model
- Borland TLINK
Syntax:
MapSSF [options] fspec1 [fspec2 [...]]
All options are preceded with - or /
| -g#,# | restrict symbol group types to specified #'s (SYM32 files only). Only symbols with group types matching one of these values will be loaded. The group type in SYM32 files is NOT the same as the group ID defined in a .WSG file. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -l | ignore line number information in .MAP file. This option may be used to suppress loading of line number symbols when space is at a premium. Typically, line numbers take about 80% of symbol space. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -o | overwrite existing symbol table (default is append). Normally,
new symbols are added to SWAT. Existing
symbols will be updated. This option forces the symbol table to be flushed at the beginning of MAPSSF. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -sxxxxm | set selector to xxxx hex, where m is
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -tfname | echo 386SWAT tables to file fname. This option may be used to create a file in 386SWAT symbol format (.SSF file). | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -wfname[.wsg] | read Windows Symbol Group file fname.wsg. This feature may be
used for .MAP files or for .SYM files. As an example, the QMAX.WSG
file contains:
Association of public symbols with groups is sometimes not sufficient. Both code and data are in PGROUP, but different selectors are used. We therefore specify the individual data segments within PGROUP that will be assigned selector 18h (EDATA and VALSEG). Since segments are more completely specified in the MAP file than groups, MAPSSF will check for segment matches before checking for group matches. We also use this mechanism to avoid loading the NCODE and NDATA segments within PGROUP. Group matches are made by checking group origin values, which are sorted by value in descending order, against addresses. ALL groups which COULD include an address, from highest to lowest, are checked for inclusion in the .WSG file. The first match determines what changes are made to the matching symbol address. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -v | sort symbols by value (default is by name). This option controls which section of the .MAP file is read. If included on the command line, the "By Value" section is read. By default, the "By Name" section is read. By controlling the order in which symbols are passed to SWAT, the display in SWAT's Ctrl-F6 screen may be sorted by name or by value. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -x# | set debugging level to # (default is 0 -- no messages). This is useful for debugging only. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -y | default to recognizing files as SYM32 format. Normally, MAPSSF recognizes files as .MAP or .SYM by their extension. If neither extension is used, this option forces recognition as SYM32. |
Syntax: Device=d:\path\swatcmd.exe
command
or swatcmd command
SWATCMD can be loaded via Device=
or from the DOS command line. It may be used to pass commands to
386SWAT under program control. Invoking SWATCMD
with no options brings up 386SWAT at an INT 03h (assuming TRAPSKIP
or TRAPDEBUG
is active).
Syntax: SWATRUN [options] progname [arguments]
SWATRUN is a program loader for debugging V86 mode programs. It creates a breakpoint before the first instruction of the target program, and will also attempt to load symbols into 386SWAT via the VCPI debugger API (VCPIDBG.DOC) with the appropriate segment fixup.
If no file extension is specified, SWATRUN looks first for a .COM file then an .EXE file. The filename specified is converted into a fully qualified pathname including drive letter, directory, and extension. SWATRUN will load this file using DOS function 4B01 to determine the segment fixup.
SWATRUN will then look for a matching .SSF file, and if found, will
load it directly into 386SWAT. If an .SSF file is not found, SWATRUN
will look for a matching .MAP file. If a .MAP file is found, SWATRUN
will invoke MAPSSF with the proper parameters to fixup and load the parsed
.MAP file. See the section on MAPSSF for further information on .MAP
file formats supported and on creation of .SSF files. .SSF file loading
is faster, but does not currently report symbol allocation table shortages.
If your symbols do not appear to be loaded from an .SSF file, delete the
.SSF file. SWATRUN will try to load symbols from the .MAP file with
MAPSSF. MAPSSF will report any changes required to the 386SWAT.PRO
file.
Valid SWATRUN options are
| /b | Generate Int 1 at beginning of SWATRUN. This is useful only for debugging the loading process |
| /mMAPFILE | Load MAPFILE instead of progname.map. If no .SSF file is present, SWATRUN constructs a map file name by taking the path and basename of the .EXE or .COM file and adding .MAP. This filename is passed to MAPSSF if it exists. This option may be used to specify a map file on another drive:directory. |
| /n | Do not load symbols. If SWATRUN has already run once, the target program did not go resident, and symbols were loaded, run SWATRUN with this option on subsequent invocations. |
| /o | Overwrite existing SWAT symbols. This option is passed to MAPSSF. Any existing symbols will be destroyed. |
| /sSYMFILE | Load SYMFILE instead of progname.ssf. SWATRUN normally constructs a .ssf file name by taking the path and basename of the .EXE or .COM file and adding .SSF. If this file exists and is in the proper format created by MAPSSF, it will be loaded into 386SWAT. |
| /? | Display a help message. |
Version 6.03
In case you do not need to restore a saved register set, the saved state can be cleared with the RC command.
1. The Branch Trace Facility (BTF)
can be turned on and off via the command line BTF ON/OFF.
2. The Last Branch/Exception From/To registers can be displayed
at all times in the lower right corner of the screen. This feature
can be turned on and off via the command line LBR ON/OFF.
Version 5.10.038
The SWATVXD.EXE file is the accompanying VxD to 386SWAT. As long
as it is in the same directory as the debugger file (386SWAT.LOD), it is
loaded automatically by 386SWAT. If for some reason you don't wish
that to happen, using the command WIN NOSWAT
prevents the VxD from loading.
Switches
The VxD provides Windows services for 386SWAT, and is useful for debugging VxDs.
The VxD's operation is controlled by various switches all of which appear in the [386SWAT] section of your SYSTEM.INI file.
SWATDualDisplay
Squirt cool info to the mono display -- you must use this option to
get the most useful output to the mono screen.
SSF=d:\path\name.SSF
Specify the WIN.SSF file -- this file is generated when 386SWAT VxD
is built and contains useful symbol information about Windows VxDs.
SWATVxDROM
In Sys_Critical_Init, mark the Page Table Entries of all VxD code segments
as read-only (normally, they are read-writable!). This feature was
implemented in an attempt to figure out why Windows was crashing (I thought
that there might be a bad pointer out there). While I didn't find anything
in particular, it is handy to rule out this problem. If a VxD code
segment is written into, an Invalid Page Fault is generated (see below).
Likely, you'll find that a single IPF due to this feature is generated
at the start of Windows. The default action (taken by pressing Esc
at the command line) is to test for a RO page and set the RW bit.
This clears the IPF for this one page only. From that point on, all IPFs
should be examined carefully. If this switch is set, the SWATVxDIPF
switch is also set automatically.
SWATVxDIPF
In Sys_Critical_Init, hook the Invalid Page Fault handler. This
feature, in conjunction with the IPF command
in 386SWAT can be useful for debugging these beasts. The mono screen
contains a formatted message of the incoming data to the IPF hook procedure.
This feature is preliminary and provides a way to get control at the point
of the IPF -- after that, you're on your own. If you can suggest
more things 386SWAT can do to help debug IPFs, I'm very open to suggestions.
HookIRQ
Hook IRQ0-7 locally if 386SWAT hooks them. This switch is necessary
for some VxDs (RAM Doubler in particular) as they don't install correctly
if they find some IRQs not hooked by selector 28h. Go figure.
Debugging VxDs
To debug VxDs at their entry points, use the following switches:
HookVxD=DDB_Name[,#s]
Trap at a particular VxD by name. Note that DDB_Name is
case sensitive, and # specifies message numbers (optional).
See the VxD Filename and Message
# tables below.
HookPM=DDB_Name,reg,#
Trap at a particular VxD's PM API entry. reg specifies
a 16-bit register used by the VxD for the function code, and # specifies
the functions to trap. For example, HookPM=SHELL,DX,3
traps at the WINOLDAP hook
Some VxD filenames, DDB names, Device_ID, and their switches:
WINA20.386 'LA20HMA
' xxxxh
NOLOW64KPAGING
NOLOW64KPAGINGPORT
Debug
' ' 0002h
*VPICD
'VPICD ' 0003h
*VDMAD
'VDMAD ' 0004h
*VTD
'VTD ' 0005h
*V86MMGR
'V86MMGR ' 0006h
*PAGESWAP
'PageSwap' 0007h
*VKD
'VKD ' 000Dh
INITPS2MOUSEATEXIT=ON/OFF
*DOSMGR
'DOSMGR ' 0015h
*WSHELL
'SHELL ' 0017h
*PAGEFILE
'PageFile' 0021h
386MAX.VXD 'LoadHi
' 001Ch
SWATVXD.EXE 'SWATVXD ' 2400h
VDDVGA.386 'VDD
' 000Ah
VNETWARE.386 'DOSNET ' 001Ah
PM_BIOS.386 'PMVDD ' 2250h
0000h Sys_Critical_Init
001Bh Sys_Dynamic_Device_Init
0001h Device_Init
001Ch Sys_Dynamic_Device_Exit
0002h Init_Complete
001Dh Create_Thread
0003h Sys_VM_Init
001Eh Thread_Init
0004h Sys_VM_Terminate
001Fh Terminate_Thread
0005h System_Exit
0020h Thread_Not_Executeable
0006h Sys_Critical_Exit
0021h Destroy_Thread
0007h Create_VM
0022h PNP_New_Devnode
0008h VM_Critical_Init
0023h W32_DeviceIOControl
0009h VM_Init
0024h Sys_VM_Terminate2
000Ah VM_Terminate
0025h System_Exit2
000Bh VM_Not_Executeable
0026h Sys_Critical_Exit2
000Ch Destroy_VM
0027h Vm_Terminate2
000Dh VM_Suspend
0028h Vm_Not_Executeable2
000Eh VM_Resume
0029h Destroy_VM2
000Fh Set_Device_Focus
002Ah VM_Suspend2
0010h Begin_Message_Mode
002Bh End_Message_Mode2
0011h End_Message_Mode
002Ch End_PM_App2
0012h Reboot_Processor
002Dh Device_Reboot_Notify2
0013h Query_Destroy
002Eh Crit_Reboot_Notify2
0014h Debug_Query
002Fh Close_VM_Notify2
0015h Begin_PM_App
0030h Get_Contention_Handler
0016h End_PM_App
0031h Kernel32_Initialized
0017h Device_Reboot_Notify 0032h Kernel32_Shutdown
0018h Crit_Reboot_Notify
0019h Close_VM_Notify
001Ah Power_Event
Message Number Traps
To stop inside 386SWAT at various message numbers, use the following switches:
SWATSysCriticalInit
SWATDeviceInit
SWATInitComplete
SWATSysVMInit
SWATSysVMTerminate
SWATSystemExit
SWATSysCriticalExit
SWATCreateVM
SWATVMCriticalInit
SWATVMInit
SWATVMTerminate
SWATVMNotExecuteable
SWATDestroyVM
SWATVMSuspend
SWATVMResume
SWATSetDeviceFocus
SWATBeginMessageMode
SWATEndMessageMode
SWATRebootProcessor
SWATQueryDestroy
SWATDebugQuery
SWATBeginPMApp
SWATEndPMApp
SWATDeviceRebootNotify
SWATCritRebootNotify
SWATCloseVMNotify
SWATPowerEvent
SWATSysDynamicDeviceInit
SWATSysDynamicDeviceExit
SWATCreateThread
SWATThreadInit
SWATTerminateThread
SWATThreadNotExecuteable
SWATDestroyThread
SWATPNPNewDevnode
SWATW32DeviceIOControl
SWATSysVMTerminate2
SWATSystemExit2
SWATSysCriticalExit2
SWATVMTerminate2
SWATVMNotExecuteable2
SWATDestroyVM2
SWATVMSuspend2
SWATEndMessageMode2
SWATEndPMApp2
SWATDeviceRebootNotify2
SWATCritRebootNotify2
SWATCloseVMNotify2
SWATGetContentionHandler
SWATKernel32Initialized
SWATKernel32Shutdown
Internal Use Only
The following switches are for internal use only.
SSFPrefix=name
Specify device name and DDB prefix.
SWATDebugThruExit
Call 386SWAT's INIT_PROT during Sys_Critical_Exit. This allows
debugging on the way out of 386SWAT. Now that 386SWAT is a Windows
kernel debugger, this switch is unnecessary.
RealModeBreak=ON/OFF
Hit an INT 1 early in RealModeInit.
Rehook123=ON/OFF
Blast INTs 1, 2, and 3 into the IDT. To avoid trouble with WDEB386.
Now that 386SWAT is a Windows kernel debugger, there should be less need
to run WDEB386.
================================================
FILE: swat/SWATWDBG.HTM
================================================
386SWAT runs as a debugger under Windows 3.x and 95/98 when accompanied by its VxD. This file (SWATVXD.EXE) should be in the same directory as the debugger file (386SWAT.LOD) and is loaded automatically when Windows starts. To take advantage of the information provided by the VxD, you should have a monochrome monitor attached to your system. For more details about the VxD including how to configure it, see the file SWATVXD.DOC. ================================================ FILE: swat/SWATWISH.HTM ================================================
Here's my wish list (in no particular order) of things I would like this debugger to do. Feel free to contribute yours in our newsgroup:
If you program under Windows 3.1x or Win95/98 (but not WinNT), 386SWAT presents itself to Windows as a very low-level debugger. Essentially, 386SWAT is provided the same level of access as the remote debugger WDEB386 shipped with Windows. WDEB386 requires that you run it from a separate system with a COM cable running between the two systems. In contrast, 386SWAT runs on the same system being debugged, however it does require that you have a monochrome adapter and monitor installed in the system.
For more details on this topic, see the file WINKDBG.DOC. ================================================ FILE: swat/VCPIDBG.DOC ================================================ Preliminary Proposal For Debugger Extensions to VCPI Specification 30 May 89 Amended 22 Aug 90 Amended 9 Aug 94 Ŀ Rationale The premise is that there is already a resident debugger in the system which the client wishes to use. These calls provide an interface between the client and the debugger to allow that to happen. In order for the debugger to support this interface, it should intercept INT 67h during its initialization and troll for the following calls, ignoring most others. The debugger may wish to troll for the "Set 8259A Interrupt Vector Mappings" call in order to properly determine where the timer and keyboard interrupts start. Ŀ V86 Mode Program Interface OVERVIEW If your program enters PM via DPMI using a memory manager with an integrated DPMI host, skip all this as 386SWAT hooks into the host's GDT and IDT through the INTRUDE option. Note that DPMI hosts which install as a separate device driver appear to the memory manager (and thus to 386SWAT) as a VCPI client, so the above advice to skip this document does not apply. I haven't tried to debug a DPMI client in this context, so I'm not sure how to do it. If your program enters PM via VCPI, use functions DEF0, DE01, DEF4, DEF2, and DEF3 in that order so that 386SWAT is properly initialized in your program's GDT and IDT. If your program enters PM from RM and uses paging, use functions DEF0, DEF4, DEF2, DEF3, and DEF9 in that order although function DEF9 can be executed anytime after function DEF0 executes successfully. If your program enters PM from RM and does not use paging, use functions DEF0, DEF4, DEF2, and DEF3 in that order. When debugging VCPI clients, be sure to enable the VMSINT flag either by putting the keyword VMSINT into your 386SWAT profile, or by typing VMSINT ON from the 386SWAT command line. INT 67h AX = 0DEF0h Determine Debugger Presence INPUTS: None. OUTPUTS: AH = 00h if successful = 84h if VCPI not supported = 8Fh if debugger support not present BH = Debugger specification major version # BL = ... minor ... This call determines whether or not a resident debugger is present in the system. The current specification version number is 5.00; that is, BH = 05, BL = 00. These values are in binary such that if the specification version number were 10.12, this call would return BH = 0Ah, BL = 0Ch. INT 67h AX = 0DEF1h Get Debugger Information INPUTS: None. OUTPUTS: AH = 00h if successful EDX = Physical address of debugger information This call returns the physical address of a certain data structure inside 386SWAT for use in conjunction with the 386SWAT Virutal Device for Windows 3. The contents of the structure is as follows: INFO_LEN dw ? ; Byte count of structure INFO_BASE dd ? ; Physical base address of SWAT code segment SWTINF_VER dw ? ; Version # of structure More fields are defined which are used by SWATVXD. INT 67h AX = 0DEF2h Initialize Debugger Interface, Task Gate INPUTS: ES:DI ==> GDT entries for debugger support If ES = 0, EDI ==> ... BX = initial selector OUTPUTS: AH = 00h if successful = 84h if VCPI not supported = 8Fh if debugger support not present BX:EDX = Address of protected mode entry point This call allows the debugger to setup its own GDT entries as it pleases. The number of entries it may use is limited to thirty (30) so the client can statically allocate space in its GDT. Each GDT entry used by SWAT is a TSS selector, and the IDT entries used (when DEF3 is called) are Task Gates (cf. DEFD). The starting selector number in BX is needed so that the "Initialize Debugger IDT Entry" call knows what selectors to use when initializing an IDT entry. Consequently, this call must precede that call in order to know the proper selector value. If you are writing a PM program which enters PM via VCPI, your call to the VCPI Get PM Interface (DE01) must precede this call as 386SWAT needs to hook into the latter call in order to ensure that its PTEs are visible when the VCPI client is active (and thus know what its new linear address is). The selector and offset returned identify the address of the protected mode entry point in the debugger (see below). This entry point should be called via a USE32 far call as it might be a task gate. INT 67h AX = 0DEF3h Initialize Debugger IDT Entry INPUTS: BX = interrupt # ES:DI ==> IDT entry for interrupt BX If ES = 0, EDI ==> ... OUTPUTS: AH = 00h if successful = 83h if interrupt # not supported by the debugger = 84h if VCPI not supported = 8Fh if debugger support not present This call allows the client to selectively activate the debugger's interrupt handlers. Because some debuggers may support more interrupt handlers than others, it is recommended that the client call this function for all interrupts between 00h and 1Fh. If the debugger doesn't support a particular interrupt number, it will return an error code which the caller can ignore. Some interrupt numbers may be handled wholly by the debugger (not passed on to the previous handler). Some may be chained, with the debugger handling only certain situations (as may be the case if the 8259A's interrupt numbers overlay CPU fault interrupt numbers). For this reason, the client should initialize the IDT entry with a valid handler in case the debugger passes along the interrupt. Because this call may be invoked multiple times on the same interrupt number, the debugger must check the existing entry to see if it is the same as the value to which it would initialize the entry. If that's the case, the debugger should ignore the call and return a successful result code. If you are writing a PM program which enters PM via VCPI, your call to the VCPI Get PM Interface (DE01) must precede this call as 386SWAT needs to hook into the latter call in order to ensure that its PTEs are visible when the VCPI client is active (and thus know what its new linear address is). INT 67h AX = 0DEF4h Set New Debugger CR3 and Linear Address INPUTS: EBX = new CR3 or -1 if unchanged EDX = new linear address or -1 if unchanged. OUTPUTS: AH = 00h if successful This call is used in conjunction with functions DEF2 and DEF3 if your program enters PM without using either VCPI or DPMI. In that case, this case must precede the calls to functions DEF2 and DEF3 and you must also call function DEF9. If EDX == -1 on entry, SWAT will return the current linear address in EDX upon return. INT 67h AX = 0DEF5h (Reserved) INT 67h AX = 0DEF6h Symbol Table Management Functions INPUTS: BL = 00h Append ECX names from DS:ESI ECX = # names to append DS:ESI ==> table of names to append (see SYMC_STR) OUTPUTS: AH = 00h if successful AH = 88h if not ECX = # symbols appended SYMC_STR struc SYMC_FVEC df ? ; Seg:Off or Sel|Off SYMC_FLAG dw ? ; Flags: see SYMFL_REC below SYMC_GRP dw ? ; Group # (arbitray value may be used ; with TS command from the command ; line to group symbols) SYMC_NAMLEN db ? ; Length byte db ? dup (?) ; ASCII name (no terminating zero) SYMC_STR ends SYMFL_REC record $SYMFL_VM:1,$SYMFL_TYP:5,$SYMFL_RSV:10 @SYMFL_VM equ mask $SYMFL_VM ; 1 = symbol is for VM ; 0 = ... PM ; The above flag is meaningful for _DAT and _LN ; types only. @SYMFL_TYP equ mask $SYMFL_TYP ; Data types: see @SYMTYP_xxx below @SYMTYP_DAT equ 0 ; Code or data @SYMTYP_LN equ 1 ; Line number record constructed by MAPSSF @SYMTYP_ABS equ 2 ; ABS record @SYMTYP_SWT equ 3 ; Symbol is for SWAT internal use INPUTS: BL = 01h Search for name DS:ESI DS:ESI ==> name to search for (see SYMC_STR) OUTPUTS: AH = 00h if successful AH = A0h if not found INPUTS: BL = 02h Translate old symbol to new DS:ESI ==> SYMTRAN_STR (see below) OUTPUTS: AH = 00h if successful Use this call to translate a symbol's segment/selector and group to a new segment/selector and base. SYMTRAN_STR struc SYMTRAN_OSEL dw ? ; Old segment/selector SYMTRAN_OGRP dw ? ; Old group # SYMTRAN_NFLAG dw ? ; New flags SYMTRAN_NSEL dw ? ; New segment/selector SYMTRAN_NBASE dd ? ; New base (to be added to all offsets) SYMTRAN_FLAGS dw ? ; Flags for match significance SYMTRAN_STR ends ; Flags used in SYMTRAN_FLAGS to indicate which elements in SYMTRAN_STR ; are to be ignored. ; $SYMFL_ADDVMSEG indicates that the new segment value is to be added ; to all V86 mode segments. SYMTFL_REC record $SYMTFL_IGOSEL:1,$SYMTFL_IGOGRP:1,$SYMTFL_IGNFLAG:1,\ $SYMTFL_IGNSEL:1,$SYMTFL_ADDVMSEG:1,$SYMTFL_RSVD:11 INPUTS: BL = 03h Flush the symbol table OUTPUTS: AH = 00h if successful INPUTS: BL = 04h Append without replacing existing symbols (allow duplicates of existing symbols) (same as BL=00h) OUTPUTS: AH = 00h if successful INPUTS: BL = 05h Execute ASCIIZ command in 386SWAT DS:ESI ==> ASCIIZ string containing a command OUTPUTS: AH = 00h if successful Use this call to execute a command on the 386SWAT command line. This function is used by the SWATCMD.EXE device driver. INPUTS: BL = 06h Copy ASCIIZ string to error log DS:ESI ==> ASCIIZ string OUTPUTS: AH = 00h if successful INT 67h AX = 0DEF7h (Reserved) INT 67h AX = 0DEF8h (Reserved) INT 67h AX = 0DEF9h Fill in 386SWAT's PTEs INPUTS: ECX = maximum # PTEs ES:DI can hold ES:DI ==> save area for PTEs If ES = 0, EDI ==> ... OUTPUTS: AH = 00h if successful AH = 8Bh if 386SWAT needs more PTEs than specified ECX = # additional PTEs 386SWAT needs Use this call in conjunction with function DEF4 if your program enters PM without using VCPI or DPMI so that 386SWAT's PTEs are visible in your program's address space. To determine how many PTEs SWAT needs without filling them in, call this function with ECX = 0, and on return (with AH = 8Bh), ECX has the number of PTEs SWAT needs. INT 67h AX = 0DEFAh (Reserved) INT 67h AX = 0DEFBh (Reserved) INT 67h AX = 0DEFCh (Reserved) INT 67h AX = 0DEFDh Initialize Debugger Interface, Interrupt Gate INPUTS: ES:DI ==> GDT entries for debugger support If ES = 0, EDI ==> ... BX = initial selector OUTPUTS: AH = 00h if successful = 84h if VCPI not supported = 8Fh if debugger support not present BX:EDX = Address of protected mode entry point This call allows the debugger to setup its own GDT entries as it pleases. The number of entries it may use is limited to four (4) so the client can statically allocate space in its GDT. Each GDT entry used by SWAT is a Code, Data, or LDT selector, and the IDT entries used (when DEF3 is called) are Interrupt Gates (cf. DEF2). The starting selector number in BX is needed so that the "Initialize Debugger IDT Entry" call knows what selectors to use when initializing an IDT entry. Consequently, this call must precede that call in order to know the proper selector value. If you are writing a PM program which enters PM via VCPI, your call to the VCPI Get PM Interface (DE01) must precede this call as 386SWAT needs to hook into the latter call in order to ensure that its PTEs are visible when the VCPI client is active (and thus know what its new linear address is). The selector and offset returned identify the address of the protected mode entry point in the debugger (see below). This entry point should be called via a USE32 far call as it might be a task gate. Ŀ Protected Mode Program Interface For the moment, there are no defined functions. However, when there are, we will require that AH = 0DEh and that the subfunction code be in AL. Ŀ Tips On Debugging DPMI Programs If you are using an integrated DPMI host such as 386MAX, no special action is needed on your part to debug a DPMI program. 386SWAT installs itself as a PL0 extension to the memory manager and thus also to the DPMI host. Place breakpoints in your code as needed as well as step right over the far call to the DPMI host which switches from VM to PM. If you using a non-integrated DPMI host such as QDPMI.SYS which is distributed with QEMM, then essentially you are debugging a VCPI client which provides DPMI services to its clients. See the next section for details on debugging VCPI programs. Ŀ Tips On Debugging VCPI Programs Assuming SWAT has successfully intruded into the MM's (memory manager's) PM context, it is on the same level as MM -- that is, you should think of it as a PL0 extension of the VCPI host. This means that you need to tell SWAT to intrude into the VCPI client's context. Some VCPI clients are easy to break into, some are quite hostile (not maliciously, it's just that they don't leave any wiggle room). There's no magic to this. Fundamentally, SWAT needs room in the GDT for its selectors. See the above description on how to get your program to cooperate with SWAT, but if you want to get up and running quickly, you might first try these steps: * Allocate about 30 selectors at the end of the GDT, * Initialize the selectors to all zero, * Plant an INT 01h/03h in the PM portion of your code or whatever you prefer, * Type VMSINT ON on the SWAT command line, * Start your program. The only tricky part is to ensure that any breakpoints you set to which SWAT would respond occur after the stack in the VCPI client has been setup (and preferably also DS, ES, FS, and GS). Typically, this means that you can debug through every single instruction except for the handful which occur during the handoff from the VCPI host's PM context to the VCPI client's PM context. The reason for this is that the VCPI PM handoff depends upon descriptor caching. In particular, the VCPI spec has the client specifying the state of all EGP registers, but only the CS segment registers. Thus the VCPI client starts off execution with its own environment (GDT, LDT, IDT, TR, and CR3) in effect, but on the VCPI host's stack. The value of that stack selector quite likely is invalid in the client's GDT/LDT, so the VCPI client must switch to its own stack ASAP. Of course, if an interrupt/exception occurs before then, when the CPU attempts to use the (presumably) invalid stack it would trigger a Double Fault, quickly followed by a Triple Fault and system shutdown. One other sharp corner I've run into is with VCPI clients which assume that their TSS won't be used by the CPU, so they "save" a few bytes by re-using that data area. SWAT uses TSS selectors for its IDT entries, so when SWAT gains control the CPU saves the preceding context into the caller's TSS. Also, it's a nice touch to save your CR3 into the appropriate place in the TSS, but SWAT will do that for you in case you forget. Other debuggers might not be so thoughtful. Also, if you switch LDTs from the original one, be sure to save the current one in the TSS as that field is read-only. That is, the CPU doesn't save the current LDT there when it performs a task switch, so SWAT can't "see" your LDT otherwise. If your program changes the PIC base(s), use the VCPI call (DE0B) to inform SWAT of the change. As far as symbols are concerned, use MAPSSF with a .WSG file. See the SWATSYM.DOC for more details. After SWAT pops up in PM within your program, type TS on the SWAT command line to tell SWAT to recalculate the base address of all symbols and symbols should appear. The .WSG file tells SWAT the value of various selectors, but of course it can't know at that time the base address. That's why you need to tell SWAT to recalculate. Ŀ Tips On Debugging RM Programs Debugging Real Mode programs is quite similar to debugging VCPI programs, except that you must use the debugging interface to allow SWAT to gain a foothold into your PM context. Because there is no interrupt (such as INT 67h for VCPI) when a RM programs enters PM, your program must cooperate with SWAT in order for it to be of use. Also note that you cannot trace through the instructions which setup the resources the CPU needs to run in PM. In particular, this includes LGDT, LIDT, and MOV CR0,r32 (or LMSW), as well as those instructions which setup the segment registers in PM, especially CS and SS (note that CS is setup by executing a far jump). ================================================ FILE: swat/VCPIDBG.HTM ================================================
The premise is that there is already a resident debugger in the system
which the client wishes to use. These calls provide an interface
between the client and the debugger to allow that to happen. In order
for the debugger to support this interface, it should intercept INT 67h
during its initialization and troll for the following calls, ignoring most
others. The debugger may wish to troll for the "Set 8259A Interrupt
Vector Mappings" call in order to properly determine where the timer and
keyboard interrupts start.
If your program enters PM via DPMI using a memory manager with an integrated DPMI host, skip all this as 386SWAT hooks into the host's GDT and IDT through the INTRUDE option. Note that DPMI hosts which install as a separate device driver appear to the memory manager (and thus to 386SWAT) as a VCPI client, so the above advice to skip this document does not apply. I haven't tried to debug a DPMI client in this context, so I'm not sure how to do it.
If your program enters PM via VCPI, use functions DEF0, DE01, DEF2, and DEF3 in that order so that 386SWAT is properly initialized in your program's GDT and IDT.
If your program enters PM from RM and uses paging, use functions DEF0, DEF4, DEF2, DEF3, and DEF9 in that order although function DEF9 can be executed anytime after function DEF0 executes successfully.
If your program enters PM from RM and does not use paging, use functions DEF0, DEF2, and DEF3 in that order.
When debugging VCPI clients, be sure to enable the VMSINT flag either
by putting the keyword VMSINT
into your 386SWAT profile, or by typing VMSINT
ON from the 386SWAT command line.
INFO_LEN
dw ?
; Byte count of structure
INFO_BASE dd
? ; Physical base address of SWAT code
segment
SWTINF_VER dw
? ; Version # of structure
More fields are defined which are used by SWATVXD.
If you are writing a PM program which enters PM via VCPI, your call to the VCPI Get PM Interface (DE01) must precede this call as 386SWAT needs to hook into the latter call in order to ensure that its PTEs are visible when the VCPI client is active (and thus know what its new linear address is).
The selector and offset returned identify the address of the protected mode entry point in the debugger (see below). This entry point should be called via a USE32 far call as it might be a task gate.
Some interrupt numbers may be handled wholly by the debugger (not passed on to the previous handler). Some may be chained, with the debugger handling only certain situations (as may be the case if the 8259A's interrupt numbers overlay CPU fault interrupt numbers). For this reason, the client should initialize the IDT entry with a valid handler in case the debugger passes along the interrupt.
Because this call may be invoked multiple times on the same interrupt number, the debugger must check the existing entry to see if it is the same as the value to which it would initialize the entry. If that's the case, the debugger should ignore the call and return a successful result code.
If you are writing a PM program which enters PM via VCPI, your call to the VCPI Get PM Interface (DE01) must precede this call as 386SWAT needs to hook into the latter call in order to ensure that its PTEs are visible when the VCPI client is active (and thus know what its new linear address is).
SYMC_FVEC df ?
; Seg:Off or Sel|Off
SYMC_FLAG dw ?
; Flags: see SYMFL_REC below
SYMC_GRP dw ?
; Group # (arbitray value may be used
; with TS command from the command
; line to group symbols)
SYMC_NAMLEN db ?
; Length byte
db ? dup (?)
; ASCII name (no terminating zero)
SYMC_STR ends
SYMFL_REC record $SYMFL_VM:1,$SYMFL_TYP:5,$SYMFL_RSV:10
@SYMFL_VM equ mask $SYMFL_VM ; 1
= symbol is for VM
; 0 = ... PM
; The above flag is meaningful for _DAT and _LN
; types only.
@SYMFL_TYP equ mask $SYMFL_TYP ; Data
types: see @SYMTYP_xxx below
@SYMTYP_DAT equ 0
; Code or data
@SYMTYP_LN equ 1
; Line number record constructed by MAPSSF
@SYMTYP_ABS equ 2
; ABS record
@SYMTYP_SWT equ 3
; Symbol is for SWAT internal use
SYMTRAN_STR struc
SYMTRAN_OSEL dw ?
; Old segment/selector
SYMTRAN_OGRP dw ?
; Old group #
SYMTRAN_NFLAG dw ?
; New flags
SYMTRAN_NSEL dw ?
; New segment/selector
SYMTRAN_NBASE dd ?
; New base (to be added to all offsets)
SYMTRAN_FLAGS dw ?
; Flags for match significance
SYMTRAN_STR ends
; Flags used in SYMTRAN_FLAGS to indicate which elements
in SYMTRAN_STR
; are to be ignored.
; $SYMFL_ADDVMSEG indicates that the new segment
value is to be added
; to all V86 mode segments.
SYMTFL_REC record $SYMTFL_IGOSEL:1,$SYMTFL_IGOGRP:1,$SYMTFL_IGNFLAG:1,\
$SYMTFL_IGNSEL:1,$SYMTFL_ADDVMSEG:1,$SYMTFL_RSVD:11
For the moment, there are no defined functions. However, when
there are, we will require that AH = 0DEh
and that the subfunction code be in AL.
If you are using an integrated DPMI host such as 386MAX, no special action is needed on your part to debug a DPMI program. 386SWAT installs itself as a PL0 extension to the memory manager and thus also to the DPMI host. Place breakpoints in your code as needed as well as step right over the far call to the DPMI host which switches from VM to PM.
If you using a non-integrated DPMI host such as QDPMI.SYS which is distributed
with QEMM, then essentially you are debugging a VCPI client which provides
DPMI services to its clients. See the next section for details on
debugging VCPI programs.
Assuming SWAT has successfully intruded into the MM's (memory manager's) PM context, it is on the same level as MM -- that is, you should think of it as a PL0 extension of the VCPI host. This means that you need to tell SWAT to intrude into the VCPI client's context.
Some VCPI clients are easy to break into, some are quite hostile (not maliciously, it's just that they don't leave any wiggle room). There's no magic to this. Fundamentally, SWAT needs room in the GDT for its selectors. See the above description on how to get your program to cooperate with SWAT, but if you want to get up and running quickly, you might first try these steps:
One other sharp corner I've run into is with VCPI clients which assume that their TSS won't be used by the CPU, so they "save" a few bytes by re-using that data area. SWAT uses TSS selectors for its IDT entries, so when SWAT gains control the CPU saves the preceding context into the caller's TSS. Also, it's a nice touch to save your CR3 into the appropriate place in the TSS, but SWAT will do that for you in case you forget. Other debuggers might not be so thoughtful.
Also, if you switch LDTs from the original one, be sure to save the current one in the TSS as that field is read-only to the CPU. That is, the CPU doesn't save the current LDT there when it performs a task switch, so SWAT can't "see" your LDT otherwise.
As far as symbols are concerned, use MAPSSF
with a .WSG file. See the SWATSYM.DOC
for more details. After SWAT pops up in PM within your program, type
TS on the SWAT command line to tell SWAT
to recalculate the base address of all symbols and symbols should appear.
The .WSG file tells SWAT the value
of various selectors, but of course it can't know at that time the base
address. That's why you need to tell SWAT to recalculate.
Debugging Real Mode programs is quite similar to debugging VCPI programs, except that you must use the debugging interface to allow SWAT to gain a foothold into your PM context. Because there is no interrupt (such as INT 67h for VCPI) when a RM programs enters PM, your program must cooperate with SWAT in order for it to be of use.
Also note that you cannot trace through the instructions which setup
the resources the CPU needs to run in PM. In particular, this includes
LGDT, LIDT, and MOV CR0,r32 (or LMSW),
as well as those instructions which setup the segment registers in PM,
especially CS and SS
(note that CS is setup by executing a far
jump).
================================================
FILE: swat/WHATSNEW
================================================
Version 6.04
* Implement UNREAL as keyword in the 386SWAT.PRO file.
Version 6.03
* Support for display of MMX & SSE instructions
* Display MMX and SSE registers via Alt-F11.
* Support for display of FCMOVcc, FCOMI, FUCOMI, FCOMIP, FUCOMIP
instructions
* Implement FSONLY keyword to allow SWAT to install in Windows w/o
DVGA or MDA because you promise to bring up SWAT in a Full Screen
DOS window only.
* Implement X15 keyword to tell SWAT to pass through INT 15h memory
size calls (thus not protecting its own allocated memory). This is
a debugging keyword only, not for general use.
* Fix bug where all Family 6 CPUs were thought to support the Last
Branch/Exception feature, whereas, in fact, only Intel CPUs do.
This would cause SWAT to reboot during initialization on certain
Cyrix CPUs.
* Display + or - sign after jcc instruction if a Branch Hint prefix
(2Eh or 3Eh) is present.
Version 6.02
* Support MDA hidden by PCI VGA or PCI AGP controller, and secondary
PCI VGA adapter. See SWATIDSP.HTM for more details.
* Support 4MB pages in PTE display as well as the PTE and SPTE
commands.
* Fix bug when using BC on current instruction and G to another
instruction.
* Fix bug in display of MOV r32,[EBP*n+disp32].
* Display appropriate comments on PCI calls (INT 1Ah).
* Emulate references to the debug registers (DRn) if the GD bit is set
in DR7.
* Implement search for not a value, e.g., S addr addr ~val.
* Implement DLG16 & DLG32 commands to display dialogs when in Windows.
* Extend monitor expressions to BD and BC commands.
* Implement BP command to break on page references.
Version 6.01
* Implement UNREAL command to allow debugging of Unreal Mode. This
mode is a variant of Real Mode in which any segment register can
access all of the 4GB address space. That is, instead of the normal
64KB length of a segment, the length is 4GB. This command can
enable all or just some of the segment registers for Unreal Mode.
See SWATCMD.HTM for more details.
Version 6.00.002
* Implement s-F10 to save the current screen into the last screen
buffers.
* Fix various bugs in 386SWAT's RM Windows support.
* Implement QS command to display nearest symbol at or before a given
address.
* Implement DTE command to display Descriptor Table Entry on the
command line.
* Implement MDB command to display a Windows Module Database.
* Implement TDB command to display a Windows Task Database.
* Implement .MDB to return Sel|0 of current MDB.
* Implement .TDB to return Sel|0 of current TDB.
* Display PDE and PTE bits with PTE command.
Version 6.00.001
* Code reorganization to accommodate logged in versions 5.10.071-9
* Documentation moved over to HTML format.
Version 6.00.000
* Implement support for Windows kernel debugging. This major upgrade
is described (to some degree) in WINKDBG.DOC; also see 386SWAT.DOC
and SWATVXD.DOC.
Version 5.10.079
* Add More Service Routine Text
Add service routine text displays for Win95 VMM routines. This text
is displayed when decoding INT 20h calls.
Version 5.10.078
* Fix .VMSTK and .VMRET Commands
For some reason, the implementations of the .VMSTK and .VMRET
commands didn't work, so now they do. Moreover, .VMSTK is now
called .VMCRS (for Client Register Struc).
Version 5.10.077
* Implement Time Stamp Counter Display
On CPUs which support it (TSC bit set in CR4), display the # clocks
executed since the last time 386SWAT was entered. There is a
certain amount of overhead in each entry to 386SWAT, so the numbers
displayed will never be at the level of single instruction clock
counts, but it is a good measure of time over a longer set of
instructions.
Version 5.10.076
* Handle New Windows Keyboard Keys
The Microsoft Natural keyboard contains three new keys:
* Left Windows key
* Right Windows key
* Application key
These keys have new scan codes which our keyboard handler now
recognizes, although we don't do much with them as yet. The left
and right Windows keys are meant to be modifiers just as Shift,
Ctl-, and Alt-keys are modifiers. The Application key is meant to
be an actual keystroke, so I've assigned arbitrary key codes to it
in its Unshifted, Shift-, Ctl-, and Alt-states.
For the moment, until someone can think of something better, the
Application key invokes the Help menu.
Version 5.10.075
* Miscellaneous internal changes
Version 5.10.074
* Use Ctrl-Shift-PrtSc To Print Instruction Window Only
When printing multiple instruction screens, repeating the register
display and other information on the second and subsequent screens
is unnecessary. To this end, the key combination Ctrl-Shift-PrtSc
prints the instruction portion of the display only, assuming the
instruction window is displayed (if not, the entire screen is
printed as usual).
Version 5.10.073
* Implement Saved Register Clear Command
In case you do not need to restore a saved register set, the saved
state can be cleared with the RC command.
* In the operand analysis display for an IRETd with NT=1, the back
link TSS selector is omitted as there's no room for it and all of
the other information being displayed.
Version 5.10.072
* Implement Command Recall
Commands entered on the command line are saved in a ring buffer
whose length can be changed from the default of 1024 via the profile
keyword CMDHIST=nnn.
Previous commands can be retrieved via the keystrokes Alt-<
(previous command) and Alt-> (next command). Pressing either of
these keys repeatedly scrolls through the buffer in the chosen
direction. The keystroke Alt-? displays a history of (up to 25)
commands from which a command can be chosen by scrolling up or down
through the list, or by typing the letter next to the command. A
command may be deleted from this list via the Del key.
* A bug was fixed when running under Windows where a jump was taken
with the wrong sense (JZ vs. JNZ) if Win386 services are available.
* A new help screen to describe the various searching options is
defined.
Version 5.10.071
* Fix Bug When Searching For Bytes
In an earlier TWT, when handling Page Faults, the ZF flag was
cleared without realizing that subsequent code depended upon it
being set. The effect was that searches for bytes were never found.
This change fixes that.
Version 5.10.070
* Implement INSERT Command
To debug Windows at the lowest level, we need to be able to insert
ourselves into Windows startup shortly after it enters PM. To this
end, the INSERT command is available. It is used from the 386SWAT
command line at the point just before Windows enters PM.
Version 5.10.069
* Allow Fill Command On Physical Memory
The Fill (F) command used to change data in memory now allows an
optional trailing P also optionally followed by a CR3, just as the
Unassemble and other commands allow.
Version 5.10.068
* Fix Bug In Implicit Stack References
When displaying the data (if any) pointed to by the current
instruction, if the instruction used an implicit stack reference
(such as the PUSH instruction) we sometimes would use the wrong base
register (ESP vs. SP). This TWT fixes that bug.
Version 5.10.067
* AutoFault for GP Faults
The Autofault facility has been extended to GP Faults. This means
that an attempt is made to interpret each GP Fault intercepted by
386SWAT in a short sentence. Type Shift-F4 to see the last
Autofault error message. Because GP Faults are many and varied,
some cases will be missed (marked as unknown) or mistaken. Please
notify the author as you encounter such cases with the exact
circumstances of the GP Fault so they may be corrected.
Version 5.10.066
* When Running Under Windows, Map In/Out VM's First Megabyte
Previously, when our local keyboard handler was active, we avoided
checking and setting the keyboard values in the BIOS data area if
Windows was active because we couldn't be sure that that memory
region was mapped in. Now that I have discovered Win386 (INT 22h)
services, we can map in/out that region around references to it.
Version 5.10.065
* Set the GD Bit In DR7
Because some programs get a kick out of resetting the debug
registers which we've carefully setup, this change has 386SWAT
automatically set the Global Debug (GD) bit in DR7 on startup so
that we can stop such programs before they can do any harm. Perhaps
it doesn't surprise you that Windows is the chief reason for this
feature.
Version 5.10.064
* Display Real Mode Interrupt Vector Table
As it's a very common data structure to view, the keystroke Shift-F5
now displays the RM IVT.
Version 5.10.063
* Filter Leading Text From Symbols
Some symbols, especially from Windows programs written in C, are
prefaced with text such as "__imp__", "_", and the like which adds
the symbol's length but not understanding. This feature allows you
to specify in the 386SWAT profile leading text which is to be
stripped from each symbol.
SYMFILTER = text1 [text2 [...]]
The default settings are
SYMFILTER=__imp__ _
Up to 128 characters can be specified in this way.
Version 5.10.062
* Fix Bug When Displaying Long Symbol Names
When we display a symbol names of 50 chars or greater at the top of
a data screen, we're off by one in our calculations which can cause
the name to fold to the next line.
Version 5.10.061
* Fix Bug In The Stack Width Change Calculation
Previously, the calculation of when to change stack width (words or
dwords) occurred only when the code selector changed. In fact, it
should be checked when the stack selector changes (duh!).
Version 5.10.060
* Fix Bug With Mis-aligned Stack In GP Fault Handler
If the user hooks GP Faults, the handler in LCL_INT0D uses a stack
structure which is missing one word in the middle. It's amazing it
has worked at all so far.
Version 5.10.059
* Implement Support For P6 Features
Two P6 features are supported by this TWT.
1. The Branch Trace Facility (BTF) can be turned on and off via the
command line BTF ON/OFF.
2. The Last Branch/Exeception From/To registers can be displayed at
all times in the lower right corner of the screen. This feature
can be turned on and off via the command line LBR ON/OFF.
* Skip Over Read/Write From/To Debug Registers If GD Bit Is Set
When the GD bit in DR7 is set, any read from or write to a debug
register triggers a Debug Fault (and the CPU clears the GD bit from
DR7 so the Debug Fault handler can use those registers).
Some environments (Microsoft Windows comes to mind) clear the debug
registers upon entry (and at other times) thus making it difficult
debug in that context. With this change, setting the GD bit traps
reads and writes of those registers and handles them transparently.
A read from a debug register returns the actual value. A write to a
debug register is ignored. The GD bit can be set from the 386SWAT
command line via
R DR7.GD=1
If you desire this behavior to be the default, use SWATCMD with the
above argument.
Version 5.10.058
* Add .VM addresses for Windows debugging
Especially when debugging calls from a Windows app down to a VM
interrupt handler, it is sometimes useful to know where we'll come
back to in Windows on the other side of the ARPL wall. .VMRET will
often work if the call was made via DPMI SIMVMI (function 0300h) or
an INT instruction emulated by the DPMI host (Windows).
* Add Go Monitor command and .CSIP to 386SWAT
The GM (go monitor) command takes an expression which will be
evaluated as the CPU single-steps (equivalent to Pad-plus or F11).
No display will occur until 1) the monitor expression evaluates TRUE
or 2) 386SWAT is invoked by some other means (GP fault, NMI, Ctrl-
Alt-Pad5, etc.)
Boolean expressions may be constructed using the dyadic operators
&&, ||, <, <=, ==, >=, and >. Operator precedence is the same as
the C language.
For example:
GM ah
executes until AH is non-zero.
GM [.csip == 21cd && ah!=9
executes until the current instruction is INT 21 and AH is any
value other than 9 (DOS display string).
GM cx == 0
executes until CX is 0.
GM
executes until the last expression specified with GM is TRUE.
There are some limitations:
1. Currently, GM does not single-step across mode switches via INT
(but will handle any mode switch handled by Pad Plus).
2. It is slow as molasses.
3. With the addition of boolean operators like && and ||
precedence becomes more of something one would reasonably
expect.
4. GM will not work in TSS mode currently (non-critical, failure
mode is the expression doesn't trigger).
Version 5.10.057
* Fix Bug When Testing For Extended Memory
Due to an oversight, when I put in the code to determine the amount
of extended memory using the INT 15h/0E801h call, I put it in after
the INT 15h/0DA88h, instead of before. Because of a bug in Phoenix
4.04 BIOSes, which crash on the 0DA88h call, the order is important.
Also, when setting up the IDT entry for VCPI debugging using TSSes,
we used to set the offset to -1 (because the IDT selector is a TSS
and the offset isn't used). For convenience, I'm now setting the
low-order byte of the offset to the interrupt #. That way, when
looking at the IDT in memory (not via F4) it's easy to tell which
interrupt it covers.
Version 5.10.056
* Workaround Feature In Win95
Because of a quirk in Win95 (what, only one!), when we blast in the
PTEs for 386SWAT to address the monochrome and color video buffers
from local addresses, we need to preserve the AVL bits and set the
accessed bit so this PTE won't be thought of as one available for
allocation.
Version 5.10.055
* Handle Large Size 386SWAT With VCPI Clients
If the resident portion of 386SWAT becomes too large (perhaps a
large SYMSIZE or SAVESCREEN), then we might not be able to debug
VCPI clients because our footprint exceeds the 4MB limit (one page
directory) for VCPI. If this happens, we should at least warn the
user in case s/he intends to debug VCPI clients.
* Fix Bug With Unused GDT Entry
When 386SWAT loads via the 386MAX profile, it is passed its linear
address when it is a VCPI client in the third of the three GDT
entries allocated for load modules. This is done because 386SWAT's
PTEs are part of 386MAX's and get relocated by 386MAX when a VCPI
client loads.
When 386SWAT intrudes into a Memory Manager, we don't use the third
GDT entry in the same way, and in some cases we might not even
allocate a third GDT entry if we have found existing GDT entries for
an all memory selector and one which maps CR3. In this case (I
encountered it when intruding into QEMM), we can mistakenly
reference the third GDT entry. This TWT fixes that.
* Check For Additional Autofault Errors For TSS Faults
If a TSS fault occurs, there are some additional reasons for it
which we now test for and report on, such as invalid selectors in
the back link TSS when a return from a nested TSS occurs.
At the same time, I included some additional fault error messages
which occur when we're using TSSs ourselves (typically when we're
debugging VCPI clients) which we we're checking for before. This
also involves moving that error message text from the data to the
code segment to match where the Autofault code expects it.
Also, I changed references to $PTE_0 to $PTE_G as that's its new
definition, and checked for Page Fault problems related to that bit
if PTE Global Extensions are enabled in CR4.
Version 5.10.054
* Handle Multiple GDTs When VCPI Debugging
While tracking down a bug in the CDROM game The 11th Hour, I found
that 386SWAT needed to handle intruding into multiple GDTs as this
game appear to use up to three different ones, alternating between
two quite frequently. We now support up to eight alternating GDTs.
At the same time, I fixed a bug where 386SWAT was not correctly
recognizing whether or not it had already intruded into a GDT. This
had the effect of filling up the GDT with 386SWAT's TSS selectors
which crashed the system in quick order.
Finally, while running VCPITEST to see if I had broken anything in
the process, I decided to remove the check for VMSINT=ON from the
VCPI call DE01 (Get PM Interface) in order to allow Intrude 386SWAT
to work with a cooperating VCPI client without having to set that
variable. This means that Intrude 386SWAT will insert its PTEs into
every such call, but that should be harmless. The VMSINT=ON setting
still controls whether or not 386SWAT intrudes into the VCPI call
DE0C (Switch From VM To PM).
Version 5.10.053
* Enable Debugging Extensions (If Supported) At Virtual Init Time
The Pentium CPU's debugging extensions are supported in 386SWAT via
the BD command on an I/O port at which time the $DE bit is set in
CR4. This change enables them at an earlier time so any other
program (such as 386MAX) can modify its behavior depending upon
whether or not the $DE bit is set.
Version 5.10.052
* Make Device Driver 386SWAT Sensitive to Another Extended Memory
Function
The recent change to 386MAX to support the 0E801h Extended Memory
function call needs to be copied to device-loading 386SWAT not only
so it can detect how much extended memory is in the system, but also
so it can lie to any subsequent program requesting the extended
memory size through that interface.
Version 5.10.051
* Implement Show PTE Command
Strolling through a large set of Page Tables such as under Windows
can be tiresome, hence there's a new command. The SPTE command
works exactly likely the PTE command (displaying the Linear
address/PDE/PTE on the command line) as well as displaying the
corresponding PTE (as if you had pressed F5 and scrolled down to the
appropriate entry).
At the same time, I allowed Ctrl-Up and -Down to scroll through the
PDEs/PTEs one entry at a time (Up and Down scroll through one line
at a time).
Version 5.10.050
* Fix Bug When Using TSS For Faults
A previous TWT changed a local routine to be more self-sufficient by
setting DS within the routine instead of relying upon the caller to
set this register. Alas, that was a mistake as in some cases we
rely upon the Invisible Descriptor Cache, particularly when we're
accessing selector values in the caller's LDT. This TWT fixes that
to use two routines, one which assumes the global DS has been set,
one which does not.
At the same time, I fixed a problem with device-loading 386SWAT
where software INTs 01h, 02h, 03h, and 68h are not being enabled if
VME is.
Version 5.10.049
* Fix Bug With DEBUG=PMI And Device-Loading 386SWAT
If we're loading as Device 386SWAT at startup, INIT_PROT is called
at the point where 386SWAT is a temporary VCPI client of the MM.
Thus the active IDT is that of the VCPI client and INIT_PROT is
setting up the MM's IDT where the VCPI client has calculated the
IDT's linear address in the VCPI client's linear address space.
All this is background to say that we can't signal an INT 01h if
DEBUG=PMI is specified because the active IDT (that of the VCPI
client) does not have its IDT entries setup for debugging unless
there's a preceding 386SWAT in the picture. This changes enforces
that condition.
Version 5.10.048
* Call REST_PROT/INIT_PROT On Windows Entry/Exit With Device-Loading
386SWAT
When 386SWAT is loaded as a device driver (whether it was intruding
into an existing memory manager or loading as VCPI SWAT), previously
it wasn't handling the transitions into and out of Windows.
When Windows starts up, 386SWAT needs to disable itself (by calling
its REST_PROT entry point) so that it is in the proper state when
the 386SWAT VxD calls 386SWAT's INIT_PROT entry point after Windows
loads. Correspondingly, when Windows terminates, the VxD calls
386SWAT at its REST_PROT and 386SWAT needs to call its INIT_PROT
entry point to re-enable it.
When 386SWAT is loaded from within 386MAX, MAX handles calling the
proper REST_PROT/INIT_PROT entry points. When 386SWAT is loaded as
a device driver, these calls were not made.
Now they are.
Version 5.10.047
* Swap Out Local IDT Entries Around TOGINT Call
When 386SWAT is active, it hooks various interrupts for its own use
such as the timer, keyboard, cascade, and mouse (the latter two in
case there's a PS/2-style mouse which goes through the keyboard
controller).
When we toggle an interrupt via command line (TOGINT xx xx ...), or
keystroke (Alt-F1, etc.), we need to swap out our local entries
around the toggle so that we save the new entry in the proper
(global) location. In particular, this affects TOGINT 0A which is
hooked locally.
Version 5.10.045 & 5.10.046
* Allow Search Command Of PTEs
When tracking down a bug in Win95, I found it useful to extend the
search command to search through the PTEs for a specific value. The
new syntax is
S addr addr # PTE
S addr L expr # PTE
where PTE can be any expression.
At the same time, I fixed a bug where a Page Fault during the
display of the searched for data caused a crash.
* Miscellaneous Changes
1. Display appropriate comment on DPMI interrupt lines. This also
involves defining a new segment to hold the DPMI function values
(as words).
2. For display of PTEs, note the PDE which contains the top line of
the display as well as the range of linear addresses covered by
the top line.
3. For display of PDEs, note the range of linear addresses covered
by the top line.
4. Display display of PTEs and PDEs, handle not present entries by
displaying "????????".
5. Change the initial mask for memory display to allow 32-bit
values.
6. Change the number of entries displayed in dword format to eight
by squeezing the entries together. Note that the previous width
can be obtained via the dd/4 command.
7. Save the previous d?/?? value for later use separately for each
width.
8. If the selector passed to any routine which calls GETARWBASE is
not present, return with CF=1 to indicate an error. This change
is needed by WINSWAT to avoid displaying an incorrect label for
not present selectors. As it turns out, without this change and
with the new KRNL386, USER, GDI symbol display in WINSWAT the
label displayed for not present selectors is that of the Windows
routine BOZOSLIVEHERE.
Version 5.10.044
* Prepare for Winswat
A feature needed by WINSWAT is the ability to set a temporary
breakpoint from a Windows program. This requires that we fill in
the rest of the fields where else this feature is used.
A feature needed by WINSWAT is the ability to refresh debug hooks
when a selector's linear address changes.
Version 5.10.043
* Make Device Driver 386SWAT Sensitive to PCI Extended Memory Function
The recent change to 386MAX to support the PCI Extended Memory
function call needs to be copied to device-loading 386SWAT not only
so it can detect how much extended memory is in the system, but also
so it can lie to any subsequent program requesting the extended
memory size through that interface.
Version 5.10.042
* Implement Data Width Switch
When displaying data via the Dx command, a new switch allows you to
specify the number of elements to be displayed per line. For
example, to display five (instead of the usual eight) words per
line, use DW/5.
This feature is a stopgap until I implement a more general data
record display as in the Periscope debugger.
Version 5.10.041
* Use Monochrome Adapter If Present
Rather than switch to the mono adapter every time I startup the
system, I thought it easier to implement a keyword to do the same.
With this keyword (MONO) present, if a monochrome adapter is present
in the system, it becomes the initial display screen for 386SWAT.
The monochrome adapter has always been supported by 386SWAT -- this
just makes it the initial display screen as opposed to the color
monitor.
Version 5.10.040
* Allow TSS Debugging In VM
If a program enters PM from RM and asks 386SWAT to enter its GDT and
IDT, as usual we setup TSS selectors for the interrupts we manage.
If this program subsequently enters VM, we need to handle the
interrupt via a TSS from VM differently as the stack and register
interpretation (segments vs. selectors) are different. Previously,
our TSS interrupt code expected to be entered from PM only, so a
change is needed.
Also, when debugging such a RM program where the user sets a
breakpoint shortly after entering PM (via setting the PE bit in CR0)
but before setting TR, I found that 386SWAT failed miserably because
it was depending upon there being a valid back link in the local
TSS. Thus, more changes were needed to handle an invalid back link.
In conjunction with this change, the register set command (R) is
enhanced to allow TR and LDTR (a.k.a. LDT) to be read and set, so
the user can setup a valid back link should the need arise.
Also, when 386SWAT is installed as a RM debugger, avoid setting TR
to our local TSS as that changes it from an invalid value to a valid
value. Unfortunately, this doesn't prevent another program from
doing the same, but at least we're not the culprit. BTW, unlike the
LDTR, there seems to be no way to clear (and thus invalidate) the
Task Register once it's set. Setting TR to zero (which is after all
its initial state), causes a GP Fault even though the current value
of TR may be already be invalid. Thus, once TR is set to an invalid
(and possibly non-zero) value, it stays that way until set to a
valid value.
* Clear NT bit in EPM.ASM
After switching into PM, the code in EPM.ASM should clear the NT bit
in case a subsequent IRET/D occurs (as it does) in order to avoid a
TSS Fault. Thanks to John Fine for pointing this out.
Version 5.10.039
* Support Spain 172 Keyboard Layout
Thanks to Roberto Deza Asensio, 386SWAT now supports this keyboard
layout.
Version 5.10.038
* Include Function-Specific Text in INT 21h Comments
Because they occur so often in code, the display of INT 21h
instructions which are the current instruction now includes
function-specific text (e.g., "Write File (handle)").
Version 5.10.037
* Calculate SYMSIZE based upon the size of the loaded symbol table
Previously, I had attempted to calculate SYMSIZE based upon the size
of the incoming .SSF file and it didn't work. This time it does.
The effect is that you don't need to use SYMSIZE with a LOADSYM,
thus reducing wasted space in 386SWAT's symbol table as well as
perhaps avoiding a mistake when calculating SYMSIZE and finding it
is too small.
* Fix Bug In MAPSSF
Due to a bug in my linker, certain far calls weren't fixed up
properly.
Version 5.10.036
* Support International Keyboards
One of 386SWAT's design goals is to be as unassuming about the
system as possible, intruding into the system at an absolute
minimum. As part of achieving this goal, 386SWAT has its own
keyboard handler so it can debug keyboard actions within the BIOS as
well as not depend upon the system's keyboard routines or data being
intact and functional.
One consequence of this is that 386SWAT needs to be changed in order
to support international keyboards which is what this TWT
accomplishes.
To this end, the keyword KEYB= is recognized in the 386SWAT profile.
At the start, the only keyboard supported is the German one -- its
keyboard layout is 129, so the KEYB= value is GR129. Others can be
supported as the need arises. See file 386SWAT.DOC under the KEYB=
entry for the list of supported keyboards.
Thanks to Armin Kunaschik, 386SWAT now supports this keyboard
layout.
Version 5.10.035
* Include INT 03h and INTO in GPSKIP=INT Processing
When I put in GPSKIP=INT, I checked for the INT xxh opcode (0CDh),
but forgot about INT 03h (0CCh) and INTO (0CEh). These cases are
now covered.
Version 5.10.034
* Implement Return Address and Goto Return Address
A common address to jump to is the (near or far) return address of a
subroutine. This is made easier by using shortened forms of the
commands one might use to extract these addresses. For details, see
the "Common Memory References" section in 386SWAT.DOC.
Version 5.10.033
* Make NORMLIDT The Default
I've encountered enough circumstances debugging RM where RM LIDT
redirection has gotten in the way, that I've decided that it's best
to make NORMLIDT the default and use the (new) keyword RMLIDT to
enable it when necessary.
Version 5.10.032
* Parse I/O Command line Instructions as LVALs
For greater generality, the command line I/O instructions now allow
an LVAL instead of just an atom. Also, the IMR command line action
displays the original values not the ones set by 386SWAT.
Version 5.10.031
* Validate Back Link In Operand Analysis Display for IRET/D
On occasion, I've had the system go poof on an IRET/D when the NT
bit was set (and I didn't notice that) and the back link TSS was
invalid for some reason (either bad TSS selector, or something was
wrong with the TSS, such as the CR3 value was invalid). This TWT
checks for that condition and reports it as part of the operand
analysis display for the IRET/D instructions.
Version 5.10.030
* Ensure Default Options Set If No Profile
If the user omits a profile on the 386SWAT device line, we skip out
before setting default options. Now we don't.
Version 5.10.029
* Handle Invalid Symbol Selectors
If we upload symbols with an invalid selector (say, the *.WSG file
is for another context -- DPMI vs. VCPI vs. RM), the call to
GETBASE returns an error along with EAX=-1. If this value is used
for the linear address, the symbol is marked as invalid and the
address hash code gets confused. This change checks for the above
eventuality and sets the pseudo-linear address to zero to avoid this
problem. BTW, the symptom is that (say) SWATRUN hangs when
uploading symbols if it has two or more symbols with the same
(invalid) linear address, e.g. 300|0.
* At the same time, I upgraded the grammar for LS and PS to accept
lvals instead of just atoms, thus allowing a wider variety of ways
of specifying the arguments to these commands. Others in this same
vein to follow.
Version 5.10.028
* Make INTRUDE The Default Option
Now that INTRUDE is reasonably well debugged, I'm making it the
default option so users don't need to remember to use it (which has
happened several times). This will reduce the number of tech
support questions I get from users of 386SWAT on the Internet. In
case the user needs to use the VCPI client version of 386SWAT, the
disabling option VCPISWAT is defined.
Version 5.10.027
* Fix Bug When Running in RM
An earlier TWT introduced a bug (for RM SWAT only) which set the
B-bit in the stack selector. The problem is that I forgot to reset
that bit when returning to RM. The solution is to define a new
selector which has the same characteristics as DTE_SS, except with
the B-bit is clear. Before returning to RM, we switch to this new
selector so as to return to an environment which is compatible with
RM.
Version 5.10.026
* Handle SIGINT 1/3
While debugging an incompatibilty with ViruSafe, I needed a minor
enhancement to SIGINT to overcome their attempts to fool a RM
debugger. They used many tricks including self-modifying code, as
well as installing their own INT 01h/03h handlers. At one point
their code signals INT 01h which 386SWAT intercepts, of course. I
needed to signal this interrupt to them, but SIGINT 1 invoked it as
a PM interrupt, which proceeded to crash the system. The solution
was to signal INT 01h/03h as a VM interrupt, as well as ensure that
TF is set in the return flags if it's INT 01h from a single-step (as
opposed to a software interrupt INT 01h).
* At the same time, I needed to save the incoming value of DR6 which
triggered another change (and bug fix). The bug fixed is an
incorrect data value width in a struc missed when I changed the code
segment from USE16 to USE32.
* A related change cleaned up (and documented) the tests which handle
the case where 386SWAT is entered other than through a debug
exception, but with the GD bit set.
Version 5.10.025
* Display MAC Entries
After many years of wading through MAC entries, I decided to
implement a separate display screen for them (actually, Win95 pushed
me over the edge -- this is a variant of "The devil made me do it").
The keyboard combination of Ctl-M brings up this screen.
* There's also a separate command MACBASE which allows you to set the
base address of the MAC chain in case it's different from .DMAC.
This is handy when displaying the DOS subsegment chain.
Version 5.10.024
* Fix Bug In PATH= Profile Routine
When converting over to USE32 data, I missed a place where I should
have cleared the high-order word of a 32-bit register.
Also, in the process of debugging this problem, I put in several
more Shift debugging messages.
Version 5.10.023
* Fix Bug With VCPI Get Protected Mode Interface Calls
In order for us to provide debugging services to VCPI clients, we
need to insert our PTEs into the VCPI client's address space. There
are several contexts in which this might occur:
1. 386SWAT is loaded via LOAD= with 386MAX: our PTEs are
automatically copied to the VCPI client's address space as part
of 386MAX's response to the Get Protected Mode Interface (GPMI
-- DE01h) call.
2. 386SWAT is loaded as a VCPI client to a memory manager:
previously we didn't handle this case. Now we use the newly
defined RMDEV_GPMITAIL label in low DOS memory which this
TWT defines an return point in order to catch the tail of
the GPMI call. At this point, we switch back to our code in
extended memory, and copy our PTEs to the end of the GPMI
caller's PTE buffer.
3. 386SWAT intruded into a MM (possibly 386MAX): previously we
placed a PM return address on the stack and passed control
on to the MM. This doesn't work with all MMs as some check
the VM bit in the flags when interpreting the segment
registers saved on the stack. Now we use the newly defined
DEV_GPMITAIL label which this TWT defines as a return point
in order to catch the tail of the GPMI call. At this point,
we switch back to our code in extended memory, and copy our
PTEs to the end of the GPMI caller's PTE buffer.
Version 5.10.022
* Avoid Page Fault on LIN2PPTE Accesses
The LIN2PPTE subroutine translates a linear address to a pointer to
the corresponding PTE according to a specific CR3. Sometimes we
need to read more than one PTE from the Page Directory which doesn't
always work (because the subroutine doesn't know how many PTEs to
map in the case we're not mapping relative to the current CR3). A
solution to this is to tell the subroutine how many PTEs are to be
mapped in.
Version 5.10.021
* Compatibility With PMODE
There is a popular shareware DOS extender available on the Internet
called PMODE which is used to create PM programs. When it is run as
a VCPI client, it allocates selectors from the top down in the GDT
-- the same as 386SWAT does. PMODE uses the AVL bit in the DTE to
mark a selector as in use, so this change has us set that bit in the
selectors we allocate so PMODE doesn't write on top of our
selectors.
Version 5.10.020
* Fix Bug When Swapping INTs
When we swap IDT entries (say when displaying the IDT via F4) so we
see or act upon the global IDT entries, we don't swap INTs 74h and
76h. Now we do.
Version 5.10.019
* Use Same DPL When Hooking Interrupts
Some memory managers (pssst, it's EMM386) set the DPL of various
entries in the IDT to zero expecting the CPU to signal a GP Fault if
the corresponding software interrupt occurs. When we intrude into
their PL0 context, previously we were setting the DPL to three
because we didn't expect to encounter a MM which had a fetish with
GP Faults. Now we retain the same DPL as the original IDT entry
except for INTs 01h and 03h. They are handled differently so we can
issue the corresponding software interrupts and gain control
immediately instead of having to hook the GP Fault handler and pick
them off there.
Version 5.10.018
* Fix Disappearing Cursor Bug
For years we've put up with this bug. Now it's fixed.
The problem occurs in any of three contexts:
* when reviewing last screens (Alt-F10),
* when switching between color and mono adapters (Alt-F7), or
* when swapping screens (say, when exiting 386SWAT).
The problem occurs because not all programs maintain a consistent
set of data values in the BIOS data area on which we rely (e.g., the
dependence between the cursor type and the cursor emulation bit).
The fix is to read the cursor start and end line values upon entry
and restore those values in the above circumstances. A new routine,
GET6845 is defined for the read starting value part. I seem to
recall that the original definition of the 6845 registers was that
they were write-only, but apparently they are now readable as well.
At the same time, while testing the different contexts in which
386SWAT changes the cursor type, I noticed that the Enter command
handles the INS key, but not the XINS key, so I changed it.
Version 5.10.017
* Implement Exit Command
A common command line sequence is to set AH to 4C, SIGINT 21, and G.
This is now done via a command called EXIT.
Version 5.10.016
* Add Debugging Displays To 386SWAT During Initialization
To help me figure out why 386SWAT wasn't installing under Win95, I
made several changes:
* Add some debugging displays (press and hold either shift key when
386SWAT is loading ala Shift-MAX).
* If there are no VCPI services (DEBUG=NOVCPI in 386MAX), fail
gracefully.
* Ensure interrupts are re-enabled upon returning from VCPI/PM.
* Ensure that the B-bit is set in our stack selector.
* Avoid calling CHECK_I92 if we're in VM as it can reboot the system
(learned the hard way).
* Put in a check to avoid calling OLDINT67_VEC if it's zero (who can
argue with that?).
* Avoid a bug in MASM 5.10b which generates a word fixup when it
should generate a dword fixup. Any questions on why I want to
write my own assembler?
================================================
FILE: swat/WINKDBG.DOC
================================================
Notes on Windows Kernel Debugging
---------------------------------
Windows implements a low-level debugging interface called Kernel
Debugging (WKD) which obtains control very early in the startup
sequence of Windows. The program WDEB386 shipped with the Windows SDK
is an example of one such program; 386SWAT is another.
When Windows sees that a kernel debugger is present, it informs the
debugger about a number of events as they occur, principally parameter
errors made by running Windows programs as they call Windows API
functions. For example, if a program calls EnableWindow with an
invalid window handle (say, zero), this error is passed on to the WKD
for processing before returning to the calling program. In this way,
a great many errors can be caught before they can cause any harm.
By default, 386SWAT is a WKD. If, for some reason, you wish to
disable this feature, place the keyword NOWINK into your 386SWAT
profile.
At the current time, you must have a monochrome adapter and monitor
attached to your system in order for WKD services to be allowed by
386SWAT. 386SWAT tests for a mono card and disables WKD if not found.
New Command Line Actions
------------------------
* To enable or disable WKD services entirely, use
WKD [ON|OFF]
This option is available outside Windows only. If you have
specified NOWINK in the 386SWAT profile, this option has no effect
-- you must remove that profile option and reboot the system to
enable WKD services.
* To change the response to a parameter error, use
WKD [NOISY|QUIET]
If NOISY is specified, then each parameter error is followed by the
prompt "Break, Ignore, Quiet?" to which you must respond B, I, or
Q.
A response of Q, is equivalent to setting WKD QUIET after which
parameter errors are logged to the screen, but execution does not
stop for any more parameter errors until you break into 386SWAT and
type WKD NOISY on 386SWAT's command line. This is the default
state, so you'll see parameter errors fly past on the mono screen
without pause. I recommend that when you are debugging your own
code, use WKD NOISY; otherwise, use WKD QUIET. This is because many
commercial packages contain so many parameter errors that you'll
constantly be interrupted with a BIQ prompt.
A response of I, ignores this one error only and continues
execution.
A response of B, triggers a single step breakpoint at an IRETD
inside 386SWAT. Tracing over this instruction returns to the
instruction inside Windows immediately following the call to
386SWAT (typically an INT 41h). Tracing from this point on
eventually gets you back to a call to LogError which was called by
the Windows API function which encountered the invalid parameter,
and then back to the application which called the Windows API with
an invalid parameter. If the application is Win32, then you'll have
to trace back through a thunking layer before you get to the
application. This can be quite tricky. At some point, I'll try to
put in some stack tracing to aid this process.
* Sometimes Windows and/or applications make direct calls to LogError
as opposed to indirect calls as part of a parameter error. These
calls are uncommon, so there is a separate switch to control
386SWAT's response to such an event. Use
WKD LOGERROR [ON|OFF]
to change the response of stopping at an IRETD inside 386SWAT or
not. The default setting is OFF.
* Sometimes an application is bad enough that it triggers an
unrecoverable fault (typically a GP or Page Fault) and it must be
terminated. Windows gives the WKD a crack at it first to which
386SWAT responds by stopping in the application at the faulting
instruction, thus giving you the opportunity to debug the problem.
If you wish to tell Windows to handle the fault itself (typically
by terminating the application), use
WKD FAULT SKIP
If you do not want to receive any more fault messages from Windows,
use
WKD FAULT OFF
To restart receiving fault messages, use
WKD FAULT ON
* The keystroke Ctrl-K brings up a menu from which you can choose to
display various internal Windows structures. At the moment, the
only one which has been fleshed out is the display of the Windows
Global Heap (WGH). The WGH contains one entry for each globally
allocated region of memory. For example, the various code and data
segments of KRNL386.EXE are global allocations and have entries in
this table. To scroll through this table, use the usual up and down
arrow keys as well as Page Up and Page Down.
To search through the WGH for a specific entry, use the SGH command:
SGH [/b|/s|/h|/o] [/c] expr
The expression (expr) entered is interpreted as a base address if /b
is specified, size if /s, handle if /h, and owner if /o. If /c is
specified, the search continues from the currently displayed entry;
otherwise, the search starts at the top of the heap. If the value
is found in the WGH, the WGH is displayed with the matching entry at
the top of the screen. If you wish to repeat the search (perhaps
becayse there are multiple entries with the same owner), either
retype the command (and specify /c so the search continues with the
next entry after the one at the top of the screen), or start the
command with a slash so it is not erased after successful execution.
If the value is not found in the WGH, an error message is displayed.
* If Invalid Page Faults are being trapped by SWAT's VxD (see
SWATVXD.DOC for more details), use the IPF command to control how
these events are to be handled.
IPF [/d] [/s] [/r] expr
where /d tells SWAT not to display a message on the mono screen
describing this event, /s tells SWAT not to stop when this event
occurs, /r tells SWAT to remove this entry from its local tables,
and expr is an expression which evaulates to a linear address
corresponding to the Invalid Page Fault.
* To display the memory pointed to by a selector as a Module Database,
use
MDB expr
* To display the memory pointed to by a selector as a Task Database,
use
TDB expr
* Certain commands usually preceded with a dot are automatically
passed to Windows for processing by WIN386, assuming Windows is
active and WIN386 services are available. These commands include
? Display a Help screen with the available commands
.? Display a list of registered dot commands (e.g., .M)
.R [#] Display the registers of the current thread (or thread #)
.VM [#] Display the complete VM status of the current VM (or #)
.VC [#] Display the current VM's (or #) control block
.VH [#] Display a VMM linked list, given list handle
.VR [#] Display the registers of the current VM (or #)
.VS [#] Display the current VM's (or #) virtual stack
.VL Display list of all valid VM handles
.DS Display protected mode stack with labels
.VMM Display menu of VMM state info
. Windows implements a low-level debugging interface called Kernel Debugging
(WKD) which obtains control very early in the startup sequence of Windows.
The program WDEB386 shipped with the Windows SDK is an example of one such
program; 386SWAT is another.
When Windows sees that a kernel debugger is present, it informs the
debugger about a number of events as they occur, principally parameter
errors made by running Windows programs as they call Windows API functions.
For example, if a program calls EnableWindow with an invalid window handle
(say, zero), this error is passed on to the WKD for processing before returning
to the calling program. In this way, a great many errors can be caught
before they can cause any harm.
By default, 386SWAT is a WKD. If, for some reason, you wish to
disable this feature, place the keyword NOWINK
into your 386SWAT profile.
At the current time, you must have a monochrome adapter and monitor
attached to your system in order for WKD services to be allowed by 386SWAT.
386SWAT tests for a mono card and disables WKD if not found.
New Command Line Actions
The WKD command takes several options:
If you do not want to receive any more fault messages from Windows,
use
WKD FAULT
OFF
To restart receiving fault messages, use
WKD FAULT ON
* The keystroke Ctrl-K brings up a menu of items to
display from which you can choose various internal Windows structures.
At the moment, the only one which has been fleshed out is the display of
the Windows Global Heap (WGH). The WGH contains one entry for each
globally allocated region of memory. For example, the various code
and data segments of KRNL386.EXE are global allocations and have entries
in this table. To scroll through this table, use the usual up and
down arrow keys as well as Page Up and Page Down.
* To search through the WGH for a specific
entry, use the SGH command:
SGH
[/b|/s|/h|/o]
[/c] expr
The expression (expr) entered is interpreted as a base address
if /b is specified, size if /s,
handle if /h, and owner if /o.
If /c is specified, the search continues
from the currently displayed entry; otherwise, the search starts at the
top of the heap. If the value is found in the WGH, the WGH is displayed
with the matching entry at the top of the screen. If you wish to
repeat the search (perhaps because there are multiple entries with the
same owner), either retype the command (and specify /c
so the search continues with the next entry after the one at the top of
the screen), or start the command with a slash so it is not erased after
successful execution. If the value is not found in the WGH, an error message
is displayed.
* If Invalid Page Faults are being trapped
by SWAT's VxD (see SWATVXD.DOC for more details),
use the IPF command to control how these events are to be handled.
IPF
[/d] [/s]
[/r] expr
where /d tells SWAT not to display a message
on the mono screen describing this event, /s
tells SWAT not to stop when this event occurs, /r
tells SWAT to remove this entry from its local tables, and expr is
an expression which evaulates to a linear address corresponding to the
Invalid Page Fault.
* To display the memory pointed to by a selector as a Module Database,
use
MDB
expr
* To display the memory pointed to by a selector as a Task Database,
use
TDB
expr
* Certain commands usually preceded with a dot are automatically passed
to Windows for processing by WIN386, assuming Windows is active and WIN386
services are available. These commands include
Profile Options
Because 386SWAT is a WKD, it is given much more information to process.
Much of that data is stored in its internal log file (acessible via Ctl-F10)
which means that you should increase the log file's default size.
You might experiment with different values, but I suggest using something
on the order of LOGSIZE=100000.
During Windows startup while still in RM/VM, Windows loads the real
mode portion of all of the VxDs to be used this Windows session.
At this time, Windows tells the WKD about these VxDs and where they will
be loaded in memory when in PM, and 386SWAT logs this information in two
ways.
The first way is to write a line to the mono display with the data about
each segment (code or data) of the form
Loaded VxD VxDname,
module Modname at address
len length
The second way is to append a symbol to 386SWAT's symbol table of the
form VxDname_Codennnn or VxDname_Datannnn,
where nnnn is a decimal sequence number starting at 1
and is used to distinguigh one code/data segment from another. By
convention in Windows, code segments are in selector 0028h
and data segments are in selector 0030h.
These symbols are useful when debugging VxDs.
In order to save this information, you need to tell
386SWAT how much space to reserve. The profile option WKDLS
(Load Segments) is used for this purpose. The actual
value depends upon how many VxD code and data segments are loaded on your
system. I use WKDLS=700. If you
have no interest in debugging VxDs, omit this profile option as the default
value is zero which reserves no space. You might also want to increase
the profile value for SYMSIZE.
I use SYMSIZE=700000 (you do have 32MB of
RAM, don't you?).
Debugging DLLs
The Windows SDK (3.1x or 95) contains separate copies of various system
DLLs each of which contain more messages and checks on Windows execution.
I haven't played with these much, so you're on your own, but it may be
very worthwhile. They also contain symbol files for which I haven't
as yet put in code to load.
SWAT VxD
This version of 386SWAT no longer requires that you load SWATVXD.EXE
as a resident program (thus saving you a few bytes of DOS memory), but
the VxD must be in same directory as 386SWAT.LOD. The VxD is now
loaded by 386SWAT when Windows starts up. See the file SWATVXD.DOC
for more information on how to configure the VxD.
Loading 386SWAT
If you are running 386MAX as your memory manager, you could load 386SWAT
from the 386MAX profile with the LOAD= keyword.
However, because this mechanism doesn't provide for 386SWAT to have a DOS
footprint and such a footprint is needed for WKDing, I do not recommend
loading SWAT this way anymore. Instead, load 386SWAT as a device
driver on the line which immediately follows the line which loads the memory
manager. 386SWAT must load in low DOS memory, so don't try to load
it high. The low DOS memory footprint of 386SWAT is about 2KB.
================================================
FILE: switcher/main.cpp
================================================
#include
WKD [ON|OFF]
WKD [NOISY|QUIET]
WKD LOGERROR
[ON|OFF]
WKD FAULT
SKIP
?
Display a Help screen with the available commands
.?
Display a list of registered dot commands (e.g., .M)
.R [#]
Display the registers of the current thread (or thread #)
.VM [#]
Display the complete VM status of the current VM (or #)
.VC [#]
Display the current VM's (or #) control block
.VH [#]
Display a VMM linked list, given list handle
.VR [#]
Display the registers of the current VM (or #)
.VS [#]
Display the current VM's (or #) virtual stack
.VL
Display list of all valid VM handles
.DS
Display protected mode stack with labels
.VMM
Display menu of VMM state info
.dev_name
Display device-specific info