	PAGE	80,132 ;
	TITLE	DEBCOM3.ASM - PART3 DEBUGGER COMMANDS
;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */
; ROUTINES TO PERFORM DEBUGGER COMMANDS

	IF1
	    %OUT COMPONENT=DEBUG, MODULE=DEBCOM3
	ENDIF
.XLIST
.XCREF
	include	mi.inc			; cas -- missing equates
	include	version.inc		; cas -- missing equates
	include	syscall.inc		; cas -- missing equates
	INCLUDE DOSSYM.INC
        INCLUDE debug.inc
	INCLUDE DPL.ASM
.CREF
.LIST
CODE	SEGMENT PUBLIC BYTE
CODE	ENDS

CONST	SEGMENT PUBLIC BYTE
	EXTRN	USER_PROC_PDB:WORD,RSTACK:WORD,STACK:BYTE
	EXTRN	DSSAVE:WORD,CSSAVE:WORD,IPSAVE:WORD,axSAVE:WORD,dxSAVE:WORD
	EXTRN	SSSAVE:WORD,SPSAVE:WORD,FLSAVE:WORD
	EXTRN	NEXTCS:WORD,NEXTIP:WORD, RSETFLAG:BYTE
CONST	ENDS

CSTACK	SEGMENT STACK
CSTACK	ENDS

DATA	SEGMENT PUBLIC BYTE
	EXTRN	BRKCNT:WORD,TCOUNT:WORD,SWITCHAR:BYTE,BPTAB:BYTE
	EXTRN	BP_ERROR:BYTE,COMP_ARG1:WORD,COMP_ARG2:WORD,COMP_ARG3:WORD
	EXTRN	COMP_ARG4:WORD,COMP_ARG5:WORD,COMP_ARG6:WORD,COMP_PTR:BYTE
	EXTRN	ARG_BUF:BYTE,ARG_BUF_PTR:BYTE
	EXTRN	FZTRACE:BYTE, SYNERR_PTR:BYTE
	EXTRN	BEGSEG:WORD
	IF	IBMVER
	    EXTRN   cpu_not_8088:byte
	    EXTRN   OLD_MASK:BYTE
	ENDIF
	EXTRN	SAVESTATE:BYTE
DATA	ENDS

DG	GROUP	CODE,CONST,CSTACK,DATA

CODE	SEGMENT PUBLIC BYTE
ASSUME	CS:DG,DS:DG,ES:DG,SS:DG
	PUBLIC	COMPARE,INPUT,OUTPUT,GO
	PUBLIC	TRACE,ZTRACE,SKIP_FILE
	EXTRN	GETHEX:NEAR,GETEOL:NEAR,CRLF:NEAR,ERR:NEAR, PERR:NEAR
	EXTRN	HEX:NEAR,DIGIT:NEAR,SCANP:NEAR,DISPREG:NEAR
	EXTRN	COMMAND:NEAR,DABORT:NEAR,DELIM1:NEAR,DELIM2:NEAR
	EXTRN	NMIINT:NEAR,NMIINTEND:NEAR,PRINTF_CRLF:NEAR
	EXTRN	ADDRESS:NEAR,HEXIN:NEAR,DSRANGE:NEAR
; just like trace except skips OVER next INT or CALL.
DEBCOM3:
ZTRACE:
	MOV	FZTRACE,-1
	CALL	SETADD
	CALL	SCANP
	CALL	HEXIN
	MOV	DX,1
	JC	ZSTOCNT
	MOV	CX,4
	CALL	GETHEX
	CALL	CHECKNONE
ZSTOCNT:
	MOV	[TCOUNT],DX
	CALL	GETEOL
	MOV	DX,NEXTCS
	MOV	CSSAVE,DX
	MOV	DX,NEXTIP
	MOV	IPSAVE,DX
ZSTEP:
	MOV	ES,[CSSAVE]		; point to instruction to execute
	MOV	DI,[IPSAVE]		; include offset in segment
	XOR	DX,DX			; where to place breakpoint
get_opcode:
	MOV	AL,ES:[DI]		; get the opcode
	cmp	al,0f0h 		; lock
	je	is_override
	cmp	al,26h			; es:
	je	is_override
	cmp	al,2eh			; cs:
	je	is_override
	cmp	al,36h			; ss:
	je	is_override
	cmp	al,3eh			; ds:
	jne	not_override
Is_override:
;	inc	dx			; this seemed to put us in an endless
	inc	di			; loop, try this.
	jmp	get_opcode
