;******************************************************
;		   TPFAST.ASM 4.03
;	      Fast screen writing routines
;	 Copyright (c) TurboPower Software 1987.
; Portions copyright (c) Sunny Hill Software 1985, 1986
;     and used under license to TurboPower Software
;		 All rights reserved.
;******************************************************

	INCLUDE TPCOMMON.INC

;****************************************************** Data

DATA	SEGMENT BYTE PUBLIC

	;Pascal variables

	EXTRN	DirectVideo : BYTE		;If false, use BIOS
	EXTRN	WindMin : WORD			;Min. XY coordinates
	EXTRN	WindMax : WORD			;Max. XY coordinates
	EXTRN	CurrentPage : BYTE		;Current video page
	EXTRN	CurrentMode : BYTE		;Current video mode
	EXTRN	CurrentWidth : BYTE		;Current width of display
	EXTRN	CurrentHeight : BYTE		;Current height of display
	EXTRN	InTextMode : BYTE		;False if in graphics mode
	EXTRN	TextAttr : BYTE 		;Current video attribute
	EXTRN	NormalAttr : BYTE		;Attribute for NormVideo
	EXTRN	CheckSnow : BYTE		;If true, check for retrace
	EXTRN	VideoSegment : WORD		;Segment of Video Memory

DATA	ENDS


CODE	SEGMENT BYTE PUBLIC

	ASSUME	CS:CODE, DS:DATA

	PUBLIC	FastWrite, FastWriteWindow, FastText, FastRead
	PUBLIC	ChangeAttribute, MoveScreen

	EXTRN	MapColor : FAR			;Pascal routine

;****************************************************** CalcOffset

;calculate Offset in video memory.
;On entry, AX has Row, DI has Column
;On exit, CX and ES have VideoSegment, DI has offset,
; and DL = 1 if snow checking is needed

CalcOffset	PROC NEAR

	DEC	AX			;Row to 0..24 range
	MOV	CX, WP CurrentWidth	;CX = Rows per column
	MUL	CX			;AX = Row * CurrentWidth
	DEC	DI			;Column to 0..79 range
	ADD	DI,AX			;DI = (Row * CurrentWidth) + Col
	SHL	DI,1			;Account for attribute bytes
	MOV	CX,VideoSegment 	;CX = VideoSegment
	MOV	ES,CX			;ES:DI points to VideoSegment:Row,Col
	CLD				;Set direction to forward
	MOV	DL,CheckSnow		;Get snow check into DL
	CMP	DL,True 		;Is it set?
	JNE	CalcExit		;Exit if not
	CMP	CH,0B8h 		;Writing to CGA memory?
	JE	CalcExit		;Exit if so
	SetZero DL			;Otherwise turn snow checking off
CalcExit:
	RET				;Return

CalcOffset	ENDP

;****************************************************** FastWrite

;procedure FastWrite(St : String; Row, Col, Attr : Byte);
;Write St at Row,Col in Attr (video attribute) without snow

FWAttr		EQU	BYTE PTR SS:[BX+4]
FWCol		EQU	BYTE PTR SS:[BX+6]
FWRow		EQU	BYTE PTR SS:[BX+8]
FWSt		EQU	DWORD PTR SS:[BX+10]

FastWrite     PROC FAR

	StackFrame			;Set up stack frame
	PUSH	DS			;Save DS
	SetZero AH			;AH = 0
	MOV	AL,FWRow		;AX = Row
	SetZero CH			;CH = 0
	MOV	CL,FWCol		;CX = Column
	MOV	DI,CX			;DI = Column
	CALL	CalcOffset		;Call routine to calculate offset
	GetDSPtr	FWSt		;DS:SI points to St[0]
	SetZero CX			;CX = 0
	LODSB				;AL = Length(St); DS:SI -> St[1]
	MOV	CL,AL			;CX = Length
	JCXZ	FWExit			;If string empty, exit
	MOV	AH,FWAttr		;AH = Attribute
	SHR	DL,1			;If snow checking is off...
	JNC	FWNoWait		; use FWNoWait routine
	MOV	DX,03DAh		;Point DX to CGA status port
FWGetNext:
	LODSB				;Load next character into AL
					; AH already has Attr
	MOV	BX,AX			;Store video word in BX
	WaitForRetrace			;Wait for an opportunity to write
	WordToCGA	BX		;Move the word
	LOOP	FWGetNext		;Get next character
	JMP	SHORT FWExit		;Done
FWNoWait:
	LODSB				;Load next character into AL
					; AH already has Attr
	STOSW				;Move video word into place
	LOOP	FWNoWait		;Get next character
