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).