;-----------------------------------------------------------------------;
;									;
;   RIPAUX.ASM -							;
;									;
;	Debugging Support Routines					;
;									;
;-----------------------------------------------------------------------;

		TITLE	RIPAUX.ASM - Debugging Support Routines

;-----------------------------------------------------------------------;
;   INCLUDES								;
;-----------------------------------------------------------------------;

?RIPAUX = 1
.xlist
win3deb = 1
include kernel.inc
include newexe.inc
.list

;-----------------------------------------------------------------------;
;   DATA SEGMENT DEFINITION						;
;-----------------------------------------------------------------------;

DataBegin

externD  pDisableProc

if ROM
externD prevInt21Proc
endif


if KDEBUG

externW  fWinX
externW  hExeHead
externB  szDebugStr

externW  DebugOptions
externW  DebugFilter
externB  fKTraceOut                ; Used by DebugWrite to ignore traces
else

externB  szExitStr1
externB  szExitStr2
externB  szFatalExit

endif

DataEnd

;-----------------------------------------------------------------------;
;   CODE SEGMENT DEFINITION						;
;-----------------------------------------------------------------------;

sBegin	CODE

assumes CS,CODE
assumes DS,NOTHING
assumes ES,NOTHING


;-----------------------------------------------------------------------;
;   EXTERNS								;
;-----------------------------------------------------------------------;

externFP ExitKernel
externFP FatalAppExit

if KDEBUG

externFP FatalExitC
externFP KOutDebugStr
externNP DebugOutput2
externNP LogParamError2
else

ife ROM
externD  prevInt21Proc
endif
externFP InternalDisableDOS

endif

externNP DebugLogError
externNP DebugLogParamError


; SAVEREGS - Preserve all registers
;
SAVEREGS    macro
if PMODE32
.386
	push	ds	  ; preserve all registers
	push    es
	push    fs
	push    gs
	pushad
.286p
else
	push	ds
	push    es
	pusha
endif;  PMODE32
endm

; RESTOREREGS - Restore registeres preserved with SAVEREGS
;
RESTOREREGS macro
if PMODE32
.386
	popad
	pop	gs
	pop	fs
	pop	es
	pop	ds
.286p
else
	popa
	pop     es
	pop	ds
endif;  PMODE32
endm



if      KDEBUG

cProc FatalExit,<PUBLIC,FAR>
	parmW	errCode
cBegin nogen
	push	bp
	mov	bp, sp
	push	ds
	pusha
	setkernelds
	push	[bp+6]
	cCall	FatalExitC
	or	ax, ax
        popa
	pop	ds
	jz	@F
	INT3_DEBUG	; Stop here so WDEB386 can generate same stack trace.
@@:
	pop	bp
	ret     2
cEnd nogen

BegData
szCRLF  db      13,10,0
EndData

; trashes ES - but caller saves it
cProc	KOutDSStr,<PUBLIC,FAR>,<ds>
	parmW	string
cBegin

if PMODE32
.386p
	pushad				; save upper 16 bits of EAX, etc
	push	fs
	push	gs
.286p
endif
	pushf
	push	cs
	push	ds
	push	es
	push	ss
	pusha
	SetKernelDS

	mov	si,string
	call	KOutDebugStr		; expects pusha right before call
	popa
	add	sp, 8			; don't need to restore seg regs
	popf
if PMODE32
.386p
	pop	gs
	pop	fs
	popad
.286p
endif
cEnd

cProc   _krDebugTest,<PUBLIC, FAR, NONWIN>,<DS>
	ParmW   flags
	ParmW	psz
	LocalW	axSave
	LocalW	bxSave
	LocalW	esSave
	LocalB	fBreakPrint
cBegin
	assumes ds,NOTHING
	mov	axSave,ax	; save these regs before they're trashed
	mov	bxSave,bx
	mov	esSave,es

;
; If this .exe hasn't been relocated yet, just call FatalExit.
;
	mov     ax,cs:MyCSDS
	cmp     ax,1000h        ;; DATA should be selector, not addr
	jc      @F
	jmp     kdtnobreak
@@:
	mov     es,ax