FWExit:
	POP	DS			;Restore DS
	RET	10			;Remove parameters and return

FastWrite	ENDP

;****************************************************** FastWriteWindow

;procedure FastWriteWindow(S : string; Row, Col, Attr : Byte);
;Write a string using window-relative coordinates.

FastWriteWindow PROC FAR

	StackFrame			;Set up stack frame
	MOV	AL,FWAttr		;Load Attribute into AL
	PUSH	AX			;PUSH parameter onto stack
	CALL	MapColor		;Call color mapping routine
	StackFrame			;Set up stack frame again
	MOV	FWAttr,AL		;Reload Attr with mapped attribute
	MOV	AL,FWRow		;AL = Row
	ADD	AL,WindMin.YLow 	;Adjust for current window
	MOV	FWRow,AL		;Reload Row
	MOV	AL,FWCol		;AL = Col
	ADD	AL,WindMin.XLow 	;Adjust for current window
	MOV	FWCol,AL		;Reload Col
	JMP	FastWrite		;Let FastWrite do the rest

FastWriteWindow ENDP

;****************************************************** FastText

;procedure FastText(St : string; Row, Col : Byte);
;Write St at Row,Col without changing the underlying video attribute.

FTCol		EQU	BYTE PTR SS:[BX+4]
FTRow		EQU	BYTE PTR SS:[BX+6]
FTStr		EQU	DWORD PTR SS:[BX+8]

FastText	PROC FAR

	StackFrame			;Set up stack frame
	PUSH	DS			;Save DS
	SetZero AH			;AH = 0
	MOV	AL,FTRow		;AX = Row
	SetZero CH			;CH = 0
	MOV	CL,FTCol		;CX = Column
	MOV	DI,CX			;DI = Column
	CALL	CalcOffset		;Call routine to calculate offset
	GetDSPtr	FTStr		;DS:SI points to St[0]
	SetZero CX			;CX = 0
	LODSB				;AL = Length(St); DS:SI -> St[1]
	MOV	CL,AL			;CX = Length
	JCXZ	FTExit			;If string empty, exit
	SHR	DL,1			;If snow checking is off...
	JNC	FTNoWait		; use FTNoWait routine
	MOV	DX,03DAh		;Point DX to CGA status port
FTGetNext:
	LODSB				;Load next character into AL
					; AH already has Attr
	MOV	AH,AL			;Store char in AH
	WaitForRetrace			;Wait for an opportunity to write
	MOV	AL,AH			;Move char back to AL
	STOSB				;And then to screen
	STI				;Allow interrupts
	INC	DI			;Skip attribute bytes
	LOOP	FTGetNext		;Get next character
	JMP	SHORT FTExit		;Done
FTNoWait:
	MOVSB				;Move character to screen
	INC DI				;Skip attribute bytes
	LOOP	FTNoWait		;Get next character
FTExit:
	POP	DS			;Restore DS
	RET	8			;Remove parameters and return

FastText	ENDP

;****************************************************** FastRead

;procedure FastRead(Number, Row, Col : Byte; var St : string);
;Read Number bytes from the screen into St starting at Row,Col without snow

FRSt		EQU	DWORD PTR SS:[BX+4]
FRCol		EQU	BYTE PTR SS:[BX+8]
FRRow		EQU	BYTE PTR SS:[BX+10]
FRNum		EQU	BYTE PTR SS:[BX+12]

FastRead      PROC FAR

	StackFrame			;Set up stack frame
	PUSH	DS			;Save DS
	SetZero AH			;AH = 0
	MOV	AL,FRRow		;AX = Row
	SetZero CH			;CH = 0
	MOV	CL,FRCol		;CX = Column
	MOV	DI,CX			;DI = Column
	CALL	CalcOffset		;Call routine to calculate offset
	MOV	DS,CX			;CX still has VideoSegment
	MOV	SI,DI			;DS:SI points to VideoSegment:Row,Col
	GetPtr	FRSt			;ES:DI points to St[0]
	SetZero AH			;AH = 0
	MOV	AL,FRNum		;AX = number of bytes to read
	STOSB				;Set length byte
	MOV	CX,AX			;CX = Length
	JCXZ	FRExit			;If string empty, exit
	SHR	DL,1			;If snow checking is off...
	JNC	FRNoWait		; use FWNoWait routine
	MOV	DX,03DAh		;Point DX to CGA status port
FRGetNext:
	WaitForRetrace			;Wait for an opportunity to write
	LODSB				;Load next char into AX
	STI				;Allow interrupts
	STOSB				;Store the character in St
	INC	SI			;Skip attribute
	LOOP	FRGetNext		;Get next character
	JMP	SHORT FRExit		;Done
