>>>>>>>> This file contains the sources for LCTERM, a communications
>>>>>>>> program for the DEC Rainbow 100/100+ written by Larry Campbell
>>>>>>>> of DEC in Computer Innovations C86.  It incorporates both
>>>>>>>> KERMIT and MODEM2 file transfer protocols.
>>>>>>>>
>>>>>>>> Lines beginning with ">>>>>>>>" delimit the various source files
>>>>>>>> and show their names.  The files follow:
>>>>>>>>
>>>>>>>> BUF.C
/*
 * BUF.C
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *	Implements simple ring buffers
 */

#include <stdio.h>		/* just for definition of NULL */
#include "buf.h"

/*
 * create_buf (size)
 *
 *	Create ring buffer of SIZE characters.  Return NULL if failure,
 *	address of buffer descriptor if successful.
 */

struct ring_buf *create_buf (size)
int size;
{
struct ring_buf
    *new_buf;

if (size < 2)
    return (NULL);
if ((new_buf = malloc (sizeof (struct ring_buf))) == NULL)
    return (NULL);
if ((new_buf->cdata = malloc (size)) == NULL)
    {
    free (new_buf);
    return (NULL);
    };
new_buf->putter = 0;
new_buf->taker = 0;
new_buf->size = size;
return (new_buf);
}

/*
 * destroy_buf (buf)
 *
 *	Release buffer and descriptor
 */

destroy_buf (buf)
struct ring_buf *buf;
{
free (buf->cdata);
free (buf);
}

/*
 * put_buf (buf, char)
 *
 *	Put character into buffer.  Return true (1) if successful,
 *	false (0) if buffer full.
 */

int put_buf (buf, c)
struct ring_buf *buf;
char c;
{
int
    next;

if ((next = ((buf->putter + 1) % buf->size)) == buf->taker)
    return (0);
buf->cdata[buf->putter] = c;
buf->putter = next;
return (1);
}

/*
 * get_buf (buf)
 *
 *	Get character from buffer, return 0 if buffer empty (user should
 *	verify nonemptiness by calling buf_emptyp first)
 */

char get_buf (buf)
struct ring_buf *buf;
{
char
    c;

if (buf->taker == buf->putter)
    return (0);
c = buf->cdata[buf->taker];
buf->taker = (buf->taker + 1) % buf->size;
return (c);
}

/*
 * un_get_buf (buf, c)
 *
 *	"unget" a character from a buffer, return true (1) if successful,
 *	false (0) if failure because buffer full or character being
 *	"ungotten" wasn't the same character "gotten"
 */

int un_get_buf (buf, c)
struct ring_buf *buf;
char c;
{
int
    dest;

if ((dest = buf->taker - 1) < 0)
    dest = buf->size - 1;
if (buf->putter == dest)
    return (0);				/* buffer full */
if (buf->cdata[dest] != c)
    return (0);				/* caller trying to cheat */
buf->taker = dest;
return (1);
}

/*
 * buf_emptyp (buf)
 *
 *	Test for emptiness of a buffer
 */

int buf_emptyp (buf)
struct ring_buf *buf;
{
return (buf->putter == buf->taker);
}

/*
 * buf_fullp (buf)
 *
 *	Test for fullness of buffer
 */

buf_fullp (buf)
struct ring_buf *buf;
{
return (((buf->putter + 1) % buf->size) == buf->taker);
}
>>>>>>>> BUF.H
/*
 * BUF.H
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *	Ring buffer definitions
 *
 */

struct ring_buf
    {
    int size;
    char *cdata;
    int putter;
    int taker;
    };
>>>>>>>> BYTES.H
/*
 * \usr\rainbow\bytes.h
 *
 *	C definitions of RAM locations and constants used by
 *	DEC Rainbow 100 firmware.  Generated more or less mechanically
 *	from BYTES.LST, which is generated from BYTES.A86, part
 *	of the firmware source.  All symbols are of the form
 *	ROM_xxxxxx, where xxxxxx is the symbol name used in the
 *	firmware source.  Dollar signs are replaced with underscores,
 *	since C doesn't like dollar signs in identifiers.
 *
 *	source:	BYTES.A86 version X00-09, December 19, 1983.
 *	This file generated March 18, 1984.
 */

#define ROM_A_VTAD 0x0007
#define ROM_AABYTE 0x1EC0
#define ROM_ACK 0x0006
#define ROM_ACRPT1 0x1004
#define ROM_ACRPTR 0x1003
#define ROM_AILLIN 0x1000
#define ROM_ALLUPS 0x00B3
#define ROM_ALTROM 0x0003
#define ROM_ALTSPC 0x0004
#define ROM_ALTSTD 0x0003
#define ROM_ANSI 0x0001
#define ROM_ANYCHR 0x0008
#define ROM_APOSTR 0x0027
#define ROM_APTOFF 0x0000
#define ROM_APTON 0xFFFF
#define ROM_AROMGR 0x0004
#define ROM_ARPCNT 0x0F69
#define ROM_ARPENA 0x0040
#define ROM_ARPFLG 0x0040
#define ROM_ARPKEY 0x0F6A
#define ROM_ASBCUR 0x0080
#define ROM_ASBLHI 0x0ED5
#define ROM_ASBLNK 0x0EC5
#define ROM_ASBYTE 0x0EC1
#define ROM_ASCCAN 0x0018
#define ROM_ASCCR 0x000D
#define ROM_ASCESC 0x001B
#define ROM_ASCII 0x0000
#define ROM_ASCLF 0x000A
#define ROM_ASCSP 0x0020
#define ROM_ASCSUB 0x001A
#define ROM_ASCTLD 0x007E
#define ROM_ATRIBS 0x0F40
#define ROM_ATROFS 0x1000
#define ROM_AUTFRE 0x1EE3
#define ROM_AUTOIN 0x00C1
#define ROM_AUTOPF 0x0ED6
#define ROM_AUTREP 0x0001
#define ROM_AUTUOF 0x00D9
#define ROM_AVTADD 0x1ECF
#define ROM_BACKSP 0x0008
#define ROM_BEL 0x0007
#define ROM_BIT0 0x0001
#define ROM_BIT1 0x0002
#define ROM_BIT10 0x0400
#define ROM_BIT11 0x0800
#define ROM_BIT12 0x1000
#define ROM_BIT13 0x2000
#define ROM_BIT14 0x4000
#define ROM_BIT15 0x8000
#define ROM_BIT2 0x0004
#define ROM_BIT3 0x0008
#define ROM_BIT4 0x0010
#define ROM_BIT5 0x0020
#define ROM_BIT6 0x0040
#define ROM_BIT7 0x0080
#define ROM_BIT8 0x0100
#define ROM_BIT9 0x0200
#define ROM_BLANK 0x0002
#define ROM_BLINK 0x0004
#define ROM_BLKCNT 0x003F
#define ROM_BLOB 0x0002
#define ROM_BOTTOM 0x0F59
#define ROM_BREAK 0x00E7
#define ROM_BRKCNT 0x0F91
#define ROM_BRKDET 0x0040
#define ROM_BRKIPR 0x0020
#define ROM_BRX1 0x0001
#define ROM_BRX16 0x0002
#define ROM_BRX64 0x0003
#define ROM_BUFCNT 0x0FB3
#define ROM_BUFEMP 0x0FB5
#define ROM_BUFSPA 0x0F6C
#define ROM_C_URLE 0x000A
#define ROM_C_URSO 0x000D
#define ROM_C132VC 0x0010
#define ROM_C9600 0x000E
#define ROM_CAN 0x0018
#define ROM_CAPSLK 0x0008
#define ROM_CARR 0x000D
#define ROM_CBLINK 0x0ED7
#define ROM_CCCHAD 0x0ED8
#define ROM_CHARCN 0x0F93
#define ROM_CHARPL 0x0050
#define ROM_CHAR_P 0x0040
#define ROM_CHBLNK 0x0ECB
#define ROM_CHR132 0x0084
#define ROM_CHRDIR 0x0002
#define ROM_CHRSET 0x0F38
#define ROM_CLICON 0x0001
#define ROM_CLRLSB 0x0000
#define ROM_CLRMSB 0x0004
#define ROM_CLRSIZ 0x005B
#define ROM_CLRVFI 0x0009
#define ROM_CLUSTE 0x0001
#define ROM_CNTABL 0x0EC5
#define ROM_CNTRL 0x0004
#define ROM_CNTRLM 0x001F
#define ROM_COL132 0x0001
#define ROM_COL80 0x0000
#define ROM_COM1ST 0x0FEB
#define ROM_COMMA 0x00D2
#define ROM_COMPLE 0x0082
#define ROM_COMPOS 0x0084
#define ROM_COMPOS 0x0020
#define ROM_COMP_I 0x0008
#define ROM_COMREQ 0x0FE5
#define ROM_COMSTM 0x0FE6
#define ROM_COMTM3 0x0FE9
#define ROM_CONCHR 0x0020
#define ROM_CRLFON 0x0001
#define ROM_CSRATR 0x0EDA
#define ROM_CSRCNT 0x0F2B
#define ROM_CSRCOL 0x0F41
#define ROM_CSRFLG 0x0F2C
#define ROM_CSRLIN 0x0F42
#define ROM_CSRPOS 0x0F41
#define ROM_CSRTYP 0x0F2D
#define ROM_CTBLK 0x1EC8
#define ROM_CURLEN 0x1ED2
#define ROM_CURLIN 0x0F43
#define ROM_CURSET 0x0002
#define ROM_CURSOR 0x1ED5
#define ROM_D_YNMS 0x000B
#define ROM_DARROW 0x00AB
#define ROM_DB5 0x0000
#define ROM_DB6 0x0004
#define ROM_DB7 0x0008
#define ROM_DB8 0x000C
#define ROM_DBELCT 0x0EC9
#define ROM_DC1 0x0011
#define ROM_DC2 0x0012
#define ROM_DC3 0x0013
#define ROM_DC4 0x0014
#define ROM_DEBASB 0x0000
#define ROM_DEBMOD 0x0000
#define ROM_DEBSTK 0x0000
#define ROM_DEBUG 0x0000
#define ROM_DEBVFR 0x0000
#define ROM_DEL 0x007F
#define ROM_DELCHR 0x007F
#define ROM_DHBATR 0x0000
#define ROM_DHTATR 0x0004
#define ROM_DIAGTS 0x0010
#define ROM_DISBEL 0x00A1
#define ROM_DISCLI 0x0099
#define ROM_DISCNT 0x00B9
#define ROM_DISFRE 0x1EDD
#define ROM_DISPOS 0x1EDB
#define ROM_DLE 0x0010
#define ROM_DLY133 0x0008
#define ROM_DO 0x0083
#define ROM_DOUBCO 0x0023
#define ROM_DOUBW 0x0008
#define ROM_DP7S 0x0004
#define ROM_DPCHAR 0x0000
#define ROM_DPMOD 0x0001
#define ROM_DRKBCK 0x000B
#define ROM_DSKHMF 0x0012
#define ROM_DSKRDF 0x0013
#define ROM_DSKWRF 0x0014
#define ROM_DSR 0x0080
#define ROM_DTR 0x0002
#define ROM_DTREVR 0x0010
#define ROM_DTRNOW 0x0008
#define ROM_DWATR 0x0002
#define ROM_DYNMSG 0x1ED3
#define ROM_EH 0x0080
#define ROM_EM 0x0019
#define ROM_ENABEL 0x0023
#define ROM_ENACLI 0x001B
#define ROM_ENACNT 0x00BB
#define ROM_ENQ 0x0005
#define ROM_ENTER 0x00D8
#define ROM_EOT 0x0004
#define ROM_ER 0x0010
#define ROM_ERASE 0x00A1
#define ROM_ERRCOD 0x0040
#define ROM_ERROR 0x00B5
#define ROM_ERRSTA 0x0038
#define ROM_ESCAPE 0x001B
#define ROM_ESCBFR 0x0002
#define ROM_ESCINP 0x0EDB
#define ROM_ESCPTR 0x0EDC
#define ROM_ESCSTR 0x0FB7
#define ROM_ESD 0x0040
#define ROM_ETB 0x0017
#define ROM_ETX 0x0003
#define ROM_EXITTS 0x0080
#define ROM_F10 0x0091
#define ROM_F14 0x0093
#define ROM_F17 0x0095
#define ROM_F18 0x0097
#define ROM_F19 0x0099
#define ROM_F20 0x009B
#define ROM_F4 0x0087
#define ROM_F6 0x0089
#define ROM_F7 0x008B
#define ROM_F8 0x008D
#define ROM_F9 0x008F
#define ROM_FALSE 0xFF00
#define ROM_FCNFLG 0x0001
#define ROM_FDXA 0x0000
#define ROM_FE 0x0020
#define ROM_FF 0x000C
#define ROM_FILLIN 0x0000
#define ROM_FILLN1 0x0001
#define ROM_FIRST 0x0F94
#define ROM_FLASH 0x0001
#define ROM_FLGPRT 0x0F66
#define ROM_FLSHEN 0x0002
#define ROM_FNCLMT 0x0024
#define ROM_FOUR 0x0004
#define ROM_FS 0x001C
#define ROM_G0 0x0F39
#define ROM_G1 0x0F3A
#define ROM_GCSR 0x0002
#define ROM_GCSRC 0x1EE5
#define ROM_GRAF52 0x00FF
#define ROM_GRAFIX 0x0002
#define ROM_GS 0x001D
#define ROM_HELP 0x0082
#define ROM_HOLD 0x0080
#define ROM_HOLDLE 0x0088
#define ROM_HT 0x0009
#define ROM_HYPHEN 0x00CF
#define ROM_HZ50 0x0033
#define ROM_HZ501 0x0009
#define ROM_HZ502 0x000C
#define ROM_HZ503 0x000F
#define ROM_HZ60 0x0022
#define ROM_INERR 0x00B6
#define ROM_INHIBX 0x0089
#define ROM_INSERT 0x009D
#define ROM_INTZ80 0x0000
#define ROM_IR 0x0040
#define ROM_JMPTST 0x00CB
#define ROM_K_AMER 0x0000
#define ROM_K_BRIT 0x0000
#define ROM_K_CANA 0x0001
#define ROM_K_DANI 0x0000
#define ROM_K_DUTC 0x0000
#define ROM_K_FINN 0x0000
#define ROM_K_FLEM 0x0000
#define ROM_K_FREN 0x0001
#define ROM_K_GERM 0x0002
#define ROM_K_ITAL 0x0000
#define ROM_K_NORW 0x0000
#define ROM_K_SPAN 0x0000
#define ROM_K_SWED 0x0000
#define ROM_K_SWIS 0x0001
#define ROM_K_SWIS 0x0002
#define ROM_K1 0x0001
#define ROM_K10 0x0008
#define ROM_K100H 0x0100
#define ROM_K10D 0x000A
#define ROM_K10H 0x0010
#define ROM_K11 0x0009
#define ROM_K11D 0x000B
#define ROM_K12 0x000A
#define ROM_K12D 0x000C
#define ROM_K13 0x000B
#define ROM_K13D 0x000D
#define ROM_K14 0x000C
#define ROM_K14D 0x000E
#define ROM_K15 0x000D
#define ROM_K15D 0x000F
#define ROM_K16 0x000E
#define ROM_K16D 0x0010
#define ROM_K17 0x000F
#define ROM_K18D 0x0012
#define ROM_K19D 0x0013
#define ROM_K2 0x0002
#define ROM_K20D 0x0014
#define ROM_K20H 0x0020
#define ROM_K23D 0x0017
#define ROM_K24D 0x0018
#define ROM_K254D 0x00FE
#define ROM_K3 0x0003
#define ROM_K30H 0x0030
#define ROM_K37Q 0x001F
#define ROM_K4 0x0004
#define ROM_K5 0x0005
#define ROM_K6 0x0006
#define ROM_K60 0x0030
#define ROM_K7 0x0007
#define ROM_K7000 0x7000
#define ROM_K80H 0x0080
#define ROM_K8D 0x0008
#define ROM_K8H 0x0008
#define ROM_K9D 0x0009
#define ROM_K9H 0x0009
#define ROM_KAH 0x000A
#define ROM_KBDFAI 0x003E
#define ROM_KBDFLG 0x0F90
#define ROM_KBDID 0x1FFB
#define ROM_KBDLCK 0x00B7
#define ROM_KBH 0x000B
#define ROM_KBRKCT 0x0ECF
#define ROM_KCH 0x000C
#define ROM_KDH 0x000D
#define ROM_KEH 0x000E
#define ROM_KEY0 0x00B1
#define ROM_KEY1 0x00B4
#define ROM_KEY2 0x00B7
#define ROM_KEY3 0x00BA
#define ROM_KEY4 0x00BD
#define ROM_KEY5 0x00C0
#define ROM_KEY6 0x00C3
#define ROM_KEY7 0x00C6
#define ROM_KEY8 0x00C9
#define ROM_KEY9 0x00CC
#define ROM_KEYCNT 0x0F67
#define ROM_KEYEND 0x0F66
#define ROM_KEYFLG 0x1FF9
#define ROM_KEYMOD 0x0001
#define ROM_KEYREQ 0x0FE8
#define ROM_KEYS 0x0F5A
#define ROM_KEYSFU 0x0009
#define ROM_KEYSIZ 0x000C
#define ROM_KFFH 0x00FF
#define ROM_KFH 0x000F
#define ROM_KLEDOF 0x0011
#define ROM_KLEDON 0x0013
#define ROM_KPDMSK 0x003F
#define ROM_KPRTCT 0x0EC7
#define ROM_KYBDCS 0x0011
#define ROM_KYBDDR 0x0010
#define ROM_KYDOWN 0x003D
#define ROM_L_DANI 0x0040
#define ROM_L_DUTC 0x0008
#define ROM_L_ENGL 0x0001
#define ROM_L_FINN 0x0200
#define ROM_L_FREN 0x0002
#define ROM_L_GERM 0x0004
#define ROM_L_ITAL 0x0080
#define ROM_L_NORW 0x0010
#define ROM_L_SPAN 0x0100
#define ROM_L_SWED 0x0020
#define ROM_LANG_C 0x0003
#define ROM_LANG_I 0x0007
#define ROM_LANG_M 0x001F
#define ROM_LANLEN 0x0F37
#define ROM_LARROW 0x00AF
#define ROM_LASTPT 0x0006
#define ROM_LATINC 0x0F2E
#define ROM_LATOFD 0x0F24
#define ROM_LATOFS 0x0EF4
#define ROM_LATOFU 0x0EF2
#define ROM_LBSTER 0x001E
#define ROM_LCKKYB 0x0080
#define ROM_LCKYBD 0x0080
#define ROM_LINATR 0x0EBF
#define ROM_LINC 0x0F26
#define ROM_LINEAD 0x0F27
#define ROM_LINEF 0x000A
#define ROM_LINES 0x0019
#define ROM_LINKIN 0x0010
#define ROM_LITBCK 0x000A
#define ROM_LNKPND 0x0008
#define ROM_LNLC1 0x1ED7
#define ROM_LNLC2 0x1ED9
#define ROM_LOKESC 0x0009
#define ROM_LONIBB 0x000F
#define ROM_M_AJFL 0x0000
#define ROM_M_INBA 0x0005
#define ROM_M_INFL 0x0001
#define ROM_M_INMA 0x0004
#define ROM_MAINTP 0x000A
#define ROM_MAJFLD 0x1EC8
#define ROM_MARK 0x00A3
#define ROM_MAXCPF 0x0014
#define ROM_MBENB 0x0004
#define ROM_METRO 0x00AD
#define ROM_MINBAS 0x1ECD
#define ROM_MINFLD 0x1EC9
#define ROM_MINMAX 0x1ECC
#define ROM_MNONE 0x0000
#define ROM_MOD173 0x005E
#define ROM_MOD200 0x004E
#define ROM_MODFGS 0x0F8F
#define ROM_MOVE 0x009F
#define ROM_MSGFLG 0x0FCF
#define ROM_MSGSNT 0x0080
#define ROM_MSGSTR 0x0FD0
#define ROM_MSTAT 0x0F44
#define ROM_MTPRTC 0x1EE6
#define ROM_M_BRK 0x005A
#define ROM_M_COMP 0x00B1
#define ROM_M_DEL 0x00BC
#define ROM_M_ENTE 0x0095
#define ROM_M_ESC 0x0071
#define ROM_M_F4 0x0059
#define ROM_M_HOLD 0x0056
#define ROM_M_LOCK 0x00B0
#define ROM_M_RET 0x00BD
#define ROM_M_SETU 0x0058
#define ROM_M_TAB 0x00BE
#define ROM_M_UP 0x00AA
#define ROM_N_OTCU 0x000E
#define ROM_N_VMAD 0x0002
#define ROM_N_VMMA 0x0009
#define ROM_NAK 0x0015
#define ROM_NBRPAR 0x0EDE
#define ROM_NBRUSE 0x0EDF
#define ROM_NEXT 0x00A7
#define ROM_NOATR 0x000E
#define ROM_NOCODE 0x00FF
#define ROM_NORM52 0x0000
#define ROM_NOTCUR 0x1ED6
#define ROM_NOTKBD 0x0FED
#define ROM_NSTP1 0x0040
#define ROM_NSTP15 0x0080
#define ROM_NSTP2 0x00C0
#define ROM_NULL 0x0000
#define ROM_NVMADD 0x1ECA
#define ROM_NVMMAX 0x1ED1
#define ROM_NVMMSK 0x000F
#define ROM_NXTKEY 0x0FCD
#define ROM_OCSCOL 0x0F29
#define ROM_OCSLIN 0x0F2A
#define ROM_OCSPOS 0x0F29
#define ROM_OE 0x0010
#define ROM_OLDGX 0x0F36
#define ROM_ONE 0x0001
#define ROM_ONEPAR 0x0080
#define ROM_ONLINE 0x0000
#define ROM_ORGMOD 0x0001
#define ROM_OUTBUF 0x0F95
#define ROM_OUTERR 0x00B5
#define ROM_OUTND 0x001E
#define ROM_P300 0x0002
#define ROM_P5016 0x0006
#define ROM_PARFGS 0x0F2F
#define ROM_PARM0 0x0EE0
#define ROM_PARM1 0x0EE1
#define ROM_PCMFLG 0x0F54
#define ROM_PCMOFF 0x0000
#define ROM_PCMON 0xFFFF
#define ROM_PCMSTM 0x0F55
#define ROM_PE 0x0008
#define ROM_PEN 0x0010
#define ROM_PERIOD 0x00D5
#define ROM_PEVEN 0x0020
#define ROM_PF1 0x00DB
#define ROM_PF2 0x00DE
#define ROM_PF3 0x00E1
#define ROM_PF4 0x00E4
#define ROM_PODD 0x0000
#define ROM_PORT0 0x0000
#define ROM_PREV 0x00A5
#define ROM_PRIOR 0x0004
#define ROM_PRNKEY 0x0085
#define ROM_PROTCT 0x0ED1
#define ROM_PROUTE 0x0009
#define ROM_PWRCNT 0x0F92
#define ROM_PWRGOO 0x0008
#define ROM_PWRIPR 0x0002
#define ROM_PWRREQ 0x00FD
#define ROM_RARROW 0x00AD
#define ROM_RAWBFI 0x0F6F
#define ROM_RAWBFO 0x0F6D
#define ROM_RAWEND 0x0F8F
#define ROM_RAWKEY 0x0F71
#define ROM_RAWSIZ 0x000E
#define ROM_REINDE 0x00D3
#define ROM_REINKY 0x00B3
#define ROM_REMAPV 0x0004
#define ROM_REQKYI 0x00AB
#define ROM_RESERV 0x00FF
#define ROM_RESETP 0x0D41
#define ROM_RESUMX 0x008B
#define ROM_REVBIT 0x0001
#define ROM_REVVID 0x000D
#define ROM_RIGHT 0x0F57
#define ROM_RMODEM 0x0FEE
#define ROM_RS 0x001E
#define ROM_RTS 0x0020
#define ROM_RXEN 0x0004
#define ROM_RXRDY 0x0002
#define ROM_SAMRT1 0x003C
#define ROM_SAVATR 0x1EC4
#define ROM_SAVCP 0x0F4F
#define ROM_SAVCR 0x0F38
#define ROM_SAVCRL 0x000E
#define ROM_SAVEND 0x0F46
#define ROM_SAVLNK 0x1EC6
#define ROM_SAWCSI 0x000C
#define ROM_SBASE 0x1FF7
#define ROM_SBCONH 0x000B
#define ROM_SBCONS 0x25E0
#define ROM_SBOT 0x1EFA
#define ROM_SBRK 0x0008
#define ROM_SBRKCT 0x0ED3
#define ROM_SCAN_T 0x0010
#define ROM_SCRATR 0x0007
#define ROM_SCRBEG 0x0012
#define ROM_SCRCTR 0x0F30
#define ROM_SCRDWN 0x0001
#define ROM_SCRFLG 0x000C
#define ROM_SCRFRE 0x0D41
#define ROM_SCRINP 0x0004
#define ROM_SCRN50 0x0009
#define ROM_SCRPT1 0x0004
#define ROM_SCRPTR 0x0003
#define ROM_SCS 0x0080
#define ROM_SETDIS 0x007F
#define ROM_SETIPR 0x0004
#define ROM_SETKEY 0x0002
#define ROM_SETUF 0x0001
#define ROM_SETUP 0x0081
#define ROM_SHFAD1 0x1EEA
#define ROM_SHFAD2 0x1EEE
#define ROM_SHFDT1 0x1EEC
#define ROM_SHFDT2 0x1EF0
#define ROM_SHFIN 0x0000
#define ROM_SHFLA2 0x1EF2
#define ROM_SHFOUT 0x00FF
#define ROM_SHFPND 0x0002
#define ROM_SHFTIN 0x000F
#define ROM_SHFTLE 0x0084
#define ROM_SHIFT 0x0002
#define ROM_SHLINE 0x0017
#define ROM_SHSW 0x0006
#define ROM_SIGER 0x1FFA
#define ROM_SILOK 0x0001
#define ROM_SILOS 0x1EE7
#define ROM_SISO 0x0F3C
#define ROM_SLORAT 0x001E
#define ROM_SMODE 0x0000
#define ROM_SMSCNT 0x0ECD
#define ROM_SMSRAT 0x1EE8
#define ROM_SMTHSC 0x1EF5
#define ROM_SNDBEL 0x00A7
#define ROM_SNDCLI 0x009F
#define ROM_SNDPWR 0x0004
#define ROM_SO 0x000E
#define ROM_SOH 0x0001
#define ROM_SPCGRC 0x0002
#define ROM_SPSAV 0x1FF7
#define ROM_SRTCUR 0x0002
#define ROM_SSFLG 0x0F3B
#define ROM_STATER 0x0040
#define ROM_STKFLG 0x1EF6
#define ROM_STKLEN 0x00FD
#define ROM_STKSWP 0x0001
#define ROM_STPCUR 0x0001
#define ROM_STRINX 0x0FCC
#define ROM_STRZ80 0x0020
#define ROM_STX 0x0002
#define ROM_SUACPD 0x1EF9
#define ROM_SUB 0x001A
#define ROM_SVAREA 0x0F46
#define ROM_SWVFRE 0x1EE1
#define ROM_SWVPOS 0x1EDF
#define ROM_SYN 0x0016
#define ROM_SYNDET 0x0040
#define ROM_SYPAR1 0x1FFC
#define ROM_SYSPAR 0x1FFE
#define ROM_TERM 0x00FF
#define ROM_TERMOD 0x1EF8
#define ROM_THREE 0x0003
#define ROM_TILDE 0x00BF
#define ROM_TOGHLD 0x0008
#define ROM_TOP 0x0F58
#define ROM_TRMMOD 0x0001
#define ROM_TRUE 0x00FF
#define ROM_TSTMOD 0x00B8
#define ROM_TWO 0x0002
#define ROM_TXEMPT 0x0004
#define ROM_TXEN 0x0001
#define ROM_TXLOCK 0x0002
#define ROM_TXRDY 0x0001
#define ROM_UARROW 0x00A9
#define ROM_UCOFF 0x003E
#define ROM_UK 0x0001
#define ROM_UKLANG 0x0001
#define ROM_UNUSED 0x00FF
#define ROM_US 0x001F
#define ROM_USEIT 0x0F3E
#define ROM_VDCTRL 0x000C
#define ROM_VFRATE 0x0023
#define ROM_VFRIPF 0x0002
#define ROM_VIDCTL 0x010C
#define ROM_VIDTIM 0x0004
#define ROM_VOL1 0x0007
#define ROM_VOL2 0x0006
#define ROM_VOL3 0x0005
#define ROM_VOL4 0x0004
#define ROM_VOL5 0x0003
#define ROM_VOL6 0x0002
#define ROM_VOL7 0x0001
#define ROM_VOL8 0x0000
#define ROM_VT 0x000B
#define ROM_VT52GM 0x0F45
#define ROM_WAITLE 0x0081
#define ROM_WID80 0x0000
#define ROM_WRPPND 0x0002
#define ROM_XLINBR 0x0F33
#define ROM_XMTRCV 0x0FEC
#define ROM_XTRA 0x0F31
#define ROM_XTRATR 0x1EF4
#define ROM_X_OFF 0x0F6B
#define ROM_Z80ORI 0x0F35
#define ROM_Z80RUN 0x0001
#define ROM_Z80STP 0x0000
#define ROM_Z80TAS 0x0F34
#define ROM_ZERO 0x0000
>>>>>>>> DOSERROR.C
/*
 * DOSERROR.C
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 * char *dos_error (error_num)
 *
 *	Return address of string describing an MS-DOS error code
 *	(useful only for Xenix-style calls, CP/M-style calls have
 *	different error codes)
 */

