$NOLIST

$INCLUDE (WinCnsts~Inc~)

NAME  WindowCharRoutines

CGROUP GROUP CODE

PUBLIC FntLoadFont, FntSetFont, FntProportionalFont
PUBLIC FntCharHeight, FntLineHeight, FntBaseLine
PUBLIC FntAverageCharWidth, FntCharWidth, FntCharsWidth
PUBLIC FntSetFontOrientation

PUBLIC WinDrawChars, WinDrawGrayChars
PUBLIC WinEraseChars, WinInvertChars, WinPatternChars


EXTRN  drawWindow: WORD, currPtnXferMode: BYTE, currPattern: BYTE
EXTRN  currOrientation: BYTE, currFontInfoAddr: DWORD

EXTRN  DosAlloc: NEAR, DosFree: NEAR, RetDosAndIntelPtrs: NEAR
EXTRN  CsrMaybeTurnCursorOff: NEAR, CsrTurnCursorOn: NEAR

EXTRN	GfxDrawChars: FAR, GfxPatternChars: FAR


CODE SEGMENT PUBLIC 'CODE'
ASSUME  CS:CGROUP
$EJECT

; FntLoadFont: PROCEDURE (pFontFilename, pError) PTR CLEAN;

pFontFilename EQU DWORD PTR [BP+10]
pError        EQU DWORD PTR [BP+06]

fileSize      EQU  WORD PTR [BP-02]


FntLoadFont PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	PUSH	CX	; Make room on stack for local variable

	LDS	DX, pFontFilename
	MOV	AX, 3D00H	; MsAttach w/read access
	INT	21H	; Call MsDos
	JC	FntLoadFontError

	MOV	BX, AX	; All other file system calls use conn in BX

	MOV	DX, 2
	XOR	CX, CX	; CX:DX = filePos to seek to (i.e. 2)
	MOV	AX, 4200H	; Seek to here
	INT	21H	; Call MsDos
	JC	FntLoadFontErrorAfterAttach

	PUSH	SS
	POP	DS
	LEA	DX, fileSize
	MOV	CX, 2
	MOV	AH, 3FH	; Read
	INT	21H	; Call MsDos
	JC	FntLoadFontErrorAfterAttach

	XOR	DX, DX
	MOV	CX, DX	; CX:DX = filePos to seek to (i.e. 0)
	MOV	AX, 4200H	; Seek to here
	INT	21H	; Call MsDos
	JC	FntLoadFontErrorAfterAttach

	PUSH	BX	; Save file conn
	MOV	BX, fileSize
	ADD	BX, 15
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	CALL	DosAlloc	; Interface call to Int21, fnc 48h
	POP	BX	; File conn
	JC	FntLoadFontErrorAfterAttach

	MOV	DS, AX	; Address to read into
	MOV	ES, AX	; Address to return (in ES:BX)
	XOR	DX, DX
	MOV	CX, fileSize
	MOV	AH, 3FH	; Read from file
	INT	21H	; Call MsDos
	JNC	FntLoadFontExit

	CALL	DosFree	; Interface call to Int21, fnc 49h
	JMP	FntLoadFontErrorAfterAttach

FntLoadFontExit:
	XOR	BX, BX	; Return address is ES:BX = Seg:0

	LDS	SI, pError
	MOV	DS:[SI], BX	; Return error code is 0
	JMP	FntLoadFontRet

FntLoadFontErrorAfterAttach:
	PUSH	AX	; Save error code
	MOV	AH, 3EH	; MsDetach
	INT	21H	; Call MsDos
	POP	AX	; error code

FntLoadFontError:
	LDS	SI, pError
	MOV	DS:[SI], AX

	XOR	BX, BX	; Address returned when there is
	MOV	ES, BX	; an error is 0:0

FntLoadFontRet:
	CALL	RetDosAndIntelPtrs	; Return ptr in ES:BX and DX:AX
	
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	8
FntLoadFont ENDP

PURGE pFontFilename, pError, fileSize
$EJECT

; FntSetFont PROCEDURE (pNewFontInfo) PTR CLEAN;

