;       SCCSID = @(#)dyndtra.asm	6.1 90/11/17
SCCSID	equ	"@(#)dyndtra.asm		13.13 90/09/17"
;
;	IBM/Microsoft Confidential
;
;	Copyright (c) IBM Corporation 1987, 1989
;	Copyright (c) Microsoft Corporation 1987, 1989
;
;	All Rights Reserved
;
PAGE	,132
TITLE	DYNDTRA.ASM - Dynamic Trace DTR low-level MASM routines
NAME	DYNDTRA

;***	DYNDTRA.ASM -- Dynamic Trace DTR low-level MASM routines
;
;	This file implements fast low-level routines to manage the
;	DynamicTrace DTR records.
;
;	These routines contain highly optimized code - read with care.
;
;  Change Activity:
;
;  19th Mar 96 RJM Implement new RPN OPs for Merlin
;  11th Jun 96 RJM Implement more new RPN OPs for Merlin

INCL_DOSERRORS	equ 0			;for bseerr.inc

.xlist
	include dosseg.inc
	include basemaca.inc
	include osmaca.inc
	include fnotask.inc
	include dossymc.inc
	include sigdef.inc
	include except.inc
	include osconst.inc
	include infoseg.inc
	include pgexport.inc
	include vmexport.inc
	include selxport.inc
	include tkexport.inc
	include curdir.inc
	include dirent.inc
	include screen.inc
	include sem.inc
	include find.inc
	include tkptda.inc
	include seldesc.inc
	include sched.inc
	include kmode.inc
	include mi.inc
	include bseerr.inc
;	include pglocal.inc

	include dynxport.inc
	include dynapi.inc
	include dynlocal.inc
.list

if	RAS

	CpuMode 386
	ModName ,FNO_DYNDTRA_ASM

	EXTRN32 _DYNDynamicTrace
	EXTRN32 _SELVirtToLin

	FARCODE HIGH32
DYNSWAP_TEXT	segment

;***LP	_dynDTRCompile - Prepare a tracepoint for the tracepoint interpreter
;
;	_dynDTRCompile prepares a tracepoint for the tracepoint interpreter.
;
;	This will guarantee the following :
;	      * MTEs can be swappable.
;	      * The interpreter can trust that all opcodes are valid.
;	      * The interpreter can trust that all operands are valid.
;
;	This imposes the following restrictions :
;	      * Opcodes must be sequentially contiguous.
;	      * No more than DYN_MAXTARGETS simultaneous separate forward
;		references may be used at once per tracepoint.
;
;	_dynDTRCompile uses pdtlv->dtlv_pasTargets as a temporary workspace
;	to hold it's jump target information.  This is just an array of
;	DYN_MAXTARGETS shorts, allocated from the heap.  See dynAddTarget.
;	dtlv_pasTargets is allocated and freed in _dynDTOFixup only, and is
;	otherwise invalid.
;
;	ulong_t dynDTRCompile	(pdto, pdtr, pdtlv)
;
;	ENTRY	pdto	- DTO pointer
;		pdtr	- DTR pointer
;		pdtlv	- Local variables pointer, not required.
;
;	EXIT	ulong_t, standard error code
;
;	Pseudocode :
;
;	setup registers as needed;
;	goto LOOP_START:
;
;	While (not end of code list) {
;
;	    *	if opcode has a dword operand:
;			verify operand; skip 2 bytes; fall through
;	    *	if opcode has a word operand:
;			verify operand; skip 1 byte; fall through
;	    *	if opcode has a byte operand:
;			verify operand; skip 1 byte; fall through
;	    *	if opcode has no operand:
;			fall through
;
;		if (at a previously calculated jump target)
;			remove the jump target using a jump out-of-line.
;		else if (passed over next known jump target)
;			error.
;
;	    LOOP_START:
;		get opcode
;		if (opcode < MAXOPCODE)
;			goto error.
;
;		Jump through jump table indexed by the current opcode.
;		Possible entry points marked here with '*'.
;
;	    *	add a jump target
;	    *	Verify DynTrc Variable index opearand (immediate mode only).
;	    *	Push OXS: Verify operand is within dti_padtl bounds
;	    *	Push OXF: Verify operand is within dti_padtl bounds
;	}
;

;	Opcode jump table for _dynDTRCompile:

apfnCompile:
;					Opcode	(Mneumonic)	Operand

dd	offset	FLAT:com40	;	00h	Push	EAX
dd	offset	FLAT:com40	;	01h	Push	ECX
dd	offset	FLAT:com40	;	02h	Push	EDX
dd	offset	FLAT:com40	;	03h	Push	EBX
dd	offset	FLAT:com40	;	04h	Push	ESP
dd	offset	FLAT:com40	;	05h	Push	EBP
dd	offset	FLAT:com40	;	06h	Push	ESI
dd	offset	FLAT:com40	;	07h	Push	EDI
dd	offset	FLAT:com40	;	08h	Push	ES
dd	offset	FLAT:com40	;	09h	Push	CS
dd	offset	FLAT:com40	;	0ah	Push	SS
dd	offset	FLAT:com40	;	0bh	Push	DS
dd	offset	FLAT:com40	;	0ch	Push	EFlags
dd	offset	FLAT:com40	;	0dh	Push	EIP
dd	offset	FLAT:com40	;	0eh	Push	FS
dd	offset	FLAT:com40	;	0fh	Push	GS
dd	offset	FLAT:com20	;	10h	Push	W	<WORD>
dd	offset	FLAT:com60	;	11h	Push	OXS	<BYTE>
dd	offset	FLAT:com40	;	12h	Push	WIS
dd	offset	FLAT:com40	;	13h	Push	SIS
dd	offset	FLAT:com30	;	14h	Log	WN	<BYTE>
dd	offset	FLAT:com40	;	15h	Log	MRS
dd	offset	FLAT:com40	;	16h	Add
dd	offset	FLAT:com20	;	17h	NOP	W	<WORD>
dd	offset	FLAT:com20	;	18h	NOP	W	<WORD>
dd	offset	FLAT:com40	;	19h	Log	ARS
dd	offset	FLAT:com10	;	1ah	Push	D	<DWORD>
dd	offset	FLAT:com90	;	1bh	Push	OXF	<PushOXF>
dd	offset	FLAT:com40	;	1ch	Push	FIF
dd	offset	FLAT:com40	;	1dh	Cnvrt	FXS
dd	offset	FLAT:com40	;	1eh	Cnvrt	SXF
dd	offset	FLAT:com40	;	1fh	Cnvrt	DXS
dd	offset	FLAT:com40	;	20h	Cnvrt	SXD
dd	offset	FLAT:com30	;	21h	Log	DN	<BYTE>
dd	offset	FLAT:com40	;	22h	Log	MRF
dd	offset	FLAT:com40	;	23h	Log	ARF
dd	offset	FLAT:com40	;	24h	Sub
dd	offset	FLAT:com40	;	25h	Mul
dd	offset	FLAT:com30	;	26h	Pop	N	<BYTE>
dd	offset	FLAT:com80	;	27h	Move	V	<VAR>
dd	offset	FLAT:com80	;	28h	Push	V	<VAR>
dd	offset	FLAT:com80	;	29h	Inc	V	<VAR>
dd	offset	FLAT:com40	;	2ah	Move	VIi
dd	offset	FLAT:com40	;	2bh	Push	VIi
dd	offset	FLAT:com40	;	2ch	Inc	VIi
dd	offset	FLAT:com70	;	2dh	Jmp	N	<JMP TARGET>
dd	offset	FLAT:com70	;	2eh	JmpZ	N	<JMP TARGET>
dd	offset	FLAT:com70	;	2fh	JmpN	N	<JMP TARGET>
dd	offset	FLAT:com70	;	30h	JmpP	N	<JMP TARGET>
dd	offset	FLAT:com40	;	31h	Abort
dd	offset	FLAT:com80	;	32h	Or	V	<VAR>
dd	offset	FLAT:com40	;	33h	Remove
;RJM
dd	offset	FLAT:com40	;	34h	SysDump
dd	offset	FLAT:com40	;	35h	And
dd	offset	FLAT:com40	;	36h	Or 
dd	offset	FLAT:com40	;	37h	Xor
dd	offset	FLAT:com40	;	38h	Not
dd	offset	FLAT:com40	;	39h	Exit
dd	offset	FLAT:com40	;	3ah	ProcDump
;rjm2
dd	offset	FLAT:com20	;	3bh	SetMin W
dd	offset	FLAT:com20	;	3ch	SetMaj W
dd	offset	FLAT:com40	;	3dh	SetMin 
dd	offset	FLAT:com40	;	3eh	SetMaj 
dd	offset	FLAT:com40	;	3fh	Xchg  

.errnz ($ - offset apfnCompile - 4*(DYN_MAXOPCODE+1))

Procedure	_dynDTRCompile
	ASSUME	DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

	ArgVar	dc_pdto,DWORD
	ArgVar	dc_pdtr,DWORD
	ArgVar	dc_pdtlv,DWORD

	EnterProc
	SaveReg <ebx, edi, esi>

;	Set up the compiler.

	mov	edi,dc_pdtlv
	mov	edi,[edi].dtlv_pasTargets
	lea	edi,[edi].2*(DYN_MAXTARGETS-1)	; (EDI)-> last jump target
	mov	word ptr [edi],0		; invalidate last jump target
	mov	ebx,dc_pdtr			; (EBX) = pdtr
	mov	esi,ebx				; (ESI) = pdtr
	add	esi,size dtr_s			; (ESI) = start of DT code
	movzx	ecx,[ebx].dtr_cbDTCODE		; (ECX) = DT code length
	jmp	short com40			; Get compiler started...

;	------	COMPILER START	--------
;	Usual Register Contents :
;	(EAX) = Available Scratch Register
;	(EBX) = pdtr
;	(ECX) = Remaining DT code length
;	(EDX) = Available Scratch Register
;	(ESI) = Current DT code location
;	(EDI) = Current jump target pointer (0 terminated list)
;	(DS) = (ES) = Flat

;	Account for a DWORD Operand

com10:	jecxz	short com100			; error_invalid_operand
	inc	esi
	dec	ecx

	jecxz	short com100			; error_invalid_operand
	inc	esi
	dec	ecx

;	Account for a WORD Operand

com20:	jecxz	short com100			; error_invalid_operand
	inc	esi
	dec	ecx

;	Account for a BYTE Operand

com30:	jecxz	short com100			; error_invalid_operand
	inc	esi
	dec	ecx

;	No operand.
;	------	COMPILER LOOP ENTRY POINT --------

com40:	jecxz	short com120			; Done - NO_ERROR
	cmp	cx,[edi]			; Check next jump target
	jb	short com100			; invalid jump target exists
	jne	short com50			; no jump target - skip

;	Remove a jump target from top of list.
;	There are no duplicate jump targets in the list.

	inc	edi				; remove a jump target.
	inc	edi

;	We have removed any previous jump target references at this point.

com50:	lodsb					; get opcode
	dec	ecx				; one less byte
	cmp	al,DYN_MAXOPCODE		; above max. opcode?
	ja	short com100			; yes - invalid opcode

;	Dispatch to the operand handler.  (AL) = Opcode

	movzx	eax,al
	jmp	DWORD PTR cs:apfnCompile[4*eax] ; jump through table

;	PushOXS: Validate the object number.

com60:	jecxz	short com100		; error_invalid_operand
	mov	edx,dc_pdto		; (EDX) = pdto
	movzx	eax,BYTE PTR [esi]	; (EAX) = Object number
	or	eax,eax			; Can't have zero object number
	jz	short com61

	cmp	eax,[edx].dto_dti.dti_cdtl
	jbe	short com30		; Operand OK.  Account for BYTE operand

com61:	mov	eax,ERROR_INVALID_SEGMENT_NUMBER
	jmp	short com110		; Object number too large.

;	Add a new jump target.

com70:	mov	edx,dc_pdtlv
	mov	edx,[edx].dtlv_pasTargets
	CCALL32 dynAddTarget		; not C Callable.
	or	eax,eax
	jz	short com40		; NO_ERROR (operand fetched)
	jmp	short com100		; Bad jump target

;	Verify the Variable Index WORD operand falls into the array.

com80:	cmp	ecx,2			; room for a 2-byte operand?
	jb	short com100		; error_invalid_operand

	mov	edx,dc_pdto		; (EDX) = pdto
	movzx	eax,WORD PTR ds:[esi]	; (EAX) = Variable Index
	cmp	ds:[edx].dto_dth.dth_clVar,eax	; Valid variable Index ?
	jna	short com100		; no - error_invalid_variable_index
	jmp	short com20		; yes - Remove the WORD operand

;	PushOXF: Validate the object number.

com90:	cmp	ecx,4			; room for a 4-byte operand?
	jb	short com100		; error_invalid_operand

	mov	edx,dc_pdto		; (EDX) = pdto
	mov	eax,DWORD PTR [esi]	; (EAX) = Object number
	or	eax,eax			; Can't have zero object number
	jz	short com61		; ERROR_INVALID_SEGMENT_NUMBER

	cmp	eax,[edx].dto_dti.dti_cdtl
	jbe	short com10		; Operand OK.  Account for DWORD operand

	jmp	short com61		; Object number too large.
					; ERROR_INVALID_SEGMENT_NUMBER

;	------	COMPILER END	--------

;	ERROR_INVALID_DATA detected in compiler.

com100: mov	eax,ERROR_INVALID_DATA	; Invalid Operand

;	Error detected in compiler.
;	(EAX) = Error Code

com110: mov	ecx,dc_pdtr
	and	[ecx].dtr_Reserved,NOT DTR_COMPILED ; Tracepoint not compiled.
	jmp	short com130

;	No error detected in compiler.  Tracepoint compiled OK.

com120:
ifdef	MISCSTRICT
;	Because of the way the jump targets are managed, there should
;	be no 'leftover' jump targets on the success path.

	cmp	word ptr [edi],0	; any leftover jump targets?
	jne	short comie		; yes - Panic
endif
	mov	ecx,dc_pdtr
	or	[ecx].dtr_Reserved,DTR_COMPILED ; Tracepoint is compiled.
	xor	eax,eax			; NO_ERROR

;	dynDTRCompile exit

com130: RestoreReg <esi, edi, ebx>
	LeaveProc
	RETC

ifdef	MISCSTRICT
comie:	Panic	<'dynDTRCompile: Leftover Jump Target'>
endif

EndProc _dynDTRCompile


;***LP	dynAddTarget - Add a jump target for dynDTRCompile
;
;	dynAddTarget calculates the next jump location relative to the
;	start of the code sequence, and sorts that value into the jump
;	table work area using a simple insertion sort.  The jump target
;	with the largest value is first in the jump target table.
;
;	Jump targets are calculated wherever a jump instruction is found
;	in the instruction stream during _dynDTRCompile.
;
;	As _dynDTRCompile compiles the tracepoint, whenever it is at
;	a valid instruction boundary, it checks the value in (CX) (number
;	of bytes left inthe instruction stream) against the top jump target.
;	Note: (CX) decreases to zero as we compile the tracepoint, so
;	jump targets are relative to the end of the instruction stream.
;
;	This comparison can have the following results :
;
;	(CX) > next jump target.
;		Do nothing to jump target array.
;
;	(CX) = next jump target
;		Remove one jump target from the array.  It is valid.
;
;	(CX) < next jump target
;		An invalid jump target exists.
;
;	The implementation of dynAddTarget limits the number of
;	unique simultaneous forward jump targets to 256.  This can
;	be increased later if found to be too small, but this should
;	be plenty to handle almost all cases.
;
;	ENTRY	(EBX) = pdtr
;		(ECX) = bytes left in code
;		(EDX) = start of jump target work area
;		(ESI) = current code location (word jump operand)
;		(EDI) = current jump target pointer (0 terminated list)
;
;	EXIT	(EAX) = NO_ERROR
;			(EBX) = pdtr
;			(ECX) = bytes left in code (after jump instruction)
;			(EDX) = start of jump target work area
;			(ESI) = current code location (after jump instruction)
;			(EDI) = new jump target pointer.
;			Zero flag set
;		or
;		(EAX) = Error Code
;			Zero flag clear
;
;	USES	EAX, ECX, ESI, EDI, Flags
;

Procedure dynAddTarget,near,local

	SaveReg <ebx>

	jecxz	short dat7		; error_invalid_operand
	dec	ecx
	jecxz	short dat7		; error_invalid_operand
	dec	ecx

	lodsw				; (AX) = operand
	movzx	eax,ax			; (EAX) = relative jump distance
	add	eax,esi			; (EAX) = flat jump location
	sub	eax,ebx			; (AX) = dtr-relative jump location
	sub	eax,size dtr_s		; (AX) = code-relative jump location
	neg	ax
	add	ax,[ebx].dtr_cbDTCODE	; (AX) = CBDTCODE - (AX)

	cmp	ax,cx			; valid jump target?
	ja	short dat7		; no - we will not encounter it.
	je	short dat8		; Jump to next instruction - NO_ERROR.

;	(AX) = DynTrc jump target.  This is the value of (CX) when the target
;	opcode is reached.  We now need to insert this jump target into
;	the jump target work area in DESCENDING order, so that we need
;	only check the top jump target as we compile the code.
;
;	Find the hole to use.  Zero marks the end of the list, but zero
;	can also be a jump target.

	lea	ebx,[edi]-2
dat1:	inc	ebx			; Move to next jump target in list
	inc	ebx
	cmp	ax,[ebx]		; check jump target.
	je	short dat8		; ignore duplicate jump targets...
	jb	short dat1		; not at the hole yet...

	cmp	edi,edx			; any work area left?
	je	short dat6		; no - too_many_jump_targets

;	Here, we insert a new jump target in the list.
;	(EDI) -> last jump target in existing list
;	(EBX) -> One entry past insertion point of jump target

	dec	edi			; Add one jump target to list.
	dec	edi			; (EDI) -> Last jump target in new list

;	Using a simple insertion sort, we insert the new entry, and
;	ripple the rest of the list to make room.

dat2:	dec	ebx
	dec	ebx			; (EBX) -> New insertion point
	xchg	ax,[ebx]		; Ripple copy toward last target
	cmp	ebx,edi			; (EBX) at last target yet?
	jne	dat2			; no. continue.

	jmp	short dat8		; jump target inserted. NO_ERROR

dat6:	mov	eax,ERROR_INVALID_DATA	; error_too_many_jump_targets
	jmp	short dat9

dat7:	mov	eax,ERROR_INVALID_DATA	; error_invalid_operand
	jmp	short dat9

dat8:	xor	eax,eax			; NO_ERROR
dat9:	RestoreReg <ebx>
	RETC

EndProc dynAddTarget


;***LP	_dynDTRValidate - Check the tracepoint validity
;
;	_dynDTRValidate makes sure the DTR is valid.
;
;	dtr_Opcode is checked for equality with the byte it points
;	to, and the instruction at that address is verified that the
;	single-step will not interfere.
;
;	The dtr_Opcode byte and the byte in user space pointed to by
;	dtr_Offset MUST be identical, even if it is an opcode prefix.
;
;	If an opcode prefix is found in user space, the prefix
;	is skipped, until there are no more prefixes.
;
;	After skipping prefixes, if the opcode is in the badinstrtbl,
;	an error is returned.
;
;	If the opcode is in the spclintrtbl, the ModRM byte is checked.
;
;	ulong_t dynDTRValidate	(pdto, pdtr, pdtlv)
;
;	ENTRY	pdto	- DTO pointer
;		pdtr	- DTR pointer
;		pdtlv	- Local variables pointer, with these fields set:
;		pdtlv-> dtlv_pAliasedPoint
;		pdtlv-> dtlv_cbAlias
;		pdtlv-> dtlv_pAlias
;
;	EXIT	ulong_t, standard error code
;
;	USES	EAX, ECX, EDX, EFlags

;	Prefix Opcodes

prefixtbl	db	066h		; OPSIZ
		db	067h		; ADDRSIZ
		db	026h		; ES:
		db	036h		; SS:
		db	02Eh		; CS:
		db	03Eh		; DS:
		db	064h		; FS:
		db	065h		; GS:
		db	0f2h		; REP, REPE
		db	0f3h		; REPNE
		db	0f0h		; LOCK

NUMPREFIX	equ	$ - prefixtbl

;	Trace points may not be placed on instructions that effect
;	the CPU trace and interrupt flags.

badinstrtbl	db	09Ch	; pushf (or pushfd) - bad instruction table
		db	0CCh	; int 3
		db	0CDh	; int n
		db	0CEh	; into
		db	062h	; bound
		db	069h	; imul immed byte (or word)
		db	06Bh	; imul immed word (or dword)
NUMBADINSTR	equ	$ - badinstrtbl

;	The opcodes in the spclinstrtbl require analysis of the next byte.
;	includes div, idiv, mul, imul, not, neg, test.

spclinstrtbl	db	0F6h	; div, idiv, mul, imul, byte (or word)
		db	0F7h	; div, idiv, mul, imul, word (or dword)
NUMSPCLINSTR	equ	$ - spclinstrtbl

;	 overridetbl,  badinstrtbl and spclinstrtbl must be contiguous.

if2
.errnz (prefixtbl + NUMPREFIX - badinstrtbl)
.errnz (badinstrtbl + NUMBADINSTR - spclinstrtbl)
endif

;	If the opcode is in the spclinstrtbl, the next (ModRM) byte must
;	be examined.  If bit 5 is on in this byte, the instruction is
;	one of (div, idiv, mul, imul), and is disallowed.
;	(test, ????, not, neg) have this bit off.  See manual for details.
;
;	F6 /0	test immediate
;	F6 /1	????
;	F6 /2	not
;	F6 /3	neg
;	F6 /4	mul
;	F6 /5	imul
;	F6 /6	div
;	F6 /7	idiv
;
;	/n instruction format : mod|n|r/m == 76|543|210, so if bit 5 is
;	on, /n is /4, /5, /6, or /7, so the instruction is disallowed.
;

SPCLBADMASK	equ	020h
PREFETCHLENGTH	equ	15

Procedure	_dynDTRValidate
	ASSUME	DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING

	ArgVar	val_pdto,DWORD
	ArgVar	val_pdtr,DWORD
	ArgVar	val_pdtlv,DWORD

	EnterProc
	SaveReg <ebx, edi, esi>

	mov	ebx,val_pdtr		; (EBX) = pdtr
	mov	edx,val_pdtlv		; (EDX) = pdtlv
	mov	esi,[ebx].dtr_Offset	; (ESI) = flat offset of tracepoint
	sub	esi,[edx].dtlv_pAliasedPoint	; (ESI) = page offset of TP.

ifdef	MISCSTRICT
;	(ESI) is relative to the start of a page, and may not cross
;	a page boundary.  Verify this here.

	test	esi,NOT PAGEMASK
	jnz	valie1
endif

;	(ESI) = Distance from start of alias to start of instruction

	mov	ecx,[edx].dtlv_cbAlias	; (ECX) = alias region size (4K or 8K)
	sub	ecx,esi			; (ECX) = maximum accessable bytes

;	(ECX) = number of accessable bytes in alias region.
;	If this is more than the maximum length of an instruction, set
;	the number of bytes to scan to the maximum instruction length.

	cmp	ecx,PREFETCHLENGTH
	jb	short val0

	mov	ecx,PREFETCHLENGTH

;	(EBX) = val_pdtr
;	(ECX) = Number of bytes of code to scan, adjusted for a one-page
;		alias. An alias is normally two pages long, unless the
;		object is not large enough.  PAGESIZE >= (ECX) >= 1
;	(EDX) = val_pdtlv
;	(ESI) = code pointer, relative to start of alias.

val0:	add	esi,[edx].dtlv_pAlias	; (ESI) = flat pointer to aliased code
	lodsb				; (AL) = opcode byte
	lea	edx,[ecx]-1		; (EDX) = number of bytes left to scan.

	cmp	al,[ebx].dtr_Opcode	; (AL) must match opcode
	jne	short val6		; Error - invalid opcode byte

;	Skip any prefix opcodes.
;
;	Note: Because (CS) = FLAT and (ES) = FLAT here, we can use
;	(ES:EDI) for referencing the tables without moving CS to ES.
;	(AL) = opcode byte to check.
;	(EBX) = val_pdtr
;	(EDX) = Number of remaining bytes to scan.
;	(ESI) = Next byte to scan

val1:	mov	edi,offset FLAT:prefixtbl	; (ES:EDI) -> prefixtbl
	mov	ecx,NUMPREFIX		; (ECX) = length of prefix table.
	repne	scasb			; Is this is a prefix?
	jne	short val2		; no - check the opcode

;	Skip this prefix byte.
;	(EDX) = number of remaining bytes to scan.  (error if zero)

	dec	edx			; Try to move to next byte.
	jc	short val6		; out of bytes - invalid tracepoint

	lodsb				; (AL) = next opcode, inc (ESI)
	jmp	short val1		; scan for more prefixes.

;	All prefix bytes have been ignored at this point.
;	If in bad instruction table, return an error.
;	(AL) = opcode byte to check.
;	(EBX) = val_pdtr
;	(EDX) = Number of remaining bytes to scan.
;	(ESI) = Next byte to scan
;	(EDI) = offset FLAT:badinstrtbl (implicit)

val2:	mov	ecx,NUMBADINSTR		; (ECX) = length of badinstrtbl.
	repne	scasb			; Is this is a bad instruction?
	je	short val6		; yes - return an error.

;	If not a special instruction, return OK.
;	(EDI) = offset FLAT:spclinstrtbl (implicit)

	mov	ecx,NUMSPCLINSTR	; (ECX) = length of spclinstrtbl.
	repne	scasb			; Is this is a special instruction?
	jne	short val8		; no - return OK.

;	This is a 'special' instruction.  We check the Mod R/M byte to
;	differentiate between (mul, imul, div, idiv) and (not, neg, test)
;
;	Get ModRM byte

	sub	edx,1			; Try to move to next byte.
	jz	short val6		; out of bytes - invalid tracepoint

	lodsb				; (AL) = next opcode, inc (ESI)

	test	al,SPCLBADMASK		; check SPCLBADMASK
	jz	short val8		; SPCLBADMASK is off - NO_ERROR
					; Fall thru to invalid tracepoint.

;	The tracepoint is invalid :
;	* Opcode mismatch
;	* (EDX) decremented to zero at some point, (ran out of bytes)
;	* Opcode found in badinstrtbl
;	* SPCLBADMASK set on the Mod R/M byte of a 'special' instruction.

val6:	mov	eax,ERROR_INVALID_DATA	; Invalid tracepoint

;	Error case: clear DTR_VALID and return Error Code
;	(EAX) = Error Code
;	(EBX) = pdtr

	and	[ebx].dtr_Reserved,NOT DTR_VALID; Mark tracepoint invalid
	jmp	short val9

;	Success case: set DTR_VALID and return NO_ERROR
;	(EBX) = pdtr

val8:	or	[ebx].dtr_Reserved,DTR_VALID	; Mark tracepoint valid
	xor	eax,eax			; NO_ERROR

val9:	RestoreReg <esi, edi, ebx>
	LeaveProc
	RETC

valie1: Panic	<'dynDTRValidate : bad alias'>

EndProc _dynDTRValidate

DYNSWAP_TEXT	ENDS


FARCODE HIGH3
HIGH3CODE	SEGMENT

;***EP	w_DynamicTrace - 16-bit DosDynamicTrace entry point.
;
;	w_DynamicTrace is a 16-bit interface procedure to _DYNDynamicTrace.
;	It is called via SCI for compatability with the old DosDynamicTrace
;	interface.
;
;	w_DynamicTrace can handle both the level 1 and level 2 tracepoint
;	object formats.
;
;	ENTRY	(BX)	= Subfunction
;		(DX:SI) = 16:16 request packet pointer
;		(CX:DI) = 16:16 results packet pointer (must be null)
;
;	EXIT	(EAX) = standard error code
;		Carry set if (EAX) is not NO_ERROR.
;
;	USES	EAX, EBX, ECX, EDX, EDI, ESI, DS, ES, Flags

Procedure w_DynamicTrace,faronly
	ASSUME	DS:NOTHING, ES:NOTHING, FS:NOTHING, GS:NOTHING, SS:NOTHING
	FlatContext <ds,es>,ax
	xchg	edi,edx			; Save Request packet selector in (EDI)
	CCALL32 _SELVirtToLin <edx,ecx> ; Thunk Result packet from (CX:DX)
	push	eax			; Save presUser
	CCALL32 _SELVirtToLin <esi,edi> ; Thunk Request packet from (DI:SI)
	pop	edx			; (EDX) = presUser (Flat)
					; (EAX) = preqUser (Flat)
	movzx	ebx,bx			; (EBX) = Subfunction
	CCALL32 _DYNDynamicTrace <ebx,eax,edx>	; (Subfunc, preqUser, presUser)
	cmp	eax,1			; set carry based on (EAX), for SCI
	cmc
	RETC
EndProc w_DynamicTrace

HIGH3CODE ENDS

endif	; RAS

END