static char
    ebuf[40];

char *dos_error (error_num)
int error_num;
{
switch (error_num)
    {
    case  1: return ("invalid function");
    case  2: return ("file not found");
    case  3: return ("path not found");
    case  4: return ("too many open files");
    case  5: return ("access denied");
    case  6: return ("invalid handle");
    case  7: return ("arena trashed");
    case  8: return ("not enough memory");
    case  9: return ("invalid block");
    case 10: return ("bad environment");
    case 11: return ("bad format for EXE file");
    case 12: return ("invalid access");
    case 13: return ("invalid data");
    case 15: return ("invalid drive");
    case 16: return ("can't remove current directory");
    case 17: return ("can't rename across devices");
    case 18: return ("no more files");
    default:
	sprintf (ebuf, "unknown error code %d", error_num);
	return (ebuf);
    };
}
>>>>>>>> DOSFUN.ASM
; \usr\dos\dosfun.asm
;
; Various routines to call generic MS-DOS functions for C programs.
;
; Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard, Mass. USA
;
; This software may be freely copied and disseminated for
; noncommercial purposes, if and only if this entire copyright
; statement and notice is included intact.  This software, and
; the information contained herein, may not be used for commercial
; purposes without my prior written permission.
;

get_date		equ 2AH
get_time		equ 2CH
break			equ 33H

dos	macro call_number
	mov ah,call_number
	int 21H
	endm

include \ubin\c86\v210\model.h
include \ubin\c86\v210\prologue.h

	public get_break, set_break
	public tod_hours, tod_minutes, tod_seconds
	public toy_year, toy_month, toy_date, toy_dow

set_break:
	push bp
	mov bp,sp
	mov al,1		;"set" function
	mov dl,4[bp]		;get state to set
	dos break
	pop bp
	ret

get_break:
	mov al,0		;"get" function
	dos break
	mov al,dl
	mov ah,0
	ret

tod_hours:
	dos get_time
	mov al,ch
	mov ah,0
	ret

tod_minutes:
	dos get_time
	mov al,cl
	mov ah,0
	ret

tod_seconds:
	dos get_time
	mov al,dh
	mov ah,0
	ret

toy_year:
	dos get_date
	mov ax,cx
	ret

toy_month:
	dos get_date
	mov al,dh
	mov ah,0
	ret

toy_date:
	dos get_date
	mov al,dl
	mov ah,0
	ret

toy_dow:
	dos get_date
	mov ah,0
	ret

include \ubin\c86\v210\epilogue.h

	end
>>>>>>>> ENVFETCH.C
#define CPEEK(off,seg) ((unsigned char) peek (off, seg))

/*
 * env_fetch (name, envseg, dest)
 *
 *	Search environment at segment address for variable named and
 *	if found store value of variable at dest.  Return true (nonzero)
 *	if found.
 */

env_fetch (name, envseg, dest)
char *name, *dest;
unsigned int envseg;
{
unsigned int
    envoff;

char
    c,
    *dp,
    leftpart[132];

envoff = 0;				/* starting environment offset */
dp = leftpart;
while (1)
    {
    c = CPEEK (envoff++, envseg);
    switch (c)
	{
	case '=':
            *dp = 0;			/* make ASCIZ */
            if (strcmp (leftpart, "COMSPEC") == 0)
                {
		dp = dest;
		while (*dp++ = CPEEK (envoff++, envseg));
                return (1);
                };
            while (CPEEK (envoff++, envseg));	/* skip to end */
	    dp = leftpart;		/* reset destination pointer */
	    break;
	case 0:
	    if (CPEEK (envoff + 1, envseg) == 0)
		return (0);
	    dp = leftpart;
	    break;
	default:
	    *dp++ = c;
	};
    };
}
>>>>>>>> HARDWARE.ASM
; \usr\rainbow\hardware.asm
;
; Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard, Mass. USA
;
; This software may be freely copied and disseminated for
; noncommercial purposes, if and only if this entire copyright
; statement and notice is included intact.  This software, and
; the information contained herein, may not be used for commercial
; purposes without my prior written permission.
;
;	This file provides various routines to interface with the
;	the hardware on a DEC Rainbow 100.  The routines which
;	manipulate AUX (comm port) require MS-DOS version 2.05
;	or later, which has the new BIOS which supports all this.
;	This code has not been tested on a 100+, but probably will work.

aux_input		equ 3
aux_output		equ 4
io_control		equ 44H

dos	macro call_number
	mov ah,call_number
	int 21H
	endm

include \ubin\c86\v210\model.h
include \ubin\c86\v210\prologue.h

	public _put_line, _putattr, getkey, getaux, putaux
	public disable_cursor, enable_cursor, auxctl
	public aux_hungry, putcon, auxres

; _put_line (row, col, length, string)

_put_line:
	push bp
	mov bp,sp
	mov ax,2		;chars only, no attributes
	mov bl,4[bp]		;row
	mov bh,6[bp]		;col
	mov cx,8[bp]		;byte count
	mov si,10[bp]		;char offset
	mov di,14H		;function code
	mov bp,ds		;for ROM code
	int 18H			;write to screen
	pop bp
	ret

; _putattr (row, col, length, attribute)

_putattr:
	push bp
	mov bp,sp
	mov di,8
	int 18H			;disable cursor
	mov ax,1		;attributes only
	mov bl,BYTE PTR 4[bp]	;row
	mov bh,BYTE PTR 6[bp]	;col
	mov cx,8[bp]		;byte count
	mov dx,10[bp]		;attributes offset
	mov di,14H		;function code
	mov bp,ds		;for ROM code
	int 18H			;write to screen
	mov di,0AH		;reenable cursor
	int 18H
	pop bp
	ret

disable_cursor:
	mov di,8
	int 18H
	ret

enable_cursor:
	mov di,0AH
	int 18H
	ret

;Read keyboard - return -1 if no char available, -2 if level 2 sequence
; in progress, otherwise return (16-bit extended ASCII) character

getkey:
	mov di,6
	int 18H			;get char and status
	cmp cl,0FFH		;char available?
	je getkey_0		;yes, return it
	mov ax,0FFFFH		;no, return -1
	cmp cl,0		;if no char available
	je getkey_0		; ..
	mov ax,0FFFEH		;otherwise return -2
getkey_0:
	ret

;Put character out to console (must bypass DOS to avoid ctrl-C monkeying
; and have any speed left)

putcon:
	push bp
	mov bp,sp
	mov di,0
	mov al,4[bp]
	int 18H
	pop bp
	ret

;Reset AUX (comm port) to NVM settings

auxres:
	push bp
	mov al,2		;IOCTL fancy stuff
	mov auxfun,1		;function code (reset to NVM settings)
	mov bx,3		;AUX device
	mov dx,OFFSET auxfun	;addr of IOCTL packet
	dos io_control		;do it
	pop bp
	ret

; auxctl (buffer)
; char *buffer
;
; General IOCTL interface to BIOS (requires DOS v2.05 or later)
;

auxctl:
	push bp
	mov bp,sp		;stack frame
	mov al,2		;function code
	mov bx,3		;builtin AUX handle
	mov dx,4[bp]		;get caller's buffer address
	dos io_control		;call the BIOS
	pop bp
	ret

;Read AUX (comm port), return -1 if no char available, else char

getaux:
	push bp
	mov al,2		;IOCTL function
	mov bx,3		;AUX handle
	mov dx,OFFSET auxfun	;IOCTL packet address in DS:DX
	mov auxfun,7		;Get char or FF if none there
	dos io_control		; ..
	cmp auxret,0FFH		;char available?
	jne noaux		;no
	mov al,auxchr		;yes, return char
	mov ah,0		;zero high-order byte
	pop bp
	ret
noaux:
	mov ax,0FFFFH
	pop bp
	ret

putaux:
	push bp
	mov bp,sp
	mov al,2		;IOCTL function
	mov bx,3		;AUX handle
	mov dx,OFFSET auxfun	;IOCTL packet address in DS:DX
	mov auxfun,10		;Put char (nonblocking) function
	mov cl,4[bp]		;Char to send
	mov auxchr,cl
	dos io_control		; ..
	mov al,auxret		;Return success/failure code to caller
	mov ah,0
	pop bp
	ret

aux_hungry:
	push bp
	mov al,2		;IOCTL function
	mov bx,3		;AUX handle
	mov dx,OFFSET auxfun	;IOCTL packet address in DS:DX
	mov auxfun,9		;Read transmitter status
	dos io_control		; ..
	mov al,auxret		;Return status to caller
	mov ah,0
	pop bp
	ret
nothungry:
	mov ax,0
	pop bp
	ret


include \ubin\c86\v210\epilogue.h

@datab	segment

;AUX IOCTL packet (requires MS-DOS v2.05)

auxfun	db (?)
auxret	db (?)
auxchr	db (?)
auxsts	db (?)
auxbuf	db (?)

@datab	ends

	end
>>>>>>>> KERMIT.C
#include <stdio.h>
#include "kermit.h"

/*
 * KERMIT.C
 *
 *	KERMIT protocol routines
 *
 *  Adapted from:
 *
 *  UNIX Kermit, Columbia University, 1981, 1982, 1983
 *	Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
 *
 *
 *  EXTERNAL INTERFACE:
 *
 *    INITIALIZE PROTOCOL MODULE (must be called before using protocol)
 *	kerini();
 *
 *    SENDING FILES
 *	filnam = "filespec";  sendsw();
 *
 *    RECEIVING FILES
 *	filnam = "filespec";  recsw();
 *
 *    SERVER COMMANDS
 *	sendcmdsw('char',"cmd")
 *
 *  The following external routines must be supplied:
 *
 *	    char read_modem ()			gets char from modem (blocking)
 *	    write_modem (char *s, int count)	writes string to modem
 *	    flushinput ()			empty modem input buffer
 *	    printmsg (char *s)			writes message to screen
 *	    notify_hook ()		called when packets leave or arrive to
 *					give a hook for displaying stats on
 *					screen
 */

