;++
;
; Cheapskate formatter.  Vaguely similar to RUNOFF but not at all compatible.
;
; By John Wilson.
;
; Reads from STDIN, writes to STDOUT.
;
; 13-Dec-1990	JMBW	Created.
; 02-Jul-1994	JMBW	Added .PAGE.
; 21-May-2000	JMBW	Added _ and # characters (_ only works on #).
;
;--
	.radix	8
;
bs=	10
lf=	12
ff=	14
cr=	15
;
nxspc=	377			;non-expandable space substitute char
;
code	segment
	assume	cs:code
;
start:	; begin here
	mov	ax,cs		;copy CS to ES
	mov	es,ax
	mov	ds,ax		;and DS
	mov	di,offset olin	;pt at output line buf (.LM 0)
;
nextl:	; read next line
	call	glin		;get a line
	jc	eof		;EOF
	mov	ds:lptr,si	;save ptr
	lodsb			;get char
	test	al,al		;blank line?
	jz	nextl		;yes, don't confuse w/paragraph
	cmp	al,'.'		;command?
	je	cmd		;yes, handle it
	cmp	al,' '		;blank or tab or something?
	jbe	newpar		;yes, new paragraph
nextw:	; process next word
	call	gword		;get one
	jc	nextl		;EOL
	call	makspc		;make space for it
	add	ds:col,cx	;bump col
	cmp	byte ptr ds:uline,0 ;underlining?
	jnz	doulin		;yes
	rep	movsb		;copy word
	jmp	short nextw	;loop
doulin:	mov	ax,bs*400+'_'	;_, backspace
	stosw			;save
	movsb			;char
	loop	doulin		;loop
	jmp	short nextw	;loop
;
newpar:	; new paragraph
	cmp	di,ds:lmarg	;is there a line yet?
	je	$+5		;no, don't double space
	 call	fline		;finish this line
	call	fline		;add a blank one
	mov	al,' '		;blank
	mov	cx,4		;4 of them (NEXTW will add the 5th)
	add	ds:col,cx	;count 'em
	rep	stosb		;save
	jmp	short nextw	;start parsing words
;
eof:	; end of file
	cmp	di,ds:lmarg	;anything to flush?
	je	$+5		;no
	 call	fline		;flush last line
	lds	cx,dword ptr ds:optr ;get output ptr
	xor	dx,dx		;offset=0
	mov	bx,1		;stdout
	mov	ah,40h		;func=write
	int	21h		;flush buf
	mov	ax,4C00h	;func=exit
	int	21h
;+
;
; Command processing.
;
;-
cmd:	cmp	byte ptr ds:[si],'.' ;two in a row?
	je	cmd10		;yes, they just want a dot
cmd1:	push	di		;save DI
	call	gword		;get a word
	jc	cmd9		;nothing, return (never on 1st cmd)
	mov	bx,si		;copy
	mov	dx,cx
cmd2:	; convert to upper case
	mov	al,[bx]		;get a char
	cmp	al,'a'		;lower?
	jb	cmd3		;no
	cmp	al,'z'
	ja	cmd3
	and	byte ptr [bx],not 40 ;convert
cmd3:	inc	bx		;+1
	dec	dx		;done all?
	jnz	cmd2		;loop if not
	mov	bx,si		;copy again
	mov	dx,cx
	mov	si,offset cmdtab ;pt at cmd table
cmd4:	; check next cmd in tab
	lodsb			;get min length
	test	al,al		;end of tab?
	jz	cmd8		;yes
	cmp	dl,al		;is our string too short?
	jb	cmd5		;yes, forget it
	mov	di,bx		;copy ptr
	mov	cx,dx		;len
	rep	cmpsb		;match?
	je	cmd7		;yes, skip
cmd5:	dec	si		;back up 1 (in case of 0)
cmd6:	lodsb			;get a byte
	test	al,al		;end of string?
	jnz	cmd6		;loop if not
	inc	si		;skip addr
	inc	si
	jmp	short cmd4	;loop