assumes es,DATA
;
; First use DebugOptions to determine whether we should print anything
; and/or break with a fatalexit.
;
	errnz   low(DBF_SEVMASK)
	errnz   low(DBF_TRACE)
	errnz   low(DBF_WARNING)
	errnz   low(DBF_ERROR)
	errnz   low(DBF_FATAL)

	mov     bx,es:DebugOptions
	mov     ax,flags
	and     ah,high(DBF_SEVMASK)
	mov     al,2                ; fBreak = FALSE, fPrint = TRUE
	cmp     ah,high(DBF_TRACE)
	jnz     notrace

	push    ax
	mov     es:fKTraceOut, 1    ; Set flag for DebugWrite
	mov     ax,flags            ; if (!((flags & DBF_FILTERMASK) & DebugFilter))
	and     ax,DBF_FILTERMASK
	test    ax,es:DebugFilter
	pop     ax
	jnz     @F
	and     al,not 2            ;    fPrint = FALSE
	jmp     short kdtnobreak
@@:
	test    bx,DBO_TRACEBREAK   ; else if (DebugOptions & DBO_TRACEBREAK)
	jnz     kdtbreak            ;    fBreak = TRUE
	jmp     short kdtnobreak

notrace:
	cmp     ah,high(DBF_WARNING)
	jnz     nowarn
	test    bx,DBO_WARNINGBREAK
	jnz     kdtbreak
	jmp     short kdtnobreak
nowarn:
	cmp     ah,high(DBF_ERROR)
	jnz     dofatal
	test    bx,DBO_NOERRORBREAK
	jnz     kdtnobreak
	jmp     short kdtbreak
dofatal:
	test    bx,DBO_NOFATALBREAK
	jnz     kdtnobreak
	errn$   kdtbreak
kdtbreak:
	or      al,1            ; fBreak = TRUE
kdtnobreak:
;
; If DBO_SILENT, then fPrint = FALSE
;
	test    bx,DBO_SILENT
	jz      @F
	and     al,not 2
@@:
	mov     fBreakPrint,al
;
; if (fBreak || fPrint)
;    print out the string
;
	or      al,al           ; if !(fBreak | fPrint)
	jz      kdtnoprint

assumes es,NOTHING
	mov	es,esSave	; restore registers
	mov	ax,axSave
	mov	bx,bxSave
	push    psz
	call    KOutDSStr       ; output the string

	push    offset szCRLF
	call    KOutDSStr

kdtnoprint:
	test    fBreakPrint,1   ; if fBreak, then FatalExit
	jz      kdtexit
kdtdobreak:
	cCall   FatalExit,<flags>
kdtexit:
	SetKernelDS es
	mov	es:fKTraceOut,0 ; Clear DebugWrite flag

	mov	ax, axSave
	mov	bx, bxSave
	mov	es, esSave
cEnd

ifdef DISABLE

flags	equ	word ptr [bp+6]
msg	equ	word ptr [bp+8]
appDS	equ	word ptr [bp-2]
appAX	equ	word ptr [bp-4]
;myDS	equ	word ptr [bp-6]
_krDebugTest proc far			;Per-component - check right flags
public _krDebugTest
	push	bp
	mov	bp, sp
	push	ds			; at BP-2
	push	ax			; at BP-4
	mov	ax, _DATA
	cmp	ax, 1000h		;; DATA should be selector, not addr
	jnc	skip
	mov	ds, ax
	assume	ds:_DATA

	mov	ax, [flags]		; See if component enabled
	and	ax, [_krInfoLevel]
	and	al, byte ptr [_Win3InfoLevel] ; See if system enabled
	cmp	ax, [flags]
	jnz	skip

	push	bx            		; Print it, so format message
	test	al, DEB_ERRORS
	mov	bx, dataoffset STR_krError
	jnz	@F
	test	al, DEB_WARNS
	mov	bx, dataoffset STR_krWarn
	jnz	@F
	test	al, DEB_TRACES
	mov	bx, dataoffset STR_krTrace
	jz	short deb_no_msg_type

@@:	push	bx
	call	KOutDSStr

deb_no_msg_type:
	mov	bx, dataoffset STR_krTable
	or	ah, ah
	jz	deb_show_it
@@:     add	bx, 2			; get next string table entry
	shr	ah, 1			; find which category
	jnz	@B

