;*DDK*************************************************************************/
;
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
;      /*****************************************************************/
;      /*                                                               */
;      /*                                                               */
;      /*****************************************************************/
;      /******************* START OF SPECIFICATIONS *********************/
;      /*                                                               */
;      /*  SOURCE FILE NAME: TRACE.ASM                                  */
;      /*                                                               */
;      /*  DESCRIPTIVE NAME: Trace support                              */
;      /*                                                               */
;      /*                                                               */
;      /*  STATUS:  Version 1.0                                         */
;      /*                                                               */
;      /*  NOTES: This module provides tracing support which can be     */
;      /*         used for debugging. Macros are defined in PEN.INC     */
;      /*         to add trace data. The trace is extracted and         */
;      /*         formatted by the pen device driver tool (PENTL.EXE).  */
;      /*                                                               */
;      /*         The trace table must be allocated before tracing.     */
;      /*         This can be done throught the pen tool or can be      */
;      /*         allocated during driver initialization with by:       */
;      /*                                                               */
;      /*         device=c:\penpm\pendd.sys trace=1000                  */
;      /*                                                               */
;      /*         1000 can be any value trace table size.               */
;      /*                                                               */
;      /*  ENTRY POINTS:                                                */
;      /*      See public statements                                    */
;      /*  EXTERNAL REFERENCES:                                         */
;      /*      See extrn statements                                     */
;      /*                                                               */
;      /******************* END  OF  SPECIFICATIONS *********************/
.xlist
  include pensegs.inc
  include pen.inc
  include penei.inc
  include penioctl.inc
  include devsym.inc
  include devhlp.inc
  include struc.inc
.list

.386p
;------------------------------------------------------------------------------
;declare external routines
;------------------------------------------------------------------------------

extrn skip_to_white : near
extrn skip_white    : near
extrn str_i_cmp     : near
extrn get_dec_num   : near

;------------------------------------------------------------------------------
;declare external variables
;------------------------------------------------------------------------------

extrn Device_Help : dword

;------------------------------------------------------------------------------
; local equates
;------------------------------------------------------------------------------

fUSE_DEKKO  equ 0       ; 1= use dekko trace 0= don't
CR equ 0Dh
LF equ 0Ah

;------------------------------------------------------------------------------
; local data that is device instance independent
;------------------------------------------------------------------------------

DSEG SEGMENT

; share same trace buffer for all devcies
public trcNext
trcSize       dw  0  ; count of DWORD entries
trcWrap       db  0
trcDS         dw  0
trcPhys_h     dw  0
trcPhys_l     dw  0

trcNext       dw  0
trcStart      dw  0

DSEG ends

DSEGI segment
  opt_trace     db  'TRACE=',0
  cnf_trace     dw  0             ; size of trace to allocate at init time
DSEGI ends

CSEG SEGMENT
     ASSUME    CS:CGROUP, SS:nothing, ES:nothing, DS:DGROUP

;--- TRACE ROUTINES -----------------------------------------------------------
;
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
; Trace ABSOLUTE packet
;  di = event packet
; returns
;  maintains all regs and indicators
;------------------------------------------------------------------------------
Public   Trc_ABS
Trc_ABS   Proc  Near
  pushf
  DISABLE
  mov   ax,[di].cev_devicebits
  mov   cx,TRACE_ABS_EH
  call  doTrc

  mov   eax,[di].lev_dev_x
  mov   cx,TRACE_ABS_XH
  call  doTrc

  mov   eax,[di].lev_dev_y
  mov   cx,TRACE_ABS_YH
  call  doTrc

  mov   ax,[di].lev_dev_z
  mov   cx,TRACE_ABS_ZH
  call  doTrc
  popf
  ret
Trc_ABS   EndP

;------------------------------------------------------------------------------
; Trace a byte
;  al = byte to trace
; returns
;  maintains all regs and indicators
;------------------------------------------------------------------------------
Public   Trc_Byte
Trc_Byte  Proc  Near
  pushf
  DISABLE
  push  ax
  push  cx
  mov   cx,TRACE_DATA_H
  mov   ah,0
  call  doTrc
  pop   cx
  pop   ax
  popf
  ret
Trc_Byte  EndP
;------------------------------------------------------------------------------
; Trace a byte (first byte of packet)
;  al = byte to trace
; returns
;  maintains all regs and indicators
;------------------------------------------------------------------------------
Public   Trc_Byte1
Trc_Byte1 Proc  Near
  pushf
  DISABLE
  push  ax
  push  cx
  mov   cx,TRACE_DATA_1H
  mov   ah,0
  call  doTrc
  pop   cx
  pop   ax
  popf
  ret