Not_override:
	CMP	AL,11101000B		; direct intra call
	JZ	ZTRACE3 		; yes, 3 bytes
	CMP	AL,10011010B		; direct inter call
	JZ	ZTRACE5 		; yes, 5 bytes
	CMP	AL,11111111B		; indirect?
	JZ	ZTRACEMODRM		; yes, go figure length
	CMP	AL,11001100B		; short interrupt?
	JZ	ZTRACE1 		; yes, 1 byte
	CMP	AL,11001101B		; long interrupt?
	JZ	ZTRACE2 		; yes, 2 bytes
	CMP	AL,11100010B		; loop
	JZ	ZTRACE2 		; 2 byter
	CMP	AL,11100001B		; loopz/loope
	JZ	ZTRACE2 		; 2 byter
	CMP	AL,11100000B		; loopnz/loopne
	JZ	ZTRACE2 		; 2 byter
	AND	AL,11111110B		; check for rep
	CMP	AL,11110010B		; perhaps?
	JZ	FOO1
	JMP	STEP			; can't do anything special, step
FOO1:
	MOV	AL,ES:[DI+1]		; next instruction
	AND	AL,11111110B		; ignore w bit
	CMP	AL,10100100B		; MOVS
	JZ	ZTRACE2 		; two byte
	CMP	AL,10100110B		; CMPS
	JZ	ZTRACE2 		; two byte
	CMP	AL,10101110B		; SCAS
	JZ	ZTRACE2 		; two byte
	CMP	AL,10101100B		; LODS
	JZ	ZTRACE2 		; two byte
	CMP	AL,10101010B		; STOS
	JZ	ZTRACE2 		; two byte
	JMP	short STEP		; bogus, do single step

ZTRACEMODRM:
	MOV	AL,ES:[DI+1]		; get next byte
	AND	AL,11111000B		; get mod and type
	CMP	AL,01010000B		; indirect intra 8 bit offset?
	JZ	ZTRACE3 		; yes, three byte whammy
	CMP	AL,01011000B		; indirect inter 8 bit offset
	JZ	ZTRACE3 		; yes, three byte guy
	CMP	AL,10010000B		; indirect intra 16 bit offset?
	JZ	ZTRACE4 		; four byte offset
	CMP	AL,10011000B		; indirect inter 16 bit offset?
	JZ	ZTRACE4 		; four bytes
	CMP	AL,11010000B		; indirect through reg?
	JZ	ZTRACE2 		; two byte instruction
	JMP	short STEP		; can't figger out what this is!
ZTRACE5:
	INC	DX
ZTRACE4:
	INC	DX
ZTRACE3:
	INC	DX
ZTRACE2:
	INC	DX
ZTRACE1:
	INC	DX
	ADD	DI,DX			; offset to breakpoint instruction
	MOV	WORD PTR [BPTAB],DI	; save offset
	MOV	WORD PTR [BPTAB+2],ES	; save segment
	MOV	AL,ES:[DI]		; get next opcode byte
	MOV	BYTE PTR [BPTAB+4],AL	; save it
	MOV	BYTE PTR ES:[DI],0CCH	; break point it
	MOV	[BRKCNT],1		; only this breakpoint
	JMP	DEXIT			; start the operation!

; Trace 1 instruction or the number of instruction specified
; by the parameter using 8086 trace mode. Registers are all
; set according to values in save area
TRACE:
	MOV	FZTRACE,0
	CALL	SETADD
	CALL	SCANP
	CALL	HEXIN
	MOV	DX,1
	JC	STOCNT
	MOV	CX,4
	CALL	GETHEX
	CALL	CHECKNONE
STOCNT:
	MOV	[TCOUNT],DX
	CALL	GETEOL
	MOV	DX,NEXTCS
	MOV	CSSAVE,DX
	MOV	DX,NEXTIP
	MOV	IPSAVE,DX
STEP:
	MOV	[BRKCNT],0
; The 286 has a problem with trace mode and software interrupt instructions;
; it treats them as atomic operations.	We simulate the operation in software.
	MOV	ES,[CSSAVE]		; Get next instruction pointer
	MOV	DI,[IPSAVE]
	MOV	AL,ES:[DI]		; get next opcode
	cmp	al,0e4h 		; check for 'IN' opcode
	jne	not_inal_op
	cmp	es:byte ptr[di+1],21h
	jne	not_mask_op
	add	[ipsave],2
	JMP	short SETalmask