deb_show_it:
	push	[bx]			;; push parameter
	call	KOutDSStr
	pop	bx			;; restore reg

	mov	ax, [appAX]		; print message passed in
	push	ds
	mov	ds, appDS               ; restore App ds for error strings
	push	[msg]
	call	KOutDSStr
	pop	ds			; restore kernel DS

	mov	ax, [flags]		; shall we have a breakpoint?
	and	ax, [_krBreakLevel]
	and	al, byte ptr _Win3BreakLevel
	cmp	ax, [flags]
	jnz	skip

	INT3_DEBUG
skip:
	test    byte ptr [flags], DEB_FERRORS
	jz	@F

        push    0
        push    cs:MyCSDS
	push	word ptr [bp+8]
	cCall	FatalAppExit	;,<0,DGROUP,[bp+8]>
@@:
	pop	ax
	pop	ds
	pop	bp
	retf
_krDebugTest endp
endif;  DISABLE

endif;  KDEBUG

ife KDEBUG			; RETAIL ONLY SECTION STARTS HERE!

;-----------------------------------------------------------------------;
;									;
;   FatalExit() -							;
;									;
;-----------------------------------------------------------------------;

; Retail version.  The Debug version is in RIP.C.

	assumes	ds, nothing
	assumes	es, nothing

cProc FatalExit,<PUBLIC,FAR>
	parmW	errCode
cBegin
	SetKernelDS
        push    0
	push    ds
	push    dataOffset szFatalExit
	cCall   IFatalAppExit   ;,<0, ds, dataOffset szUndefDyn>
cEnd

cProc FatalExitDeath,<PUBLIC,FAR>

	parmW	errCode

	localD	pbuf
	localV	buf,6
cBegin

if 0
	mov	ax,4CFFh
	int	21h
else
	SetKernelDS
	cCall	TextMode		; Is USER started?

;	cmp	pDisableProc.sel,0
;	je	fex1
;	cCall	pDisableProc		; Yes, Call USER's DisableOEMLayer()
;					;  to get to the text screen
;	jmps	fex1a
;fex1:
;	mov	ax, 6			; set text mode
;	int	10h
;fex1a:

; Is Int 21h hooked?

	cmp	prevInt21Proc.sel,0
	je	fex2
	cCall	InternalDisableDOS	; Yes, disable Int 21h

fex2:
	mov	cx,errCode		; No output if error code is zero
	jcxz	fe4

; Write "FatalExit = " message to STDOUT.

	mov	bx,1
	mov	dx,dataOffset szExitStr1
	mov	cx,20
	mov	ah,40h
	int	21h

; Error code of FFFF means Stack Overflow.

	mov	dx,errCode
	inc	dx
	jnz	fe1
	mov	dx,dataOffset szExitStr2 ; "Stack Overflow" message
	mov	cx,17
	mov	ah,40h
	int	21h
	jmps	fe4

	UnSetKernelDS
fe1:

; Write out error code in Hex.

	dec	dx
	push	ss
	pop	es
	lea	di,buf
	mov	ax,'x' shl 8 OR '0'
	stosw
	mov	cx,4
	rol	dx,cl			    ; Rotate most significant into view
fe2:
	mov	ax,dx
	and	ax,0Fh
	push	cx
	mov	cl,4
	rol	dx,cl			    ; Rotate next byte into view
	pop	cx
	add	al,'0'
	cmp	al,'9'
	jbe	fe3
	add	al,'A'-':'
fe3:
	stosb
	loop	fe2
	mov	ax,10 shl 8 OR 13
	stosw
	xor	ax,ax
	stosb
	lea	dx,buf
	push	ss
	pop	ds
	mov	cx,8
	mov	bx,1
	mov	ah,40h
	int	21h			    ; Write it out
fe4:
	mov	ax,errcode
	cCall	ExitKernel,<ax>
endif
cEnd
    

else				    ; DEBUG ONLY SECTION STARTS HERE!

;-----------------------------------------------------------------------;
;									;
;   GetSymFileName() -							;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc GetSymFileName,<PUBLIC,NEAR>,<ds,si,di>

ParmW hExe
ParmD lpName

cBegin
	cld
	les	di,lpName
	SetKernelDS
