;++
;
;   WOW v1.0
;
;   Copyright (c) 1991, Microsoft Corporation
;
;   WINUTIL.ASM
;   Win16 general utility routines
;
;   History:
;
;   Created 28-May-1991 by Jeff Parsons (jeffpar)
;   Copied from WIN31 and edited (as little as possible) for WOW16.
;   At this time, all we want is MultDiv() and LCopyStruct(), which the
;   edit controls use.
;--

;****************************************************************************
;*									    *
;*  WINUTIL.ASM -							    *
;*									    *
;*	General Utility Routines					    *
;*									    *
;****************************************************************************

	title WINUTIL.ASM - General Utility routines

ifdef WOW
NOEXTERNS equ 1
SEGNAME equ <TEXT>
endif
.xlist
include user.inc
.list


;*==========================================================================*
;*									    *
;*  FFFE Segment Definition -						    *
;*									    *
;*==========================================================================*

createSeg _%SEGNAME, %SEGNAME, WORD, PUBLIC, CODE

assumes cs,%SEGNAME
assumes ds,DATA

ExternFP <GetStockObject>
ExternFP <GetTextExtent>
ExternFP <TextOut>

ExternA <__WinFlags>

ifdef FE_SB			; **** April,26,1990 by KenjiK ****
ExternFP	IsDBCSLeadByte
endif

sBegin DATA
sEnd DATA

sBegin %SEGNAME


;*--------------------------------------------------------------------------*
;*									    *
;*  MultDiv() - 							    *
;*									    *
;*--------------------------------------------------------------------------*

; Calc a * b / c, with 32-bit intermediate result

cProc MultDiv, <PUBLIC, FAR>

;ParmW a
;ParmW b
;ParmW c

cBegin nogen
        mov     bx,sp
                                ; calc (a * b + c/2) / c
        mov     ax,ss:[bx+8]    ; ax = a
        mov     cx,ss:[bx+4]    ; cx = c
        or      cx,cx
	jz	mdexit		; just return A if we'll get divide by 0
        mov     dx,ss:[bx+6]    ; dx=b
        imul    dx

        mov     bx,cx           ; save a copy of c in bx for later
        shr     cx,1            ; add in cx/2 for rounding
        add     ax,cx
        adc     dx,0            ; add in carry if needed
                        	; get c from bx register since idev mem
        idiv    bx              ; doesn't work on tandy 2000's
mdexit:
        retf    6
cEnd nogen


ifdef DISABLE

;*--------------------------------------------------------------------------*
;*									    *
;*  min() -								    *
;*									    *
;*--------------------------------------------------------------------------*

cProc min, <FAR, PUBLIC>

;ParmW a
;ParmW b

cBegin nogen
        mov     bx,sp
	mov	ax,ss:[bx+6]    ;ax = a
        mov     cx,ss:[bx+4]    ;cx = b
	cmp	ax,cx
	jl	min10
	mov	ax,cx
min10:
        retf    4
cEnd nogen


;*--------------------------------------------------------------------------*
;*									    *
;*  max() -								    *
;*									    *
;*--------------------------------------------------------------------------*

cProc max, <FAR, PUBLIC>

;ParmW a
;ParmW b

cBegin nogen
        mov     bx,sp
	mov	ax,ss:[bx+6]   ;ax = a
        mov     cx,ss:[bx+4]   ;cx = b
	cmp	ax,cx
	jg	max10
	mov	ax,cx
max10:
        retf    4
cEnd nogen


;*--------------------------------------------------------------------------*
;*									    *
;*  umin() -								    *
;*									    *
;*--------------------------------------------------------------------------*

cProc umin, <FAR, PUBLIC>

;ParmW a
;ParmW b

cBegin nogen
        mov     bx,sp
	mov	ax,ss:[bx+6]    ;ax = a
        mov     cx,ss:[bx+4]    ;cx = b
	cmp	ax,cx
	jb	umin10
	mov	ax,cx
umin10:
        retf    4
cEnd nogen


;*--------------------------------------------------------------------------*
;*									    *
;*  umax() -								    *
;*									    *
;*--------------------------------------------------------------------------*

cProc umax, <FAR, PUBLIC>

;ParmW a
;ParmW b

cBegin nogen
        mov     bx,sp
	mov	ax,ss:[bx+6]   ;ax = a
        mov     cx,ss:[bx+4]   ;cx = b
	cmp	ax,cx
	ja	umax10
	mov	ax,cx
umax10:
        retf    4
cEnd nogen

endif	; DISABLE

;*--------------------------------------------------------------------------*
;*									    *
;*  LFillStruct() -							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc LFillStruct, <PUBLIC, FAR, NODATA, ATOMIC>, <di>

parmD lpStruct
parmW cb
parmW fillChar

cBegin
        les     di,lpStruct
        mov     cx,cb
        mov     ax,fillChar
        cld
	rep	stosb

cEnd LFillStruct


