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(®s, ®s); 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 MA@)75HMV!#/_@SXL!0%U$(`\6W4+QP8L!0(`B\?I@P"#/BP%`G4EBAPJ__:' MB0,$=>F`^SMTY/:'B0,#=`C'!BP%``#KU<<&+`4``(H$*N0+P'3J/0<`=.4] M"`!T$3T)`'05/0T`=-8]&P!T&^LQ@WX&`'3)3^O&BSZB`(M&!IF+S_?Y*_KK MMH,^+`4`=06X`0#K`C/`HRP%"\!T`^E[_T?KFUY?B^5=PU6+[#/`Z-<!,\!0 M4*"W`)A0Z)8`@\0&HS`%,\!04*"_`)A0Z(0`@\0&HRX%]P8P!8``=!6A,`4, M(%"X`0!0H+<`F%#H9`"#Q`;W!BX%@`!T%:$N!0P@4+@!`%"@OP"84.A'`(/$ M!HOE7<-5B^PSP.AM`8,^,`4`=!/_-C`%N`$`4*"W`)A0Z"$`@\0&@SXN!0!T M$_\V+@6X`0!0H+\`F%#H!P"#Q`:+Y5W#58OLN`X`Z"P!QD;S1(I&!HA&\HM& M!(E&](I&"(A&^,9&^0"-1O)0C4;R4.@G!H/$!(M&^(OE7<-5B^R+7@3VAZ0$ M`70%,\#IU`F+3@B+5@:T/\TA<P2T">L*]H>X!(!T`^@#`.FW"597_(ORB_J+ MR.,;M`VL.L1T%SP:=0?&AZ0$`>L%B`5'XNN+QRO"7U[#@_D!=`>`/`IT[>OH M]H>X!$!T&;@`1,TA]\(@`'4*C1:J`+0_S2%RU;`*ZR_&!JH``(T6J@"T/\TA M<L(+P'0:@WX(`70@N?__B]&X`4+-(;D!`(`^J@`*=`>P#8M6!NN4BU8&ZY*` M/JH`"G79Z[I5B^R#[`175O]V!.@J"(/$`HOPC48(4/]V!O]V!.BY"H/$!HOX M_W8$5NB."(/$!(O'7E^+Y5W#68O<*]AR"CL>K`!R!(OC_^'IS015B^R#[`97 M5O]V!.A*$8/$`HOX_W8&Z-0'@\0"B_#_=@97N`$`4/]V!.CI"(/$"(E&_/]V M!E;H-@B#Q`0Y?OQU"8M>!(I!_YCK`[C__UY?B^5=PU6+[(M>!/:'N`0@=`ZX M`D(SR8O1S2%S`^EG"/:'N`2`=0/I@0"+3@B+5@8>!S/`_%=6B_"+^N-EN`I` M\JYU,E&+SRO*2>,0S2&<`_"=<P2T">M)"\!T+T:Y`@"ZK@"T0,TA<P2T">LT M"\!T&EF+U^O%48O/*\K-(9P#\)US!+0)ZQH+P'46]H>X!$!T"XM>!H`_&G4# M^.L$^;@('%ER`HO&7E_IW`>+3@B+5@:T0,TA<P2T">OMX^L+P'7G]H>X!$!T M"HO:@#\:=0/XZ];YN`@<Z]!5B^PR_X@^I@&+1@8R[:D(`'0"M2"(+J4!)0,` M/0,`=06!=@8!`+IZ`;0:S2&Y)P"+5@2T3LTA<P/IFP"+1@:+R"4`!3T`!74' MN`41^>E?!\8&IP$!D:D``G0+BU8$N`!#S2'IH`"I`P!T5ZD`@'52]@;7!(!U M2[`""@:F`8M6!+0]S2%RQY.X`$3-(?;"@'4MN?__B]&X`D+-(??9NGH!M#_- M(0O`=!:`/GH!&G4/]]F+T;@"0LTA,\FT0,TAM#[-(8I&!B0#"@:F`8M6!+0] MS2%R.^F#!?=&!@`!=0:X`@#I:/_&!J<!`/]V".B[!5F)3@@R[?8&I@'_=0?W M1@8"`'4#@.'^BU8$M#S-(7,#Z9T&]@:F`?]U!_=&!@(`=3*3M#[-(8I&!B0# M"@:F`8M6!+0]S2%RV/8&IP$!=13W1@@!`'0-@,D!DXM6!+@!0\TAD^D%!56+ M[(/L`E:+=@0+]GP,@_X4?0?VA+@$`741QP:4!`D`QP:B!`8`N/__ZT"*A+@$ M)8``B4;^@7X&`(!U!X"DN`1_ZPR!?@8`0'40@(RX!("#?OX`=!.X`$#K$<<& ME`06`,<&H@0!`.N^N`"`7HOE7<-5B^R#[`)75HMV!+\"``OV="*`/`!T'5;H M4@X#YU!65^A%_8/$!HO'4+C.`E!7Z#?]@\0&H2P#.0:4!'T3@SZ4!`!\#(L> ME`31XXN'X@+K`[C1`HOP5N@4#H/$`E!65^@&_8/$!K@!`%"XWP)05^CW_(/$ M!EY?B^5=P[^P`8LV`@`K]X'^`!!R`[X`$/J.UX'$CA'[<P/I20&!Y/[_-HDF M-@,VB28T`XO&L033X$@VHS(#M##-(3:CF@0\`G,J'C/`4`X?NM<'M`G-(<M$ M3U,@,BXP(&]R(&QA=&5R(')E<75I<F5D#0HD`_>)-@(`C,,KWO?;M$K-(3:, M'I($BS8L`!X6N``US2$VB1XN`S:,!C`##A^X`"6Z!@G-(3:+#B(%XR(VQ08D M!8S:,]LV_QX@!7,#Z<P`-L4&*`6,VKL#`#;_'B`%!Q_\ORP%N9`1*\\SP/.J M%A8''^@-!KL$`+@`1,TA<@KVPH!T!8"/N`1`2WGLZ/\!Z%P!NRP%@?LL!7,( M4_\76T-#Z_*Y!@`KX;Z<!(O\%@?SI.C,`#/MZ%CW4.AF``T*4W1A8VL@;W9E M<F9L;W<-"@T*1&EV:61E(&5R<F]R#0H-"D9L;V%T:6YG('!O:6YT(&YO="!L M;V%D960-"K$2NJX(,NT.'[L"`+1`S2$6'[C_`%#H-0&Q$+K`".OEL1VZT`CK MWE6+[(,^F`0`=`3_%I@$_W8$Z!,!B^5=PU6+[%=6BWX$BP6+70*+302+50:+ M=0B+?0K-(5>+?@:)!8E=`HE-!(E5!HEU"(]%"G($,_;K".B<`[X!`(L%B74, M7E]=PU6+[(/L"%=6BS:@!.L7N`P`4+C8!%#_-.A?#8/$!@O`=`B#Q@*#/`!U MY(,\`'0YBSR#QPS'1OX``(H%1YB)1OKK'(H%F#W_`'4$,L#K`HH%BU[^_T;^ MB(>X!/].^D>#?OH`==['!```7E^+Y5W#CP;F!(X>D@0SR8O!B^F+^4F+-BP` M"_9T"([&\JY%KG7Z19=`)/Z+_='E`\46'^C\^8O/B_P#_8OL%@>.WC/V2>,- MB7X`146LJ@K`=?KB\XE.`!8?B2:@!/\FY@15B^R[+`6!^RP%<PA3_Q=;0T/K M\N@A!`KD=`J`?@0`=03&1@3^'L46+@.X`"7-(1^+#B(%XP>[`@#_'B`%BT8$ MM$S-(8\&Z`2.'I($%@>^@`"LF#/2)H`^F@0#<F50C@8L`#/`B\CWT8OX\JXF M.`5U^8/'`XO(]]&+]R:*!4=!.N!T%#PB=`@\"70$/"!UZU@6![Z!`.LK3RO^ M"_]T\8O/0EN+PP/!!0,`)?[_*^"+_!X&'Q8'\Z2P(*J+RQ^^@0#K#HO(!`0D M_BO@B_RX0R"K\Z2+P:J+]!8?4(O<B_ZLJ@K`=#T\(G4?5O\&G`2L"L!T+CPB M=0>`??]<=01/JNOM1\9%_P#KU>@W`'303E9&_P:<!*RJ"L!T".@E`'7UZ^&J MB_1+2SOS<PBMAP>)1/[K\HO<"])U`O\'B2:>!/\FZ`0\"70"/"##58OLQ@:E M`0#_=@;H7P!9B4X&BU8$,NVT/,TA<DV+V(M6!+@`0\TA]\$!`'4$,LGK`K$0 M]T8&`$!U#O=&!@"`=0KV!M<$@'4#@,F`"@ZE`8#)`;@`1,TAB\/VPH!T`X#) M0(B/N`3&AZ0$`.D%`56+[*&6!/?0BUX$(\.!XP#`J(!U`X#+`8E>!%W#58OL M5HMV!('^N`!U//9$!@QU-HI$!YB+V-'C]H=0`0%U)\=$!&`%BD0'F(O8T>/& MAU`!`<<&F`27%\=$`@`"BT0$B02X`0#K-('^T`!U+/9$!@QU)HI$!YB+V-'C M]H=0`0%U%[@``E#H80N#Q`*)1`0+P'0&@$P&".NY,\!>B^5=PU6+[%:+=@:# M?@0`=$R!_K@`=26*1`>84.@A"H/$`@O`=!96Z'<*@\0"BD0'F(O8T>/&AU`! M`.L:@?[0`'4;5NA;"H/$`O]T!.CQ"H/$`H!D!O<SP(D$B40$7HOE7<-R"#/` MB^5=PW,&Z`T`N/__B^5=PS+DZ`$`PZ*B!`KD=2.`/IH$`W(-/")S#3P@<@6P M!>L'D#P3=@*P$[OJ!->8HY0$PXK$Z_=5B^R#[`975HMV!(M^"HM&!O=F"(E& M^HE&_`O`=#OV108,=3Z*10>8B]C1X_:'4`$!=2__30*#?0(`?`R*!(L=_P6( M!RKDZPM7B@284.A&"(/$!/9%!B!T!3/`Z=H`1O]._/9%!@AU$HI%!YB+V-'C M]H=0`0%U`^FC`(-^_`!U`^F)`(M&_#E%`G(<4%;_->A8"H/$!HM&_"E%`HM& M_`$%QT;\``#KTX-]`@!T(?]U`E;_->@T"H/$!HM%`@$%BT4"*4;\`W4"QT4" M``#KK/]-`H-]`@!\#(H$BQW_!8@'*N3K"U>*!)A0Z*L'@\0$]D4&('481O]. M_.E__U>*!)A0Z),'@\0$]D4&('0-BT;Z*T;\*]+W=@;K'T;_3OR#?OP`=.G_ M30*#?0(`?,^*!(L=_P6(!RKDZ\Y>7XOE7<-5B^Q6,_:Y-0`RY/RL,N#B^X#T M5?X.-0!U!H@F-`#K$0KD=`VZ-@"[`@"Y&0"T0,TA,L"B-0!>B^5=PU6+[+AB M`>A5]5=6BW8&C8:B_J,^!8M&!*,V!8M&"*,Z!3/`HTP%HTH%@#P`=0/I20&` M/"5T`^D*`<<&1`4!`#/`HT(%HS(%HT8%HS@%HSP%HS0%HU`%HU(%QP9`!2`` MZS*`/"UU!O\&4@7K)X`\*W4,_P9"!<<&-`4``.L6@#P@=0V#/D(%`'4*_P8T M!>L$_P90!4:*!)A0Z`<&@\0""\!UOU:X2`50Z)`%@\0$B_"`/"YU$O\&/`5& M5KA$!5#H>06#Q`2+\(`\;'4'QP8X!0(`1H`\`'4#Z:``B@28B8:>_CU%`'0* M/4<`=`4]6`!U"?\&,@6#AI[^((N&GOXM8P`]%0!W/@/`DR[_I_4/_P9&!<<& M4`4``+@*`%#HAP"#Q`+K4+@(`.ORN!``Z^TSP%#HH`'KZ;@!`.OU_[:>_N@> M`NO;B_[K0>,/OP_H#^@/Z`_Q#_$/\0_Q#_$/\0_Q#]$/\0_Q#_$/VP_Q#[L/ M\0_Q#]8/@SY,!0!T!:%*!>L@1NFF_H`])70&1X`]`'7UB\<KQE!6Z!X#@\0$ MB_?IBOY>7XOE7<-5B^RX&`#HL/-75H-^!`IT!/\&1@6#/C@%`'06BQXZ!8L' MBU<"B4;XB5;Z@P8Z!03K*8,^1@4`=!"+'CH%BP>)1OC'1OH``.L-BQXZ!8L' MF8E&^(E6^H,&.@4"@SY0!0!T#8M&^`M&^G0%BT8$ZP(SP*-.!8LV/@6#/D8% M`'4J@W[Z`'TD@WX$"G47Q@0M1HM&^(M6^O?8@](`]]J)1OB)5OK'1O8!`.L% MQT;V``"+_8/O&/]V!%?_=OK_=OCHL`2#Q`B#/CP%`'0@5^A7!(/$`HL.1`4K MR(E._NL$Q@0P1HM&_O]._@O`?_**!8@$@SXR!0!T!SQA?`.`+"!&1X!]_P!U MYH,^1@4`=12A0@4+!C0%=`N#?O8`=06X`0#K`C/`4.A5`H/$`EY?B^5=PU6+ M[+@(`.B"\E=6QP9`!2``@WX$`'00O@$`H3H%@P8Z!0*)1OSK,XL>.@6+!XE& M_(,&.@4""\!U!<=&_/X$_W;\Z+,#@\0"B_"#/CP%`'0*.09$!7,$BS9$!8L^ M2`4K_H,^4@4`=0=7Z`X!@\0"5O]V_.AM`8/$!(,^4@4`=`=7Z/8`@\0"7E^+ MY5W#58OLN`(`Z/;QH3H%B4;^@SX\!0!U!L<&1`4&`/\V,@7_-D0%_W8$_S8^ M!?]V_NA3`X/$"H-^!&=T!H-^!$=U&(,^4`4`=1&#/D0%`'0*_S8^!>@Y`X/$ M`H,^4`4`=!&#/D0%`'4*_S8^!>@K`X/$`H,&.@4(QP9.!0``H4(%"P8T!702 M_W;^Z"(#@\0""\!T!;@!`.L",\!0Z"0!B^5=PU6+[#/`Z%?Q5H,^3`4`=3B+ M'C8%_T\"@W\"`'P1BD8$BQXV!8LW_P>(!"KDZPW_-C8%_W8$Z.8"@\0$0'4& M_P9,!>L$_P9*!5Z+Y5W#58OLN`(`Z`GQ5U:#/DP%`'51BW8$"_9^2NLSBQXV M!?]/`H-_`@!\$:!`!8L>-@6+/_\'B`4JY.L._S8V!?\V0`7HC0*#Q`1`=03_ M!DP%B\9."\!_QH,^3`4`=0>+1@0!!DH%7E^+Y5W#58OLN`(`Z*#P5U:+=@2+ M?@:#/DP%`'5,ZS6+'C8%_T\"@W\"`'P2B@2+'C8%BP__!XO9B`<JY.L._S8V M!8H$F%#H)`*#Q`1`=03_!DP%1HO'3PO`=<2#/DP%`'4'BT8&`09*!5Y?B^5= MPU6+[+@*`.@V\%=6BS8^!3/`B4;\B4;XBSY(!5;HC`&#Q`*)1OHK^"M^!*%. M!;$#T_@K^(,^4@4`=16`/"UU$(,^0`4P=0FLF%#HE/Z#Q`*#/D`%,'0+"_]^ M!X,^4@4`=!F#?@0`=`;_1OCH7@"#/DX%`'0&_T;\Z&\`@SY2!0!U)E?HJ/Z# MQ`*#?@0`=`F#?O@`=0/H-`"#/DX%`'0)@W[\`'4#Z$(`_W;Z5NCH_H/$!(,^ M4@4`=`W'!D`%(`!7Z&O^@\0"7E^+Y5W#58OL,\#H;.^#/D(%`'0$L"OK`K`@ MF%#H^_V+Y5W#58OL,\#H3N^X,`!0Z.C]@\0"@SY.!1!U%8,^,@4`=`2P6.L" ML'B84.C,_8/$`HOE7<-5B^RX`@#H&^]75HMV!H`\*G4.BQXZ!8,&.@4"BS]& MZSHS_X`\,'PS@#PY?RXY/CP%=0N`/#!U!L<&0`4P`*R8B\_1X='A`\_1X0/( M@^DPB_F`/#!\!8`\.7[CBUX$B3^+QEY?B^5=PU6+[+@"`.BS[E:^!07K#8H$ M.$8$=06X`0#K"$:`/`!U[C/`7HOE7<-5B^Q7'@>+?@0SP+G___*NB\%`0/?8 M7UW#58OLZ';SB^5=PU6+[.AL\XOE7<-5B^SH8O.+Y5W#58OLZ%CSB^5=PU6+ M[.A.\XOE7<-5B^RS`.GR`U6+[(/L!%=6BW8&BD0&F*F#`'0&]D0&0'0&N/__ MZ=H`]D0&`70&@$P&(.ON@$P&`H!D!N\SP(E$`HOXB7[^]D0&"'4/BD0'F(O8 MT>/VAU`!`70PBSPK?`0+_WX25_]T!(I$!YA0Z$#N@\0&B4;^BT0$0(D$QT0" M_P&+7`2*1@2(!^MJ]D0&!'5.@?ZX`'4MBD0'F%#H:0"#Q`(+P'4UQP:8!)<7 MQT0$8`6*1`>8B]C1X\:'4`$!QP1A!>NXN``"4.A+`8/$`HE$!`O`=`:`3`8( MZYN`3`8$OP$`5XU&!%"*1`>84.C![8/$!HE&_CE^_G0#Z3'_BD8$*N1>7XOE M7<-5B^R+7@2#^Q1]$8/[`'P,]H>X!$!T!;@!`.L",\!=PU6+[%=6'@>+3@CC M+XO9BWX$B_<SP+G___*N0??9.\MV`HO+B_Z+=@;SIHI$_S/).D7_=P5T!4'K M`O?1B\%>7UW#58OL@^P$5U:+=@0S_U;HW0*#Q`**1`8D`SP"=3SV1`8(=0^* M1`>8B]C1X_:'4`$!=">+!"M$!(E&_`O`?AM0_W0$BD0'F%#H_>R#Q`8[1OQT M!X!,!B"___^+1`2)!,=$`@``B\=>7XOE7<-5B^R#[`175KZP`#/_ZQ>*1`:8 MJ8,`=`M6Z'G_@\0"0'0!1X/&"#DV>`%SXXO'7E^+Y5W#58OLBUX$@$_^`8OE M7<-5B^Q65[L*!8,_`'4I'@>X!0#HL`%U!3/`F>LD0"3^HPH%HPP%EL<$`0"# MQ@3'1/[^_XDV$`6+3@2,V([`Z%H`7UZ+Y5W#58OL5U8>!XM^!(MV!HO7BTX( M._YV%(O&`\$[^',,`_$#^4Y/_?.D_.LAB\<+QM'H<PJ+QS/&T>ARZZ1)B]G1 MZ?.ET>MS!8H$)H@%B\)>7UW#Z<L`@_GN<_A!@.'^BW<"_*V+_J@!=$)(.\%S M%8O0`_"MJ`%T-`/"!0(`B_>)1/[KYHO^=`P#^8E,_BO!2(D%ZP4#^?Y,_HO& MC-J!^K`!=`4FC!X8!8E_`L,FQ@8<!0(]_O]T)8O^`_"MJ`%T\HO^2#O!<[V+ MT`/PK:@!=.(#P@4"`(OWB43^Z^:+1P@+P'0$CMCK$R;^#AP%=!",V#VP`70% M)HX>%`6+-^N]BW<&,\#H60`[QG0-)`%`0)CH30!T#?Y-_N@+`'0%EDY.ZYHS MP)G#48M%_J@!=`,KR$E!0;K_?R8[%AH%=@31ZG7UB\$#QG(5`\)R#??2(\(K MQN@,`'4(]]+1ZG7E,\!9PU)1Z!T`=!A7B_Z+\`/RQT3^_O^)=P:+UBO72HE5 M_EA96L-34#/2'E)24+@!`%`&'^AW`(/$"(/Z_Q]:6W0""]+#5U:+3@J+1@2+ M5@:+?@A7'@?\DPK`=!.#^0IU#@O2>0JP+:KWVX/2`/?:B_>2,](+P'0"]_&3 M]_&2A],$,#PY=@($)ZJ+P@O#=>*(!4^LA@6(1/]/._=R]5A>7UW#58OLBUX$ 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 M_____Q.`RP#6`$-/3B`@("`@&ULR2DYA;G-I+G-Y<R!V,BXR.B!.97<@04Y3 M22!D<FEV97(@*$,I($1A;FEE;"!+96=E;"P@4&%S861E;F$L($-!(#$Y.#8- M"AH``````0```QA/```````'```````````````#````````````6QL``'@` M````````=P``````H64`B]BT`)**Q[0`*]!"BL/^P/8F9P`"QX#4``/`E\-P M"2,!(P$?`28!-0%)`50!F`&8`2,!(P$NB1Y<`"Z,!EX`R_M04U%25597'@8N MQ1Y<`(I'`L1W#HM/$CP,=R"3T>.,R([8_Y>S`"[%'EP`#0`!B4<#!Q]?7EU: M65M8R^@*`.OF+L<&BP`!`,^X$PC#,\##XPJ+_E'HXP)9JN+X,\##Z",#=`O% M'EP`B$<-,\#K`[@``L/H#P.X``)T`C/`PS/`HX,`HX<`HXL`HX\`N$``CL`F MH1H`)J,<`#/`PP#['@904U%25597N0$`C,N.PX[;OG(!B`3H"@!?7EU:65M8 M!Q_/C`9O`+A``([8H4D`+J)C`/[,+H@F90"@8@"8`\"3BX=0`"ZC9@"A8P`N MHW,`H4X`T>C1Z-'HT>B`Q+`N@#YC``=T`X#$"`X?HVT`CL#HJ_[H%`%R`^B? M`8L>=0"*)FL`CAYO`/SC(RZ#/F$``'50Z/4`<@/I#`&L/!QR(B[7JTK@]70$ MZP3@YW1HC,B.V*!E`/[`*L*B9@#H+0$SP,,\&W0:/`UT+SP*=&`\"70+/`AT M$#P'=!CIP@#K>9#II`+IE`(N.A9E`'<#3T]"Z[CH(@(*P.NQ+J!E`/[`*L*T M`"OX*_@NBA9E`$(NBB9K``P!ZY1!+O8&8``!=05/3T+KABZ*%F4`0BOZ*_HN MH&0`+C@&9P!R!>@%`>L0+OX&9P`NH&4`M`!``\`#^"Z*)FL`#`'I4O\NH&4` M_L`JPE&U`(K(@.$']MF`P0@KT9RP(.@6`','\ZN=6>DL_P/1Z#P`2N+ZG5GI M'_\N@#YC``1R"BZ`/F,`!W0"^,/YP^CI_W,(Z?G^K#P<<@TNU^@.`$K@\W0& MZ?7^Z0;_Z5?_4E%34"Z@90#^P"K"+J)F`"Z+%F8`,]NT`LT0N0$`6%"*W+<` MM`G-$%A;65K#Z)S_<RJ+W]'KBQ9S`+`.[NL`0HK'[NL`2K`/[NL`0HK#[KA` M`([8+J%F`*-0`,.X%H_HGO_#M`#H9/]S%RZ*)FL`@.1_+H`^8P`'=0>`_`%U M`K0'PU!345+HVO^*_+`!M`:Q`+4`+HH690`NBC9D`,T06EE;6,.+-N`$"L!T M`K0`.S;>!'8.BPP[1/YT"X/N!"OQZ^P+]NL'@^X"*_$ZP,.Y!`"[?P"#PP2+ M-POVX??#Z.W_="G_#TZ+;P(^B@*#[@%R&8'[@P!T$PK`=0__#SZ*(L<&@P`! M`(@F>`#K&[0`S18+P'3XZ(W_=0J)#H\`B3:1`.N[/`!TV</HH_]T"4Z+;P(^ MB@+K&K0!S19T%PO`=0:T`,T6Z_#H6_]U!0/QBD3_@,P!PQ0%`P!04U%25597 ML+:Z0P#N2BZAAP3K`.Z&X.L`[KIA`.L`[%`,`^L`[@:X0`".P":+'FP`N?__ M)J%L`"O#+CL&B01_`N+Q!U@D_.Y?7EU:65M8PP``,#`P,#`NB1YQ`"ZC80#I M-/TNBQYQ`"[_)F$`Z9D`X@6X`@7KX*P\6W7Q+HL>W`0NQ@<`+L8&VP0`X@6X M'07KQ:P\/70'/#]T`T[K!^(%N#`%Z[*L/#!R%CPY=Q(L,"Z(!R[&!ML$`>)B MN*@%ZY<\(G0$/"=U="ZB:@#B!;A>!>N$K"XZ!FH`=!,NB`<N.Q[>!(/3`.+K MN%X%Z6G_+L8'`.(&N(4%Z5W_K#P[=`)!3N*BN#`%Z4[_+L<&80```"Z*)FL` M+HL>=0#I:_VL/#!R&CPY=Q8L,"Z&!U*R"O;B6BX`!^+GN*@%Z1O_/#MU$"X[ M'MX$@],`+L8&VP0`ZYX\0'*U/'IWL3Q:=@8\87*I+`9641X.'RQ`F`/`!44& MB\LNBS;<!"O.+O8&VP0!=`%!DZ!E`$`JPJ)F`+0`K`K`=0%`_Q<?65XNBB9K M`"Z+'G4`+L<&80````O2Z>7[U`H%,#"&X*J&X*K#30@`!_H&YP;M!J\&KP:O M!K`&KP9/!T8'(P@?"*\&KP91"*\&KP:O!J\&KP:O!J\&KP:O!J\&KP:O!J\& MKP:O!K`&KP:2"*\&KP:O!@H)'`=S!Z\&J`>O!J\�>O!A0'KP:O!J\&#@FO M!L,*P'0!2*)G`#/`@_D"<@:L"L!T`4BB9@"A9@`Z!F4`=@:@90"B9@`Z)F0` M=@:@9`"B9P#HK?G#``9F`.O;*`9F`',%Q@9F``#KS@`&9P#KR"@&9P!S!<8& M9P``Z[NA9@"C:`##H6@`HV8`ZZQ."\EU!,8$`$&L4;D6`)"[*PF#PP,Z!^#Y M=0N+1P$@!FL`""9K`%GBX,-2BPYF`(KUZQ0\`G4?QP9F````Z#?Y4C/)BC9D M`.@P_(K\BA9E`+@`!LT06L-74@:,R([`_;^``*!G`/[`Z+/^L#NJH&8`_L#H MJ/ZP4JJP#8D^B0"JN((`*\>CAP#\!UI?PPO)=%G\3DE)K0K`=`)!3E<&45;] M'@?H%OQU%H/!!(LNW@0!#MX$3HO^`_F+SBO-\Z1>68L^W@2#[P2+WRO9@^L0 M.Q[<!'(2B4T#B44!XP:D@\8"XOJ)/MX$!U_\PP8>!_V+/N`$N`$`J[@`<JNP M$*J)/MX$!^ODM`;K`K0'BCYD`"H^9P`ZQW8"L`!0Z%_[BOQ8L0"*+F<`4HH6 M90"*-F0`S1!:P[4!ZP*U`.BI^G,Y.L)V`HK"5Y&+Z;4`B_<#\0/Q]MD"RK4` M_/;$`70,A_[]B\%(`\`#^`/P!A_SI8O-Z`O[L"#SJ_Q?P['_/`=U!H@.8`#K M:SPK=4*T$KL0_\T0]\/\_G5:Z$WZ<U6T`*!C`,T0N!(1LP#-$+@`$K,@S1"T M`;D'!\T0BQ9S`+`*[NL`0K`'[L8&9``JZPFT`,T0Q@9D`!BX0``>CMBA20`? MHF,`_LR()F4`QP9F````Z(KWP[$`ZX:+'G4`XP].K4EU`HK@`MB`UP"()\,S MP(@'0_[`=?GK]```!P'_"`3X`07_@`?X<`B(`![X`!_X!"#X`B'X!B+X`2/X M!23X`R7X!RB/`"F/0"J/("N/8"R/$"V/4"Z/,"^/<#/`CMB[;`#'!Q<!C$\" MNZ0`QP=S`8Q/`@X?#@?\OW`)B3[<!(''``*)/MX$1[`0JK@`<JNX`0")/N`$ 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