[net.sources] nansi.sys sources + demo

nather@ut-sally.UUCP (Ed Nather) (07/17/86)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sally!nather on Thu Jul 17 13:11:16 CDT 1986
# Contents:  readme nansi.doc nansi.mkf nansi.sup demo.uue nansi.uue
 
echo x - readme
sed 's/^@//' > "readme" <<'@//E*O*F readme//'
This package includes the source code for nansi.sys, an improved
console driver for MS-DOS running on IBM PC computers or their clones,
a makefile for how to assemble it, a uuencoded copy of the assembled
(executable) driver, documentation describing the driver, and a short 
demo program (uuencoded) to show off its strong point: very rapid
screen updates from multi-character write operations.

The demo program takes a single argument on the command line; it should
be the filename of a text file.  It then displays the first 24 text lines
from the file on the console screen and quits.  Try it first with your
current console driver (e.g. ansi.sys), then install the executable
copy of nansi.sys in its place and run demo again on the same text file.

I think the author of nansi.sys did us all a tremendous favor.


Ed Nather
Astronomy Dept, U of Texas @ Austin
{allegra,ihnp4}!{noao,ut-sally}!utastro!nather
nather@astro.AS.UTEXAS.EDU
@//E*O*F readme//
chmod u=rw,g=r,o=r readme
 
echo x - nansi.doc
sed 's/^@//' > "nansi.doc" <<'@//E*O*F nansi.doc//'

nansi   - enhanced MS-DOS ansi console driver

SYNOPSIS
        Include in \config.sys the line
                device=nansi.sys