;*--------------------------------------------------------------------------*
;*									    *
;*  LCopyStruct() -							    *
;*									    *
;*--------------------------------------------------------------------------*

; LCopyStruct(pbSrc, pbDst, cb)

cProc LCopyStruct, <FAR, PUBLIC>
;ParmD pSrc
;ParmD pDest
;ParmW cb
cBegin nogen
        mov     bx,sp

	mov	cx,ss:[bx+4]        ; cx = cb
	jcxz	lcopynone	    ; Nothing to do if count == 0

        push    si
        push    di
        mov     dx,ds               ; save ds

	lds	si,ss:[bx+4+2+4]    ; ds:si = pSrc
	les	di,ss:[bx+4+2]	    ; es:di = pDst

	cmp	si,di		    ; Could they overlap?
        jae     lcopyok

	mov	ax,cx		    ; Yes: copy from the top down
        dec     ax
	dec	ax
        add     si,ax
        add     di,ax

        std
	shr	cx,1
	rep	movsw
	jnc	@F		    ; an odd byte to blt?
	inc	si		    ; went one too far: back up.
	inc	di
	movsb
@@:
	cld
	jmps	lcopydone

lcopyok:
	cld
	shr	cx,1
	rep	movsw
	jnc	@F		    ; an odd byte to blt?
	movsb
@@:

lcopydone:
        mov     ds,dx
        pop     di
        pop     si
lcopynone:
	retf	4+4+2
cEnd nogen


ifndef WOW
;*--------------------------------------------------------------------------*
;*									    *
;*  The following routines are "Movable DS" equivalents of their normal     *
;*  counterparts.  The PS stands for "Pointer Safe."  They prevent problems *
;*  which occur when an app passes in a pointer to an object in its DS	    *
;*  which we somehow cause to move.  To prevent this problem, we copy what  *
;*  the pointer points to into USER's DS and use a pointer to our copy	    *
;*  instead.								    *
;*									    *
;*--------------------------------------------------------------------------*


;*--------------------------------------------------------------------------*
;*									    *
;*  PSGetTextExtent() - 						    *
;*									    *
;*--------------------------------------------------------------------------*

ifndef PMODE


cProc PSGetTextExtent, <PUBLIC, FAR, NODATA>, <si,di>

ParmW hdc
ParmD lpch
ParmW cch

LocalV rgch, 128		; max 128 chars

cBegin
        mov     ax,__WinFlags
        test    al,WF_PMODE
        errnz   high(WF_PMODE)
        jz      PSGTE_RealMode

        push    hdc
        pushd   lpch
        push    cch
        jmp     short PSGTE_GetExtent

PSGTE_RealMode:
        lea     di,rgch         ; es:di = dest
        push    ss
        pop     es
        lds     si,lpch         ; ds:si = src
        mov     cx,cch          ; count = min(cch, 128)
        mov     ax,128
        cmp     cx,ax
        jbe     gte100
	xchg	ax,cx

gte100: push	hdc		; Push args before rep movsb destroys
	push	ss		; cx and di
	push	di
	push	cx

	cld
	rep movsb		; Copy string to local storage

PSGTE_GetExtent:
	call	GetTextExtent

cEnd PSGetTextExtent


;*--------------------------------------------------------------------------*
;*									    *
;*  PSTextOut() -							    *
;*									    *
;*--------------------------------------------------------------------------*

; void PSTextOut(hdc, lpch, cch)

cProc PSTextOut,<PUBLIC, FAR, NODATA>, <si, di>

ParmW hdc
ParmW x
ParmW y
ParmD lpch
ParmW cch

LocalV rgch, 255		; max 255 chars

cBegin
        mov     ax,__WinFlags
        test    al,WF_PMODE
        errnz   high(WF_PMODE)
        jz      PSTO_RealMode

        push    hdc
        push    x
        push    y
        pushd   lpch
        push    cch
        jmp     short PSTO_TextOut

PSTO_RealMode:
        lea     di,rgch         ; es:di = dest
        push    ss
        pop     es
        lds     si,lpch         ; ds:si = src
        mov     cx,cch          ; count = min(cch, 255)
        mov     ax,255
        cmp     cx,ax
        jbe     to100
        xchg    ax,cx

to100:	push	hdc		; Push args before rep movsb destroys
	push	x		; cx and di
        push    y
	push	ss		; Push &rgch[0]
        push    di
	push	cx

	cld
	rep movsb		; copy string before we go

PSTO_TextOut:
	call	TextOut

cEnd PSTextOut

endif ; ifndef PMODE


;*--------------------------------------------------------------------------*
;*									    *
;*  FindCharPosition() -						    *
;*									    *
;*--------------------------------------------------------------------------*

; Finds position of char ch in string psz. If none, returns the length of
; the string.

cProc FindCharPosition, <FAR, PUBLIC, NODATA>, <si, ds>

ParmD psz
ParmB char

cBegin
        lds     si,psz