not_inal_op:
	cmp	al,0ech 		; in al,DX ?
	jne	not_mask_op
	cmp	dxsave,21h
	jne	not_mask_op
	add	[ipsave],1
SETalmask:
	mov	ax,[axsave]
	in	al,21h
	mov	[axsave],ax
	JMP	SETENVIRON

not_mask_op:
	CMP	AL,0CDH 		; trace over an interrupt?
	JZ	DOINT			; no, check for other special cases
	CMP	AL,0CEH 		; how about int overflow
	JNZ	CHECKCC
	TEST	FLSAVE,F_OVERFLOW	 ; see it overflow is present
	JZ	CHECKOP
	MOV	BX,4			; INTO = INT 4
	DEC	IPSAVE			; INTO is a singel byte
	JMP	SHORT DOVAL
CHECKCC:
	CMP	AL,0CCH
	JNZ	CHECKOP
	MOV	BX,3			; INT 3 = CC
	DEC	IPSAVE
	JMP	SHORT DOVAL
DOINT:
; We have a software interrupt.  Get destination vector
	MOV	BL,BYTE PTR ES:[DI+1]	; get vector number
	XOR	BH,BH			; clear out upper
DOVAL:
	SHL	BX,1			; word index
	SHL	BX,1			; dword index
	XOR	DI,DI			; interrupt table
	MOV	ES,DI
	MOV	AX,ES:[BX]		; point to vector
	MOV	BX,ES:[BX+2]		; point to vector
; AX:BX is the vector.	Swap it with currect CS:IP
	XCHG	AX,IPSAVE		; new CS:IP
	XCHG	BX,CSSAVE
; AX:BX is old CS:IP.  We 'PUSH' flags, oldCS and oldIP, reset flags (ifl) and
; set CS:IP to point to interrupt instruction.
	MOV	ES,SSSAVE		; point to user stack
	MOV	DI,SPSAVE
; Take old flags and PUSH the flags.
	MOV	CX,FLSAVE		 ; get flags
	SUB	DI,2			; PUSHF
	MOV	ES:[DI],CX		; rest of push
; Push the old CS
	SUB	DI,2			; PUSH CS
	MOV	ES:[DI],BX		; rest of push
; Push the old IP
	SUB	DI,2			; PUSH IP
	ADD	AX,2			; increment IP
	MOV	ES:[DI],AX		; rest of push
; Update stack
	MOV	SPSAVE,DI		; store
; Take flags and turn interrupts off and trace mode off
	AND	CX,NOT F_INTERRUPT	; CLI
	AND	CX,NOT F_TRACE		; no trace
	MOV	FLSAVE,CX		 ; rest of CLI
; Set up correct process and go to normal reentry code.
	IF	NOT SYSVER
	    MOV     BX,[USER_PROC_PDB]
	    MOV     AH,SET_CURRENT_PDB
	    INT     21H
	ENDIF
	JMP	SETENVIRON
; We need to special case the following instructions that may push a TRACE bit
; on the stack:  PUSHF (9C)

; Save the opcode in A Special place
CHECKOP:
	MOV	RSETFLAG,AL		; no bits to turn off
SETTRACE:
	OR	FLSAVE,F_TRACE		 ; Turn on trace bit
	IF	IBMVER
;
;	the following stuff can be inhibited on 286/386/etc CPUs.  The
;	  designers of the later chips added logic to enhance trace
;	  mode operation.  The reason why we just don't run it all of
;	  the time is that it can cause a system crash if the instruction
;	  being traced causes an overflow condition because nobody will
;	  turn interrupts back on.

	test	cpu_not_8088,1		; see if 8088/8086
	jnz	no_fix_8088

	    CLI
	    IN	    AL,MASK_PORT	; Get current mask
	    JMP     SHORT FOO
FOO:
	    MOV     [OLD_MASK],AL	; Save it
	    MOV     AL,INT_MASK 	; New mask
	    OUT     MASK_PORT,AL	; Set it
	    STI

no_fix_8088:

	ENDIF
DEXIT:
	IF	NOT SYSVER
	    MOV     BX,[USER_PROC_PDB]
	    MOV     AH,SET_CURRENT_PDB
	    INT     21H
	ENDIF
