	IF2
		%OUT [Pass 1 Completed]
	ENDIF


PUBLIC  Ctrl_Character, CBack, InsertCRLF, SaveEnd , $NoUpdate
PUBLIC  CCenter, CDelete, .Msg, .CLRMsg, $Wait, CYSave, CDelK, $MsgAdd
PUBLIC	.InvMsg

EXTRN	.OutCH:Near, FModeline:Near, .OutStr:Near, .Piss:Near, .InCH:Near
EXTRN	$History:Byte, RSearch:Near, ISearch:Near, .Error0:Near
EXTRN	$WorkSpaceSegment:Word, $WorkSpaceEnd:Word, .?Col:Near, FHELP:Near
EXTRN	$Repeat:Word, $Repeat:Word, $KBufPtr:Word, CRUBOUT:NEAR
EXTRN	NumWinsScr:Word, FillParagraph:Near, GetColumn:Near
EXTRN	NextScreen:NEAR, ScrollUp:NEAR


;---------------------------------------------------------------------------
	; NOTE FOR ADAPTATIONS:
	;
	; The following routines inside CTRL-C(haracters) directly manipulate
	; the screen:
	;
	;	.MSG		prints a message in the message-field area
	;	.INVMSG		prints a message in reverse video
	;	.CLRMSG		clears the message-field area
	;
	;	[The message-field area is below the modeline and above]
	;	[the last line]


;---------------------------------------------------------------------------
TITLE Control Command Handler

	; The routines in this module execute commands invoked by
	; pressing CTL and another character simultaneously, e.g. CTL-@,
	; CTL-A, CTL-B ...

Program SEGMENT PARA	PUBLIC  'code'
	ASSUME  CS:Program, DS:Program, ES:Program, SS:Program

; <----------------------------------------------------------------------->

	INCLUDE FDEF.DEF
	$Free	=	(Offset CInvalid)

	$NoUpdate	dw	0	; not used ?

	SaveEnd		dw	0	; holds end of yankback buffer

	$Wait		dw	30	; holds time interval of msg display
	$MsgAdd		dw	0	; remembers where the next msg
					; should go to the screen ( append )

	Error1Msg	db	'*** FileStart ***'
	Error2Msg	db	'*** FileEnd ***'
	CInvalidT0	db	'*** CTL-'
	CInvalidT1	db	'? unknown ***'


; <---------------------------- Jump Table ----------------------------->
CJTable	dw	Offset SetMark		; ^@
	dw	Offset CBegl		; ^A
	dw	Offset CBack		; ^B
	dw	$Free			; ^C
	dw	Offset CDelete  	; ^D
	dw	Offset CEndL		; ^E
	dw	Offset CForward 	; ^F
	dw	$Free			; ^G
	dw	Offset CRubout		; ^H = Help
	dw	Offset TabIn		; ^I
	dw	$Free			; ^J
	dw	Offset CKill		; ^K
	dw	Offset CCenter  	; ^L
	dw	Offset InsertCRLF
	dw	Offset CNext		; ^N
	dw	Offset CTL_O		; ^O
	dw	Offset CPrevious  	; ^P
	dw	Offset LitIns		; ^Q
	dw	Offset RSearch  	; ^R
	dw	Offset ISearch  	; ^S
	dw	Offset BExchange	; ^T
	dw	$Free			; ^U
	dw	Offset NextScreen 	; ^V
	dw	Offset CRegKill 	; ^W
	dw	$Free			; ^X
	dw	Offset CYank		; ^Y
	dw	Offset Scrollup		; ^Z
	dw	$Free
	dw	$Free
	dw	$Free
	dw	$Free
	dw	Offset CHELP		; ^_
	dw	$Free


;***************************************************************************
	; The following routine directs the caller to the correct routine
	; by using above jump table

Ctrl_Character  PROC	Near

CParse: mov	AL, DL			; adjust AL to a 16bit pointer in
	xor	AH, AH			; the table
	mov	BX, AX
	add	BX, BX
	jmp	CS:CJTable [BX]

