$NOLIST

NAME  WindowSubs

$INCLUDE (WinCnsts~Inc~)

CGROUP GROUP CODE

; This module contains routines that do not affect the display visually
; but affect the current window information block instead.

PUBLIC InitWindowVariables
PUBLIC RetDosAndIntelPtrs

PUBLIC RctSetRectangle, RctCopyRectangle
PUBLIC RctInsetRectangle, RctOffsetRectangle
PUBLIC RctRectanglesIntersect, RctPtInRectangle
PUBLIC RctGetFramesRectangle

PUBLIC WinGetWindowExtent, WinGetDisplayExtent
PUBLIC WinWindowToDisplayPt, WinDisplayToWindowPt
PUBLIC WinSetPattern, WinSetPtnXferMode
PUBLIC WinGetPattern, WinGetPtnXferMode
PUBLIC WinSetClip, WinGetClip
PUBLIC WinClipRectangle

; There are also some variables and routines that are used by
; other window modules that are not exported to the outside.

PUBLIC activeWindow, drawWindow, displayWindow, firstWindow
PUBLIC currOrientation, currFontInfoAddr, currPtnXferMode, currPattern
PUBLIC crsrFontInfoAddr, lastCursorWindow, workCursorWindow
PUBLIC cursorOffCount, cursorOriginX, cursorOriginY, cursorLocX, cursorLocY
PUBLIC cursorLeft, cursorTop, cursorRight, cursorBottom

PUBLIC WinResetClip, ClipLine, Encode


CODE SEGMENT  PUBLIC 'CODE'
	ASSUME CS:CGROUP

; Meanings of code bits used by Encode

leftBit	 EQU 1
rightBit	 EQU 2
topBit	 EQU 4
bottomBit EQU 8

; Global variables for window routines

activeWindow     DW 0	; window where cursor blinks, etc.
drawWindow       DW ?	; window where all output goes to
displayWindow    DW ?	; window used by the system
firstWindow      DW ?	; first window in the window list

currOrientation  DB ?
currPtnXferMode  DB drawVerb	; Init pattern transfer mode to be draw
currPattern      DB 8 DUP (0FFH)	; Init fill pattern to be solid

currFontInfoAddr DD ?

crsrFontInfoAddr DD ?
lastCursorWindow DW ?
workCursorWindow DW ?
cursorOffCount   DW 1	; Cursor initially is not drawn
cursorLocX       DW 0	; Cursor first appears at top of display
cursorLocY       DW 0	; Cursor first appears at left of display
cursorOriginX    DW ?
cursorOriginY    DW ?
cursorLeft       DW ?
cursorTop        DW ?
cursorRight      DW ?
cursorBottom     DW ?
$EJECT

; InitWindowVariables: PROCEDURE;

; This initialize window variables.  It has to be called, because once a
; program is TSR'd, more than one program can call it and things have to be
; re-initialized for each program.  This is called by MniInitialize.

InitWindowVariables PROC NEAR
	PUSH	DS
	MOV	AX, CS
	MOV	DS, AX	; DS points to CGROUP
	MOV	ES, AX	; ES points to CGROUP
	LEA	DI, CS:currPattern
	MOV	CX, 4
	MOV	AX, 0FFFFH
	CLD
	REP	STOSW	; Set pattern to all FF's
	MOV	DS:currPtnXferMode, drawVerb

	XOR	AX, AX	; Zero
	MOV	DS:activeWindow, AX	; activeWindow = no window

	MOV	DS:cursorLocX, AX	; cursor x loc = 0
	MOV	DS:cursorLocY, AX	; cursor y loc = 0
	MOV	DS:cursorOffCount, 1	; cursor off count = off
	POP	DS
	RET
InitWindowVariables ENDP


; This routine will copy ES:BX into DX:AX.  This is done so that returned
; pointers are acceptable to both Intel compilers & MsDos based compilers.

RetDosAndIntelPtrs PROC NEAR
	MOV	DX, ES
	MOV	AX, BX
	RET
RetDosAndIntelPtrs ENDP
$EJECT

; PROCEDURE RctSetRectangle (VAR r: Rectangle;
;                            topLftX, topLftY, extentX, extentY: Integer);

pRect   EQU DWORD PTR [BP+12]
topLftX EQU  WORD PTR [BP+10]
topLftY EQU  WORD PTR [BP+8]
extentX EQU  WORD PTR [BP+6]
extentY EQU  WORD PTR [BP+4]