fcp100: lodsb                           ; get a byte
        or      al,al
        jz      fcp200
ifdef   FE_SB				; **** April,26,1990 by KenjiK ****
	sub	ah,ah
        push	ax
        cCall   IsDBCSLeadByte,<ax>     ; first byte of double byte?
        test    ax,ax
        pop     ax
        jz      fcp150                  ; no just do normal stuff
        lodsb                           ; skip second byte of double byte
        jmps    fcp100                  ; and do again
fcp150:
endif
        cmp     al,char
        jnz     fcp100
fcp200: xchg	ax,si	    ; calc char index: pch - 1 - psz
        dec     ax
	sub	ax,off_psz

cEnd FindCharPosition

endif ;WOW

sEnd %SEGNAME

ifndef WOW
;*==========================================================================*
;*									    *
;*  RUNAPP Segment Definition - 					    *
;*									    *
;*==========================================================================*

createSeg _RUNAPP, RUNAPP, BYTE, PUBLIC, CODE

sBegin RUNAPP

assumes cs,RUNAPP
assumes ds,DATA


;*--------------------------------------------------------------------------*
;*									    *
;*  CrunchX2() -							    *
;*									    *
;*--------------------------------------------------------------------------*

; This routine copies the pile of bits from the source pointer
; to the dest pointer doing the integer crunch as it goes
; it will favor black (0) to white (1) and will keep the destinations
; widthbytes even.
;
; Assumptions: Neither source nor destination are greater than 64K
;	       Either the two bitmaps, lpSrc and lpDst are disjoint
;		   or lpDst <= lpSrc (i.e. we fill from the front)
;	       WidthBytes is even

cProc CrunchX2, <FAR, PUBLIC, NODATA>, <ds, si, di>

parmD	lpSrc
parmD	lpDst
parmW	WidthBytes
parmW	Height

LocalW	dwb		; destination width if not corrected
LocalW	destinc 	; used to force dest width even each scan

cBegin
        cld
        lds     si,lpSrc
        les     di,lpDst
        mov     bx,Height

	; Compute destination widthbytes
        mov     ax,WidthBytes           ; must be even
        shr     ax,1                    ; widthbytes for the destination
        mov     dwb,ax
        and     ax,1                    ; iff odd must inc dest pointer
        mov     destinc,ax              ; at the end of each scan
        sub     di,ax

NextSX: dec	bx
        jl      exitX
        mov     cx,dwb
        add     di,destinc

CrunchBX:
        lodsw
        mov     dx,ax
        rol     ax,1
        and     dx,ax               ; this and selects 0 in favor of 1
        mov     al,dl
        shl     ax,1
        shl     al,1
        shl     ax,1
        shl     al,1
        shl     ax,1
        shl     al,1
        shl     ax,1
        mov     al,dh
        shl     ax,1
        shl     al,1
        shl     ax,1
        shl     al,1
        shl     ax,1
        shl     al,1
        shl     ax,1
        mov     al,ah
        stosb
        loop    CrunchBX
        jmp     NextSX
exitX:

cEnd CrunchX2


;*--------------------------------------------------------------------------*
;*									    *
;*  CrunchY() - 							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc CrunchY, <FAR, PUBLIC, NODATA>, <ds, si, di>

parmD	lpSrc
parmD	lpDst
parmW	WidthBytes
parmW	Height
parmW	scale

LocalW	groupcount	;Height/scale
LocalW	groupinc	;WidthBytes * Height/Scale
LocalW	scancount	;counter of bytes in scan reset each group
LocalW	bytecount	;number of bytes joined = scale - 1

cBegin
        cld
        lds     si,lpSrc
        les     di,lpDst

	; Compute group count
        mov     bx,scale                ; only scale positive
        cmp     bx,1
        jle     CopyBitmap
        mov     ax,Height
        xor     dx,dx
        div     bx
	mov	groupcount,ax		; Height/scale
        mov     cx,WidthBytes           ; must be even
        dec     bx
        mov     bytecount,bx
        mov     ax,bx
        mul     cx
	mov	groupinc,ax		; WidthBytes * (scale - 1)
        mov     dx,cx
        mov     bx,si
        sub     bx,groupinc
        dec     bx
        dec     bx

NextGroup:
        dec     groupcount
        jl      exitY
        add     bx,groupinc
        mov     ax,dx
        shr     ax,1
        mov     scancount,ax

NextByte:
        dec     scancount
        jl      NextGroup
        inc     bx
        inc     bx
        mov     si,bx
        mov     ax,[si]
        mov     cx,bytecount

CrunchBY:
        add     si,dx
        and     ax,[si]
        loop    CrunchBY
        stosw
        jmp     NextByte

CopyBitmap:
        mov     ax,Height
        mul     WidthBytes
        shr     ax,1
        mov     cx,ax
        rep movsw

exitY:
cEnd CrunchY

sEnd RUNAPP

endif ;WOW

	end