#define COUNTED_STRING(s) strlen(s),s

/* Global Variables */

int	size,		    /* Size of present data */
	rpsiz,		    /* Maximum receive packet size */
	spsiz,		    /* Maximum send packet size */
	pad,		    /* How much padding to send */
	timint,		    /* Timeout for foreign host on sends */
	n,		    /* Packet number */
	numtry,		    /* Times this packet retried */
	oldtry,		    /* Times previous packet retried */
	maxtry,		    /* Maximum number of times to retry */
	packets_sent,	    /* Packets sent */
	packets_received,   /* packets received */
	bad_packets,	    /* packets received with checksum errors */
	naked_packets,	    /* packets naked by other end */
	remote,		    /* -1 means we're a remote kermit */
	image,		    /* -1 means 8-bit mode */
	binary,		    /* Nonzero => binary files ("wb" or "rb" fopen) */
	quote_8bit,	    /* Nonzero => 8th-bit quoting */
	debug,		    /* indicates level of debugging output (0=none) */
	filnamcnv,	    /* -1 means do file name case conversions */
	filecount;	    /* Number of files left to send */

long
	bytes_xferred;

char	state,		    /* Present state of the automaton */
	padchar,	    /* Padding character to send */
	eol,		    /* End-Of-Line character to send */
	quote_8bit_char,    /* character to use for 8th-bit quoting */
	quote,		    /* Quote character in incoming data */
	**filelist,	    /* List of files to be sent */
	*filnam,	    /* Current file name */
	recpkt[MAXPACKSIZ], /* Receive packet buffer */
	packet[MAXPACKSIZ]; /* Packet buffer */

FILE	*fp;		    /* File pointer for current disk file */

jmp_buf
    abort_env,		    /* Transaction abort SETJMP buffer */
    env;		    /* Timeout SETJMP buffer */

/*
 *  k e r i n i
 *
 *  Init protocol routines
 *
 */

kerini()
{
eol = CR;			/* EOL for outgoing packets */
quote = MYQUOTE;		/* Standard control-quote char "#" */
quote_8bit_char = MY8BITQUOTE;	/* Standard 8th-bit quote char */
pad = 0;			/* No padding */
binary = TRUE;			/* Default is binary files */
quote_8bit = FALSE;		/* Default is no 8th-bit quoting */
padchar = NULL;			/* Use null if any padding wanted */
fp = NULL;			/* File pointer */
timint = DEFTIM;		/* Initial timeout value */
maxtry = MAXTRY;		/* Initial retry counter value */

#if UCB4X			/* Default to 7-bit masking, CRLF */
image = FALSE;			/* translation and filename case */
filnamcnv = TRUE;		/* conversion for UNIX systems */
#else
image = TRUE;			/* Default to no processing for */
filnamcnv = FALSE;		/* non-UNIX systems */
#endif

filecount = 1;			/* One file at a time */
}

/*
 *  s e n d s w
 *
 *  Sendsw is the state table switcher for sending files.  It loops until
 *  either it finishes, or an error is encountered.  The routines called
 *  by sendsw are responsible for changing the state.
 *
 */

sendsw()
{
    char sinit(), sfile(), sdata(), seof(), sbreak();

    if (setjmp(abort_env)) return(FALSE);
    if (debug) printf ("Entering sendsw\n");
    state = 'S';			/* Send initiate is the start state */
    n = 0;				/* Initialize message number */
    packets_sent = packets_received = 0;/* Initialize statistics */
    bad_packets = naked_packets = 0;
    bytes_xferred = 0L;
    numtry = 0;				/* Say no tries yet */
    while(TRUE)				/* Do this as long as necessary */
    {
	if (debug) printf("sendsw state: %c\n",state);
	switch(state)
	{
	    case 'S':	state = sinit();  break; /* Send-Init */
	    case 'F':	state = sfile();  break; /* Send-File */
	    case 'D':	state = sdata();  break; /* Send-Data */
	    case 'Z':	state = seof();	  break; /* Send-End-of-File */
	    case 'B':	state = sbreak(); break; /* Send-Break */
	    case 'C':	return (TRUE);		 /* Complete */
	    case 'A':
		if (fp != NULL) fclose(fp); fp = NULL;
		return (FALSE);			 /* "Abort" */
	    default:
		if (fp != NULL) fclose(fp); fp = NULL;
		return (FALSE);			 /* Unknown, fail */
	}
    }
}


/*
 *  s i n i t
 *
 *  Send Initiate: send this host's parameters and get other side's back.
 */

char sinit()
{
    int num, len;			/* Packet number, length */

    if (numtry++ > maxtry) return('A'); /* If too many tries, give up */

    spar(packet);			/* Fill up init info packet */
    spack('S',n,7,packet);		/* Send my init info */
    switch(rpack(&len,&num,recpkt))	/* What was the reply? */
    {
	case 'N':
	    naked_packets++;		/* count NAKs */
	    notify_hook ();		/* maybe diddle screen */
	    return(state);		/* NAK, try it again */

	case 'Y':			/* ACK */
	    if (n != num)		/* If wrong ACK, stay in S state */
		return(state);		/* and try again */
	    rpar(recpkt);		/* Get other side's init info */

	    if (eol == 0) eol = '\n';	/* Check and set defaults */
	    if (quote == 0) quote = MYQUOTE;

	    numtry = 0;			/* Reset try counter */
	    n = (n+1)%64;		/* Bump packet count */
	    return('F');		/* OK, switch state to F */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE: return(state);	/* Receive failure, try again */

	default: return('A');		/* Anything else, just "abort" */
   }
 }


/*
 *  s f i l e
 *
 *  Send File Header.
 */

char sfile()
{
    int num, len;			/* Packet number, length */
    char filnam1[50],			/* Converted file name */
	*newfilnam,			/* Pointer to file name to send */
	*cp;				/* char pointer */

    if (numtry++ > maxtry) return('A'); /* If too many tries, give up */
    
    if (fp == NULL)			/* If not already open, */
    {	if (debug) printf("   Opening %s for sending.\n",filnam);
	if (binary)
	    fp = fopen(filnam,"rb");	/* open the file to be sent */
	else
	    fp = fopen(filnam,"r");
	if (fp == NULL)			/* If bad file pointer, give up */
	{
	    error("Cannot open file %s",filnam);
	    return('A');
	}
    }

    strcpy(filnam1, filnam);		/* Copy file name */
    newfilnam = cp = filnam1;
    while (*cp != '\0')			/* Strip off all leading directory */
	{				/* and device names */
	char c;
	if ((c = *cp++) == '\\' || c == ':')
	    newfilnam = cp;
	};

    if (filnamcnv)			/* Convert lower case to upper	*/
	for (cp = newfilnam; *cp != '\0'; cp++)
	    if (*cp >= 'a' && *cp <= 'z')
		*cp ^= 040;

    len = cp - newfilnam;		/* Compute length of new filename */

    printmsg("Sending %s as %s",filnam,newfilnam);

    spack('F',n,len,newfilnam);		/* Send an F packet */
    switch(rpack(&len,&num,recpkt))	/* What was the reply? */
    {			
	case 'N':			/* NAK, just stay in this state, */
	    num = (--num<0 ? 63:num);	/* unless it's NAK for next packet */
	    if (n != num)		/* which is just like an ACK for */ 
		{
		naked_packets++;
		notify_hook ();
		return(state);		/* this packet so fall thru to... */
		};

	case 'Y':			/* ACK */
	    if (n != num) return(state); /* If wrong ACK, stay in F state */
	    numtry = 0;			/* Reset try counter */
	    n = (n+1)%64;		/* Bump packet count */
	    size = bufill(packet);	/* Get first data from file */
	    return('D');		/* Switch state to D */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE: return(state);	/* Receive failure, stay in F state */

	default:    return('A');	/* Something else, just "abort" */
    }
}


/*
 *  s d a t a
 *
 *  Send File Data
 */

char sdata()
{
    int num, len;			/* Packet number, length */

    if (numtry++ > maxtry) return('A'); /* If too many tries, give up */

    spack('D',n,size,packet);		/* Send a D packet */
    switch(rpack(&len,&num,recpkt))	/* What was the reply? */
    {		    
	case 'N':			/* NAK, just stay in this state, */
	    num = (--num<0 ? 63:num);	/* unless it's NAK for next packet */
	    if (n != num)		/* which is just like an ACK for */
		{
		naked_packets++;
		notify_hook ();
		return(state);		/* this packet so fall thru to... */
		};
		
	case 'Y':			/* ACK */
	    if (n != num) return(state); /* If wrong ACK, fail */
	    numtry = 0;			/* Reset try counter */
	    n = (n+1)%64;		/* Bump packet count */
	    if ((size = bufill(packet)) == EOF) /* Get data from file */
		return('Z');		/* If EOF set state to that */
	    return('D');		/* Got data, stay in state D */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE: return(state);	/* Receive failure, stay in D */

	default:    return('A');	/* Anything else, "abort" */
    }
}


/*
 *  s e o f
 *
 *  Send End-Of-File.
 */

char seof()
{
    int num, len;			/* Packet number, length */
    if (numtry++ > maxtry) return('A'); /* If too many tries, "abort" */

    spack('Z',n,0,packet);		/* Send a 'Z' packet */
    switch(rpack(&len,&num,recpkt))	/* What was the reply? */
    {
	case 'N':			/* NAK, just stay in this state, */
	    num = (--num<0 ? 63:num);	/* unless it's NAK for next packet, */
	    if (n != num)		/* which is just like an ACK for */
		{
		naked_packets++;
		notify_hook ();
		return(state);		/* this packet so fall thru to... */
		};

	case 'Y':			/* ACK */
	    if (n != num) return(state); /* If wrong ACK, hold out */
	    numtry = 0;			/* Reset try counter */
	    n = (n+1)%64;		/* and bump packet count */
	    if (debug) printf("	  Closing input file %s, ",filnam);
	    fclose(fp);			/* Close the input file */
	    fp = NULL;			/* Set flag indicating no file open */ 

	    if (debug) printf("looking for next file...\n");
/*	    if (gnxtfl() == FALSE) */	/* No more files go? */
		return('B');		/* if not, break, EOT, all done */
	    if (debug) printf("	  New file is %s\n",filnam);
	    return('F');		/* More files, switch state to F */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE: return(state);	/* Receive failure, stay in Z */

	default:    return('A');	/* Something else, "abort" */
    }
}


/*
 *  s b r e a k
 *
 *  Send Break (EOT)
 */

char sbreak()
{
    int num, len;			/* Packet number, length */
    if (numtry++ > maxtry) return('A'); /* If too many tries "abort" */

    spack('B',n,0,packet);		/* Send a B packet */
    switch (rpack(&len,&num,recpkt))	/* What was the reply? */
    {
	case 'N':			/* NAK, just stay in this state, */
	    num = (--num<0 ? 63:num);	/* unless NAK for previous packet, */
	    if (n != num)		/* which is just like an ACK for */
		{
		naked_packets++;
		notify_hook ();
		return(state);		/* this packet so fall thru to... */
		};

	case 'Y':			/* ACK */
	    if (n != num) return(state); /* If wrong ACK, fail */
	    numtry = 0;			/* Reset try counter */
	    n = (n+1)%64;		/* and bump packet count */
	    return('C');		/* Switch state to Complete */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE: return(state);	/* Receive failure, stay in B */

	default:    return ('A');	/* Other, "abort" */
   }
}

/*
 *  r e c s w
 *
 *  This is the state table switcher for receiving files.
 */

recsw()
{
    char rinit(), rfile(), rdata();	/* Use these procedures */

    if (setjmp(abort_env)) return(FALSE);
    state = 'R';			/* Receive-Init is the start state */
    n = 0;				/* Initialize message number */
    numtry = 0;				/* Say no tries yet */
    packets_sent = packets_received = 0;/* Initialize statistics */
    bad_packets = naked_packets = 0;
    bytes_xferred = 0L;

    while(TRUE)
    {
	if (debug) printf(" recsw state: %c\n",state);
	switch(state)			/* Do until done */
	{
	    case 'R':	state = rinit(); break; /* Receive-Init */
	    case 'F':	state = rfile(); break; /* Receive-File */
	    case 'D':	state = rdata(); break; /* Receive-Data */
	    case 'C':	return(TRUE);		/* Complete state */
	    case 'A':
		if (fp != NULL) fclose(fp); fp = NULL;
		return(FALSE);			/* "Abort" state */
	}
    }
}
    
/*
 *  r i n i t
 *
 *  Receive Initialization
 */
  