cmd7:	; found it
	lodsb			;skip to end
	test	al,al
	jnz	cmd7
	lodsw			;get addr
	pop	di		;restore
	mov	cx,ds:wid	;many routines care
	call	ax		;call it
	jmp	short cmd1	;see if there's more
cmd8:	; error
	mov	di,offset badcmd ;ptr
	mov	si,bx		;copy cmd
	mov	cx,dx
	rep	movsb
	mov	ax,lf*400+cr	;crlf
	stosw
	mov	dx,offset bad	;pt at msg
	mov	cx,di		;len
	sub	cx,dx
	mov	bx,2		;stderr
	mov	ah,40h		;func=write
	int	21h		;do it
cmd9:	pop	di		;restore
	jmp	nextl		;parse next line
cmd10:	; ".." at begn of line
	inc	ds:lptr		;skip the first '.'
	jmp	nextw		;start eating words
;
cmdtab	label	byte
	db	3,'.BREAK',0
	dw	brk
	db	2,'.CENTER',0
	dw	center
	db	3,'.FILL',0
	dw	fill
	db	3,'.FRIGHT',0
	dw	fright
	db	4,'.LITERAL',0
	dw	litrl
	db	3,'.LMARGIN',0
	dw	lmrgn
	db	2,'.PAGE',0
	dw	npage
	db	3,'.SKIP',0
	dw	skip
	db	3,'.SPACING',0
	dw	space
	db	4,'.SUPER',0
	dw	super
	db	2,'.UNDERLINE',0
	dw	undln
	db	2,'.WIDTH',0
	dw	wdth
	db	0
;
	subttl	command routines
;+
;
; .BREAK
;
; Make sure the next line starts at the beginning of a line.
;
;-
brk:	cmp	di,ds:lmarg	;is there a line yet?
	je	$+5		;no, don't double space
	 jmp	fline		;flush and return
	ret
;+
;
; .CENTER text     (or text may be on the following line)
;
; Center text around column wid/2.
;
;-
center:	cmp	di,ds:lmarg	;is there a line yet?
	je	$+5		;no, don't double space
	 call	fline		;flush if not at bol
	call	linarg		;get a line argument
	jc	ctr5		;EOF
	mov	cx,ds:wid	;get space to center in
	sub	cx,bx		;find # blanks on both sides
	jbe	ctr2		;none, leave it as is
	shr	cx,1		;/2
ctr1:	; enter here from FRIGHT
	mov	al,' '		;# to save
	rep	stosb		;pad to left
ctr2:	; enter here from FRIGHT
	mov	cx,bx		;copy length
	cmp	byte ptr ds:uline,0 ;underlining?
	jnz	ctr3		;yes
	rep	movsb		;copy the line
	jmp	fline		;flush it, return
ctr3:	cmp	byte ptr [si],' ' ;blank?
	je	ctr4		;yes, don't underline
	mov	ax,bs*400+'_'	;underline, BS
	stosw
ctr4:	movsb			;copy the char
	loop	ctr3		;loop
	jmp	fline		;flush, return
ctr5:	pop	ax		;flush return addr
	jmp	eof		;go handle EOF
;+
;
; .FILL n
;
; If n is 1, fill all complete lines to right margin with blanks.
; If n is 0, don't (default).
;
;-
fill:	call	gword		;get a word
	jc	fill1		;none
	call	gnum		;get number
	mov	ds:fillp,al	;save
fill1:	ret
;+
;
; .FRIGHT text    (or text may be next line)
;
; Outputs text flush against right margin, either at the
; end of this line or on the next if there isn't enough room.
;
;-
fright:	call	linarg		;get arg
	jc	fr4		;nothing, never mind
	mov	cx,ds:wid	;get width
	sub	cx,bx		;find # columns to write first
	jc	fr3		;just write it
	mov	ax,di		;copy curr col
	sub	ax,ds:lmarg	;subtract base
	cmp	ax,cx		;.le. the one we want?
	jbe	fr1		;yes, cool
	; flush the current line
	push	cx		;save
	call	fline		;flush
	pop	cx		;restore
	xor	ax,ax		;curr column is 0