; Unfortunately, any system call we issue will muck with the current extended
; errors.  Here we must restore the extended error state so that if the user
; program gets it, we do not interfere.
	MOV	AX,(SERVERCALL SHL 8) + 10
	MOV	DX,OFFSET DG:SAVESTATE
	INT	21H
	PUSH	DS
	XOR	AX,AX
	MOV	DS,AX
	MOV	WORD PTR DS:[12],OFFSET DG:BREAKFIX ; Set vector 3--breakpoint instruction
	MOV	WORD PTR DS:[14],CS
	MOV	WORD PTR DS:[4],OFFSET DG:REENTER ; Set vector 1--Single step
	MOV	WORD PTR DS:[6],CS
	CLI
	IF	SETCNTC
	    MOV     WORD PTR DS:[8CH],OFFSET DG:CONTC ; Set vector 23H (CTRL-C)
	    MOV     WORD PTR DS:[8EH],CS
	ENDIF
	POP	DS
	MOV	SP,OFFSET DG:STACK
	POP	AX
	POP	BX
	POP	CX
	POP	DX
	POP	BP
	POP	BP
	POP	SI
	POP	DI
	POP	ES
	POP	ES
	POP	SS
	MOV	SP,[SPSAVE]
	PUSH	[FLSAVE]
	PUSH	[CSSAVE]
	PUSH	[IPSAVE]
	MOV	DS,[DSSAVE]
	IRET
STEP1:
	CALL	CRLF
	CALL	DISPREG
	TEST	FZTRACE,-1
	JNZ	STEPZ
	JMP	STEP
STEPZ:	JMP	ZSTEP

; Re-entry point from CTRL-C. Top of stack has address in 86-DOS for
; continuing, so we must pop that off.
CONTC:
	ADD	SP,6
	JMP	SHORT REENTERREAL

; Re-entry point from breakpoint. Need to decrement instruction
; pointer so it points to location where breakpoint actually
; occured.
BREAKFIX:
	PUSH	BP
	MOV	BP,SP
	DEC	WORD PTR [BP].OLDIP
	POP	BP
	JMP	short REENTERREAL

; Re-entry point from trace mode or interrupt during execution.  All registers
; are saved so they can be displayed or modified.
INTERRUPT_FRAME STRUC
OLDBP	DW	?
OLDIP	DW	?
OLDCS	DW	?
OLDF	DW	?
OLDERIP DW	?
OLDERCS DW	?
OLDERF	DW	?
INTERRUPT_FRAME ENDS

ASSUME	CS:DG,DS:NOTHING,ES:NOTHING,SS:NOTHING
; ReEnter is the main entry point for breakpoint interrupts and for trace mode
; interrupts.  We treat both of these cases identically:  save state, display
; registers and go for another command.  If we get NMI's, we skip them or if
; it turns out that we are debugging ourselves, we skip them.

; Due to bogosities in the 808x chip, Consider tracing over an interrupt and
; then setting a breakpoint to where the interrupt returns.  You get the INT 3
; and then trace mode gets invoked!  This is why we ignore interrupts within
; ourselves.
REENTER:
	PUSH	BP
	MOV	BP,SP			; get a frame to address from
	PUSH	AX
;	MOV	AX,CS
;	CMP	AX,[BP].OLDCS		; Did we interrupt ourselves?
;	JNZ	GOREENTER		; no, go reenter
	IF	IBMJAPAN
	    MOV     AX,[BP].OLDIP
	    CMP     AX,OFFSET DG:NMIINT ; interrupt below NMI interrupt?
	    JB	    GOREENTER		; yes, go reenter
	    CMP     [BP].OLDIP,OFFSET DG:NMIINTEND
	    JAE     GOREENTER		; interrupt above NMI interrupt?
	    POP     AX			; restore state
	    POP     BP
	    SUB     SP,6		; switch TRACE and NMI stack frames
	    PUSH    BP
	    MOV     BP,SP		; set up frame
	    PUSH    AX			; get temp variable
	    MOV     AX,[BP].OLDERIP	; get NMI Vector
	    MOV     [BP].OLDIP,AX	; stuff in new NMI vector
	    MOV     AX,[BP].OLDERCS	; get NMI Vector
	    MOV     [BP].OLDCS,AX	; stuff in new NMI vector
	    MOV     AX,[BP].OLDERF	; get NMI Vector
	    AND     AH,0FEH		; turn off Trace if present
	    MOV     [BP].OLDF,AX	; stuff in new NMI vector
	    MOV     [BP].OLDERF,AX
	    MOV     [BP].OLDERIP,OFFSET DG:REENTER ; offset of routine
	    MOV     [BP].OLDERCS,CS	; and CS
	    POP     AX
	    POP     BP
	    IRET			; go try again
	ENDIF