pNewFont EQU DWORD PTR [BP+6]

FntSetFont PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LES	BX, CS:currFontInfoAddr	; ES:BX => current font info record
	LDS	SI, pNewFont	; DS:SI => new font info record
	MOV	AX, DS	; If pointer to new font info record
	OR	AX, SI	; is zero then don't change current font
	JZ	FntSetFontRet	; but return current font to caller

	MOV	AL, CS:currOrientation	; If the current window orientation
	CMP	AL, DS:[SI].fiOrientation	; matches that of the new font
	JE	FntSetFontOkayOrientation	; then no need to rotate it into place

	PUSH	DS
	PUSH	SI	; pFontInfo of new font
	PUSH	AX	; new orientation
	CALL	FntSetFontOrientation
	LDS	SI, pNewFont	; Reload this value after rotating font

FntSetFontOkayOrientation:
	LES	BX, CS:currFontInfoAddr	; ES:BX => current font info record
	MOV	WORD PTR CS:currFontInfoAddr+0, SI
	MOV	WORD PTR CS:currFontInfoAddr+2, DS

FntSetFontRet:
	CALL	RetDosAndIntelPtrs	; Return ptr in ES:BX and DX:AX
	
	POP	BP
	POP	DS
	RET	4
FntSetFont ENDP

PURGE pNewFont
$EJECT

; FntProportionalFont PROCEDURE BOOLEAN CLEAN;

FntProportionalFont PROC NEAR
	LES	BX, CS:currFontInfoAddr
	MOV	AX, ES:[BX].fiPixWidth
	MOV	BX, 1	; RETURN (TRUE) - proportional font
	OR	AX, AX	; If pixWidth field is zero then
	JZ	FntPropFontRet	; the font is a proportional font

	DEC	BX	; RETURN (FALSE) - not proportional font

FntPropFontRet:
	XCHG	AX, BX
	RET
FntProportionalFont ENDP


; FntCharHeight PROCEDURE WORD CLEAN;

FntCharHeight PROC NEAR
	LES	BX, CS:currFontInfoAddr
	MOV	AX, ES:[BX].fiPixHeight
	RET
FntCharHeight ENDP


; FntLineHeight PROCEDURE WORD CLEAN;

FntLineHeight PROC NEAR
	LES	BX, CS:currFontInfoAddr
	MOV	AX, ES:[BX].fiPixHeight
	ADD	AX, ES:[BX].fiExtLead
	RET
FntLineHeight ENDP


; FntBaseLine PROCEDURE WORD CLEAN;

FntBaseLine PROC NEAR
	LES	BX, CS:currFontInfoAddr
	MOV	AX, ES:[BX].fiBaseLine
	RET
FntBaseLine ENDP


; FntAverageCharWidth PROCEDURE WORD CLEAN;

FntAverageCharWidth PROC NEAR
	LES	BX, CS:currFontInfoAddr
	MOV	AX, ES:[BX].fiAvgWidth
	RET
FntAverageCharWidth ENDP
$EJECT

; FntCharWidth PROCEDURE (char) WORD CLEAN;

char EQU BYTE PTR [BP+4]

FntCharWidth PROC NEAR
	PUSH	BP
	LEA	AX, char
	PUSH	SS
	PUSH	AX
	MOV	CX, 1
	PUSH	CX
	CALL	FntCharsWidth
	POP	BP
	RET	2
FntCharWidth ENDP

PURGE char


; FntCharsWidth: PROCEDURE (pChars, len) WORD CLEAN;

pChars    EQU DWORD PTR [BP+08]
lenChars  EQU  WORD PTR [BP+06]

FntCharsWidth PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	XOR	AX, AX	; Initially length is zero
	MOV	CX, lenChars
	JCXZ	FntCharsWidthRet	; Length is zero if no chars

	LDS	SI, CS:currFontInfoAddr
	LES	DI, pChars

	MOV	DH, DS:[SI].fiLastChar	; Save last valid char index in DH
	MOV	DL, DS:[SI].fiFirstChar	; Save first valid char index in DL