fr1:	; pad
	sub	cx,ax		;find # blanks to add
	jmp	short ctr1	;do it, copy line
fr3:	; line won't fit
	cmp	di,ds:lmarg	;at left marg?
	jz	ctr2		;yes, just write
	push	bx		;save
	call	fline		;flush
	pop	bx		;restore
	jmp	short ctr2	;write, return
fr4:	; EOF
	pop	ax		;flush return
	jmp	eof		;handle EOF
;+
;
; .LITERAL
;
; Lines are copied directly to output file until line starting with $
; (quote with $$ if you really mean it).
;
;-
litrl:	cmp	di,ds:lmarg	;is there a line yet?
	je	$+5		;no, don't double space
	 call	fline		;not at bol
lit1:	call	glin		;get a line
	jc	lit5		;EOF
	lodsb			;get 1st char
	cmp	al,'$'		;end?
	jne	lit3		;no
	lodsb			;get next char
	cmp	al,'$'		;quoted?
	jne	lit4		;no, end
lit2:	stosb			;write char
	lodsb			;get a char
lit3:	test	al,al		;EOL?
	jnz	lit2		;loop
	call	fline		;flush
	jmp	short lit1	;loop
lit4:	dec	si		;back up
	mov	ds:lptr,si	;set ptr
	ret
lit5:	pop	ax		;lose R.A.
	jmp	eof		;EOF
;+
;
; .LMARGIN n / +n / -n
;
; Set or adjust left margin.
;
;-
lmrgn:	call	gword		;get arg
	jc	lm5		;nothing
	lodsb			;get 1st char
	cmp	al,'+'		;plus?
	je	lm1		;skip
	cmp	al,'-'		;minus?
	je	lm1		;skip
	dec	si		;unget
lm1:	push	ax		;save
	call	gnum		;get value
	pop	bx		;restore
	cmp	bl,'+'		;add?
	je	lm2		;yes
	cmp	bl,'-'		;subtract?
	je	lm3		;yes
	add	ax,offset olin	;add base
	jmp	short lm4
lm2:	add	ax,ds:lmarg	;add
	jmp	short lm4
lm3:	mov	bx,ds:lmarg	;fetch
	sub	bx,ax		;subtract
	mov	ax,bx		;copy
lm4:	; new left marg posn in ax
	cmp	ax,offset olin	;too far to left?
	jae	lm6		;no, skip
lm5:	mov	ax,offset olin	;stop at left marg
lm6:	mov	bx,ax		;copy
	xchg	ax,ds:lmarg	;set new value, get old
	cmp	di,ax		;was di at left marg?
	jne	lm7		;no, complete line with old margin
	; blank out to new value
	mov	di,offset olin	;pt at obuf
	mov	cx,bx		;copy
	sub	cx,di		;find # to indent
	mov	al,' '		;blank
	rep	stosb		;fill, update di
lm7:	ret
;+
;
; .PAGE
;
;-
npage:	cmp	di,ds:lmarg	;is there a line yet?
	je	$+5		;no, don't double space
	 call	fline		;flush if not bol
;;; this is extremely cheesy and will probably need fixing when I add the
;;; final version of the "page" concept.
;;; all this does is squeeze a ^L into the buffer
	push	di		;save
	push	es
	les	di,dword ptr ds:optr ;pt at output buf
	mov	cx,ds:octr	;# free bytes
	test	cx,cx		;any?
	jnz	npage1		;yes
	; flush buffer
	push	ds
	push	es		;copy es
	pop	ds		;to ds
	xor	dx,dx		;offset=0
	mov	cx,di		;length
	mov	bx,1		;stdout
	mov	ah,40h		;func=write
	int	21h		;do it
	xor	di,di		;pt at begn
	mov	cx,-1000	;space
	pop	ds		;restore