RctSetRectangle PROC NEAR
	PUSH	BP
	MOV	BP, SP

	LES	DI, pRect
	CLD
	MOV	AX, topLftX
	STOSW
	MOV	AX, topLftY
	STOSW
	MOV	AX, extentX
	STOSW
	MOV	AX, extentY
	STOSW

	POP	BP
	RET	12
RctSetRectangle ENDP

PURGE pRect, topLftX, topLftY, extentX, extentY



; PROCEDURE RctCopyRectangle (VAR srcRect, destRect: Rectangle);

pSrcRect EQU DWORD PTR [BP+10]
pDstRect EQU DWORD PTR [BP+6]

RctCopyRectangle PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pSrcRect
	LES	DI, pDstRect
	CLD
	MOVSW
	MOVSW
	MOVSW
	MOVSW

	POP	BP
	POP	DS
	RET	8
RctCopyRectangle ENDP

PURGE pSrcRect, pDstRect
$EJECT

; PROCEDURE RctInsetRectangle (VAR r: Rectangle; delta: Integer);

pRect   EQU DWORD PTR [BP+8]
delta   EQU  WORD PTR [BP+6]

RctInsetRectangle PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pRect
	MOV	AX, delta
	ADD	DS:[SI].rectTopLeftX, AX
	ADD	DS:[SI].rectTopLeftY, AX

	ADD	AX, AX
	SUB	DS:[SI].rectExtentX, AX
	SUB	DS:[SI].rectExtentY, AX

	POP	BP
	POP	DS
	RET	6
RctInsetRectangle ENDP

PURGE pRect, delta



; PROCEDURE RctOffsetRectangle (VAR r: Rectangle; deltaX, deltaY: Integer);

pRect   EQU DWORD PTR [BP+10]
deltaX  EQU  WORD PTR [BP+8]
deltaY  EQU  WORD PTR [BP+6]

RctOffsetRectangle PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pRect
	MOV	AX, deltaX
	MOV	BX, deltaY
	ADD	DS:[SI].rectTopLeftX, AX
	ADD	DS:[SI].rectTopLeftY, BX

	POP	BP
	POP	DS
	RET	8
RctOffsetRectangle ENDP

PURGE pRect, deltaX, deltaY
$EJECT

; PROCEDURE RctRectanglesIntersect (VAR src1, src2, result: Rectangle): Boolean;

src1    EQU DWORD PTR [BP+14]
src2    EQU DWORD PTR [BP+10]
result  EQU DWORD PTR [BP+6]

RctRectanglesIntersect PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, src1
	LES	DI, src2

	MOV	AX, DS:[SI].rectTopLeftX
	ADD	AX, DS:[SI].rectExtentX
	DEC	AX	; AX has src1.right
	MOV	BX, DS:[SI].rectTopLeftY
	ADD	BX, DS:[SI].rectExtentY
	DEC	BX	; BX has src1.bottom

	MOV	CX, ES:[DI].rectTopLeftX
	ADD	CX, ES:[DI].rectExtentX
	DEC	CX	; CX has src2.right
	MOV	DX, ES:[DI].rectTopLeftY
	ADD	DX, ES:[DI].rectExtentY
	DEC	DX	; DX has src2.bottom

	CMP	DS:[SI].rectTopLeftY, DX	; If top of src1 is below bottom of src2
	JG	NoIntersection	; then rectangles do not intersect

	CMP	DS:[SI].rectTopLeftX, CX	; If left of src1 is to right of src2's right
	JG	NoIntersection	; then rectangles do not intersect

	CMP	BX, ES:[DI].rectTopLeftY	; If bottom of src1 is above top of src2
	JL	NoIntersection	; then rectangles do not intersect

	CMP	AX, ES:[DI].rectTopLeftX	; If right of src1 is left of src2's right
	JGE	RectsIntersect	; then rectangles do not intersect

NoIntersection:
	XOR	AX, AX	; RETURN (FALSE) and set result to zero
	LES	DI, result
	CLD
	STOSW
	STOSW
	STOSW
	STOSW
	JMP	SHORT RctRectsIntersectRet

RectsIntersect:
	CMP	DX, BX	; If src1.bottom < src2.bottom then
	JLE	DontSwapBottoms

	XCHG	DX, BX