GOREENTER:
	IF	IBMVER
	test	cpu_not_8088,1
	jnz	no_restore_pic_mask
	    MOV     AL,CS:[OLD_MASK]	; Recover Old mask
	    OUT     MASK_PORT,AL	; Restore it
no_restore_pic_mask:
	ENDIF

	MOV	AL,CS:[RSETFLAG]
; Determine, based on the previous instruction, what we are supposed to do
; to flags on the users stack.
	CMP	AL,09CH 		; PUSHF
	JNZ	NOFIX
; OlderIP = flags.  Turn off trace bit
	AND	[BP].OLDERIP,NOT F_TRACE
NOFIX:
	POP	AX
	POP	BP
REENTERREAL:
	MOV	CS:[SPSAVE+SEGDIF],SP
	MOV	CS:[SSSAVE+SEGDIF],SS
	MOV	CS:[FLSAVE],CS
	MOV	SS,CS:[FLSAVE]
	MOV	SP,OFFSET DG:RSTACK
	ASSUME	SS:DG

	PUSH	ES
	PUSH	DS
	PUSH	DI
	PUSH	SI
	PUSH	BP
	DEC	SP
	DEC	SP
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	AX
	PUSH	SS
	POP	DS
	ASSUME	DS:DG

	MOV	SS,[SSSAVE]
	MOV	SP,[SPSAVE]
	ASSUME	SS:NOTHING

	POP	[IPSAVE]
	POP	[CSSAVE]
	POP	AX
	AND	AX,NOT F_TRACE		; TURN OFf trace mode bit
	MOV	[FLSAVE],AX
	MOV	[SPSAVE],SP
SETENVIRON:
	PUSH	DS
	POP	ES
	ASSUME	ES:DG

	PUSH	DS
	POP	SS
	ASSUME	SS:DG

	MOV	SP,OFFSET DG:STACK
	PUSH	DS
	XOR	AX,AX
	MOV	DS,AX
	ASSUME	DS:NOTHING

	IF	SETCNTC
	    MOV     WORD PTR DS:[8CH],OFFSET DG:DABORT ; Set Ctrl-C vector
	    MOV     WORD PTR DS:[8EH],CS
	ENDIF
	POP	DS
	ASSUME	DS:DG

	STI
	CLD
; Since we are about to issue system calls, let's grab the current user's
; extended error info.
	MOV	AH,GETEXTENDEDERROR
	INT	21H
	ASSUME	DS:NOTHING,ES:NOTHING

	MOV	SAVESTATE.DPL_AX,AX
	MOV	SAVESTATE.DPL_BX,BX
	MOV	SAVESTATE.DPL_CX,CX
	MOV	SAVESTATE.DPL_DX,DX
	MOV	SAVESTATE.DPL_SI,SI
	MOV	SAVESTATE.DPL_DI,DI
	MOV	SAVESTATE.DPL_DS,DS
	MOV	SAVESTATE.DPL_ES,ES
	MOV	AX,CS
	MOV	DS,AX
	MOV	ES,AX
	ASSUME	DS:DG,ES:DG

	IF	NOT SYSVER
	    MOV     AH,GET_CURRENT_PDB
	    INT     21H
	    MOV     [USER_PROC_PDB],BX
	    MOV     BX,BEGSEG
	    MOV     AH,SET_CURRENT_PDB
	    INT     21H
	ENDIF
	MOV	SI,OFFSET DG:BPTAB
	MOV	CX,[BRKCNT]
	JCXZ	SHOREG
	PUSH	ES
CLEARBP:
	LES	DI,DWORD PTR [SI]
	ADD	SI,4
	MOVSB
	LOOP	CLEARBP
	POP	ES
SHOREG:
	DEC	[TCOUNT]
	JZ	CHECKDISP
	JMP	STEP1
CHECKDISP:
	CALL	CRLF
	CALL	DISPREG
	JMP	COMMAND

; Input from the specified port and display result
INPUT:
	MOV	CX,4			; Port may have 4 digits
	CALL	GETHEX			; Get port number in DX
	CALL	GETEOL

	IN	AL,DX			; Variable port input

	PUSH	CS
	POP	ES
	MOV	DI,OFFSET DG:ARG_BUF
	CALL	HEX			; And display

	XOR	AL,AL
	STOSB
	MOV	DX,OFFSET DG:ARG_BUF_PTR
	JMP	PRINTF_CRLF