npage1:	mov	al,ff		;form feed
	stosb			;save
	dec	cx		;count it
	mov	ds:optr,di	;update
	mov	ds:octr,cx
	pop	es		;restore
	pop	di
	ret
;+
;
; .SKIP n
;
; Output n blank lines (regardless of spacing).
;
;-
skip:	cmp	di,ds:lmarg	;is there a line yet?
	je	$+5		;no, don't double space
	 call	fline		;flush if not bol
	call	gword		;get next word
	mov	cx,1		;[default value]
	jc	skipcx		;no #, use default
	call	gnum		;get value
	mov	cx,ax
skipcx:	; output cx blank lines
	push	cx		;save
	mov	di,offset olin	;pt at base of buf (no blanks)
	mov	ax,lf*400+cr	;crlf
	stosw			;write it
	call	folin		;dump one
	pop	cx		;restore
	loop	skipcx		;loop
	mov	word ptr ds:olin,"  " ;in case lmarg .gt. 0
	ret
;+
;
; .SPACING n
;
; Set line spacing to n.
;
;-
space:	mov	ds:linspc,1	;default
	call	gword		;next word
	jc	sp1		;use default
	call	gnum		;get value
	mov	ds:linspc,ax	;save
sp1:	ret
;+
;
; .SUPER word
;
; Prints word with all digits in superscript, ONLY on a NEC 5530 Spinwriter.
;
;-
super:	call	gword		;get next word
	jc	sup5		;just kidding eh?
	call	makspc		;possibly split output line
	add	ds:col,cx	;bump col
	xor	ah,ah		;not in alternative char set
	mov	bx,offset necsup ;pt at table
	mov	dl,ds:uline	;underlining?
	test	dl,dl
	jz	sup1		;nope
	 mov	dx,bs*400+'_'	;_, backspace
sup1:	test	dl,dl		;underlining?
	jz	sup3		;no
	test	ah,ah		;in annoying mode?
	jz	sup2		;no
	mov	al,17		;^O, clear flag
	stosb			;out of annoying mode
sup2:	mov	ax,dx		;_, backspace
	stosw			;save it
	cbw			;ah=0
sup3:	lodsb			;get next byte
	cmp	al,'0'		;digit?
	jb	sup6		;no
	cmp	al,'9'
	ja	sup6
	; it's a digit
	sub	al,'0'		;convert
	xlat			;look up the letter
	test	ah,ah		;already sent ^N?
	jnz	sup4		;yep
	mov	ah,al		;no, save (and set flag)
	mov	al,16		;^N
	stosb
	mov	al,ah		;recover
sup4:	stosb			;save the char
	loop	sup1		;loop
	test	ah,ah		;in weird mode?
	jz	sup5		;no
	mov	al,17		;^O
	stosb
sup5:	ret
sup6:	; not a digit
	test	ah,ah		;in ^N mode?
	jz	sup4		;no, just save
	mov	ah,al		;save char
	mov	al,17		;^O
	stosb
	mov	al,ah		;recover
	xor	ah,ah		;ah=0
	jmp	short sup4	;loop
;
necsup	db	'PQWERTYUIO'	;super digits 0-9
;+
;
; .UNDERLINE n
;
; Turns underlining on if n is 1, off if 0.
;
;-
undln:	call	gword		;get word
	jc	ul1		;no op
	call	gnum		;get value
	mov	ds:uline,al	;save flag
ul1:	ret
;+
;
; .WIDTH n
;
; Set width of page to n columns.  Takes effect at next blank line.
;
;-
wdth:	call	gword		;get word
	jc	wid1		;none, no op
	call	gnum		;get value
	mov	ds:nwid,ax	;save
	cmp	ds:col,0	;at left marg?
	jnz	wid1		;no
	mov	ds:wid,ax	;take effect now
wid1:	ret
;+
;
; Get a number.
;
; ds:si	number
;
; Value returned in ax.
;
;-
gnum:	xor	bx,bx		;init
	mov	cx,10d		;radix