FntCharsWidthLoop:
	MOV	BL, ES:[DI]
	INC	DI
	CMP	BL, DL
	JB	FntCharsWidthUseDefault

	CMP	BL, DH
	JBE	FntCharsWidthValidChar

FntCharsWidthUseDefault:
	MOV	BL, DS:[SI].fiDfltChar

FntCharsWidthValidChar:
	SUB	BL, DL
	XOR	BH, BH
	SHL	BX, 1
	SHL	BX, 1
	ADD	AX, WORD PTR DS:[SI+BX].fiChInfBase
	LOOP	FntCharsWidthLoop

FntCharsWidthRet:
	POP	BP
	POP	DS
	RET	6
FntCharsWidth ENDP

PURGE pChars, lenChars
$EJECT

; PROCEDURE: WinDrawChars (pChars, count, topLeftX, topLeftY);

params  STRUC
	drawCharsMode  DB ?
	filler         DB ?

	charTopClip    DW ?
	charLeftClip   DW ?
	charBottomClip DW ?
	charRightClip  DW ?

  charBottomY    DW ?
  charRightX     DW ?

	oldBP          DW ?
	oldDS          DW ?
	returnIP       DW ?
;	returnCS       DW ?	; Not needed if a NEAR procedure

	charTopY       DW ?
	charLeftX      DW ?
	numChars       DW ?
	pChars         DD ? 
params  ENDS

localBytes     EQU 14
loc	        EQU [BP-localBytes]
paramBytes     EQU 10

WinPatternChars LABEL NEAR
	MOV	AL, CS:currPtnXferMode
	OR	AL, ptnVerbsBit
	JMP	SHORT WinCharsRoutine

WinInvertChars LABEL NEAR
	MOV	AL, invertVerb
	JMP	SHORT WinCharsRoutine

WinEraseChars LABEL NEAR
	MOV	AL, eraseVerb
	JMP	SHORT WinCharsRoutine

WinDrawGrayChars LABEL NEAR
	MOV	AL, mergeVerb	; Use merge to indicate gray (not merge)
	JMP	SHORT WinCharsRoutine

WinDrawChars PROC NEAR
	MOV	AL, drawVerb

WinCharsRoutine:
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB  SP, localBytes

	MOV	loc.drawCharsMode, AL
	MOV	CX, loc.numChars
	OR	CX, CX
	JNZ	WinDrawCharsNotZero
	JMP	WinDrawCharsRet

WinDrawCharsNotZero:
	XOR	AX, AX
	MOV	loc.charTopClip, AX
	MOV	loc.charLeftClip, AX
	MOV	loc.charBottomClip, AX
	MOV	loc.charRightClip, AX

	MOV	DS, CS:drawWindow
	LES	BX, CS:currFontInfoAddr

	MOV	AL, CS:currOrientation
	CMP	AL, ES:[BX].fiOrientation
	JE	WinDrawCharsOkayOrientation

	PUSH	ES
	PUSH	BX	; pFontInfo
	PUSH	AX	; new orientation
	CALL	FntSetFontOrientation
	LES	BX, CS:currFontInfoAddr	; Reload this value after rotating font

WinDrawCharsOkayOrientation:
	MOV	AX, ES:[BX].fiPixHeight

	ADD	AX, loc.charTopY
	DEC	AX
	MOV	loc.charBottomY, AX

	PUSH	ES
	PUSH	BX
	LES	AX, loc.pChars
	PUSH	ES
	PUSH	AX
	PUSH	CX
	CALL	FntCharsWidth

	ADD	AX, loc.charLeftX
	DEC	AX
	MOV	loc.charRightX, AX

	MOV	AX, loc.charLeftX
	MOV	BX, loc.charTopY
	MOV	CX, loc.charRightX
	MOV	DX, loc.charBottomY

	CMP	AX, DS:wiClipLeft	; If left of chars is left of clip area
	JL	WinDrawCharsNeedClipping	; then need to clip the characters

	CMP	BX, DS:wiClipTop	; If top of chars is above top of clip area
	JL	WinDrawCharsNeedClipping	; then need to clip the characters

	CMP	CX, DS:wiClipRight	; If right of chars is right of clip area
	JG	WinDrawCharsNeedClipping	; then need to clip the characters

	CMP	DX, DS:wiClipBottom	; If bottom of chars is below bottom of clip
	JG	WinDrawCharsNeedClipping	; area then need to clip the characters
	JMP	WinDrawCharsPassParms