; Output a value to specified port.
OUTPUT:
	MOV	CX,4			; Port may have 4 digits
	CALL	GETHEX			; Get port number
	PUSH	DX			; Save while we get data
	MOV	CX,2			; Byte output only
	CALL	GETHEX			; Get data to output
	CALL	GETEOL
	XCHG	AX,DX			; Output data in AL
	POP	DX			; Port in DX

	OUT	DX,AL			; Variable port output

	RETURN

SETADD:
	MOV	DX,CSSAVE		; set up start addresses
	MOV	NEXTCS,DX
	MOV	DX,IPSAVE
	MOV	NEXTIP,DX
	MOV	BP,[CSSAVE]
	CALL	SCANP
	CMP	BYTE PTR [SI],"="
	RETNZ
	INC	SI
	CALL	ADDRESS
	MOV	NEXTCS,AX
	MOV	NEXTIP,DX
	RETURN

; Jump to program, setting up registers according to the
; save area. up to 10 breakpoint addresses may be specified.
GO:
	MOV	RSETFLAG,0
	CALL	SETADD
	XOR	BX,BX
	MOV	DI,OFFSET DG:BPTAB
GO1:
	CALL	SCANP
	JZ	DEXEC
	MOV	BP,[CSSAVE]
	PUSH	DI
	PUSH	BX			;AN000; DMS;SAVE BX - ADDRESS KILLS IT
	CALL	ADDRESS
	POP	BX			;AN000; DMS;RESTORE BX
	POP	DI
	MOV	[DI],DX 		; Save offset
	MOV	[DI+2],AX		; Save segment
	ADD	DI,5			; Leave a little room
	INC	BX
	CMP	BX,1+BPMAX
	JNZ	GO1
	MOV	DX,OFFSET DG:BP_ERROR	; BP ERROR
	JMP	ERR
DEXEC:
	MOV	[BRKCNT],BX
	MOV	CX,BX
	JCXZ	NOBP
	MOV	DI,OFFSET DG:BPTAB
	PUSH	DS
SETBP:
	LDS	SI,ES:DWORD PTR [DI]
	ADD	DI,4
	MOVSB
	MOV	BYTE PTR [SI-1],0CCH
	LOOP	SETBP
	POP	DS
NOBP:
	MOV	DX,NEXTCS
	MOV	CSSAVE,DX
	MOV	DX,NEXTIP
	MOV	IPSAVE,DX
	MOV	[TCOUNT],1
	JMP	DEXIT

SKIP_FILE:
	MOV	AH,CHAR_OPER
	XOR	AL,AL			; Get switch char subfunction
	INT	21H
	MOV	CS:[SWITCHAR],DL	; GET THE CURRENT SWITCH CHARACTER
FIND_DELIM:
	LODSB
	CALL	DELIM1
	JZ	GOTDELIM
	CALL	DELIM2
	JNZ	FIND_DELIM
GOTDELIM:
	DEC	SI
	RETURN

COMPARE:
	CALL	DSRANGE
	PUSH	CX
	PUSH	AX
	PUSH	DX
	CALL	ADDRESS 		; Same segment
	CALL	GETEOL
	POP	SI
	MOV	DI,DX
	MOV	ES,AX
	POP	DS
	POP	CX			; Length
	DEC	CX
	CALL	COMP			; Do one less than total
	INC	CX			; CX=1 (do last one)
COMP:
	REPE	CMPSB
	RETZ
; Compare error. Print address, value; value, address.
	DEC	SI
	MOV	CS:COMP_ARG1,DS
	MOV	CS:COMP_ARG2,SI
	XOR	AH,AH
	LODSB
	MOV	CS:COMP_ARG3,AX
	DEC	DI
	MOV	AL,ES:[DI]
	MOV	CS:COMP_ARG4,AX
	MOV	CS:COMP_ARG5,ES
	MOV	CS:COMP_ARG6,DI
	INC	DI
	PUSH	DS
	PUSH	CS
	POP	DS
	MOV	DX,OFFSET DG:COMP_PTR
	CALL	PRINTF_CRLF
	POP	DS
	XOR	AL,AL
	JMP	SHORT COMP

	PROCEDURE CHECKNONE,NEAR
	OR	DX,DX
	RETNZ
	MOV	DX,OFFSET DG:SYNERR_PTR ; ERROR MESSAGE
	JMP	PERR
	ENDPROC CHECKNONE

CODE	ENDS
	END	DEBCOM3