gn1:	lodsb			;get a char
	sub	al,'0'		;cvt
	cmp	al,9d		;valid?
	ja	gn2		;no
	cbw			;ah=0
	xchg	ax,bx		;swap
	mul	cx		;*10d
	add	bx,ax		;add it in
	jmp	short gn1	;loop
gn2:	mov	ax,bx		;copy
	ret
;+
;
; Get a line argument to a command.
;
; CF=1	EOF
; Else SI=addr, BX=len (non-zero).
;
;-
linarg:	mov	si,ds:lptr	;get ptr into line
larg1:	lodsb			;get a char
	test	al,al		;end of line?
	jz	larg2		;yes, get next
	cmp	al,' '		;blank or ctrl?
	jbe	larg1		;ignore if so
	dec	si		;unget
	jmp	short larg3	;skip
larg2:	call	glin		;get next line
	jc	larg5		;never mind
larg3:	; .asciz line at ds:si
	mov	bx,si		;copy
larg4:	lodsb			;get a byte
	test	al,al		;end?
	jnz	larg4		;no, loop
	dec	si		;back up
	mov	ds:lptr,si	;no more commands
	xchg	bx,si		;swap
	sub	bx,si		;find length (CF=0)
	jz	larg2		;=0, try again
larg5:	ret
;+
;
; Make sure there's space for a word of cx chars on the output line.
; (Break if necessary.)  Preserves si.
;
;-
makspc:	cmp	ds:col,0	;at left marg?
	jz	newl		;yes, it fits even if too long
	mov	dx,1		;load # blanks to insert
	mov	bx,di		;copy ptr
maksp1:	dec	bx		;-1
	cmp	bx,offset olin	;past boln?
	jb	onebl		;yes
maksp2:	mov	al,[bx]		;get previous char
	cmp	al,')'		;any of the below before a close of some kind?
	je	maksp1		;back up if so
	cmp	al,'"'
	je	maksp1
	cmp	al,'.'		;end of sentence?
	je	twobl
	cmp	al,'?'
	je	twobl
	cmp	al,':'
	je	twobl
	cmp	al,';'
	je	twobl
	cmp	al,'!'
	jne	onebl
twobl:	inc	dx		;+1
onebl:	mov	bx,ds:col	;get current col
	add	bx,dx		;add # blanks
	add	bx,cx		;+length of word
	cmp	bx,ds:wid	;too wide?
	ja	splitl		;yes, split the line
	; add the word on to the line
	xchg	cx,dx		;swap
	mov	al,' '		;blank
	add	ds:col,cx	;bump col
	rep	stosb		;add blank(s)
	mov	cx,dx		;restore
newl:	ret
splitl:	; split output line
	push	cx		;save length
	push	si		;and ptr
	cmp	byte ptr cs:fillp,0 ;filling?
	jz	nofill		;no
	; fill to right margin
	mov	dx,ds:wid	;get width
	sub	dx,ds:col	;find # blanks to add
	jz	nofill		;none, easy
	; count number of embedded [runs of] blanks in line already
	mov	si,ds:lmarg	;pt at 1st char
	xor	bx,bx		;# blanks in line
	mov	cx,ds:col	;get width
ct1:	; skip (leading) blanks (may be paragraph first time around)
	lodsb			;get a char
	cmp	al,' '		;blank?
	loope	ct1		;loop if so (eventually not)
	jcxz	ct3		;may have been only 1 char
ct2:	; skip to end of word
	lodsb			;get a char
	cmp	al,' '		;blank?
	loopne	ct2		;loop if not
	jne	ct3		;not blank, done
	inc	bx		;blanks begin here, count them
	jmp	short ct1	;go skip them
ct3:	; bx contains # of runs of blanks
	test	bx,bx		;one big hefty word?
	jz	nofill		;yes, relax
	mov	cx,-1		;init