WinDrawCharsNeedClipping:
	CMP	AX, DS:wiClipRight	; If left of chars is right of clip area
	JGE	HopToWinDrawCharsRet	; then don't draw any chars at all

	CMP	BX, DS:wiClipBottom	; If top of chars is below bottom of clip
	JGE	HopToWinDrawCharsRet	; area then don't draw any chars at all

	CMP	CX, DS:wiClipLeft	; If right of chars is left of clip area
	JLE	HopToWinDrawCharsRet	; then don't draw any chars at all

	CMP	DX, DS:wiClipTop	; If bottom of chars is above top of clip
	JG	WinCheckForClipOnTop	; area then don't draw any chars at all

HopToWinDrawCharsRet:
	JMP	WinDrawCharsRet

WinCheckForClipOnTop:
	MOV	DI, DS:wiClipTop
	CMP	BX, DI	; If top of chars is below top of clip area
	JGE	WinCheckForClipOnBottom	; then no clipping occurs on the top

	SUB	BX, DI
	NEG	BX
	MOV	loc.charTopClip, BX
	MOV	loc.charTopY, DI

WinCheckForClipOnBottom:
	MOV	DI, DS:wiClipBottom
	CMP	DX, DI	; If bottom of chars is above bottom of clip
	JLE	WinCheckForClipOnLeft	; area then no clipping occurs on the bottom

	SUB	DX, DI
	MOV	loc.charBottomClip, DX
	MOV	loc.charBottomY, DI

WinCheckForClipOnLeft:
	MOV	CX, loc.charLeftX
	CMP	CX, DS:wiClipLeft	; If left of chars is right of clip area
	JGE	WinCheckForClipOnRight	; then no clipping occurs at the left

	LES	DI, loc.pChars
	MOV	BX, loc.numChars

WinClipLeftLoop:
	PUSH	ES
	PUSH	DI
	PUSH	BX
	PUSH	CX

	PUSH	ES
	PUSH	DI
	MOV	AX, 1
	PUSH	AX
	CALL	FntCharsWidth

	POP	CX
	POP	BX
	POP	DI
	POP	ES

	ADD	CX, AX
	CMP	CX, DS:wiClipLeft
	JGE	WinFoundClipLeftPt

	DEC	BX
	INC	DI
	JMP	SHORT WinClipLeftLoop

WinFoundClipLeftPt:
	MOV	loc.numChars, BX
	MOV	WORD PTR loc.pChars, DI
	SUB	CX, AX
	MOV	DI, DS:wiClipLeft
	SUB	CX, DI
	NEG	CX
	MOV	loc.charLeftClip, CX
	MOV	loc.charLeftX, DI

WinCheckForClipOnRight:
	MOV	CX, loc.charRightX
	CMP	CX, DS:wiClipRight	; If right of chars is left of clip area
	JLE	WinClippingDone	; then no clipping occurs at the right

	LES	DI, loc.pChars
	MOV	BX, loc.numChars

WinClipRightLoop:
	PUSH	ES
	PUSH	DI
	PUSH	BX
	PUSH	CX

	PUSH	ES
	ADD	DI, BX
	DEC	DI
	PUSH	DI
	MOV	AX, 1
	PUSH	AX
	CALL	FntCharsWidth

	POP	CX
	POP	BX
	POP	DI
	POP	ES

	SUB	CX, AX
	CMP	CX, DS:wiClipRight
	JLE	WinFoundClipRightPt

	DEC	BX
	JMP	SHORT WinClipRightLoop

WinFoundClipRightPt:
	MOV	loc.numChars, BX
	ADD	CX, AX
	MOV	DI, DS:wiClipRight
	SUB	CX, DI
	MOV	loc.charRightClip, CX
	MOV	loc.charRightX, DI