DontSwapBottoms:
	CMP	CX, AX	; If src1.right < src2.right then
	JLE	DontSwapRights

	XCHG	CX, AX

DontSwapRights:
	MOV	BX, ES:[DI].rectTopLeftY
	CMP	BX, DS:[SI].rectTopLeftY
	JGE	DontSwapTops

	MOV	BX, DS:[SI].rectTopLeftY

DontSwapTops:
	MOV	AX, ES:[DI].rectTopLeftX
	CMP	AX, DS:[SI].rectTopLeftX
	JGE	DontSwapLefts

	MOV	AX, DS:[SI].rectTopLeftY

DontSwapLefts:
	SUB	CX, AX
	INC	CX	; Change CX back to extent x
	SUB	DX, BX
	INC	DX	; Change DX back to extent y
	LES	DI, result
	CLD
	STOSW
	XCHG	AX, BX
	STOSW
	XCHG	AX, CX
	STOSW
	XCHG	AX, DX
	STOSW
	MOV	AL, 1	; RETURN (TRUE)

RctRectsIntersectRet:
	POP	BP
	POP	DS
	RET	12
RctRectanglesIntersect ENDP

PURGE src1, src2, result
$EJECT

; FUNCTION RctPtInRectangle (x,y: Integer; VAR r: Rectangle): Boolean;

xValue  EQU  WORD PTR [BP+12]
yValue  EQU  WORD PTR [BP+10]
pRect   EQU DWORD PTR [BP+6]

RctPtInRectangle PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	XOR	DI, DI	; Return value is initially FALSE

	LDS	SI, pRect
	MOV	CX, DS:[SI].rectTopLeftX
	MOV	DX, DS:[SI].rectTopLeftY
	MOV	AX, xValue
	MOV	BX, yValue

	CMP	AX, CX
	JL	RctPtInRectangleRet

	CMP	BX, DX
	JL	RctPtInRectangleRet

	ADD	CX, DS:[SI].rectExtentX	; RightX (+1)
	ADD	DX, DS:[SI].rectExtentY	; BottomY (+1)

	CMP	AX, CX
	JGE	RctPtInRectangleRet

	CMP	BX, DX
	JGE	RctPtInRectangleRet

	INC	DI

RctPtInRectangleRet:
	XCHG	AX, DI

	POP	BP
	POP	DS
	RET	8
RctPtInRectangle ENDP

PURGE xValue, yValue, pRect
$EJECT

; RctGetFramesRectangle: PROCEDURE
;  (frameType: WORD; pRect: PTR, pObscRect: PTR);

; Be careful here: Some callers may pass in pRect = pObscRect
; (As a matter of fact I do that from WinGetWindowFrameRect)

frameType EQU  WORD PTR [BP+14]
pRect     EQU DWORD PTR [BP+10]
pObscRect EQU DWORD PTR [BP+6]

RctGetFramesRectangle PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pRect	; Source rectangle
	LES	DI, pObscRect	; Dest rectangle
	CLD
	MOVSW
	MOVSW
	MOVSW
	MOVSW	; Dest rect = source rect

	MOV	AX, frameType
	MOV	CX, AX	; Bits 0-1 have inner frame width
	AND	CX, 3	; Get width of inner frame
	SHR	AX, 1
	SHR	AX, 1	; Bits 2-3 have interframe spacing width
	MOV	BX, AX
	AND	BX, 3	; Get width of space between frames
	ADD	CX, BX	; Add space width to outset total
	SHR	AX, 1
	SHR	AX, 1	; Bits 4-5 have outer frame width
	MOV	BX, AX
	AND	BX, 3	; Get width of space between frames
	ADD	CX, BX	; Add space width to outset total
	JZ	RctAdjustForShadows	; No need to adjust if no frame at all

	PUSH	AX	; Save frame data
	NEG	CX	; Move rectangle out (vs in)
	LDS	SI, pObscRect	; Dest rectangle
	PUSH	DS
	PUSH	SI	; @rectangle
	PUSH	CX	; inset amount
	CALL	RctInsetRectangle	; Frame around the frame for bold / picture
	POP	AX	; Restore frame data

RctAdjustForShadows:
	SHR	AX, 1
	SHR	AX, 1
	MOV	CX, AX
	AND	CX, 3	; Width of 3-D frame
	JZ	RctGetFramesRectRet	; Return if no 3-D frame

	LDS	SI, pObscRect	; Dest rectangle
	TEST	AL, 1000b
	JNZ	SouthShadow

	SUB	DS:[SI].rectTopLeftY, CX	; Move top side up by CX pixels