;;	cmp	fWinX,0
;;	je	slowboot
;;	mov	ax,hExe
;;	cmp	hExeHead,ax
;;	mov	ds,ax
;;	UnSetKernelDS
;;	je	makename
;;	mov	si,ds:[ne_pfileinfo]
;;	or	si,si
;;	jnz	havename
;;makename:
;;	mov	si,ds:[ne_restab]
;;	xor	ax,ax
;;	lodsb
;;	mov	cx,ax
;;	rep	movsb
;;appendext:
;;	mov	ax,'S.'
;;	stosw
;;	mov	ax,'MY'
;;	stosw
;;	xor	ax,ax
;;	stosb
;;	jmps	namedone
;;
;;slowboot:
	mov	ds,hExe
	mov	si,ds:[ne_pfileinfo]
havename:
	add	si,opFile
nameloop:
	lodsb
	cmp	al,'.'
	je	appendext
	stosb
	jmp	nameloop
appendext:
	mov	ax,'S.'
	stosw
	mov	ax,'MY'
	stosw
	xor	ax,ax
	stosb
	les	ax,lpName
	mov	dx,es
cEnd


;-----------------------------------------------------------------------;
;									;
;   OpenSymFile() -							;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc OpenSymFile,<PUBLIC,NEAR>,<ds,si,di>

ParmD	path

LocalV	Buffer,MaxFileLen

cBegin
	lds	dx,path		; get pointer to pathname
	mov	di,3D40h	; open file function code (SHARE_DENY_NONE)

	; We don't use open test for existance, rather we use get
	; file attributes.  This is because if we are using Novell
	; netware, opening a file causes Novell to execute its
	; own path searching logic, but we want our code to do it.

	mov	ax,4300h		; Get file attributes
	int	21h			; Does the file exist?
	jc	opnnov			; No, then don't do the operation
	mov	ax,di			; Yes, open the file then
	int	21h
	jnc	opn2
opnnov:
	mov	ax, -1
opn2:	mov	bx,ax
cEnd


;-----------------------------------------------------------------------;
;									;
;   GetDebugString() -							;
;									;
;-----------------------------------------------------------------------;

; Finds the 'strIndex'-th string in 'szDebugStr'.  The strings are defined
;  in STRINGS.ASM.

	assumes	ds, nothing
	assumes	es, nothing

cProc GetDebugString,<PUBLIC,NEAR>,<di>

parmW	strIndex

cBegin
	SetKernelDS	es
	mov	di,dataOffset szDebugStr
	mov	bx,strIndex
	cld
gds1:
	dec	bx
	jl	gdsx
	xor	ax,ax
	mov	cx,-1
	repne	scasb
	cmp	es:[di],al
	jne	gds1
	xor	di,di
	mov	es,di
gdsx:
	mov	ax,di
	mov	dx,es
	UnSetKernelDS	es
cEnd


;-----------------------------------------------------------------------;
;									;
;   GetExeHead() -							;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc GetExeHead,<PUBLIC,NEAR>

cBegin	nogen
	push	ds
	SetKernelDS
	mov	ax,[hExeHead]
	pop	ds
	UnSetKernelDS
	ret
cEnd	nogen


;-----------------------------------------------------------------------;
;									;
;   LSHL() -								;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc LSHL,<PUBLIC,NEAR>

cBegin	nogen
	pop	bx
	pop	cx
	pop	ax
	xor	dx,dx
lshl1:
	shl	ax,1
	rcl	dx,1
	loop	lshl1
	jmp	bx
cEnd	nogen


;-----------------------------------------------------------------------;
;									;
;   FarKernelError() -							;
;									;
;	05-09-91 EarleH modified to save and restore all registers.	;
;									;
;-----------------------------------------------------------------------;

; Far entry point for KernelError().  Allows the multitude of calls to
; KernelError() be made as near calls.

	assumes	ds, nothing
	assumes	es, nothing

cProc FarKernelError,<PUBLIC,FAR,NODATA>

cBegin nogen
	push	bp
	mov	bp,sp

	SAVEREGS
	mov	ax, _DATA
	mov	ds, ax
;	push	[bp+14]
	push	[bp+12]
	push	ds		; seg of string
	push	[bp+10]
	push	[bp+8]
	push	[bp+6]
	mov	bp,[bp]
	call	KernelError
	or	ax, ax
	RESTOREREGS
	pop	bp
	jz	@F
	INT3_DEBUG
@@:
	ret	10
cEnd	nogen

