===============================================================================
     From: Ingo Rohloff                  Refer: 0       
  Subject: WRITING YOUR OWN DEBUGGER         1: 80xxx          Status: Public 
-------------------------------------------------------------------------------
 >  AM>         So, my question is:
 >  AM>                 Does anyone have an example of using the registers as
 >  AM> an aid in setting a break point and looking at what the machine state
 >  AM> is???

Well I coded something with Debug-registers so here are some
explanations first:

- The debug registers are all 32-Bit.
- DR0-DR3 store the physical break addresses (so you can use
  only 4 break addresses at the same time).
  e.g. 01c00h:0200h  becomes  01c200h
- DR4-DR5 are reserved for other new processors just ignore
  them

- DR6 is the status register. It is set when the interrupt
  is triggered. It has the following structure:


    1111111111111111
Bit FEDCBA9876543210 FEDCBA9876543210

    0000000000000000 TSI000000000RRRR
                     SSC         3210

  R0 set means the address in dr0 was accessed
  R1 means the same for dr1 and so on

  IC means In-Circuit-Emulator is active => Debug registers
  are ignored

  SS Single-Step Mode => TF is set in EFLAGS
  TS TaskSwitch => happens if the T-Bit is set in the new TSS of the
     new task

- DR7 is the control register for the four break adress
  registers DR0-DR3. Structure:

    1111111111111111
Bit FEDCBA9876543210 FEDCBA9876543210

    RRMMRRMMRRMMRRMM 000000GLGLGLGLGL
    3333222211110000       SS33221100

Rx means range. The field can be set to:
00 = Interrupt, if the exact byte-address in the corresponding
     DRx Register is accessed.

01 = Interrupt, if some part of the word-address in the
     corredponding DRx Register is accessed. The address in the
     DRx register must be even

10 = reserved

11 = Interrupt, if some part of the Dword-address in the
     corresponding DRx Register is accessed.
     DRx mod 4 must be zero.

Mx means mode. The field can be set to:
00 = Rx must be 00 for this mode. An interrupt is triggered
     if the FIRST byte of a command is read from the address stored
     in DRx

01 = Interrupt if something is written to the corresponding
     address

10 = reserved

11 = Interrupt if something is written or read to or from
     the corresponding address


The Gx and Lx define if an interrupt is triggered or not.
If both of them are set to zero for one address the processor
changes the bits in DR6 but doesn't trigger an interrupt.

If Gx is set the processor ignores the corresponding Lx bit.
The interrupt is triggered every time the break address is
accessed.

The Lx bits are cleared with every Task switch, so it is
possible to code a breakpoint for one specific task. To do
that, you have to set the T-Flag in the TS-Segment for the
task you want to debug. If the OS switches to that task
an interrupt is triggered, so that the debugger can set
the Lx bits properly. It is complicated and I don't think
you'll need it for your purpose. Just set the corresponding
Gx bit.

The GS bit means global securety. If it isn't set the
processor doesn't store the correct return address on
the stack, if an debug-interrupt is triggered. (It has
something to do with the parallel-technics in the processor. Setting
the GS bit means slowing down the machine, but not much...)

The LS bit does the same, but it is cleared with every task
switch, just like the Lx bits....


here's some sample code:
TESTED:
----------------------snip---------------------------------
; Use TASM... or MASM ;-)
.386P
masm
code segment para use16
assume cs:code,ds:code
org 0100h
start:
  mov ax,03501h  ; Get&Save old Int_1 Segment and Offset
  int 021h
  mov Old_Se,Es
  mov Old_Of,Bx

  mov ax,02501h          ; Set new Int_1
  mov dx,offset new_int1
  int 021h

  xor eax,eax            ; calculate DR0-Break address
  mov ax,cs
  shl eax,4
  add eax,offset BREAK
  mov dr0,eax
                         ; set GS and G1 bit in DR7
                         ; R0=0 M0=0 => interrupt if
                         ; command is read at DR0.
  mov eax,00000000000000000000001000000010b
  mov dr7,eax
  xor cx,cx

wait1:  loop wait1       ;wait a little
break:                   ; there the break happens
  mov dx,offset endstr   ; output message
  mov ax,0900h
  int 021h
  mov dx,old_of          ; Get&Set old Int_1 Handler
  mov ds,CS:[old_se]
  mov ax,02501h
  int 021h
  mov ax,04C00h          ; Exit...
  int 021h

new_int1:
  xor eax,eax
  mov dr7,eax         ; Reset Dr7, nothing will happen anymore...
  mov ax,0900h
  mov dx,offset outstr  ; Output message
  int 021h
  iret

outstr db 0Dh,0Ah
       db "Hello this program was breaked with the Debug-Registers"
       db 0Dh,0Ah,0Dh,0Ah,"$"
endstr db "Ok, back from break...",0Dh,0Ah,"$"

old_se dw 0
old_of dw 0


code ends
end start
-------------------------snip------------------------------

BTW: It looks like the DR0 Registers are not protected...
     That means that you can use them under EMM386.EXE under
     DOS... I don't know if this works with other memory
     managers too...
have fun and stay tuned
  Ingo

--- FMail 0.96
 * Origin: Pascal and C are fast... but ASM IS FASTER !! (2:2480/36.7)
===============================================================================