ct4:	inc	cx		;+1
	sub	dx,bx		;subtract # slots from # needed
	jnc	ct4		;loop if it fit
	add	dx,bx		;find # left over
	; bx/ # of blank groups in line
	; cx/ # of blanks to add to each group of blanks
	; dx/ # which get cx+1 (dx<bx)
	mov	si,di		;pt off EOL
	mov	di,ds:lmarg	;pt at left marg
	add	di,ds:wid	;pt off right marg
fl1:	; copy word, including the preceding blank
	dec	si		;-1
	mov	al,[si]		;get a char
	dec	di		;-1
	mov	[di],al		;copy
	cmp	al,' '		;blank?
	jne	fl1		;loop if not
	jcxz	fl3		;don't always add a blank
	mov	ah,cl		;get count
fl2:	dec	di		;-1
	mov	[di],al		;add a blank
	dec	ah		;-1
	jnz	fl2		;loop
fl3:	; decide whether this blank is a lucky winner
	;;; for now always fill to the right
	test	dx,dx
	jz	fl4
	dec	di		;-1
	mov	[di],al		;add one blank
	dec	dx
fl4:	; see if we're done
	dec	bx		;done?
	jz	fl6
	; copy any remaining blanks
fl5:	dec	si		;-1
	mov	al,[si]
	dec	di		;-1
	mov	[di],al		;copy
	cmp	al,' '		;blank?
	jne	fl1		;loop if not
	jmp	short fl5	;yes
fl6:	mov	di,ds:lmarg	;pt off right marg again
	add	di,ds:wid
nofill:	call	fline		;dump this line
	pop	si		;restore
	pop	cx
	ret
;+
;
; Flush output line.
;
; es:di	pointer into OLIN
;
;-
fline:	mov	ax,lf*400+cr	;add crlf
	mov	cx,ds:linspc	;get line spacing
	rep	stosw		;yep
folin:	; flush output line buffer (DI points to end)
	push	si		;save DS:SI
	mov	si,offset olin	;pt at buf
	sub	di,si		;find length
	jz	flin4		;nothing, skip
	mov	bx,di		;copy # to copy
	les	di,dword ptr ds:optr ;pt at output buf
	mov	cx,ds:octr	;# free bytes
	jcxz	flin6		;go flush
flin1:	; CX is # free bytes in OBUF, BX is # to move
	cmp	cx,bx		;will it fit?
	jb	flin5		;no
	; it will fit
	sub	cx,bx		;find new len
	mov	ds:octr,cx	;save
	mov	cx,bx		;# to move
flin2:	lodsb			;get a char
	cmp	al,nxspc	;non-expandable space substitute char?
	jne	flin3
	 mov	al,' '		;yes, change to blank
flin3:	stosb			;save
	loop	flin2
	mov	ds:optr,di	;update ptr
	push	cs		;copy CS to ES
	pop	es
flin4:	pop	si		;restore
	mov	di,offset olin	;pt at begn of buf
	mov	cx,ds:lmarg	;pt past blanks
	sub	cx,di		;find # blanks
	mov	al,' '		;blank
	rep	stosb		;blanks
	mov	ds:col,cx	;at left marg
	mov	ax,ds:nwid	;get next width
	mov	ds:wid,ax	;copy
	ret
flin5:	; won't all fit, copy part and page to next buffer
	sub	bx,cx		;# to go after this
	rep	movsb		;copy
flin6:	; flush output buf
	push	bx		;save
	push	ds
	push	es		;copy ES to DS
	pop	ds
	xor	dx,dx		;offset=0
	mov	cx,di		;length
	mov	bx,1		;STDOUT
	mov	ah,40h		;func=write
	int	21h		;do it
	xor	di,di		;pt at begn
	mov	cx,-1000	;space
	pop	ds		;restore
	pop	bx
	jmp	short flin1	;try again
;+
;
; Get a word from the input line.
;
; Returns si=begn of word, cx=length.
;
; CF=1 on EOL.
;
;-
gword:	push	di		;save
	mov	si,ds:lptr	;get ptr