Trc_Byte1 EndP
;------------------------------------------------------------------------------
; Trace a device dependent word
;  ax = word to trace
;  cl = index
; returns
;  maintains all regs and indicators
;------------------------------------------------------------------------------
Public   Trc_DD
Trc_DD    Proc  Near
  pushf
  DISABLE
  push  ax
  push  cx
  xor   ch,ch
  or    cx,TRACE_DD_H
  call  doTrc
  pop   cx
  pop   ax
  popf
  ret
Trc_DD    EndP

;------------------------------------------------------------------------------
; Trace an interrupt (put a special mark in trace)
;
; returns
;  maintains all regs and indicators
;------------------------------------------------------------------------------
Public   Trc_INT
Trc_INT   Proc  Near
  pushf
  DISABLE
  push  ax
  push  cx
  mov   cx, TRACE_INT_H
  mov   ax,0
  call  doTrc
  pop   cx
  pop   ax
  popf
  ret
Trc_INT   EndP

;------------------------------------------------------------------------------
; Trace a timer tick (put a special mark in trace)
;
; returns
;  maintains all regs and indicators
; note: Does not fill up trace with ticks, instead, increment trace entry
;       as the number of ticks since the last trace event.
;------------------------------------------------------------------------------
Public   Trc_Tick
Trc_Tick  Proc  Near
  .if  <trcSize ne 0>         ; check for trace table
     pushf
     DISABLE
     push  ax
     push  cx
     push  si

     mov   si, trcNext
     push  ds
     mov   ds, trcDS
     .if  <<word ptr [si+6]> eq TRACE_TICK_H>
        inc word ptr [si+4]
        pop ds
     .else
        pop   ds
        mov   cx, TRACE_TICK_H
        mov   ax,1
        call  doTrc
     .endif

     pop   si
     pop   cx
     pop   ax
     popf
  .endif
  ret
Trc_Tick  EndP

;------------------------------------------------------------------------------
; Actually do the trace entry
;  ax = low order
;  cx = high order
;------------------------------------------------------------------------------
Public  doTrc
doTrc   Proc
  .if   <trcSize ne 0>

     push  di
     mov   di,trcNext
     push  ds
     mov   ds, trcDS
     mov   word ptr [di],ax
     mov   word ptr [di+2],cx
     pop   ds
     sub   di,4
     .if   c               ; do the wrap around
       mov  trcWrap, 1
       mov  di, trcStart
     .endif
     mov   trcNext,di
     pop   di
  .endif
  xor ax,ax
  ret
doTrc     EndP

;--- IOCTL   ROUTINES ---------------------------------------------------------
;
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
; Return the size of the trace table
;
; The returned size includes one for the first entry which is used to return
; the number of valid entries on the query trace table command.
;
; We waste one entry to make tracing ticks easiler. Tick trace looks ahead one
; entry to see if its a tick trace. We would need a special check not to look
; past the end of the table, instead we just waste one entry at the end.
;
; So, if 10 entries are requested on the Set Trace Table Size IOCTL, we alloc
; storage for 10 entries, then waste one. We return 10 on Query Trace table
; to include the first LONG which contains the number of valid entries. If the
; table is full or has wrapped, a 9 would be in this first location.
;------------------------------------------------------------------------------
public Gio_qtts
Gio_qtts proc    ; query trace table size
  mov   ax, trcSize
  mov   word ptr gs:[si].dqtts_size,ax
  mov   word ptr gs:[si].dqtts_size+2,0
  xor   ax,ax
  ret
Gio_qtts endp

;------------------------------------------------------------------------------
; Reset the trace back to start
;
; note: this is not really needed as the trace is a wrap trace, but the Trace
;       program dumps starting at trcStart as a simification.
;------------------------------------------------------------------------------
public Gio_rtb
Gio_rtb proc     ; reset trace buffer
  push di
  mov  di, trcStart
  mov  trcNext, di
  mov  trcWrap, 0
  pop  di
  xor  ax,ax
  ret
Gio_rtb endp

;------------------------------------------------------------------------------
; Copy the trace table to the Trace application's buffer
;  gs:si = data packet
; note: handle generic IOCTL dump command
;------------------------------------------------------------------------------
public Gio_qtt
Gio_qtt proc     ; query trace table

  push  gs
  pop   es
  mov   di,si
  .if  <trcSize eq 0>         ; check for trace table
     mov  word ptr es:[di],0
     mov  word ptr es:[di+2],0
  .else
     DISABLE
     mov  cx, trcStart        ; ax = addr of last entry in trace table
     sub  cx, trcNext         ; calculate trace byte count
     shr  cx,2                ; convert to dword count
     .if  <trcWrap ne 0>
        mov  ax, trcSize
        dec  ax               ; one is wasted
        mov  dx, ax
        sub  dx, cx
     .else
        mov  ax, cx
     .endif
     mov  word ptr es:[di],ax
     add  di,2
     mov  word ptr es:[di],0
     add  di,2

     sal  cx, 1         ; convert to word count
     mov  si, trcNext   ; calculate trace byte count
     add  si,4          ; adv to first entry (not the next available)
     push ds
     mov  ds, trcDS
     rep  movsw         ; move it
     pop  ds

     .if  <trcWrap ne 0>
        mov  cx, dx
        sal  cx, 1
        mov  si, 0
        push ds
        mov  ds, trcDS
        rep  movsw     ; move it
        pop  ds
     .endif

     ENABLE
  .endif
  xor  ax,ax
  ret
