;       SCCSID = @(#)dostrace.asm       6.1 90/11/17
;      SCCSID = @(#)dostrace.asm        13.4 89/02/01
PAGE    ,132
NAME    DOSTRACE
TITLE   DOSTRACE.ASM - Dump Trace Buffer Debugger Command
.286p

;***    DOSTRACE.ASM
;
;
;       Dump Trace Buffer Command Support
;

.xlist
        include dosseg.inc
        include basemaca.inc
        include osmaca.inc
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debprt.inc
        include struc.inc
.list

;
;   Local Macros
;

CheckMinor  macro
    Pcall   ScanP
    .if nz
        StrCmp  pTXT, <dataOFFSET MinMsg>, MinMsgLen
        .if z
            add     pTXT, MinMsgLen
            Pcall   EvalWord
            mov     sminor, dx
        .else
            Pcall   perror
        .endif
    .endif
endm

StrCmp      macro   src, dest, count

    mov     si, src
    mov     di, dest
    mov     cx, count
    repe    cmpsb

endm

if RAS
        ReferGlobal     ras_stda_phys, WORD
        ReferGlobal     ras_stda_addr, WORD
endif

DataSegmentStart

Extern  pTXT,           WORD
Extern  pTXTstart,      WORD

;
;   Trace Buffer Header Structure
;

stda struc              ;structure template for header part of STDA
id      db 8 dup (?)    ;start with ASCII for 'SYSTRACE'
first   dw ?            ;offset from start of STDA to start of Circular Buffer
last    dw ?            ;offset from start of STDA to end of Circular Buffer
current dw ?            ;offset from start of STDA to next byte to write to
stda ends

;
;   Trace Record Format
;

record_header   struc
time_stamp      dw  ?                 ; time stamp (optional)
flags           db  ?                 ; flag byte
pid             dw  ?                 ; process ID
minor_code      dw  ?                 ; minor event code
length_vdata    dw  ?                 ; length of variable data
major_code      db  ?                 ; major event code
record_header   ends

;
;   Trace Flag byte definitions
;

DYNLINK         equ 01h               ; 0 = Kernel call
                                      ; 1 = Dyamic link call
NO_TIME_STAMP   equ 02h               ; 0 = time stamp exists
                                      ; 1 = no time stamp
PROTECT_MODE    equ 04h               ; 0 = real mode
                                      ; 1 = protect mode

;
;   Bit # Definitions for Trace Flags
;

B_DYNLINK       equ 0
B_NO_TIME_STAMP equ 1
B_PROTECT_MODE  equ 2


;
;   Copy of trace record being formatted
;

header  record_header <>              ; header
vdata   db      75 dup(?)             ; variable data

;
;   Global Variables for Formatting Routines
;

ret_code dw     0                     ; Return code

quit    db      0                     ; set to 1 when all records have
                                      ;   been formatted
wrapped db      0                     ; set to 1 when the data in the
                                      ;   STDA has wrapped around
_current    dw  0                     ; current from trace buffer

dcount      dw  0

smajor      db  0

sminor      dw  0


MajMsg  db  'MAJ='
MajMsgLen   equ $-MajMsg

MinMsg  db  'MIN='
MinMsgLen   equ $-MinMsg

;
;   Prints a Trace Record Header structure
;

DumpHeader      label   byte

        OutBlanks 2
        OutStr  "MAJ="
        OutByteHexPtr major_code

        OutBlanks 2
        OutStr  "MIN="
        OutWordHexPtr minor_code

        OutBlanks 2
        OutStr  "PID="
        OutWordHexPtr pid

        OutBlanks 2
        OutStr  "CONTEXT="

        JumpIfBitSet    flags, B_DYNLINK, ndl1

        OutStr  "KERNEL"
        JumpOp  ndl2

ndl1    label   byte
        OutStr  "API"

ndl2    label   byte
        OutStr  ":"
        JumpIfBitSet    flags, B_PROTECT_MODE, pm1

        OutStr  "REAL"
        JumpOp  pm2

pm1     label   byte
        OutStr  "PROTECT"

pm2     label   byte

        JumpIfBitSet    flags, B_NO_TIME_STAMP, nts

        OutBlanks 2
        OutStr  "TS="
        OutWordHexPtr time_stamp

nts     label   byte

        OutCrLf
        EndOp


DataSegmentEnd




CodeSegmentStart

;***    DumpTraceBuffer
;
;       This Dumps the System Trace Data Area
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   ALL
;
;       .t <count> <MAJ=maj_code>
;
;
        assumeDATA <ds, es, ss>

DefProc DumpTraceBuffer, NEAR, PUBLIC

LocVar  TraceAddr, <size AddrS>

BeginProc
        mov     sminor, 0
        mov     smajor, 0
        mov     dcount, 0ffffh

        Pcall   ScanP                               ; Scan to 1st Parm
        .if nz near                                 ; If Parm
            StrCmp  pTXT, <dataOFFSET MajMsg>, MajMsgLen
            .if z                                   ; If MAJ Parm OK
                add     pTXT, MajMsgLen
                Pcall   EvalByte
                mov     smajor, dl
                CheckMinor
            .else                                   ; Else Check For Count
                Pcall   EvalWord
                mov     dcount, dx
                Pcall   ScanP
                .if nz                              ; Scan to 2nd Parm
                    StrCmp  pTXT, <dataOFFSET MajMsg>, MajMsgLen
                   .if z                            ; If MAJ Parm OK
                        add     pTXT, MajMsgLen
                        Pcall   EvalByte
                        mov     smajor, dl
                        CheckMinor
                   .else
                        Pcall   perror
                   .endif
                .endif
            .endif
        .endif

;
;       Setup Variables in Debugger Data Segment
;

        mov     ret_code, 0
        mov     quit, 0
        mov     wrapped, 0

if RAS
        DosContext  <es>
        mov     ax, es:ras_stda_addr    ; (ax) = stda selector
        DataContext <es>
        assumeDATA  <es>
        .if <ax eq 0>
            putstr  <"Trace Not On", CR, LF>
        .else

            lea     bx,TraceAddr
            Pcall   FillSelAddr, <bx, ax, 0, 0>

;
;       Set up Loop to Format Each Record
;

            Pcall   GetWordOff, <current, 0>
            dec     dx
            mov     _current, dx
            mov     si, dx

;
;       Dump the Trace Records
;
            xor     ax, ax                          ; record dump counter

            .repeat

                Pcall   Get_Record                  ; get a trace record
                .leave  <quit ne 0>                 ; leave if through

                .if <smajor eq 0>
                    Pcall   Display_Record          ; format and display the record
                    inc     ax
                .else
                    mov     bl, smajor
                    .if < bl eq header.major_code>
                        .if <sminor eq 0>
                            Pcall   Display_Record
                            inc     ax
                        .else
                            mov     bx, sminor
                            .if <bx eq header.minor_code>
                                Pcall   Display_Record
                                inc     ax
                            .endif
                        .endif
                    .endif
                .endif

                .leave  <ax eq dcount>

            .until <wrapped ne 0> and
            .until <si be dx>

        .endif

endif

EndProc


;***    Get_Record
;
;       Gets a trace record and copies it to the debugger's data segment.
;
;       ENTRY:
;           SI = Offset of Last Byte of Current Trace Record
;
;       EXIT:
;           SI = Offset of last byte of Next Trace Record
;           DS:header = header data from trace record
;           DS:vdata = variable length data portion from trace record
;
;       USES:
;           SI
;
;       EFFECTS:
;           Quit is set to 1 if there is no more trace data to process
;
        assumeDATA <ds, es, ss>

DefProc Get_Record, NEAR, LOCAL

        SaveReg <ax,bx,cx,dx,di>

;       Copy the trace record header

        mov     di, dataOFFSET header.major_code    ; look at major code
        mov     cx, size record_header          ; length of common header

        .repeat

            Pcall   GetWordOff, <first, 0>
            .if <si b dx>                       ; has the buffer wrapped?
                Pcall   GetWordOff, <last, 0>
                mov     si, dx                  ; yes, go to end of buffer
                inc     wrapped                 ; indicate wrap
            .endif                              ; end buffer wrap

            Pcall   GetByteOff, <si, 0>
            mov     byte ptr [di], al           ; Move 1 byte
            dec     si                          ; Note Reverse Direction
            dec     di

            .if <cx eq 3> and                   ; test for no time stamp
            test    header.flags, NO_TIME_STAMP
            .if nz
                sub     cx, 2                   ; don't overlay time stamp
            .endif                              ; end time stamp check

        .loop                                   ; Copy the entire header

;       Copy the variable data, if any

        mov     cx, header.length_vdata         ; Variable data length

        .if <cx ne 0>                           ; Does data exist
            mov       di, dataOFFSET vdata-1    ; yes, point to variable data
            add       di, cx
            .repeat
                Pcall   GetWordOff, <first, 0>
                .if <si b dx>                   ; has the buffer wrapped?
                    Pcall   GetWordOff, <last, 0>
                    mov     si, dx              ; yes, point to end of buffer
                    inc     wrapped             ; indicate wrap occured
                .endif

                Pcall   GetByteOff, <si, 0>
                mov     byte ptr [di], al       ; Move 1 byte
                dec     si                      ; Note Reverse Direction
                dec     di

            .loop
        .endif                                  ; end data exist

;       If this is a null record, all data has been formatted

        .if     <header.major_code eq 0> and
        .if     <header.minor_code eq 0>
            mov     quit, 1                     ; No more data
        .endif

;       If this record crosses the current buffer pointer, all
;       data has already been formatted

        .if     <wrapped gt 1> or
        .if     <wrapped ne 0> and
        .if     <si b current>
            mov   quit, 1                       ; Everything formatted
        .endif

        RestReg <di,dx,cx,bx,ax>

EndProc




;***    Display_Record
;
;       Displays the record stored in the debugger's data segment.
;
;       ENTRY:
;           DS = Debugger Data Segment
;
;       EXIT:
;           Prints Trace Record on Debugger Console
;
;       USES:
;
        assumeDATA <ds, es, ss>


DefProc Display_Record, NEAR, LOCAL

        pusha

;
;       Dump The Header
;

        mov     si, dataOFFSET DumpHeader
        mov     di, dataOFFSET header
        Pcall   OpInterpret

;
;       Dump The Variable Length Portion
;

        mov     cx, header.length_vdata     ; (cx) = #bytes in vdata to dump
        mov     si, dataOFFSET vdata        ; (ds:si) = address of vdata
        xor     bx, bx                      ; (bx) = count of bytes/line

        .if <cx ne 0>

            .repeat

                putstr  <"    ">

                .if <cx a 16>
                    mov     bx, 16
                .else
                    mov     bx, cx
                .endif

                Pcall   HexRowDump

                .if <bx eq 16>
                    putstr  <"   ">
                .else
                    SaveReg <ax,cx>
                    mov     ax, bx
                    shl     ax, 1
                    add     ax, bx
                    add     ax, 3
                    mov     cx, 54d
                    sub     cx, ax
                    .repeat
                        Pcall   blank
                    .loop
                    RestReg <cx,ax>
                .endif

                Pcall   AsciiRowDump
                Pcall   crlf

                add     si, bx
                sub     cx, bx

            .until <cx eq 0>

        .endif

        popa

EndProc

;
;   HexRowDump
;
;   Entry:
;       BX = # to print in row
;       SI = address of data to print
;
        assumeDATA <ds, es, ss>

DefProc HexRowDump, NEAR, LOCAL

    SaveReg <ax,cx,si>

    mov     cx, bx

    .repeat

        Pcall   blank   ; print blank
        lodsb
        Pcall   hex     ; print byte in (al)

    .loop

    RestReg <si,cx,ax>


EndProc


;
;   AsciiRowDump
;
;   Entry:
;       BX = # to print in row
;       SI = address of data to print
;
        assumeDATA <ds, es, ss>

DefProc AsciiRowDump, NEAR, LOCAL

    SaveReg <ax,cx,si>

    mov     cx, bx

    .repeat

        lodsb

        and     al, 7fh                 ; ASCII uses 7 bits
        cmp     al, 7fh                 ; Don't try to print RUBOUT
        jz      noprt
        cmp     al, " "                 ; Check for control characters
        jnc     prin
noprt:
        mov     al, "."                 ; If unprintable character
prin:
        Pcall   putchar                 ; Print ASCII character

    .loop

    RestReg <si,cx,ax>

EndProc

CodeSegmentEnd

        end