WinClippingDone:
	MOV	AX, loc.charLeftX
	MOV	BX, loc.charTopY
	MOV	CX, loc.charRightX
	MOV	DX, loc.charBottomY

;	At this point AX, BX, CX and DX should have char left, top, right and bottom

WinDrawCharsPassParms:
	MOV	SI, CX	; Pass right in SI
	MOV	DI, DX	; Pass bottom in DI
	MOV	CX, AX	; Pass left in CX
	MOV	DX, BX	; Pass top in DX
	CALL	CsrMaybeTurnCursorOff	; DS must point to current draw window !!!
	PUSH	AX	; Save whether it was actually turned off

WinDrawCharsAfterCursorChk:
	MOV	AX, loc.charLeftX
	MOV	BX, loc.charTopY
	MOV	CX, loc.charRightX
	MOV	DX, loc.charBottomY

	PUSH	DS:wiDisplayAddr
	PUSH	DS:wiDisplayWidth
	PUSH	DS:wiDisplayHeight

	SUB	CX, AX
	INC	CX
	SUB	DX, BX
	INC	DX
	ADD	AX, DS:wiBoundsTopLeftX
	ADD	BX, DS:wiBoundsTopLeftY

	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX

	PUSH	loc.charTopClip
	PUSH	loc.charLeftClip
	PUSH	loc.charBottomClip
	PUSH	loc.charRightClip

	LES	AX, loc.pChars
	PUSH	ES
	PUSH	AX
	PUSH	loc.numChars

	LES	AX, CS:currFontInfoAddr
	PUSH	ES
	PUSH	AX

	MOV	AL, loc.drawCharsMode	; If just drawing regular
	OR	AL, AL	; characters then use faster
	JZ	WinDrawNormalChars	; version of GfxDrawChars

	TEST	AL, ptnVerbsBit	; If this is a WinPatternChars call
	JNZ	WinDrawCharsPassPattern	; then use the global defined pattern

	CMP	AL, mergeVerb	; If draw mode specified is merge then
	JE	WinDrawCharsPassGrayPtn	; really make it draw gray chars

WinEraseOrInvertChars:
	MOV	BX, 0FFFFH	; Pattern for normal erase/invert is FFFFH
	JMP	SHORT PushPredefinedPattern

WinDrawCharsPassGrayPtn:
	MOV	AL, drawVerb	; Reset merge back to draw
	MOV	BX, 055AAH	; Pattern for WinDrawGrayChars

PushPredefinedPattern:
	PUSH	BX
	PUSH	BX
	PUSH	BX
	PUSH	BX
	JMP	SHORT WinDrawCharsPassXferMode

WinDrawCharsPassPattern:
	PUSH	WORD PTR CS:currPattern+6
	PUSH	WORD PTR CS:currPattern+4
	PUSH	WORD PTR CS:currPattern+2
	PUSH	WORD PTR CS:currPattern+0

WinDrawCharsPassXferMode:
	AND	AX, 3
	PUSH	AX	; Pattern transfer mode

	CALL	GfxPatternChars
	JMP	SHORT WinDrawCharsExit

WinDrawNormalChars:
	CALL	GfxDrawChars

WinDrawCharsExit:
	POP	AX	; State of cursor before drawing chars
	OR	AL, AL	; If cursor was not turned off for this call
	JZ	WinDrawCharsRet	; then return

	CALL	CsrTurnCursorOn	; Else turn cursor back on

WinDrawCharsRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	paramBytes
WinDrawChars ENDP

PURGE params, localBytes, loc, paramBytes
PURGE oldBP, oldDS, returnIP; returnCS
PURGE pChars, numChars, charLeftX, charTopY, charRightX, charBottomY
PURGE charTopClip, charLeftClip, charBottomClip, charRightClip
$EJECT

; This routine will determine if a font image needs to be rotated to match
; the requested new orientation.  If it does, then it determines
; the most efficient way to do that and calls RotateFontImages to do it.