gwrd1:	lodsb			;get a char
	test	al,al		;EOL?
	jz	gwrd5		;yes
	cmp	al,' '		;blank or ctrl char?
	jbe	gwrd1		;yes, ignore
	dec	si		;back up
	mov	bx,si		;copy ptr
	mov	di,si		;copy ptr
gwrd2:	lodsb			;get a char
	cmp	al,'_'		;quoting next char?
	je	gwrd4
	cmp	al,'#'		;non-expandable space?
	je	gwrd6
	cmp	al,' '		;blank, ctrl char, EOL?
	jbe	gwrd7		;yes, end of word
gwrd3:	mov	[di],al		;save
	inc	di
	jmp	short gwrd2	;loop
gwrd4:	lodsb			;get next char
	test	al,al		;EOL?
	jnz	gwrd3		;no, save it
gwrd5:	pop	di		;restore
	stc			;EOL
	ret
gwrd6:	mov	al,nxspc	;substitute character
	jmp	short gwrd3	;go save it
gwrd7:	dec	si		;back up
	mov	ds:lptr,si	;save
	sub	di,bx		;length (CF=0)
	mov	cx,di
	mov	si,bx		;pt at word
	pop	di		;restore
	ret
;+
;
; Read a line from the input file.
; Returns ds:si pointing to it (.ASCIZ).
;
; C=1 on EOF.
;
;-
glin:	push	es		;save es:di
	push	di
	lds	si,dword ptr ds:iptr ;get input buf
	mov	cx,cs:ictr	;# to go
	push	cs		;copy cs
	pop	es		;to es
	mov	di,offset lbuf	;pt at line buf
	jcxz	glin3		;go refill buffer
glin1:	; get next char
	lodsb			;get a char
	cmp	al,cr		;cr?
	je	glin2		;yes, ignore
	cmp	al,lf		;EOL?
	je	glin4		;yes
	stosb			;save the char
glin2:	loop	glin1		;loop
glin3:	; refill ibuf
	xor	dx,dx		;offset
	mov	cx,-1000	;length
	xor	bx,bx		;stdin
	mov	ah,3Fh		;func=read
	int	21h		;read a bufferload
	mov	cx,ax		;get length
	jcxz	glin5		;EOF
	xor	si,si		;reinit ptr
	jmp	short glin1	;try again
glin4:	; EOL
	xor	al,al		;load 0, CF=0
	stosb			;mark EOL
	push	cs		;copy CS to DS
	pop	ds
	mov	ds:iptr,si	;update ptr
	dec	cx		;-1 for LF
	mov	ds:ictr,cx	;save length
	mov	si,offset lbuf	;pt at buf
	pop	di		;restore
	pop	es
	ret
glin5:	; EOF
	cmp	di,offset lbuf	;anything in buf?
	jne	glin4		;yes, return it (EOF next time)
	push	cs		;copy CS to DS
	pop	ds
	pop	di		;restore
	pop	es
	stc			;error return
	ret
;
wid	dw	80d		;max width of line
nwid	dw	80d		;next width
linspc	dw	1		;line spacing
lmarg	dw	olin		;left margin position
col	dw	0		;current column (including lmarg)
uline	db	0		;NZ => underlining
fillp	db	0		;NZ => filling to right marg
;
iptr	dw	?		;curr offset into ibuf
	dw	seg ibuf
ictr	dw	0		;# bytes in buf
;
optr	dw	0		;curr offset into obuf
	dw	seg obuf
octr	dw	-1000		;# free bytes
;
bad	db	'?Bad command:  '
badcmd	db	40+2 dup(?)	;cmd, crlf
;
olin	db	1000 dup(?)	;line output buffer
;
lptr	dw	?		;ptr into lbuf
lbuf	db	132d+1 dup(?)	;input line buffer
;
code	ends
ibuf	segment
	db	177000 dup(?)
ibuf	ends
obuf	segment
	db	177000 dup(?)
obuf	ends
pdl	segment stack 'stack'	;goddamn Borland geeks!!!
	dw	100h dup(?)
pdl	ends
	end	start