;-----------------------------------------------------------------------;
;									;
;   NearKernelError() -							;
;									;
;	05-09-91 EarleH modified to save and restore all registers.	;
;									;
;-----------------------------------------------------------------------;

cProc NearKernelError,<PUBLIC,NEAR,NODATA>

cBegin nogen
	push	bp
	mov	bp,sp

	SAVEREGS
	mov	ax, _DATA
	mov	ds, ax

;	push	[bp+12]		; only pass 4 words now
	push	[bp+10]		; err code
	push	ds		; seg of string
	push	[bp+8]		; offset of string
	push	[bp+6]
	push	[bp+4]
	mov	bp,[bp]		; hide this stack frame
	call	KernelError
	or	ax, ax
	RESTOREREGS
	pop	bp
	jz	@F
	INT3_DEBUG
@@:
	ret	8
cEnd	nogen


;-----------------------------------------------------------------------;
;									;
;   IsCodeSelector() -							;
;									;
;-----------------------------------------------------------------------;

cProc IsCodeSelector,<PUBLIC,NEAR>

parmW Candidate

cBegin
	mov	ax,Candidate
	lar	dx,ax
	jz	lar_passed
	xor	ax,ax
	jmp	ics_ret
lar_passed:
	xor	ax,ax
	test	dh,00001000b		; Executable segment descriptor?
	jz	ics_ret			; No
	push	cs			; Yes
	pop	dx
	and	dx,3
	and	Candidate,3		; RPL matches that of CS?
	cmp	dx,Candidate
	jne	ics_ret			; No
	inc	ax			; Yes
ics_ret:
cEnd

endif;  DEBUG                           ; DEBUG ONLY SECTION ENDS HERE


;-----------------------------------------------------------------------;
;									;
;   DebugBreak() -							;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

if KDEBUG
db_tab	dw	db_t, db_w, db_e, db_f, db_k, db_3
db_len	equ	($ - db_tab) / 2
endif

cProc	DebugBreak,<PUBLIC,FAR>

cBegin	nogen
if KDEBUG
	cmp	ax, 0DACh
	jnz	db_3
	cmp	bx, db_len
	jae	db_3
	shl	bx, 1
	jmp	db_tab[bx]
db_t:	krDebugOut DEB_TRACE, "Test trace"
	jmps	db_end
db_w:	krDebugOut DEB_WARN, "Test warning"
	jmps	db_end
db_e:	krDebugOut DEB_ERROR, "Test error"
	jmps	db_end
db_f:	krDebugOut DEB_FERROR, "Test fatal error"
	jmps	db_end
db_k:	kerror	0, "This is a kernel error"
	jmps	db_end
db_3:
endif

	INT3_DEBUG		    ; Jump to debugger if installed
db_end:
	ret
cEnd	nogen

;-----------------------------------------------------------------------;
;									;
;   TextMode() - Enter text mode for debugging load failure		;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc TextMode,<PUBLIC,NEAR,NODATA>,<ds>
cBegin
	SetKernelDS
	cmp	word ptr [pDisableProc+2],0
	je	tm1
	cCall	[pDisableProc]
	jmps	tm2
tm1:
	mov	ax, 3
	int	10h
tm2:
cEnd




;-----------------------------------------------------------------------;
;									;
;   DoAbort() - 							;
;									;
;-----------------------------------------------------------------------;

	assumes	ds, nothing
	assumes	es, nothing

cProc DoAbort,<PUBLIC,NEAR>

cBegin	nogen
	SetKernelDS
	cCall	TextMode
;	cmp	word ptr [pDisableProc+2],0
;	je	doa1
;	cCall	[pDisableProc]
;doa1:
	mov	ax,1
	cCall	ExitKernel,<ax>
	UnSetKernelDS
cEnd	nogen

;-----------------------------------------------------------------------;
;
; HandleParamError
;
; This entry point is jumped to by the parameter validation code of
; USER and GDI (and KERNEL).  Its job is to parse the error code
; and, if necessary, RIP or jmp to the error handler routine
;
; Stack on entry:   Far return addr to validation code
;                   Saved error handler offset
;                   Saved BP
;                   API far ret addr
;
;-----------------------------------------------------------------------;

ERR_WARNING             equ 08000h      ; from error.h/windows.h/layer.inc