DESCRIPTION
        Nansi.sys is a console driver which understands ANSI control
        sequences. It has several advantages over ANSI.SYS (the driver
        supplied with DOS):
        1. It supports new escape sequences (see below).
        2. It provides MUCH faster output under certain conditions.
        3. It supports the 43-line mode of the EGA.
        4. The darned bell is now 1/4 second instead of 1/2 second long.

        What a console driver does:
        When you, for example, type
                C:> type foo.txt
        COMMAND.COM opens the file foo.txt, reads it, and writes it to
        the console driver, which puts it up on the screen.

        Both ansi.sys and nansi.sys use IBM Video BIOS to control the screen.
        However, nansi.sys bypasses BIOS if the screen is in a text mode; this
        allows much faster operation under certain conditions.

        While putting text up on the screen, (n)ansi.sys keeps a lookout for
        the escape character (chr(27), known as ESC); this character signals
        the start of a terminal control sequence.
        Terminal control sequences follow the format
                ESC [ param; param; ...; param cmd
        where
                ESC     is the escape character chr$(27).
                [       is the left bracket character.
                param   is an ASCII decimal number, or a string in quotes.
                cmd     is a case-specific letter identifying the command.
        Usually, zero, one, or two parameters are given.  If parameters
        are omitted, they usually default to 1; however, some commands
        (KKR and DKOCT) treat the no-parameter case specially.
        Spaces are not allowed between parameters.

        For example, both ESC[1;1H and ESC[H send the cursor to the home
        position (1,1), which is the upper left.

        Either single or double quotes may be used to quote a string.
        Each character inside a quoted string is equivalent to one numeric
        parameter.  Quoted strings are normally used only for the Keyboard
        Key Reassignment command.

Control Sequences
        The following table lists the sequences understood by nansi.sys.
        Differences between nansi.sys and the standard ansi.sys are marked
        with a vertical bar (|).

Cursor Positioning
Short   Long name               Format          Notes
CUP     cursor position         ESC[y;xH        Sets cursor position.
HVP     cursor position         ESC[y;xf        Same as CUP; not recommended.
CUU     cursor up               ESC[nA          n = # of lines to move
CUD     cursor down             ESC[nB
CUF     cursor forward          ESC[nC          n = # of columns to move
CUB     cursor backward         ESC[nD
DSR     Device Status, Report!  ESC[6n          Find out cursor position.
CPR     Cursor Position report  ESC[y;xR        Response to DSR, as if typed.
SCP     Save Cursor Position    ESC[s           Not nestable.
RCP     Restore Cursor Position ESC[u

Editing
ED      Erase in Display        ESC[2J  Clears screen.
EL      Erase in Line           ESC[K   Clears to end of line.
IL  |   Insert Lines            ESC[nL  Inserts n blank lines at cursor line.
DL  |   Delete Lines            ESC[nM  Deletes n lines including cursor line.
ICH |   Insert Characters       ESC[n@  Inserts n blank chars at cursor.
DCH |   Delete Characters       ESC[nP  Deletes n chars including cursor char.


Mode-Setting
SGR     Set Graphics Rendition  ESC[n;n;...nm   See character attribute table.
SM      Set Mode                ESC[=nh         See screen mode table.
RM      Reset Mode              ESC[=nl         See screen mode table.
IBMKKR  Keyboard Key Reass.     ESC["string"p
        The first char of the string gives the key to redefine; the rest
        of the string is the key's new value.
        To specify unprintable chars, give the ASCII value of the char
        outside of quotes, as a normal parameter.
        IBM function keys are two byte strings; see the IBM Basic manual.
        For instance, ESC[0;";dir a:";13;p redefines function key 1 to
        have the value "dir a:" followed by the ENTER key.
      | If no parameters given, all keys are reset to their default values.

DKOCT | Output char translate   ESC[n;ny
      | When first char is encountered in output request, it is replaced with
      | the second char.  This might be useful for previewing text before
      | sending it to a printer with a funny print wheel.
      | If no parameters are given, all chars are reset to normal.


Character Attributes
        The Set Graphics Rendition command is used to select foreground
        and background colors or attributes.
        When you use multiple parameters, they are executed in sequence, and
        the effects are cumulative.
           Attrib code          Value
                0               All attributes off (normal white on black)
                1               Bold
                4               Underline
                5               Blink
                7               Reverse Video
                8               Invisible (but why?)
                30-37           foregnd blk/red/grn/yel/blu/magenta/cyan/white
                40-47           background

Screen Modes
        The IBM BIOS supports several video modes; the codes given in the
        BIOS documentation are used as parameters to the Set Mode command.
      | (In bitmap modes, the cursor is simulated with a small blob (^V).)
            Mode Code           Value
                0               text 40x25 Black & White
                1               text 40x25 Color
                2               text 80x25 Black & White
                3               text 80x25 Color
                4               bitmap 320x200 4 bits/pixel
                5               bitmap 320x200 1 bit/pixel
                6               bitmap 640x200 1 bit/pixel
                7               (cursor wrap kludge)
                13 (EGA)        bitmap 320x200 4 bits/pixel ?
                14 (EGA)        bitmap 640x200 4 bits/pixel
                16 (EGA)        bitmap 640x350 4 bits/pixel
        Mode 7 is an unfortunate kludge; Setting mode 7 tells the cursor
        to wrap around to the next line when it passes the end of a line;
        Resetting mode 7 tells the cursor to not wrap, but rather stay put.
      | If your computer has the Enhanced Graphics Adaptor, modes between
      | 8 and 15 are also supported; see the EGA BIOS for info.
      | The EGA also lets you use a shorter character cell in text modes
      | in order to squeeze 43 lines of text out of the 25-line text modes.
      | To enter 43 line mode, set the desired 25-line text mode (0 to 3),
      | then Set Mode 43.  For instance: ESC[=3h ESC[=43h.
      | To exit 43 line mode, set the desired 25-line text mode again.
      | Nansi.sys ignores mode 43 unless there is an EGA on your computer.

Faster Output
      | Any program that sets the console to RAW mode, and buffers its
      | output properly, can achieve extremely high screen update speeds in
      | return for giving up the special functions of the keys ^C, ^S, and ^P.
      | See IOCTL in the MS-DOS 3.x Technical Reference for more info.
        Also, a small improvement in speed may be noticed with some
        programs that use the DOS console in normal mode, as this driver
        efficiently implements the (standard but undocumented) INT 29h
        most-favored-device putchar used by DOS.

EXAMPLES
        See the file setraw.doc for Macro Assembler, Lattice C,
        and Microsoft C routines for entering and leaving raw mode.

BUGS
        Insert and delete character do not work in graphics modes.
        Graphics mode writing is slow.
        The simulated cursor in graphics mode slows down single-char
        writes by a factor of 3; it should be disable-able.
        Does not support erase-to-end-of-screen and other useful functions.

Version
        This version, 2.2, created February 1986.  Problems should
        be reported to Daniel Kegel, 1-60 CIT, Pasadena, CA 91126
        (or, after June 1986, 2648 169th Ave SE, Bellevue, Wa. 98008).
        Your suggestions for improvement would be most welcome.

NOTE
        This program may be distributed for educational and personal use
        only.  Commercial use is verboten; get in touch with the author.

FILES
        nansi.doc       - this file
        nansi.hex       - compiled version  <Not supported by INFO-IBMPC>
                Convert nansi.hex into nansi.sys with the following commands
                (you'll end up with 256 extra bytes at end, but who cares?):
                        debug                   # Run DOS debugger
                        N nansi.hex             # Load the hex file
                        L
                        N nansi.sys             # Write it out
                        W
                        Q                       # Quit- back to DOS.
        nansi.cat       - concatenated sources
                nansi   - makefile- shows how to assemble & link
                nansi.asm       - device driver guts
                nansi_d.asm     - definitions
                nansi_p.asm     - ANSI parameter grabber
                nansi_f.asm     - ANSI function handlers
                nansi_i.asm     - device driver init routine
        setraw.cat      - concatenated examples
                setraw.asm      - for assembly programs
                setraw.msc      - for Microsoft C programs
                setraw.lc       - for Lattice C programs
                rawtest.lc      - example for Lattice C only
-- 

@//E*O*F nansi.doc//
chmod u=rw,g=r,o=r nansi.doc
 
echo x - nansi.mkf
sed 's/^@//' > "nansi.mkf" <<'@//E*O*F nansi.mkf//'
nansi.obj:	nansi.asm nansi_d.asm
	masm nansi,,nansi;

nansi_p.obj:	nansi_p.asm
	masm nansi_p,,nansi_p;

nansi_f.obj:	nansi_f.asm nansi_d.asm
	masm nansi_f,,nansi_f;

nansi_i.obj:	nansi_i.asm nansi_d.asm
	masm nansi_i,,nansi_i;

nansi.sys:	nansi.obj nansi_p.obj nansi_f.obj nansi_i.obj
	link /map nansi nansi_p nansi_f nansi_i;
	exe2bin nansi nansi.sys
	rm nansi.exe

@//E*O*F nansi.mkf//
chmod u=rw,g=r,o=r nansi.mkf
 
echo x - nansi.sup
sed 's/^@//' > "nansi.sup" <<'@//E*O*F nansi.sup//'
Support Routines for nansi.sys
The three files setraw.asm, setraw.lc, and setraw.msc contain routines
to allow use of RAW mode from assembly, Lattice C, and Microsoft C.

Lattice C is a little wierder than most, so rawtest.lc was included
to show how to use setraw.lc; the other languages require no special
setup to use setraw.

;---- setraw.asm ----------------------------------------------
	public	getraw, setraw
;----- dos ----------------------
; Call DOS function # n.
dos	macro	fn
	mov	ah, fn
	int	21h
	endm

code	segment para public 'CODE'
assume cs:code
;----- Getraw ---------------------------------------------
; Returns AX=1 if file BX is a device in raw mode; 0 otherwise.
; Returns Carry set & errorcode in AX if DOS error.

getraw	proc	near
	mov	al, 0
	DOS	44h		; Get attributes
	jc	gr_exit		; bad file handle?
	mov	ax, 20h
	and	al, dl		; get that bit
	mov	cl, 4
	shr	ax, cl
	clc
gr_exit:
	ret
getraw	endp

;----- Setraw -------------------------------------------
; Sets Raw state of file BX to (AX != 0) if file is a device.
; Returns Carry set & errorcode in AX if DOS error.
setraw	proc	near
	mov	cx, ax
	mov	al, 0
	DOS	44h
	jc	sr_exit
	test	al, 80h		; It it a device?
	jz	sr_exit		; nope- do nothing.
	; Current mode in DX; set CX = 20H if CX nonzero.
	or	cx, cx
	jz	sr_ax0
		mov	cx, 20h
sr_ax0: and	dx, 00cfh	; clear old raw bit and hi byte,
	or	dx, cx		; set new value.
	mov	al, 1
	DOS	44h
sr_exit:
	ret
setraw	endp
code	ends
	end
;---- end of setraw.asm ------------------------------------------
/*--- setraw.msc -------------------------------------------------
  Routines to set and reset raw mode on stdin/stdout.
  For Microsoft C.
------------------------------------------------------------------*/
#include <dos.h>
/* Use the IOCTL DOS function call to change stdin and stdout to raw mode.
 * For stdin, this prevents MSDOS from trapping ^P, ^S, ^C, thus freeing us
 * of ^P toggling 'echo to printer'.
 * For stdout, this radically speeds up the output because there is no
 * checking for these special characters in the input buffer whenever
 * screen output is occurring.
 * Note that only the stdin OR stdout ioctl need be changed since
 * apparently they are handled as the same device.
 * Thanks to Mark Zbikowski (markz@microsoft.UUCP) for helping me with
 * this.
 * --- This stolen from sources to the mighty game HACK ---
 */
#define DEVICE		0x80
#define RAW		0x20
#define IOCTL		0x44
#define STDIN		fileno(stdin)
#define STDOUT		fileno(stdout)
#define GETBITS		0
#define SETBITS		1
static unsigned	old_stdin, old_stdout, ioctl();
/*--- set_raw() ----------
  Call this to set raw mode; call restore_raw() later to restore
  console to old rawness state.
--------------------------*/
set_raw()
{
	old_stdin = ioctl(STDIN, GETBITS, 0);
	old_stdout = ioctl(STDOUT, GETBITS, 0);
	if (old_stdin & DEVICE)
		ioctl(STDIN, SETBITS, old_stdin | RAW);
	if (old_stdout & DEVICE)
		ioctl(STDOUT, SETBITS, old_stdout | RAW);
}
restore_raw()
{
	if (old_stdin)
		(void) ioctl(STDIN, SETBITS, old_stdin);
	if (old_stdout)
		(void) ioctl(STDOUT, SETBITS, old_stdout);
}
static unsigned
ioctl(handle, mode, setvalue)
unsigned setvalue;
{
	union REGS regs;

	regs.h.ah = IOCTL;
	regs.h.al = mode;
	regs.x.bx = handle;
	regs.h.dl = setvalue;
	regs.h.dh = 0;			/* Zero out dh */
	intdos(&regs, &regs);
	return (regs.x.dx);
}
/*-- end of setraw.msc --*/
/*------ setraw.lc ----------------------------------------------
Lattice C routines which get and set the current raw/cooked state
of a file, given its Lattice file descriptor.
Useful when trying to obtain high console output speeds.
----------------------------------------------------------------*/

#include "\lc\dos.h"
#define CARRY 0x1
#define ERROR (-1)
#define TRUE 1
#define FALSE 0

extern _oserr;

/*---- getraw --------------------------------------------------
Returns TRUE if file fd is a device in raw mode; FALSE otherwise.
Returns ERROR, puts errorcode in _oserr, if DOS error.
----------------------------------------------------------------*/
getraw(fd)
int fd;
{
	union REGS inregs;
	union REGS outregs;
	int	flags;

	if (fd > 2) fd+=2;		/* convert to DOS fd */
	inregs.x.bx = fd;
	inregs.x.ax = 0x4400;		/* get file attributes */
	flags = intdos(&inregs, &outregs);
	if (flags & CARRY) {
		_oserr = outregs.x.ax;
		return -1;
	}
	return (outregs.x.dx & 0x20) ? TRUE : FALSE;
}

/*---- setraw --------------------------------------------------
Sets Raw state of file fd to raw_on (if file is not a device, does nothing).
Returns zero if successful.
Returns ERROR & errorcode in _oserr if DOS error.
----------------------------------------------------------------*/
setraw(fd, raw_on)
int fd, raw_on;
{
	union REGS inregs;
	union REGS outregs;
	int	flags;

	if (fd > 2) fd+=2;		/* convert to DOS fd */

	inregs.x.ax = 0x4400;		/* get file attributes */
	inregs.x.bx = fd;
	flags = intdos(&inregs, &outregs);
	if (flags & CARRY) {
		_oserr = outregs.x.ax;
		return ERROR;
	}
	if ((outregs.x.ax & 0x80) == 0)	/* return zero if not device */
		return 0;

	outregs.x.ax = 0x4401;		/* set file attributes */
	outregs.x.bx = fd;
	outregs.x.dx &= 0xcf;		/* clear old raw bit & hi byte */
	if (raw_on) outregs.x.dx |= 0x20;	/* maybe set new raw bit */
	flags = intdos(&outregs, &inregs);
	if (flags & CARRY) {
		_oserr = inregs.x.ax;
		return ERROR;
	}
	return 0;
}
/*-- end of setraw.lc --*/
/*------ rawtest.lc -------------------------------------------------
Lattice C program to demonstrate the difference in speed between
DOS's raw and cooked modes when writing to the DOS console.
Requires setraw.c; make the demo as follows:
	lc setraw rawtest
	link \lc\s\c rawtest setraw,rawtest,,\lc\s\lc
and run it by typing
	rawtest

Note- Lattice C's raw mode (i.e. using mode "rb" or "wb" with fopen)
is not the same as DOS's raw mode, and does not affect speed.

What does affect speed is whether output is performed with single-character
DOS calls, which is the default.  To get a speed improvement, you must 
open "con" WITHOUT a trailing colon, and use that file for high-speed output;
see section 5.2 (Device I/O) of the Lattice manual.

When using MS-DOS raw mode, the console is in totally unbuffered mode-
echo is turned off, no printer echoing is done, and no line editing
is done, regardless of which file setraw was applied to.  This means
that the console must be in non-raw ("cooked") mode for line-oriented
console input to work properly.

Note: no speed difference will be noticed when using the standard console
driver ANSI.SYS that comes with DOS; you must be using NANSI.SYS to
get massively fast output.  
To use nansi.sys, insert the following line in \config.sys:
	device = nansi.sys
and put nansi.sys on the top level directory; the system will load
it at boot time.
(If there was already a line invoking plain old ansi.sys, remove it.)
--------------------------------------------------------------------*/

#include "\lc\stdio.h"

char	response[128];

main(argc, argv)
int	argc;
char	**argv;
{
	FILE	*fp;
	int	fd;
	int	old_rawness;
	int	i;

	fp = fopen("con", "w");
	if (fp == NULL) fprintf(stderr, "can't open 'con'!");
	fd = fileno(fp);		/* get Level 1 file descriptor */
	old_rawness = getraw(fd);	/* Save old raw/cooked state */

	setraw(fd, 0);			/* make sure we're in cooked mode */

	puts("Cooked mode test (hit return):");
	gets(response);
	fprintf(fp, "\033[2J");		/* clear screen */
	for (i=0; i<20; i++)
		fprintf(fp, "This is cooked mode!  Why is it so darned slow? %d\n", i);
	fflush(fp);

	puts("Raw mode test (hit return):");
	gets(response);			/* must be in cooked mode to use gets */
	setraw(fd, 1);			/* enter raw mode */
	fprintf(fp, "\033[2J");		/* clear screen */
	for (i=0; i<20; i++)
		fprintf(fp, "-- This is raw mode- it's clearly faster than cooked! %d\n", i);
	fflush(fp);			/* finish writing while in raw mode */
	setraw(fd, old_rawness);	/* go back to old raw/cooked state  */

	fclose(fp);
}
/*--- end of rawtest.lc ---*/
-- 

@//E*O*F nansi.sup//
chmod u=rw,g=r,o=r nansi.sup
 
echo x - demo.uue
sed 's/^@//' > "demo.uue" <<'@//E*O*F demo.uue//'
begin 644 demo.exe
M35HL`!(`!``@`$<!___)`@`(3)N%!P``'@````$`Z`*U`88'``"\&```#AD`
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````%6+[+@,`.@!!%8S]HEV],=&^E``QT;X50#'
M1ORR";@`@%"@OP"84.AO!H/$!+@`@%"@MP"84.A@!H/$!.CL`8-^!`%U`^G$
M`+@"@%"+7@;_=P+H[P2#Q`2)1O9`=1J+7@;_=P*X60!0N,``4.AD`X/$!N@@
M`NF4`+C0!U"XL@E0_W;VZ(<"@\0&HZ``0'4MN'8`4+C``%#H.0.#Q`3KT[A/
M`%#_=OSH:`"#Q`2)1OR#/J8``'02H:8``4;TH:@`_P:H`#T8`'S7N+@`4/]V
M^NA,`X/$!/]V]+BR"5"@OP"84.B-`X/$!HOP0'4*N)@`4.@6!H/$`KBX`%#_
M=OCH'@.#Q`0SP%#H"0B#Q`)>B^5=PU6+[#/`Z/$"5HMV!#/`HZ0`HZ8`@#P`
M=#*`/`IT+?\&I@"#/BP%`'4%@#P<<Q'_-J0`5N@T`(/$!`$&I`#K!/\&I`"A
MI``Y1@9]%X`\"G4(_P:D`/\&I@"`/`!T`4:+QNL#1NNO7HOE7<-5B^RX`@#H
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
M@&<&SUW#58OL5E<&@WX(`'4XOS8#BU8&BT8$2'4'Z%,`<B?K2(LVA@-(=!$[
M]W0-BT0"B48,5N@Z`%YS,(/&!('^A@-S!`O2=0:X__^9ZQV+VH/##]';L0/3
MZ[1(S2%RZ9*)!(E4`HDVA@,SP`=?7HOE7<.+3@R+]SE,`G0,@\8$@?Z&`W7R
M^>L_B]H#''(YB]..P3OW=08Y'C(#<R:#PP_1V]'KT>O1ZSOW=0D#V:&2!"O8
MCL"T2LTA<@T[]W4$B18R`Y*'!(O1PP``````````````````````````````
M`````````$,@3&EB<F%R>2`M("A#*4-O<'ER:6=H="!-:6-R;W-O9G0@0V]R
M<"`Q.3@U``%.=6QL('!O:6YT97(@87-S:6=N;65N=`T*`!M;,DH`&UM!`$-A
M;B=T(&]P96X@(B5S(B!T;R!R96%D(&ET+@H`17)R;W(@<F5A9&EN9R!F:6QE
M("(E<R(*+"!A<F=V6S%=`&1E;6\Z(``````(````````````)A(-"K('``"R
M!P$``````````@$````````&`@```````(0#`````````@0`````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M```````````````````````````!````````````````````````````````
M````````````````````2`$`````````````````````````````````````
M````````````````````````17)R;W(@,```3F\@<W5C:"!F:6QE(&]R(&1I
M<F5C=&]R>0``````07)G(&QI<W0@=&]O(&QO;F<`17AE8R!F;W)M870@97)R
M;W(`0F%D(&9I;&4@;G5M8F5R````3F]T(&5N;W5G:"!C;W)E`%!E<FUI<W-I
M;VX@9&5N:65D`````$9I;&4@97AI<W1S`$-R;W-S+61E=FEC92!L:6YK````
M`$EN=F%L:60@87)G=6UE;G0``%1O;R!M86YY(&]P96X@9FEL97,`````3F\@
M<W!A8V4@;&5F="!O;B!D979I8V4``````$UA=&@@87)G=6UE;G0`4F5S=6QT
M('1O;R!L87)G90``4F5S;W5R8V4@9&5A9&QO8VL@=V]U;&0@;V-C=7(`.B``
M56YK;F]W;B!E<G)O<@`*``"H`;`!L0'+`<P!S0'.`<\!X0'S`0,"!`(%`A4"
M)P(H`BD"*@(V`D@"20)*`DL"7`)=`G$"<@)S`G0"C`*-`HX"CP*0`IX"KP*P
M`B4``````````````+`!````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`#8#`"`@("`@("`@("@H*"@H("`@("`@("`@("`@("`@("`@2!`0$!`0$!`0
M$!`0$!`0$(2$A(2$A(2$A(00$!`0$!`0@8&!@8&!`0$!`0$!`0$!`0$!`0$!
M`0$!`0$0$!`0$!""@H*"@H("`@("`@("`@("`@("`@("`@("`A`0$!`@````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M````````````````````````````````````````````````````````````
M`````````````````````````````````````````````````(&!@0$!````
M````````````````````````````````.T-?1DE,15])3D9/`````````!8"
M`A@-"0P,#`<(____$@T2`O\H;G5L;"D`*RT@(P``````````````````````
2`"``````````````````````
`
end
@//E*O*F demo.uue//
chmod u=rw,g=r,o=r demo.uue
 
echo x - nansi.uue
sed 's/^@//' > "nansi.uue" <<'@//E*O*F nansi.uue//'
begin 644 nansi.sys
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
9JXD^=0`SP*K^P'7[,\#%-EP`B7P.C$P0PP*)
`
end
@//E*O*F nansi.uue//
chmod u=rw,g=r,o=r nansi.uue
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      20     150     941 readme
     196    1285    9811 nansi.doc
      17      35     367 nansi.mkf
     266    1217    7967 nansi.sup
     198     200   12079 demo.uue
      59      61    3474 nansi.uue
     756    2948   34639 total
!!!
wc  readme nansi.doc nansi.mkf nansi.sup demo.uue nansi.uue | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
-- 
Ed Nather
Astronomy Dept, U of Texas @ Austin
{allegra,ihnp4}!{noao,ut-sally}!utastro!nather
nather@astro.AS.UTEXAS.EDU

nather@ut-sally.UUCP (Ed Nather) (07/17/86)

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by sally!nather on Thu Jul 17 13:13:38 CDT 1986
# Contents:  nansi.asm nansi_d.asm nansi_f.asm nansi_i.asm nansi_p.asm
 
echo x - nansi.asm
sed 's/^@//' > "nansi.asm" <<'@//E*O*F nansi.asm//'
; New ANSI terminal driver.
; Optimized for speed in the case of multi-character write requests.
; (C) 1986 Daniel Kegel, Pasadena, CA
; May be distributed for educational and personal use only
; The following files make up the driver:
;	nansi.asm   - all DOS function handlers except init
;	nansi_p.asm - parameter parser for ANSI escape sequences
;	nansi_f.asm - ANSI command handlers
;	nansi_i.asm - init DOS function handler
;
; Daniel Kegel, Bellevue, Washington & Pasadena, California
; Revision history:
; 5  july 85: brought up non-ANSI portion except forgot backspace
; 6  july 85: split off ANSI stuff into other files, added backspace
; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
; 12 july 85: fixed some scrolling bugs, began adding compaq flag
; 9  aug 85:  added cursor position reporting
; 10 aug 85:  added output character translation
; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
;	      direct cursor positioning, takeover of BIOS write_tty,
;	      noticed & squashed 2 related bugs in tab expansion
; 13 feb 86:  Squashed them again, harder
; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
;	      If the addition of the beep period to the
;	      BIOS low timer word results in an overflow, the beep will be
;	      supressed. Also made code compatible eith earlier versions
;	      of assembler.
;------------------------------------------------------------------------

	include nansi_d.asm	; definitions

	; from nansi_f.asm
	extrn	f_escape:near, f_in_escape:near

	; from nansi_p.asm
	extrn	param_end:word, redef_end:word

	; from nansi_i.asm
	extrn	dosfn0:near

	; to nansi_p.asm
	public	f_loopdone
	public	f_not_ansi
	public	f_ansi_exit

	; to both nansi_p.asm and nansi_f.asm
	public	cur_x, cur_y, max_x, cur_attrib

	; to nansi_f.asm
	public	xy_to_regs, get_blank_attrib
	public	port_6845
	public	wrap_flag
	public	cur_parm_ptr
	public	cur_coords, saved_coords, max_y
	public	escvector, string_term
	public	cpr_esc, cprseq
	public	video_mode
	public	lookup
	public	in_g_mode

	; to nansi_i.asm
	public	req_ptr, break_handler
	public	int_29
	if	takeBIOS
	public	new_vid_bios, old_vid_bios
	endif

	; to all modules
	public	xlate_tab_ptr

;--- seg_cs is the CS: override prefix
; (assembler forgets cs: on second "xlat dummy_cs_byte")
seg_cs	macro
	db	2eh
	endm

;--- push_all, pop_all ------------------------------------------------
; Save/restore all user registers.
push_all	macro
	if	is_8088
	push	ax
	push	bx
	push	cx
	push	dx
	push	bp
	push	si
	push	di
	else
	pusha
	endif
	endm

pop_all macro
	if	is_8088
	pop	di
	pop	si
	pop	bp
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	else
	popa
	endif
	endm

keybuf	struc				; Used in getchar
len	dw	?
adr	dw	?
keybuf	ends


ABS40	segment at 40h
	org	1ah
buffer_head	dw	?	; Used in 'flush input buffer' dos call.
buffer_tail	dw	?

	org	49h
crt_mode	db	?
crt_cols	dw	?
crt_len		dw	?
crt_start	dw	?
cursor_posn	dw	8 dup (?)
cursor_mode	dw	?
active_page	db	?
addr_6845	dw	?
crt_mode_set	db	?	; = 7 only if monochrome display adaptor
crt_palette	db	?
	org	6ch
timer_low	dw	?	; low word of time-of-day counter (18.2 hz)

ABS40	ends

	page

CODE	segment byte public 'CODE'
assume	cs:code, ds:code

	; Device Driver Header

	org	0

	dd	-1			; next device
	dw	8013h			; attributes
	dw	strategy		; request header pointer entry
	dw	interrupt		; request entry point
	db	'CON'			; device name (8 char)
	db	5 dup (20h)		;  ... and 5 blanks)

	; Identification- in case somebody TYPEs the assembled driver
	db	27,'[2J'
	db	"Nansi.sys v2.2"
	ife	is_8088
	db	"(80286)"
	endif
	db	': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
	db	13, 10, 26


;----- variable area --------------------
req_ptr label	dword
req_off dw	?
req_seg dw	?

wrap_flag	db	1	; 0 = no wrap past line end
escvector	dw	0	; state vector of ESCape sequencor
video_mode	db	3	; ROM BIOS video mode (2=BW, 3=color)
max_y		db	24
max_cur_x	label	word	; used to get both max & cur at once
max_x		db	79	; line width (79 for 80x25 modes)
cur_coords	label	word
cur_x		db	0	; cursor position (0 = left edge)
cur_y		db	0	;		  (0 = top edge)
saved_coords	dw	?	; holds XY after a SCP escape sequence
string_term	db	0	; either escape or double quote
cur_attrib	db	7	; current char attributes
cur_page	db	0	; current display page
video_seg	dw	?	; segment of video card
f_cptr_seg	dw	?	; part of fastout write buffer pointer
cur_parm_ptr	dw	?	; last byte of parm area now used
port_6845	dw	?	; port address of 6845 card
xlate_tab_ptr	dw	?	; pointer to output translation table
		if	takeBIOS
old_vid_bios	dd	?	; pointer to old video bios routine
		endif

brkkeybuf	db	3	; control C
fnkeybuf	db	?	; holds second byte of fn key codes
cpr_buf		db	8 dup (?), '['
cpr_esc		db	1bh	; descending buffer for cpr function

; following four keybufs hold information about input
; Storage order determines priority- since the characters making up a function
; key code must never be separated (say, by a Control-Break), they have the
; highest priority, and so on.	Keyboard keys (except ctrl-break) have the
; lowest priority.

fnkey	keybuf	<0, fnkeybuf>	; fn key string (0 followed by scan code)
cprseq	keybuf	<0>		; CPR string (ESC [ y;x R)
brkkey	keybuf	<0, brkkeybuf>	; ^C
xlatseq keybuf	<0>		; keyboard reassignment string

;------ xy_to_regs --------------------------------------------
; on entry: x in cur_x, y in cur_y
; on exit:  dx = chars left on line, di = address
; Alters ax, bx.
xy_to_regs	proc	near
	; Find number of chars 'till end of line, keep in DX
	mov	ax, max_cur_x
	mov	bx, ax			; save max_x & cur_x for next block
	mov	ah, 0			; ax = max_x
	xchg	dx, ax
	mov	al, bh
	mov	ah, 0			; ax = cur_x
	sub	dx, ax
	inc	dx			; dx is # of chars till EOL
	; Calculate DI = current address in text buffer
	mov	al, bl			; al = max_x
	inc	al
	mul	cur_y
	add	al, bh			; al += cur_x
	adc	ah, 0			; AX is # of chars into buffer
	add	ax, ax
	xchg	di, ax			; DI is now offset of cursor.
	ret
xy_to_regs	endp


;------- dos_fn_tab -------------
; This table is used in "interrupt" to call the routine that handles
; the requested function.

max_cmd equ	12
dos_fn_tab:
	dw	dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
	dw	dosfn7, dosfn8, dosfn8, nopcmd, nopcmd

;------- strategy ----------------------------------------------------
; DOS calls strategy with a request which is to be executed later.
; Strategy just saves the request.

strategy	proc	far
	mov	cs:req_off,BX
	mov	cs:req_seg,ES
	ret
strategy	endp

;------ interrupt -----------------------------------------------------
; This is where the request handed us during "strategy" is
; actually carried out.
; Calls one of 12 subroutines depending on the function requested.
; Each subroutine returns with exit status in AX.

interrupt	proc	far
	sti
	push_all			; preserve caller's registers
	push	ds
	push	es

	; Read requested function information into registers
	lds	bx,cs:req_ptr
	mov	al,[BX+02h]		; al = function code
;
; The next instruction blows up MASM 1.0 but who cares!!
;
	les	si,[BX+0Eh]		; ES:SI = input/output buffer addr
	mov	cx,[BX+12h]		; cx = input/output byte count

	cmp	al, max_cmd
	ja	unk_command		; too big, exit with error code

	xchg	bx, ax
	shl	bx, 1			; form index to table of words
	mov	ax, cs
	mov	ds, ax
	call	word ptr dos_fn_tab[bx]
int_done:
	lds	bx,cs:req_ptr		; report status
	or	ax, 100h		; (always set done bit upon exit)
	mov	[bx+03],ax

	pop	ES			; restore caller's registers
	pop	DS
	pop_all
	ret				; return to DOS.

unk_command:
	call	badcmd
	jmp	int_done

interrupt	endp

;----- BIOS break handler -----------------------------------------
; Called by BIOS when Control-Break is hit (vector was set up in Init).
; Simply notes that a break was hit.  Flag is checked during input calls.

break_handler	proc
	mov	cs:brkkey.len, 1
	iret
break_handler	endp

	page

;------ badcmd -------------------------------------------------------
; Invalid function request by DOS.
badcmd	proc	near
	mov	ax, 813h		; return "Error: invalid cmd"
	ret
badcmd	endp


;------ nopcmd -------------------------------------------------------
; Unimplemented or dummy function request by DOS.
nopcmd	proc	near
	xor	ax, ax			; No error, not busy.
	ret
nopcmd	endp

;------- dos function #4 ----------------------------------------
; Reads CX characters from the keyboard, places them in buffer at
; ES:SI.
dosfn4	proc	near
	jcxz	dos4done
	mov	di, si
dos4lp: push	cx
	call	getchar
	pop	cx
	stosb
	loop	dos4lp
dos4done:
	xor	ax, ax			; No error, not busy.
	ret
dosfn4	endp

;-------- dos function #5: non-destructive input, no wait ------
; One-character lookahead into the keyboard buffer.
; If no characters in buffer, return BUSY; otherwise, get value of first
; character of buffer, stuff into request header, return DONE.
dosfn5	proc	near
	call	peekchar
	jz	dos5_busy

	lds	bx,req_ptr
	mov	[bx+0Dh], al
	xor	ax, ax			; No error, not busy.
	jmp	short dos5_exit
dos5_busy:
	MOV	ax, 200h		; No error, busy.
dos5_exit:
	ret

dosfn5	endp

;-------- dos function #6: input status --------------------------
; Returns "busy" if no characters waiting to be read.
dosfn6	proc	near
	call	peekchar
	mov	ax, 200h		; No error, busy.
	jz	dos6_exit
	xor	ax, ax			; No error, not busy.
dos6_exit:
	ret
dosfn6	endp

;-------- dos function #7: flush input buffer --------------------
; Clears the IBM keyboard input buffer.  Since it is a circular
; queue, we can do this without knowing the beginning and end
; of the buffer; all we need to do is set the tail of the queue
; equal to the head (as if we had read the entire queue contents).
; Also resets all the device driver's stuffahead buffers.
dosfn7	proc	near
	xor	ax, ax
	mov	fnkey.len, ax		; Reset the stuffahead buffers.
	mov	cprseq.len, ax
	mov	brkkey.len, ax
	mov	xlatseq.len, ax

	mov	ax, abs40
	mov	es, ax
	mov	ax, es:buffer_head	; clear queue by making the tail
	mov	es:buffer_tail, ax	; equal to the head

	xor	ax, ax			; no error, not busy.
	ret
dosfn7	endp

	page
	if	takeBIOS
;--- new_vid_bios -------------------------------------------
; New_vid_bios simply replaces the write_tty call.
; All other calls get sent to the old video bios.
; This gives BIOS ANSI capability.
; However, it takes away the escape character.
; If this is not desired, just tell init to not take over the vector.

new_vid_bios	proc
	cmp	ah, 14
	jz	nvb_write_tty
	jmp	dword ptr cs:old_vid_bios
nvb_write_tty:
	push	cx
	mov	cl, cs:cur_attrib
	; If in graphics mode, BL is new color
	call	in_g_mode		; returns carry set if text mode
	jc	nvb_wt_text
		mov	cs:cur_attrib, bl	; ja?
nvb_wt_text:
	int	29h			; write AL
	mov	cs:cur_attrib, cl	; restore color
	pop	cx
	iret

new_vid_bios	endp
	endif

;------ int_29 ----------------------------------------------
; Int 29 handles DOS quick-access putchar.
; Last device loaded with attribute bit 4 set gets accessed for
; single-character writes via int 29h instead of via interrupt.
; Must preserve all registers.
; Installed as int 29h by dosfn0 (init).
int_29_buf	db	?

int_29	proc	near
	sti
	push	ds
	push	es
	push_all
	mov	cx, 1
	mov	bx, cs
	mov	es, bx
	mov	ds, bx
	mov	si, offset int_29_buf
	mov	byte ptr [si], al
	call	dosfn8
	pop_all
	pop	es
	pop	ds
	iret
int_29	endp

	page
;------ dosfn8 -------------------------------------------------------
; Handles writes to the device (with or without verify).
; Called with
;  CX	 = number of bytes to write
;  ES:SI = transfer buffer
;  DS	 = CS, so we can access local variables.

dosfn8	proc	near

	mov	f_cptr_seg, es	; save segment of char ptr

	; Read the BIOS buffer address/cursor position variables.
	mov	ax, abs40
	mov	ds, ax
	assume	ds:abs40

	; Find current video mode and screen size.
	mov	ax,word ptr crt_mode	; al = crt mode; ah = # of columns
	mov	cs:video_mode, al
	dec	ah			; ah = max column
	mov	cs:max_x, ah

	; Find current cursor coordinates.
	mov	al,active_page
	cbw
	add	ax,ax
	xchg	bx,ax
	mov	ax,cursor_posn[bx]
	mov	cs:cur_coords,AX

	; Find video buffer segment address; adjust it
	; so the offset is zero; return in AX.

	; DS is abs40.
	; Find 6845 address.
	mov	ax, addr_6845
	mov	cs:port_6845, ax
	; Find video buffer address.
	MOV	AX,crt_start
	shr	ax, 1
	shr	ax, 1
	shr	ax, 1
	shr	ax, 1
	add	ah, 0B0h		; assume it's a monochrome card...
	CMP	cs:video_mode,07
	jz	d8_gots
	add	ah, 8			; but if not mode 7, it's color.
d8_gots:
	push	cs
	pop	ds
	assume	ds:code
	mov	video_seg, ax
	mov	es, ax
	call	xy_to_regs		; Set DX, DI according to cur_coords.

	; | If in graphics mode, clear old pseudocursor
	call	in_g_mode
	jc	d8_no_cp
		call	pseudocursor	; write block in xor
d8_no_cp:
	mov	bx, xlate_tab_ptr	; get pointer to translation table

	mov	ah, cur_attrib
	mov	ds, f_cptr_seg		; get segment of char ptr
	assume	ds:nothing
	cld				; make sure we'll increment

	; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
	; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
	; At that speed, it takes 32 milliseconds to fill a screen.

	; Get a character, put it on the screen, repeat 'til end of line
	; or no more characters.
	jcxz	f_loopdone		; if count = 0, we're already done.
	cmp	cs:escvector, 0		; If in middle of an escape sequence,
	jnz	f_in_escapex		; jump to escape sequence handler.

f_tloop:; | If in graphics mode, jump to alternate loop
	; | What a massive kludge!  A better approach would have been
	; | to collect characters for a "write n chars" routine
	; | which would handle both text and graphics modes.
	call	in_g_mode
	jc	f_t_cloop
	jmp	f_g_cloop

f_t_cloop:
	LODSB				; get char! (al = ds:[si++])
	cmp	al, 28			; is it a control char?
	jb	f_control		;  maybe...
f_t_nctl:
	seg_cs
	xlat
	STOSW				; Put Char! (es:[di++] = ax)
	dec	dx			; count down to end of line
	loopnz	f_t_cloop		; and go back for more.
	jz	f_t_at_eol		; at end of line; maybe do a crlf.
	jmp	short f_loopdone

f_looploop:
f_ansi_exit:				; in case we switched into
	loopnz	f_tloop			; a graphics mode
f_t_at_eol:
	jz	f_at_eol

f_loopdone:

	;--------- All done with write request -----------
	; DI is cursor address; cursor position in cur_y, dl.
	mov	ax, cs
	mov	ds, ax			; get our segment back
	assume	ds:code

	; Restore cur_x = max_x - dx + 1.
	mov	al, max_x
	inc	al
	sub	al, dl
	mov	cur_x, al
	; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
	call	set_pseudocursor
	; Return to DOS.
	xor	ax, ax			; No error, not busy.
	ret

	;---- handle control characters ----
	; Note: cur_x is not kept updated in memory, but can be
	; computed from max_x and dx.
	; Cur_y is kept updated in memory.
f_control:
	cmp	al, 27			; Is it an escape?
	jz	f_escapex
	cmp	al, 13			; carriage return?
	jz	f_cr
	cmp	al, 10			; line feed?
	jz	f_lf
	cmp	al, 9			; tab?
	jz	f_tabx
	cmp	al, 8			; backspace?
	jz	f_bs
	cmp	al, 7			; bell?
	jz	f_bell
	jmp	f_nctl			; then it is not a control char.

f_tabx: jmp	f_tab
f_escapex:
	jmp	f_escape
f_in_escapex:
	jmp	f_in_escape

f_bs:	;----- Handle backspace -----------------
	; Moves cursor back one space without erasing.	No wraparound.
	cmp	dl, cs:max_x		; wrap around to previous line?
	ja	fbs_wrap		; yep; disallow it.
	dec	di			; back up one char & attrib,
	dec	di
	inc	dx			; and note one more char left on line.
fbs_wrap:
	jmp	f_looploop

f_bell: ;----- Handle bell ----------------------
	; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
	call	beep
	or	al, al			; clear z
; old (more portable) version:
;	mov	ax, 0e07h		; "write bell to tty simulator"
;	mov	bx, 0			; "page zero, color black"
;	int	10h			; call BIOS
;	mov	ah, cs:cur_attrib	; restore current attribute
;	mov	bx, cs:xlate_tab_ptr	; restore translate table address
;	or	al, al			; al still 7; this clears Z.
	jmp	f_looploop		; Let main loop decrement cx.

f_cr:	;----- Handle carriage return -----------
	; di -= cur_x<<1;		set di= address of start of line
	; dx=max_x+1;			set bx= chars left in line
	mov	al, cs:max_x
	inc	al
	sub	al, dl			; Get cur_x into ax.
	mov	ah, 0
	sub	di, ax
	sub	di, ax
	mov	dl, cs:max_x		; Full line ahead of us.
	inc	dx
	mov	ah, cs:cur_attrib	; restore current attribute
	or	al, 1			; clear z
	jmp	f_looploop		; and let main loop decrement cx

f_at_eol:
	;----- Handle overrunning right end of screen -------
	; cx++;				compensate for double loop
	; if (!wrap_flag) { dx++; di-=2; }
	; else do_crlf;
	inc	cx
	test	cs:wrap_flag, 1
	jnz	feol_wrap
		dec	di
		dec	di
		inc	dx
		jmp	f_looploop
feol_wrap:
	; dx=max_x+1;			set bx= chars left in line
	; di -= 2*(max_x+1);
	; do_lf
	mov	dl, cs:max_x
	inc	dx
	sub	di, dx
	sub	di, dx
	; fall thru to line feed routine

f_lf:	;----- Handle line feed -----------------
	; if (cur_y >= max_y) scroll;		scroll screen up if needed
	; else { cur_y++; di += max_x<<1;	else increment Y

	mov	al, cs:max_y
	cmp	cs:cur_y, al
	jb	flf_noscroll
		call	scroll_up		; preserves bx,cx,dx,si,di
		jmp	short flf_done
flf_noscroll:
	inc	cs:cur_y
	mov	al, cs:max_x
	mov	ah, 0
	inc	ax
	add	ax, ax
	add	di, ax
flf_done:
	mov	ah, cs:cur_attrib		; restore current attribute
	or	al, 1			; clear z
	jmp	f_looploop		; and let main loop decrement cx

f_tab:	;----- Handle tab expansion -------------
	; Get cur_x into al.
	mov	al, cs:max_x
	inc	al
	sub	al, dl
	; Calculate number of spaces to output.
	push	cx			; save cx
	mov	ch, 0
	mov	cl, al			; get zero based x coordinate
	and	cl, 7
	neg	cl
	add	cl, 8			; 0 -> 8, 1 -> 8, ... 7 -> 1
	sub	dx, cx			; update chars-to-eol, maybe set z
	pushf				; || save Z for main loop
	; ah is still current attribute.  Move CX spaces to the screen.
	mov	al, ' '
	call	in_g_mode		; | graphics mode
	jnc	f_tab_putc		; |
	REP	STOSW
	popf				; || restore Z flag for main loop test
	pop	cx			; restore cx
	jmp	f_looploop		; Let main loop decrement cx.

;--------------- graphics mode support -----------------------

f_tab_putc:	; graphics mode- call putc to put the char
	add	dx, cx			; move back to start of tab
f_tp_lp:
	call	putchar
	dec	dx			; go to next cursor position
	loop	f_tp_lp
	popf				; Z set if wrapped around EOL
	pop	cx
	jmp	f_looploop

;---- in_g_mode -------------
; Returns Carry set if not in a graphics mode.
; Preserves all registers.

in_g_mode	proc	near
	cmp	cs:video_mode, 4
	jb	igm_stc
	cmp	cs:video_mode, 7
	jz	igm_stc
	clc
	ret
igm_stc:
	stc
	ret
in_g_mode	endp

;---- Where to go when a character turns out not to be special
f_nctl:
f_not_ansi:
	call	in_g_mode
	jnc	f_g_nctl		; graphics mode
f_jmptnctl:
	jmp	f_t_nctl		; text mode

;---- Alternate main loop for graphics mode ----
f_g_cloop:
	LODSB				; get char! (al = ds:[si++])
	cmp	al, 28			; is it a control char?
	jb	f_g_control		;  maybe...
f_g_nctl:
	seg_cs
	xlat
	call	putchar
	dec	dx			; count down to end of line
	loopnz	f_g_cloop		; and go back for more.
	jz	f_g_at_eol		; at end of line; maybe do a crlf.
	jmp	f_loopdone

f_g_control:	jmp	f_control
f_g_at_eol:	jmp	f_at_eol

;---- putchar ------------------------------------------------
; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
; On entry, registers set up as per xy_to_regs.
; Preserves all registers.
putchar proc	near
	push	dx
	push	cx
	push	bx
	push	ax
	; 1. Set cursor position.
	mov	al, cs:max_x
	inc	al
	sub	al, dl
	mov	cs:cur_x, al
	mov	dx, cs:cur_coords	; get X & Y into DX
	xor	bx, bx			; choose dpy page 0
	mov	ah, 2			; chose "Set Cursor Position"
	int	10h			; call ROM BIOS
	; 2. Write char & attribute.
	mov	cx, 1
	pop	ax			; get char in AL
	push	ax
	mov	bl, ah			; attribute in BL
	mov	bh, 0
	mov	ah, 9
	int	10h
	pop	ax
	pop	bx
	pop	cx
	pop	dx
	ret
putchar endp

;---- set_pseudocursor ------------
; If in graphics mode, set pseudocursor, else set real cursor.
; Destroys DS!!!!

set_pseudocursor	proc	near
	call	in_g_mode
	jnc	pseudocursor
; old (more portable, but slower) version
;	mov	dx, cur_coords		; get X & Y into DX
;	xor	bx, bx			; choose dpy page 0
;	mov	ah, 2			; chose "Set Cursor Position"
;	int	10h			; call ROM BIOS

	; Write directly to 6845 cursor address register.
	mov	bx, di
	shr	bx, 1			; convert word index to byte index

	mov	dx, port_6845
	mov	al, 0eh
	out	dx, al

	jmp	$+2
	inc	dx
	mov	al, bh
	out	dx, al

	jmp	$+2
	dec	dx
	mov	al, 0fh
	out	dx, al

	jmp	$+2
	inc	dx
	mov	al, bl
	out	dx, al

	; Set cursor position in low memory.
	assume	ds:abs40
	mov	ax, abs40
	mov	ds, ax
; Does anybody ever use anything but page zero?
;	mov	al,active_page
;	cbw
;	add	ax,ax
;	xchg	bx,ax
	mov	ax, cs:cur_coords
	mov	cursor_posn,ax
	ret

	assume	ds:code
set_pseudocursor	endp


;---- pseudocursor --------------------------------------------------
; Writes a color 15 block in XOR at the current cursor location.
; Preserves DS, ES, BX, CX, DX, SI, DI.
; Should be disableable- the pseudocursor slows down single-char
; writes by a factor of three.
pseudocursor	proc	near
	mov	ax, 8f16h	; xor, color 15, ^V (small block)
	call	putchar
	ret
pseudocursor	endp

;--------------- end of graphics mode support --------------------

dosfn8	endp

;--- get_blank_attrib ------------------------------------------------
; Determine new attribute and character for a new blank region.
; Use current attribute, just disallow blink and underline.
; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
; Returns result in AH, preserves all other registers.
get_blank_attrib	proc	near
	mov	ah, 0
	call	in_g_mode
	jnc	gb_aok			; if graphics mode, 0 is bkgnd

	mov	ah, cs:cur_attrib
	and	ah, 7fh			; disallow blink
	cmp	cs:video_mode, 7	; monochrome?
	jnz	gb_aok
		cmp	ah, 1		; underline?
		jnz	gb_aok
		mov	ah, 7		; yep- set it to normal.
gb_aok: ret
get_blank_attrib	endp


;---- scroll_up ---------------------------------------------------
; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
; Moves screen up 1 line, fills the last line with blanks.
; Attribute of blanks is the current attribute sans blink and underline.

scroll_up	proc	near
	push	ax
	push	bx
	push	cx
	push	dx

	call	get_blank_attrib
	mov	bh, ah			; color to use on new blank areas
	mov	al, 1			; AL is number of lines to scroll.
	mov	ah, 6			; BIOS: scroll up
	mov	cl, 0			; upper-left-x of data to scroll
	mov	ch, 0			; upper-left-y of data to scroll
	mov	dl, cs:max_x		; lower-rite-x
	mov	dh, cs:max_y		; lower-rite-y (zero based)
	int	10h			; call BIOS to scroll a rectangle.

	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
scroll_up	endp

;---- lookup -----------------------------------------------
; Called by getchar, peekchar, and key to see if a given key has
; been redefined.
; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
; Returns with Z cleared if no redefinition; otherwise,
; Z is set, SI points to redefinition string, CX is its length.
; Preseves AL, all but CX and SI.
; Redefinition table organization:
;  Strings are stored in reversed order, first char last.
;  The word following the string is the character to be replaced;
;  the next word is the length of the string sans header.
; param_end points to the last byte used by the parameter buffer;
; redef_end points to the last word used by the redef table.

lookup	proc	near
	mov	si, redef_end		; Start at end of table, move down.
	or	al, al
	jz	lu_lp
	mov	ah, 0			; clear extraneous scan code
lu_lp:	cmp	si, param_end
	jbe	lu_notfound		; If below redef table, exit.
	mov	cx, [si]
	cmp	ax, [si-2]		; are you my mommy?
	jz	lu_gotit
	sub	si, 4
	sub	si, cx			; point to next header
	jmp	lu_lp
lu_notfound:
	or	si, si			; clear Z
	jmp	short lu_exit
lu_gotit:
	sub	si, 2
	sub	si, cx			; point to lowest char in memory
	cmp	al, al			; set Z
lu_exit:
	ret
lookup	endp

;---- searchbuf --------------------------------------------
; Called by getchar and peekchar to see if any characters are
; waiting to be gotten from sources other than BIOS.
; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
searchbuf	proc	near
	; Search the stuffahead buffers.
	mov	cx, 4			; number of buffers to check for chars
	mov	bx, offset fnkey - 4
sbloop: add	bx, 4			; point to next buffer record
	mov	si, [bx].len
	or	si, si			; empty?
	loopz	sbloop			; if so, loop.
	ret
searchbuf	endp

;---- getchar -----------------------------------------------
; Returns AL = next char.
; Trashes AX, BX, CX, BP, SI.
getchar proc	near
gc_searchbuf:
	; See if any chars are waiting in stuffahead buffers.
	call	searchbuf
	jz	gc_trykbd		; No chars?  Try the keyboard.
	; A nonempty buffer was found.
	dec	[bx].len
	dec	si
	mov	bp, [bx].adr		; get pointer to string
	mov	al, byte ptr ds:[bp][si]; get the char
	; Recognize function key sequences, move them to highest priority
	; queue.
	sub	si, 1			; set carry if si=0
	jc	gc_nofnkey		; no chars left -> nothing to protect.
	cmp	bx, offset fnkey
	jz	gc_nofnkey		; already highest priority -> done.
	or	al, al
	jnz	gc_nofnkey		; nonzero first byte -> not fnkey.
	; Found a function key; move it to highest priority queue.
	dec	[bx].len
	mov	ah, byte ptr ds:[bp][si]; get the second byte of fn key code
gc_fnkey:
	mov	fnkey.len, 1
	mov	fnkeybuf, ah		; save it.
gc_nofnkey:
	; Valid char in AL.  Return with it.
	jmp	short gcdone

gc_trykbd:
	; Actually get a character from the keyboard.
	mov	ah, 0
	int	16h			; BIOS returns with char in AX
	; If it's Ctrl-break, it has already been taken care of.
	or	ax, ax
	jz	gc_trykbd

	; Look in the reassignment table to see if it needs translation.
	call	lookup			; Z=found; CX=length; SI=ptr
	jnz	gc_noredef
	; Okay; set up the reassignment, and run thru the translation code.
	mov	xlatseq.len, cx
	mov	xlatseq.adr, si
	jmp	gc_searchbuf
gc_noredef:
	; Is it a function key?
	cmp	al, 0
	jz	gc_fnkey		; yep- special treatment.
gcdone: ret	; with character in AL.

getchar endp

;---- peekchar -----------------------------------------------
; Returns Z if no character ready, AL=char otherwise.
; Trashes AX, BX, CX, BP, SI.
peekchar	proc	near
pc_searchbuf:
	call	searchbuf
	jz	pc_trykbd		; No chars?  Try the keyboard.
	; A nonempty buffer was found.
	dec	si
	mov	bp, [bx].adr		; get pointer to string
	mov	al, byte ptr ds:[bp][si]; get the char
	; Valid char from buffer in AL.  Return with it.
	jmp	short pcdone
pc_trykbd:
	; Actually peek at the keyboard.
	mov	ah, 1
	int	16h			; BIOS returns with char in AX
	jz	pcexit
	; If it's control-break, it's already been taken care of.
	or	ax, ax
	jnz	pc_notbrk
	mov	ah, 0
	int	16h			; so get rid of it!
	jmp	short pc_trykbd
pc_notbrk:
	; Look in the reassignment table to see if it needs translation.
	call	lookup			; Z=found; CX=length; SI=ptr
	jnz	pcdone			; Nope; just return the char.
	; Okay; get the first code to be returned.
	add	si, cx
	mov	al, [si-1]
pcdone: or	ah, 1			; NZ; char ready!
pcexit: ret	; with character in AL, Z true if no char waiting.
peekchar	endp

;---- beep ------------------------------------------------------
; Beep speaker; period given by beep_div, duration by beep_len.
; Preserves all registers.

beep_div	dw	1300		; fairly close to IBM beep
beep_len	dw	3		; 3/18 sec- shorter than IBM

beep	proc	near
	push_all

	mov	al, 10110110b		; select 8253
	mov	dx, 43h			; control port address
	out	dx, al
	dec	dx			; timer 2 address
	mov	ax, cs:beep_div
	jmp	$+2
	out	dx, al			; low byte of divisor
	xchg	ah, al
	jmp	$+2
	out	dx, al			; high byte of divisor
	mov	dx, 61h
	jmp	$+2
	in	al, dx			; get current value of control bits
	push	ax
	or	al, 3
	jmp	$+2
	out	dx, al			; turn speaker on

	; Wait for desired duration by monitoring time-of-day 18 Hz clock
	push	es
	mov	ax, abs40
	mov	es, ax
	assume	es:abs40
	mov	bx, timer_low
	mov	cx, -1
beeplp:	mov	ax, timer_low
	sub	ax, bx
	cmp	ax, cs:beep_len
	jg	beepover
	loop	beeplp
beepover:
	pop	es
	assume	es:code

	; Turn off speaker
	pop	ax
	and	al, not 3		; turn speaker off
	out	dx, al
	pop_all
	ret
beep	endp

CODE	ends

	end				; of nansi.asm
@//E*O*F nansi.asm//
chmod u=rw,g=r,o=r nansi.asm
 
echo x - nansi_d.asm
sed 's/^@//' > "nansi_d.asm" <<'@//E*O*F nansi_d.asm//'
; Definitions for the new ANSI driver.
; (C) 1986 Daniel Kegel
; May be distributed for educational and personal use only

takeBIOS	equ	0	; take over BIOS write_tty if true
is_8088		equ	1	; no fancy instructions if true
cls_homes_too	equ	1	; set true for ANSI.SYS compatibility

;Comment this out if running MASM 1.0

	if	is_8088
		.8086
	else
		.286c
	endif

@//E*O*F nansi_d.asm//
chmod u=rw,g=r,o=r nansi_d.asm
 
echo x - nansi_f.asm
sed 's/^@//' > "nansi_f.asm" <<'@//E*O*F nansi_f.asm//'
; The ANSI control subroutines.
; (C) 1986 Daniel Kegel, Pasadena, CA
; May be distributed for educational and personal use only
; Each routine is called with the following register usage:
;  AX = max(1, value of first parameter)
;  Z flag is set if first parameter is zero.
;  CX = number of paramters
;  SI = offset of second parameter from CS
;  DS = CS
;  ES:DI points to the current location on the memory-mapped screen.
;  DX is number of characters remaining on the current screen line.
; The control routine is free to trash AX, BX, CX, SI, and DS.
; It must preserve ES, and can alter DX and DI if it wants to move the
; cursor.
;
; Revisions:
;  19 Aug 85: Fixed horrible bug in insert/delete line.
;  26 Aug 85: Fixed simple limit-to-one-too-few-lines bug in ins/del line;
;  anyway, it inserts 24 lines when on line 2 now.  Whether it's fixed...
;  4 Sept 85: Fixed bug created on 26 Aug 85; when limiting ins/del line
;  count, we are clearing, not scrolling; fixed BIOS call to reflect this.
;  30 Jan 86: Added EGA cursor patch
;  31 Jan 86: Disabled insert/delete char in graphics modes
;	      Implemented keyboard redefinition reset
;  1 Feb 86: added video_mode and max_x test after mode set
;----------------------------------------------------------------

	include nansi_d.asm

	; To nansi_p.asm
	public	ansi_fn_table

	; From nansi.asm
	extrn	port_6845:word
	extrn	cur_coords:word, saved_coords:word
	extrn	cur_x:byte, max_x:byte
	extrn	cur_y:byte, max_y:byte
	extrn	cur_attrib:byte, wrap_flag:byte
	extrn	xy_to_regs:near
	extrn	get_blank_attrib:near
	extrn	xlate_tab_ptr:word
	extrn	cpr_esc:byte, cprseq:word
	extrn	video_mode:byte
	extrn	lookup:near
	extrn	in_g_mode:near

	; from nansi_p.asm
	extrn	param_buffer:word	; used in keyboard programming
	extrn	param_end:word
	extrn	redef_end:word

keybuf	struc				; used in making cpr sequence
len	dw	?
adr	dw	?
keybuf	ends


ABS40	segment at 40h
	org	1ah
buffer_head	dw	?	; Used in 'flush input buffer' dos call.
buffer_tail	dw	?

	org	49h
crt_mode	db	?
crt_cols	dw	?
crt_len		dw	?
crt_start	dw	?
cursor_posn	dw	8 dup (?)
cursor_mode	dw	?
active_page	db	?
addr_6845	dw	?
crt_mode_set	db	?
crt_palette	db	?

ABS40	ends

code	segment byte public 'CODE'
	assume cs:code, ds:code

;----- byteout ---------------------------------------------------
; Converts al to a decimal ASCII string (in 0..99),
; stores it at ES:DI++.  Returns DI pointing at byte after last digit.
; Destroys DL.

byteout proc	near
	aam
	add	ax, 3030h
	xchg	ah, al
	stosb
	xchg	ah, al
	stosb
	ret
byteout endp

;----- ansi_fn_table -----------------------------------
; Table of offsets of terminal control subroutines in order of
; the character that invokes them, @..Z, a..z.	Exactly 53 entries.
; All the subroutines are defined below in this module.
ansi_fn_table	label	word
	dw	ic,  cup, cdn, cfw, cbk		; @, A, B, C, D
	dw	nul, nul, nul, hvp, nul		; E, F, G, H, I
	dw	eid, eil, il,  d_l, nul		; J, K, L, M, N
	dw	nul, dc,  nul, nul, nul		; O, P, Q, R, S
	dw	nul, nul, nul, nul, nul		; T, U, V, W, X
	dw	nul, nul			; Y, Z
	dw	nul, nul, nul, nul, nul		; a, b, c, d, e
	dw	hvp, nul, sm,  nul, nul		; f, g, h, i, j
	dw	nul, rm,  sgr, dsr, nul		; k, l, m, n, o
	dw	key, nul, nul, scp, nul		; p, q, r, s, t
	dw	rcp, nul, nul, nul, xoc		; u, v, w, x, y
	dw	nul				; z

ansi_functions	proc	near		; set return type to NEAR

;----- nul ---------------------------------------------
; No-action ansi sequence; called when unknown command given.
nul:	ret

;----- Cursor Motion -----------------------------------------------

;-- cursor to y,x
hvp:	or	al, al		; First parameter is desired Y coordinate.
	jz	hvp_yok
	dec	ax		; Convert to zero-based coordinates.
hvp_yok:mov	cur_y, al
	; Get second parameter, if it is there, and set X with it.
	xor	ax, ax
	cmp	cx, 2		; was there a second parameter?
	jb	hvp_xok
	lodsb			; yes.
	or	al, al
	jz	hvp_xok
	dec	ax		; convert to zero-based coordinates.
hvp_xok:mov	cur_x, al

	; Clip to maximum coordinates.
hvp_set:
	mov	ax, cur_coords		; al = x, ah = y
	cmp	al, max_x
	jbe	hvp_sxok
		mov	al, max_x
		mov	cur_x, al
hvp_sxok:
	cmp	ah, max_y
	jbe	hvp_syok
		mov	al, max_y
		mov	cur_y, al
hvp_syok:
	; Set values of DX and DI accordingly.
	call	xy_to_regs
	ret

;-- cursor forward --
cfw:	add	cur_x, al
	jmp	hvp_set

;-- cursor back -----
cbk:	sub	cur_x, al
	jae	cbk_ok
		mov	cur_x, 0
cbk_ok: jmp	hvp_set

;-- cursor down -----
cdn:	add	cur_y, al
	jmp	hvp_set

;-- cursor up -------
cup:	sub	cur_y, al
	jae	cup_ok
		mov	cur_y, 0
cup_ok: jmp	hvp_set

;-- save cursor position --------------------------------------
scp:	mov	ax, cur_coords
	mov	saved_coords, ax
	ret

;-- restore cursor position -----------------------------------
rcp:	mov	ax, saved_coords
	mov	cur_coords, ax
	jmp	hvp_set		; Clip in case we have switched video modes.

;-- set graphics rendition ------------------------------------
; Modifies the color in which new characters are written.

sgr:	dec	si		; get back pointer to first parameter
	or	cx, cx		; Did he give any parameters?
	jnz	sgr_loop
		mov	byte ptr [si], 0	; no parameters, so fake
		inc	cx			; one with the default value.
	; For each parameter
sgr_loop:
		lodsb				; al = next parameter
		; Search color table
		push	cx
		mov	cx, colors
		mov	bx, offset color_table-3
sgr_search:
			add	bx, 3
			cmp	al, byte ptr [bx]
			loopnz	sgr_search	; until match found or done
		jnz	sgr_loopx

		; If parameter named a known color, set the current
		; color variable.
		mov	ax, [bx+1]
		and	cur_attrib, al
		or	cur_attrib, ah
sgr_loopx:
		pop	cx
		loop	sgr_loop		; until no more parameters.
	ret

;-- erase in line ----------------------------------------
; Uses BIOS to scroll away a one-line rectangle
eil:	push	dx
	mov	cx, cur_coords
	mov	dh, ch
	jmp	short scrollem

;-- erase in display -------------------------------------
; Uses BIOS to scroll away all of display
eid:	cmp	al, 2
	jnz	eid_ignore	; param must be two
	if	cls_homes_too
		mov	cur_coords, 0
		call	xy_to_regs
	endif
	push	dx
	xor	cx, cx
	mov	dh, max_y
scrollem:
	call	get_blank_attrib
	mov	bh, ah
	mov	dl, max_x
	mov	ax, 600h
	int	10h
	pop	dx
eid_ignore:
	ret

;-- device status report --------------------------------
; Stuffs an escape, a left bracket, current Y, semicolon, current X,
; a capital R, and a carriage return into input stream.
; The coordinates are 1 to 3 decimal digits each.

dsr:	push	di
	push	dx
	push	es
	mov	ax, cs
	mov	es, ax
	std			; Store string in reversed order for fun
	mov	di, offset cpr_esc - 2
	mov	al, cur_y
	inc	al		; convert to one-based coords
	call	byteout		; row
	mov	al, ';'		; ;
	stosb
	mov	al, cur_x
	inc	al		; convert to one-based coords
	call	byteout		; column
	mov	al, 'R'		; R ANSI function 'Cursor Position Report'
	stosb
	mov	al, 13
	mov	word ptr cprseq.adr, di ; save pointer to last char in string
	stosb				; send a carriage return, too
	mov	ax, offset cpr_esc
	sub	ax, di			; ax is # of characters in string
	mov	word ptr cprseq.len, ax ; pass info to the getchar routine
	cld
	pop	es
	pop	dx
	pop	di
	ret

;-- keyboard reassignment -------------------------------
; Key reassignment buffer is between param_end and redef_end+2, exclusive.
; When it shrinks or grows, param_end is moved.
; Format of an entry is as follows:
;   highest address -> length:word (may be 0)
;		       key to replace:word     (either hi or low byte is zero)
;			   .
;			   .	new key value, "length" bytes long
;			   .
;   lowest address  -> next entry, or free space.
; If no arguments are given, keyboard is reset to default condition.
; Otherwise, first parameter (or first two, if first is zero) defines
; the key whose value is to be changed, and the following parameters
; define the key's new, possibly zero-length, value.

key:
	; Is this a reset?
	or	cx, cx
	jz	key_init
	; Get the first (or first two) parameters
	cld
	dec	si	; point to first param
	dec	cx	; Assume it's a fn key, get two params
	dec	cx
	lodsw
	or	al, al	; Is it a function key?
	jz	key_fnkey
		; It's not a function key- put second param back
		inc	cx
		dec	si
key_fnkey:
	; Key to redefine now in AX.  If it's already redefined,
	; lookup will set Z, point SI to redef string, set CX to its length.
	push	di
	push	es
	push	cx
	push	si

	std			; moving up, must move from top down
	push	ds
	pop	es		; string move must have ES=DS
	call	lookup		; rets Z if redefined...
	jnz	key_newkey
	; It's already defined.  Erase its old definition- i.e., move
	; region param_end+1..SI-1 upwards CX+4 bytes, add CX+4 to param_end.
	add	cx, 4
	mov	bp, param_end	; save old value in bp...
	add	param_end, cx
	dec	si		; start at (SI-1)
	mov	di, si
	add	di, cx		; move to (start + CX+4)
	mov	cx, si
	sub	cx, bp		; length of region old_param_end+1..start
	rep	movsb
key_newkey:
	; Key not redefined.  See if there's enough room to redefine it.
	pop	si		; get back pointer to redef string
	pop	cx		; get back number of bytes in redef string
	mov	di, param_end	; hi byte of new redef record, hi byte of len
	sub	di, 4		; hi byte of new data field
	mov	bx, di
	sub	bx, cx		; hi byte of remaining buffer space
	sub	bx, 16		; better be at least 16 bytes room
	cmp	bx, param_buffer
	jb	key_popem	; nope- forget it.
	; Nothing in the way now!
	mov	[di+3], cx	; save length field
	mov	[di+1], ax	; save name field
	jcxz	key_nullstring
key_saveloop:			; save data field
	movsb
	add	si, 2		; input string ascending, output descending
	loop	key_saveloop
key_nullstring:
	mov	param_end, di	; save adr of new hi byte of free area
key_popem:
	pop	es
	pop	di

key_exit:
	cld
	ret

key_init:
	; Build the default redefinition table:
	;	control-printscreen -> control-P
	push	es
	push	ds
	pop	es
	std
	mov	di, redef_end
	mov	ax, 1
	stosw
	mov	ax, 7200h	; control-printscreen
	stosw
	mov	al, 16		; control P
	stosb
	mov	param_end, di	; save new bottom of redef table
	pop	es
	jmp	key_exit

;---- Delete/Insert Lines -------------------------------
; AL is number of lines to delete/insert.
; Preserves DX, DI; does not move cursor.

d_l:	; Delete lines.
	mov	ah, 6			; BIOS: scroll up
	jmp	short il_open

il:	; Insert lines.
	mov	ah, 7			; BIOS: scroll down

il_open:
	; Whether inserting or deleting, limit him to (max_y - cur_y) lines;
	; if above that, we're just clearing; set AL=0 so BIOS doesn't burp.
	mov	bh, max_y
	sub	bh, cur_y
	cmp	al, bh
	jbe	il_ok			; DRK 9/4...
		mov	al, 0		; he tried to move too far
il_ok:
	push	ax
	call	get_blank_attrib
	mov	bh, ah			; color to use on new blank areas
	pop	ax			; AL is number of lines to scroll.

	mov	cl, 0			; upper-left-x of data to scroll
	mov	ch, cur_y		; upper-left-y of data to scroll
	push	dx
	mov	dl, max_x		; lower-rite-x
	mov	dh, max_y		; lower-rite-y (zero based)
	int	10h			; call BIOS to scroll a rectangle.
	pop	dx
	ret				; done.

;-- Insert / Delete Characters ----------------------------
; AL is number of characters to insert or delete.
; Preserves DX, DI; does not move cursor.

ic:	mov	ch, 1			; 1 => swap dest & source below
	jmp	short dc_ch

dc:	mov	ch, 0

dc_ch:
	call	in_g_mode
	jnc	dc_ret			; | if in graphics mode, ignore.

	; AL = number of chars to ins or del (guarenteed nonzero).
	; Limit him to # of chars left on line.
	cmp	al, dl
	jbe	dc_cok
		mov	al, dl
dc_cok:
	push	di			; DI is current address of cursor
	xchg	ax, cx			; CX gets # of chars to ins/del
	mov	bp, cx			; BP gets # of columns to clear.

	; Set up source = destination + cx*2, count = dx - cx
	mov	ch, 0			; make it a word
	mov	si, di
	add	si, cx
	add	si, cx
	neg	cl
	add	cl, dl
	mov	ch, 0			; CX = # of words to transfer
	cld				; REP increments si & di

	; If this is an insert, then flip transfer around in both ways.
	test	ah, 1
	jz	dc_noswap
		xchg	di, si		; source <-> dest
		std			; up <-> down
		mov	ax, cx		; make move over same range
		dec	ax
		add	ax, ax		; AX=dist from 1st to last byte.
		add	di, ax		; Start transfer at high end of block
		add	si, ax		;  instead of low end.
dc_noswap:
	; Move those characters.
	push	es
	pop	ds
	rep	movsw
	mov	cx, bp
	; Figure out what color to make the new blanks.
	call	get_blank_attrib
	mov	al, ' '
	; Blank out vacated region.
	rep	stosw

	; All done.
	cld				; restore normal REP state and
	pop	di			;  cursor address.
dc_ret: ret


;---- set / reset mode ---------------------------------------
; Sets graphics/text mode; also sets/resets "no wrap at eol" mode.
sm:	mov	cl, 0ffh	; set
sm_rs:
	; Is it "wrap at eol" ?
	cmp	al, 7
	jnz	sm_notwrap
		mov	wrap_flag, cl	; true = wrap at EOL
		jmp	short sm_done
sm_notwrap:
	; Is it "set highest number of screen lines available"?
	cmp	al, 43
	jnz	sm_video
		; Only valid for the Enhanced Graphics Adaptor on
		; a monochrome display or an enhanced color display.
		; Test presence of EGA by calling BIOS fn 12h.10h.
		mov	ah, 12h
		mov	bx, 0ff10h
		int	10h			; bh=0-1, bl=0-3 if EGA
		test	bx, 0FEFCH
		jnz	sm_done			; sorry, charlie
;		mov	port_6845, 3d4h
;		mov	al, video_mode
;		and	al, 7
;		cmp	al, 7			; monochrome monitor?
;		jnz	sm_colormon
;			mov	byte ptr port_6845, low(3b4h)
;sm_colormon:
		; 43 line mode only allowed in text modes, for now.
		call	in_g_mode
		jnc	sm_done

		mov	ah, 0			; "Set video mode"
		mov	al, video_mode		; Re-init current mode
		int	10h

		mov	ax,1112h		; Load 8x8 font
		mov	bl,0			; (instead of 8x14)
		int	10h

		mov	ax, 1200h		; Load new printscreen
		mov	bl, 20h
		int	10h

		mov	ah,1
		mov	cx,0707h		; (Load cursor scan lines)
		int	10h
		; | Patch; this gotten by painful observation of
		; | IBM's professional editor.	I think there's a
		; | documented bug in Video Bios's "load cursor scan line"
		; | call; try looking in latter 1985 PC Tech Journal.
		mov	dx, port_6845		; '6845' command reg
		mov	al, 10
		out	dx, al
		jmp	$+2
		inc	dx
		mov	al, 7
		out	dx, al			; set cursor start line
		; Assume that gets us 43 lines.
		mov	max_y, 42
		jmp	short sm_home
sm_video:
	; It must be a video mode.  Call BIOS.
	mov	ah, 0		; "set video mode"
	int	10h
	; Assume that gets us 25 lines.
	mov	max_y, 24
sm_home:
	; Read the BIOS buffer address/cursor position variables.
	mov	ax, abs40
	push	ds
	mov	ds, ax
	assume	ds:abs40
	; Find current video mode and screen size.
	mov	ax,word ptr crt_mode	; al = crt mode; ah = # of columns
	pop	ds
	mov	video_mode, al
	dec	ah			; ah = max column
	mov	max_x, ah

	; Since cursor may end up in illegal position, it's best to
	; just go home after switching video modes.
	mov	cur_coords, 0
	call	xy_to_regs
sm_done:
	ret

rm:	mov	cl, 0		; reset
	jmp	sm_rs

;------- Output Character Translation ----------------------
; A decidedly nonstandard function, possibly useful for editing files
; intended to be printed by daisywheel printers with strange wheels.
; (The letter 'y' was chosen to conflict with the VT100 self-test command.)
; Usage: ESC [ #1;#2 y
; where #1 is the character to redefine
;	#2 is the new display value
; If only ESC [ #1 y is sent, character #1 is reset to its default value.
; If only ESC [ y is sent, the entire table is reset to the default value.
; (If only ESC [ #1; y is sent, character #1 is set to zero... sigh.)

xoc:	; Xlate output character
	mov	bx, xlate_tab_ptr
	jcxz	xoc_reset	; if no parameters, reset table to 1:1
	dec	si		; point to first parameter
	lodsw			; first parameter to AL, second to AH
	dec	cx		; is parameter count 1?
	jnz	xoc_bothparams
		mov	ah, al	; if only one param, reset that char.
xoc_bothparams:
	add	bl, al
	adc	bh, 0		; bx points to entry for char AL
	mov	byte ptr [bx], ah	; change that entry
xoc_done:
	ret

xoc_reset:
	; Fill table with default values- i.e. 0, 1, 2, ... 255.
	xor	ax, ax
xoc_loop:
	mov	byte ptr [bx], al
	inc	bx
	inc	al
	jnz	xoc_loop
	jmp	xoc_done

ansi_functions	endp	; end dummy procedure block



;-------- Color table -----------------------------------------
; Used in "set graphics rendition"
colors	equ	22			; number of colors in table
color_table:
	db	0, 000h,07h		; all attribs off; normal.
	db	1, 0ffh,08h		; bold
	db	4, 0f8h,01h		; underline
	db	5, 0ffh,80h		; blink
	db	7, 0f8h,70h		; reverse
	db	8, 088h,00h		; invisible

	db	30,0f8h,00h		; black foreground
	db	31,0f8h,04h		; red
	db	32,0f8h,02h		; green
	db	33,0f8h,06h		; yellow
	db	34,0f8h,01h		; blue
	db	35,0f8h,05h		; magenta
	db	36,0f8h,03h		; cyan
	db	37,0f8h,07h		; white

	db	40,08fh,00h		; black background
	db	41,08fh,40h		; red
	db	42,08fh,20h		; green
	db	43,08fh,60h		; yellow
	db	44,08fh,10h		; blue
	db	45,08fh,50h		; magenta
	db	46,08fh,30h		; cyan
	db	47,08fh,70h		; white


code	ends
	end				; of nansi_f.asm

@//E*O*F nansi_f.asm//
chmod u=rw,g=r,o=r nansi_f.asm
 
echo x - nansi_i.asm
sed 's/^@//' > "nansi_i.asm" <<'@//E*O*F nansi_i.asm//'
;------ nansi_i.asm ----------------------------------------------
; Contains code only needed at initialization time.
; (C) 1986 Daniel Kegel
; May be distributed for educational and personal use only
;-----------------------------------------------------------------

	include nansi_d.asm		; definitions

	; to nansi.asm
	public	dosfn0

	; from nansi.asm
	extrn	break_handler:near
	extrn	int_29:near
	if	takeBIOS
	extrn	new_vid_bios:near
	extrn	old_vid_bios:dword
	endif
	extrn	req_ptr:dword
	extrn	xlate_tab_ptr:word

	; from nansi_p.asm
	extrn	param_buffer:word	; adr of first byte free for params
	extrn	param_end:word		; adr of last byte used for params
	extrn	redef_end:word		; adr of last used byte for redefs


code	segment byte public 'CODE'
	assume	cs:code, ds:code

;-------- dos function # 0 : init driver ---------------------
; Initializes device driver interrupts and buffers, then
; passes ending address of the device driver to DOS.
; Since this code is only used once, the buffer can be set up on top
; of it to save RAM.

dosfn0	proc	near
	; Install BIOS keyboard break handler.
	xor	ax, ax
	mov	ds, ax
	mov	bx, 6Ch
	mov	word ptr [BX],offset break_handler
	mov	[BX+02], cs
	; Install INT 29 quick putchar.
	mov	bx, 0a4h
	mov	word ptr [bx], offset int_29
	mov	[bx+2], cs
	if	takeBIOS
	; Install INT 10h video bios replacement.
	mov	bx, 40h
	mov	ax, [bx]
	mov	word ptr cs:old_vid_bios, ax
	mov	ax, [bx+2]
	mov	word ptr cs:old_vid_bios[2], ax
	mov	word ptr [bx], offset new_vid_bios
	mov	word ptr [bx+2], cs
	endif

	push	cs
	pop	ds
	push	cs
	pop	es			; es=cs so we can use stosb
	cld				; make sure stosb increments di

	; Calculate addresses of start and end of parameter/redef buffer.
	; The buffer occupies the same area of memory as this code!
	; ANSI parameters are accumulated at the lower end, and
	; keyboard redefinitions are stored at the upper end; the variable
	; param_end is the last byte used by params (changes as redefs added);
	; redef_end is the last word used by redefinitions.
	mov	di, offset dosfn0
	mov	param_buffer, di
	add	di, 512
	mov	param_end, di	; addr of last byte in free area
	inc	di

	; Build the default redefinition table:
	;	control-printscreen -> control-P
	; (Must be careful not to write over ourselves here!)
	mov	al, 16		; control P
	stosb
	mov	ax, 7200h	; control-printscreen
	stosw
	mov	ax, 1		; length field
	mov	redef_end, di	; address of last used word in table
	stosw

	; Build a 1:1 output character translation table.
	; It is 256 bytes long, starts just after the param/redef buffer,
	; and is the last thing in the initialized device driver.
	mov	xlate_tab_ptr, di
	xor	ax, ax
init_loop:
	stosb
	inc	al
	jnz	init_loop

	xor	ax, ax
	; Return ending address of this device driver.
	; Status is in AX.
	lds	si, req_ptr
	mov	word ptr [si+0Eh], di
	mov	[si+10h], cs
	; Return exit status in ax.
	ret

dosfn0	endp

code	ends
	end				; of nansi_i.asm
@//E*O*F nansi_i.asm//
chmod u=rw,g=r,o=r nansi_i.asm
 
echo x - nansi_p.asm
sed 's/^@//' > "nansi_p.asm" <<'@//E*O*F nansi_p.asm//'
; A state machine implementation of the mechanics of ANSI terminal control
; string parsing.
; (C) 1986 Daniel Kegel, Pasadena, CA
; May be distributed for educational and personal use only
;
; Entered with a jump to f_escape when driver finds an escape, or
; to f_in_escape when the last string written to this device ended in the
; middle of an escape sequence.
;
; Exits by jumping to f_ANSI_exit when an escape sequence ends, or
; to f_not_ANSI when a bad escape sequence is found, or (after saving state)
; to f_loopdone when the write ends in the middle of an escape sequence.
;
; Parameters are stored as bytes in param_buffer.  If a parameter is
; omitted, it is stored as zero.  Each character in a keyboard reassignment
; command counts as one parameter.
;
; When a complete escape sequence has been parsed, the address of the
; ANSI routine to call is found in ansi_fn_table.
;
; Register usage during parsing:
;  DS:SI points to the incoming string.
;  CX holds the length remaining in the incoming string.
;  ES:DI points to the current location on the memory-mapped screen.
;  DX is number of characters remaining on the current screen line.
;  BX points to the current paramter byte being assembled from the incoming
;  string.  (Stored in cur_parm_ptr between device driver calls, if needed.)
;
; The registers are set as follows before calling the ANSI subroutine:
;  AX = max(1, value of first parameter)
;  CX = number of paramters
;  SI = offset of second parameter from CS
;  DS = CS
;  ES:DI points to the current location on the memory-mapped screen.
;  DX is number of characters remaining on the current screen line.
; The subroutine is free to trash AX, BX, CX, SI, and DS.
; It must preserve ES, and can alter DX and DI if it wants to move the
; cursor.
;
; Revision history:
; 7 July 85: created by DRK
;------------------------------------------------------------------------

	; From nansi.asm
	extrn	f_not_ANSI:near		; exit: abort bad ANSI cmd
	extrn	f_ANSI_exit:near	; exit: good cmd done
	extrn	f_loopdone:near		; exit: ran out of chars. State saved.
	extrn	escvector:word		; saved state: where to jump
	extrn	cur_parm_ptr:word	; saved state: where to put next param
	extrn	string_term:byte	; saved state: what ends string
	extrn	cur_x:byte, max_x:byte	; 0 <= cur_x <= max_x
	extrn	cur_attrib:byte		; current color/attribute
	extrn	xlate_tab_ptr:word

	; from nansi_f.asm
	extrn	ansi_fn_table:word	; ANSI subroutine table

	; Used in nansi.asm
	public	f_escape		; entry: found an escape
	public	f_in_escape		; entry: restore state, keep parsing

	; Used in nansi_i.asm and nansi_f.asm
	public	param_buffer, param_end, redef_end


code	segment byte public 'CODE'
	assume	cs:code

; More saved state
in_num		db	?	; true if between a digit and a semi in parse

param_buffer	dw	3000h	; address of first byte free for new params
param_end	dw	3030h	; address of end of free area
redef_end	dw	3030h	; address of end of redefinition area
		; These initialized values are only for debugging

;----- next_is -------------------------------------------------------
; Next_is is used to advance to the next state.  If there are characters
; left in the input string, we jump immediately to the new state;
; otherwise, we shut down the recognizer, and wait for the next call
; to the device driver.
next_is macro	statename
	loop	statename
	mov	ax, offset statename
	jmp	sleep
	endm

;----- sleep --------------------------------------------------------
; Remember bx and next state, then jump to device driver exit routine.
; Device driver will re-enter at f_in_escape upon next invocation
; because escvector is nonzero; parsing will then be resumed.
sleep:	mov	cs:cur_parm_ptr, bx
	mov	cs:escvector, ax
	jmp	f_loopdone

;----- f_in_escape ---------------------------------------------------
; Main loop noticed that escvector was not zero.
; Recall value of BX saved when sleep was jumped to, and jump into parser.
f_in_escape:
	mov	bx, cs:cur_parm_ptr
	jmp	word ptr cs:escvector

fbr_syntax_error_gate:		; jumped to from inside f_bracket
	jmp	syntax_error

;----- f_escape ------------------------------------------------------
; We found an escape.  Next character should be a left bracket.
f_escape:
	next_is f_bracket

;----- f_bracket -----------------------------------------------------
; Last char was an escape.  This one should be a [; if not, print it.
; Next char should begin a parameter string.
f_bracket:
	lodsb
	cmp	al, '['
	jnz	fbr_syntax_error_gate
	; Set up for getting a parameter string.
	mov	bx, cs:param_buffer
	mov	byte ptr cs:[bx], 0
	mov	cs:in_num, 0
	next_is f_get_args

;----- f_get_args ---------------------------------------------------
; Last char was a [.  If the current char is a '=' or a '?', eat it.
; In any case, proceed to f_get_param.
; This is only here to strip off the strange chars that follow [ in
; the SET/RESET MODE escape sequence.
f_get_args:
	lodsb
	cmp	al, '='
	jz	fga_ignore
	cmp	al, '?'
	jz	fga_ignore
		dec	si		; let f_get_param fetch al again
		jmp	short f_get_param
fga_ignore:
	next_is f_get_param

;----- f_get_param ---------------------------------------------------
; Last char was one of the four characters "]?=;".
; We are getting the first digit of a parameter, a quoted string,
; a ;, or a command.
f_get_param:
	lodsb
	cmp	al, '0'
	jb	fgp_may_quote
	cmp	al, '9'
	ja	fgp_may_quote
		; It's the first digit.  Initialize current parameter with it.
		sub	al, '0'
		mov	byte ptr cs:[bx], al
		mov	cs:in_num, 1	; set flag for sensing at cmd exec
		next_is f_in_param
fgp_may_quote:
	cmp	al, '"'
	jz	fgp_isquote
	cmp	al, "'"
	jnz	fgp_semi_or_cmd		; jump to code shared with f_in_param
fgp_isquote:
	mov	cs:string_term, al	; save it for end of string
	next_is f_get_string		; and read string into param_buffer

;----- f_get_string -------------------------------------
; Last character was a quote or a string element.
; Get characters until ending quote found.
f_get_string:
	lodsb
	cmp	al, cs:string_term
	jz	fgs_init_next_param
	mov	byte ptr cs:[bx], al
	cmp	bx, cs:param_end
	adc	bx, 0			; if bx<param_end bx++;
	next_is f_get_string
; Ending quote was found.
fgs_init_next_param:
	mov	byte ptr cs:[bx], 0	; initialize new parameter
	; | Eat following semicolon, if any.
	next_is f_eat_semi

;----- f_eat_semi -------------------------------------
; Last character was an ending quote.
; If this char is a semi, eat it; else unget it.
; Next state is always f_get_param.
f_eat_semi:
	lodsb
	cmp	al, ';'
	jz	fes_eaten
		inc	cx
		dec	si
fes_eaten:
	next_is f_get_param

;----- syntax_error ---------------------------------------
; A character was rejected by the state machine.  Exit to
; main loop, and print offending character.  Let main loop
; decrement CX (length of input string).
syntax_error:
	mov	cs:escvector, 0
	mov	ah, cs:cur_attrib
	mov	bx, cs:xlate_tab_ptr
	jmp	f_not_ANSI	; exit, print offending char

;------ f_in_param -------------------------------------
; Last character was a digit.
; Looking for more digits, a semicolon, or a command character.
f_in_param:
	lodsb
	cmp	al, '0'
	jb	fgp_semi_or_cmd
	cmp	al, '9'
	ja	fgp_semi_or_cmd
		; It's another digit.  Add into current parameter.
		sub	al, '0'
		xchg	byte ptr cs:[bx], al
		push	dx
		mov	dl, 10
		mul	dl
		pop	dx
		add	byte ptr cs:[bx], al
		next_is f_in_param
	; Code common to states get_param and in_param.
	; Accepts a semicolon or a command letter.
fgp_semi_or_cmd:
	cmp	al, ';'
	jnz	fgp_not_semi
		cmp	bx, cs:param_end	; prepare for next param-
		adc	bx, 0			; if bp<param_end bp++;
		; Set new param to zero, enter state f_get_param.
		mov	cs:in_num, 0		; no longer inside number
		jmp	fgs_init_next_param	; spaghetti code attack!
fgp_not_semi:
	; It must be a command letter.
	cmp	al, '@'
	jb	syntax_error
	cmp	al, 'z'
	ja	syntax_error
	cmp	al, 'Z'
	jbe	fgp_is_cmd
	cmp	al, 'a'
	jb	syntax_error
		; It's a lower-case command letter.
		; Remove hole between Z and a to save space in table.
		sub	al, 'a'-'['
fgp_is_cmd:
	; It's a command letter.  Save registers, convert letter
	; into address of routine, set up new register usage, call routine.
	push	si			; These three registers hold info
	push	cx			; having to do with the input string,
	push	ds			; which has no interest at all to the
					; control routine.

	push	cs
	pop	ds			; ds is now cs

	sub	al, '@'			; first command is @: insert chars
	cbw
	add	ax, ax
	add	ax, offset ansi_fn_table
	; ax is now pointer to command routine address in table

	mov	cx, bx
	mov	si, param_buffer	; si is now pointer to parameters
	sub	cx, si			;
	test	in_num, 1
	jz	fip_out_num
		inc	cx
fip_out_num:				; cx is now # of parameters

	xchg	ax, bx			; save pointer to routine in bx

	; Calculate cur_x from DX.
	mov	al, max_x
	inc	ax
	sub	al, dl
	mov	cur_x, al

	; Get first parameter into AX; if defaulted, set it to 1.
	mov	ah, 0
	lodsb
	or	al, al
	jnz	fgp_callem
		inc	ax
fgp_callem:
	; Finally, call the command subroutine.
	call	word ptr [bx]

	pop	ds
	pop	cx
	pop	si

	mov	ah, cs:cur_attrib	; Prepare for STOSW.
	mov	bx, cs:xlate_tab_ptr	; Prepare for translation.
	mov	cs:escvector, 0		; No longer parsing escape sequence.
	; Set flags for reentry at loopnz
	or	dx, dx			; "Any columns left on line?"
	; Re-enter at bottom of main loop.
	jmp	f_ansi_exit


code	ends
	end				; of nansi_p.asm

@//E*O*F nansi_p.asm//
chmod u=rw,g=r,o=r nansi_p.asm
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    1097    4803   27869 nansi.asm
      16      63     360 nansi_d.asm
     634    2935   16445 nansi_f.asm
     110     474    2913 nansi_i.asm
     303    1496    9308 nansi_p.asm
    2160    9771   56895 total
!!!
wc  nansi.asm nansi_d.asm nansi_f.asm nansi_i.asm nansi_p.asm | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
-- 
Ed Nather
Astronomy Dept, U of Texas @ Austin
{allegra,ihnp4}!{noao,ut-sally}!utastro!nather
nather@astro.AS.UTEXAS.EDU