char rinit()
{
    int len, num;			/* Packet length, number */

    if (numtry++ > maxtry) return('A'); /* If too many tries, "abort" */

    spack('R',n,strlen(filnam),filnam);	/* Send "receive init" */

    switch(rpack(&len,&num,recpkt))	/* Get a packet */
    {
	case 'S':			/* Send-Init */
	    rpar(recpkt,len);		/* Get the other side's init data */
	    spar(packet);		/* Fill up packet with my init info */
	    spack('Y',n,7,packet);	/* ACK with my parameters */
	    oldtry = numtry;		/* Save old try count */
	    numtry = 0;			/* Start a new counter */
	    n = (n+1)%64;		/* Bump packet number, mod 64 */
	    return('F');		/* Enter File-Receive state */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case 'N':			/* NAK */
	    naked_packets++;
	    notify_hook();
	    return(state);		/* stay in this state */

	case FALSE:			/* Didn't get packet */
	    spack('N',n,0,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:     return('A');	/* Some other packet type, "abort" */
    }
}


/*
 *  r f i l e
 *
 *  Receive File Header
 */

char rfile()
{
    int num, len;			/* Packet number, length */
    char filnam1[50];			/* Holds the converted file name */

    if (numtry++ > maxtry) return('A'); /* "abort" if too many tries */

    switch(rpack(&len,&num,packet))	/* Get a packet */
    {
	case 'S':			/* Send-Init, maybe our ACK lost */
	    if (oldtry++ > maxtry) return('A'); /* If too many tries "abort" */
	    if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
	    {				/* Yes, ACK it again with  */
		spar(packet);		/* our Send-Init parameters */
		spack('Y',num,7,packet);
		numtry = 0;		/* Reset try counter */
		return(state);		/* Stay in this state */
	    }
	    else return('A');		/* Not previous packet, "abort" */

	case 'Z':			/* End-Of-File */
	    if (oldtry++ > maxtry) return('A');
	    if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
	    {				/* Yes, ACK it again. */
		spack('Y',num,0,0);
		numtry = 0;
		return(state);		/* Stay in this state */
	    }
	    else return('A');		/* Not previous packet, "abort" */

	case 'F':			/* File Header (just what we want) */
	    if (num != n) return('A');	/* The packet number must be right */
	    strcpy(filnam1, packet);	/* Copy the file name */

	    if (filnamcnv)		/* Convert upper case to lower */
		for (filnam=filnam1; *filnam != '\0'; filnam++)
		    if (*filnam >= 'A' && *filnam <= 'Z')
			*filnam |= 040;
	    if (fp != NULL)
		fclose (fp);
	    if ((fp = fopen(filnam1,"r")) != NULL)
		{
		error("File %s already exists",filnam1);
		spack('E',n,COUNTED_STRING("File already exists"));
		fclose(fp);
		fp = NULL;
		return('A');
		};
	    if (binary)
	        fp = fopen(filnam1,"wb");	/* Try to open a new file */
	    else
		fp = fopen(filnam1,"w");
	    if (fp == NULL)
		{ error("Cannot create %s",filnam1); return('A'); }
	    else
		{
#if MSDOS			/* convert to uppercase, 8.3 length name */
		int i;  char c, *sp;
		char name[NAMELEN + 1], ext[EXTLEN + 1];
		sp = filnam1;
		i = 0;
		while ((c = *sp++) && c != '.' && i < NAMELEN)
		    name[i++] = toupper (c);
		name[i] = 0;
		if (c && (c != '.'))
		    while ((c = *sp++) && c != '.') ;
		i = 0;
		if (c)
		    while ((c = *sp++) && i < EXTLEN)
			ext[i++] = toupper (c);
		ext[i] = 0;
		printmsg("Receiving %s as %s.%s",packet,name,ext);
#else
		printmsg("Receiving %s as %s",packet,filnam1);
#endif
		};

	    spack('Y',n,0,0);		/* Acknowledge the file header */
	    oldtry = numtry;		/* Reset try counters */
	    numtry = 0;			/* ... */
	    n = (n+1)%64;		/* Bump packet number, mod 64 */
	    return('D');		/* Switch to Data state */

	case 'B':			/* Break transmission (EOT) */
	    if (num != n) return ('A'); /* Need right packet number here */
	    spack('Y',n,0,0);		/* Say OK */
	    return('C');		/* Go to complete state */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE:			/* Didn't get packet */
	    spack('N',n,0,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:    return ('A');	/* Some other packet, "abort" */
    }
}


/*
 *  r d a t a
 *
 *  Receive Data
 */

char rdata()
{
    int num, len;			/* Packet number, length */
    if (numtry++ > maxtry) return('A'); /* "abort" if too many tries */

    switch(rpack(&len,&num,packet))	/* Get packet */
    {
	case 'D':			/* Got Data packet */
	    if (num != n)		/* Right packet? */
	    {				/* No */
		if (oldtry++ > maxtry)
		    return('A');	/* If too many tries, abort */
		if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
		{			/* Previous packet again? */
		    spack('Y',num,6,packet); /* Yes, re-ACK it */
		    numtry = 0;		/* Reset try counter */
		    return(state);	/* Don't write out data! */
		};
		bad_packets++;
		notify_hook();
		return('A');		/* sorry, wrong number */
	    }
	    /* Got data with right packet number */
	    if (! bufemp(packet,len))
		{
		spack('E',n,COUNTED_STRING("Write failed (disk full?)"));
		error("Write failed (disk full?)");
		return('A');
		};
	    spack('Y',n,0,0);		/* Acknowledge the packet */
	    oldtry = numtry;		/* Reset the try counters */
	    numtry = 0;			/* ... */
	    n = (n+1)%64;		/* Bump packet number, mod 64 */
	    return('D');		/* Remain in data state */

	case 'F':			/* Got a File Header */
	    if (oldtry++ > maxtry)
		return('A');		/* If too many tries, "abort" */
	    if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
	    {				/* It was the previous one */
		spack('Y',num,0,0);	/* ACK it again */
		numtry = 0;		/* Reset try counter */
		return(state);		/* Stay in Data state */
	    }
	    else return('A');		/* Not previous packet, "abort" */

	case 'Z':			/* End-Of-File */
	    if (num != n) return('A');	/* Must have right packet number */
	    spack('Y',n,0,0);		/* OK, ACK it. */
	    fclose(fp);			/* Close the file */
	    fp = NULL;
	    n = (n+1)%64;		/* Bump packet number */
	    return('F');		/* Go back to Receive File state */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE:			/* Didn't get packet */
	    spack('N',n,0,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:     return('A');	/* Some other packet, "abort" */
    }
}

/*
 *  s e n d c m d s w
 *
 *  This is the state table switcher for sending server commands files.
 */

sendcmdsw(cmdchr,cmdstr)
char cmdchr, *cmdstr;
{
    char resp_init(), resp_data ();

    if (setjmp(abort_env)) return(FALSE);
    state = 'R';			/* Response-Init is the start state */
    n = 0;				/* Initialize message number */
    numtry = 0;				/* Say no tries yet */
    packets_sent = packets_received = 0;/* Initialize statistics */
    bad_packets = naked_packets = 0;
    bytes_xferred = 0L;

    while(TRUE)
    {
	if (debug) printf(" sendcmdsw state: %c\n",state);
	switch(state)			/* Do until done */
	{
	    case 'R':	state = resp_init(cmdchr, cmdstr); /* Response-Init */
			break;
	    case 'D':	state = resp_data(); break; /* Response-Data */
	    case 'C':	return(TRUE);		/* Complete state */
	    case 'A':	return(FALSE);		/* "Abort" state */
	}
    }
}
    
/*
 *  r e s p _ i n i t
 *
 *  Server command response init
 */
  
char resp_init(cmdchr,cmdstr)
char cmdchr, *cmdstr;
{
    int len, num;			/* Packet length, number */

    if (numtry++ > maxtry) return('A'); /* If too many tries, "abort" */

    spack(cmdchr,n,strlen(cmdstr),cmdstr); /* Send server command */

    switch(rpack(&len,&num,recpkt))	/* Get a packet */
    {
	case 'Y':			/* ACK (short reply) */
	    if (n != num) return(state); /* If wrong ACK, fail */
	    numtry = 0;			/* Reset try counter */
	    n = (n+1)%64;		/* Bump packet count */
	    putmsgc(recpkt,len);	/* Type the response */
	    return('C');		/* Transaction complete */

	case 'N':			/* NAK */
	    naked_packets++;
	    notify_hook();
	    return(state);		/* stay in this state */

	case 'Z':			/* End-Of-File */
	    if (oldtry++ > maxtry) return('A');
	    if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
	    {				/* Yes, ACK it again. */
		spack('Y',num,0,0);
		numtry = 0;
		return(state);		/* Stay in this state */
	    }
	    else return('A');		/* Not previous packet, "abort" */

	case 'F':			/* File Header (Long reply) */
	    if (num != n) return('A');	/* The packet number must be right */
	    putmsgc(recpkt,len);	/* Type "filename", if any */
	    spack('Y',n,0,0);		/* Acknowledge the file header */
	    oldtry = numtry;		/* Reset try counters */
	    numtry = 0;			/* ... */
	    n = (n+1)%64;		/* Bump packet number, mod 64 */
	    return('D');		/* Switch to Data state */

	case 'B':			/* Break transmission (EOT) */
	    if (num != n) return ('A'); /* Need right packet number here */
	    spack('Y',n,0,0);		/* Say OK */
	    return('C');		/* Go to complete state */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE:			/* Didn't get packet */
	    spack('N',n,0,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:    return ('A');	/* Some other packet, "abort" */
    }
}


/*
 *  r e s p _ d a t a
 *
 *  Receive server command response data (long response)
 */

char resp_data()
{
    int num, len;			/* Packet number, length */
    if (numtry++ > maxtry) return('A'); /* "abort" if too many tries */

    switch(rpack(&len,&num,packet))	/* Get packet */
    {
	case 'D':			/* Got Data packet */
	    if (num != n)		/* Right packet? */
	    {				/* No */
		if (oldtry++ > maxtry)
		    return('A');	/* If too many tries, abort */
		if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
		{			/* Previous packet again? */
		    spack('Y',num,6,packet); /* Yes, re-ACK it */
		    numtry = 0;		/* Reset try counter */
		    return(state);	/* Don't write out data! */
		};
		bad_packets++;
		notify_hook();
		return('A');		/* sorry, wrong number */
	    }
	    putmsgc(packet,len);	/* show the user */
	    spack('Y',n,0,0);		/* Acknowledge the packet */
	    oldtry = numtry;		/* Reset the try counters */
	    numtry = 0;			/* ... */
	    n = (n+1)%64;		/* Bump packet number, mod 64 */
	    return('D');		/* Remain in data state */

	case 'F':			/* Got a File Header */
	    if (oldtry++ > maxtry)
		return('A');		/* If too many tries, "abort" */
	    if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
	    {				/* It was the previous one */
		spack('Y',num,0,0);	/* ACK it again */
		numtry = 0;		/* Reset try counter */
		return(state);		/* Stay in Data state */
	    }
	    else return('A');		/* Not previous packet, "abort" */

	case 'Z':			/* End-Of-File */
	    if (num != n) return('A');	/* Must have right packet number */
	    spack('Y',n,0,0);		/* OK, ACK it. */
	    n = (n+1)%64;		/* Bump packet number */
	    return('F');		/* Go back to Receive File state */

	case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return('A');		/* abort */

	case FALSE:			/* Didn't get packet */
	    spack('N',n,0,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:     return('A');	/* Some other packet, "abort" */
    }
}

/*
 *  s p a c k
 *
 *  Send a Packet
 */

spack(type,num,len,data)
char type, *data;
int num, len;
{
    int i;				/* Character loop counter */
    char chksum, buffer[100];		/* Checksum, packet buffer */
    register char *bufp;		/* Buffer pointer */

    flushinput ();			/* Flush pending input */

    if (debug)				/* Display outgoing packet */
    {
	if (data != NULL)
	    data[len] = '\0';		/* Null-terminate data to print it */
	printf("  spack type: %c\n",type);
	printf("	 num:  %d\n",num);
	printf("	 len:  %d\n",len);
	if (data != NULL)
	    printf("	    data: \"%s\"\n",data);
    }
  
    bufp = buffer;			/* Set up buffer pointer */
    for (i=1; i<=pad; i++) write_modem(&padchar,1); /* Issue any padding */

    *bufp++ = SOH;			/* Packet marker, ASCII 1 (SOH) */
    *bufp++ = tochar(len+3);		/* Send the character count */
    chksum  = tochar(len+3);		/* Initialize the checksum */
    *bufp++ = tochar(num);		/* Packet number */
    chksum += tochar(num);		/* Update checksum */
    *bufp++ = type;			/* Packet type */
    chksum += type;			/* Update checksum */

    for (i=0; i<len; i++)		/* Loop for all data characters */
    {
	*bufp++ = data[i];		/* Get a character */
	chksum += data[i];		/* Update checksum */
    }
    chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
    *bufp++ = tochar(chksum);		/* Put it in the packet */
    *bufp++ = eol;			/* Extra-packet line terminator */
    write_modem(buffer,bufp-buffer);	/* Send the packet */
    if (type != 'Y' && type != 'N')
	packets_sent++;			/* Count all but ACKs and NAKs */
    notify_hook ();			/* Give a chance to update display */
}

/*
 *  r p a c k
 *
 *  Read a Packet
 */

rpack(len,num,data)
int *len, *num;				/* Packet length, number */
char *data;				/* Packet data */
{
    int i, done;			/* Data character number, loop exit */
    char t,				/* Current input character */
	type,				/* Packet type */
	cchksum,			/* Our (computed) checksum */
	rchksum;			/* Checksum received from other host */

#if UCB4X				/* TOPS-20 can't handle timeouts... */
    if (setjmp(env)) return FALSE;	/* Timed out, fail */
    signal(SIGALRM,clkint);		/* Setup the timeout */
    if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
    alarm(timint);
#endif /* UCB4X */

#if RAINBOW
    if (setjmp(env)) return FALSE;	/* Timed out, fail */
    if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
    alarm(timint);			/* Setup timeout */
#endif

    while (t != SOH)			/* Wait for packet header */
	t = read_modem () & 0177;	/* Handle parity */

    done = FALSE;			/* Got SOH, init loop */
    while (!done)			/* Loop to get a packet */
    {
	t = read_modem ();		/* Get a character */
	if ((!image) || quote_8bit)
	    t &= 0177;			/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	cchksum = t;			/* Start the checksum */
	*len = unchar(t)-3;		/* Character count */

	t = read_modem ();		/* Get a character */
	if ((!image) || quote_8bit)
	    t &= 0177;			/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	cchksum = cchksum + t;		/* Update checksum */
	*num = unchar(t);		/* Packet number */

	t = read_modem ();		/* Get a character */
	if ((!image) || quote_8bit)
	    t &= 0177;			/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	cchksum = cchksum + t;		/* Update checksum */
	type = t;			/* Packet type */

	for (i=0; i<*len; i++)		/* The data itself, if any */
	{				/* Loop for character count */
	    t = read_modem ();		/* Get character */
	    if ((!image) || quote_8bit)
		t &= 0177;		/* Handle parity */
	    if (t == SOH) continue;	/* Resynch if SOH */
	    cchksum = cchksum + t;	/* Update checksum */
	    data[i] = t;		/* Put it in the data buffer */
	}
	data[*len] = 0;			/* Mark the end of the data */

	t = read_modem ();		/* Get last character (checksum) */
	rchksum = unchar(t);		/* Convert to numeric */
	t = read_modem ();		/* get EOL character and toss it */
	if ((!image) || quote_8bit)
	    t &= 0177;			/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	done = TRUE;			/* Got checksum, done */
    }

#if (UCB4X | RAINBOW)
    alarm(0);				/* Disable the timer interrupt */
#endif

    if (debug)				/* Display incoming packet */
    {
	if (data != NULL)
	    data[*len] = '\0';		/* Null-terminate data to print it */
	printf("  rpack type: %c\n",type);
	printf("	 num:  %d\n",*num);
	printf("	 len:  %d\n",*len);
	if (data != NULL)
	    printf("	    data: \"%s\"\n",data);
    }
					/* Fold in bits 7,8 to compute */
    cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */

    flushinput ();			/* Flush any padding, etc. */

    if (type != 'Y' && type != 'N')
	packets_received++;		/* count all but ACKs and NAKs */
    notify_hook ();			/* maybe update screen */
    if (cchksum != rchksum)
	{
	bad_packets++;
	notify_hook ();
	return(FALSE);
	};

    return(type);			/* All OK, return packet type */
}


/*
 *  b u f i l l
 *
 *  Get a bufferful of data from the file that's being sent.
 *  Only control-quoting is done; 8-bit & repeat count prefixes are
 *  not handled.
 */

bufill(buffer)
char buffer[];				/* Buffer */
{
    int i,				/* Loop index */
	t,				/* Char read from file */
	count;				/* byte count */
    char t7;				/* 7-bit version of above */

    i = 0;				/* Init data buffer pointer */
    count = 0;
    while((t = getc(fp)) != EOF)	/* Get the next character */
    {
	count++;
	t7 = t & 0177;			/* Get low order 7 bits */

	if (quote_8bit && t == quote_8bit_char)
	    buffer[i++] = quote;
	if (quote_8bit && (t & 0200))
	    {
	    buffer[i++] = quote_8bit_char;
	    t = t7;
	    };
	if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
	{				    /* special handling? */
	    if (t=='\n' && !image)
	    {				/* Do LF->CRLF mapping if !image */
		buffer[i++] = quote;
		buffer[i++] = ctl('\r');
	    }
	    buffer[i++] = quote;	/* Quote the character */
	    if (t7 != quote)
	    {
		t = ctl(t);		/* and uncontrolify */
		t7 = ctl(t7);
	    }
	}
	if (image)
	    buffer[i++] = t;		/* Deposit the character itself */
	else
	    buffer[i++] = t7;

	if (i >= spsiz-8)		/* Check length */
	    {
	    bytes_xferred += (long) count;
	    return(i);
	    };
    }
    bytes_xferred += (long) count;
    if (i==0) return(EOF);		/* Wind up here only on EOF */
    return(i);				/* Handle partial buffer */
}


/*
 *	b u f e m p
 *
 *  Put data from an incoming packet into a file.
 */

bufemp(buffer,len)
char  buffer[];				/* Buffer */
int   len;				/* Length */
{
    int i,
	count;				/* Counters */
    unsigned char t;			/* Character holder */
    int q8bit;

    count = 0;
    for (i=0; i<len; i++)		/* Loop thru the data field */
    {
	q8bit = FALSE;
	t = buffer[i];			/* Get character */
	if (quote_8bit && t == quote_8bit_char)
	    {
	    q8bit = TRUE;
	    t = buffer[++i];
	    };
	if (t == quote)			/* Control quote? */
	    {				/* Yes */
	    t = buffer[++i];		/* Get the quoted character */
	    if ((t & 0177) != quote	/* special? */
		&& (! (quote_8bit && t == quote_8bit_char)))
		t = ctl(t);		/* No, uncontrollify it */
	    };
	if (t==CR && !image)		/* Don't pass CR if in image mode */
	    continue;
	if (q8bit)
	    t |= 0200;
	count++;
	if (fputc(t,fp) == EOF)
	    {
	    error("File write failed: c='%x', fp='%x'", t, fp);
	    bytes_xferred += (long) count;
	    return(FALSE);
	    };
    }
bytes_xferred += (long) count;
return(TRUE);
}


/*
 *  g n x t f l
 *
 *  Get next file in a file group
 */

gnxtfl()
{
    if (--filecount <= 0) return FALSE;
    if (debug) printf("	  gnxtfl: filelist = \"%s\"\n",*filelist);
    filnam = *(filelist++);
    return TRUE;			/* else succeed */
}


/*
 *  s p a r
 *
 *  Fill the data array with my send-init parameters
 *
 */

spar(data)
char data[];
{
    data[0] = tochar(MAXPACKSIZ);	/* Biggest packet I can receive */
    data[1] = tochar(MYTIME);		/* When I want to be timed out */
    data[2] = tochar(MYPAD);		/* How much padding I need */
    data[3] = ctl(MYPCHAR);		/* Padding character I want */
    data[4] = tochar(MYEOL);		/* End-Of-Line character I want */
    data[5] = MYQUOTE;			/* Control-Quote character I send */
    if (quote_8bit)
	data[6] = MY8BITQUOTE;		/* 8th-bit quote character */
    else
	data[6] = 'N';			/* Won't do 8th-bit quoting */
}


/*  r p a r
 *
 *  Get the other host's send-init parameters
 *
 */

rpar(data,len)
char data[];
int len;
{
int i;
    spsiz = unchar(data[0]);		/* Maximum send packet size */
    if (spsiz > MAXPACKSIZ)
	{
	printmsg ("Truncating remote's MAXPACKSIZ from %d\n", spsiz);
	spsiz = MAXPACKSIZ;
	};
    if ((i = unchar(data[1])) > timint)
	{
	printmsg("Timeout increased to %d seconds at remote's request", i);
	timint = i;
	};
    pad = unchar(data[2]);		/* Number of pads to send */
    padchar = ctl(data[3]);		/* Padding character to send */
    eol = unchar(data[4]);		/* EOL character I must send */
    quote = data[5];			/* Incoming data quote character */
    if (len >= 7)			/* If other side mentioned 8th-bit */
	switch (data[6])
	    {
	    case 'N':
		if (quote_8bit)
		    printmsg("Remote refused 8th-bit quoting");
		quote_8bit = FALSE;
		break;
	    case 'Y':
		break;
	    default:
		if ((data[6] < 33 || data[6] > 126)
		    || (data[6] > 62 && data[6] < 96))
		    {
		    printmsg("Remote requested invalid 8th-bit quote char");
		    quote_8bit = FALSE;
		    break;
		    };
		quote_8bit = TRUE;
		quote_8bit_char = data[6];
	    };	    
}

/*
 *  p u t m s g c
 *
 *  Print counted message
 */

putmsgc(msg,len)
char *msg;
int len;
{
char
    buf[132];

if (len >= 132)
    {
    callers_printmsg("putmsgc - message too long");
    return;
    };
strncpy(buf,msg,len);
buf[len] = 0;
callers_printmsg(buf);
}

/*
 *  p r i n t m s g
 *
 *  Print message on standard output if not remote.
 */

/*VARARGS1*/
printmsg(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
char
    msg[132];

    if (!remote)
    {
	sprintf(msg,fmt,a1,a2,a3,a4,a5);
	callers_printmsg(msg);
    }
}

/*
 *  e r r o r
 *
 *  Print error message.
 *
 *  If local, print error message with printmsg.
 *  If remote, send an error packet with the message.
 */

/*VARARGS1*/
error(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
    char msg[80];
    int len;

    if (remote)
    {
	sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
	len = strlen(msg);
	spack('E',n,len,msg);		/* Send the error packet */
    }
    else
	printmsg(fmt, a1, a2, a3, a4, a5);

    return;
}

/*
 *  p r e r r p k t
 *
 *  Print contents of error packet received from remote host.
 */
prerrpkt(msg)
char *msg;
{
char
    emsg[132];

    sprintf(emsg,"Aborted by remote: %s",msg);
    callers_printmsg(emsg);
    return;
}
>>>>>>>> KERMIT.H
/* Symbol Definitions */

#define IBM_UTS 0
#define UCB4X 0
#define RAINBOW 1
#define MSDOS 1

#if MSDOS
#define NAMELEN 8		/* MS-DOS filenames are 8.3 */
#define EXTLEN 3
#endif

#define MAXPACKSIZ  94	    /* Maximum packet size */
#define SOH	    1	    /* Start of header */
#define CR	    13	    /* ASCII Carriage Return */
#define SP	    32	    /* ASCII space */
#define DEL	    127	    /* Delete (rubout) */

#define MAXTRY	    5	    /* Times to retry a packet */
#define MYQUOTE	    '#'	    /* Quote character I will use */
#define MY8BITQUOTE '&'	    /* 8th-bit quote character */
#define MYPAD	    0	    /* Number of padding characters I will need */
#define MYPCHAR	    0	    /* Padding character I need (NULL) */

#if IBM_UTS
#define MYEOL	    '\r'    /* End-Of-Line character for UTS systems */
#else
#define MYEOL	    '\n'    /* End-Of-Line character I need */
#endif

#define MYTIME	    10	    /* Seconds after which I should be timed out */
#define MAXTIM	    60	    /* Maximum timeout interval */
#define MINTIM	    2	    /* Minumum timeout interval */
#define DEFTIM	    10	    /* Default timeout (before send init) */

#define TRUE	    -1	    /* Boolean constants */
#define FALSE	    0


/* Macro Definitions */

/*
 * tochar: converts a control character to a printable one by adding a space.
 *
 * unchar: undoes tochar.
 *
 * ctl:	   converts between control characters and printable characters by
 *	   toggling the control bit (ie. ^A becomes A and A becomes ^A).
 */
#define tochar(ch)  ((ch) + ' ')
#define unchar(ch)  ((ch) - ' ')
#define ctl(ch)	    ((ch) ^ 64 )
>>>>>>>> LCTERM.C
/*
 * \usr\kermit\lcterm.c
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This program may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This program, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *	File transfer and terminal emulator facility.
 *	Supports KERMIT and XMODEM protocols for file transfer
 *	Currently runs only on DEC Rainbow 100 running MS-DOS
 *	version 2.05 or later.  CAVEAT:  this program has not been
 *	tested well on a 100+.  In particular, some of the video
 *	routines access firmware storage which may differ on a 100+.
 *	You have been warned.
 */

/* Revision history
 *
 *	2.14	Convert to CI-C86 version 2.10.
 *	2.13	Improve message windowing in KERMIT/XMODEM screens;  fix
 *		"doubled screen" bug;  change KERMIT RECEIVE to GET.
 *	2.12	Update help screen.
 *	2.11	Restore user's break state during push.
 *	2.10	Make MAIN SCREEN synonym for ADDTNL OPTIONS;  make
 *		RETURN synonym for DO in menus;  ignore shift bit in menus
 *	2.09	Clean up scripts;  change #wnn to #w'nn';  add script
 *		commands to set KERMIT parameters
 *	2.08	Add support for eighth-bit quoting in KERMIT xfers.
 *	2.07	Add keypad and cursor key support (this code will also
 *		eventually handle user-defined macro keys)
 *	2.06	Move more Rainbow-specific stuff to separate modules
 *	2.05	Add XMODEM commands to scripts
 *	2.04	Timestamp printmsg output
 *	2.03	Reorganize connect code for clarity and speed
 *	2.02	Make log files append, not overwrite
 *	2.01	Add scripts
 *	1.13	Close log file when EXITing
 *	1.12	Changes to work with split up vid.c
 *	1.11	Set scroll region to whole screen when pushing
 *	1.10	Better HELP key response in terminal mode
 *	1.09	Add autopush on INTERRUPT key
 *	1.08	Fix bug in finding COMSPEC env var when it's not first
 *	1.07	Fix screen bug induced by PUSH, scroll, POP, TERM
 *	1.06	Fix bug in memory release at startup which broke 128K systems
 *	1.05	Slight speedup of terminal output
 */

#define LCTERM_VERSION "LC-Term version 2.14"

#include <stdio.h>
#include "video.h"
#include "rainbow.h"
#include "bytes.h"
#include "buf.h"

#define CONFIRM_BOX_LOC 4, 45

/*
 * Rainbow-specific hack -- keypad mode is determined by peeking at
 * firmware bit, rather than by parsing the escape sequences ourselves
 */

#define keypad_mode (peek (ROM_KEYFLG, SCREEN_BUF_SEG) & 0x1)

#define ABORT(s) { puts (s); leave (); }
#define DOS_ERROR(n) { fputs (dos_error (n), stderr); leave (); }
#define DOS(n) { srv.ax = n << 8;\
		 status = sysint21 (&srv, &rrv);\
		 if (status & 1) DOS_ERROR (rrv.ax); };

/* script escape character */

#define SCRIPT_ESC '#'

/* These are really FORWARD declarations */

extern int
    capture_file (),
    close_capture (),
    close_script (),
    connect (),
    debug_mode (),
    hangup (),
    kermit_finish (),
    kermit_logout (),
    kermit_get (),
    kermit_remote_dir (),
    kermit_send (),
    kmenu (),
    kpadding (),
    kparams_menu (),
    kretry (),
    ktimeout (),
    leave (),
    lower_dtr (),
    message (),
    modem_menu (),
    modem_send (),
    modem_receive (),
    mpush (),
    normal_mode (),
    open_script (),
    params_menu (),
    raise_dtr (),
    raw_menu (),
    send_file (),
    toggle_binary (),
    toggle_8bit_quote ();

/* Here are the "real" external declarations */

extern int
    auxres (),
    buf_emptyp (),
    buf_fullp (),
    create_buf (),
    destroy_buf (),
    exit (),
    kerini (),
    put_buf (),
    readfile (),
    sendfile (),
    recsw (),
    sendsw (),
    un_get_buf ();

extern struct saved_video
    *pop_up_boxed_text ();

extern char
    get_buf ();

extern char
    *filnam;

extern int
    debug,
    pad,
    timint,
    maxtry,
    binary,
    quote_8bit,
    packets_sent,
    packets_received,
    bad_packets,
    naked_packets;

extern long
    bytes_xferred;

extern int
    mdm_packets_sent,
    mdm_packets_received,
    mdm_bad_packets,
    mdm_naked_packets;

extern long
    mdm_bytes_xferred;

static int
    timer,
    status,
    cursor_is_off,
    saved_screen,
    saved_row,
    saved_col,
    printmsg_row,
    hours,
    minutes,
    seconds,
    prev_packets_sent,
    prev_packets_received,
    prev_bad_packets,
    prev_naked_packets,
    esc_seq_state;

static long
    prev_bytes_xferred;

#define PROMPT_BUF_SIZE 80

static unsigned char
    screen_char_buffer[SCREEN_BUF_SIZE],
    screen_attr_buffer[SCREEN_BUF_SIZE],
    screen_cursor_buffer[ROM_KEYS - ROM_SAVCR],
    screen_lat_buffer[ROM_Z80TAS - ROM_LATOFU],
    prompt[PROMPT_BUF_SIZE],
    *prompt_ptr,
    *key_string,
    comspec[132];

static struct saved_video
    *kermit_box_save;

static jmp_buf
    escape_jmp;

static struct regval {int ax, bx, cx, dx, si, di, ds, es;};
static struct segval {int scs, sss, sds, ses;};

static struct regval srv, rrv;
static struct segval seg;

static long
    prev_mdm_bytes_xferred;

static int
    prev_mdm_packets_sent,
    prev_mdm_packets_received,
    prev_mdm_bad_packets,
    prev_mdm_naked_packets;

static struct menu_item
    m_main_menu[] =
    {
	{"Enter terminal emulation", &connect},
	{"KERMIT file transfer menu", &kmenu},
	{"XMODEM file transfer menu", &modem_menu},
	{"Raw text file transfer menu", &raw_menu},
	{"Hang up the phone", &hangup},
	{"Set parameters", &params_menu},
	{"Push to new shell", &mpush},
	{"Information", &message},
	{"Exit LC-Term", &leave},
	{"", 0}
    };

static struct menu_item
    m_kermit_menu[] =
    {
	{"Send file to remote", &kermit_send},
	{"Get file from remote", &kermit_get},
	{"Remote directory", &kermit_remote_dir},
	{"Stop remote server", &kermit_finish},
	{"Log out remote server", &kermit_logout},
	{"Set parameters", &kparams_menu},
	{"Done", DONE},
	{"", 0}
    };

static struct menu_item
    m_raw_xfer_menu[] =
    {
	{"Log terminal output to a file", &capture_file},
	{"Close log file", &close_capture},
	{"Send file to remote system", &send_file},
	{"Process script", &open_script},
	{"Close script", &close_script},
	{"Done", DONE},
	{"", 0}
    };

static struct menu_item
    m_params_menu[] =
    {
	{"Raise DTR", &raise_dtr},
	{"Lower DTR", &lower_dtr},
	{"Debug mode", &debug_mode},
	{"Normal mode", &normal_mode},
	{"Reset comm port", &auxres},
	{"Done", DONE},
	{"", 0}
    };

static struct menu_item
    m_kparams_menu[] =
    {
	{"Toggle 8th-bit quoting", &toggle_8bit_quote},
	{"Set retry limit", &kretry},
	{"Set padding value", &kpadding},
	{"Toggle Binary/Text mode", &toggle_binary},
	{"Set timeout value", &ktimeout},
	{"Done", DONE},
	{"", 0}
    };

static struct menu_item
    m_modem_menu[] =
    {
	{"Send file", &modem_send},
	{"Receive file", &modem_receive},
	{"Done", DONE},
	{"", 0}
    };

static FILE
    *script,
    *capture,
    *stuff;

static int
    last_second,
    delay_counter,
    old_break_state;

static struct ring_buf
    *key_to_comm_buf;

#define BUFSIZ 132

#define KEY_TAB_LENGTH 22

struct
    {
    int key;
    unsigned char *string;
    unsigned char *keypad_mode_string;
    } key_tab[KEY_TAB_LENGTH] =
    {
	{KEY_KEYPAD_PF1, "\033OP", 0},
	{KEY_KEYPAD_PF2, "\033OQ", 0},
	{KEY_KEYPAD_PF3, "\033OR", 0},
	{KEY_KEYPAD_PF4, "\033OS", 0},
	{KEY_KEYPAD_0, "0", "\033Op"},
	{KEY_KEYPAD_1, "1", "\033Oq"},
	{KEY_KEYPAD_2, "2", "\033Or"},
	{KEY_KEYPAD_3, "3", "\033Os"},
	{KEY_KEYPAD_4, "4", "\033Ot"},
	{KEY_KEYPAD_5, "5", "\033Ou"},
	{KEY_KEYPAD_6, "6", "\033Ov"},
	{KEY_KEYPAD_7, "7", "\033Ow"},
	{KEY_KEYPAD_8, "8", "\033Ox"},
	{KEY_KEYPAD_9, "9", "\033Oy"},
	{KEY_KEYPAD_DASH, "-", "\033Om"},
	{KEY_KEYPAD_COMMA, ",", "\033Ol"},
	{KEY_KEYPAD_PERIOD, ".", "\033On"},
	{KEY_KEYPAD_ENTER, "\015", "\033OM"},
	{KEY_UP_ARROW, "\033[A", 0},
	{KEY_DOWN_ARROW, "\033[B", 0},
	{KEY_RIGHT_ARROW, "\033[C", 0},
	{KEY_LEFT_ARROW, "\033[D", 0}
    };

/*
 * Main program
 *
 */

main (argc, argv)
int argc;
char **argv;
{
int
    v,
    majorv,
    minorv,
    k;

script = stuff = capture = timer = cursor_is_off = saved_screen = debug = 0;
prompt_ptr = delay_counter = esc_seq_state = key_string = 0;

clear_screen ();	/* Init video states */
/*
 * Check DOS version (modem handler requires 2.05 or later)
 *
 */
v = bdos (0x30);
majorv = v & 0xFF;
minorv = v >> 8;
if ((majorv < 2) || ((majorv == 2) && (minorv < 5)))
    {
    cursor (1, 1);
    puts ("LC-Term requires MS-DOS version 2.05 or later...  sorry.");
    exit ();
    };
kerini ();			/* Init KERMIT.C protocol module */
mdmini ();			/* Init MODEM.C protocol module */
raise_dtr ();
init_aux ();			/* init comm port */
key_to_comm_buf = create_buf (BUFSIZ);
segment_init ();		/* init segment memory, COMSPEC string */

old_break_state = get_break ();
set_break (0);			/* turn off DOS break "feature" */

if (argc <= 1)
    {
    top_menu ();
    return;
    };
if (argv[1][0] == '-')
    switch (argv[1][1])
	{
	char *p, c;
	case 't':			/* straight to terminal mode */
	    connect (); break;
	case 'd':			/* dial a number, then terminal mode */
	    p = argv[1] + 2;
	    put_buf (key_to_comm_buf, 'B' - 0x40);	/* DF03 autodial chr */
	    while (c = *p++)
		put_buf (key_to_comm_buf, c);
	    connect ();
	    break;
	case 's':			/* open script file and enter */
	    p = argv[1] + 2;		/* terminal emulation */
	    if ((script = fopen (p, "r")) != NULL)
		connect ();
	    else
		script = 0;
	    break;
	default:
	    puts ("Usage: LCTERM [-t] [-d<number>]"); exit ();
	};
top_menu ();
}

/*
 * segment_init
 *
 *	Read segment registers for later use
 *
 *   	Scan our environment for the COMSPEC= string and save it away
 *	so we can find the shell (COMMAND.COM) when the user selects
 *	the PUSH function
 *
 */

segment_init ()
{
unsigned int
    envseg;

segread (&seg);				/* read segment registers */
envseg = peek (0x2C, seg.scs - 0x10);	/* get environment segment */
if (! env_fetch ("COMSPEC", envseg, comspec))
    ABORT ("can't find COMSPEC environment string");
}

message ()
{
struct saved_video
    *screen;

screen = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
clear_screen ();
rectangle (1, 1, SCREEN_ROWS - 1, SCREEN_COLUMNS - 1, ATTR_NORMAL);
putline (2, 30, LCTERM_VERSION);
putattr_i (2, 28, strlen (LCTERM_VERSION) + 4, ATTR_BOLD_REVERSE);
putline (4, 14, "A>lcterm [-t] [-sfilespec]");
putattr_i (4, 16, 6, ATTR_UNDERLINE);
putattr_i (4, 24, 2, ATTR_UNDERLINE);
putattr_i (4, 29, 10, ATTR_UNDERLINE);
putline (5, 12, "Options:");
putline (6, 14, "-t             goes directly to terminal emulation");
putline (7, 14, "-sfilespec     open `filespec' and process as a script");

#define X(row,s) putline (row, 8, s)

X (9,  "The menus  should be  self-explanatory.   LC-Term  transfers files");
X (10, "using  either  KERMIT  or XMODEM protocols,  and provides terminal");
X (11, "emulation with optional logging of the session to a file.");

X (13, "Copyright (C) 1984 by Larry Campbell.  This  program may be copied");
X (14, "and disseminated freely for noncommercial purposes, if and only if");
X (15, "this copyright notice is  included  in  all copies.  This program,");
X (16, "and the information contained herein, may not  be  used  for  com-");
X (17, "mercial purposes without my prior written permission.");

#undef X

putline (19,15, "Larry Campbell");
putline (20,15, "73 Concord St.");
putline (21,15, "Maynard, MA 01754");

#define M "Strike any key to proceed"

boxed_text (21, 51, M);
putattr_i (22, 52, strlen (M), ATTR_BLINK);

#undef M

cursor_off ();
WAIT_FOR_ANY_KEY;
cursor_on ();
restore_screen_rectangle (screen);
}

/*
 * push
 *
 *	Invoke a new shell
 */

static struct		/* execute program parameter block */
    {
    int environment;
    int cmdoff;
    int cmdseg;
    int fcb5off;
    int fcb5seg;
    int fcb6off;
    int fcb6seg;
    } params;

static char
    cmdstring[132];

push ()
{
if (cmdstring[0] == 0)		/* if PUSH and not command, clear screen 1st */
    {
    clear_screen ();
    cursor (1, 1);
    pokew (ROM_TOP, SCREEN_BUF_SEG, 0x1801);	/* set normal scroll region */
    puts ("Give EXIT command to return to LCTERM...");
    };
params.environment = peek (0x2c, seg.scs - 0x10); /* inherit our environment */
params.cmdseg = seg.sss;
params.cmdoff = cmdstring;
params.fcb5seg = params.fcb6seg = 0;
params.fcb5off = params.fcb6off = 0;
set_break (old_break_state);			/* restore break state */
status = loadexec (comspec, seg.sds, &params, seg.sds, 0, 0);
/* status = exec (seg.sds, comspec, &params); */
set_break (0);					/* disable break again */
if (status)
    {
    printf ("Loadexec failed: %s\n", dos_error (status));
    await_attention ();
    };
}

mpush ()
{
struct saved_video
    *screen;

screen = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
cmdstring[0] = 0;
cmdstring[1] = 0x0D;
push ();
restore_screen_rectangle (screen);
}

top_menu ()
{
int
    (*handler) (),
    (*parse_menu ()) (),
    code;


parse_menu (m_main_menu, 3, 27, LCTERM_VERSION, MENU_CLEAR_SCREEN + MENU_ACTIVE_MODE);
}

kmenu ()
{
parse_menu (m_kermit_menu, 6, 8, "LC-Term KERMIT", MENU_POST_CLEAR + MENU_ACTIVE_MODE);
}

raw_menu ()
{
parse_menu (m_raw_xfer_menu, 8, 15, "LC-Term Raw Text Transfer",
	    MENU_POST_CLEAR + MENU_ACTIVE_MODE + MENU_ONCE_ONLY);
}

params_menu ()
{
parse_menu (m_params_menu, 10, 20, "LC-Term Parameters", MENU_ACTIVE_MODE);
}

kparams_menu ()
{
int
    (*parse_menu ()) ();

kermit_box_save = save_screen_rectangle (16, 1, 9, 28);
display_kermit_params ();
parse_menu (m_kparams_menu, 13, 30, "LC-Term KERMIT Parameters", MENU_POST_CLEAR + MENU_ACTIVE_MODE);
erase_kermit_params_display ();
}

modem_menu ()
{
int
    (*parse_menu ()) ();

parse_menu (m_modem_menu, 7, 45, "LC-Term XMODEM", MENU_POST_CLEAR + MENU_ACTIVE_MODE);
}

raise_dtr () { outportb (2, 0); }

lower_dtr () { outportb (2, 4); }

hangup () { lower_dtr (); pause (4000); raise_dtr (); }

/*
 * PAUSE
 *
 *	Pause for n milliseconds to allow user to absorb something
 *
 */

pause (n)
int n;
{
long
    k;

for (k = LOOPS_PER_MILLISECOND * (long) n; k > 0; k--);
}

leave ()
{
reset_aux ();
if (capture != 0)
    fclose (capture);
if (stuff != 0)
    fclose (stuff);
if (script != 0)
    fclose (script);
set_break (old_break_state);
cursor (24, 1);
clear_rest_of_line ();
cursor_on ();
exit ();
}

debug_mode () { debug = 1; }

normal_mode () { debug = 0; }

toggle_binary () { binary = ! binary; display_kermit_params (); }

toggle_8bit_quote () { quote_8bit = ! quote_8bit; display_kermit_params (); }

ktimeout () { kval ("Timeout", &timint); }

kpadding () { kval ("Padding", &pad); }

kretry () { kval ("Retry limit", &maxtry); }

kval (name, addr)
char *name;
int *addr;
{
char
    c,
    buf[132];

int
    i;

extern char
    blanks[];

struct saved_video
    *help_box;

cursor_off ();
sprintf (buf, "%s: Use arrow keys, DO when done", name);
help_box = pop_up_boxed_text (22, 29, buf);
putattr_i (23, 30, strlen (buf), ATTR_BLINK);
while (1)
    {
    while ((c = kbdin ()) < 0);
    switch (c)
	{
	case KEY_UP_ARROW:
	    if (++(*addr) > 99) { FEEP;  *addr = 99;  };
	    display_kermit_params ();
	    break;
	case KEY_DOWN_ARROW:
	    if (--(*addr) < 0) {FEEP;  *addr = 0;  };
	    display_kermit_params ();
	    break;
	case KEY_DO:
	    for (i = 22; i <= 24; i++)
		put_line (i, 29, 51, blanks);
	    cursor_on ();
	    restore_screen_rectangle (help_box);
	    return;
	default:
	    FEEP;
	};
    };
}

display_kermit_params ()
{
char
    buf[6];

rectangle (16, 1, 8, 27, ATTR_NORMAL);
horizontal_cut (18, 1, 28, ATTR_NORMAL);
putline (17, 7, "KERMIT Parameters");
putattr_i (17, 7, strlen ("KERMIT Parameters"), ATTR_BOLD);
putline (19, 2, "Eighth-bit quoting:    ");
putline (20, 2, "Retry limit:           ");
putline (21, 2, "No. of padding chars:  ");
putline (22, 2, "File mode:             ");
putline (23, 2, "Timeout (seconds):     ");

if (quote_8bit)
    putline (19, 2+23, "YES");
else
    putline (19, 2+23, " NO");
sprintf (buf, "%2d", maxtry);
putline (20, 2+24, buf);
sprintf (buf, "%2d", pad);
putline (21, 2+24, buf);
if (binary)
    putline (22, 2+20, "BINARY");
else
    putline (22, 2+20, "  TEXT");
sprintf (buf, "%2d", timint);
putline (23, 2+24, buf);
}

erase_kermit_params_display ()
{
int
    i;

putattr_i (17, 7, strlen ("KERMIT Parameters"), ATTR_NORMAL);
restore_screen_rectangle (kermit_box_save);
}

save_screen ()
{
unsigned int
    j,
    k,
    cursorpos;

struct
    {
    int cs;
    int ss;
    int ds;
    int es;
    } regs;

segread (&regs);
cursorpos = peek (ROM_CSRPOS, SCREEN_BUF_SEG);
saved_row = cursorpos >> 8;
saved_col = cursorpos & 0xFF;
disable_cursor ();
movblock (SCREEN_BUF_OFFSET,		/* save characters */
	  SCREEN_BUF_SEG,
	  screen_char_buffer,
	  regs.ds,
	  SCREEN_BUF_SIZE);
movblock (SCREEN_ATTR_OFFSET,		/* save attributes */
	  SCREEN_BUF_SEG,
	  screen_attr_buffer,
	  regs.ds,
	  SCREEN_BUF_SIZE);
movblock (ROM_SAVCR,			/* save cursor state */
	  SCREEN_BUF_SEG,
	  screen_cursor_buffer,
	  regs.ds,
	  ROM_KEYS - ROM_SAVCR);
movblock (ROM_LATOFU,
	  SCREEN_BUF_SEG,
	  screen_lat_buffer,
	  regs.ds,
	  ROM_Z80TAS - ROM_LATOFU);
enable_cursor ();
saved_screen = 1;
}

restore_screen ()
{
struct
    {
    int cs;
    int ss;
    int ds;
    int es;
    } regs;

segread (&regs);
disable_cursor ();
movblock (screen_char_buffer,
	  regs.ds,
	  SCREEN_BUF_OFFSET,
	  SCREEN_BUF_SEG,
	  SCREEN_BUF_SIZE);
movblock (screen_attr_buffer,
	  regs.ds,
	  SCREEN_ATTR_OFFSET,
	  SCREEN_BUF_SEG,
	  SCREEN_BUF_SIZE);
movblock (screen_cursor_buffer,
	  regs.ds,
	  ROM_SAVCR,
	  SCREEN_BUF_SEG,
	  ROM_KEYS - ROM_SAVCR);
movblock (screen_lat_buffer,
	  regs.ds,
	  ROM_LATOFU,
	  SCREEN_BUF_SEG,
	  ROM_Z80TAS - ROM_LATOFU);
enable_cursor ();
cursor (saved_row, saved_col);
saved_screen = 0;
}

/*
 * connect
 *
 *	Terminal emulation.  MAIN SCREEN key gets you the top menu.
 *	INTERRUPT key pushes to new shell.  EXIT key just exits.
 */

connect ()
{
int
    t,
    com_char;

struct saved_video
    *save;

char
    c;

save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
if (! saved_screen)
    {
    clear_screen ();
    putline (1, 1, "Connected to communications port, press MAIN SCREEN for menu...");
    cursor (2, 1);
    }
else
    restore_screen ();
if (setjmp (escape_jmp))	/* MAIN SCREEN key longjmps thru here */
    {
    save_screen ();
    restore_screen_rectangle (save);
    return;
    };
while (1)
    {
    if (delay_counter > 0)		/* check for timer running */
	if ((t = tod_seconds ()) != last_second)
	    {
	    last_second = t;
	    delay_counter--;
	    };
    keyinput ();
    if ((! buf_emptyp (key_to_comm_buf)) && aux_hungry ())
        {			/* buffer nonempty and AUX hungry */
        switch (c = get_buf (key_to_comm_buf))
            {
            case '\n': c = 015;
            default:
                if (putaux (c))
                    break;
                else
                    un_get_buf (key_to_comm_buf, c);
            };
        };
    keyinput ();
    com_char = getaux ();
    if (com_char > 0)
	process_aux_input (com_char);
    };
}

/*
 * process_aux_input
 *
 *	Handle incoming character.  This is where inquiries and state
 *	changes caused by incoming escape sequences get handled.
 */

process_aux_input (com_char)
int com_char;
{
com_char = com_char & 0x7F;	/* strip 8th bit */
if (prompt_ptr != 0)		/* waiting for prompt? */
    if (*prompt_ptr == com_char)	/* yes, is this it? */
        {
        if (*(++prompt_ptr) == 0)	/* match done? */
            prompt_ptr = 0;
        }
    else
        prompt_ptr = prompt;	/* match failed, reset ptr to start */
switch (esc_seq_state)		/* This grunge handles the terminal */
    {				/* type inquiry (ESC [ c) -- the */
    case 0:				/* only inquiry we support */
        if (com_char == 033)
            esc_seq_state = 1;
        else
            put_to_screen (com_char);
        break;
    case 1:
        switch (com_char)
            {
            case '[':
                esc_seq_state = 2; break;
            case 'Z':
                return_terminal_id (); esc_seq_state = 0; break;
            default:
                esc_seq_state = 0;
                put_to_screen (033);
                put_to_screen (com_char);
            };
        break;
    case 2:
        switch (com_char)
            {
            case 'c':
                return_terminal_id (); esc_seq_state = 0; break;
            case '0':
                esc_seq_state = 3; break;
            default:
                esc_seq_state = 0;
                put_to_screen (033);
                put_to_screen ('[');
                put_to_screen (com_char);
            };
        break;
    case 3:
        if (com_char == 'c')
            return_terminal_id ();
        else
            {
            put_to_screen (033);
            put_to_screen ('[');
            put_to_screen ('0');
            put_to_screen (com_char);
            };
        esc_seq_state = 0;
        break;
    default:
        put_to_screen (com_char);
    };
}

/*
 * put_to_screen (chr)
 *
 *	Write a character to the screen, and log file if one is open
 */

put_to_screen (c)
int c;
{
putcon ((char) c);
if (capture != 0 && c != 015 && c != ('Z' - 0100))
    fputc (c, capture);
}

/*
 * return_terminal_id
 *
 *	Return escape sequence identifying me as a VT102
 */

return_terminal_id ()
{
put_buf (key_to_comm_buf, 033);
put_buf (key_to_comm_buf, '[');
put_buf (key_to_comm_buf, '?');
put_buf (key_to_comm_buf, '6');
put_buf (key_to_comm_buf, 'c');
}

/*
 * keyinput
 *
 *	Get keyboard input if any and stuff into buffer.
 *
 *	Calls leave() if EXIT key hit.
 *	Pushes to new shell if INTERRUPT hit.
 *	Does longjmp thru escape_jmp if ADDTNL OPTIONS hit.
 */

keyinput ()
{
int
    key_char,
    k;

if (key_string)				/* if fn key string pending, */
    {
    if ((key_char = *key_string++) == 0)/* get next char */
	{
	key_string = 0;			/* if string exhausted, zero pointer */
	key_char = getkey ();		/* and try keyboard */
	};
    }
else
    key_char = getkey ();		/* if no fn key string, try keyboard */
if (key_char == -2)			/* if level 2 sequence in progress, */
    key_char = bdos (7, 0) & 0x77;	/* get char from DOS */
if (key_char > 0)			/* if char available */
    {
    if (key_char & 0x100)		/* see if function key */
	switch (key_char &= 0xFF)	/* ignore shifty bits */
            {
            case KEY_EXIT:
		leave ();
	    case KEY_MAIN_SCREEN:
            case KEY_ADDTNL_OPTIONS:
		longjmp (escape_jmp, 1);
	    case KEY_INTERRUPT:
		save_screen ();
		push ();
		restore_screen ();
		return;
            case KEY_HELP:
		do_help ();
		break;
            default:
		for (k = 0; k < KEY_TAB_LENGTH; k++)
		    if (key_char == key_tab[k].key)
			{
			if (keypad_mode && key_tab[k].keypad_mode_string)
			    key_string = key_tab[k].keypad_mode_string;
			else
			    key_string = key_tab[k].string;
			return;
			};
		FEEP;			/* no mapping for this key */
            }
    else
	{				/* non-function key */
	if (! put_buf (key_to_comm_buf, key_char))
	    {
	    FEEP;
	    boxed_text (2, 50, "Keyboard buffer full");
	    };
	return;
	};
    };
/*
 * If buffer hungry, check for stuff or script file
 */

if (buf_fullp (key_to_comm_buf))
    return;

if (script != 0)
    return (do_script ());

if (stuff != 0)
    {
    if ((key_char = fgetc (stuff)) == EOF)
        { fclose (stuff);  stuff = 0;  }
    else
        put_buf (key_to_comm_buf, key_char);
    return;
    };
}

do_script ()
{
char
    c,
    key_char,
    buf[40],
    *dp;

if (delay_counter > 0)		/* quit if timer running */
    return;
if (prompt_ptr)			/* quit if waiting for a prompt */
    return;
if ((key_char = fgetc (script)) == EOF)
    {
    fclose (script);
    script = 0;
    delay_counter = 0;
    }
else
    {
    if (key_char == SCRIPT_ESC)		/* script escape char? */
        switch (key_char = fgetc (script))
            {
            case EOF:
                fclose (script);
                script = 0;
		delay_counter = 0;
                break;
            case SCRIPT_ESC:
                put_buf (key_to_comm_buf, key_char);
                break;
	    case 'c':
		if ((key_char = fgetc (script)) == EOF) break;
		cmdstring[0] = 2;		/* length of command */
		cmdstring[1] = '/';
		cmdstring[2] = 'C';
		dp = cmdstring + 3;		/* shell command string */
		while ((c = fgetc (script)) != key_char)
		    {
		    *dp++ = c;
		    if (++cmdstring[0] > 131) break;
		    };
		*dp++ = 0x0D;			/* magic cookie for DOS */
		*dp = 0;			/* another one */
		push ();			/* go get 'em */
		break;
	    case 'e':
		leave ();
	    case 'h':
		hangup ();
		break;
	    case 'k':			/* KERMIT commands */
		kermit_script ();
		break;
	    case 'm':			/* XMODEM commands */
		modem_script ();
		break;
	    case 'p':
		get_quoted_string (prompt, PROMPT_BUF_SIZE, script);
		prompt_ptr = prompt;
		break;
	    case 'l':			/* log file functions */
		switch (fgetc (script))
		    {
		    case 'o':		/* open a log file */
			get_quoted_string (buf, 40, script);
			if (capture) fclose (capture);
			capture = fopen (buf, "a");
			break;
		    case 'c':		/* close log file */
			if (capture) fclose (capture);
			capture = 0;
			break;
		    };
		break;
            case 'w':
		get_quoted_string (buf, 40, script);
		delay_counter = atoi (buf) + 1;
		if (delay_counter < 0) delay_counter = 0;
		last_second = tod_seconds ();	/* init timer */
		break;
            }
    else				/* else just stuff it */
        put_buf (key_to_comm_buf, key_char);
    };
}

/*
 * kermit_script
 *
 *	Called by script handler to process 'k' commands:
 *
 *		kc'nn'		set retry count to nn
 *		kr'file'	receive file
 *		ks'file'	send file
 *		kt'nn'		set timeout to nn seconds
 *		kx		stop remote server
 *		k8+		turn on 8th-bit quoting
 *		k8-		turn it off
 */

kermit_script ()
{
struct saved_video
    *save;

char
    c,
    d,
    *dp,
    *msg,
    buf[40];

switch (fgetc (script))
    {
    case 'c':
	get_quoted_string (buf, 40, script);
	maxtry = atoi (buf);
	break;
    case 'g':
    case 'r':
	get_quoted_string (buf, 40, script);
        filnam = buf;
        save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
        kermit_screen ();
        if (recsw ())
	    boxed_text (CONFIRM_BOX_LOC, "File received OK");
        else
	    {
	    boxed_text (CONFIRM_BOX_LOC, "Receive failed, script aborted");
	    fclose (script);
	    script = 0;
            await_attention ();
	    };
        restore_screen_rectangle (save);
        cursor_on ();
	break;
    case 's':
	get_quoted_string (buf, 40, script);
        filnam = buf;
        save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
        kermit_screen ();
        if (sendsw ())
            boxed_text (CONFIRM_BOX_LOC, "File sent OK");
        else
	    {
            boxed_text (CONFIRM_BOX_LOC, "Send failed, script aborted");
	    fclose (script);
	    script = 0;
	    await_attention ();
	    };
        restore_screen_rectangle (save);
        cursor_on ();
	break;
    case 't':
	get_quoted_string (buf, 40, script);
	timint = atoi (buf);
	break;
    case 'x':
        save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
        kermit_screen ();
        if (sendcmdsw ('G', "F"))
            boxed_text (CONFIRM_BOX_LOC, "Server stopped OK");
        else
	    {
            boxed_text (CONFIRM_BOX_LOC, "Stop failed, script aborted");
	    fclose (script);
	    script = 0;
            await_attention ();
	    };
        restore_screen_rectangle (save);
        cursor_on ();
	break;
    case '8':
	switch (fgetc (script))
	    {
	    case '+': quote_8bit = 1; break;
	    case '-': quote_8bit = 0; break;
	    };
	break;
    };
}

/*
 * get_quoted_string (buf, bufn, fd)
 *
 *	Get a quoted string from a file, storing into buffer
 */

get_quoted_string (dp, bufn, fd)
char *dp;
int bufn;
FILE *fd;
{
char
    c,
    d;

d = fgetc (fd);			/* get delimiter character */
while ((c = fgetc (fd)) != d && bufn-- >= 0)
    *dp++ = c;			/* copy filespec */
*dp = 0;
}

/*
 * modem_script
 *
 *	Called by script handler to process 'm' commands:
 *
 *		ms'file'	send file
 *		mr'file'	receive file
 */

modem_script ()
{
struct saved_video
    *save;

char
    c,
    d,
    *dp,
    *msg,
    buf[40];

switch (fgetc (script))
    {
    case 'r':
	d = fgetc (script);		/* get delimiter character */
	dp = buf;
	while ((c = fgetc (script)) != d)
	    *dp++ = c;			/* copy filespec */
	*dp = 0;
        save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
        modem_screen ();
	save_aux_params ();
	disable_auto_xon_xof ();
        if (readfile (buf))
	    boxed_text (CONFIRM_BOX_LOC, "File received OK");
        else
	    {
	    boxed_text (CONFIRM_BOX_LOC, "Receive failed, script aborted");
	    fclose (script);
	    script = 0;
            await_attention ();
	    };
	restore_aux_params ();
        restore_screen_rectangle (save);
        cursor_on ();
	break;
    case 's':
	d = fgetc (script);		/* get delimiter character */
	dp = buf;
	while ((c = fgetc (script)) != d)
	    *dp++ = c;			/* copy filespec */
	*dp = 0;
        save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
        modem_screen ();
	save_aux_params ();
	disable_auto_xon_xof ();
        if (sendfile (buf))
            boxed_text (CONFIRM_BOX_LOC, "File sent OK");
        else
	    {
            boxed_text (CONFIRM_BOX_LOC, "Send failed, script aborted");
	    fclose (script);
	    script = 0;
	    await_attention ();
	    };
	restore_aux_params ();
        restore_screen_rectangle (save);
        cursor_on ();
	break;
    };
}

capture_file ()
{
int
    k;

struct saved_video
    *save;

char
    buf[40];

if (capture) fclose (capture);
pop_up_get_string (20, 1, "Name of file to receive:", buf, 40);
if ((capture = fopen (buf, "a")) == NULL)
    {
    capture = 0;
    save = pop_up_boxed_text (21, 2, "Can't open that file");
    await_attention ();
    restore_screen_rectangle (save);
    };
}

close_capture ()
{
if (capture != 0)
    fclose (capture);
capture = 0;
}

/*
 * await_attention
 *
 *	Feep, pop up box asking user to strike key, and return
 */

await_attention ()
{
struct saved_video
    *box;

#define MSG "Strike any key to proceed"

disable_cursor ();
FEEP;
box = pop_up_boxed_text (3 + CONFIRM_BOX_LOC, MSG);
putattr_i (1 + 3 + CONFIRM_BOX_LOC + 1, strlen (MSG), ATTR_BOLD_BLINK);
WAIT_FOR_ANY_KEY;
enable_cursor ();
restore_screen_rectangle (box);
}

kermit_get ()
{
int
    k;

struct saved_video
    *save;

char
    *msg,
    buf[40];

pop_up_get_string (20, 1, "Name of file to receive:", buf, 40);
filnam = buf;
save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
kermit_screen ();
if (recsw ())
    msg = "File received OK";
else
    msg = "Receive failed";
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

kermit_remote_dir ()
{
int
    k;

struct saved_video
    *save;

char
    *msg,
    buf[40];

pop_up_get_string (20, 1, "(Optional) wildcard filespec:", buf, 40);
save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
kermit_screen ();
buf[0] = 'D';			/* directory command */
if (sendcmdsw ('G', buf))
    msg = "Directory list OK";
else
    msg = "Directory list failed";
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

send_file ()
{
int
    k;

char
    *msg,
    buf[40];

struct saved_video
    *save;

pop_up_get_string (20, 1, "Name of file to send:", buf, 40);
if ((stuff = fopen (buf, "r")) == NULL)
    {
    stuff = 0;
    save = pop_up_boxed_text (21, 2, "Can't open that file");
    await_attention ();
    restore_screen_rectangle (save);
    };
}

open_script ()
{
int
    k;

char
    *msg,
    buf[40];

struct saved_video
    *save;

pop_up_get_string (20, 1, "Name of script file:", buf, 40);
if ((script = fopen (buf, "r")) == NULL)
    {
    script = 0;
    save = pop_up_boxed_text (21, 2, "Can't open that file");
    await_attention ();
    restore_screen_rectangle (save);
    };
}

close_script ()
{
if (script)
    {
    fclose (script);
    delay_counter = 0;
    script = 0;
    };
}

kermit_send ()
{
int
    k;

struct saved_video
    *save;

char
    *msg,
    buf[40];

pop_up_get_string (20, 1, "Name of file to send:", buf, 40);
filnam = buf;
save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
kermit_screen ();
if (sendsw ())
    msg = "File sent OK";
else
    msg = "Send failed";
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

modem_send ()
{
int
    k;

struct saved_video
    *save;

char
    *msg,
    buf[40];

pop_up_get_string (20, 1, "Name of file to send:", buf, 40);
save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
modem_screen ();
save_aux_params ();
disable_auto_xon_xof ();
if (sendfile (buf))
    msg = "File sent OK";
else
    msg = "Send failed";
restore_aux_params ();
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

modem_receive ()
{
int
    k;

struct saved_video
    *save;

char
    *msg,
    buf[40];

pop_up_get_string (20, 1, "Name of file to receive:", buf, 40);
save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
modem_screen ();
save_aux_params ();
disable_auto_xon_xof ();
if (readfile (buf))
    msg = "File received OK";
else
    msg = "Receive failed";
restore_aux_params ();
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

modem_screen ()
{
if (debug) return;
printmsg_row = 13;
cursor_off ();
clear_screen ();
prev_mdm_bytes_xferred = 0L;
prev_mdm_packets_sent = 0,
prev_mdm_packets_received = 0,
prev_mdm_bad_packets = 0,
prev_mdm_naked_packets = 0;

rectangle (1, 1, SCREEN_ROWS - 1, SCREEN_COLUMNS, ATTR_NORMAL);
horizontal_cut (12, 1, SCREEN_COLUMNS, ATTR_NORMAL);

#define TITLE "LC-Term XMODEM"

putline (2, 33, TITLE);
putattr_i (2, 32, strlen (TITLE) + 2, ATTR_REVERSE);

rectangle (4, 4, 7, 23, ATTR_NORMAL);
putline (5, 12, "Counters");
putattr_i (5, 12, strlen("Counters"), ATTR_BOLD);
horizontal_cut (6, 4, 24, ATTR_NORMAL);
putline (7, 5, "Packets sent:        0");
putline (8, 5, "Packets received:    0");
putline (9, 5, "Error packets:       0");
putline (10,5, "NAKed packets:       0");
cursor (24, 1);
}

mdm_notify_hook ()
{
char
    buf[16];

if (debug) return;
if (prev_mdm_bytes_xferred != mdm_bytes_xferred)
    {
    sprintf (buf, "%8ld", mdm_bytes_xferred);
    putline (6, 19, buf);
    prev_mdm_bytes_xferred = mdm_bytes_xferred;
    };
if (prev_mdm_packets_sent != mdm_packets_sent)
    {
    sprintf (buf, "%5d", mdm_packets_sent);
    putline (7, 22, buf);
    prev_mdm_packets_sent = mdm_packets_sent;
    };
if (prev_mdm_packets_received != mdm_packets_received)
    {
    sprintf (buf, "%5d", mdm_packets_received);
    putline (8, 22, buf);
    prev_mdm_packets_received = mdm_packets_received;
    };
if (prev_mdm_bad_packets != mdm_bad_packets)
    {
    sprintf (buf, "%5d", mdm_bad_packets);
    putline (9, 22, buf);
    prev_mdm_bad_packets = mdm_bad_packets;
    };
if (prev_mdm_naked_packets != mdm_naked_packets)
    {
    sprintf (buf, "%5d", mdm_naked_packets);
    putline (10, 22, buf);
    prev_mdm_naked_packets = mdm_naked_packets;
    };
}

do_help ()
{
save_screen ();
boxed_text (4, 5, "Press MAIN SCREEN for menu, EXIT to leave LC-Term, INTERRUPT for shell");
await_attention ();
restore_screen ();
}

kermit_finish ()
{
struct saved_video
    *save;

char
    *msg;

save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
kermit_screen ();
if (sendcmdsw ('G', "F"))
    msg = "Server stopped OK";
else
    msg = "Stop failed";
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

kermit_logout ()
{
struct saved_video
    *save;

char
    *msg;

save = save_screen_rectangle (1, 1, SCREEN_ROWS, SCREEN_COLUMNS);
kermit_screen ();
if (sendcmdsw ('G', "L"))
    msg = "Server logged out OK";
else
    msg = "Logout failed";
boxed_text (CONFIRM_BOX_LOC, msg);
await_attention ();
restore_screen_rectangle (save);
cursor_on ();
}

kermit_screen ()
{
char
    fspec[80];

if (debug) return;
printmsg_row = 13;
cursor_off ();
clear_screen ();
prev_bytes_xferred = 0L;
prev_packets_sent = 0,
prev_packets_received = 0,
prev_bad_packets = 0,
prev_naked_packets = 0;

rectangle (1, 1, SCREEN_ROWS - 1, SCREEN_COLUMNS - 1, ATTR_NORMAL);
horizontal_cut (12, 1, SCREEN_COLUMNS, ATTR_NORMAL);

#define TITLE "LC-Term KERMIT"

putline (2, 33, TITLE);
putattr_i (2, 32, strlen (TITLE) + 2, ATTR_REVERSE);

rectangle (3, 4, 8, 23, ATTR_NORMAL);
putline (4, 12, "Counters");
putattr_i (4, 12, strlen("Counters"), ATTR_BOLD);
horizontal_cut (5, 4, 24, ATTR_NORMAL);
putline (6, 5, "Bytes xferred:       0");
putline (7, 5, "Packets sent:        0");
putline (8, 5, "Packets received:    0");
putline (9, 5, "Error packets:       0");
putline (10,5, "NAKed packets:       0");
cursor (24, 1);
}

notify_hook ()
{
char
    buf[16];

if (debug) return;
if (prev_bytes_xferred != bytes_xferred)
    {
    sprintf (buf, "%8ld", bytes_xferred);
    putline (6, 19, buf);
    prev_bytes_xferred = bytes_xferred;
    };
if (prev_packets_sent != packets_sent)
    {
    sprintf (buf, "%5d", packets_sent);
    putline (7, 22, buf);
    prev_packets_sent = packets_sent;
    };
if (prev_packets_received != packets_received)
    {
    sprintf (buf, "%5d", packets_received);
    putline (8, 22, buf);
    prev_packets_received = packets_received;
    };
if (prev_bad_packets != bad_packets)
    {
    sprintf (buf, "%5d", bad_packets);
    putline (9, 22, buf);
    prev_bad_packets = bad_packets;
    };
if (prev_naked_packets != naked_packets)
    {
    sprintf (buf, "%5d", naked_packets);
    putline (10, 22, buf);
    prev_naked_packets = naked_packets;
    };
}

check_time ()
{
static int
    old_seconds = 0;

seconds = tod_seconds ();
if (seconds == old_seconds)		/* has clock ticked? */
    return;				/* no, just return */
old_seconds = seconds;
hours = tod_hours ();
minutes = tod_minutes ();
draw_clock (hours, minutes, seconds);
if (timer > 0)
    {
    extern jmp_buf env;
    if (--timer <= 0)
	{ printmsg ("Timed out");  longjmp (env, 1); };
    };
}

alarm (sec)
int sec;
{
timer = sec;
}

draw_clock (h, m, s)
int h, m, s;
{
char
    buf[9];

if (debug) return;
sprintf (buf, "%02d:%02d:%02d", h, m, s);
boxed_text (4, 30, buf);
}

/*
 * Routines called by KERMIT.C (protocol module)
 *
 */

kabort ()
{
extern jmp_buf
    abort_env;

printmsg ("Aborted at user request");
longjmp (abort_env, 1);
}

static struct menu_item
    kabort_menu[] =
    {
	{"Abort this transaction", &kabort},
	{"Resume this transaction", DONE},
	{"", 0}
    };

monitor_kbd ()
{
int
    c;

c = kbdin ();
if (c >= 0)
    if (c & 0x100)
        switch (c & 0xFF)
            {
            case KEY_EXIT:
                leave ();
            case KEY_ADDTNL_OPTIONS:
                parse_menu (kabort_menu, 8, 30, "LC-Term Transfer in Progress",
                            MENU_POST_CLEAR + MENU_ACTIVE_MODE);
                break;
            case KEY_HELP:
                printmsg ("EXIT key to exit, ADDTNL_OPTIONS for more options");
                break;
            default:
                FEEP;
            }
    else
	FEEP;
check_time ();
}

unsigned char read_modem ()
{
int
    c;

while ((c = getaux()) == -1)
    monitor_kbd ();
return ((unsigned char) c);
}

flushinput ()
{
if (debug)
    fputs ("Input flushed... ", stdout);
while ((getaux ()) != -1)
    monitor_kbd ();
if (debug)
    puts ("OK");
}

write_modem (cdata, count)
unsigned char *cdata;
int count;
{
char
    c;

if (debug)
    {
    int i, j;
    if (count > 9) j = 9;  else j = count;
    printf ("WRITE_MODEM: count is (%d), msg is (%x", count, cdata[0]);
    for (i = 1; i < j; i++)
        printf (", %x", cdata[i]);
    if (count > 9) fputs ("...", stdout);
    puts (")");
    };
while (count-- > 0)
    {
    c = *cdata++;
    while (! aux_hungry ())
	monitor_kbd ();
    if (! putaux (c))
	{ count++;  cdata--;  };
    };
}

callers_printmsg (s)
char *s;
{
char
    buf[81];

if (*s == 0)
    return;
sprintf (buf, "%02d:%02d:%02d %.68s", hours, minutes, seconds, s);
if (debug)
    {
    fputs (buf, stdout);
    return;
    };
put_line (printmsg_row, 2, 78, blanks);
putline (printmsg_row, 2, buf);
if (++printmsg_row > (SCREEN_ROWS - 2))
    printmsg_row = 12;
}

cursor_on ()
{
if (cursor_is_off)
    enable_cursor ();
cursor_is_off = 0;
}

cursor_off ()
{
if (! cursor_is_off)
    disable_cursor ();
cursor_is_off = 1;
}
>>>>>>>> MENU.C
/*
 * \usr\c\menu.c
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *	This module provides a general-purpose "windowing" menu handler
 */

#include <stdio.h>
#include "video.h"
#include "rainbow.h"

/* This array contains 132 blanks (ASCII 040) */

char
    blanks[132] =
"                                                                  \
                                                                  ";

extern struct saved_video
    *pop_up_boxed_text ();

/*
 * Simple-minded menu handler.
 *
 *	menu_addr		Array of menu_type entries
 *	row, col		Position of upper left corner
 *	title			String used as caption
 *	flags			Various bits
 *
 */

int (*parse_menu ()) (menu_addr, row, col, title, flags)
struct menu_item menu_addr[];
int row, col, flags;
char *title;
{
#define PROMPT "Select, then press DO:"

int
    prompt_length = strlen (PROMPT),
    i,
    length,
    width,
    safety_width,
    (*handler_routine) (),
    selection,
    old_selection,
    selection_row,
    selection_col,
    help_present,
    do_flag,
    c;

char
    buf[132];

struct menu_item
    *item;

struct saved_screen_rectangle
    *saved_region,
    *help_box;

width = 0;
help_present = 0;
for (length = 0; (menu_addr[length].handler) != 0; length++)
    if ((i = strlen (menu_addr[length].name)) > width)
	width = i;
if ((i = strlen (title)) > width) width = i;
if (length > 9)
    abort ("Menu >9 items not supported");

safety_width = width + 6;		/* includes sides and numbers */
if ((prompt_length + 2) > safety_width)
    safety_width = prompt_length + 2;
saved_region = save_screen_rectangle (row, col, length + 5, safety_width);
paint_menu (menu_addr, row, col, title, flags, length, width);

/* Now handle menu selection.  If MENU_ACTIVE_MODE, do this repeatedly,
 * calling semantic routines until user chooses DONE entry. */

while (1)
    {
    putline (length + row + 4, col + 1, PROMPT);
    putattr_i (length + row + 4, col + 1, prompt_length, ATTR_BOLD);
    selection_row = row + length + 4;
    selection_col = col + prompt_length + 2;
    selection = 0;
    old_selection = 0;
    while (1)
        {
        do_flag = 0;
        cursor (selection_row, selection_col);	/* blink in useful place */
	buf[0] = ' ';
	if (selection) buf[1] = selection + '0'; else buf[1] = ' ';
	buf[2] = 0;
	putline (selection_row, selection_col - 1, buf);
        while ((c = kbdin ()) < 0);	/* poll keyboard until real char */
	if (help_present)	/* zap previous help box if necessary */
	    {
	    restore_screen_rectangle (help_box);
	    help_present = 0;
	    };
	c = map_keypad_numbers (c);	/* map keypad numbers to normal ones */
	if ((! (c & 0x100)) && ((c & 0xFF) == 015))
	    c = KEY_DO | 0x100;		/* make RETURN synonym for DO */
        if (c & 0x100)			/* function key? */
            {
            switch (c & 0xFF)
                {
                case KEY_DOWN_ARROW:
                    selection++; if (selection > length) selection = 1; break;
                case KEY_UP_ARROW:
                    selection--; if (selection < 1) selection = length; break;
		case KEY_KEYPAD_ENTER:
                case KEY_DO:
		    if (selection != 0)		/* if something chosen */
			{ do_flag = 1; break; }; /* go for it */
		    FEEP;			/* else feep and fall thru */
                case KEY_HELP:			/* to help */
                    help_box =
			pop_up_boxed_text (22, 2,
"Use arrow keys to move up and down, or type the number of the item you want");
		    help_present = 1;
                    break;
                default:
#ifdef DEBUG
		    sprintf (buf, "Invalid character 0x%3x", c);
		    boxed_text (10, 10, buf);
#endif
		    FEEP;
                };
            }
        else
            {			/* Got a real character, see if number */
            int i;
            i = (c & 0xFF) - '0';
            if ((i < 1) || (i > length))
		FEEP;
            else
                selection = i;
            };
        if (old_selection != selection)
            {
            putattr_i (row + 2 + old_selection, col + 1, width + 4, ATTR_NORMAL);
            putattr_i (row + 2 + selection, col + 1, width + 4, ATTR_REVERSE);
            };
        old_selection = selection;
        if (do_flag) break;
        };
    item = &menu_addr[selection - 1];

/* Erase prompt line */

    put_line (length + row + 4, col + 1, prompt_length + 2, blanks);
    putattr_i (length + row + 4, col + 1, prompt_length + 2, ATTR_NORMAL);

/* If not active mode, just return selection to caller */

    if (! (flags & MENU_ACTIVE_MODE))
	{
        if (flags & MENU_POST_CLEAR)
            menu_clear_region (row, col, length, width);
	restore_screen_rectangle (saved_region);
	return (item->handler);
	};

/* If active mode, call routine or return if user chose DONE */

    if (item->handler == DONE)
	{
	restore_screen_rectangle (saved_region);
	return (DONE);
	};
    handler_routine = item->handler;
    (*handler_routine) ();
    putattr_i (row + 2 + selection, col + 1, width + 4, ATTR_NORMAL);

/* If MENU_ONCE_ONLY, restore screen and return to caller */

    if (flags & MENU_ONCE_ONLY)
	{
	restore_screen_rectangle (saved_region);
	return (DONE);
	};
    };
}

paint_menu (menu_addr, row, col, title, flags, length, width)
struct menu_item menu_addr[];
int row, col, flags, length, width;
char *title;
{
char buf[132];

int i;

/* Clear screen, or menu region */

disable_cursor ();
if (flags & MENU_CLEAR_SCREEN)
    clear_screen ();
else					/* clear rectangle to be occupied */
    menu_clear_region (row, col, length, width);

/* Now either quickly or slowly (animated) draw menu outlines */

if (flags & MENU_ANIMATE)
    for (i = 1; i <= length + 3; i++)	/* animatedly draw rectangle */
	{
	put_line (row + i - 1, col + 1, width + 4, blanks);
	rectangle (row, col, i, width + 5, ATTR_NORMAL);
	}
else
    rectangle (row, col, length + 3, width + 5, ATTR_NORMAL);
horizontal_cut (row + 2, col, width + 6, ATTR_NORMAL);	/* title line */

/* Put title, reverse video, centered in title box at top of menu */

putattr_i (row + 1, col + 1, width + 4, ATTR_REVERSE);
putline (row + 1, col + ((width+4)/2) - (strlen (title) / 2) + 1, title);

/* Write the menu items and their numbers */

for (i = 0; (menu_addr[i].handler) != 0; i++)
    {
    sprintf (buf, " %d: %s", i + 1, menu_addr[i].name);
    putline (i + row + 3, col + 1, buf);
    };
enable_cursor ();
}

menu_clear_region (row, col, length, width)
{
int
    i;

disable_cursor ();
for (i = row; i <= row + length + 4; i++)	/* clear menu */
    {
    put_line (i, col, width + 5, blanks);
    putattr_i (i, col, width + 5, ATTR_NORMAL);
    };
enable_cursor ();
}

map_keypad_numbers (c)
int c;
{
if (c & 0x100)				/* if function key */
    switch (c & 0xFF)
	{
	case KEY_KEYPAD_0: c = '0'; break;
	case KEY_KEYPAD_1: c = '1'; break;
	case KEY_KEYPAD_2: c = '2'; break;
	case KEY_KEYPAD_3: c = '3'; break;
	case KEY_KEYPAD_4: c = '4'; break;
	case KEY_KEYPAD_5: c = '5'; break;
	case KEY_KEYPAD_6: c = '6'; break;
	case KEY_KEYPAD_7: c = '7'; break;
	case KEY_KEYPAD_8: c = '8'; break;
	case KEY_KEYPAD_9: c = '9'; break;
	case KEY_KEYPAD_ENTER: c = KEY_DO | 0x100; break;
	};
return (c);
}
>>>>>>>> MODEM.C
#include <stdio.h>

#define	FALSE 0
#define TRUE 1

#define DOTS		50	/* SECTOR COUNTING DOTS PER LINE */
#define	SECSIZ		0x80
#define	BUFSIZ		0x1000	/* Text buffer */
#define	ERRORMAX	20	/* MAX ERRORS BEFORE ABORT */
#define	RETRYMAX	15	/* MAXIMUM RETRYS BEFORE ABORT */

#define	SOH		1	/* START OF SECTOR CHAR */
#define	EOT		4	/* end of transmission char */
#define	ACK		6	/* acknowledge sector transmission */
#define	NAK		21	/* error in transmission detected */

static char
    bufr[ BUFSIZ ],
    filename[ 14 ];

static int
    fd;

int
    mtimeout;

int
    mdm_packets_sent,
    mdm_packets_received,
    mdm_bad_packets,
    mdm_naked_packets;

long
    mdm_bytes_xferred;

extern jmp_buf
    abort_env,
    env;

extern int
    debug;

extern unsigned char
    read_modem ();

mdmini()
{
mtimeout = 60;
mdm_bytes_xferred = 0L;
mdm_packets_sent = 0;
mdm_packets_received = 0;
mdm_bad_packets = 0;
mdm_naked_packets = 0;
}

sendchar(data)
unsigned int data;
{
unsigned char
    buf[1];

buf[0] = (unsigned char) data;
write_modem (buf, 1);
}

readfile(file)
char *file;
{
int
    firstchar,
    sectnum,
    sectcurr,
    sectcomp,
    errors,
    errorflag;

unsigned int
    checksum,
    j,
    bufptr;

mdmini ();
if ((fd = creat(file,BWRITE)) < 0)
    {
    error("cannot open %s (%x)",file,fd);
    return(FALSE);
    }
else
    printmsg("receiving %s",file);

sectnum = errors = bufptr = 0;
flushinput();
sendchar(NAK);
if (setjmp(env)) return(FALSE);		/* timeout */
if (setjmp(abort_env)) return(FALSE);

while (firstchar != EOT && errors != ERRORMAX)
    {
    errorflag = FALSE;
    alarm (mtimeout);			/* set timeout trap */
    do					/* get sync char */
	{
        firstchar = read_modem();
	if (debug)
	    printf("Got char '%x'\n", firstchar);
	} while (firstchar != SOH && firstchar != EOT);
    if (firstchar == SOH)
	{
        sectcurr = read_modem();
        sectcomp = read_modem();
	if (debug) printf("Sector %x, ~%x\n", sectcurr, sectcomp);
        if ((sectcurr + sectcomp) == 255)
	    {
            if (sectcurr == (sectnum + 1 & 0xff))
		{
                checksum = 0;
                for (j = bufptr;j < (bufptr + SECSIZ);j++)
		    {
                    bufr[j] = read_modem();
                    checksum = (checksum + bufr[j]) & 0xff;
                    }
                if (checksum == read_modem())
		    {
		    mdm_packets_received++;
		    mdm_notify_hook ();
                    errors = 0;
                    sectnum++;
                    bufptr += SECSIZ;
		    mdm_bytes_xferred += SECSIZ;
                    if (bufptr == BUFSIZ)
			{
                        bufptr = 0;
                        if (write(fd,bufr,BUFSIZ) == EOF)
			    {
                            error("error writing file");
                            close(fd);
			    alarm(0);
                            return(FALSE);
                            };
                        };
		    flushinput ();
                    sendchar(ACK);
                    }
                else
		    {
		    mdm_bad_packets++;
		    mdm_notify_hook ();
                    errorflag = TRUE;
		    if (debug)
			{
                        printmsg("checksum error, expected ");
                        printmsg("<%02x>",checksum);
			};
                    }
                }
            else
		{
                if (sectcurr == (sectnum & 0xff))
		    {
                    printmsg("received duplicate sector %d",sectnum);
		    flushinput();
                    sendchar(ACK);
                    }
                else
		    {
		    if (debug)
			error("synch error");
		    mdm_bad_packets++;
		    mdm_notify_hook ();
		    errorflag = TRUE;
		    }
                }
            }
        else
	    {
	    if (debug)
		error("sector number error");
	    mdm_bad_packets++;
	    mdm_notify_hook ();
	    errorflag = TRUE;
	    }
        }
    if (errorflag == TRUE)
	{
        errors++;
        printmsg("error %d",errors);
	flushinput();
        sendchar(NAK);
        }

    };	/* end while */
	    
if ((firstchar == EOT) && (errors < ERRORMAX))
    {
    sendchar(ACK);
    write(fd,bufr,bufptr);
    close(fd);
    alarm(0);
    return(TRUE);
    }
alarm(0);
return(FALSE);
}

sendfile(file)
char *file;
{
int
    sectnum,
    sectors,
    attempts;

unsigned int
    checksum,
    j,
    bufptr;

char
    c;

mdmini ();
if ((fd = open(file,BREAD)) < 0)
    {
    error("cannot open %s",file);
    return(FALSE);
    }
else
    printmsg("sending %s",file);

attempts = 0;
sectnum = 1;
j = 0;

if (setjmp(env)) return(FALSE);
if (setjmp(abort_env)) return(FALSE);

alarm(mtimeout);
while (((c = read_modem ()) != NAK) && (j++ < (ERRORMAX*2)))
    if (debug)
	printf ("Ate char '%x'\n", c);

if (j >= (ERRORMAX*2))
    { error("Receiver not sending NAKs");  alarm(0);  return(FALSE); };

if (debug)
    printf ("Got ACK: '%x'\n", c);

flushinput ();
alarm(mtimeout);

while ((sectors = read(fd,bufr,BUFSIZ))
        && (attempts != RETRYMAX))
    {
    if (sectors == EOF)
	{
        error("error reading file");
        close(fd);
	alarm(0);
        return(FALSE);
        };
    bufptr = 0;
    do
        {
        attempts = 0;
        do
            {
	    alarm(mtimeout);
            if (debug)
                if (((sectnum - 1) % DOTS) == 0)
                    printf("\012\015<%4d>.",sectnum);
                else
                    printf(".");
            sendchar(SOH);
            sendchar(sectnum);
            sendchar(~sectnum);
            checksum = 0;
	    write_modem(&bufr[bufptr],SECSIZ);
            for (j = bufptr; j < (bufptr+SECSIZ); j++)
                checksum += bufr[j];
            sendchar(checksum);
	    flushinput();
	    mdm_packets_sent++;
	    mdm_notify_hook ();
            attempts++;
	    c = read_modem();
	    if (c != ACK) mdm_naked_packets++;
	    if (debug)
		printf ("Response char is '%x'\n", c);
            } while ((c != ACK) && 
                (attempts != RETRYMAX));
        bufptr += SECSIZ;
	mdm_bytes_xferred += SECSIZ;
        sectnum++;
        } while ((bufptr < sectors) && (attempts != RETRYMAX));
    };

close(fd);
if (attempts == RETRYMAX)
    {
    error("no acknowledgment of sector, aborting");
    alarm(0);
    return(FALSE);
    }
else
    {
    attempts = 0;
    do
	{
        sendchar(EOT);
        attempts++;
	} while ((read_modem() != ACK) && (attempts != RETRYMAX));
    if (attempts == RETRYMAX)
        error("no acknowledgment of end of file");
    };
alarm(0);
return(TRUE);
}
>>>>>>>> RAINBOW.H
/*
 * \usr\rainbow\rainbow.h
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *	Hardware-specific definitions for the DEC Rainbow 100 (model A)
 */

#define LOOPS_PER_MILLISECOND 9

#define SCREEN_ROWS 24
#define SCREEN_COLUMNS 80

#define SCREEN_BUF_SIZE (ROM_SCRFRE + 1)
#define SCREEN_BUF_SEG 0xEE00
#define SCREEN_BUF_OFFSET 0x0
#define SCREEN_ATTR_OFFSET 0x1000

/*
 * Attribute bits in attribute RAM
 */

#define ATTR_NORMAL 016
#define ATTR_REVERSE 017
#define ATTR_BOLD 014
#define ATTR_BLINK 012
#define ATTR_UNDERLINE 006

/* Combinations */

#define ATTR_BOLD_REVERSE 015
#define ATTR_BOLD_BLINK 010
#define ATTR_REVERSE_BLINK 013

#define FEEP putcon ('\007')	/* ding */

/* LK201 function key codes as returned by ROM function 6 */

#define KEY_HELP 0
#define KEY_DO 1
#define KEY_COMPOSE_CHARACTER 2
#define KEY_PRINT_SCREEN 3
#define KEY_F4 5
#define KEY_INTERRUPT 7
#define KEY_RESUME 9
#define KEY_CANCEL 0xB
#define KEY_MAIN_SCREEN 0xD
#define KEY_EXIT 0xF
#define KEY_ADDTNL_OPTIONS 0x11
#define KEY_F17 0x13
#define KEY_F18 0x15
#define KEY_F19 0x17
#define KEY_F20 0x19
#define KEY_FIND 0x1B
#define KEY_INSERT_HERE 0x1D
#define KEY_REMOVE 0x1F
#define KEY_SELECT 0x21
#define KEY_PREV_SCREEN 0x23
#define KEY_NEXT_SCREEN 0x25
#define KEY_UP_ARROW 0x27
#define KEY_DOWN_ARROW 0x29
#define KEY_RIGHT_ARROW 0x2B
#define KEY_LEFT_ARROW 0x2D
#define KEY_KEYPAD_PF1 0x59
#define KEY_KEYPAD_PF2 0x5C
#define KEY_KEYPAD_PF3 0x5F
#define KEY_KEYPAD_PF4 0x62
#define KEY_KEYPAD_0 0x2F
#define KEY_KEYPAD_1 0x32
#define KEY_KEYPAD_2 0x35
#define KEY_KEYPAD_3 0x38
#define KEY_KEYPAD_4 0x3B
#define KEY_KEYPAD_5 0x3E
#define KEY_KEYPAD_6 0x41
#define KEY_KEYPAD_7 0x44
#define KEY_KEYPAD_8 0x47
#define KEY_KEYPAD_9 0x4A
#define KEY_KEYPAD_DASH 0x4D
#define KEY_KEYPAD_COMMA 0x50
#define KEY_KEYPAD_PERIOD 0x53
#define KEY_KEYPAD_ENTER 0x56

#define HORIZ_BAR_CHAR 0x12	/* horizontal bar */
#define VERT_BAR_CHAR 0x19	/* vertical bar */
#define ULC_CHAR 0x0D		/* upper left corner */
#define URC_CHAR 0x0C		/* upper right corner */
#define LLC_CHAR 0x0E		/* lower left corner */
#define LRC_CHAR 0x0B		/* lower right corner */
#define CROSS_CHAR 0x0F		/* crossing lines */
#define LEFT_T_CHAR 0x15	/* left T */
#define RIGHT_T_CHAR 0x16	/* right T */
#define TOP_T_CHAR 0x18		/* top T */
#define BOTTOM_T_CHAR 0x17	/* bottom T */

/*
 *	Offsets into AUX ctl buffer (this stuff should really be in
 *	RAINBOW.H)
 *	
 */

#define AUX_FUNCTION 0
#define AUX_FUNC_RETC 1
#define AUX_CHARACTER 2
#define AUX_CHAR_STAT 3
#define AUX_BUFFER 4
#define AUX_CCB 4

#define CCB_DEVNUM 0		/* device number */
#define CCB_MODE 1		/* mode */
#define CCB_STOPB 2		/* stop bits */
#define CCB_DATAB 3		/* data bits */
#define CCB_XMITP 4		/* transmit parity */
#define CCB_RCVBD 5		/* receive baud */
#define CCB_XMTBD 6		/* transmit baud */
#define CCB_XONCH 7		/* XON character */
#define CCB_XOFCH 8		/* XOF character */
#define CCB_RCVXX 9		/* receive XON/XOF */
#define CCB_XMTXX 10		/* transmit XON/XOF */
#define CCB_ALTBUF 11		/* alternate buffer size */
#define CCB_BUFOFF 13		/* buffer offset */
#define CCB_BUFSEG 15		/* buffer segment */

>>>>>>>> VID2.C
/*
 * \usr\c\vid2.c
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *
 * Low-level video hacking routines.  Originally written for the DEC
 * Rainbow and slowly trying to become more hardware-independent.
 *
 */

#include <stdio.h>
#include "video.h"
#include "rainbow.h"

int
    cursor_row,
    cursor_column;


/*
 * putline (row, column, string)
 *
 *	Rapidly put text to screen
 *
 */

putline (row, col, string)
int row, col;
char *string;
{
int
    count;

count = strlen (string);
#ifdef DEBUG
if (row < 1) row = 1;
if (row > SCREEN_ROWS) row = SCREEN_ROWS;
if (col < 1) col = 1;
if ((col + count) > SCREEN_COLUMNS + 1) count = (SCREEN_COLUMNS + 1) - col;
#endif
if (count > 0)
    put_line (row, col, count, string);
}

/*
 * putcons (string)
 *
 *	Writes escape sequence to console via ROM call (bypassing
 *	DOS to avoid keyboard input meddling)
 */

putcons (string)
char *string;
{
char
    c;

while ((c = *string++) != 0)
    putcon (c);
}

clear_rest_of_line ()
{
putcons ("\033[K");
}

cursor (row, column)
int row, column;
{
char
    buf[32];

cursor_row = row;
cursor_column = column;
sprintf (buf, "\033[%d;%dH", row, column);
putcons (buf);
}

double_height_top (row)
int row;
{
cursor (row, 1);
putcons ("\033#3");
}

double_height_bottom (row)
int row;
{
cursor (row, 1);
putcons ("\033#4");
}

double_width (row)
int row;
{
cursor (row, 1);
putcons ("\033#6");
}

/*
 * put_line_attr (row, length, attribute)
 *
 *	Set attribute for an entire line
 */

put_line_attr (row, n, attr)
int row, n, attr;
{
int i;
char line_buf[132];

for (i = 0; i < n; i++)
    line_buf[i] = (char) attr;
putattr (row, 1, n, line_buf);
}

/*
 * putattr_i (row, column, length, attribute)
 *
 *	Set attribute for string of characters
 */

putattr_i (row, col, n, attr)
int row, col, n, attr;
{
char
    abuffer[132];

int
    i;

for (i = 0; i < n; i++)
    abuffer[i] = attr;
putattr (row, col, n, abuffer);
}

/*
 * Draw a rectangle
 * Parameters:
 *	ulr, ulc   - upper left row, upper left column
 *	h, w	   - height, width
 *      attr       - attribute bits
 */

rectangle (ulr, ulc, h, w, attr)
int ulr, ulc, h, w, attr;
{
int
    lrr,
    lrc,
    row,
    column;

char
    cbuffer[133],
    abuffer[133];

disable_cursor ();
lrr = ulr + h;
lrc = ulc + w;
cbuffer[0] = ULC_CHAR;			/* upper left corner */
abuffer[0] = attr;
for (column = ulc + 1; column < lrc; column++)
    {
    cbuffer[column - ulc] = HORIZ_BAR_CHAR;	/* horizontal line */
    abuffer[column - ulc] = attr;	/* attribute bits */
    };
cbuffer[lrc - ulc] = URC_CHAR;		/* upper right corner */
abuffer[lrc - ulc] = attr;
cbuffer[(lrc - ulc) + 1] = 0;
putline (ulr, ulc, cbuffer);
putattr (ulr, ulc, (lrc - ulc) + 1, abuffer);
for (row = ulr + 1; row < lrr; row++)
    {
    cbuffer[0] = 0x19;			/* vertical bar */
    cbuffer[1] = 0;
    putline (row, ulc, cbuffer);
    putattr (row, ulc, 1, abuffer);
    putline (row, lrc, cbuffer);
    putattr (row, lrc, 1, abuffer);
    };
cbuffer[0] = LLC_CHAR;			/* lower left corner */
abuffer[0] = attr;
for (column = ulc + 1; column < lrc; column++)
    {
    cbuffer[column - ulc] = HORIZ_BAR_CHAR;	/* horizontal line */
    abuffer[column - ulc] = attr;	/* attribute bits */
    };
cbuffer[lrc - ulc] = LRC_CHAR;		/* lower right corner */
abuffer[lrc - ulc] = attr;
cbuffer[(lrc - ulc) + 1] = 0;
putline (lrr, ulc, cbuffer);
putattr (lrr, ulc, (lrc - ulc) + 1, abuffer);
enable_cursor ();
}

/*
 * Section an existing rectangle, either horizontally or vertically
 *
 */

horizontal_cut (row, column, length, attr)
int row, column, length;
{
int
    n;

char
    cbuffer[133],
    abuffer[133];

cbuffer[0] = LEFT_T_CHAR;		/* left T */
abuffer[0] = attr;
for (n = 1; n < (length - 1); n++)
    {
    cbuffer[n] = HORIZ_BAR_CHAR;	/* horizontal line */
    abuffer[n] = attr;
    };
cbuffer[n] = RIGHT_T_CHAR;		/* right T */
abuffer[n] = attr;
cbuffer[n + 1] = 0;
putline (row, column, cbuffer);
putattr (row, column, length, abuffer);
}

vertical_cut (row, column, length, attr)
int row, column, length, attr;
{
int
    this_row;

char
    cbuffer[2],
    abuffer[1];

cbuffer[0] = TOP_T_CHAR;
cbuffer[1] = 0;
putline (row, column, cbuffer);
abuffer[0] = attr;
putattr (row, column, 1, abuffer);
cbuffer[0] = VERT_BAR_CHAR;
for (this_row = row + 1; this_row < (row + length - 1); this_row++)
    {
    putline (this_row, column, cbuffer);
    putattr (this_row, column, 1, abuffer);
    };
cbuffer[0] = BOTTOM_T_CHAR;
putline (row + length - 1, column, cbuffer);	/* Bottom T */
putattr (row + length - 1, column, 1, abuffer);
}

kbdin ()
{
int
    c;

c = getkey ();
if (c == -2)				/* if interference from DOS */
    c = bdos (7, 0) & 0x77;		/* and get char from DOS */
return (c);
}

boxed_text (row, col, text)
int row, col;
char *text;
{
int
    length;

#define MAX_ROW SCREEN_ROWS - 2

length = strlen (text);
if (row > MAX_ROW) row = MAX_ROW;
if (row < 1) row = 1;
if (col < 1) col = 1;
if ((col + length) > SCREEN_COLUMNS) length = SCREEN_COLUMNS - col;
rectangle (row, col, 2, length + 1, ATTR_NORMAL);
putline (row + 1, col + 1, text);
}
>>>>>>>> VIDEO.H
/*
 * \USR\C\VIDEO.H
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 * Video hacking definitions
 *
 */

#define MENU_CLEAR_SCREEN 2		/* clear screen before drawing menu */
#define MENU_ANIMATE 4			/* draw menu like window shade */
#define MENU_POST_CLEAR 8		/* clear menu from screen when done */
#define MENU_ACTIVE_MODE 16		/* menu actively calls semantic 
					   routines, quits when "done" entry
					   selected */
#define MENU_ONCE_ONLY 32		/* call just one routine then quit */

#define DONE -1

struct menu_item
    {
    char *name;
    int (*handler) ();
    };

#define WAIT_FOR_ANY_KEY while (kbdin () < 0)

struct saved_video
    {
    struct saved_video *next;
#ifdef DEBUG
    int magic;
#endif
    int length;
    int row;
    int col;
    char *text;
    char *attributes;
    };
>>>>>>>> WINDOW.C
/*
 * \usr\c\window.c
 *
 *	Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard Mass.
 *
 *	This software may be freely copied and disseminated for
 *	noncommercial purposes, if and only if this entire copyright
 *	statement and notice is included intact.  This software, and
 *	the information contained herein, may not be used for commercial
 *	purposes without my prior written permission.
 *
 *	High-level screen management routines - provide rudimentary
 *	window-oriented functionality for character-mapped video displays.
 *	Based on low-level video functions supplied by vid2.c.
 */

#include <stdio.h>
#include "video.h"
#include "rainbow.h"

char
    screen_chars[SCREEN_ROWS * SCREEN_COLUMNS];

char
    screen_attributes[SCREEN_ROWS * SCREEN_COLUMNS];


/*
 * Copy counted string to screen, copying to our own area
 */

put_line (row, col, count, string)
int row, col, count;
char *string;
{
int
    k,
    index;

char
    *sp,
    *dp;

dp = &screen_chars[(row - 1) * SCREEN_COLUMNS + (col - 1)];
sp = string;
movmem (sp, dp, count);
_put_line (row, col, count, string);	/* call hardware-dependent routine */
}

/*
 * Put attributes to screen
 */

putattr (row, col, count, string)
int row, col, count;
char *string;
{
int
    k,
    index;

char
    *sp,
    *dp;

dp = &screen_attributes[(row - 1) * SCREEN_COLUMNS + (col - 1)];
sp = string;
movmem (sp, dp, count);
_putattr (row, col, count, string);	/* call hardware-dependent routine */
}

/*
 * getline (row, col, dest, n)
 *
 *	Return state of character cells on screen
 */

getline (row, col, dest, n)
int row, col, n;
char *dest;
{
char
    *sp;

sp = &screen_chars[((row - 1) * SCREEN_COLUMNS) + (col - 1)];
movmem (sp, dest, n);
}

/*
 * getattr (row, col, dest, n)
 *
 *	Like getline but gets attributes
 */

getattr (row, col, dest, n)
int row, col, n;
char *dest;
{
char
    *sp;

sp = &screen_attributes[((row - 1) * SCREEN_COLUMNS) + (col - 1)];
movmem (sp, dest, n);
}

clear_screen ()
{
int
    j,
    k;

putcons ("\033[2J");
setmem (screen_chars, SCREEN_ROWS * SCREEN_COLUMNS, 0);
setmem (screen_attributes, SCREEN_ROWS * SCREEN_COLUMNS, ATTR_NORMAL);
}

/*
 * save_screen_rectangle
 *
 *	Returns pointer to chain of 'struct saved_video' nodes.
 */

struct saved_video *save_screen_rectangle (row, col, h, w)
int row, col, h, w;
{
int
    r;

struct saved_video
    dummy,
    *prev,
    *ptr;

disable_cursor ();
#ifdef DEBUG
if (row < 1 || (row + h - 1) > SCREEN_ROWS ||
    col < 1 || (col + w - 1) > SCREEN_COLUMNS)
    { printf ("\n?BAD ARGS TO SAVE_SCREEN_RECTANGLE\n"); exit (); };
#endif
ptr = &dummy;
for (r = row; r < row + h; r++)
    {
    prev = ptr;
    ptr = malloc (sizeof (struct saved_video));
    if (ptr == NULL)
	{ printf ("\n?MALLOC\n"); exit (); };
    prev->next = ptr;
    ptr->next = 0;
#ifdef DEBUG
    ptr->magic = 12321;
#endif
    ptr->row = r;
    ptr->col = col;
    ptr->length = w;
    ptr->text = malloc (w);
    ptr->attributes = malloc (w);
    getline (r, col, ptr->text, w);
    getattr (r, col, ptr->attributes, w);
    };
enable_cursor ();
return (dummy.next);
}

/* restore_screen_rectangle
 *
 *	Restores characters and attributes saved by 'save_screen_rectangle',
 *	and returns storage to heap.
 */

restore_screen_rectangle (ptr)
struct saved_video *ptr;
{
struct saved_video
    *next;

disable_cursor ();
while (ptr != 0)
    {
#ifdef DEBUG
    if (ptr->magic != 12321)
	{ printf ("\n? BAD NODE IN RESTORE_SCREEN_RECTANGLE\n"); exit (); };
#endif
    put_line (ptr->row, ptr->col, ptr->length, ptr->text);
    putattr (ptr->row, ptr->col, ptr->length, ptr->attributes);
    next = ptr->next;
    free (ptr->text);
    free (ptr->attributes);
    free (ptr);
    ptr = next;
    };
enable_cursor ();
}

struct saved_video *pop_up_boxed_text (row, col, text)
int row, col;
char *text;
{
int
    length;

struct saved_video
    *box;

#define MAX_ROW SCREEN_ROWS - 2

length = strlen (text);
if (row > MAX_ROW) row = MAX_ROW;
if (row < 1) row = 1;
if (col < 1) col = 1;
if ((col + length + 1) > SCREEN_COLUMNS) length = SCREEN_COLUMNS - col - 1;
box = save_screen_rectangle (row, col, 3, length + 2);
rectangle (row, col, 2, length + 1, ATTR_NORMAL);
putline (row + 1, col + 1, text);
return (box);
}

/*
 * pop_up_get_string
 *
 *	Pop up a rectangle with a prompt, get a string, strip off
 *	the newline, and disappear the box, restoring what it covered
 */

pop_up_get_string (row, col, prompt, buf, len)
int row, col, len;
char *prompt, *buf;
{
struct saved_video
    *save;

int
    prompt_length,
    k,
    width;

prompt_length = strlen (prompt);
width = prompt_length + len;
save = save_screen_rectangle (row, col, 3, width + 1);
rectangle (row, col, 2, width, ATTR_NORMAL);
putline (row + 1, col + 1, prompt);
cursor (row + 1, col + prompt_length + 2);
fgets (buf, len, stdin);
for (k = 0; k < len; k++)
    {
    if (buf[k] == 0) break;
    if (buf[k] == '\n')
	{ buf[k] = 0;  break;  };
    };
restore_screen_rectangle (save);
}