LabelFP <PUBLIC, HandleParamError>
	push	bp
	mov	bp,sp
	push    bx          ; save err code

	push	bx	    ; push err code

        push    [bp+4]      ; push err return addr as place where error occured
        push    [bp+2]

	push	cx	    ; push parameter
	push	ax
        call    far ptr LogParamError   ; yell at the guy

	pop	bx
	pop	bp

	test	bh,high(ERR_WARNING)  ; warn or fail?
	errnz	low(ERR_WARNING)
	jnz	@F

        pop     bx
        pop     ax          ; pop far return addr

        pop     bx          ; get error handler address

	pop	bp	    ; restore BP
	and	bp,not 1    ; Make even in case "inc bp" for ATM

        push    ax
        push    bx          ; far return to handler

        xor     ax,ax       ; set dx:ax == 0 for default return value
	cwd
        xor     cx,cx       ; set cx == 0 too, for kernel error returns

        mov     es,ax       ; clear ES just in case it contains
                            ; a Windows DLL's DS...
@@:
        retf

cProc   DebugFillBuffer,<PUBLIC, FAR, PASCAL, NONWIN>,<DI>
ParmD   lpb
ParmW   cb
cBegin
if KDEBUG
assumes ES,data
        mov     ax,_DATA
        mov     es,ax
        test    es:DebugOptions,DBO_BUFFERFILL
assumes ES,nothing
        jz      dfbexit
        les     di,lpb
        mov     cx,cb
        mov     ax,(DBGFILL_BUFFER or (DBGFILL_BUFFER shl 8))
        cld
        shr     cx,1
        rep     stosw
        rcl     cx,1
        rep     stosb
dfbexit:
endif;  KDEBUG
cEnd

;========================================================================
;
; void FAR _cdecl DebugOutput(UINT flags, LPCSTR lpszFmt, ...);
;
; NOTE: there is a CMACROS bug with C that causes the parameter offsets
;       to be calculated incorrectly.  Offsets calculated by hand here.
;
cProc   DebugOutput,<PUBLIC, FAR, C, NONWIN>
flags   equ <[bp+2+4]>      ; parmW flags   point past ret addr & saved bp
lpszFmt equ <[bp+2+4+2]>    ; parmD lpszFmt
cBegin
if KDEBUG
        push    bp          ; generate a stack frame
        mov     bp,sp

        SAVEREGS            ; save all registers

        push    ds
        SetKernelDS

        lea     ax,flags    ; point at flags, lpszFmt, and rest of arguments
        push    ss
        push    ax
        call    DebugOutput2

        UnsetKernelDS
        pop     ds
        or      ax,ax       ; test break flag
        RESTOREREGS

        pop     bp

        jz      @F          ; break if needed
        INT3_DEBUG
@@:
endif
cEnd

;========================================================================
;
; void WINAPI LogError(UINT err, void FAR* lpInfo);
;
cProc   LogError,<PUBLIC, FAR, PASCAL, NONWIN>
ParmD   err
ParmW   lpInfo
cBegin
        SAVEREGS
assumes ds,NOTHING

        cCall   DebugLogError,<err, lpInfo>

        RESTOREREGS
cEnd

;========================================================================
;
; void WINAPI LogParamError(UINT err, FARPROC lpfn, void FAR* param);
;
cProc   LogParamError,<PUBLIC, FAR, PASCAL, NONWIN>
ParmW   err
ParmD   lpfn
ParmD   param
cBegin
assumes ds,NOTHING
        SAVEREGS

        ; Call debugger hook (note the reversed parameter order)
	;
	cCall   DebugLogParamError,<param, lpfn, err>

if KDEBUG
	push    ds
	SetKernelDS
assumes ds,DATA
	mov	bx, [bp+0]		; address of faulting code
	mov	bx, ss:[bx+0]
;	mov	bx, ss:[bx+0]
	mov	bx, ss:[bx+4]
	cCall   LogParamError2,<err, lpfn, param, bx>
	UnsetKernelDS
	pop     ds
assumes ds,NOTHING
        or      ax,ax               ; test break flag
endif
	RESTOREREGS
if KDEBUG
        jz      @F                  ; break if needed
        INT3_DEBUG
@@:
endif
cEnd

;-----------------------------------------------------------------------

sEnd	CODE

end