SouthShadow:
	ADD	DS:[SI].rectExtentY, CX	; Y extent gets larger in either case

	TEST	AL, 0100b
	JZ	EastShadow

	SUB	DS:[SI].rectTopLeftX, CX	; Move left side to left by CX pixels

EastShadow:
	ADD	DS:[SI].rectExtentX, CX	; X extent gets larger in either case

RctGetFramesRectRet:
	POP	BP
	POP	DS
	RET	10
RctGetFramesRectangle ENDP

PURGE frameType, pRect, pObscRect
$EJECT

;********************************************************
;*			                      *
;* WinGetWindowExtent: PROCEDURE (pExtentX, pExtentY);  *
;*			                      *
;********************************************************

pExtentX EQU DWORD PTR [BP+10]
pExtentY EQU DWORD PTR [BP+6]

WinGetWindowExtent PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, CS:drawWindow

	LES	DI, pExtentX	; ES:DI ^ extentX
	MOV	AX, DS:wiBoundsExtentX
	STOSW	; extent.x := windowExtent.x

	LES	DI, pExtentY	; ES:DI ^ extentY
	MOV	AX, DS:wiBoundsExtentY
	STOSW	; extent.y := windowExtent.y

	POP	BP
	POP	DS
	RET	4
WinGetWindowExtent ENDP

PURGE pExtentX, pExtentY
$EJECT

;*********************************************************
;*		             	                       *
;* WinGetDisplayExtent: PROCEDURE (pExtentX, pExtentY);  *
;*		         	                       *
;*********************************************************

pExtentX EQU DWORD PTR [BP+10]
pExtentY EQU DWORD PTR [BP+6]

WinGetDisplayExtent PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, CS:displayWindow

	LES	DI, pExtentX	; ES:DI ^ extentX
	MOV	AX, DS:wiBoundsExtentX
	STOSW	; extent.x := windowExtent.x

	LES	DI, pExtentY	; ES:DI ^ extentY
	MOV	AX, DS:wiBoundsExtentY
	STOSW	; extent.y := windowExtent.y

	POP	BP
	POP	DS
	RET	4
WinGetDisplayExtent ENDP

PURGE pExtentX, pExtentY
$EJECT

; 	 WinWindowToDisplayPt: PROCEDURE (pX, pY);
;
;    This converts window coordinates (relative) to display coordinates

pX     EQU DWORD PTR [BP+10]
pY	   EQU DWORD PTR [BP+6]

WinWindowToDisplayPt PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, CS:drawWindow
	MOV	AX, DS:wiBoundsTopLeftX
	MOV	BX, DS:wiBoundsTopLeftY

	LDS	SI, pX
	ADD	DS:[SI], AX
	LDS	SI, pY
	ADD	DS:[SI], BX

	POP	BP
	POP	DS
	RET	8
WinWindowToDisplayPt ENDP

PURGE pX, pY
$EJECT

; 	 WinDisplayToWindowPt: PROCEDURE (pX, pY);
;
;    This converts display coordinates (absolute) to window coordinates

pX     EQU DWORD PTR [BP+10]
pY	   EQU DWORD PTR [BP+6]

WinDisplayToWindowPt PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, CS:drawWindow
	MOV	AX, DS:wiBoundsTopLeftX
	MOV	BX, DS:wiBoundsTopLeftY

	LDS	SI, pX
	SUB	DS:[SI], AX
	LDS	SI, pY
	SUB	DS:[SI], BX

	POP	BP
	POP	DS
	RET	8
WinDisplayToWindowPt ENDP

PURGE pX, pY
$EJECT

;    WinSetPattern: PROCEDURE (pPtnBytes) CLEAN
;
;    This will set the fill pattern for the window routines

pPtnBytes EQU DWORD PTR [BP+6]

WinSetPattern PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pPtnBytes	; DS:SI ^ fill pattern
	PUSH	CS
	POP	ES
	LEA	DI, CS:currPattern
	MOV	CX, 4
	CLD
	REP	MOVSW

	POP	BP
	POP	DS
	RET	4
WinSetPattern ENDP

PURGE pPtnBytes
$EJECT

;    WinGetPattern: PROCEDURE (pPtnBytes) CLEAN
;
;    This will set the fill pattern for the window routines