Gio_qtt endp

;------------------------------------------------------------------------------
; Allocate or re-size trace table
;  fs:di = PSTTS packet
;------------------------------------------------------------------------------
public Gio_stts     ; Set Trace Table Size
Gio_stts proc
entryCount equ bp-4
traceSize equ bp-2
  mov  bx, word ptr fs:[di].pstts_size

; free the old one

initentry:
  enter 4,0
  push bx
  .if  <trcSize ne 0> ; free old buffer
     mov  trcSize, 0  ; stop traceing
     mov  ax, trcPhys_h
     mov  bx, trcPhys_l
     mov  dl, DevHlp_FreePhys
     call Device_Help
  .endif
  pop  bx

; allocate new size
; bx = size in dwords

  .if  <zero bx>
     xor ax,ax         ; stop tracing
  .else
     .if  <bx a 4000>
        mov  bx, 4000  ; limit how big
     .endif
     .if  <bx lt 10>
        mov  bx, 10    ; limit how small
     .endif
     mov  [entryCount],bx
     mov  ax, 0
     sal  bx, 2        ; convert to byte count
     mov  [traceSize],bx
     mov  dh, 0        ; above 1 meg
     mov  dl, DevHlp_AllocPhys
     call Device_Help
     .if  nc
        mov  trcPhys_h,ax
        mov  trcPhys_l,bx
        mov  cx, [traceSize]
        mov  si, trcDS
        mov  dl, DevHlp_PhysToGDTSelector
        call Device_Help
        .if  nc
           mov  ax, [traceSize]
           sub  ax, 8        ; back up and waste one entry
           mov  trcNext , ax
           mov  trcStart, ax
           mov  trcWrap, 0
           mov  ax, [entryCount]
           mov  trcSize, ax
           xor  ax, ax
        .endif
     .endif
     .if c
        mov  ax,STERR+STECODE
     .endif
  .endif
  leave
  ret
Gio_stts endp

CSEG ends

;-- INITIALIZATION ROUTINE ----------------------------------------------------
;
;------------------------------------------------------------------------------
CSEGI SEGMENT
     ASSUME    CS:CGROUP, SS:nothing, ES:nothing, DS:DGROUP
;------------------------------------------------------------------------------
; prepare for trace
;------------------------------------------------------------------------------
public Trc_Init
Trc_Init Proc  Near
  push  es     ; save parmline
  push  di

; allocate a selector to be used for the trace buffer

  push  ds
  pop   es
  mov   di, offset trcDS
  mov   cx, 1
  mov   dl, DevHlp_AllocGDTSelector
  call  Device_Help

  pop   di     ; restore parmline
  pop   es

; see if trace= is specified

  call    skip_to_white             ; Skip driver name

pcl_loop:
  call    skip_white                ; Skip any/all blanks til 1st char
  mov     al, es:[di]               ; Get 1st parameter/keyword char
  cmp     al, CR                    ; Scan remaining line chars for
  jz      pcl_loop_exit             ; Keywords and parameters
  cmp     al, LF                    ; until end of line condition
  jz      pcl_loop_exit             ; reached
  cmp     al, 0
  jz      pcl_loop_exit

  cld
  call    DParse
  jmp     pcl_loop
pcl_loop_exit:

; allocate a trace table at init time if requested

  mov  bx, cnf_trace
  .if  <nonzero bx>
     call initentry
  .endif

  ret
Trc_Init  EndP

;----------------------------------------------------------------------------
; Parses command line options given on CONFIG.SYS's DEVICE= line
; Look for trace parameter, ignore others.
;  es:[di] => possible command line option
;----------------------------------------------------------------------------
temp_di equ     [bp]-2
public Dparse
Dparse  proc near
  enter   2,0

  call    skip_white
  mov     temp_di, di

; look for TRACE= parm

  mov     si, offset opt_trace
  call    str_i_cmp
  .if     <zero ax> ; found TRACE=, get type number
     mov  temp_di,di
     call get_dec_num
     .if <di ne temp_di>
        mov  cnf_trace, ax
     .endif
  .endif

parse_exit:
  call    skip_to_white
  leave
  ret
Dparse endp

CSEGI    ENDS
end