CInvalid:				; ohoh - table held INVALID
	add	 AL, 40h		; adjust the character to ASCII
	mov	CS:CInvalidT1, AL	; put it into the msg to be sent
	mov	SI, Offset CInvalidT0	; and announce the error
	mov	CX, 21
	jmp	.Error0

Ctrl_Character  ENDP

;***************************************************************************
;----------------------------Ctrl Functions---------------------------------
	; SETMARK sets the mark at the current cursor position

	SetMarkT	db	'[Mark Set]'

SetMark PROC	Near

	mov	AX, DS:BCB.FCursor		; get cursor position
	mov	DS:BCB.Mark, AX			; save as new mark
	mov	SI, Offset SetMarkT
	mov	CX, 10
	jmp	.Msg

SetMark ENDP

;---------------------------------------------------------------------------
	; The following routine moves the cursor back by one character

CBack	PROC	Near

	mov	BX, DS:BCB.FCursor		; load cursor value
	cmp	BX, 100h			; beginning of file ?
	jbe	CBackErr			; then error
	dec	BX				; go back and check if on LF
	cmp	Byte Ptr DS:[BX], Byte Ptr LF	; if so go back again
	jne	CBack0
	dec	BX
CBack0: mov	DS:BCB.FCursor, BX		; and save new cursor
	ret

CBackErr:mov	SI, Offset Error1Msg		; display an error message
	mov	CX, 17
	jmp	.Error0

CBack	ENDP

;---------------------------------------------------------------------------
	; insert a new line after the cursor position

CTL_O	PROC	NEAR

	call	InsertCRLF			; insert a CRLF, and go back
	call	.Piss
	jmp	CBack

CTL_O	ENDP

;---------------------------------------------------------------------------
	; advances cursor by one [see CBackward for comments]

CForward	PROC	NEAR
	mov	BX, DS:BCB.FCursor
	cmp	BX, DS:BCB.FEnd
	jae	CForwardErr	
	inc	BX
	cmp	Byte Ptr DS:[BX], Byte Ptr LF
	jne	CForward0
	inc	BX
CForward0:
	mov	DS:BCB.FCursor, BX
	ret

CForwardErr:
	mov	SI, Offset Error2Msg
	mov	CX, 15
	jmp	.Error0

CForward	ENDP

;---------------------------------------------------------------------------
	; advances the cursor to the next line

CNext	PROC	NEAR

	mov	BX, Offset $History		; check whether last command
	inc	BX				; was a CTL-P or CTL-N
	cmp	Byte Ptr CS:[BX], 16
	je	CNext0
	cmp	Byte Ptr CS:[BX], 14
	jne	CNext1

CNext0:						; is consecutive CTL-N/P
	mov	AX, DS:BCB.FOldCurCol
	mov	CS:CDesiredCol, AX		; this is where we want to be
	jmp	CNext2

CNext1:						; is a first CTL-N/P
	mov	BX, DS:BCB.FCursor		; so get where it is
	call	GetColumn
	mov	DS:BCB.FOldCurCol, CX		; and remember it for another
	mov	CS:CDesiredCol, CX		; next or previous line after



	; now we know the desired column we want to get to

CNext2:	mov	BX, DS:BCB.FCursor

CNC1:	cmp	Byte Ptr [BX], LF	; loop until we are at end of file
	je	CNC2			; or at LF
	cmp	BX, DS:BCB.Fend
	jae	CNC2
	inc	BX
	jmp	CNC1

CNC2:	cmp	BX, DS:BCB.FEnd		; don't allow CTL-N at least line
	jae	CNErr