pPtnBytes EQU DWORD PTR [BP+6]

WinGetPattern PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LES	DI, pPtnBytes	; DS:SI ^ fill pattern
	PUSH	CS
	POP	DS
	LEA	SI, CS:currPattern
	MOV	CX, 4
	CLD
	REP	MOVSW

	POP	BP
	POP	DS
	RET	4
WinGetPattern ENDP

PURGE pPtnBytes
$EJECT

;    WinSetPtnXferMode: PROCEDURE (mode) CLEAN
;
;    This will set the pattern transfer mode for the window routines

xferMode EQU BYTE PTR [BP+4]

WinSetPtnXferMode PROC NEAR
	PUSH	BP
	MOV	BP, SP

	MOV	AL, xferMode
	AND	AL, 3
	MOV	CS:currPtnXferMode, AL

	POP	BP
	RET	2
WinSetPtnXferMode ENDP

PURGE xferMode


;    WinGetPtnXferMode: PROCEDURE BYTE CLEAN
;
;    This will return the pattern transfer mode of the window routines

WinGetPtnXferMode PROC NEAR
	MOV	AL, CS:currPtnXferMode
	RET
WinGetPtnXferMode ENDP
$EJECT

;*******************************************
;*			         *
;* PROCEDURE WinSetClip (VAR r: Rectangle) *
;*			         *
;*******************************************

pRect      EQU DWORD PTR [BP+6]

locExtentY  EQU WORD PTR [BP-2]
locExtentX  EQU WORD PTR [BP-4]
locTopLeftY EQU WORD PTR [BP-6]
locTopLeftX EQU WORD PTR [BP-8]

WinSetClip PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	CALL	WinResetClip

  ; move the VAR parameter to a local on the stack

	SUB	SP, 8	; Make room on stack for local variable

	CLD
	MOV	CX, 4	; Size of rect (in words)
	LDS	SI, pRect	; Source of copy
	PUSH	SS
	POP	ES
	MOV	DI, SP	; Dest of copy
	REP	MOVSW

	LEA	AX, locTopLeftX
	PUSH	SS
	PUSH	AX
	CALL	WinClipRectangle	; Clip the rectangle

  ; make the local rect  be the clip rectangle

	MOV	DS, CS:drawWindow

	MOV	AX, locTopLeftX
	MOV	DS:wiClipLeft, AX

	ADD	AX, locExtentX
	DEC	AX
	MOV	DS:wiClipRight, AX

	MOV	AX, locTopLeftY
	MOV	DS:wiClipTop, AX

	ADD	AX, locExtentY
	DEC	AX
	MOV	DS:wiClipBottom, AX

	MOV	SP, BP
	POP	BP
	POP	DS
	RET	4
WinSetClip ENDP

PURGE pRect, locTopLeftX, locTopLeftY, locExtentX, locExtentY
$EJECT

;*******************************************
;*			         *
;* PROCEDURE WinGetClip (VAR r: Rectangle) *
;*			         *
;*******************************************

pRect EQU DWORD PTR [BP+6]

WinGetClip PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	DS, CS:drawWindow
	MOV	AX, DS:wiClipLeft
	MOV	BX, DS:wiClipTop
	MOV	CX, DS:wiClipRight
	MOV	DX, DS:wiClipBottom

	SUB	CX, AX	; clip rectangle is stored and used
	INC	CX	; as an "absolute" rectangle but the
	SUB	DX, BX	; users pass it in (and should get it back)
	INC	DX	; as a "relative" rectangle.

	LDS	SI, pRect
	MOV	DS:[SI].rectTopLeftX, AX
	MOV	DS:[SI].rectTopLeftY, BX
	MOV	DS:[SI].rectExtentX,  CX
	MOV	DS:[SI].rectExtentY,  DX

	POP	BP
	POP	DS
	RET	4
WinGetClip ENDP

PURGE pRect
$EJECT

;***************************
;*                         *
;* PROCEDURE WinResetClip; *
;*                         *
;***************************

WinResetClip PROC NEAR
	PUSH	DS

	MOV	DS, CS:drawWindow

	XOR	AX, AX
	MOV	DS:wiClipLeft, AX
	MOV	DS:wiClipTop, AX

	MOV	AX, DS:wiBoundsExtentX
	DEC	AX
	MOV	DS:wiClipRight, AX

	MOV	AX, DS:wiBoundsExtentY
	DEC	AX
	MOV	DS:wiClipBottom, AX

	POP	DS
	RET