; FntSetFontOrientation: PROCEDURE (pFontInfo, newOrientation);
;   pFont          PTR;
;   newOrientation BYTE;

pFontInfo      EQU DWORD PTR [BP+8]
newOrientation EQU  BYTE PTR [BP+6]

rotateLeft     EQU 0
rotateRight    EQU 1

FntSetFontOrientation PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP

	LDS	SI, pFontInfo
	MOV	DL, DS:[SI].fiOrientation	; If the current orientation
	MOV	DH, newOrientation	; and the new orientation
	CMP	DH, DL	; are the same then
	JE	RotateFontExit	; they are already correct

	MOV	CX, DS:[SI].fiFilesize	; Store in CX for MOVB later
	MOV	BX, CX
	ADD	BX, 15
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	SHR	BX, 1
	CALL	DosAlloc	; Interface call to Int21, fnc 48h
	JC	RotateFontExit	; WHAT HAPPENS IF THERE IS AN ERROR ?

	MOV	ES, AX
	XOR	DI, DI

	MOV	AX, DX	; Put orientations into temp register
	AND	AH, 1	; If new and old orientation are either
	AND	AL, 1	; east <-> west or north <-> south
	CMP	AL, AH	; then rotate 180 degrees otherwise
	JNE	Rotate90Degrees	; rotate it 90 degrees left or right

Rotate180Degrees:
	PUSH	ES	; Save tempFontInfo ptr

	PUSH	DS
	PUSH	SI	; pFontInfo
	PUSH	ES
	PUSH	DI	; pTempFontInfo
	MOV	AL, rotateLeft	; rotate direction
	PUSH	AX
	CALL	RotateFontImages

	LDS	SI, pFontInfo
	POP	ES	; sTempFontInfo
	XOR	DI, DI
	MOV	AL, ES:[DI].fiOrientation	; Need to update the fonts
	INC	AX	; temporary orientation
	AND	AL, 3	; so the next call to rotate font images
	MOV	ES:[DI].fiOrientation, AL	; will know that it is different

	MOV	AL, rotateLeft	; rotate direction
	JMP	SHORT RotateFontFinish

Rotate90Degrees:
	PUSH	SI
	PUSH	DI
	SHR	CX, 1	; DS:SI; ES:DI; and CX set above
	CLD
	REP	MOVSW
	JNC	RotateEvenCopyAmount

	MOVSB