CNC3:	inc	BX			; now we are at column 1 of the
					; next line.

	cmp	Word Ptr DS:[BX], CRLF	; if we hit the end of a line,
	je	FoundPosition		; 	[this is all we can do
	cmp	BX, DS:BCB.FEnd		; the fileend is also the end of all
	jae	FoundPosition		; 	[efforts
	call	GetColumn		; check if we have reached the desired
	cmp	CX, CS:CDesiredCol	;	[column
	jae	FoundPosition
	jmp	CNC3			; and advance if we haven't yet


CNErr:  mov	SI, Offset Error2Msg
	mov	CX, 15
	jmp	.Error0

FoundPosition:
	mov	DS:BCB.Fcursor, BX
	ret
CNext	ENDP



	CDesiredCOl	dw	0	



;---------------------------------------------------------------------------
	; This routine [improvable] moves the cursor to the previous line.
	; ( for comments please see CTL-N [CNext] )

CPrevious	PROC	NEAR

	mov	BX, Offset $History
	inc	BX
	cmp	Byte Ptr CS:[BX], 16
	je	CPrevious0
	cmp	Byte Ptr CS:[BX], 14
	jne	CPrevious1
CPrevious0:					; is a consecutive CTL-N/P
	mov	AX, DS:BCB.FOldCurCol
	mov	CS:CdesiredCol, AX		; this is where it shall be
	jmp	CPreviousCont

CPrevious1:					; is a first CTL-N/P
	mov	BX, DS:BCB.FCursor		; so get where it is
	call	GetColumn
	mov	DS:BCB.FOldCurCol, CX		; and remember it for another
	mov	CS:CDesiredCol, CX

CPreviousCont:
	mov	BX, DS:BCB.FCursor

CPC1:	cmp	Byte Ptr [BX], LF	; loop until we are at start of file
	je	CPC2			; or at LF
	cmp	BX, 0FFh
	je	CPC2
	dec	BX
	jmp	CPC1

CPC2:	dec	BX
	cmp	BX, 100h		; error at line 1
	jb	CPreviousErr

CPC3:	cmp	Byte Ptr [BX], LF	; loop until we are at start of file
	je	CPC4			; or at LF again, this is our new line
	cmp	BX, 0FFh
	je	CPC4
	dec	BX
	jmp	CPC3

CPC4:	inc	BX			; now we are at column 1 of the
					; line.
	cmp	Word Ptr DS:[BX], CRLF	; if we hit the end of a line,
	je	FoundPosition		; this is all we can do
	call	GetColumn		; check if we have reached it
	cmp	CX, CS:Cdesiredcol
	jae	FoundPosition
	jmp	CPC4			; and advance if we haven't


CPreviousErr:
	mov	SI, Offset Error1Msg
	mov	CX, 17
	jmp	.Error0

CPrevious	ENDP


;---------------------------------------------------------------------------
	; This routine inserts a CRLF when CR is pressed

InsertCRLF	PROC	NEAR

	mov	DS:BCB.Dirty, TRUE
	test	DS:BCB.EDMode, Wrapmode
	jz	NormalCRLF
	mov	BX, DS:BCB.FCursor
	cmp	Word Ptr DS:[BX-2], CRLF
	je	NormalCRLF			; opening a new paragraph
	cmp	BX, 100h
	jbe	NormalCRLF
	call	FillParagraph
NormalCRLF:
	mov	BX, CS:$KBufPtr
	mov	Byte Ptr CS:[BX], CR
	inc	BX
	mov	Byte Ptr CS:[BX], LF
	inc	BX
	mov	CS:$KBufPtr, BX
	call	.Piss
	ret

InsertCRLF	ENDP


;---------------------------------------------------------------------------
	; CCenter centers the line on which the cursor is. It is not
	; 100% correct, but will miss by +/- 1 line in strange situations.
	; The advantage is much simpler code and much faster execution.


DefCenter	EQU	 10
LineLength	EQU	 79


CCenter	PROC	NEAR
	mov	CX, CS:NumWinsScr	; find out what a centered line
					; means (could be one or two windows)
	xor	CX, 1			; 1<>0 = n*6+4
	add	CX, CX			; *2
	mov	AX, CX
	add	CX, CX			; *4
	add	CX, AX			; *6
	add	CX, 3			; 10 or 6

	cmp	CS:$Repeat, 0		; now reinterpret the argument
	je	.CC1
	cmp	CS:$Repeat, 1
	jne	.CC0
		mov	CS:$Repeat, 0	; use this line to center
		mov	 BX, DS:BCB.FCursor
		jmp	 .CCGoStart

.CC0:	mov	 CX, CS:$Repeat
	mov	 CS:$Repeat, 0

.CC1:
			mov	 BX, DS:BCB.FCursor

; go back CX physical lines | If (BX<=100h)  -> ERROR

.CCGoBack:	mov	AX, LineLength
			.CCPhysBack:
				dec	 BX
				cmp	 Byte Ptr [BX], LF
				je		.CCPhysBackEnd
				dec	 AX
				jnz	 .CCPhysback
			.CCPhysBackEnd:
		cmp	Byte Ptr [BX], LF
		jne	.CCPhysB1
		dec	BX			; point to the CR ( also on this line )
		.CCPhysB1:
		cmp	BX, 100h
		jbe	.CCErr
		loop	.CCGoBack

; BX points now to a character on this physical line
.CCGoStart:

		call	GetColumn		; BX holds cursor

		call	Findcolumn1		; returns in DX what the real
						; column should be

.CCGoLoop:	call	GetColumn		; this will go back until
		cmp	CX, DX			; it hits the real start
		jbe	AtLineStart
		dec	BX
		jmp	.CCGoLoop

ATLineStart:	mov	DS:BCB.ScrStart, BX
		cmp	CX, DX
		jb	HitTabLine
		ret
HitTabLine:	inc	BX
		ret

.CCErr:		mov	DS:BCB.ScrStart, 100h
		ret

;...........................................................................
	; CX holds a column - I want to know the column number of
	; the first character on this line.

FindColumn1	PROC	NEAR
	mov	DX, 1

FAgain:	add	DX, 79
	cmp	DX, CX
	jbe	FAgain
	sub	DX, 79		; return the real start
	ret
FindColumn1	ENDP
CCenter ENDP


;---------------------------------------------------------------------------
	; CDelete deletes one character

CDelete PROC	NEAR

	mov	DS:BCB.Dirty, TRUE		; buffer is modified

	xor	CX, CX
	mov	DX, DS:BCB.FEnd			; first check whether
	mov	BX, DS:BCB.FCursor		; we are at the end
	cmp	BX, DX				; of the buffer
	jae	CDeleteErr
	cmp	Byte Ptr DS:[BX], CR		; then check whether we are at
	jne	CDelete0			; a lineend = delete 2 chars
	inc	CX

CDelete0:
	inc	CX				; well one has to be deleted
	mov	SI, BX				; Cursor
	mov	DI, SI				; Cursor + #chars to be del
	add	SI, CX
	sub	DS:BCB.FEnd, CX			; of course our file shrinks
	mov	CX, DX				; by 1/2 chars | calculate
	sub	CX, SI				; how many bytes have to be
	inc	CX				; moved
	cld
	mov	AX, DS				; use the correct register
	mov	ES, AX
	clc					; and set up word, instead of
	shr	CX, 1				; slower byte move
	inc	CX
	rep	movsw

	ret

CDeleteErr:
	mov	SI, Offset Error2Msg
	mov	CX, 15
	jmp	.Error0

CDelete ENDP

;---------------------------------------------------------------------------
	; TABIN inserts a TAB ( ^I ) into the buffer.

Tabin	PROC	NEAR

	mov	BX, CS:$KBufPtr		; insert a "real" tab
	mov	Byte Ptr CS:[BX], 9
	inc	BX
	mov	CS:$KBufPtr, BX
	jmp	.Piss
	ret
	
	call	.Piss			; get rid of any leftover characters
	call	.?col			; which may not be necessary
	mov	DL, ' '			; calculate how many spaces should
	mov	AX, DS:BCB.FCurCol	; be inserted
	dec	AX
	and	AX, 111b		; which is AX:= AX mod 8
	mov	CX, 8
	sub	CX, AX

	mov	BX, CS:$KBufPtr

TabIn0: mov	Byte Ptr CS:[BX], ' '	; now insert our spaces
	inc	BX
	loop	TabIn0

	mov	CS:$KBufPtr, BX

	jmp	.Piss			; and output them

TabIn	ENDP

;---------------------------------------------------------------------------
	; CBEGL moves the cursor to the linestart


CBegL	PROC	NEAR

	mov	BX,DS:BCB.FCursor
CBegLoop:
	cmp	Byte Ptr [BX], LF	; loop until we are at start of file
	je	CBegFound		; or at LF
	cmp	BX, 0FFh
	je	CBegFound
	dec	BX
	jmp	CBegLoop

CBegFound:
	inc	BX			; and save the cursor
	mov	DS:BCB.FCursor, BX
	ret

CBegL	ENDP

;---------------------------------------------------------------------------
	; CENDL moves the cursor to the end of the line

CEndL	PROC	NEAR

	mov	BX,DS:BCB.FCursor
CEndLLoop:
	cmp	Byte Ptr [BX], CR
	je	CEndFound
	cmp	BX, DS:BCB.FEnd
	jae	CEndFound
	inc	BX
	jmp	CEndLLoop

CEndFound:
	mov	DS:BCB.FCursor, BX
	ret

CEndL	ENDP

;---------------------------------------------------------------------------
	; This routines exchanges two characters

BExchange	PROC	NEAR 

	mov	DS:BCB.Dirty, TRUE	; buffer is modified

	mov	BX, DS:BCB.FCursor	; load cursor
	cmp	BX, 100h		; if it is start of file, then error
	jbe	BExchangeErr

	mov	AL, DS:[BX]		; if it is end of line, then special
	cmp	AL, CR			; is done to reverse last two chars
	je	BEx1			; which allows typing correction

	mov	AH, DS:[BX-1]		; if we are at start of a line,
	cmp	AH, LF			; then we have an error, too
	je	BExchangeErr

	mov	DS:[BX-1], AL		; exchange current and last char
	mov	DS:[BX], AH
	cmp	BX, DS:BCB.FEnd		; check whether we are at FileEnd
	jae	BExEnd1			; if so, don't advance
	inc	DS:BCB.FCursor		; otherwise do advance
BExEnd1:ret

BEx1:	cmp	BX, 101h		; we are at eoln, which says to
	jbe	BExchangeErr		; exchange last two -1 chars
	mov	AL, DS:[BX-1]		; of course check for all errors etc.
	cmp	AL, LF
	je	BExchangeErr
	mov	AH, DS:[BX-2]
	cmp	AL, LF
	je	BExchangeErr
	mov	DS:[BX-2], AL
	mov	DS:[BX-1], AH
	ret

BExchTM db	'*** LineStart ***'
BExchangeErr:
	mov	CX, 18
	mov	SI, Offset BExchTM
	jmp	.Error0

BExchange	ENDP

;---------------------------------------------------------------------------
	; This routine inserts a literal character

	C_QMsg  db	'CTL-Q '

LitIns  PROC	NEAR

	mov	DS:BCB.Dirty, TRUE	; buffer is modified

	call	.CLRMsg			; put up the CTL-Q message
	mov	SI, Offset C_QMsg
	mov	CX, 6
	call	.Msg

LInfLoop:call	.InCH			; get a character
	jz	LInfLoop

LitIns1:cmp	AL, 1Ah			; EOF is an invalid character to
	je	LitInsErr1		; insert
	cmp	AL, LF			; LF would confuse too many functions
	je	LitInsErr2

	push	AX
	mov	DL, AL			; clear the screen
	call	.CLRMsg
	pop	AX

	mov	BX, CS:$KBufPtr		; and insert it into the real buffer
	mov	Byte Ptr CS:[BX], AL
	inc	BX
	mov	CS:$KBufPtr, BX
	jmp	.Piss


LitInsErr1T	db	'*** Ctrl-Z Invalid ***'
LitInsErr2T	db	'*** Ctrl-J Invalid ***'
LitInsErr1:
	mov	SI, Offset LitInsErr1T
	jmp	LitInsErrC
LitInsErr2:
	mov	SI, Offset LitInsErr2T
LitInsErrC:
	mov	CX, 22
	jmp	.Error0

LitIns  ENDP


;------------------------------------------------------------------------
	; Calls the help routine. Once upon a time, this could take a
	; number from the last error or environment, and directly jump
	; to the appropriate help message.

CHelp	PROC	NEAR
	jmp	FHelp
	ret
CHELP	ENDP

;---------------------------------------------------------------------------
	; CYANK yanks back from the current workspacesegment ( byte 0 to
	; cs:saveend ) into the main buffer.

CYank	PROC	NEAR

	mov	DS:BCB.Dirty, TRUE
	mov	ES, CS:$WorkSpaceSegment ; ES:SI (CX) define the string length
	xor	SI, SI
	mov	CX, CS:SaveEnd

	call	.OutSTR			; outstr will do the rest
	ret

CYank	ENDP

;---------------------------------------------------------------------------
	; KILL kills up to the end of the line.

CKill	PROC	NEAR

	mov	SI, DS:BCB.FCursor	; load the cursor

	mov	BX, SI
	xor	CX, CX
	cmp	BX, DS:BCB.FEnd		; if it is on the fileend, then do
	jae	CKillErr		; nothing

	mov	AL, DS:[BX+1]		; if we are at the end of a line,
	cmp	AL, LF			; then just kill the CRLF
	je	CKA

CKLoop: inc	BX			; otherwise, kill everything up to
	mov	AL, DS:[BX]		; the CR
	cmp	AL, LF
	je	CKFound
	inc	CX
	cmp	AL, EOF
	jne	CKLoop
					; SI = Start  CX = Number of character
CKFound:call	CYSave			; now save in the yank back buffer
	jmp	CDELK			; and delete it from the file

CKA:	mov	CX, 2			; this is a CRLF to save
	call	CYSave			; so save it
	jmp	CDELK			; and delete it

CKillErr:
	mov	SI, Offset Error2Msg
	mov	CX, 15
	jmp	.Error0

CKill	ENDP

;---------------------------------------------------------------------------
	; This routine will kill the entire region

	CRegKillTE	db	'*** Cursor on Mark ***'

CRegKill	PROC	Near
	mov	SI, DS:BCB.Mark		; get the mark
	mov	AX, DS:BCB.FCursor	; get the cursor
	cmp	AX, SI			; if they are the same, there is no
	je	CRegKillErr		; region, if they are reversed, change
	ja	CRegKill0		; them.
	xchg	AX, SI
CRegKill0:
	mov	DS:BCB.FCursor, SI	; this is our new cursor after kill
	mov	CX, AX			; this is our region length
	sub	CX, SI

	call	CYSaveNewKill		; this will have to be a new save
	jmp	CDELK			; and. after saving, delete it.

CRegKillErr:
	mov	SI, Offset CRegKillTE
	mov	CX, 22
	jmp	.Error0

CRegKill	ENDP

;---------------------------------------------------------------------------
	; This procedure will delete, starting at ES:SI, CX characters

CDelK	PROC	Near

	mov	DS:BCB.Dirty, TRUE
	mov	DI, SI
	add	SI, CX
	sub	DS:BCB.FEnd, CX

	add	CX, DS:BCB.FEnd
	sub	CX, SI
	inc	CX
	cld
	mov	AX, DS
	mov	ES, AX
	clc
	shr	CX, 1
	inc	CX
	rep	movsw
	ret

CDELK	ENDP

;---------------------------------------------------------------------------
	; This procedure will first check whether it should add to the old
	; save buffer, or whether to create a new one. Then it saves the
	; area in the workspace segment

CYSave  PROC	Near

	mov	BX, Offset $History	; First it checks whether this and
	cmp	Byte Ptr CS:[BX+1], 11  ; previous command was CTL-K
	jne	CYSavenewkill
MayCYSadd:
	cmp	Byte Ptr CS:[BX], 11	; how about old one ?
	je	CYSadd

CYSavenewkill:
	cmp	CX, CS:$WorkSpaceEnd	; will kill fit into our segment ?
	jae	CYSOF

	xor	DI, DI			; yes, then the length of our new
	mov	CS:SaveEnd, CX		; save is what we insert
CYLbl:  push	CX			; which we will do right here
	push	SI
	mov	ES, CS:$WorkSpaceSegment
	rep	movsb
	pop	SI
	pop	CX
	ret

CYSadd: mov	DI, CS:SaveEnd		; will additional stuff still fit ?
	add	DI, CX
	cmp	DI, CS:$WorkSpaceEnd
	jae	CYSOF
	sub	DI, CX			; reget additional characters ( not
	add	CS:Saveend, CX		; necessary I think
	jmp	CYLbl			; and save it

CYSOFT  db	'*** Kill Space Exhausted ***'
CYSOF:  pop	AX	 ; return to upper level ( avoid call CDELK )
	mov	CX, 28
	mov	SI, Offset CYSOFT
	jmp	.Error0

CYSave  ENDP



;***************************************************************************
;				SCREEN
;
;	The following routines put up messages below the modeline
;
;---------------------------------------------------------------------------

MsgIntens	db	Lowintensity	; holds the color of the next msg

;...........................................................................
.InvMsg	PROC	NEAR
	mov	CS:MsgIntens, Inverse	; this will set the default msg
	jmp	.Msg			; intensity to inverse, and call
					; the standard message.
.InvMsg	ENDP
;...........................................................................
.Msg	PROC	Near
	push	SI			; save all
	push	CX
	pushf
	cld				; insert strings direction forward

	mov	AX, Screensegment	; sets the screen segment
	mov	ES, AX

	mov	DI, CS:$MsgAdd		; CS:$MsgAdd holds the end of the
	add	DI, StartofMsgField	; last message: DI holds now the
					; start of the message pointer

dummy	Label	Byte
.msg1:	lods	CS:dummy		; get a byte from the message
	mov	AH, CS:MsgIntens	; what is the msg's color ?
	stosw				; put a word with the character in
					; AL and the color in AH to the screen

	cmp	DI, EndofMsgfield-5	; check whether we have reached
	jae	.MAgain			; the screen end
		loop	.msg1		; no, so put other bytes to the scr

					; msg is on scr now

	sub	DI, StartofMsgField-6	; remember where this message ended
	mov	CS:$MsgAdd, DI
	mov	CS:MsgIntens, Lowintensity	; clear the default color

	popf				; restore the registers
	pop	CX
	pop	SI

					; and assign a wait state of 15 sec
	mov	CS:$Wait, Word Ptr 15	; before erasing screen messages
	ret




	; The following routine, .MAgain is called when a msg does not fit at
	; the end after the previous message. It clears the msg-area, and
	; attempts to put up the message now at the start of the msg area


.MOFMsg db	'*** Message Line Overflow ***'
.MAgain:call	.CLRMsg			; clear the screen

	popf
	pop	CX
	pop	SI

	cmp	CX, 75
	jbe	.MAg2			; if the message is larger than an
					; entire line, then ...

	mov	CX, 29			; ... report an error instead
	mov	SI, Offset .MOfMsg
	jmp	.Error0
.MAg2:  jmp	.Msg
.Msg	ENDP

;...........................................................................
	; CLRMSG clears the message area

.ClrMsg		 PROC	Near
	push	ES			; This will clear the message line
	push	DI
	mov	AX, Screensegment	; get screen segment
	mov	ES, AX
	mov	DI, StartofMsgField	; get message line start
	mov	AX, ' '			; get a space character
	mov	CX, (EndofMsgField-StartofMsgField)/2 ; and put it n times
					; to the screen
	rep	stosw
	mov	CS:$Wait, 0FFFFh	; wait forever until we reerase
	mov	CS:$MsgAdd, 0		; and remember that there is nothing
	pop	DI			; on message line
	pop	ES
		ret
.CLRMsg		 ENDP



	PROGRAM ENDS
END