WinResetClip ENDP
$EJECT

;**************************************************
;*			                *
;* PROCEDURE WinClipRectangle (VAR r: Rect);      *
;*			                *
;*  during loop: ES:[BP] = clip -Left or -Right   *
;*               DS:[DI] = rect.topLeft.x or .y   *
;*			                *
;**************************************************

pRect EQU DWORD PTR [BP+6]

WinClipRectangle PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	MOV	ES, CS:drawWindow

	LDS	DI, pRect

	XOR	SI, SI	; Loop counter is initially zero
	LEA	BP, ES:wiClipLeft

  ; is DS:wiClipLeft < topLeft.x ?

ClipRectLoop:
	MOV	CX, ES:[BP].rectExtentX
	MOV	AX, ES:[BP].rectTopleftX
	CMP	AX, DS:[DI]
	JLE	LeftOK

  ; truncate the rect from the left

	PUSH	AX
	SUB	AX, DS:[DI]
	SUB	DS:[DI].rectExtentX, AX
	POP	WORD PTR DS:[DI]
	JMP	SHORT ClipXExtent

  ; check for right side clipping

LeftOK:
	CMP	CX, DS:[DI]
	JNL	ClipXExtent

  ; truncate at the right

	MOV	DS:[DI], CX
	XOR	BX, BX
	MOV	DS:[DI].rectExtentX, BX

  ; clip the extent if necessary

ClipXExtent:
	MOV	BX, DS:[DI].rectExtentX
	ADD	BX, DS:[DI]
	INC	CX
	CMP	BX, CX
	JLE	XExtentOK

  ; truncate from the right

	SUB	CX, DS:[DI]
	MOV	DS:[DI].rectExtentX, CX

  ; now test the vertical

XExtentOK:
	OR	SI, SI
	JNZ	ClipRectEnd

	INC	SI
	INC	DI
	INC	DI
	INC	BP
	INC	BP
	JMP	SHORT ClipRectLoop

ClipRectEnd:
	POP	BP
	POP	DS
	RET	4
WinClipRectangle ENDP

PURGE pRect
$EJECT

; This routine is no longer part of MiniGRiD.  It is only commented out
; versus erased in case someone decides they need it someday.

;**************************************************
;*			                *
;* FUNCTION WinClipLine	                *
;*   (VAR x1, y1, x2, y2: Integer): Boolean;      *
;*			                *
;**************************************************

;pX1	EQU [BP+18]
;pY1	EQU [BP+14]
;pX2	EQU [BP+10]
;pY2	EQU [BP+06]

;WinClipLine PROC NEAR
;	PUSH	DS
;	PUSH	BP
;	MOV	BP, SP

;	MOV	DS, CS:drawWindow

;	MOV	CX, 4	; Copy parameters into local vars
;	LEA	SI, pX1

;PushingParams:
;	LES	DI, DWORD PTR SS:[SI]
;	PUSH	WORD PTR ES:[DI]
;	SUB	SI, 4
;	LOOP	PushingParams

;	CALL	ClipLine	; Clip the line

;	MOV	CX, 4	; Copy locals back into parameters
;	LEA	SI, pY2

;PoppingParams:
;	LES	DI, DWORD PTR SS:[SI]
;	POP	ES:WORD PTR [DI]
;	ADD	SI, 4H
;	LOOP	PoppingParams

;	POP	BP
;	POP	DS
;	RET	16
;WinClipLine ENDP

;PURGE pX1, pY1, pX2, pY2
$EJECT

; Parameters to 'ClipLine'

x1		 EQU 2
y1		 EQU 4
x2		 EQU 6
y2		 EQU 8

; Locals for 'ClipLine'

lineCode1 EQU 0
lineCode2 EQU 2

; aa := aa + (ab-aa)*(cc-ba) DIV (bb - ba)