RotateEvenCopyAmount:
	POP	DI
	POP	SI

	MOV	AL, DL	; current font orientation
	OR	AL, AL	; If the curr font orientation is not north
	JNZ	Rotate90NotNorth	; then numbers will work as is

	MOV	AL, 4	; else make north be 4 (to avoid neg #'s)

Rotate90NotNorth:
	SUB	AL, DH
	CMP	AL, rotateRight
	JE	RotateFontFinish

	MOV	AL, rotateLeft

RotateFontFinish:
	PUSH	ES	; Save tempFontInfo

	PUSH	ES
	PUSH	DI
	PUSH	DS
	PUSH	SI
	PUSH	AX
	CALL	RotateFontImages

	POP	ES	; @tempFontInfo
	CALL	DosFree	; Interface call to Int21, fnc 49h

	LDS	SI, pFontInfo
	MOV	AL, newOrientation
	MOV	DS:[SI].fiOrientation, AL

RotateFontExit:
;	MOV	SP, BP	; Not necessary if no local variables
	POP	BP
	POP	DS
	RET	6
FntSetFontOrientation ENDP

PURGE pFontInfo, newOrientation
PURGE rotateLeft, rotateRight
$EJECT

; This routine rotates the font character images either to the left or to
; the right (depending on the parameter direction).  It is assumed that the
; destination for the font info has already been allocated.

; RotateFontImages: PROCEDURE (pSrcFontInfo, pDstFontInfo, direction) CLEAN
;  DCL pSrcFontInfo  PTR;
;  DCL pDstFontImage PTR;
;  DCL direction     BYTE;

pSrcFontInfo      EQU DWORD PTR [BP+12]
pDstFontInfo      EQU DWORD PTR [BP+08]
direction         EQU  BYTE PTR [BP+06]

curOrientation    EQU  BYTE PTR [BP-02]
pixelHeight       EQU  WORD PTR [BP-04]
imageHeight       EQU  WORD PTR [BP-06]
imageWidth        EQU  WORD PTR [BP-08]

RotateFontImages PROC NEAR
	PUSH	DS
	PUSH	BP
	MOV	BP, SP
	SUB	SP, 8	; leave room on stack for local variables

	LDS	SI, pSrcFontInfo

	MOV	AL, DS:[SI].fiOrientation
	MOV	curOrientation, AL	; Save current orientation as a local

	MOV	AX, DS:[SI].fiPixHeight
	MOV	pixelHeight, AX	; Save font char height as a local

	MOV	AL, DS:[SI].fiLastChar
	SUB	AL, DS:[SI].fiFirstChar	; Num chars = (last - first) + 1 {+ 1}
	ADD	AL, 2	; There is always one extra image at the end
	MOV	AH, 0
	PUSH	AX	; Save num of chars to rotate for later

	MOV	BX, WORD PTR DS:[SI].fiBitsData

	MOV	DX, DS:[SI].fiFileSize	; Save total size of fontInfo in DX
	LES	DI, pDstFontInfo	; Destination of copy and zero fill
	MOV	CX, SIZE FontInfoType
	DEC	CX	; Don't count first byte of charInfo (base)
	MOV	AX, CX	; Save number of bytes being copied in AX
	SHR	CX, 1	; Move words since header is even
	CLD		; Copy words and increment pointer
	REP	MOVSW	; Do the copy

	PUSH	DI	; Save beginning of charInfo for destination

	SUB	DX, AX	; Bytes remaining in dest font info
	MOV	CX, DX
	XOR	AX, AX	; Fill with zeros
	SHR	CX, 1	; Fill by words
	REP	STOSW	; Do the filling
	JNC	NotOdd	; If even boundary then done

	STOSB	; Fill the last odd byte

NotOdd:
	POP	DI	; Beginning of charInfo for destination
	POP	CX	; Number of characters to rotate

ImageLoop:
	PUSH	BX	; Save next location for char bit info
	PUSH	CX	; Save number of char images left to rotate
	LODSW	; Source characters width
	STOSW	; Dest character width same even if rotated
	XCHG	CX, AX
	MOV	DX, pixelHeight
	LODSW	; Source char's image info offset
	XCHG	BX, AX	; BX=Src image info offset; AX=Dst image info
	STOSW	; Dest characters image info offset
	TEST	curOrientation, 1	; If current orientation is north or south
	JZ	OrientationIsNorthSouth	; then width and height are correct

	XCHG	CX, DX	; Exchange width and height

OrientationIsNorthSouth:
	MOV	imageWidth, CX
	MOV	imageHeight, DX

	PUSH	SI	; Next src char's width/image info offset
	PUSH	DI	; Next dst char's width/image info offset

	MOV	SI, BX
	ADD	SI, WORD PTR pSrcFontInfo
	MOV	DI, AX
	ADD	DI, WORD PTR pDstFontInfo

	MOV	BH, 80H	; Mask
	MOV	CH, 0	; Rotate base
	XOR	DX, DX	; Initialize src height position index

	TEST	direction, 1	; rotateRight = 1; rotateLeft = 0
	JNZ	RotateRight

RotateLeft:
	SUB	SI, imageHeight	; Back up to compensate for initial add
	DEC	DI	; Back up one to compensate for initial inc

LHeightLoop:
	PUSH	DX	; Save current height position index
	TEST	DL, 7	; If dest is not on a byte boundary then
	JNZ	LNotOnByteBoundary1	; don't need to increment destination ptr

	ADD	DI, imageWidth	; Increment destination ptr

LNotOnByteBoundary1:
	PUSH	DI	; Save destination ptr
	PUSH	SI	; Save source ptr

	XOR	DX, DX	; Initialize src width position index

LWidthLoop:
	TEST	DL, 7	; If source is not on a byte boundary then
	JNZ	LNotOnByteBoundary2	; don't need to get next source byte

	MOV	CL, CH	; Rotate amt = rotate amt base
	ADD	SI, imageHeight	; Next column
	MOV	BL, DS:[SI]	; Next source byte

LNotOnByteBoundary2:
	MOV	AL, BL	; Copy of source byte
	ROL	AL, CL	; Rotate into position
	AND	AL, BH	; Mask out bit of interest
	OR	ES:[DI], AL	; OR it into destination location

	DEC	DI	; Point to next destination byte
	INC	CL	; Increment rotate amount
	AND	CL, 7	; Keep it in the range from 0 to 7

	INC	DX	; Next src width position
	CMP	DX, imageWidth	; If haven't done entire width
	JB	LWidthLoop	; Then go back and do next

	ROR	BH, 1	; Rotate mask to get next line of bits
	DEC	CH	; Decrement the rotate amount base
	AND	CH, 7	; Keep it in the range from 0 to 7

	POP	SI	; Saved source ptr
	INC	SI	; Next row
	POP	DI	; Saved dest ptr

	POP	DX	; Current height position index
	INC	DX	; Next position
	CMP	DX, imageHeight	; If haven't done entire height
	JB	LHeightLoop	; Then go back and do next
	JMP	NextCharImage

RotateRight:
	DEC	SI	; Back up one to compensate for initial inc
	SUB	DI, imageWidth	; Back up to compensate for initial add

RHeightLoop:
	PUSH	DX	; Save current height position index
	TEST	DL, 7	; If dest is not on a byte boundary then
	JNZ	RNotOnByteBoundary1	; don't need to increment destination ptr

	ADD	DI, imageWidth	; Increment destination ptr

RNotOnByteBoundary1:
	PUSH	DI	; Save destination ptr
	PUSH	SI	; Save source ptr

	XOR	DX, DX	; Initialize src width position index

RWidthLoop:
	TEST	DL, 7	; If source is not on a byte boundary then
	JNZ	RNotOnByteBoundary2	; don't need to get next source byte

	MOV	CL, CH	; Rotate amt = rotate amt base
	ADD	SI, imageHeight	; Next column
	MOV	BL, DS:[SI]	; Next source byte

RNotOnByteBoundary2:
	MOV	AL, BL	; Copy of source byte
	ROL	AL, CL	; Rotate into position
	AND	AL, BH	; Mask out bit of interest
	OR	ES:[DI], AL	; OR it into destination location

	INC	DI	; Point to next destination byte
	INC	CL	; Increment rotate amount
	AND	CL, 7	; Keep it in the range from 0 to 7

	INC	DX	; Next src width position
	CMP	DX, imageWidth	; If haven't done entire width
	JB	RWidthLoop	; Then go back and do next

	ROR	BH, 1	; Rotate mask to get next line of bits
	DEC	CH	; Decrement the rotate amount base
	AND	CH, 7	; Keep it in the range from 0 to 7

	POP	SI	; Saved source ptr
	DEC	SI	; Next row
	POP	DI	; Saved dest ptr

	POP	DX	; Current height position index
	INC	DX	; Next position
	CMP	DX, imageHeight	; If haven't done entire height
	JB	RHeightLoop	; Then go back and do next

NextCharImage:
	POP	DI	; Next dst char's width/image info offset
	POP	SI	; Next src char's width/image info offset

	POP	CX	; Number of chars left
	POP	BX	; Next dest char's bit image info offset
	DEC	CX
	JZ	RotateFontImagesRet

	MOV	AX, imageHeight
	ADD	AX, 7
	SHR	AX, 1
	SHR	AX, 1
	SHR	AX, 1
	MOV	DX, imageWidth
	MUL	DL
	ADD	BX, AX
	JMP	ImageLoop

RotateFontImagesRet:
	MOV	SP, BP
	POP	BP
	POP	DS
	RET	10
RotateFontImages ENDP

PURGE pSrcFontInfo, pDstFontInfo, direction


CODE ENDS

  END