FRNoWait:
	LODSW				;Load next word into AX
	STOSB				;Move character into St
	LOOP	FRNoWait		;Get next character
FRExit:
	POP	DS			;Restore DS
	RET	10			;Remove parameters and return

FastRead      ENDP

;****************************************************** ChangeAttribute

;procedure ChangeAttribute(Number : Word; Row, Col, Attr : Byte);
;Change Number video attributes to Attr starting at Row,Col

CAAttr		EQU	BYTE PTR SS:[BX+4]
CACol		EQU	BYTE PTR SS:[BX+6]
CARow		EQU	BYTE PTR SS:[BX+8]
CANumber	EQU	WORD PTR SS:[BX+10]

ChangeAttribute       PROC FAR

	StackFrame			;Set up stack frame
	SetZero AH			;AH = 0
	MOV	AL,CARow		;AX = Row
	SetZero CH			;CH = 0
	MOV	CL,CACol		;CX = Column
	MOV	DI,CX			;DI = Column
	CALL	CalcOffset		;Call routine to calculate offset
	INC	DI			;Skip character
	MOV	AL,CAAttr		;AL = Attribute
	MOV	CX,CANumber		;CX = Number to change
	JCXZ	CAExit			;If zero, exit
	SHR	DL,1			;If snow checking is off...
	JNC	CANoWait		; use CANoWait routine
	MOV	AH,AL			;Store attribute in AH
	MOV	DX,03DAh		;Point DX to CGA status port
CAGetNext:
	WaitForRetrace			;Wait for an opportunity to write
	MOV	AL,AH			;Move Attr back to AL...
	STOSB				; and then to screen
	STI				;Allow interrupts
	INC	DI			;Skip characters
	LOOP	CAGetNext		;Look for next opportunity
	JMP	SHORT CAExit		;Done
CANoWait:
	STOSB				;Change the attribute
	INC	DI			;Skip characters
	LOOP	CANoWait		;Get next character
CAExit: 				;Next instruction
	RET	8			;Remove parameters and return

ChangeAttribute       ENDP

;****************************************************** MoveScreen

;procedure MoveScreen(var Source, Dest; Length : Word);
;Move Length words from Source to Dest without snow

MLength 	EQU	WORD PTR SS:[BX+4]
MDest		EQU	DWORD PTR SS:[BX+6]
MSource 	EQU	DWORD PTR SS:[BX+10]

MoveScreen	PROC FAR

	StackFrame			;Set up stack frame
	PUSH	DS			;Save DS
	SetZero AH			;AH = 0
	MOV	AL,CheckSnow		;Grab before changing DS
	GetPtr		MDest		;ES:DI points to Dest
	GetDSPtr	MSource 	;DS:SI points to Source
	MOV	CX,MLength		;CX = Length
	JCXZ	MSExit			;Exit if CX = 0
	CLD				;Assume forward
	MOV	BX,DS			;BX = DS
	MOV	DX,ES			;DX = ES
	CMP	DX,BX			;Same segment?
	JNE	MSForward		;If not, go forward
	CMP	SI,DI			;Check for potential overlap
	JAE	MSForward		;Go forward if Source at higher offset

	STD				;Go backwards
	DEC	CX			;CX = Number of words to add to SI/DI
	ADD	DI,CX			;Point DI to end of Dest area
	ADD	DI,CX
	ADD	SI,CX			;Point SI to end of Source area
	ADD	SI,CX
	INC	CX			;Reset CX
	INC	AH			;Flag to indicate we're going backward

MSForward:
	SHR	AL,1			;Snow checking on?
	JNC	MSNoWait		;Skip the rest of this if not
	CMP	BH,0B8h 		;See if we're reading from CGA memory
	JE	MSGetNext		;If so, wait for retrace
	CMP	DH,0B8h 		;Check segment in ES, too
	JNE	MSNoWait		;Not writing to CGA
MSGetNext:
	MOV	DX,03DAh		;Point DX to CGA status port

IF SuppressAllSnow

	OR	AH,AH			;Going forward?
	JZ	MSnotback		;If so, continue
	INC	SI			;Else, point SI/DI to last byte, rather
	INC	DI			; than the last word
MSnotback:

ENDIF

	WordMoveNoSnow			;Move CX words, preventing snow
	JMP	SHORT MSExit		;All done
MSNoWait:
	REP	MOVSW			;That's all!
MSExit:
	CLD				;Reset direction flag
	POP	DS			;Restore DS
	RET	10			;Remove parameters and return

MoveScreen	ENDP

CODE	ENDS

	END