%*DEFINE (TruncateLine (aa, ab, ba, bb, cc))
   (PUSH	WORD PTR [BP-%aa]
	PUSH	WORD PTR [BP-%ba]	; push x1
	MOV	CX, %cc	; get DS:wiClipLeft
	POP	BX	; pop x1
	SUB	CX, BX	; get DS:wiClipLeft - x1
	MOV	AX, [BP - %ab]	; get y2
	POP	SI	; pop y1
	SUB	AX, SI	; get y2 - y1
	IMUL	CX	; (y2-y1)*(left-x1) ->[DX..AX]
	MOV	CX, [BP - %bb]	; get x2
	SUB	CX, [BP - %ba]	; get x2 - x1
	IDIV	CX	; do the division
	ADD	[BP - %aa], AX	; add it to 'aa' store it back
	MOV	AX, %cc	; x1 = xLeft
	MOV	[BP - %ba], AX
	JMP	ClippingLoop)

;****************************************
;*			      *
;*  FUNCTION ClipLine: Boolean          *
;*                                      *
;*  entry: SS:[BP-2H] = x1              *
;*         SS:[BP-4H] = y1              *
;*         SS:[BP-6H] = x2              *
;*         SS:[BP-8H] = y2              *
;*  exit: values have been clipped      *
;*			      *
;****************************************

ClipLine PROC NEAR
	MOV	CX, [BP-x2]	; CX gets x2
	MOV	DX, [BP-y2]	; DX gets y2
	CALL	Encode
	PUSH	AX
	PUSH	AX	; space for lineCode1 & lineCode2
	MOV	DI, SP

  ; Loop while lineCode1 or lineCode2 is non-null

ClippingLoop:
	MOV	CX, [BP-x1]	; CX gets x1
	MOV	DX, [BP-y1]	; DX gets y1
	CALL	Encode
	MOV	SS:[DI+lineCode1], AL	; save lineCode1
	MOV	BL, SS:[DI+lineCode2]
	TEST	BL, AL
	JZ	OrTest

	XOR	AX, AX
	JMP	ClipEnd

OrTest:
	OR	BL, AL
	JNZ	CaseClip

	INC	AL	; we know AL is 0
	JMP	ClipEnd

CaseClip:	; Now clip depending on what's in c1
	OR	AL, AL
	JNZ	TestLeft

	MOV	BX, [BP-x1]	; switch [x1, y1] and [x2, y2]
	MOV	CX, [BP-x2]
	MOV	[BP-x1], CX
	MOV	[BP-x2], BX
	MOV	BX, [BP-y1]
	MOV	CX, [BP-y2]
	MOV	[BP-y1], CX
	MOV	[BP-y2], BX
	MOV	CL, SS:[DI+lineCode2]
	MOV	SS:[DI+lineCode1], CL
	MOV	SS:[DI+lineCode2], AL
	MOV	AL, CL

TestLeft:
	TEST	AL, leftBit
	JZ	TestRight

	%TruncateLine (y1,y2,x1,x2,DS:wiClipLeft)

TestRight:
	TEST	AL, rightBit
	JZ	TestTop

	%TruncateLine (y1,y2,x1,x2,DS:wiClipRight)

TestTop:
	TEST	AL, topBit
	JZ	TestBottom

	%TruncateLine (x1,x2,y1,y2,DS:wiClipTop)

TestBottom:
	TEST	AL, bottomBit
	JZ	NextLoop

	%TruncateLine (x1,x2,y1,y2,DS:wiClipBottom)

NextLoop:
	JMP	ClippingLoop

ClipEnd:
	POP	BX	; get rid of lineCode 1 & 2
	POP	BX
	RET
ClipLine ENDP
$EJECT

;**************************************************
;*                                                *
;* PROCEDURE Encode (x, y: Integer; VAR c: Code)  *
;*                                                *
;*    (see Newman and Sproull's clipping)         *
;*                                                *
;*    entry: DS => window info for current window *
;*           CX = x (for encoding)                *
;*           DX = y (for encoding)                *
;*     exit: AX = code bits                       *
;*                                                *
;**************************************************

Encode PROC NEAR
	XOR	AX, AX
	CMP	CX, DS:wiClipLeft
	JNL	xNotToLeft

	MOV	AL, leftBit
	JMP	SHORT TestY

xNotToLeft:
	CMP	CX, DS:wiClipRight
	JNG	TestY

	MOV	AL, rightBit

TestY:
	CMP	DX, DS:wiClipTop
	JNL	yNotAboveTop

	OR	AL, topBit
	JMP	SHORT EncodeEnd

yNotAboveTop:
	CMP	DX, DS:wiClipBottom
	JNG	EncodeEnd

	OR	AL, bottomBit

EncodeEnd:
	RET
Encode ENDP


CODE ENDS

END
