source/nkeys.h 644 144 13 4225 4267454300 6713 /* * Definitions of keys returned by n_getchar() * (Many kept compatible with Greenleaf functions */ #define F1 0x80 #define F2 0x81 #define F3 0x82 #define F4 0x83 #define F5 0x84 #define F6 0x85 #define F7 0x86 #define F8 0x87 #define F9 0x88 #define F10 0x89 #define SF1 0x90 #define SF2 0x91 #define SF3 0x92 #define SF4 0x93 #define SF5 0x94 #define SF6 0x95 #define SF7 0x96 #define SF8 0x97 #define SF9 0x98 #define SF10 0x99 #define CF1 0xA0 #define CF2 0xA1 #define CF3 0xA2 #define CF4 0xA3 #define CF5 0xA4 #define CF6 0xA5 #define CF7 0xA6 #define CF8 0xA7 #define CF9 0xA8 #define CF10 0xA9 #define ALT1 0xb0 #define ALT2 0xb1 #define ALT3 0xb2 #define ALT4 0xb3 #define ALT5 0xb4 #define ALT6 0xb5 #define ALT7 0xb6 #define ALT8 0xb7 #define ALT9 0xb8 #define ALT0 0xb9 #define AF1 0xE0 #define AF2 0xE1 #define AF3 0xE2 #define AF4 0xE3 #define AF5 0xE4 #define AF6 0xE5 #define AF7 0xE6 #define AF8 0xE7 #define AF9 0xE8 #define AF10 0xE9 #define HOME 0x8A /* HOME key */ #define CURLF 0x8B /* <- */ #define ENDKEY 0x8C /* END key */ #define CURUP 0x8D /* up arrow */ #define CURDN 0x8E /* down arrow */ #define PGUP 0x9A /* PgUp */ #define CURRT 0x9B /* -> */ #define PGDN 0x9C /* PgDn */ #define INSERT 0x9D /* Ins */ #define DELETE 0x9E /* Del */ #define CTRLHOME 0xAA /* Ctrl Home */ #define CTRLCURLF 0xAB /* Ctrl <- */ #define CTRLEND 0xAC /* Ctrl End */ #define CTRLPRTSC 0xAE /* Ctrl PrtSc */ #define CTRLPGUP 0xBA /* Ctrl PgUp */ #define CTRLCURRT 0xBB /* Ctrl -> */ #define CTRLPGDN 0xBC /* Ctrl PgDn */ #define REVTAB 0x8F /* Shift Tab */ #define ALTMINUS 0xBD /* Alt - */ #define ALTEQUAL 0xBE /* Alt = */ #define ALTA 0xC1 #define ALTB 0xC2 #define ALTC 0xC3 #define ALTD 0xC4 #define ALTE 0xC5 #define ALTF 0xC6 #define ALTG 0xC7 #define ALTH 0xC8 #define ALTI 0xC9 #define ALTJ 0xCA #define ALTK 0xCB #define ALTL 0xCC #define ALTM 0xCD #define ALTN 0xCE #define ALTO 0xCF #define ALTP 0xD0 #define ALTQ 0xD1 #define ALTR 0xD2 #define ALTS 0xD3 #define ALTT 0xD4 #define ALTU 0xD5 #define ALTV 0xD6 #define ALTW 0xD7 #define ALTX 0xD8 #define ALTY 0xD9 #define ALTZ 0xDA #define THENUL 0xDB source/vskeys.h 644 144 13 3761 4267454301 7113 /* * @(#)vskeys.h 1.25 (NCSA) 4/29/88 * * Virtual Screen Kernel Keys and Attribute Definitions * (vskeys.c) * * by Gaige B. Paulsen * * This file contains equates used by the program for specification of * special Keyboard characters and definition of the Attribute byte. * * Version Date Notes * ------- ------ --------------------------------------------------- * 0.01 861102 Initial coding -GP * */ #define VSUP 129 /* Up Arrow */ #define VSDN 130 /* Down Arrow */ #define VSRT 131 /* Right Arrow */ #define VSLT 132 /* Left Arrow */ #define VSK0 133 /* Keypad 0 */ #define VSK1 134 /* Keypad 1 */ #define VSK2 135 /* Keypad 2 */ #define VSK3 136 /* Keypad 3 */ #define VSK4 137 /* Keypad 4 */ #define VSK5 138 /* Keypad 5 */ #define VSK6 139 /* Keypad 6 */ #define VSK7 140 /* Keypad 7 */ #define VSK8 141 /* Keypad 8 */ #define VSK9 142 /* Keypad 9 */ #define VSKC 143 /* Keypad , */ #define VSKM 144 /* Keypad - */ #define VSKP 145 /* Keypad . */ #define VSKE 146 /* Keypad Enter */ #define VSF1 147 /* Function 1 */ #define VSF2 148 /* Function 2 */ #define VSF3 149 /* Function 3 */ #define VSF4 150 /* Function 4 */ #ifdef VSMASTER char VSIkpxlate[2][23] = { "ABCD0123456789,-.\15PQRS", "ABCDpqrstuvwxylmnMPQRS" }; #else extern char *VSIkpxlate; #endif /* * Definition of attribute bits in the Virtual Screen * * 0 - Bold * 1 - * 2 - * 3 - Underline * 4 - Blink * 5 - * 6 - Reverse * 7 - Graphics character set * */ #define VSisbold(x) (x & 0x01) #define VSisundl(x) (x & 0x08) #define VSisblnk(x) (x & 0x10) #define VSisrev(x) (x & 0x40) #define VSisgrph(x) (x & 0x80) #define VSinattr(x) (x & 0xd9) #define VSgraph(x) (x | 0x80) #define VSnotgraph(x) (x & 0x7F) #define ALTS 0xsource/hostform.h 644 144 13 11002 4267454302 7434 /* * Host and local machine configuration information. * **************************************************************************** * * * NCSA Telnet for the PC * * by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * * This program is in the public domain. * * * **************************************************************************** */ /* * Linked list of structures which describe other machines. * Arranged one per session with unique session names. */ struct machinfo { unsigned char *sname, /* pointer to name of session */ *hname, /* pointer to name of that machine */ *font, /* font name, if we can do it */ hostip[4], /* IP number of this machine */ gateway, /* gateway preference, start with 1 */ nameserv, /* nameserver preference, start with 1 */ bksp, /* backspace value */ halfdup, /* half duplex required */ crmap, /* Strange Berkeley 4.3 CR mode needed */ vtwrap, /* flag on when need wrap mode */ vtwidth; /* how wide screen should be for this session */ int clearsave, /* whether to save cleared lines */ fsize, /* font size in points */ nfcolor[3], /* normal foreground */ nbcolor[3], /* normal background */ bfcolor[3], /* blink */ bbcolor[3], ufcolor[3], /* underline */ ubcolor[3], mno, /* machine number for reference */ mstat, /* status of this machine entry */ bkscroll, /* how many lines to save */ retrans, /* initial retrans timeout */ conto, /* time out in seconds to wait for connect */ window, /* window, will be checked against buffers */ maxseg, /* maximum receivable segment size */ mtu; /* maximum transfer unit MTU (out) */ struct machinfo *next; /* surprise, its a linked list! */ }; struct machinfo *Sgethost(),*Shostlook(),*Slooknum(),*Slookip(),*Smadd(); /* * status fields for mstat, what do we know about that machine? */ #define NOIP 1 /* we don't have IP number */ #define UDPDOM 3 /* there is a UDP request pending on it */ /* The next 20 numbers are reserved for UDPDOM */ #define HAVEIP 50 /* at least we have the # */ #define HFILE 70 /* we have IP number from host file */ #define DOM 71 /* we have an IP number from DOMAIN */ #define FROMKIP 72 /* have IP# from KIP server */ /* * Configuration information which * the calling program may want to obtain from the hosts file. * The calling program should include hostform.h and call * Sgetconfig(cp) * struct config *cp; * which will copy the information to the user's data structure. */ struct config { unsigned char netmask[4], /* subnetting mask being used */ havemask, /* do we have a netmask? */ irqnum, /* which hardware interrupt */ myipnum[4], /* what is my IP #? */ me[32], /* my name description (first 30 chars) */ color[3], /* default colors to use */ hw[10], /* hardware type for network */ video[10], /* video graphics hardware available */ bios, /* flag, do we want to use BIOS for screen access */ tek, /* flag, enable tektronix graphics */ ftp, /* flag, enable ftp server */ rcp, /* flag, enable rcp server */ comkeys, /* flag, commandkeys=yes */ *termtype, /* terminal type specification */ *zone, /* AppleTalk zone for KIP NBP */ *defdom, /* default domain */ *capture, /* pointer to where the capture file name is */ *pass, /* pointer to where the password file name is */ *hpfile, /* HP file name */ *psfile, /* PS file name */ *tekfile; /* tek file name */ int nstype, /* nameserver = 1-domain 2-IEN116 */ domto, /* time out for UDP domain request */ ndom, /* number of retries for domain requests */ timesl, /* time slice */ address, /* segment address */ ioaddr; /* I/O address */ }; source/whatami.h 644 144 13 5151 4267454302 7215 /* * Whatami.h * Machine specific information for NCSA Telnet TCP/IP kernel **************************************************************************** * * * * * NCSA Telnet * * by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * Defines for TCP/IP library, are you a Mac or a PC? */ #ifndef MAC #define PC 1 #define ETHER 1 #define UB 1 #endif #ifndef ETHER #define ATALK 1 #endif /* * Defines which have to do with Ethernet addressing versus Appletalk * addressing. Ethernet has 6 bytes of hardware address, ATALK has 4 */ #ifdef ETHER #define DADDLEN 6 #define WINDOWSIZE 4096 #define TSENDSIZE 512 #define DEFWINDOW 1024 #define DEFSEG 1024 #define TMAXSIZE 1024 #define UMAXLEN 1024 #define ICMPMAX 300 #else /* define for AppleTalk */ #define DADDLEN 4 #define WINDOWSIZE 4096 #define TSENDSIZE 512 #define DEFWINDOW 512 #define DEFSEG 512 #define TMAXSIZE 512 #define UMAXLEN 512 #define ICMPMAX 300 #endif #ifdef PC /* * define length of an integer - can be 16 or 32, we need to know which */ typedef char int8; typedef unsigned char uint8; typedef int int16; typedef long int int32; typedef unsigned int uint16; typedef unsigned int uint; typedef unsigned long int uint32; #define TICKSPERSEC 18 #define SMINRTO 5 #define WRAPTIME 86400L /* in seconds, only for PC */ #define NPORTS 30 #define CONNWAITTIME 20 /* default contime in seconds */ uint32 longswap(); #define NFDEF {2,0,0} #define NBDEF {0,0,0} #define BFDEF {0,0,0} #define BBDEF {2,0,0} #define UFDEF {1,0,0} #define UBDEF {0,0,0} #else #define TICKSPERSEC 60 #define SMINRTO 25 #define NPORTS 30 #define CONNWAITTIME 20 /* default contime in seconds */ #define longswap(x) x #define intswap(x) x #define movebytes(x,y,z) movmem(y,x,z) #define movenbytes(x,y,z) movmem(y,x,z) #define n_putchar(x) putchar(x) #define n_puts(x) putln(x) #define NPORTS 30 #endif #include "netevent.h" source/netevent.h 644 144 13 3442 4267454303 7415 /* * Events for event processing in NCSA Telnet. * Used for netgetevent(). */ #define USERCLASS 1 /* the user program will accept these events */ #define ICMPCLASS 2 /* ICMP in netsleep will look for these */ #define ERRCLASS 4 /* the user may or may not read these error messages */ #define SCLASS 8 /* the background server will take these */ #define CONCLASS 0x10 /* the application manages connections with these */ #define ERR1 1 /* an error message is waiting, ERRCLASS */ #define IREDIR 1 /* ICMP redirect, ICMPCLASS */ #define CONOPEN 1 /* connection has opened, CONCLASS */ #define CONDATA 2 /* there is data available on this connection */ #define CONCLOSE 3 /* the other side has closed its side of the connection */ #define CONFAIL 4 /* connection open attempt has failed */ #define UDPDATA 1 /* UDP data has arrived on listening port, USERCLASS */ #define DOMOK 2 /* domain name ready */ #define DOMFAIL 3 /* domain name lookup failed */ #define FTPCOPEN 20 /* FTP command connection has opened */ #define FTPCLOSE 21 /* FTP command connection has closed */ #define FTPBEGIN 22 /* FTP transfer beginning, dat =1 for get, 0 for put */ #define FTPEND 23 /* FTP transfer ending */ #define FTPLIST 24 /* FTP file listing taking place */ #define FTPUSER 25 /* FTP user name has been entered */ #define FTPPWOK 26 /* FTP password verified */ #define FTPPWNO 27 /* FTP password failed */ #define RCPBEGIN 30 /* RCP beginning */ #define RCPEND 31 /* RCP ending */ #define UDPTO 1 /* UDP request from DOMAIN timed out, SCLASS */ #define FTPACT 2 /* FTP transfer is active, keep sending */ #define TCPTO 3 /* TCP for DOMAIN timed out */ #define RCPACT 4 /* rcp is active, needs CPU time */ #define RETRYCON 5 /* retry connection packet, might be lost */ failed */ #define UDPDATA 1 /* UDP data has arrived on listening port, USERCLASS */ #define DOMOK 2 /* domain name ready */ #define DOMFAIL 3 /* domain name lookup failed */ #define FTPCOPEN 20 /* FTP command connesource/windat.h 644 144 13 5122 4267454303 7050 /* * Windat -- window data structures for NCSA Telnet **************************************************************************** * * * * * NCSA Telnet * * by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * * * **************************************************************************** * Tim Krauskopf * * structure to save capabilities of windows, in addition to the * virtual vt102 screen driver. */ /* * terminal modes, can be changed by external program * VTEK is a VT terminal that can accept graphics * TEK is a tek graphics mode for a VT * VT is a VT-only type, cannot be changed to graphics mode */ #define DUMBTYPE 2 #define VTEKTYPE 1 #define TEKTYPE 4 #define VTTYPE 3 #define RASTYPE 5 #define NCOLORS 4 struct twin { unsigned short colors[NCOLORS]; unsigned char mname[16], /* name of the machine connected to */ linemode[82]; /* line mode buffer for session */ int pnum, /* port number associated */ vs, /* virtual screen associated with it */ telstate, /* telnet state for this connection */ termstate, /* terminal type for this connection */ teks, /* tektronix window number */ binary, /* negotiate for binary traffic */ igoahead, /* negotiation for suppress go-ahead */ ugoahead, /* neg. for his suppress go-ahead */ echo, /* line mode or echo mode? */ halfdup, /* half duplex mode overrides line mode */ crfollow, /* what is supposed to follow a CR? NUL or LF? */ capon, /* does this session own a capture file? */ sstat, /* stat box on screen */ bksp, /* what keycode for backspace ?*/ del; /* for delete? */ FILE *capfp; struct twin *next,*prev; }; #ifdef WINMASTER struct twin *screens[30],*current=NULL,*console,*curdisp=NULL, *wins[NPORTS]; #else extern struct twin *screens[30],*current,*console,*curdisp, *wins[NPORTS]; #endif #include "netevent.h" source/protocol.h 644 144 13 25234 4267454305 7453 /* * Protocol structures for network communication * **************************************************************************** * * * part of: * * TCP/IP kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * * This file contains the structure definitions for each type of * protocol that this program wishes to handle. A companion file, * 'protinit.c' initializes sample versions of each type of header, * improving the efficiency of sending packets with constants in most * of the fields. * */ #include "whatami.h" /************************************************************************/ /* Ethernet frames * All Ethernet transmissions should use this Ethernet header which * denotes what type of packet is being sent. * * The header is 14 bytes. The first 6 bytes are the target's hardware * Ethernet address, the second 6 are the sender's hardware address and * the last two bytes are the packet type. Some packet type definitions * are included here. * * the two-byte packet type is byte-swapped, PC is lo-hi, Ether is hi-lo */ #ifdef PC #define EXNS 0x0006 /* probably need swapping */ #define EIP 0x0008 #define EARP 0x0608 #define ERARP 0x3580 /* I guess this is RARP */ #define ECHAOS 0x0408 #else #define EXNS 0x0600 /* these don't need swapping */ #define EIP 0x0800 #define EARP 0x0806 #define ERARP 0x8035 #define ECHAOS 0x0804 #endif struct ether { uint8 dest[DADDLEN], /* where the packet is going */ me[DADDLEN]; /* who am i to send this packet */ uint16 type; /* Ethernet packet type */ }; typedef struct ether DLAYER; /*************************************************************************/ /* Dave Plummer's Address Resolution Protocol (ARP) (RFC-826) and * Finlayson, Mann, Mogul and Theimer's Reverse ARP packets. * * Note that the 2 byte ints are byte-swapped. The protocols calls for * in-order bytes, and the PC is lo-hi ordered. * */ #define RARPR 0x0004 /* RARP reply, from host, needs swap */ #define RARPQ 0x0003 /* RARP request, needs swapping */ #define ARPREP 0x0002 /* reply, byte swapped when used */ #define ARPREQ 0x0001 /* request, byte-swapped when used */ #define ARPPRO 0x0800 /* IP protocol, needs swapping */ #define HTYPE 0x0001 /* Ethernet hardware type, needs swapping */ struct plummer { DLAYER d; /* data link layer packet header */ uint16 hrd, /* hardware type, Ethernet = 1 */ pro; /* protocol type to resolve for */ uint8 hln, /* byte length of hardware addr = 6 for ETNET */ pln; /* byte length of protocol = 4 for IP */ uint16 op; /* opcode, request = 1, reply = 2, RARP = 3,4 */ uint8 sha[DADDLEN], spa[4], tha[DADDLEN], tpa[4]; /* * the final four fields (contained in 'rest') are: * sender hardware address: sha hln bytes * sender protocol address: spa pln bytes * target hardware address: tha hln bytes * target protocol address: tpa pln bytes */ }; typedef struct plummer ARPKT; /***********************************************************************/ /* ARP cache * Data structure for saving low-level information until needed */ struct acache { uint8 hrd[DADDLEN], /* hardware address for this IP address */ ip[4], /* the IP # in question */ gate; /* is this a gateway? */ int32 tm; /* time information */ }; /***********************************************************************/ /* Internet protocol * */ struct iph { uint8 versionandhdrlen; /* I prefer to OR them myself */ /* each half is four bits */ uint8 service; /* type of service for IP */ uint16 tlen, /* total length of IP packet */ ident, /* these are all BYTE-SWAPPED! */ frags; /* combination of flags and value */ uint8 ttl, /* time to live */ protocol; /* higher level protocol type */ uint16 check; /* header checksum, byte-swapped */ uint8 ipsource[4], /* IP addresses */ ipdest[4]; }; typedef struct iph IPLAYER; /* * full IP packet, with data and ip header */ struct ip { DLAYER d; IPLAYER i; union { uint8 data[536]; /* largest recommended, may include options */ uint8 options[40]; } x; }; typedef struct ip IPKT; #define PROTUDP 17 #define PROTTCP 6 /* standard protocol types for IP */ #define PROTICMP 1 /************************************************************************/ /* ICMP packet * all of them are of a similar form, some generic fields are spec'd here. */ struct icmph { uint8 type, /* ICMP type field */ code; /* ICMP code field */ uint16 check, /* checksum */ part1,part2; /* depends on type and code */ }; typedef struct icmph ICMPLAYER; struct icmp { DLAYER d; IPLAYER i; ICMPLAYER c; uint8 data[ICMPMAX]; }; typedef struct icmp ICMPKT; /**************************************************************************/ /* TCP protocol * define both headers required and create a data type for a typical * outgoing TCP packet (with IP header) * * Note: So far, there is no way to handle IP options fields * which are associated with a TCP packet. They are mutually exclusive * for both receiving and sending. Support may be added later. * * The tcph and iph structures can be included in many different types of * arbitrary data structures and will be the basis for generic send and * receive subroutines later. For now, the packet structures are optimized * for packets with no options fields. (seems to be almost all of them from * what I've observed. */ struct tcph { uint16 source,dest; /* TCP port numbers, all byte-swapped */ uint32 seq,ack; /* sequence, ACK numbers */ uint8 hlen, /* length of TCP header in 4 byte words */ flags; /* flag fields */ uint16 window, /* advertised window, byte-swapped */ check, /* TCP checksum of whole packet */ urgent; /* urgent pointer, when flag is set */ }; typedef struct tcph TCPLAYER; /* * used for computing checksums in TCP */ struct pseudotcp { uint8 source[4],dest[4], /* IP #'s for source,dest */ z,proto; /* zero and protocol number */ uint16 tcplen; /* byte-swapped length field */ }; struct tcp { DLAYER d; IPLAYER i; TCPLAYER t; union { uint8 options[40]; /* not very likely, except on SYN */ uint8 data[TMAXSIZE]; /* largest TCP data we will use */ } x; }; typedef struct tcp TCPKT; /* * flag field definitions, first two bits undefined */ #define TURG 0x20 #define TACK 0x10 #define TPUSH 0x08 #define TRESET 0x04 #define TSYN 0x02 #define TFIN 0x01 /*************************************************************************/ /* TCP queuing * data types for all TCP queuing operations * Each open port will have one of these structures assigned to it. */ struct window { uint32 nxt, /* sequence number, not byte-swapped */ ack; /* what the other machine acked */ int32 lasttime; /* (signed) used for timeout checking */ uint8 where[WINDOWSIZE], /* storage for queue */ *endbuf, /* set to end of queue */ *base, /* where useful data is in queue */ *endlim, /* first spot in queue to add more data */ push; /* flag for TCP push */ uint size, /* size of window advertised */ port, /* port numbers from one host or another */ contain; /* how many bytes in queue? */ }; struct port { struct window in,out; TCPKT tcpout; /* pre-initialized as much as possible */ uint8 state; /* connection state */ struct pseudotcp tcps; /* pseudo-tcp for checksumming */ int credit, /* choked-down window for fast hosts */ sendsize, /* MTU value for this connection */ rto; /* retrans timeout */ }; /*************************************************************************/ /* TCP states * each connection has an associated state in the connection flow. * the order of the states now matters, those less than a certain * number are the "inactive" states. */ #define SCLOSED 1 #define SLISTEN 2 #define STWAIT 3 #define SSYNR 4 #define SSYNS 5 #define SEST 6 #define SCWAIT 10 #define SFW1 7 #define SFW2 8 #define SCLOSING 9 #define SLAST 11 /* * services which we will want to use */ #define HFTP 21 #define HTELNET 23 #define HNAME 42 #define HSUNRPC 111 #define HPRINTER 515 /*************************************************************************/ /* UDP * User Datagram Protocol * Each packet is an independent datagram, no sequencing information * * UDP uses the identical checksum to TCP */ struct udph { uint16 source,dest; /* port numbers, all byte-swapped */ uint16 length, /* length of packet, including hdr */ check; /* TCP checksum of whole packet */ }; typedef struct udph UDPLAYER; struct udp { DLAYER d; IPLAYER i; UDPLAYER u; uint8 data[UMAXLEN]; /* largest UDP data we will use */ }; typedef struct udp UDPKT; struct uport { UDPKT udpout; struct pseudotcp tcps; /* pseudo-tcp for checksumming */ uint16 listen, /* what port should this one listen to? */ length; /* how much data arrived in last packet? */ uint8 data[UMAXLEN], /* incoming, last datagram of that type */ who[4], /* who sent it to me ? */ stale; /* have we read this packet yet? */ }; /*************************************************************************/ /* event queue * records what happens, especially errors, and keeps them for any * routines that poll, looking for what happened. * Eight event classes are masked in the event class byte. * There can be 256 event types per class. * The data field is handled differently by each event type. */ struct eq { uint8 eclass, /* class, defined in netevent.h */ event; /* which event */ int next, /* ordering for events in queue */ idata; /* integer data, if you feel like it */ }; /* * events which can occur and be placed into the event queue */ #define NEVENTS 50 /* classes defined in netevent.h */ source/data.h 644 144 13 10010 4267454305 6505 /* * data.h * Declarations of global variables for TCP/IP libraries * **************************************************************************** * * * * * NCSA Telnet * * by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * */ /* * Start with declarations that tell the difference between PC and other * computers */ #ifdef PC #include "pcdefs.h" #else #include "macdefs.h" #endif #ifdef MASTERDEF unsigned char us[] = {"National Center for Supercomputing Applications -- TCP/IP by Tim Krauskopf"}, nnmyaddr[DADDLEN], /* my ethernet hardware address */ broadaddr[DADDLEN], /* the broadcast address */ nnipnum[4], /* my ip number */ nnredir = 0, /* flag indicating need for redirect */ nnicmpsave[4], /* address for icmp redirect */ nnicmpnew[4], /* new gateway from icmp redirect */ nnmask[4] = {0,0,0,0}, /* the default subnet mask */ nnamask[4] = {255,0,0,0}, /* class A mask */ nnbmask[4] = {255,255,0,0}, /* class B mask */ nncmask[4] = {255,255,255,0}, /* class C mask */ broadip[4] = {0xff,0xff,0xff,0xff}; int nnipident /* ident field of outgoing ip packets */ =1, nnefirst /* first entry in event q */ =0, nnelast /* last entry in event q */ =0, nnefree /* free list for event q */ =0, nnemac /* Macintosh is using direct EtherTalk */ =0, nndto /* dlayertimeout */ =DLAYTIMEOUT, nnfromport /* can force a port number selection */ = 0, nncredit = CREDIT, /* limited window in some cases */ nnsegsize = MAXSEG; /* maximum segment size (intswapped) */ struct port *portlist[NPORTS]; /* allocate like iobuffers in UNIX */ struct uport ulist; /* buffer for UDP */ struct pseudotcp tcps; /* for checksums */ struct acache arpc[CACHELEN]; /* cache for hardware addresses */ struct eq nnq[NEVENTS]; /* event queue */ ARPKT arp; DLAYER blankd; IPKT blankip; /*ICMPKT blankicmp;*/ #else extern unsigned char nnmyaddr[DADDLEN], /* my ethernet hardware address */ broadaddr[DADDLEN] /* the broadcast address */, broadip[4], nnipnum[4], nnredir, /* flag indicating need for redirect */ nnicmpsave[4], /* address for icmp redirect */ nnicmpnew[4], /* new gateway from icmp redirect */ nnmask[4], nnamask[4], nnbmask[4], nncmask[4]; extern int nnipident /* ident field of ip */, nnefirst /* first entry in event q */, nnelast /* last entry in event q */, nndto /* dlayertimeout */, nnefree, nnemac, /* Macintosh is using direct EtherTalk */ nnfromport, /* can force a port number selection */ nncredit, nnsegsize; /* maximum segment size */ extern struct port *portlist[NPORTS]; /* allocate like iobuffers in UNIX */ extern struct uport ulist; /* buffer for UDP */ extern struct pseudotcp tcps; /* for checksums */ extern struct acache arpc[CACHELEN]; /* cache for hardware addresses */ extern struct eq nnq[NEVENTS]; /* event queue */ extern ARPKT arp; extern DLAYER blankd; extern IPKT blankip; /*extern ICMPKT blankicmp;*/ #endif /* * defines for types for functions, global to everyone */ char *nbgets(),*malloc(); uint8 *getdlayer(),*netdlayer(); char *neterrstring(); /* some more static data for driver */ /* * defines of constants and macros that everyone needs to know */ #define nnerror(A) netposterr(A) uint8 options[40]; /* not very likely, except on SYN */ uint8 data[TMAXSIZE]; /* largest TCP data we will use */ } x; }; typedef struct tcp TCPKT; /* * flag field definitions, first two bits undefined */ #define TURG 0x20 #define TACK 0x10 #define TPUSH 0x08 #define TRESET 0x04 #define TSYN 0x02 #define TFIN 0x01 /*************************************************************************/ /* TCP queuing * data types for all TCP queuing operations * Each open port will havesource/pcdefs.h 644 144 13 1661 4267454306 7035 /* * defines that are only applicable to the IBM PC/ PC-DOS environment * other files will take this one's place for other machines */ /* * hardware address for Ethernet broadcast address (used for ARP) */ #ifdef MASTERDEF unsigned char bseed[] = {0xff,0xff,0xff,0xff,0xff,0xff}, raw[17000]; #else extern unsigned char bseed[],raw[]; #endif /* * timing information is machine dependent */ long n_clicks(); #define time(A) n_clicks() #define movenbytes(A,B,C) movebytes((A),(B),(C)) /* * timeout for response to ARP packet for Ethernet */ /* was 15 */ #define DLAYTIMEOUT 4 /* * how often to poke a TCP connection to keep it alive and make * sure other side hasn't crashed. (poke) in 1/18ths sec * And, timeout interval */ #define POKEINTERVAL 3000 #define MAXRTO 100 #define MINRTO 5 #define ARPTO 20 #define CACHETO 7000 #define WAITTIME 35 #define LASTTIME 4000 #define CACHELEN 10 #define MAXSEG 1024 #define CREDIT 4096 source/tools.c 644 144 13 34777 4267454312 6757 /* * TOOLS.C * **************************************************************************** * * * part of: * * TCP/IP kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * * Portions of the driver code that are not specific to a particular protocol * */ #include "stdio.h" #include "protocol.h" #include "data.h" /************************************************************************/ /* netsleep * sleep, while demuxing packets, so we don't miss anything * */ netsleep(n) int n; { int i,nmux,redir; int32 t,gt,start; struct port *p; uint8 *pc; redir = 0; start = time(NULL); if (n) t = start + n*TICKSPERSEC; else t = start; do { nmux = demux(1); /* demux all packets */ /* * if there were packets in the incoming packet buffer, then more might * have arrived while we were processing them. This gives absolute priority * to packets coming in from the network. */ if (nmux) continue; /* * Check for any ICMP redirect events. */ if (IREDIR == netgetevent(ICMPCLASS,&i,&i)) redir = 1; /* * Check each port to see if action is necessary. * This now sends all Ack packets, due to p->lasttime being set to 0L. * Waiting for nmux == 0 for sending ACKs makes sure that the network * has a much higher priority and reduces the number of unnecessary ACKs. */ gt = time(NULL); for (i=0; i < NPORTS; i++) { p = portlist[i]; if ((p != NULL) && (p->state > SLISTEN)) { if (!p->out.lasttime) transq(p); /* takes care of all ACKs */ else if ((p->out.contain > 0) || (p->state > SEST)) { /* * if a retransmission timeout occurs, exponential back-off. * This number returns toward the correct value by the RTT measurement * code in ackcheck. * * fix: 5/12/88, if timer was at MAXRTO, transq didn't get hit - TK */ if ((p->out.lasttime + p->rto < gt)) { if (p->rto < MAXRTO) p->rto <<= 1; /* double it */ transq(p); } } if ((p->out.lasttime + POKEINTERVAL < gt) && (p->state == SEST)) transq(p); /* * check to see if ICMP redirection occurred and needs servicing. * If it needs servicing, try to get the new hardware address for the new * gateway. If getdlayer fails, we assume an ARP was sent, another ICMP * redirect will occur, this routine will reactivate, and then the hardware * address will be available in the cache. * Check all ports to see if they match the redirected address. */ if (redir && comparen(p->tcpout.i.ipdest,nnicmpsave,4)) { pc = getdlayer(nnicmpnew); if (pc != NULL) movebytes(p->tcpout.d.dest,pc,DADDLEN); } } } redir = 0; /* reset flag for next demux */ } while ((t > time(NULL)) /* done yet? */ && (time(NULL) >= start)); /* allow for wraparound of timer */ return(nmux); /* will demux once, even for sleep(0) */ } /***************************************************************************/ /* enqueue * add something to a TCP queue. Used by both 'write()' and tcpinterpret * WINDOWSIZE is the size limitation of the advertised window. */ enqueue(wind,buffer,nbytes) struct window *wind; char *buffer; int nbytes; { int i; i = WINDOWSIZE - wind->contain; if (i <= 0 || nbytes == 0) return(0); /* no room at the inn */ if (nbytes > i) nbytes = i; i = wind->where - wind->endlim; /* room at end */ i += WINDOWSIZE; if (i < nbytes) { movebytes(wind->endlim,buffer,i); movebytes(wind->where,(char *)(buffer+i),nbytes-i); wind->endlim = wind->where + nbytes - i; } else { movebytes(wind->endlim,buffer,nbytes); /* fits in one chunk */ wind->endlim += nbytes; } wind->contain += nbytes; /* more stuff here */ return(nbytes); } /*************************************************************************/ /* dequeue * used by read, this copies data out of the queue and then * deallocates it from the queue. * cpqueue and rmqueue are very similar and are to be used by tcpsend * to store unacknowledged data. * * returns number of bytes copied from the queue */ dequeue(wind,buffer,nbytes) struct window *wind; char *buffer; int nbytes; /* maximum number to copy out */ { int i; if (wind->contain == 0) return(0); if (wind->contain < nbytes) nbytes = wind->contain; i = wind->endbuf - wind->base; if (i <= nbytes) { movebytes(buffer,wind->base,i); movebytes((char *)(buffer+i),wind->where,nbytes-i); wind->base = wind->where + nbytes-i; } else { movebytes( buffer, wind->base, nbytes); if (wind->contain == nbytes) wind->base = wind->endlim = wind->where; else wind->base += nbytes; } wind->contain -= nbytes; return(nbytes); } #ifdef notneeded /**************************************************************************/ /* cpqueue * does the same thing as dequeue, but does not deallocate the data * used when transmitting TCP data. When the data is ACKed, then * rmqueue is called to deallocate the correct amount of data. */ cpqueue(wind,buffer,nbytes) struct window *wind; char *buffer; int nbytes; /* maximum number to copy out */ { int i; if (wind->contain == 0) return(0); if (wind->contain < nbytes) nbytes = wind->contain; i = wind->endbuf - wind->base; if (i < nbytes) { movebytes(buffer,wind->base,i); movebytes((char *)(buffer+i),wind->where,nbytes-i); } else movebytes( buffer, wind->base, nbytes); return(nbytes); } #endif /**************************************************************************/ /* rmqueue * does the queue deallocation that is left out of cpqueue * * rmqueue of WINDOWSIZE or greater bytes will empty the queue */ rmqueue(wind,nbytes) struct window *wind; int nbytes; /* number to remove */ { int i; if (wind->contain < nbytes) nbytes = wind->contain; i = wind->endbuf - wind->base; if (i <= nbytes) wind->base = wind->where+nbytes-i; else { if (wind->contain == nbytes) wind->base = wind->endlim = wind->where; else wind->base += nbytes; } wind->contain -= nbytes; return(nbytes); } /************************************************************************/ /* transq * * Needed for TCP, not as general as cpqueue, * but is required for efficient transmit of the whole window. * * Transmit the entire queue (window) to the other host without expecting * any sort of acknowledgement. * */ transq(prt) struct port *prt; { uint bites; int i,j,n; struct window *wind; uint32 saveseq; uint8 *endb,*whereb,*baseb; if (prt == NULL) { nnerror(406); /* NULL port for trans */ return(-1); } wind = &prt->out; /* * find out how many bytes the other side will allow us to send (window) */ bites = wind->size; if (wind->contain < bites) bites = wind->contain; /* * set up the tcp packet for this, ACK field is same for all packets */ prt->tcpout.t.ack = longswap(prt->in.nxt); /* * any more flags should be set? */ if (wind->push) /* is push indicator on? */ prt->tcpout.t.flags |= TPUSH; if ((bites <= 0) || prt->state != SEST) { /* if no data to send . . . */ tcpsend(prt,0); /* just a retransmission or ACK */ return(0); } /* * we have data to send, get the correct sequence #'s * To be really real, we should check wraparound sequence # in the loop. */ saveseq = wind->nxt; whereb = wind->where; endb = wind->endbuf; baseb = wind->base; /* * in a loop, transmit the entire queue of data */ for (i=0 ; i < bites; i += prt->sendsize) { n = prt->sendsize; if (i + n > bites) n = bites - i; j = endb - baseb; if (j < n) { movebytes(prt->tcpout.x.data,baseb,j); movebytes((char *)(prt->tcpout.x.data+j),whereb,n-j); baseb = whereb + n-j; } else { movebytes( prt->tcpout.x.data, baseb, n); baseb += n; } tcpsend(prt,n); /* send it */ wind->nxt += n; } wind->nxt = saveseq; /* get back first seq # */ return(0); } /************************************************************************/ /* comparen * Take n bytes and return identical (true=1) or not identical (false=0) * * Could be written in assembler for improved performance */ comparen(s1,s2,n) uint8 *s1,*s2; register int n; { while (n--) if (*s1++ != *s2++) return(0); return(1); } /************************************************************************/ /* netposterr * place an error into the event q * Takes the error number and puts it into the error structure */ netposterr(num) int num; { if (netputevent(ERRCLASS,ERR1,num)) netputuev(ERRCLASS,ERR1,501); /* only if we lost an event */ } /***********************************************************************/ /* netgetevent * Retrieves the next event (and clears it) which matches bits in * the given mask. Returns the event number or -1 on no event present. * Also returns the exact class and the associated integer in reference * parameters. * * The way the queue works: * There is always a dummy record pointed to by nnelast. * When data is put into the queue, it goes into nnelast, then nnelast * looks around for another empty one to obtain. * It looks at nnefree first, then bumps one from nnefirst if necessary. * When data is retrieved, it is searched from nnefirst to nnelast. * Any freed record is appended to nnefree. */ netgetevent(mask,retclass,retint) uint8 mask; int *retclass,*retint; { int i,j; i = nnefirst; while (i != nnelast) { if (mask & nnq[i].eclass) { if (i == nnefirst) nnefirst = nnq[nnefirst].next; /* step nnefirst */ else nnq[j].next = nnq[i].next; /* bypass record i */ nnq[i].next = nnefree; nnefree = i; /* install in free list */ *retint = nnq[i].idata; *retclass = nnq[i].eclass; return(nnq[i].event); } j = i; i = nnq[i].next; } return(0); } /***********************************************************************/ /* netputevent * add an event to the queue. * Will probably get the memory for the entry from the free list. * Returns 0 if there was room, 1 if an event was lost. */ netputevent(class,what,dat) int class,what,dat; { int i; i = nnelast; nnq[i].eclass = class; /* put data in */ nnq[i].event = what; nnq[i].idata = dat; if (nnefree >= 0) { /* there is a spot in free list */ nnq[i].next = nnelast = nnefree; nnefree = nnq[nnefree].next; /* remove from free list */ return(0); } else { nnq[i].next = nnelast = nnefirst; nnefirst = nnq[nnefirst].next; /* lose oldest event */ return(1); } } /***************************************************************************/ /* netputuev * put a unique event into the queue * First searches the queue for like events */ netputuev(class,what,dat) int class,what,dat; { int i; i = nnefirst; while (i != nnelast) { if (nnq[i].idata == dat && nnq[i].event == what && nnq[i].eclass == class) return(0); i = nnq[i].next; } return(netputevent(class,what,dat)); } /************************************************************************/ /* neterrstring * returns the string associated with a particular error number * * error number is formatted %4d at the beginning of the string */ static char *errs[] = {" 0 Error unknown", " 100 Network jammed, probable break in wire", " 101 Could not initialize hardware level network driver", " 102 ERROR: The conflicting machine is using the same IP number", " 103 RARP request failed, an IP number is required", " 300 Bad IP checksum", " 301 IP packet not for me", " 302 IP packet with options received", " 303 IP: unknown higher layer protocol", " 304 IP: fragmented packet received, frags not supported", " 400 TCP: bad checksum", " 401 ACK invalid for TCP syn sent", " 403 TCP in unknown state", " 404 Invalid port for TCPsend", " 405 TCP connection reset by other host", " 406 Null port specified for ackandtrans", " 407 Packet received for invalid port -- reset sent", " 500 No internal TCP ports available", " 501 Warning: Event queue filled, probably non-fatal", " 504 Local host or gateway not responding", " 505 Memory allocation error, cannot open port", " 506 Not allowed to connect to broadcast address", " 507 Reset received: syn sent, host is refusing connection", " 600 ICMP: Echo reply", " 603 ICMP: Destination unreachable", " 604 ICMP: Source Quench", " 605 ICMP: Redirect, another gateway is more efficient", " 608 ICMP: Echo requested (ping requested)", " 611 ICMP: Time Exceeded on Packet", " 612 ICMP: Parameter problem in IP", " 613 ICMP: Timestamp request", " 614 ICMP: Timestamp reply", " 615 ICMP: Information request", " 616 ICMP: Information reply", " 699 ICMP: Checksum error", " 700 Bad UDP checksum", " 800 Domain: Name request to server failed", " 801 Domain: Using default domain", " 802 Domain: name does not exist", " 803 Domain: UDP name server did not resolve the name", " 804 Domain: name server failed, unknown reason", " 805 Host machine not in configuration file", " 806 Missing IP number, requires domain lookup", " 900 Session: Cannot find or open configuration file", " 901 Session: Cannot allocate memory for processing", " 902 Session: Invalid keyword in configuration file", " 903 Session: Element too long (>200), maybe missing quote", " 904 Session: Probable missing quote marks, a field must be on one line", " 905 Session: 'name' field required before other machine entries", " 906 Session: Syntax error, invalid IP number", " 907 Session: Syntax error, Subnet mask invalid", " 908 Session: Syntax error, IP address for this PC is invalid", ""}; static char errspace[80]; /* room for user-defined errors */ char *neterrstring(errno) int errno; { int i; char s[10]; if (errno < 0) return(errspace); sprintf(s,"%4d",errno); i = 0; do { if (!strncmp(errs[i],s,4)) return(errs[i]+5); /* pointer to error message */ i++; } while (*errs[i] || i > 100); /* until NULL found */ return(errs[0]+5); /* error unknown */ } asource/minitel.c 644 144 13 4700 4267454312 7217 /* * minitel * Example TCP/IP program for the NCSA TCP/IP kernel */ #include "stdio.h" #include "whatami.h" #include "hostform.h" struct machinfo *mp; char *neterrstring(),buf[256]; main(argc,argv) int argc; char *argv[]; { int i,cnt,ev,pnum,what,dat; char *errmsg; char c; puts("National Center for Supercomputing Applications"); puts("Mini-telnet example program"); puts("July 1987\n"); if (argc < 2) exit(1); if (Snetinit()) { /* call session initialization */ errhandle(); /* Snetinit() reads config.tel file */ exit(1); } mp = Sgethost(argv[1]); /* look up in hosts cache */ if (!mp) Sdomain(argv[1]); /* not in hosts, try domain */ else { if (0 > (pnum = Snetopen(mp,23))) { errhandle(); netshut(); exit(1); } } c = 0; do { if (kbhit()) { /* has key been pressed */ c = getch(); netwrite(pnum,&c,1); /* user input sent on connection */ } /* * get event from network, these two classes return all of the events * of user interest. */ ev = Sgetevent(USERCLASS | CONCLASS | ERRCLASS,&what,&dat); if (!ev) continue; if (what == ERRCLASS) { /* error event */ errmsg = neterrstring(dat); puts(errmsg); } else if (what == CONCLASS) { /* event of user interest */ switch (ev) { case CONOPEN: /* connection opened or closed */ netpush(dat); /* negotiation */ netwrite(dat,"\377\375\001\377\375\003\377\374\030",9); break; default: break; case CONDATA: /* data arrived for me */ cnt = netread(dat,buf,80); for (i=0; i 0) { for (i=choice-1; i= 0); passwrite(argv[1]); exit(0); } /****************************************************************************/ /* dochoice * prompt for a certain password */ dochoice(c) int c; { char *p; char pwd[256],ver[256]; strcpy(space,lines[c]); do { p = strchr(space,':'); if (!p) strcat(space,":"); } while (!p); /* make sure we get a : */ *p = '\0'; p++; sprintf(space2,"Enter new password for user: %s",space); n_puts(space2); noecho(pwd); n_puts("Verify password by entering again:"); noecho(ver); if (strcmp(pwd,ver)) { n_puts("Password not verified"); return(0); } Sencompass(pwd); /* take password */ sprintf(ver,"%s:%s",space,space2); lines[c] = malloc(strlen(ver)+1); strcpy(lines[c],ver); } noecho(s) char *s; { int c; do { c = n_getchar(); if (c > 31 && c < 128) *s++ = c; } while (c > 31 && c < 128); *s = '\0'; } /****************************************************************************/ /* passwrite * write them out */ passwrite(s) char *s; { int i; if (NULL == (fout = fopen(s,"w"))) { n_puts("Cannot open file to write passwords. "); exit(0); } for (i=0; i < nnames; i++) { fputs(lines[i],fout); fputs("\n",fout); } fclose(fout); } /****************************************************************************/ /* passlist * List the current file */ passlist(s) char *s; { if (NULL == (fin = fopen(s,"r"))) { n_puts("Starting new file."); return(0); } while (NULL != fgets(space,250,fin)) { space[strlen(space)-1] = '\0'; lines[nnames] = malloc(strlen(space)+1); strcpy(lines[nnames++],space); } fclose(fin); } /****************************************************************************/ /* Scompass * compute and check the encrypted password */ Sencompass(ps) char *ps; { int i,ck; char *p,c,*en; en = space2; ck = 0; p = ps; while (*p) /* checksum the string */ ck += *p++; c = ck; for (i=0; i<10; i++) { *en = (((*ps ^ c) | 32) & 127); /* XOR with checksum */ if (*ps) ps++; else c++; /* to hide length */ en++; } *en = 0; } 5 ICMP: Redirect, another gateway is more efficient", " 608 ICMP: Echo requested (ping requested)", " 611 ICMP: Time Exceeded on Packet", " 612 ICMP: Parameter prosource/dlayer.c 644 144 13 24365 4267454315 7072 /* * DLAYER * Hardware level routines, data link layer * **************************************************************************** * * * part of: * * TCP/IP kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** */ #include "stdio.h" #include "protocol.h" #include "data.h" /************************************************************************/ /* * Address Resolution Protocol handling. This can be looked at as * Ethernet-dependent, but the data structure can handle any ARP * hardware, with minor changes here. * */ replyarp(thardware,tipnum) uint8 *thardware,*tipnum; { uint8 *pc; movebytes(&arp.tha,thardware,DADDLEN); /* who this goes to */ movebytes(&arp.tpa,tipnum,4); /* requester's IP address */ arp.op = intswap(ARPREP); /* byte swapped reply opcode */ movebytes(arp.d.dest,thardware,DADDLEN); /* hardware place to send to */ dlayersend(&arp,sizeof(arp)); /* * check for conflicting IP number with your own */ if (comparen(tipnum,nnipnum,4)) { /* we are in trouble */ pc = neterrstring(-1); sprintf(pc,"Conflict with Ethernet hardware address: %2x:%2x:%2x:%2x:%2x:%2x", thardware[0],thardware[1],thardware[2],thardware[3],thardware[4],thardware[5]); netposterr(-1); netposterr(102); return(-3); } } /************************************************************************/ /* reqarp * put out an ARP request packet, doesn't wait for response */ reqarp(tipnum) uint8 *tipnum; { #ifdef MAC if (KIP) { if (0 < KIParp(tipnum,&arp.tha)) cacheupdate(tipnum, &arp.tha); return(0); } #endif MAC movebytes(&arp.tha,broadaddr,DADDLEN); movebytes(&arp.tpa,tipnum,4); /* put in IP address we want */ arp.op = intswap(ARPREQ); /* request packet */ movebytes(arp.d.dest,broadaddr,DADDLEN); /* send to everyone */ if (dlayersend(&arp,sizeof(arp))) return(1); /* error return */ return(0); } /************************************************************************/ /* interpret ARP packets * Look at incoming ARP packet and make required assessment of usefulness, * check to see if we requested this packet, clear all appropriate flags. */ arpinterpret(p) ARPKT *p; { /* * check packet's desired IP address translation to see if it wants * me to answer. */ if (p->op == intswap(ARPREQ) && (comparen(&p->tpa,nnipnum,4))) { cacheupdate(&p->spa,&p->sha); /* keep her address for me */ replyarp(&p->sha,&p->spa); /* proper reply */ return(0); } /* * Check for a RARP reply. If present, call netsetip() */ else if (p->op == intswap(RARPR) && (comparen(&p->tha,nnmyaddr,DADDLEN))) { movebytes(nnipnum,&p->tpa,4); return(0); } /* * Check for a reply that I probably asked for. */ if (comparen(&p->tpa,nnipnum,4)) { if (p->op == intswap(ARPREP) && p->hrd == intswap(HTYPE) && /* consistency checking */ p->hln == DADDLEN && p->pln == 4 ) { cacheupdate(&p->spa,&p->sha); return(0); } } return(1); } /*************************************************************************/ /* rarp * Send a rarp request to look up my IP number */ rarp() { /* * our other fields should already be loaded */ movebytes(&arp.tha,nnmyaddr,DADDLEN); /* address to look up (me) */ movebytes(&arp.sha,nnmyaddr,DADDLEN); /* address to look up (me) */ arp.op = intswap(RARPQ); /* request packet */ movebytes(arp.d.dest,broadaddr,DADDLEN); /* send to everyone */ arp.d.type = ERARP; if (dlayersend(&arp,sizeof(arp))) return(1); /* error return */ arp.d.type = EARP; /* set back for ARP to use */ return(0); } /*************************************************************************/ /* cacheupdate * We just received an ARP, or reply to ARP and need to add the information * to the cache. * * Reset arptime so that another machine may be ARPed. This timer keeps * ARPs from going out more than one a second unless we receive a reply. */ static int32 arptime=0L; cacheupdate(ipn,hrdn) uint8 *ipn,*hrdn; { int i,found; int32 timer; found = -1; /* * linear search to see if we already have this entry */ for (i=0; found < 0 && i < CACHELEN; i++) if (comparen(ipn,&arpc[i].ip,4)) found = i; /* * if that IP number is not already here, take the oldest entry. * If it is already here, update the info and reset the timer. * These were pre-initialized to 0, so if any are blank, they will be * taken first because they are faked to be oldest. */ if (found < 0) { timer = arpc[0].tm; found = 0; for (i=1; i < CACHELEN; i++) if (arpc[i].tm < timer && !arpc[i].gate) {/* exclude gateways */ found = i; timer = arpc[i].tm; } } /* * do the update to the cache */ movebytes(&arpc[found].hrd,hrdn,DADDLEN); movebytes(&arpc[found].ip,ipn,4); arpc[found].tm = time(NULL); arptime = 0L; /* reset, allow more arps */ return(found); } /*************************************************************************/ /* cachelook * look up information in the cache * returns the cache entry number for the IP number given. * Returns -1 on no valid entry, also if the entry present is too old. * * doarp is a flag for non-gateway requests which determines whether an * arp will be sent or not. */ cachelook(ipn,gate,doarp) uint8 *ipn; int gate,doarp; { int i,haveg; /* * First option, we are not looking for a gateway, but a host on our * local network. */ if (!gate) { for (i=0; i time(NULL)) return(i); /* * no valid entry, send an ARP */ if (time(NULL) >= arptime && doarp) { /* check time limit */ reqarp(ipn); /* put out a broadcast request */ arptime = time(NULL)+ARPTO; } return(-1); } else { /* * Second option, we need a gateway. * if there is a gateway with a current ARP, use it. * if not, arp all of the gateways and return an error. Next call will * probably catch the result of the ARP. */ haveg = 0; for (i=CACHELEN-1; i >= 0; i--) if (arpc[i].gate && arpc[i].tm + CACHETO > time(NULL)) return(i); if (time(NULL) >= arptime) { for (i=CACHELEN-1; i >= 0; i--) if (arpc[i].gate) { haveg = 1; reqarp(&arpc[i].ip); /* put out a broadcast request */ } if (!haveg) /* blind luck, try ARPing even for */ reqarp(ipn); /* a node not on our net. (proxy ARP)*/ arptime = time(NULL)+ARPTO; } return(-1); } } /***************************************************************************/ /* netdlayer * get data layer address for insertion into outgoing packets. * searches based on ip number. If it finds the address, ok, else . . . * * Checks to see if the address is on the same network. If it is, * then ARPs the machine to get address. Forces pause between sending * arps to guarantee not saturating network. * * If not on the same network, it needs the ether address of a * gateway. Searches the list of machines for a gateway flag. * Returns the first gateway found with an Ethernet address. * * Returns NULL if not here, or pointer to ether address if here. * If we don't have it, this also sends an ARP request so that the * next time we are called, the ARP reply may be here by then. * */ uint8 *netdlayer(tipnum) uint8 *tipnum; { int32 t; uint8 *pc; t = time(NULL) + nndto*TICKSPERSEC; /* some seconds time out */ pc = NULL; do { if (t <= time(NULL)) /* timed out */ return(NULL); pc = getdlayer(tipnum); netsleep(0); /* can't have deadlock */ } while (pc == NULL); return(pc); } /***************************************************************************/ /* netgetrarp * Look for a RARP response to arrive * wait for nndto seconds before returning failure. * If response arrives, return success. */ netgetrarp() { int32 t,tr; t = time(NULL) + nndto*TICKSPERSEC*3; /* some seconds time out */ tr = 0L; /* one second retry */ do { if (tr <= time(NULL)) { /* need retry? */ rarp(); tr = time(NULL) + TICKSPERSEC; } if (t <= time(NULL)) { /* timed out */ netposterr(103); return(-1); } netsleep(0); /* can't have deadlock */ } while (comparen(nnipnum,"RARP",4)); /* until RARP is served */ return(0); } /***************************************************************************/ /* getdlayer * check for the hardware address one time */ uint8 *getdlayer(tipnum) uint8 *tipnum; { int needgate,i; needgate = 0; /* * Check to see if we need to go through a gateway. * If the machine is on our network, then assume that we can send an ARP * to that machine, otherwise, send the ARP to the gateway. * * Uses internet standard subnet mask method, RFC950 * if subnets are not in use, netmask has been pre-set to the appropriate * network addressing mask. */ for (i=3; i >= 0; i--) if ((nnmask[i] & tipnum[i]) != (nnmask[i] & nnipnum[i])) needgate = 1; if (needgate && (0 <= (i = cachelook(tipnum,1,1)))) return(&arpc[i].hrd); if (!needgate && (0 <= (i = cachelook(tipnum,0,1)))) return(&arpc[i].hrd); return(NULL); } /***************************************************************************/ /* netsetgate * Establish an IP number to use as a gateway. * They are added in the order that they arrive and there is a limit on * the number of gateways equal to CACHELEN/2. * ARPs them as they are added so that the Cache will get pre-filled * with gateways. * * returns 0 if ok, -1 on error (full) */ netsetgate(ipn) uint8 *ipn; { int i; for (i=CACHELEN-1 ; i >= CACHELEN/2 ; i--) if (!arpc[i].gate) { arpc[i].gate = 1; movebytes(&arpc[i].ip,ipn,4); reqarp(ipn); return(0); } return(-1); } return(0); return(1); } /************************************************************************/ /* netposterr * place an error into the event q * Takes the error number and puts it into the error structure */ netposterr(num) int num; { if (netputevsource/user.c 644 144 13 34201 4267454316 6557 /* * USER.C * Network library interface routines * Generally called by the session layer * **************************************************************************** * * * part of: * * TCP/IP kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * Revisions: * 10/87 Initial source release, Tim Krauskopf * 2/88 typedef support for other compilers (TK) * */ #define MASTERDEF 1 #include "stdio.h" #include "protocol.h" #include "data.h" #define LOWWATER 600 /***************************************************************************/ /* netread * Read from a connection buffer into a user buffer. * Returns number of bytes read, < 0 on error * Never blocks, returns 0 when there are no bytes available. */ netread(pnum,buffer,n) int pnum,n; char *buffer; { int howmany,i; struct port *p; if (pnum < 0) /* check validity */ return(-2); if (NULL == (p = portlist[pnum])) return(-2); if (p->state != SEST) { /* foreign or netclose */ if (p->state == SCWAIT) { /* ready for me to close my side? */ if (!p->in.contain) { p->tcpout.t.flags = TFIN | TACK; tcpsend(p,0); p->state = SLAST; return(-1); } /* else, still data to be read */ } else return(-1); } howmany = dequeue(&p->in,buffer,n); /* read from tcp buffer */ i = p->in.size; /* how much before? */ p->in.size += howmany; /* increment leftover room */ if (i < LOWWATER && p->in.size >= LOWWATER) /* we passed mark */ p->out.lasttime = 0L; if (p->in.contain) /* if still data to be read */ netputuev(CONCLASS,CONDATA,pnum); /* don't forget it */ return(howmany); } /************************************************************************/ /* netwrite * write something into the output queue, netsleep routine will come * around and send the data, etc. * */ netwrite(pnum,buffer,n) int pnum,n; char *buffer; { int nsent,before; struct port *p; if (pnum < 0) return(-2); p = portlist[pnum]; if (p == NULL) return(-2); if (p->state != SEST) /* must be established connection */ return(-1); before = p->out.contain; nsent = enqueue(&p->out,buffer,n); if (!before) /* if this is something new, */ p->out.lasttime = 0L; /* cause timeout to be true */ return(nsent); } /**************************************************************************/ /* netpush * attempt to push the rest of the data from the queue * and then return whether the queue is empty or not (0 = empty) * returns the number of bytes in the queue. */ netpush(pnum) int pnum; { struct port *p; if (pnum < 0) return(-2); if (NULL == (p = portlist[pnum])) return(-2); p->out.push = 1; return(p->out.contain); } /**************************************************************************/ /* netqlen * return the number of bytes waiting to be read from the incoming queue. */ netqlen(pnum) int pnum; { if (portlist[pnum] == NULL) return(-2); return(portlist[pnum]->in.contain); } /**************************************************************************/ /* netroom() * return how much room is available in output buffer for a connection */ netroom(pnum) int pnum; { if (portlist[pnum] == NULL || portlist[pnum]->state != SEST) return(-2); return(WINDOWSIZE - portlist[pnum]->out.contain); } /**************************************************************************/ /* netsegsize and neterrchange and netsetip and netgetip * * set operating parameters to change them from the default values used. */ netsegsize(newsize) int newsize; { int i; i = nnsegsize; nnsegsize = newsize; return(i); } netquench(newcredit) int newcredit; { int i; i = nncredit; nncredit = newcredit; return(i); } netarptime(t) /* dlayer timeout in secs */ int t; { nndto = t; } netsetip(st) unsigned char *st; { /* * change all dependent locations relating to the IP number * don't worry about open connections, they must be closed by higher layer */ movebytes(nnipnum,st,4); movebytes(&arp.d.me,nnipnum,4); movebytes(&arp.spa,nnipnum,4); movebytes(&blankip.i.ipsource,nnipnum,4); movebytes(ulist.tcps.source,nnipnum,4); movebytes(ulist.udpout.i.ipsource,nnipnum,4); } netgetip(st) unsigned char *st; { movebytes(st,nnipnum,4); } netsetmask(st) unsigned char *st; { movebytes(nnmask,st,4); } netgetmask(st) unsigned char *st; { movebytes(st,nnmask,4); } netfromport(port) /* next "open" will use this port */ int16 port; { nnfromport = port; } /**************************************************************************/ /* netest? * is a particular session established yet? * Returns 0 if the connection is in the established state. */ netest(pn) int pn; { struct port *p; if (pn < 0 || pn > NPORTS) return(-2); if (NULL == (p = portlist[pn])) return(-2); if (p->state == SEST) return(0); else if (p->state == SCWAIT) { if (!p->in.contain) { p->tcpout.t.flags = TFIN | TACK; tcpsend(p,0); p->state = SLAST; return(-1); } else return(0); /* still more data to be read */ } return(-1); } /**************************************************************************/ /* netlisten * Listen to a TCP port number and make the connection automatically when * the SYN packet comes in. The TCP layer will notify the higher layers * with a CONOPEN event. Save the port number returned to refer to this * connection. * * usage: portnum = netlisten(service); * int service; * */ netlisten(serv) uint serv; { int pnum; struct port *prt; uint16 nn; pnum = makeport(); if (pnum < 0) return(-2); if (NULL == (prt = portlist[pnum])) return(-2); prt->in.port = serv; prt->out.port = 0; /* accept any outside port #*/ prt->in.lasttime = time(NULL); /* set time we started */ prt->state = SLISTEN; prt->credit = 512; /* default value until changed */ prt->tcpout.i.protocol = PROTTCP; prt->tcpout.t.source = intswap(serv); /* set service here too */ /* * install maximum segment size which will be sent out in the first * ACK-SYN packet */ prt->tcpout.x.options[0] = 2; prt->tcpout.x.options[1] = 4; /* install maximum segment size */ nn = intswap(nnsegsize); movebytes(&prt->tcpout.x.options[2],&nn,2); return(pnum); } /***********************************************************************/ /* netgetftp * Provides the information that ftp needs to open a stream back to the * originator of the command connection. The other side's IP number * and the port numbers to be used to calculate the default data connection * number. Returns values in an integer array for convenient use in * PORT commands. */ netgetftp(a,pnum) int a[]; int pnum; { struct port *p; uint i; p = portlist[pnum]; a[0] = p->tcpout.i.ipdest[0]; a[1] = p->tcpout.i.ipdest[1]; a[2] = p->tcpout.i.ipdest[2]; a[3] = p->tcpout.i.ipdest[3]; i = intswap(p->tcpout.t.source); a[4] = i >> 8; a[5] = i & 255; i = intswap(p->tcpout.t.dest); a[6] = i >> 8; a[7] = i & 255; } /**************************************************************************/ /* netopen * Netopen is a cheap way to open a connection without looking up any * machine information. Uses suitable default values for everything. */ netopen(s,tport) unsigned char *s; uint tport; { return(netxopen(s,tport,MINRTO,TSENDSIZE,DEFSEG,DEFWINDOW)); } /**************************************************************************/ /* netxopen * Open a network socket for the user. * */ netxopen(machine,service,rto,mtu,mseg,mwin) uint8 *machine; uint service,rto,mtu,mseg,mwin; /* unix service port number */ { struct port *p; int pnum,ret,i; uint8 *pc,*hiset; /* * check the IP number and don't allow broadcast addresses */ if (machine[3] == 255 || !machine[3]) { nnerror(506); return(-4); } netsleep(0); /* make sure no waiting packets */ pnum = makeport(); /* set up port structure and packets */ if (pnum < 0) return(-3); p = portlist[pnum]; /* create a new port */ /* * make a copy of the ip number that we are trying for */ movebytes(p->tcpout.i.ipdest,machine,4); movebytes(p->tcps.dest,machine,4); /* pseudo header needs it */ /* * get the hardware address for that host, or use the one for the gateway * all handled by 'netdlayer' by ARPs. */ pc = netdlayer(machine); /* we have ether? */ if (pc == NULL) { /* cannot connect to local machine */ nnerror(504); return(-2); } movebytes(p->tcpout.d.dest,pc,DADDLEN); /* load it up */ /* * Add in machine specific settings for performance tuning */ if (rto > MINRTO) p->rto = rto; /* starting retrans timeout */ if (mtu < TMAXSIZE) /* largest packet space we have */ p->sendsize = mtu; /* maximum transmit size for that computer */ if (mwin < WINDOWSIZE) /* buffer size is the limit */ p->credit = mwin; /* most data that we can receive at once */ #ifdef MAC if (nnemac) { #endif /* * quick check to see if someone else is using your IP number * Some boards receive their own broadcasts and cannot use this check. * The Mac will only use this check if it is using EtherTalk. */ i = cachelook(nnipnum,0,0); /* don't bother to ARP */ if (i >= 0) { /* if it is not -1, we are in trouble */ hiset = (uint8 *)&arpc[i].hrd; pc = neterrstring(-1); sprintf(pc,"Conflict with Ethernet hardware address: %2x:%2x:%2x:%2x:%2x:%2x", hiset[0],hiset[1],hiset[2],hiset[3],hiset[4],hiset[5]); nnerror(-1); nnerror(102); netclose(pnum); return(-3); } #ifdef MAC } #endif /* * make the connection, if you can, we will get an event notification later * if it connects. Timeouts must be done at a higher layer. */ ret = doconnect(pnum,service,mseg); return(ret); } /**********************************************************************/ doconnect(pnum,service,mseg) int service,pnum,mseg; { uint16 seg; struct port *p; p = portlist[pnum]; p->tcpout.i.protocol = PROTTCP; /* this will be TCP socket */ p->tcpout.t.dest = intswap(service); /* for example, telnet=23 */ p->out.port = service; /* service is same as port num*/ p->tcpout.t.flags = TSYN; /* want to start up sequence */ p->tcpout.t.ack = 0; /* beginning has no ACK */ p->state = SSYNS; /* syn sent */ /* * install maximum segment size which will be sent out in the first * ACK-SYN packet */ p->tcpout.x.options[0] = 2; p->tcpout.x.options[1] = 4; /* install maximum segment size */ seg = intswap(mseg); movebytes(&p->tcpout.x.options[2],&seg,2); p->tcpout.t.hlen=96; /* include one more word in hdr */ tcpsend(p,4); /* send opening volley */ p->tcpout.t.hlen=80; /* normal hdr len */ /* savenxt = p->out.nxt; */ p->out.nxt += 1; /* ack should be for next byte */ return(pnum); /* do not wait for connect */ } /*************************************************************************/ /* netopen2 * Send out repeat SYN on a connection which is not open yet * Checks, and only sends one if needed. * Returns 1 if the state is still SYNS and 0 if the connection has proceeded. * The timing is all handled at a higher layer. */ netopen2(pnum) int pnum; { struct port *p; if (pnum < 0 || pnum > NPORTS) return(-1); if (NULL == (p = portlist[pnum])) return(-2); if (p->state != SSYNS) return(0); /* done our job */ /* * The connection has not proceeded to further states, try retransmission */ p->out.nxt--; p->tcpout.t.hlen=96; /* include one more word in hdr */ tcpsend(p,4); /* try sending again */ p->tcpout.t.hlen=80; /* normal hdr len */ p->out.nxt++; return(1); } /**************************************************************************/ /* netclose * Do appropriate actions to return connection state to SCLOSED which * enables the memory for that port to be reused. */ netclose(pnum) int pnum; { struct port *p; if (pnum < 0 || pnum > NPORTS) /* is a valid port? */ return(-1); if ((p = portlist[pnum]) != NULL) { /* something there */ switch (p->state) { case SLISTEN: /* we don't care anymore */ case SSYNS: p->state = SCLOSED; break; case SEST: /* must initiate close */ /* send FIN */ p->tcpout.t.flags = TACK | TFIN; tcpsend(p,0); p->state = SFW1; /* wait for ACK of FIN */ break; /* do nothing for now ?*/ case SCWAIT: /* other side already closed */ p->tcpout.t.flags = TFIN | TACK; tcpsend(p,0); p->state = SLAST; break; case STWAIT: /* time out yet? */ if (portlist[pnum]->out.lasttime + WAITTIME < time(NULL)) p->state = SCLOSED; break; case SLAST: /* five minute time out */ if (portlist[pnum]->out.lasttime + LASTTIME < time(NULL)) p->state = SCLOSED; break; default: break; } } else return(1); return(0); } /**************************************************************************/ /* netinit * Calls all of the various initialization routines that set up queueing * variables, static values, reads configuration files, etc. */ netinit() { int ret; /* * Initializes all buffers and hardware for data link layer. * Machine/board dependent. */ ret = dlayerinit(); if (ret) { nnerror(101); return(ret); } /* * initialize the template packets needed for transmission */ ret = protinit(); return(ret); /* set up empty packets */ } /*************************************************************************/ /* netshut * Close all the connections and turn off the hardware. */ netshut() { int i; for (i=0; i < NPORTS ; i++) if (portlist[i] != NULL) netclose(i); netsleep(1); dlayershut(); } nstall maximum segment size which will be sent out in the first * ACK-SYN packet */ prt->tcpout.x.options[0] = 2; prt->tcpout.x.options[1] = 4; /* install maximum segment size */ nn = intswap(nnsegsize); movebytes(&prt->tcpout.x.options[2],&nn,2); return(pnum); } /***********************************************************************/ /* netgetftp * Provides the informsource/protinit.c 644 144 13 21261 4267454317 7454 /* * Protinit.c * initialize the template packets **************************************************************************** * * * part of: * * TCP/UDP/ICMP/IP Network kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * 'protinit' initializes packets to make them ready for transmission. * For many purposes, pre-initialized packets are created for use by the * protocol routines, especially to save time creating packets for * transmit. * * Assumes that 'myaddr' is already set to my Ethernet hardware address * and any other similar protocol addresses are already initialized * (known) for the local station. * * As this is a convenient place for it, this file contains many of the * data declarations for packets which are mostly static (pre-allocated). */ #include "stdio.h" #include "protocol.h" #include "data.h" /************************************************************************/ /* main code for protinit() * for each new type of protocol, put an initialization call here. * There may be some requirement of order of initialization. */ protinit() { etherinit(); arpinit(); ipinit(); tcpinit(); udpinit(); return(0); } /*************************************************************************/ /* neteventinit * load up the pointers for the event queue * makes a circular list to follow, required for error messages */ neteventinit() { int i; for (i=0; i < NEVENTS; i++) nnq[i].next = i+1; nnq[NEVENTS-1].next = -1; nnefirst = 0; nnelast = 0; nnefree = 1; } /*************************************************************************/ /* Ethernet headers, initialize a default header to be used in * subsequent pre-initialized headers. This does something similar for * AppleTalk. */ etherinit() { movebytes(broadaddr,bseed,DADDLEN); movebytes(blankd.dest,broadaddr,DADDLEN); /* some are broadcast */ movebytes(blankd.me,nnmyaddr,DADDLEN); /* always from me */ blankd.type = EIP; /* mostly IP packets */ } /*************************************************************************/ /* ARP packets * * a very limited type of packet goes out. We currently only talk IP, so * initialize as many fields as possible, most fields are already known. * * Also initialize a reverse-arp packet to be sent out on request. (later) */ arpinit() { int i; movebytes(&arp.d,&blankd,sizeof(DLAYER)); arp.d.type = EARP; /* 0x0806 is ARP type */ arp.hrd = intswap(HTYPE); /* Ether = 1 */ arp.pro = intswap(ARPPRO); /* IP protocol = 0x0800 */ arp.hln = DADDLEN; /* Ethernet hardware length */ arp.pln = 4; /* IP length = 4 */ movebytes(arp.sha,nnmyaddr,DADDLEN); /* sender's hardware addr */ movebytes(&arp.tha,broadaddr,DADDLEN); /* target hardware addr */ movebytes(&arp.spa,nnipnum,4); /* sender's IP addr */ /* * initialize the ARP cache to 0 time, none are gateways to start */ for (i=0; istate == SCLOSED || (q->state == STWAIT && q->out.lasttime + WAITTIME < time(NULL)))) p = q; retval = i++; /* port # to return */ } while (p == NULL && i < NPORTS); /* * None available pre-allocated, get a new one, about 8.5 K with a 4K windowsize */ if (p == NULL) { p = (struct port *)malloc(sizeof(struct port)); for (i=0; portlist[i] != NULL; i++) if (i >= NPORTS) { nnerror(500); return(-1); /* out of room for ports */ } portlist[i] = p; retval = i; } if (p == NULL) { nnerror(505); return(-1); } movebytes(&p->tcpout,&blankip,sizeof(DLAYER)+sizeof(IPLAYER)); /* static initialization */ p->tcpout.i.tlen = 0; p->tcpout.t.urgent = 0; /* no urgent data */ p->tcpout.t.hlen = 20 << 2; /* header length << 2 */ p->tcps.z = 0; p->tcps.proto = PROTTCP; movebytes(p->tcps.source,nnipnum,4); setupwindow(&p->in,WINDOWSIZE); /* queuing parameters */ setupwindow(&p->out,WINDOWSIZE); do { i = time(NULL); i |= 2048; /* make sure it is at least this large */ i &= 0x3fff; /* at least this small */ for (j=0; jin.port ; j++) ; } while (j < NPORTS); if ( nnfromport ) { /* allow the from port to be forced */ i = nnfromport; nnfromport = 0; /* reset it so the next one will be random */ } p->in.port = i; p->tcpout.t.source = intswap(i); p->tcpout.t.seq = longswap(p->out.nxt); p->state = SCLOSED; p->credit = nncredit; p->sendsize = TSENDSIZE; p->rto = MINRTO; return(retval); } setupwindow(w,wsize) struct window *w; unsigned int wsize; { w->endbuf = w->where + wsize; w->base = w->endlim = w->where; w->contain = 0; /* nothing here yet */ w->lasttime = time(NULL); w->size = wsize; w->push = 0; /* * base this on time of day clock, for uniqueness */ w->ack = w->nxt = ((w->lasttime << 12) & 0x0fffffff); } ield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * 'protinit' initializes packets to make them source/confile.c 644 144 13 73565 4267463751 7243 /* * Confile.c * Split from util.c 5/88 * Reads and stores the appropriate information for the config file * * version 2, full session layer, TK started 6/17/87 * **************************************************************************** * * * part of: * * Network kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** */ #include "stdio.h" #include "whatami.h" #include "hostform.h" #ifdef PC #include "string.h" #else char *index(); #define strchr(A,B) index(A,B) #endif #define NUMSPECS 128 char /* special function types */ *neterrstring(), *malloc(); int32 time(); /* don't forget this sucker! */ static struct machinfo *Smachlist,*Smptr; struct machinfo *Sns=NULL; static unsigned char *Smachfile = {"config.tel"}, Sflags[NUMSPECS-95], /* which parms we have or have not */ *Sspace; struct config Scon = { 0,0,0,0, 0, 3, 127,0,0,1, "", 7,1,0x70, "atalk", "ega", 0, 1, 1, 1, 0, "DEC-VT100", "*", NULL, NULL, NULL, NULL, NULL, NULL, 1, 4, 3, -1, 0x0d000, 0x0300 }; int Sxxnf[3] = NFDEF, Sxxnb[3] = NBDEF, Sxxbf[3] = BFDEF, Sxxbb[3] = BBDEF, Sxxuf[3] = UFDEF, Sxxub[3] = UBDEF; static int mno=0, /* how many machines in host file */ lineno, /* line number in hosts file */ position, /* position for scanning string */ constate, /* state for config file parser */ inquote; /* flag, inside quotes now */ /* * States for config file reading state machine. * One for each type of keyword and some for controlling. */ #define CONNAME 101 #define CONHOST 102 #define CONIP 103 #define CONGATE 104 #define CONCOLOR 105 #define CONBKSP 106 #define CONBKSC 107 #define CONRETR 108 #define CONWIND 109 #define CONSEG 110 #define CONMTU 111 #define CONNS 112 #define CONTO 113 #define CONCRMAP 114 #define CONDUP 115 #define CONWRAP 116 #define CONWIDE 117 #define CONFONT 118 #define CONFSIZE 119 #define CONNF 120 #define CONNB 121 #define CONBF 122 #define CONBB 123 #define CONUF 124 #define CONUB 125 #define CONRF 126 #define CONRB 127 #define CONCLMODE 128 /* * above this line are per machine entries, below are configuration entries */ #define CONDOMTO 129 #define CONNDOM 130 #define CONMASK 131 #define CONMYIP 132 #define CONHPF 133 #define CONPSF 134 #define CONTEKF 135 #define CONJTIME 136 #define CONME 137 #define CONCCOL 138 #define CONHW 139 #define CONADDR 140 #define CONIOA 141 #define CONDEF 142 #define CONCKEYS 143 #define CONINT 144 #define CONBIOS 145 #define CONTEK 146 #define CONVIDEO 147 #define CONFTP 148 #define CONRCP 149 #define CONPASS 150 #define CONCAP 151 #define CONTTYPE 152 #define CONNSTYPE 153 #define CONFROM 154 #define CONARPTO 155 #define CONZONE 156 char *Skeyw[] = { "", "name", /* name of session */ "host", /* name of host */ "hostip", /* IP number */ "gateway", /* gateway level */ "color", /* color code ==5== */ "erase", /* value to use for backspace */ "scrollback", /* how many lines to backscroll */ "retrans", /* initial retrans time */ "rwin", /* window to allow for this host */ "maxseg", /* maximum transfer size (in) ==10== */ "mtu", /* transfer unit (out) */ "nameserver", /* name server level */ "contime", /* timeout for opening connection */ "crmap", /* map for Berkeley 4.3 compatibility */ "duplex", /* half duplex for IBM machines */ "vtwrap", /* should VT wrap? */ "vtwidth", /* width of VT100 screen */ "font", /* font to use, when given a choice */ "fsize", /* font size, in points */ "nfcolor", /* normal foreground color */ "nbcolor", /* normal background color */ "bfcolor", /* blink foreground color */ "bbcolor", /* blink background color */ "ufcolor", /* underline foreground color */ "ubcolor", /* underline background color */ "rfcolor", /* reverse foreground color */ "rbcolor", /* reverse background color */ "clearsave", /* clear screen saves lines */ /* * following are one-time entries, above are part of the data structure */ "domaintime", /* time-out for DOMAIN */ "domainretry", /* # of retries */ "netmask", /* subnetting mask */ "myip", /* local machine's IP # */ "hpfile", /* HPGL output file */ "psfile", /* postscript output file */ "tekfile", /* tektronix output file */ "timeslice", /* timer slice for multi-tasking */ "myname", /* identifying info ==15==*/ "concolor", /* console colors */ "hardware", /* network hardware */ "address", /* Address of hardware */ "ioaddr", /* ioaddress of hardware */ "domain", /* default domain for lookup */ "commandkeys", /* use command keys on mac */ "interrupt", /* interrupt request 3 or 5 */ "bios", /* use BIOS screen */ "tek", /* tektronix graphics ==20==*/ "video", /* type of video hardware */ "ftp", /* enable ftp? */ "rcp", /* enable rcp? */ "passfile", /* password file name */ "capfile", /* capture file name */ "termtype", /* terminal type */ "nameservertype", /* nameserver type */ "copyfrom", /* copy from another machine */ "arptime", /* time-out for ARPs */ "zone", /* NBP zone for Macs */ "" }; /************************************************************************/ /* Sgetconfig * copy the configuration information into the user's data structure * directly. The user can do with it what he feels like. */ Sgetconfig(cp) struct config *cp; { movebytes(cp,&Scon,sizeof(struct config)); return(0); } /************************************************************************/ /* Sreadhosts * read in the hosts file into our in-memory data structure. * Handle everything by keyword, see docs for specifications about file. */ Sreadhosts() { FILE *fp; int c,retval; Smachlist = Smptr = NULL; mno = 0; Sspace = malloc(256); /* get room for gathering stuff */ if (Sspace == NULL) { Serrline(901); return(1); } position = constate = inquote = lineno = 0; /* state vars */ if (NULL == (fp = fopen(Smachfile,"r"))) { Serrline(900); return(1); } retval = 0; while (!retval) { c = fgetc(fp); if (c == '#' && !inquote) { while (c != EOF && c != '\n' && c != '\r') /* skip to EOL */ c = fgetc(fp); } if (c == '\n' || c == '\r') lineno++; retval = Scontoken(c); /* add character to token */ } fclose(fp); free(Sspace); Smadd("default"); /* make sure name is in list */ if (retval == EOF) /* EOF is normal end */ return(0); else return(retval); } /************************************************************************/ /* ncstrcmp * No case string compare. * Only returns 0=match, 1=no match, does not compare greater or less * There is a tiny bit of overlap with the | 32 trick, but shouldn't be * a problem. It causes some different symbols to match. */ ncstrcmp(sa,sb) char *sa,*sb; { while (*sa && *sa < 33) /* don't compare leading spaces */ sa++; while (*sb && *sb < 33) sb++; while (*sa && *sb) { if ((*sa != *sb) && ((*sa | 32) != (*sb | 32))) return(1); sa++;sb++; } if (!*sa && !*sb) /* if both at end of string */ return(0); else return(1); } /************************************************************************/ /* Serrline * prints the line number of the host file error and posts the event * for the line number error and posts the hosts file error. */ Serrline(n) int n; { char *p; p = neterrstring(-1); sprintf(p,"Config file: error in line %4d",lineno+1); netposterr(-1); netposterr(n); } /************************************************************************/ /* Scontoken * tokenize the strings which get passed to Sconfile. * Handles quotes and uses separators: <33, ;:= */ Scontoken(c) int c; { int retval; if (c == EOF) { Sspace[position++] = '\0'; Sconfile(Sspace); if (!Sflags[0]) { /* make sure last entry gets copied */ if (ncstrcmp("default",Smptr->sname)) Scopyfrom("default"); else Scopyfrom("=="); } return(-1); } if (!position && Sissep(c)) /* skip over junk before token */ return(0); if (inquote || !Sissep(c)) { if (position > 200) { Serrline(903); return(1); } /* * check for quotes, a little mixed up here, could be reorganized */ if (c == '"' ) { if (!inquote) { /* beginning of quotes */ inquote = 1; return(0); } else inquote = 0; /* turn off flag and drop through */ } else { if (c == '\n') { /* check for EOL inside quotes */ Serrline(904); return(1); } Sspace[position++] = c; /* include in current string */ return(0); } } Sspace[position++] = '\0'; retval = Sconfile(Sspace); /* pass the token along */ position = 0; inquote = 0; Sspace[0] = '\0'; return(retval); } /************************************************************************/ /* Sconfile * take the characters read from the file and parse them for keywords * which require configuration action. */ Sconfile(s) char *s; { int i,a,b,c,d; switch (constate) { case 0: /* lookup keyword */ if (!(*s)) /* empty token */ return(0); for (i=1; *Skeyw[i] && ncstrcmp(Skeyw[i],s); i++) ; if (!(*Skeyw[i])) { /* not in list */ Serrline(902); return(0); /* don't die - helps backward compatibility */ } constate = 100+i; /* change to state for keyword */ /* * check if this is a machine specific parm without a machine to * give it to. "name" being the only machine specific parm allowed, of course */ if (Smptr == NULL && constate > 101 && constate <= NUMSPECS) { Serrline(905); return(1); } break; case CONNAME: /* * allocate space for upcoming parameters */ if (Smachlist == NULL) { Smachlist = (struct machinfo *)malloc(sizeof(struct machinfo)); Smptr = Smachlist; Smptr->sname = NULL; Smptr->hname = NULL; } else { if (!Sflags[0]) { if (ncstrcmp("default",Smptr->sname)) Scopyfrom("default"); else Scopyfrom("=="); /* to make sure 'default' gets set */ } Smptr->next = (struct machinfo *)malloc(sizeof(struct machinfo)); Smptr = Smptr->next; } Smptr->next = NULL; Smptr->hname = NULL; /* guarantee to be null */ Smptr->sname = malloc(position); /* size of name string */ strcpy(Smptr->sname,s); /* keep name field */ constate = 0; /* back to new keyword */ for (i=0; imno = ++mno; /* new machine number */ break; case CONHOST: /* also a name */ Smptr->hname = malloc(position); strcpy(Smptr->hname,s); constate = 0; Sflags[CONHOST-100] = 1; break; case CONIP: /* IP number for host */ if ( 4 != sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) { Serrline(906); return(3); } Smptr->hostip[0]=a; Smptr->hostip[1] =b; /* keep number */ Smptr->hostip[2]=c; Smptr->hostip[3] =d; Smptr->mstat = HFILE; constate = 0; Sflags[CONIP-100] = 1; break; case CONGATE: Smptr->gateway = atoi(s); /* gateway level */ constate = 0; Sflags[CONGATE-100] = 1; break; case CONCOLOR: /* support old format */ Smptr->nfcolor[0] = s[1]-48; Smptr->nbcolor[0] = s[0]-48; Smptr->bfcolor[0] = s[5]-48; Smptr->bbcolor[0] = s[4]-48; Smptr->ufcolor[0] = s[3]-48; Smptr->ubcolor[0] = s[2]-48; constate = 0; Sflags[CONNF-100] = 1; /* sets them all at one shot */ Sflags[CONNB-100] = 1; Sflags[CONBF-100] = 1; Sflags[CONBB-100] = 1; Sflags[CONUF-100] = 1; Sflags[CONUB-100] = 1; break; case CONNF: /* foreground normal color */ if (Scolorset(Smptr->nfcolor,s)) Sflags[CONNF-100] = 1; constate = 0; break; case CONNB: /* background normal color */ if (Scolorset(Smptr->nbcolor,s)) Sflags[CONNB-100] = 1; constate = 0; break; case CONRF: case CONBF: /* blink foreg color */ if (Scolorset(Smptr->bfcolor,s)) Sflags[CONBF-100] = 1; /* in copyfrom, r's are really b's */ constate = 0; break; case CONRB: case CONBB: /* blink bg color */ if (Scolorset(Smptr->bbcolor,s)) Sflags[CONBB-100] = 1; constate = 0; break; case CONUF: /* foreground underline color */ if (Scolorset(Smptr->ufcolor,s)) Sflags[CONUF-100] = 1; constate = 0; break; case CONUB: /* bg underline color */ if (Scolorset(Smptr->ubcolor,s)) Sflags[CONUB-100] = 1; constate = 0; break; case CONBKSP: if (!ncstrcmp(s,"backspace")) Smptr->bksp = 8; else Smptr->bksp = 127; constate = 0; Sflags[CONBKSP-100] = 1; break; case CONBKSC: Smptr->bkscroll = atoi(s); constate = 0; Sflags[CONBKSC-100] = 1; break; case CONRETR: Smptr->retrans = atoi(s); constate = 0; Sflags[CONRETR-100] = 1; break; case CONWIND: Smptr->window = atoi(s); constate = 0; Sflags[CONWIND-100] = 1; break; case CONSEG: Smptr->maxseg = atoi(s); constate = 0; Sflags[CONSEG-100] = 1; break; case CONMTU: Smptr->mtu = atoi(s); constate = 0; Sflags[CONMTU-100] = 1; break; case CONNS: Smptr->nameserv = atoi(s); if (!Sns || (Sns->nameserv > Smptr->nameserv)) /* keep NS */ Sns = Smptr; constate = 0; Sflags[CONNS-100] = 1; break; case CONTO: i = atoi(s); if (i > 2) { Smptr->conto = i; Sflags[CONTO-100] = 1; } constate = 0; break; case CONCRMAP: if (!ncstrcmp(s,"4.3BSDCRNUL")) Smptr->crmap = 0; else Smptr->crmap = 10; Sflags[CONCRMAP-100] = 1; constate = 0; break; case CONDUP: if (!ncstrcmp(s,"half")) { Smptr->halfdup = 1; Sflags[CONDUP-100] = 1; } constate = 0; break; case CONWRAP: if ('Y' == toupper(s[0])) Smptr->vtwrap = 1; else Smptr->vtwrap = 0; Sflags[CONWRAP-100] = 1; constate = 0; break; case CONCLMODE: if ('N' == toupper(s[0])) Smptr->clearsave = 0; else Smptr->clearsave = 1; Sflags[CONCLMODE-100] = 1; constate = 0; break; case CONFONT: Smptr->font = malloc(position); strcpy(Smptr->font,s); Sflags[CONFONT-100] = 1; constate = 0; break; case CONFSIZE: Smptr->fsize = atoi(s); Sflags[CONFSIZE-100] = 1; constate = 0; break; case CONWIDE: if (132 == atoi(s)) Smptr->vtwidth = 132; else Smptr->vtwidth = 80; Sflags[CONWIDE-100] = 1; constate = 0; break; /* * now the one-time entries * Generally this information goes into the "Scon" structure for later * retrieval by other routines. * */ #ifdef PC case CONMASK: if ( 4 != sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) { Serrline(907); return(3); } Scon.netmask[0]=a; Scon.netmask[1] =b; Scon.netmask[2]=c; Scon.netmask[3] =d; Scon.havemask=1; constate = 0; break; case CONMYIP: constate = 0; if (!ncstrcmp(s,"rarp")) { movebytes(Scon.myipnum,s,4); netsetip("RARP"); break; } if ( 4 != sscanf(s,"%d.%d.%d.%d",&a,&b,&c,&d)) { Serrline(908); return(3); } Scon.myipnum[0]=a; Scon.myipnum[1] =b; /* put number back in s */ Scon.myipnum[2]=c; Scon.myipnum[3] =d; netsetip(Scon.myipnum); /* make permanent set */ break; #endif case CONME: /* what my name is */ strncpy(Scon.me,s,30); Scon.me[30] = '\0'; constate = 0; break; case CONHW: /* what hardware are we using? */ i = strlen(s); if (i > 9) i = 9; s[i] = '\0'; i--; while (i--) s[i] = tolower(s[i]); strcpy(Scon.hw,s); constate = 0; break; #ifdef PC case CONINT: /* what IRQ to use */ sscanf(s,"%x",&i); Scon.irqnum = i; constate = 0; break; case CONBIOS: if (toupper(*s) == 'Y') { Scwritemode(0); Scon.bios = 1; } constate = 0; break; case CONADDR: /* segment address for board */ sscanf(s,"%x",&i); Scon.address = i; constate = 0; break; case CONIOA: /* io address for board */ sscanf(s,"%x",&i); Scon.ioaddr = i; constate = 0; break; #endif #ifdef MAC case CONCKEYS: if (toupper(*s) == 'Y') { Scon.comkeys = 1; } constate = 0; break; case CONZONE: Scon.zone = malloc(position); /* space for name */ strcpy(Scon.zone,s); /* copy it in */ constate = 0; break; case CONJTIME: /* Time slice */ i = atoi(s); if (i > 1) Scon.timesl = i; constate = 0; break; #endif case CONTEK: if (toupper(*s) == 'N') { Stekmode(0); Scon.tek = 0; } constate = 0; break; case CONVIDEO: i = strlen(s); if (i > 9) i = 9; s[i] = '\0'; strcpy(Scon.video,s); i--; while (i--) s[i] = tolower(s[i]); constate = 0; break; case CONTTYPE: Scon.termtype = malloc(position); strcpy(Scon.termtype,s); constate = 0; break; case CONCCOL: for (i=0; i<3; i++) Scon.color[i] = ((s[i*2]-48)<<4) + (s[i*2+1]-48); constate = 0; break; case CONFTP: if (toupper(*s) == 'N') Scon.ftp = 0; constate = 0; break; case CONRCP: if (toupper(*s) == 'N') Scon.rcp = 0; constate = 0; break; case CONPASS: Scon.pass = malloc(position); /* space for name */ strcpy(Scon.pass,s); /* copy it in */ constate = 0; break; case CONDEF: /* default domain */ Scon.defdom = malloc(position); /* space for name */ strcpy(Scon.defdom,s); /* copy it in */ constate = 0; break; case CONCAP: /* capture file name */ Snewcap(s); constate = 0; break; case CONFROM: /* copy the rest from another */ /* entry in the table */ if (Scopyfrom(s)) return(1); Sflags[0] = 1; /* indicate did copy from */ constate = 0; break; case CONARPTO: /* need to lengthen arp time-out (secs) */ i = atoi(s); if (i > 0) netarptime(i); constate = 0; /* don't forget me! */ break; case CONDOMTO: /* DOMAIN timeout value */ i = atoi(s); if (i > 1) Scon.domto = i; constate = 0; break; case CONNDOM: /* DOMAIN number of retries */ i = atoi(s); if (i > 1) Scon.ndom = i; constate = 0; break; case CONHPF: /* File name for HP dump */ Snewhpfile(s); constate = 0; break; case CONPSF: /* File name for PS dump */ Snewpsfile(s); constate = 0; break; case CONTEKF: /* File name for Tek dump */ Snewtekfile(s); constate = 0; break; default: constate = 0; break; } return(0); } /************************************************************************/ /* Scopyfrom * Look at the Sflags array to determine which elements to copy from * a previous machine's entries. If a machine name as been given as * "default", the state machine will fill in the fields from that * machine's entries. * * If the machine name to copyfrom is not present in the list, set the * program default values for each field. */ Scopyfrom(s) char *s; { struct machinfo *m; int i; m = Shostlook(s); /* search list */ for (i=3; i <= NUMSPECS-100; i++) /* through list of parms */ if (!Sflags[i]) { if (m) /* copy old value */ switch (100+i) { case CONHOST: Smptr->hname = m->hname; break; case CONIP: movebytes(Smptr->hostip,m->hostip,4); Smptr->mstat = m->mstat; break; case CONGATE: /* gateways cannot be copied from */ Smptr->gateway = 0; break; case CONNS: /* can't copy nameservers either */ Smptr->nameserv = 0; break; case CONBKSP: Smptr->bksp = m->bksp; break; case CONBKSC: Smptr->bkscroll = m->bkscroll; break; case CONCLMODE: Smptr->clearsave = m->clearsave; break; case CONRETR: Smptr->retrans = m->retrans; break; case CONWIND: Smptr->window = m->window; break; case CONSEG: Smptr->maxseg = m->maxseg; break; case CONMTU: Smptr->mtu = m->mtu; break; case CONTO: Smptr->conto = m->conto; break; case CONCRMAP: Smptr->crmap = m->crmap; break; case CONDUP: Smptr->halfdup = m->halfdup; break; case CONWRAP: Smptr->vtwrap = m->vtwrap; break; case CONWIDE: Smptr->vtwidth = m->vtwidth; break; case CONNF: movebytes(Smptr->nfcolor,m->nfcolor,3*sizeof(int)); break; case CONNB: movebytes(Smptr->nbcolor, m->nbcolor,3*sizeof(int)); break; case CONBF: movebytes(Smptr->bfcolor,m->bfcolor,3*sizeof(int)); break; case CONBB: movebytes(Smptr->bbcolor,m->bbcolor,3*sizeof(int)); break; case CONUF: movebytes(Smptr->ufcolor,m->ufcolor,3*sizeof(int)); break; case CONUB: movebytes(Smptr->ubcolor,m->ubcolor,3*sizeof(int)); break; case CONFONT: Smptr->font = m->font; break; case CONFSIZE: Smptr->fsize = m->fsize; break; default: break; } else switch (100+i) { /* m=NULL, install default values */ case CONHOST: Smptr->hname = NULL; break; case CONIP: Smptr->mstat = NOIP; break; case CONGATE: /* gateways cannot be copied from */ Smptr->gateway = 0; break; case CONBKSP: Smptr->bksp = 127; break; case CONBKSC: Smptr->bkscroll = 0; break; case CONCLMODE: Smptr->clearsave = 1; break; case CONRETR: Smptr->retrans = SMINRTO; break; case CONWIND: #ifdef MAC Smptr->window = 512; #else Smptr->window = DEFWINDOW; #endif break; case CONSEG: #ifdef MAC Smptr->maxseg = 512; #else Smptr->maxseg = DEFSEG; #endif break; case CONMTU: #ifdef MAC Smptr->mtu = 512; #else Smptr->mtu = TSENDSIZE; #endif break; case CONNS: /* can't copy nameservers either */ Smptr->nameserv = 0; break; case CONTO: Smptr->conto = CONNWAITTIME; break; case CONCRMAP: Smptr->crmap = 10; break; case CONDUP: Smptr->halfdup = 0; break; case CONWRAP: Smptr->vtwrap = 0; break; case CONWIDE: Smptr->vtwidth = 80; break; case CONNF: movebytes(Smptr->nfcolor,Sxxnf,3*sizeof(int)); break; case CONNB: movebytes(Smptr->nbcolor,Sxxnb,3*sizeof(int)); break; case CONBF: movebytes(Smptr->bfcolor,Sxxbf,3*sizeof(int)); break; case CONBB: movebytes(Smptr->bbcolor,Sxxbb,3*sizeof(int)); break; case CONUF: movebytes(Smptr->ufcolor,Sxxuf,3*sizeof(int)); break; case CONUB: movebytes(Smptr->ubcolor,Sxxub,3*sizeof(int)); break; case CONFONT: Smptr->font = "Monaco"; break; case CONFSIZE: Smptr->fsize = 9; break; default: break; } } Sflags[0] = 1; /* set that this machine was copied */ return(0); } /************************************************************************/ /* Smadd * If machine is there, just returns pointer, else * Add a machine to the list. Increments machine number of machine. * Puts in parameters copied from the "default" entry. * */ struct machinfo *Smadd(mname) char *mname; { int i; struct machinfo *m; /* * First, do we have the name already? */ m = Shostlook(mname); if (m) return(m); /* * Don't have name, add another record */ Smptr = (struct machinfo *)malloc(sizeof(struct machinfo)); if (Smptr == NULL) return(NULL); for (i=0; i < NUMSPECS-99; i++) Sflags[i] = 0; /* we have no parms */ Scopyfrom("default"); Smptr->sname = NULL; Smptr->hname = malloc(strlen(mname)+1); if (Smptr->hname) strcpy(Smptr->hname,mname); /* copy in name of machine */ Smptr->mno = ++mno; Smptr->mstat = NOIP; Smptr->next = Smachlist; /* add to front of machlist */ Smachlist = Smptr; return(Smptr); } /************************************************************************/ /* Shostfile * if the user wants to change the host file name from 'config.tel' to * something else. */ Shostfile(ptr) char *ptr; { Smachfile = ptr; /* * note that the area with the file name must stay allocated for * later reference, typically it is in some argv[] parm. */ } /************************************************************************/ /* get host by name * Given the name of a host machine, search our database to see if we * have that host ip number. Search first the name field, and then the * hostname field. If the IP # is given, returns a ptr to the * default machine record with that IP # installed. * Returns the pointer to a valid record, or NULL if the IP # cannot * be deciphered. */ struct machinfo *Sgethost(machine) char *machine; { int i,j,k,l; unsigned char ipto[4],myipnum[4],xmask[4]; unsigned long hnum; struct machinfo *m; m = NULL; /* * First, check for the pound sign character which means we should use * the current netmask to build an IP number for the local network. * Take the host number, install it in the ipto[] array. Then mask * in my IP number's network portion to build the final IP address. */ if ('#' == machine[0]) { /* on my local network */ netgetip(myipnum); netgetmask(xmask); /* mask of network portion of IP # */ sscanf(&machine[1],"%ld",&hnum);/* host number for local network */ for (i=3; i >= 0; i--) { ipto[i] = hnum & 255L; /* take off a byte */ hnum >>= 8; /* shift it over */ } for (i=0; i < 4; i++) ipto[i] |= (myipnum[i] & xmask[i]); /* mask new one in */ } /* * next, is it an IP number? We take it if the number is in four * parts, separated by periods. */ else if (4 == sscanf(machine,"%d.%d.%d.%d",&i,&j,&k,&l)) { /* given ip num */ ipto[0] = i; ipto[1] = j; ipto[2] = k; ipto[3] = l; } /* * lastly, it must be a name, first check the local host table * A first number of 127 means that it doesn't have an IP number, but * is in the table (strange occurrence) */ else { /* look it up */ m = Shostlook(machine); if (m == NULL) { netposterr(805); /* informative */ return(NULL); } if (m->mstat < HAVEIP) { netposterr(806); /* informative */ return(NULL); } } if (!m) { m = Shostlook("default"); movebytes(m->hostip,ipto,4); /* copy in newest host # */ m->mstat = HAVEIP; /* we have the IP # */ } return(m); } /************************************************************************/ /* Shostlook * The straightforward list searcher. Looks for either the * session name matching or the host name matching. NULL if neither. */ struct machinfo *Shostlook(hname) char *hname; { struct machinfo *m; m = Smachlist; while (m != NULL) { if (m->sname && !ncstrcmp(hname,m->sname)) return(m); m = m->next; } m = Smachlist; while (m != NULL) { if (m->hname && !ncstrcmp(hname,m->hname)) return(m); m = m->next; } return(NULL); } /************************************************************************/ /* Slooknum * get the host record by machine number, used primarily in DOMAIN name * lookup. */ struct machinfo *Slooknum(num) int num; { struct machinfo *m; m = Smachlist; while (m) { if (m->mno == num) return(m); m = m->next; } return(NULL); } /**************************************************************************/ /* Slookip * For FTP to look up the transfer options to use when running * */ struct machinfo *Slookip(ipnum) unsigned char *ipnum; { struct machinfo *m; m = Smachlist; while (m) { if (comparen(m->hostip,ipnum,4)) return(m); m = m->next; } return(NULL); } /**************************************************************************/ /* Sissep * is the character a valid separator for the hosts file? * separators are white space, special chars and :;= * */ Sissep(c) int c; { if (c < 33) return(1); if (c == ':' || c == ';' || c == '=') return(1); return(0); } /*********************************************************************/ /* Snewns() * Rotate to the next nameserver * Chooses the next highest number from the nameserv field */ Snewns() { struct machinfo *m,*low; int i; if (!Sns) /* safety, should never happen */ Sns = Smachlist; low = Sns; i = Sns->nameserv; /* what is value now? */ m = Smachlist; while (m) { if (m->nameserv == i+1) { Sns = m; return(0); } if ((m->nameserv > 0) && (m->nameserv < low->nameserv)) low = m; m = m->next; } if (Sns == low) return(1); /* no alternate */ else Sns = low; return(0); } Ssetns(ipn) unsigned char ipn[4]; { struct machinfo *m; int i; i = 0; if (NULL == (m = Slookip(ipn))) { /* have already? */ m = Smadd("=nameserv="); movebytes(m->hostip,ipn,4); m->mstat = FROMKIP; i = 1; } m->nameserv = 1; Sns = m; return(i); } /************************************************************************/ /* setgates * set up the gateway machines and the subnet mask after netinit() * and start up ftp and rcp for them. */ Ssetgates() { struct machinfo *m; int level,again; if (Scon.havemask) /* leave default unless specified */ netsetmask(Scon.netmask); /* * Search the list of machines for gateway flags. * Invoke netsetgate in increasing order of gateway level #s. * Terminates when it gets through list without finding next higher number. */ level = 0; do { level++; again = 0; m = Smachlist; while (m != NULL) { if (m->gateway == level && m->mstat >= HAVEIP) netsetgate(m->hostip); if (m->gateway == level+1) again=1; m = m->next; } } while (again); Sftpmode(Scon.ftp); #ifdef PC Srcpmode(Scon.rcp); #endif return(0); } ; case CONWIND: #ifdef MAC Smptr->window = 512; #else Smptr->window = DEFWINDOW; #endif break; case CONSEG: #ifdef Msource/pcutil.c 644 144 13 12357 4267454323 7107 /* PCUTIL.C * Utilities for the network library that are PC specific **************************************************************************** * * * part of: * * TCP/UDP/ICMP/IP Network kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** */ #include "stdio.h" #include "whatami.h" unsigned char *malloc(); /**********************************************************************/ /* * Find directory name -- return a code that indicates whether the * directory exists or not. * 0 = dir name ok * -1 = error * > 0 = dos error code, no dir by this name * * Accept certain unix conventions, like '/' for separator * * Also, append a '\' to the name before returning * * Note: There must be enough room in the string to append the '\' */ struct dosdta { char junk[21]; char att; int time,date; long int size; char name[13]; }; extern struct dosdta *dtaptr; direxist(dirname) char dirname[]; { int i,ret; char *p; if (!strcmp(dirname,".") || !dirname[0]) { dirname[0] = '\0'; return(0); } if (!strcmp(dirname,"\\")) return(0); p = dirname; while (*p) { switch (*p) { case '*': case '?': return(-1); case '/': *p = '\\'; default: break; } p++; } /* * n_findfirst will return normal files AND directories * must check attribute to see if it is really a directory */ ret = n_findfirst(dirname,0x10); /* find name */ if (ret) return(ret); if (!(dtaptr->att & 0x10)) return(-2); /* is a normal file */ i = strlen(dirname); dirname[i] = '\\'; /* extend with '\' */ dirname[++i] = '\0'; return(0); } /**********************************************************************/ /* firstname * find the first name in the given directory which matches the wildcard * specification * * must malloc enough space for the path plus a full filename * * expand '*' (unix) to '*.*' (dos) */ static char *savepath; static int rootlen; char *firstname(path) char path[]; { int i,len; char *p,*q; if (!*path) return(NULL); len = strlen(path); savepath = malloc(len+16); i = 0; rootlen = 0; q = savepath; p = path; while (*q = *p) { /* basic string copy with extras */ if (*p == '\\') rootlen = i+1; /* rootlen = position of last \ */ p++; q++; i++; } if (savepath[len-1] == '*' && rootlen == len-1) { savepath[len++] = '.'; savepath[len++] = '*'; savepath[len++] = '\0'; } if (n_findfirst(savepath,0)) return(NULL); /* * copy file name, translate to lower case */ q = &savepath[rootlen]; p = dtaptr->name; while (*p) { if (*p >= 'A' && *p <= 'Z') *q++ = *p++ + 32; else *q++ = *p++; } *q = '\0'; return(savepath); } /**********************************************************************/ /* nextname * modify the path spec to contain the next file name in the * sequence as given by DOS * * if at the end of the sequence, return NULL */ char *nextname() { char *p,*q; if (NULL == savepath) return(NULL); if (n_findnext()) { free(savepath); savepath = NULL; return(NULL); } /* * copy file name, translate to lower case */ q = &savepath[rootlen]; p = dtaptr->name; while (*p) { if (*p >= 'A' && *p <= 'Z') *q++ = *p++ + 32; else *q++ = *p++; } *q = '\0'; return(savepath); } /**********************************************************************/ /* dopwd * get the current directory, including disk drive letter */ dopwd(p,l) char *p; int l; { *p++ = 'A'+getdsk(); /* current disk drive */ *p++ = ':'; *p++ = '\\'; getcwd(p,l-2); /* get dir */ } /**********************************************************************/ /* chgdir * change directory, including disk drive letter */ #include chgdir(d) char *d; { while (*d && *d < 33) d++; if (!(*d)) return(-1); *d = toupper(*d); if (*(d+1) == ':') { chgdsk((*d)-'A'); d += 2; } if (!(*d)) return(0); /* just changed disk */ return(chdir(d)); } /**********************************************************************/ /* Scolorset * setup the color value from the config file string */ Scolorset(thecolor,st) int thecolor[3]; char *st; { thecolor[0] = lookcolor(st); return(1); } /**********************************************************************/ /* lookcolor * search a list for the given color name */ static char *colist[] = { "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white" }; lookcolor(s) char *s; { int i; for (i=0; i<7; i++) if (!ncstrcmp(colist[i],s)) return(i); return(7); } char *colorlook(cl) int cl; { return(colist[cl]); } *m; m = Smachlist; while (m) { if (comparen(m->hostip,ipnum,4)) return(m); m = m->next; } return(NULL); } /**************************************************************************/ /* Sissep * is the character a valid separator for the hosts file? * source/look.c 644 144 13 153203 4267454331 6566 /* * LOOK.C * User interface code for NCSA Telnet **************************************************************************** * * * NCSA Telnet for the PC * * by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * * This program is in the public domain. * * * **************************************************************************** * Revisions: * 10/1/87 Initial source release, Tim Krauskopf * 7/5/88 Version 2.2 Tim Krauskopf */ #define WINMASTER #define USETEK /* #define USERAS */ #define HTELNET 23 #ifndef USETEK #define leavetek() 0 #endif #include "stdio.h" #include "whatami.h" #include "signal.h" #include "fcntl.h" #include "nkeys.h" #include "windat.h" #include "hostform.h" struct twin *creatwindow(),*newin; FILE *tekfp; int stand=0, basetype = VTTYPE, ftpact=0, /* ftp transfer is active */ rcpact=0, /* rcp transfer is active */ viewmode=0, /* What are we looking at? */ transok=1, /* is file transfer enabled? */ temptek, /* where drawings go by default */ indev,outdev, /* for copying tek images */ rgdevice=0, /* tektronix device to draw on */ vton=1, capon=0, /* overall capture on or not */ foundbreak=0, /* trap Ctrl-C */ machparm=0; unsigned char s[550],parsedat[128], colors[NCOLORS] = {2,1,0x70,0}, /* base colors */ myipnum[4], *neterrstring(), *malloc(), *blankline = {" "}; int breakstop(),errhandle(); long int time(),n_clicks(),lastt; struct config def; /* Default settings obtained from host file */ struct machinfo *mp=NULL; /* Pointer to a machine information structure */ #define PSDUMP 128 main(argc,argv) int argc; char *argv[]; { int i,j; n_clear(); /* do you know where your BP is? */ n_cur(0,0); n_window(0,0,23,79); /* vt100 size */ n_color(2); n_puts("NCSA Telnet, reading configuration file . . ."); #define USAGE printf("\n\nUsage: telnet [-s] [-t] [-c color] [-h hostfile] [machinename] ...") if (*argv[1] == '?') { USAGE; n_row(); n_puts("\n\n -c 172471 sets the basic color scheme for console screen"); n_puts(" -h file full path specification of host information file"); n_puts(" -s standalone (server) mode for rcp and ftp"); n_puts(" -t disable direct writes to screen"); exit(1); } machparm = 1000; /* large number */ /* * work on parms */ for (i=1; i VSinit(30)) { /* initialize GPs virtual screens */ n_puts("Virtual screen initialization failed."); exit(1); } if (NULL == (console = creatwindow())) { n_puts("Console memory allocation failed"); exit(1); } strcpy(console->mname,"Console"); /* * introductions on the console window */ i = console->vs; RSvis(-1); vprint(i,"\n\n Console messages:\r\n\nNCSA Telnet\r\n"); vhead(i); /* * initialize network stuff */ if (j = Snetinit()) { wrest(console); errhandle(); vprint(i,"Error initializing network or getting configuration file\r\n"); if (j == -2) /* RARP failure, special case */ netshut(); exit(1); } netgetip(myipnum); /* what is my ip number? */ Sgetconfig(&def); /* get information provided in hosts file */ #ifdef USETEK /* * install tektronix */ if (stmode()) tekinit(def.video); #endif #ifdef USERAS if (!VRinit()) { vprint(i,"Error initializing raster support\r\n"); } #endif /* * Display my ethernet and IP address for the curious people */ pcgetaddr(&s[200],def.address,def.ioaddr); sprintf(s,"My Ethernet address: %x:%x:%x:%x:%x:%x\r\n",s[200],s[201], s[202],s[203],s[204],s[205]); vprint(console->vs,s); sprintf(s,"My IP address: %d.%d.%d.%d\r\n\n", myipnum[0],myipnum[1],myipnum[2],myipnum[3]); vprint(console->vs,s); #ifdef MSC signal(SIGINT,&breakstop); /* Microsoft intercept of break */ #else onbreak(&breakstop); /* Lattice intercept of break */ #endif Stask(); /* any packets for me? (return ARPs) */ /* * With the parameters left on the command line, open a connection * to each machine named. Start up all of the configuration on open * connections. */ for (i = machparm; i < argc; i++) addsess(argv[i]); if (current == NULL) { /* special viewmode for server mode */ current = console; /* special enter sequence avoids flicker */ viewmode = 6; } wrest(current); /* paint the first session's screen */ while (AF3 != dosessions()) /* serve all sessions, events */ ; endall(); } /************************************************************************/ /* vhead * place the header for new sessions in. */ vhead(v) int v; { vprint(v,"\r\nNational Center for Supercomputing Applications\r\n"); vprint(v,"NCSA Telnet for the PC version 2.2S\r\n"); vprint(v,"\nAlt-H presents a summary of special keys \r\n\n"); } /************************************************************************/ /* dosessions * dosessions is an infinite loop serving three sources of external * input in order of precedence: * - changes to the video display properties * - keyboard strokes * - network events * * What the user sees is top priority followed by servicing the keyboard * and when there is time left over, looks for network events. * * viewmode is a state variable which can determine if the screen should * be re-drawn, etc. */ dosessions() { int i,j,c,cl,dat,cvs; unsigned int sw; struct twin *t1; unsigned char *p; i = 0; switch (viewmode) { case 0: /* no special mode, just check scroll lock */ default: if (n_scrlck()) { /* scroll lock prevents text from printing */ viewmode = 1; i = n_row(); j = n_col(); c = n_color(current->colors[2]); n_cur(24,60); n_draw("* Scroll Lock *",16); /* status in lower left */ n_color(c); n_cur(i,j); } /* * This gives precedence to the keyboard over the network. */ while (0 <= (c = newkey(current))) /* do all key translations */ if (c == AF3) return(AF3); break; /* no special viewing characterisitics */ case 1: /* scrollock is active */ if (!n_scrlck()) { VSsetrgn(current->vs,0,0,79,23); viewmode = 0; /* set back if appropriate */ statline(); } /* * In scroll lock mode, take keys only for the scrollback, * The scrollback routine will never block, so we keep servicing events. */ scrollback(); break; case 2: /* console is visible */ viewmode = 9; /* check keypress before redrawing */ break; case 3: /* help screen view1 */ case 4: /* help screen view2 */ while (0 <= (c = n_chkchar())) { if (c == AF3) return(AF3); if (viewmode == 3 && c == 27) { viewmode = 4; help2(c); } else { /* restore view 0 */ if (c > 128) dokey(current,c); /* dokey might change view, if so, don't reset view 0 */ if (viewmode >= 3 && viewmode <= 4) viewmode = 10; } } break; case 5: /* DOS screen view */ viewmode = 9; /* wait for keypress */ break; case 6: /* server mode */ vprint(console->vs, "\r\nServer mode, press ESC to exit or ALT-A to begin a session\r\n"); viewmode = 7; case 7: /* server mode 2 */ j = n_chkchar(); switch (j) { case 27: vprint(console->vs,"\r\n\n Ending server mode \r\n"); return(AF3); /* leave the program */ break; case ALTA: if (!addsess(NULL)) { /* start up a new one */ viewmode = 10; } else { current = console; viewmode = 6; } break; case -1: break; /* no keypress */ default: vprint(console->vs, "\r\nYou must have an open session to use other commands.\r\n"); viewmode = 6; break; } break; case 8: if (graphit()) /* graphics menu screen */ viewmode = 10; break; case 9: /* reset current screen on keypress */ if (0 < n_chkchar()) viewmode = 10; break; case 10: /* Display current screen */ wrest(current); viewmode = 0; /* return to standard mode */ statline(); break; } /* * Check for any relevant events that need to be handled by me */ if (0 < (i = Sgetevent(USERCLASS | CONCLASS | ERRCLASS, &cl, &dat))) { sw = cl*256 + i; /* class and event combination */ cvs = console->vs; switch (sw) { case CONCLASS*256+CONOPEN: /* a connection has just opened */ t1 = wins[dat]; /* where the window pointer is stored */ if (!t1) break; t1->sstat = '/'; /* connection status */ netpush(dat); netwrite(dat,"\377\375\001\377\375\003",6); /* telnet negotiate */ if (current != t1) { current = t1; viewmode = 10; } break; case CONCLASS*256+CONCLOSE: /* connection is closing */ if (0 < netqlen(dat)) netputuev(CONCLASS,CONCLOSE,dat); /* call me again */ /* drop through, process any data */ case CONCLASS*256+CONDATA: if (viewmode) { /* not ready for it */ netputuev(CONCLASS,CONDATA,dat); break; } t1 = wins[dat]; /* where the window pointer is stored */ if (!t1) break; if (inprocess(t1)) return(AF3); break; case CONCLASS*256+CONFAIL: /* can't open connection */ t1 = wins[dat]; /* where the window pointer is stored */ if (!t1) break; /* this don't count */ vprint(cvs,"\r\nCan't open connection, timed out\r\n"); netclose(dat); /* close out attempt */ if (!t1->next) { wrest(console); return(AF3); } if (t1 == current) { current = current->next; viewmode = 10; } delwindow(t1,1); statline(); break; /* * domain nameserver results. */ case USERCLASS*256+DOMFAIL: /* domain failed */ mp = Slooknum(dat); /* get machine info */ vprint(cvs,"\r\nDOMAIN lookup failed for: "); if (mp && mp->hname) vprint(cvs,mp->hname); else if (mp && mp->sname) vprint(cvs,mp->sname); vprint(cvs,"\r\n"); break; case USERCLASS*256+DOMOK: mp = Slooknum(dat); /* get machine info */ if (mp) { vprint(cvs,"\r\nDOMAIN lookup OK for: "); /* print session name and host name */ if (mp->hname) vprint(cvs,mp->hname); if (mp->sname) { vprint(cvs," - "); vprint(cvs,mp->sname); addsess(mp->sname); } else addsess(mp->hname); vprint(cvs,"\r\n"); viewmode = 10; } break; /* * FTP status events. */ case USERCLASS*256+FTPBEGIN: /* data connection */ ftpact = dat; Sftpname(s); /* get name */ vprint(cvs,"FTP transferring: "); vprint(cvs,s); vprint(cvs,"\r\n"); ftpstart(ftpact+2,s); lastt = n_clicks(); break; case USERCLASS*256+FTPLIST: /* LIST or NLST */ vprint(cvs,"FTP directory beginning\r\n"); break; case USERCLASS*256+FTPEND: /* data connection ending */ ftpact = 0; statline(); vprint(cvs,"FTP transfer done\r\n"); break; case USERCLASS*256+FTPCOPEN: /* command connection */ vprint(cvs,"FTP server initiated from host: "); Sftphost(s); if ((NULL == (mp = Slookip(s))) || (NULL == mp->sname)) sprintf(&s[4],"%d.%d.%d.%d\r\n",s[0],s[1],s[2],s[3]); else sprintf(&s[4],"%s\r\n",mp->sname); vprint(cvs,&s[4]); break; case USERCLASS*256+FTPUSER: /* user name entered */ vprint(cvs,"FTP user "); Sftpuser(s); vprint(cvs,s); vprint(cvs," login request\r\n"); break; case USERCLASS*256+FTPPWOK: /* user password verified */ vprint(cvs,"FTP Password verified\r\n"); break; case USERCLASS*256+FTPPWNO: /* user password failed */ vprint(cvs,"FTP Password failed verification\r\n"); break; case USERCLASS*256+FTPCLOSE: /* command connection ends */ vprint(cvs,"FTP server ending session\r\n"); break; case USERCLASS*256+RCPBEGIN: /* rcp starting */ vprint(cvs,"rcp file transfer\r\n"); rcpact = 1; break; case USERCLASS*256+RCPEND: /* rcp ending */ vprint(cvs,"rcp ending\r\n"); rcpact = 0; break; #ifdef USETEK case USERCLASS*256+PSDUMP: /* dump graphics screen */ if (VGpred(indev,outdev)) { if (dat) { endump(); vprint(cvs,"Graphics writing finished\r\n"); wrest(console); viewmode = 2; } } else netputevent(USERCLASS,PSDUMP,dat); /* remind myself */ break; #endif case ERRCLASS*256+ERR1: /* error message */ p = neterrstring(dat); VSwrite(cvs,p,strlen(p)); VSwrite(cvs,"\r\n",2); default: break; } } /* * update the FTP spinner if we are in ftp */ else if (ftpact && (n_clicks() > lastt + 10)) { ftpstart(ftpact+2,s); lastt = n_clicks(); } return(c); } /*********************************************************************/ /* inprocess * take incoming data and process it. Close the connection if it * is the end of the connection. */ inprocess(tw) struct twin *tw; { int cnt; cnt = netread(tw->pnum,s,64); /* get some from incoming queue */ if (cnt < 0) { /* close this session, if over */ netclose(tw->pnum); if (tw->capon) { fclose(tw->capfp); /* close the capture file */ tw->capon = capon = 0; } n_color(tw->colors[0]); if (tw->next == NULL) /* if this is the last one */ return(-1); /* signal no sessions open */ #ifdef USETEK leavetek(); /* make Tek inactive */ #endif if (tw != current) { wrest(tw); } n_puts("\nConnection closed, press a key . . ."); if (tw == current) current = tw->next; delwindow(tw,1); viewmode = 9; return(0); } if (cnt) parse(tw,s,cnt); /* display on screen, etc.*/ return(0); } /*********************************************************************/ /* endall * clean up and leave */ endall() { netshut(); n_cur(24,0); /* go to bottom of screen */ n_color(7); n_draw(blankline,80); /* blank it out */ exit(0); /* return to DOS */ } /*********************************************************************/ /* errhandle * write error messages to the console window */ errhandle() { char *errmsg; int i,j; while (ERR1 == Sgetevent(ERRCLASS,&i,&j)) { errmsg = neterrstring(j); VSwrite(console->vs,errmsg,strlen(errmsg)); VSwrite(console->vs,"\r\n",2); } } /*********************************************************************/ /* vprint * print to a virtual screen */ vprint(w,s) int w; char *s; { VSwrite(w,s,strlen(s)); } /*********************************************************************/ helpmsg() { int i; leavetek(); i = n_color(current->colors[0]); n_clear(); n_cur(0,0); n_puts("Keyboard usage for NCSA telnet: \n"); n_puts("Alt-A add a session Alt-Y Interrupt Process"); n_puts("Alt-N next session Alt-O Abort Output"); n_puts("Alt-M message screen Alt-Q Are you there?"); n_puts("Alt-E escape to DOS shell Alt-U Erase line"); n_puts("Alt-G graphics menu Alt-K Erase Kharacter"); n_puts("Alt-C toggle capture on/off Alt-X close connection"); n_puts("Alt-R reset VT102 screen HOME exit graphics mode"); n_puts("Alt-H this help screen Ctrl-HOME clear/enter graphics mode"); n_puts("ScrLock pause/restart screen (DO NOT use Ctrl-NumLock)"); n_puts("ScrLock enter/exit scroll-back mode"); n_puts("Alt-T start file transfer as if typed: ftp [internet address]"); n_puts("Alt-I send my internet address to host as if typed"); n_puts("Alt-S skip scrolling, jump ahead"); n_puts("Alt-P change a parameter, one of:"); n_puts(" color, capture file name, backspace, session name, screen mode"); n_puts("Alt-F3 abort program completely. STRONGLY discouraged"); n_puts("\n\nPress ESC for information page, space bar to return to session:"); n_color(i); } help2(c) int c; { int i; i = n_color(current->colors[0]); n_clear(); n_cur(0,0); n_puts("NCSA Telnet for the IBM PC and Macintosh version 2.2S"); n_puts("\nNational Center for Supercomputing Applications, University of Illinois"); n_puts("written by Tim Krauskopf, Gaige B. Paulsen, and Aaron Contorer\n"); #ifndef beta n_puts("This program is in the public domain."); n_puts("Please retain the following notice:"); n_puts("\n Portions developed by the National Center for Supercomputing Applications"); n_puts(" at the University of Illinois, Urbana-Champaign."); #else n_puts("Authorized beta test version - open to technical users at your own risk."); n_puts("Please report all problems that you wish fixed before July 1"); n_puts("All rights reserved."); #endif n_puts("\n\nFor information or for disks and manuals (there is a handling fee),"); n_puts("contact NCSA at:"); n_puts("152 Computing Applications Building"); n_puts("605 E. Springfield Ave."); n_puts("Champaign, IL 61820"); n_puts("\nbugs and suggestions to telbug@ncsa.uiuc.edu"); n_puts("\nPress space bar to return to session"); n_color(i); } /*********************************************************************/ /* parse * Do the telnet negotiation parsing. * * look at the string which has just come in from outside and * check for special sequences that we are interested in. * * Tries to pass through routine strings immediately, waiting for special * characters ESC and 255 to change modes. */ #define STNORM 0 #define GOAHEAD 249 #define WILLTEL 251 #define WONTTEL 252 #define DOTEL 253 #define DONTTEL 254 #define ESCFOUND 5 #define IACFOUND 6 parse(tw,st,cnt) struct twin *tw; int cnt; unsigned char *st; { int i,cv; unsigned char *mark,*orig; cv = console->vs; orig = st; /* remember beginning point */ mark = st + cnt; /* set to end of input string */ netpush(tw->pnum); /* * traverse string, looking for any special characters which indicate that * we need to change modes. */ while (st < mark) { switch (tw->telstate) { case ESCFOUND: #ifdef USETEK if (*st == 12) { /* esc-FF */ if (tw->termstate == VTEKTYPE) { vprint(cv,"\n\r Entering Tek mode \n\r"); tw->termstate = TEKTYPE; VGgmode(rgdevice); VGuncover(temptek); current=tw; } VGwrite(temptek,"\033\014",2); orig = ++st; /* pass by ESC-FF in data */ tw->telstate = STNORM; break; } #endif #ifdef USERAS if (*st == '^') { /* esc-^ */ tw->termstate = RASTYPE; tw->telstate = STNORM; current=tw; VRwrite("\033^",2); /* echo ^ */ orig = ++st; break; } #endif parsewrite(tw,"\033",1); /* send the missing ESC */ tw->telstate = STNORM; break; case IACFOUND: /* telnet option negotiation */ if (*st == 255) { /* real data = 255 */ st++; /* real 255 will get sent */ tw->telstate = STNORM; break; } if ( 248 < *st ) { tw->telstate = *st; /* by what the option is */ st++; break; } vprint(cv,"\n\r strange telnet option\r\n"); orig = st; tw->telstate = STNORM; break; case GOAHEAD: tw->telstate = STNORM; orig = st; break; case DOTEL: sprintf(parsedat," telnet option %d %d\r\n",tw->telstate,*st); vprint(cv,parsedat); if (*st != 3) { /* anything but suppress go-ahead */ sprintf(parsedat,"%c%c%c",255,WONTTEL,*st++); netwrite(tw->pnum,parsedat,3); /* refuse it */ } else if (!tw->igoahead) { /* suppress go-ahead */ sprintf(parsedat,"%c%c%c",255,WILLTEL,*st++); netwrite(tw->pnum,parsedat,3); /* take it */ tw->igoahead = 1; } tw->telstate = STNORM; orig = st; break; case DONTTEL: sprintf(parsedat," telnet option %d %d\r\n",tw->telstate,*st++); vprint(cv,parsedat); tw->telstate = STNORM; orig = st; break; case WILLTEL: sprintf(parsedat," telnet option %d %d\r\n",tw->telstate,*st); vprint(cv,parsedat); tw->telstate = STNORM; switch(*st++) { case 3: /* suppress go-ahead */ if (tw->ugoahead) break; tw->ugoahead = 1; sprintf(parsedat,"%c%c%c",255,DOTEL,3); /* ack */ netwrite(tw->pnum,parsedat,3); break; case 1: /* echo */ if (tw->echo) break; tw->echo = 1; sprintf(parsedat,"%c%c%c",255,DOTEL,1); /* ack */ netwrite(tw->pnum,parsedat,3); netwrite(tw->pnum,tw->linemode,strlen(tw->linemode)); tw->linemode[0] = '\0'; break; default: sprintf(parsedat,"%c%c%c",255,DONTTEL,*(st-1)); netwrite(tw->pnum,parsedat,3); /* refuse it */ } orig = st; break; case WONTTEL: sprintf(parsedat," telnet option %d %d\r\n",tw->telstate,*st); vprint(cv,parsedat); tw->telstate = STNORM; switch(*st++) { /* which option? */ case 1: /* echo */ if (!tw->echo) break; tw->echo = 0; sprintf(parsedat,"%c%c%c",255,DONTTEL,1); netwrite(tw->pnum,parsedat,3); /* OK with us */ break; default: break; } orig = st; break; default: tw->telstate = STNORM; break; } /* * quick scan of the remaining string, skip chars while they are * uninteresting */ if (tw->telstate == STNORM) { /* * skip along as fast as possible until an interesting character is found */ while (st < mark && *st != 27 && *st < 255) { if (!tw->binary) *st &= 127; /* mask off high bit */ st++; } parsewrite(tw,orig,st-orig); orig = st; /* forget what we have sent already */ if (st < mark) switch (*st) { case 255: /* telnet IAC */ tw->telstate = IACFOUND; st++; break; case 27: /* ESCape code */ if (st == mark-1 || *(st+1) == 12 || *(st+1) == '^') { tw->telstate = ESCFOUND; } st++; /* strip or accept ESC char */ break; default: vprint(cv," strange char > 128\r\n"); st++; break; } } } /* end while */ } /*********************************************************************/ /* parsewrite * write out some chars from parse * Has a choice of where to send the stuff */ parsewrite(tw,dat,len) struct twin *tw; char *dat; int len; { int i; /* * send the string where it belongs * 1. Check for a capture file. If so, echo a copy of the string * 2. Check for dumb terminal type, convert special chars if so * 3. Check for Tektronix mode, sending Tek stuff * 3b. check for raster color type * 4. or, send to virtual screen anyway */ if (tw->capon) /* capture to file? */ fwrite(dat,len,1,tw->capfp); /* * raw mode for debugging, passes through escape sequences and other * special characters as <27> symbols */ if (tw->termstate == DUMBTYPE) { for (i=0; i < len ; i++,dat++) if (*dat == 27 || *dat > 126) { sprintf(parsedat,"<%d>",*dat); VSwrite(tw->vs,parsedat,strlen(parsedat)); } else VSwrite(tw->vs,dat,1); } else { #ifdef USETEK if (tw->termstate == TEKTYPE) { i = VGwrite(temptek,dat,len); if (i < len) { leavetek(); viewmode=10; parsewrite(tw,dat+i,len-i); } } else #endif #ifdef USERAS if (tw->termstate == RASTYPE) { i = VRwrite(dat,len); if (i < len) { tw->termstate = VTEKTYPE; parsewrite(tw,dat+i,len-i); } } else #endif VSwrite(tw->vs,dat,len); /* send to virtual VT102 */ } } /*********************************************************************/ breakstop() { foundbreak = 1; return(0); } /*********************************************************************/ /* newkey * filter for command key sequences */ newkey(t1) struct twin *t1; { int c; if (foundbreak) { foundbreak = 0; c = '\003'; /* ctrl-c */ } else { if (t1->echo) c = n_chkchar(); /* a char available ?*/ else if (t1->halfdup) { /* half duplex */ c = n_chkchar(); if (c == 13) { parse(t1,"\r\n",2); /* echo crlf */ vt100key(13); c = 10; } else if (c > 0 && c < 128) parse(t1,&c,1); /* echo char */ } else { /* line mode */ c = RSgets(t1->vs,t1->linemode,79); if (c == 13) { /* pressed return */ parse(t1,"\r\n",2); /* echo the return */ strcat(t1->linemode,"\r\n"); netpush(t1->pnum); netwrite(t1->pnum,t1->linemode,strlen(t1->linemode)); t1->linemode[0] = '\0'; c = 0; } else if (c > 0) { /* write string, pass c to command interp */ if (t1->linemode[0]) { netpush(t1->pnum); netwrite(t1->pnum,t1->linemode,strlen(t1->linemode)); t1->linemode[0] = '\0'; } } } } if (c <= 0) return(c); return(dokey(t1,c)); } /************************************************************************/ /* dokey * translates and sends keys and filters for command keys * */ dokey(t1,c) struct twin *t1; int c; { int i; switch (c) { case 8: c = t1->bksp; /* allows auto del instead of bs */ break; case 13: /* different CR mappings */ vt100key(13); vt100key(t1->crfollow); c = 0; break; case 127: c = t1->del; /* switch bs around, too */ break; case THENUL: /* user wants the true NUL char */ c = 0; netwrite(t1->pnum, &c, 1); /* write a NUL */ break; case CTRLHOME: /* tek clear screen */ if (current->termstate != TEKTYPE) { current->termstate = TEKTYPE; VGgmode(rgdevice); VGuncover(temptek); } VGwrite(temptek,"\033\014",2); /* clear storage and screen */ c = 0; break; case HOME: /* clear to text */ if (leavetek()) { viewmode = 10; c = 0; } break; case ALTH: /* help display */ viewmode = 3; helpmsg(); c = 0; break; case ALTP: /* change a parameter */ parmchange(); c = 0; break; case ALTR: /* reset screen values */ if (!leavetek()) { if (current->capon) { fclose(current->capfp); current->capon = capon = 0; } VSreset(current->vs); /* reset's emulator */ } wrest(current); c = 0; break; #ifdef USETEK case ALTG: /* graphics manipulation */ c = 0; leavetek(); dispgr(); break; #endif case ALTE: leavetek(); n_window(0,0,24,79); i = n_color(current->colors[0]); dosescape(); viewmode = 5; n_color(i); c = 0; break; case ALTC: /* toggle capture */ if (capon && current->capon) { /* already on */ capstat("Capture off"); fclose(current->capfp); /* close the capture file */ current->capon = capon = 0; } else if (!capon) { /* I want one */ FILE *Sopencap(); if (NULL == (current->capfp = Sopencap())) { vprint(console->vs,"\r\nCannot open capture file "); break; } capstat("Capture on "); current->capon = capon = 1; } else { vprint(console->vs,"\r\nAnother session has capture file open, cannot open two at once\r\n"); wrest(console); viewmode=2; } c=0; break; case ALTI: /* my internet address */ sprintf(s,"%d.%d.%d.%d",myipnum[0],myipnum[1],myipnum[2],myipnum[3]); netwrite(t1->pnum,s,strlen(s)); if (!t1->echo) parse(t1,s,strlen(s)); /* echo the string */ c = 0; break; case ALTT: /* an ftp command */ if (Sneedpass()) strcpy(s,"ftp "); else strcpy(s,"ftp -n "); sprintf(&s[strlen(s)],"%d.%d.%d.%d\r\n", myipnum[0],myipnum[1],myipnum[2],myipnum[3]); netwrite(t1->pnum,s,strlen(s)); if (!t1->echo) parse(t1,s,strlen(s)); /* echo the string */ c = 0; break; case ALTQ: /* are you there? */ netpush(t1->pnum); netwrite(t1->pnum,"\377\366",2); c = 0; break; case ALTO: /* abort output */ netpush(t1->pnum); netwrite(t1->pnum,"\377\365",2); c = 0; break; case ALTY: /* interrupt */ netpush(t1->pnum); netwrite(t1->pnum,"\377\364",2); c = 0; break; case ALTU: /* erase line */ netpush(t1->pnum); netwrite(t1->pnum,"\377\370",2); c = 0; break; case ALTK: /* erase char */ netpush(t1->pnum); netwrite(t1->pnum,"\377\367",2); c = 0; break; case ALTA: /* add session */ c = 0; if (0 > addsess(NULL)) { /* start up a new one */ vprint(console->vs,"\r\nPress any key to continue . . ."); viewmode = 9; } else viewmode = 10; break; case ALTM: c = 0; leavetek(); wrest(console); viewmode = 2; /* console view mode */ break; case ALTN: /* session switch */ c = 0; leavetek(); if (current->next == NULL) /* are we only one ? */ break; current = current->next; viewmode = 10; break; case ALTS: /* skip to end */ c = 0; RSvis(0); while (0 < (i = netread(t1->pnum,s,500))) parse(t1,s,i); viewmode = 10; break; case ALTX: /* close the connection */ leavetek(); n_puts("\n Are you sure you want to close the connection? Y/N"); c = nbgetch(); /* get the answer */ if (tolower(c) == 'y') { n_puts("\n Attempting to close . . ."); netclose(t1->pnum); Stask(); netputuev(CONCLASS,CONCLOSE,t1->pnum); } else viewmode=10; /* redraw screen */ c = 0; break; case AF9: statcheck(); break; case AF3: return(AF3); break; default: break; } if (c > 0) vt100key(c); /* send it, with VT100 translation */ return(c); } /**************************************************************************/ /* dispgr * display graphics menu screen */ dispgr() { int c,i,j,k,l; c = n_color(current->colors[0]); n_clear(); n_cur(0,0); n_puts("ALT-G Graphics menu"); n_puts("< Press the appropriate function key or ESC to resume >\n"); strcpy(s," F1 - Write postscript to a file called: "); strcat(s,def.psfile); n_puts(s); n_puts( " F2 - Change postscript output file name\n"); strcpy(s," F3 - Write HPGL code to a file called: "); strcat(s,def.hpfile); n_puts(s); n_puts( " F4 - Change HPGL output file name\n"); strcpy(s," F5 - Write Tektronix 4014 codes to a file called: "); strcat(s,def.tekfile); n_puts(s); n_puts( " F6 - Change Tektronix output file name\n"); VGwhatzoom(temptek,&i,&j,&k,&l); sprintf(s," View region is currently: %d,%d,%d,%d",i,j,k,l); n_puts(s); n_puts(" F7 - Set a new view region (Zoom, Pan)"); n_puts(" RETURN - draw picture on screen in current zoom factor\n"); n_puts(" Enter choice:"); viewmode = 8; n_color(c); } /***************************************************************************/ /* dosescape * escape to dos for processing * put the connections to automated sleep while in DOS */ dosescape() { int i; n_clear(); n_cur(0,0); n_puts("Warning, some programs will interfere with network communication and can"); n_puts("cause lost connections. Do not run any network programs from this DOS shell."); n_puts("Type 'EXIT' to return to NCSA Telnet"); /* * invoke a put-to-sleep routine which calls netsleep every 8/18ths of a sec * Also: disable ftp,rcp when asleep and suppress error messages */ if (ftpact || rcpact) { n_puts("Please wait until file transfer is finished"); n_puts("\nPress any key to continue"); return(0); } tinst(); i = system("command"); /* call DOS */ tdeinst(); if (i < 0) { n_puts("\n\nError loading COMMAND.COM"); n_puts("Make sure COMMAND.COM is specified under COMSPEC."); n_puts("It must also be in a directory which is in your PATH statement."); } n_row(); n_puts("\nPress any key to return to telnet"); return(0); } /***********************************************************************/ /* creatwindow * returns a pointer to a new window */ struct twin *creatwindow() { struct twin *p; int i; p = (struct twin *)malloc(sizeof(struct twin)); if (!p) return(NULL); p->pnum = -1; p->telstate = 0; if (vton) p->termstate = basetype; else p->termstate = DUMBTYPE; p->linemode[0] = 0; p->echo = 1; p->binary = 0; p->ugoahead = 0; /* we want goahead suppressed */ p->igoahead = 0; /* we want goahead suppressed */ p->capon = 0; p->next = NULL; p->prev = NULL; p->sstat = '*'; /* connection not opened yet */ if (mp == NULL) { p->bksp = 127; p->del = 8; p->crfollow = 10; p->halfdup = 0; for (i=0; i<3; i++) /* start default colors */ p->colors[i] = colors[i]; i = VSnewscreen(0,0,80,0); /* create a new virtual screen */ } else { p->bksp = mp->bksp; if (p->bksp == 127) p->del = 8; else p->del = 127; p->crfollow = mp->crmap; p->halfdup = mp->halfdup; p->colors[0] = mp->nfcolor[0] + (mp->nbcolor[0]<<4); p->colors[2] = mp->bfcolor[0] + (mp->bbcolor[0]<<4); p->colors[1] = mp->ufcolor[0] + (mp->ubcolor[0]<<4); i = VSnewscreen(mp->bkscroll,1,80,0); /* create a new virtual screen */ if (i >= 0 && mp->vtwrap) VSwrite(i,"\033[?7h",5); /* turn wrap on */ VSscrolcontrol(i,-1,mp->clearsave); /* set clearsave flag */ } if (i < 0) return(NULL); p->vs = i; screens[i] = p; /* we need to know where it is */ return(p); } /***********************************************************************/ wrest(t) struct twin *t; { RSvis(t->vs); statline(); /* done before, moves cursor */ VSredraw(t->vs,0,0,79,23); /* redisplay, resets cursor correctly */ } /***************************************************************************/ statline() { struct twin *t1,*t2; int wn,i,c,sm,rw,cl; if (current == NULL || current == console || current->termstate == TEKTYPE) return(0); c = n_color(current->colors[0]); /* save current color */ if (current->sstat != '*') current->sstat = 254; /* this is current one */ rw = n_row(); cl = n_col(); t1 = t2 = current; wn = 0; sm = scmode(); do { n_cur(24,wn*18); n_color(t1->colors[2]); if (t1->sstat == 254 && t1 != current) t1->sstat = 176; n_putchar(t1->sstat); n_putchar(' '); i = strlen(t1->mname); if (sm) { n_cheat(t1->mname,i); /* machine name of connection */ n_cheat(blankline,16-i); /* fill out to 16 spaces */ } else { n_draw(t1->mname,i); /* machine name of connection */ n_draw(blankline,16-i); /* fill out to 16 spaces */ } if (t1->next) /* if not the only window open */ t1 = t1->next; wn++; } while (t1 != t2 && wn < 4); n_color(current->colors[0]); n_cur(24,wn*18); if (sm) n_cheat(blankline,80-wn*18); /* fill to edge of screen */ else n_draw(blankline,80-wn*18); /* fill to edge of screen */ n_color(c); n_cur(rw,cl); } /***********************************************************************/ /* inswindow * insert a window into the circular linked list * * current is used as a reference point for the new entry, the new entry * is put next in line past "current" */ inswindow(t,wtype) struct twin *t; int wtype; { struct twin *p,*q; /* * put it into the port number array */ if (wtype) wins[t->pnum] = t; /* * check for the NULL case for current. */ if (current == NULL || current == console) { current = t; statline(); return(0); } p = current; /* find surrounding elements */ if (p->prev == NULL) { /* only one now, we are adding 2nd */ t->next = p; t->prev = p; p->next = t; p->prev = t; } else { /* adding third or more */ q = p->next; /* find next one */ t->prev = p; t->next = q; /* insert it as next after current */ q->prev = t; p->next = t; } } /***********************************************************************/ /* delwindow() * take a window out of the linked list */ delwindow(t,wtype) struct twin *t; int wtype; { struct twin *p,*q; if (wtype) wins[t->pnum] = NULL; /* take out of array */ p = t->prev; q = t->next; if (p == NULL) { /* is only node */ freewin(t); current = console; return(0); } if (p == q) { /* two in list */ p->next = NULL; p->prev = NULL; } else { q->prev = p; p->next = q; /* merge two links */ } freewin(t); /* release the space */ return(0); } /************************************************************************/ /* freewin * deallocate and detach all associated memory from a window */ freewin(t) struct twin *t; { VSdetatch(t->vs); free(t); return(0); } /************************************************************************/ /* * hexbyte * return a byte taken from a string which contains hex digits */ hexbyte(st) char *st; { int i; if (*st >= 'A') i = ((*st|32)-87)<<4; else i = (*st-48)<<4; st++; if (*st > 'A') i |= (*st | 32)-87; else i += (*st-48); return(i); } /***********************************************************************/ /* tekinit * tektronix initialization */ #ifdef USETEK tekinit(dev) char *dev; { int i; if (strlen(dev) < 1 ) return(0); if (0 > VGinit()) { vprint(console->vs,"\r\nCannot initialize Tektronix driver\r\n"); return(-1); } else vprint(console->vs,"\r\nTektronix initialized\r\n"); if (!strcmp(dev,"no9")) rgdevice = 4; else if (!strcmp(dev,"ega")) rgdevice = 1; #ifdef why else if (!strcmp(dev,"ps")) rgdevice = 2; and the hplot is dev 6 #endif else if (!strcmp(dev,"hercules")) rgdevice = 3; else if (!strcmp(dev,"cga") || !strcmp(dev,"pga")) rgdevice = 5; else rgdevice = 0; /* null device */ basetype = VTEKTYPE; temptek = VGnewwin(rgdevice); /* default for drawing */ return(0); } /***********************************************************************/ /* function to write to file */ fdump(str) char *str; { fputs(str,tekfp); } fdumpc(c) char c; { fputc(c,tekfp); } endump() { VGclose(outdev); if (indev != temptek) { VGclose(indev); if (tekfp) { fclose(tekfp); tekfp = NULL; } } } /***********************************************************************/ /* graphit * Get some user choices and execute them */ graphit() { int i,j,k,l,c; c = n_chkchar(); if (c == 27) return(1); if (c < 0) return(0); switch (c) { case F2: case F4: case F6: /* prompting for file name */ n_puts("\nEnter new file name:"); nbgets(s,50); if (s[0] && s[0] != ' ') { switch (c) { case F2: Snewpsfile(s); break; case F4: Snewhpfile(s); break; case F6: Snewtekfile(s); break; } Sgetconfig(&def); } dispgr(); /* leave in graphit mode */ return(0); break; case F1: /* postscript dump */ if (*def.psfile == '+') { if (NULL == (tekfp = fopen(&def.psfile[1],"a"))) return(1); fseek(tekfp,0L,2); /* to end */ } else if (NULL == (tekfp = fopen(def.psfile,"w"))) return(1); RGPoutfunc(&fdump); /* set function */ outdev = VGnewwin(2); indev = temptek; temptek = VGnewwin(rgdevice); VGgmode(2); VGzcpy(indev,temptek); VGzcpy(indev,outdev); VGuncover(outdev); VGpage(outdev); if (VGpred(indev,outdev)) endump(); else netputevent(USERCLASS,PSDUMP,1); /* remind myself */ return(1); break; case F3: /* HPGL dump */ if (*def.hpfile == '+') { /* append feature */ if (NULL == (tekfp = fopen(&def.hpfile[1],"a"))) return(1); fseek(tekfp,0L,2); } else if (NULL == (tekfp = fopen(def.hpfile,"w"))) return(1); RGHPoutfunc(&fdump); /* set function */ outdev = VGnewwin(6); indev = temptek; temptek = VGnewwin(rgdevice); VGgmode(6); VGzcpy(indev,temptek); VGzcpy(indev,outdev); VGuncover(outdev); VGpage(outdev); if (VGpred(indev,outdev)) endump(); else netputevent(USERCLASS,PSDUMP,1); /* remind myself */ return(1); break; case F5: /* tektronix dump */ if (*def.tekfile == '+') { if (NULL == (tekfp = fopen(&def.tekfile[1],"ab"))) return(1); fseek(tekfp,0L,2); } else if (NULL == (tekfp = fopen(def.tekfile,"wb"))) return(1); fputs("\033\014",tekfp); VGdumpstore(temptek,&fdumpc); fclose(tekfp); return(1); break; case F7: /* tek view region */ n_puts("\nEnter 0-4095 for lower left xy, upper right xy."); n_puts("\nExample: 0,0,4095,3119 is full view. (default if you leave it blank)"); nbgets(s,30); if (4 != sscanf(s,"%d,%d,%d,%d",&i,&j,&k,&l)) VGzoom(temptek,0,0,4096,3119); else VGzoom(temptek,i,j,k,l); dispgr(); /* leave in graphit mode */ return(0); break; case 13: current->termstate = TEKTYPE; VGgmode(rgdevice); VGuncover(temptek); outdev = temptek; /* redraw to itself */ indev = temptek; tekfp = NULL; if (!VGpred(indev,outdev)) netputevent(USERCLASS,PSDUMP,0); /* remind myself */ viewmode = 0; /* normal logon state */ break; default: break; } return(0); } #endif /***********************************************************************/ /* menu support * New 5/22/88 - TK * Provide menus with a reasonable user interface for the user to * select colors or other parameters. * * This menu system provides two types of entries. * 1. Up to nine static choices, 0-8. Each static choice is encoded * into the data structure and automatically rotated by the user. * The user cannot select an illegal value. * 2. A string choice. A maximum string length is honored. * There must be at least 20 characters open for each field, longer * if the field is longer. Static choices cannot be longer than 20 chars. * */ /* * structure for the menu entries */ struct pt { int posx,posy, /* row and col position on screen for field */ choice, /* which is the currently visible selection */ replen; /* length of reply if a string is allowed to be entered */ char *vals[9]; /* pointers to the actual choices */ }; #define LO 5 struct pt pc[] = { /* session colors */ {LO+0,44,0,0,"black","blue","green","cyan","red","magenta","yellow","white",NULL}, {LO+1,44,0,0,"black","blue","green","cyan","red","magenta","yellow","white",NULL}, {LO+2,44,0,0,"black","blue","green","cyan","red","magenta","yellow","white",NULL}, {LO+3,44,0,0,"black","blue","green","cyan","red","magenta","yellow","white",NULL}, {LO+4,44,0,0,"black","blue","green","cyan","red","magenta","yellow","white",NULL}, {LO+5,44,0,0,"black","blue","green","cyan","red","magenta","yellow","white",NULL}, /* things which also apply to this session */ {LO+6,44,0,0,"Local echo","Remote echo",NULL}, {LO+7,44,1,0,"Backspace","Delete",NULL}, {LO+8,44,0,20,NULL," ",NULL}, {LO+9,44,0,0,"VT102 and Tek4014","Dumb TTY","VT102 only",NULL}, /* things which apply over all of telnet */ {LO+11,44,0,35,NULL," ",NULL}, {LO+12,44,1,0,"Use BIOS","Direct to screen",NULL}, {LO+13,44,0,0,"Disabled","Enabled",NULL}, {LO+0,0} }; /************************************************************************/ /* menuit * draw the current line at the required position. * If the field length (replen) is longer than 20 chars, fill in the * entire field width. All fields are padded with spaces to their * length when printed, but stored without padding. */ menuit(p,n) struct pt p[]; int n; { int i; char fmt[12]; n_cur(p[n].posx,p[n].posy); if ((i = p[n].replen) < 20) /* i = larger of replen and 20 */ i = 20; sprintf(fmt,"%%-%d.%ds",i,i); /* create format string */ sprintf(s,fmt,p[n].vals[p[n].choice]); /* put out value */ n_puts(s); } /************************************************************************/ /* makechoice * Allow the user to travel between choices on the screen, selecting * from a list of legal options. * Arrow keys change the selections on the screen. The data structure * must be set up before entering this procedure. */ makechoice(p,maxp,spec) struct pt p[]; int maxp,spec; { int i,oldln,ln,c; oldln = ln = 0; n_color(7); for (i=1; i= maxp) ln = 0; break; case PGUP: case HOME: ln = 0; break; case PGDN: case ENDKEY: ln = maxp-1; break; case 32: /* space, tab and arrows change field contents */ case 9: case CURRT: i = ++p[ln].choice; /* if blank or non-existant, reset to 0 */ if (!p[ln].vals[i] || !(*p[ln].vals[i]) || ' ' == *p[ln].vals[i]) p[ln].choice = 0; break; case CURLF: if (p[ln].choice) p[ln].choice--; else { /* if at zero, search for highest valid value */ i = 0; while (p[ln].vals[i] && *p[ln].vals[i] && ' ' != *p[ln].vals[i]) i++; if (i) p[ln].choice = i-1; } break; case 8: case 21: case 13: /* BS, Ctrl-U, or return */ /* * if allowed, the user can enter a string value. * prepare the field, by printing in a different color to set it apart. */ if (p[ln].replen) { p[ln].choice = 1; n_color(1); /* underline color */ n_cur(p[ln].posx,p[ln].posy); for (i=0; i\n"); n_puts(" --------------- Color setup and session parameters ----------------- "); n_puts(" Text: "); n_puts(" Normal Foreground (nfcolor) - "); n_puts(" Normal Background (nbcolor) - "); n_puts(" Reverse Foreground (rfcolor) - "); n_puts(" Reverse Background (rbcolor) - "); n_puts(" Underline Foreground (ufcolor) - "); n_puts(" Underline Background (ubcolor) - "); n_puts(" Use remote echo or local echo - "); n_puts(" Backspace key sends - "); n_puts(" Session name *>"); n_puts(" Terminal type - "); n_puts(" -------------- Parameters which apply to all sessions -------------- "); n_puts(" Capture file name *>"); n_puts(" Screen mode (for BIOS compatibility) - "); n_puts(" File transfer is - "); n_puts("\n\nUse arrow keys to select, Enter clears changeable field (*>)"); /* * set values for menus from our telnet-stored values */ i = current->colors[0]; /* session colors */ pc[0].choice = i & 15; pc[1].choice = i >> 4; i = current->colors[2]; pc[2].choice = i & 15; pc[3].choice = i >> 4; i = current->colors[1]; pc[4].choice = i & 15; pc[5].choice = i >> 4; pc[6].choice = current->echo; if (current->bksp == 8) /* backspace setting */ pc[7].choice = 0; else pc[7].choice = 1; pc[8].vals[0] = current->mname; /* session name */ pc[9].choice = current->termstate - 1; /* terminal type */ pc[10].vals[0] = def.capture; /* capture file name */ pc[11].choice = scmode(); /* screen write mode */ pc[12].choice = transok; /* filetransfer enable */ if (makechoice(&pc,13,1)) { /* call it and check the results */ /* * Work on results, only if user pressed 'F1', if ESC, this is skipped. * * * check for new capture file name */ if (pc[10].choice) { strcpy(s,pc[10].vals[1]); if (s[0] && s[0] != ' ') { /* no NULL names */ Snewcap(s); Sgetconfig(&def); } *pc[10].vals[1] = 0; pc[10].choice = 0; } /* * check for new screen mode, BIOS or not */ if (pc[11].choice != scmode()) Scwritemode(pc[11].choice); /* set to whatever choice is */ /* * check whether to enable or disable file transfers */ if (pc[12].choice != transok) { transok = pc[12].choice; Sftpmode(transok); Srcpmode(transok); } /* * check remote or local echo mode */ if (pc[6].choice != current->echo) { if (current->echo = pc[6].choice) { /* assign = is on purpose */ sprintf(s,"%c%c%c",255,DOTEL,1); /* telnet negotiation */ netpush(current->pnum); netwrite(current->pnum,s,3); } else { sprintf(s,"%c%c%c",255,DONTTEL,1); netpush(current->pnum); netwrite(current->pnum,s,3); } } /* * check function of backspace or delete */ if (pc[7].choice) { current->bksp = 127; /* backspace/delete are swapped */ current->del = 8; } else { current->bksp = 8; /* are normal */ current->del = 127; } /* * check new session name */ if (pc[8].choice) { strcpy(s,pc[8].vals[1]); if (s[0] != ' ' && s[0]) { /* limit of 14 chars stored */ strncpy(current->mname,s,15); current->mname[14] = 0; } *pc[8].vals[1] = 0; pc[8].choice = 0; } /* * check terminal type */ if (pc[6].choice != current->termstate-1) current->termstate = pc[9].choice + 1; /* * assign new colors */ i = pc[0].choice + (pc[1].choice <<4); /* normal color */ if (i != current->colors[0]) { current->colors[0] = i; RSsetatt(127,current->vs); /* seed the current screen */ n_color(current->colors[0]); /* seed ncolor */ } current->colors[1] = pc[4].choice + (pc[5].choice <<4); current->colors[2] = pc[2].choice + (pc[3].choice <<4); } /* * go back to telnet */ n_color(colsave); wrest(current); } /*************************************************************************/ /* addsess * Add a session to a named machine, or prompt for a machine to be named. */ addsess(st) char *st; { int i,new,cv; struct twin *newin; leavetek(); cv = console->vs; if (st == NULL) { /* no machine yet */ wrest(console); vprint(cv,"\n\r\nEnter new machine name/address, ESC to return: \r\n"); s[0] = '\0'; while (0 >= (i = RSgets(cv,s,70))) Stask(); if (i == 27 || !s[0]) return(1); vprint(cv,"\r\n\n"); /* skip down a little */ st = s; /* make a copy of the pointer to s */ } mp = Sgethost(st); /* gain access to host information */ errhandle(); if (!mp) { if (Sdomain(st) > 0) vprint(cv,"\r\nQuerying the DOMAIN name server\r\n"); else { vprint(cv,"\r\nNo nameserver, cannot resolve IP address\r\n"); return(-1); } } else { /* * tell user about it on the console */ vprint(cv,"\r\nTrying to open TCP connection to: "); vprint(cv,st); vprint(cv,"\r\n"); /* try to serve the request */ if ( 0 > (new = Snetopen(mp,HTELNET))) { errhandle(); vprint(cv,"\r\nCould not open new connection to: "); vprint(cv,st); vprint(cv,"\r\n"); return(-1); } newin = creatwindow(); if (!newin) { /* mem error */ vprint(console->vs,"\r\nMemory Allocation error for window\r\n"); return(-1); } newin->pnum = new; strncpy(newin->mname,st,14); newin->mname[14] = 0; inswindow(newin,1); vhead(newin->vs); } return(0); } #ifdef USETEK leavetek() { if (current->termstate == TEKTYPE) { VGwrite(temptek,"\037",1); /* force to alpha */ current->termstate = VTEKTYPE; VGtmode(rgdevice); /* clear graphics mode */ return(1); } return(0); /* we did nothing */ } #endif /***********************************************************************/ /* capstat * */ capstat(s) char *s; { int r,c; r = n_row(); c = n_col(); n_cur(24,54); n_draw(s,strlen(s)); n_cur(r,c); } /***********************************************************************/ /* scrollback * Take keyboard keys to manipulate the screen's scrollback * */ scrollback() { int c; if (current->termstate == TEKTYPE) return(0); while (0 < ( c = n_chkchar())) { switch (c) { case CURUP: VSscrolback(current->vs,1); break; case CURDN: VSscrolforward(current->vs,1); break; case PGUP: VSscrolback(current->vs,22); break; case PGDN: VSscrolforward(current->vs,22); break; default: break; } } } owed, the user can enter a string value. * prepare the field, by printing in a different color to set it apart. */ if (p[ln].replen) { p[ln].choice = 1; n_color(1); /* underline color */ n_cur(p[ln].posx,p[ln].posy); for (i=0; imstat < HAVEIP) return(-1); j = netxopen(m->hostip,tport,m->retrans,m->mtu,m->maxseg,m->window); /* do the open call */ if (j >= 0) { Sptypes[j] = -1; /* is allocated to user */ Stimerset(CONCLASS,CONFAIL,j,m->conto); Stimerset(SCLASS,RETRYCON,j,m->retrans/TICKSPERSEC+2); } return(j); } /**************************************************************************/ /* * special domain data structures */ #define DOMSIZE 512 /* maximum domain message size to mess with */ /* * Header for the DOMAIN queries * ALL OF THESE ARE BYTE SWAPPED QUANTITIES! * We are the poor slobs who are incompatible with the world's byte order * Mac people ignore the previous line. */ struct dhead { uint16 ident, /* unique identifier */ flags, qdcount, /* question section, # of entries */ ancount, /* answers, how many */ nscount, /* count of name server RRs */ arcount; /* number of "additional" records */ }; /* * flag masks for the flags field of the DOMAIN header */ #define DQR 0x8000 /* query = 0, response = 1 */ #define DOPCODE 0x7100 /* opcode, see below */ #define DAA 0x0400 /* Authoritative answer */ #define DTC 0x0200 /* Truncation, response was cut off at 512 */ #define DRD 0x0100 /* Recursion desired */ #define DRA 0x0080 /* Recursion available */ #define DRCODE 0x000F /* response code, see below */ /* opcode possible values: */ #define DOPQUERY 0 /* a standard query */ #define DOPIQ 1 /* an inverse query */ #define DOPCQM 2 /* a completion query, multiple reply */ #define DOPCQU 3 /* a completion query, single reply */ /* the rest reserved for future */ /* legal response codes: */ #define DROK 0 /* okay response */ #define DRFORM 1 /* format error */ #define DRFAIL 2 /* their problem, server failed */ #define DRNAME 3 /* name error, we know name doesn't exist */ #define DRNOPE 4 /* no can do request */ #define DRNOWAY 5 /* name server refusing to do request */ #define DTYPEA 1 /* host address resource record (RR) */ #define DTYPEPTR 12 /* a domain name ptr */ #define DIN 1 /* ARPA internet class */ #define DWILD 255 /* wildcard for several of the classifications */ /* * a resource record is made up of a compressed domain name followed by * this structure. All of these ints need to be byteswapped before use. */ struct rrpart { uint16 rtype, /* resource record type = DTYPEA */ rclass; /* RR class = DIN */ uint32 rttl; /* time-to-live, changed to 32 bits */ uint16 rdlength; /* length of next field */ uint8 rdata[DOMSIZE]; /* data field */ }; /* * data for domain name lookup */ struct useek { struct dhead h; uint8 x[DOMSIZE]; } question; qinit() { question.h.flags = intswap(DRD); question.h.qdcount = intswap(1); question.h.ancount = 0; question.h.nscount = 0; question.h.arcount = 0; } /*********************************************************************/ /* packdom * pack a regular text string into a packed domain name, suitable * for the name server. */ packdom(dst,src) char *src,*dst; { char *p,*q,*savedst; int i,dotflag,defflag; p = src; dotflag = defflag = 0; savedst = dst; do { /* copy whole string */ *dst = 0; q = dst + 1; /* * copy the next label along, char by char until it meets a period or * end of string. */ while (*p && (*p != '.')) *q++ = *p++; i = p - src; if (i > 0x3f) return(-1); *dst = i; *q = 0; if (*p) { /* update pointers */ dotflag = 1; src = ++p; dst = q; } else if (!dotflag && !defflag && Scon.defdom) { p = Scon.defdom; /* continue packing with default */ defflag = 1; src = p; dst = q; netposterr(801); /* using default domain */ } } while (*p); q++; return(q-savedst); /* length of packed string */ } /*********************************************************************/ /* unpackdom * Unpack a compressed domain name that we have received from another * host. Handles pointers to continuation domain names -- buf is used * as the base for the offset of any pointer which is present. * returns the number of bytes at src which should be skipped over. * Includes the NULL terminator in its length count. */ unpackdom(dst,src,buf) char *src,*dst,buf[]; { int i,j,retval; char *savesrc; savesrc = src; retval = 0; while (*src) { j = *src; while ((j & 0xC0) == 0xC0) { if (!retval) retval = src-savesrc+2; src++; src = &buf[(j & 0x3f)*256+*src]; /* pointer dereference */ j = *src; } src++; for (i=0; i < (j & 0x3f) ; i++) *dst++ = *src++; *dst++ = '.'; } *(--dst) = 0; /* add terminator */ src++; /* account for terminator on src */ if (!retval) retval = src-savesrc; return(retval); } /*********************************************************************/ /* sendom * put together a domain lookup packet and send it * uses port 53 */ sendom(s,towho,num) char *s,*towho; int16 num; { uint16 i,j,ulen; uint8 *psave,*p; psave = (uint8 *)&question.x; i = packdom(&question.x,s); /* * load the fields of the question structure a character at a time so * that 68000 machines won't choke. */ p = &question.x[i]; *p++ = 0; /* high byte of qtype */ *p++ = DTYPEA; /* number is < 256, so we know high byte=0 */ *p++ = 0; /* high byte of qclass */ *p++ = DIN; /* qtype is < 256 */ question.h.ident = intswap(num); ulen = sizeof(struct dhead)+(p-psave); netusend(towho,53,997,&question,ulen); } /**************************************************************************/ /* Sdomain * DOMAIN based name lookup * query a domain name server to get an IP number * Returns the machine number of the machine record for future reference. * Events generated will have this number tagged with them. * Returns various negative numbers on error conditions. */ Sdomain(mname) char *mname; { struct machinfo *m; if (!Sns) /* no nameserver, give up now */ return(-1); while (*mname && *mname < 33) /* kill leading spaces */ mname++; if (!(*mname)) return(-1); if (!(m = Smadd(mname))) return(-1); /* adds the number to the machlist */ if (domwait < Scon.domto) domwait = Scon.domto; /* set the minimum timeout */ qinit(); /* initialize some flag fields */ netulisten(997); /* pick a return port */ if (!m->hname) m->hname = m->sname; /* copy pointer to sname */ sendom(m->hname,Sns->hostip,m->mno); /* try UDP */ Stimerset(SCLASS,UDPTO,m->mno,domwait); /* time out quickly first time */ m->mstat = UDPDOM; return(m->mno); } /*********************************************************************/ /* getdomain * Look at the results to see if our DOMAIN request is ready. * It may be a timeout, which requires another query. */ udpdom() { struct machinfo *m; int i,uret,num; char *p; uret = neturead(&question); if (uret < 0) { /* netputevent(USERCLASS,DOMFAIL,-1); */ return(-1); } num = intswap(question.h.ident); /* get machine number */ /* * check to see if the necessary information was in the UDP response */ m = Slooknum(num); /* get machine info record */ if (!m) { netputevent(USERCLASS,DOMFAIL,num); return(-1); } /* * got a response, so reset timeout value to recommended minimum */ domwait = Scon.domto; i = ddextract(&question,m->hostip); #ifdef testing { int j; j = creat("tmp",0); write(j,&question,500); close(j); } #endif switch (i) { case 3: /* name does not exist */ netposterr(802); p = neterrstring(-1); strncpy(p,m->hname,78); /* what name */ netposterr(-1); netputevent(USERCLASS,DOMFAIL,num); Stimerunset(SCLASS,UDPTO,num); return(-1); case 0: /* we found the IP number */ Stimerunset(SCLASS,UDPTO,num); m->mstat = DOM; /* mark that we have it from DOMAIN */ netputevent(USERCLASS,DOMOK,num); return(0); case -1: /* strange return code from ddextract */ netposterr(803); break; default: netposterr(804); break; } return(0); } /**************************************************************************/ /* domto * Handle time out for DOMAIN name lookup * Retry as many times as recommended by config file */ domto(num) int num; { struct machinfo *m; m = Slooknum(num); if (!m) return(-1); if (m->mstat > UDPDOM + Scon.ndom) { /* permanent timeout */ netputevent(USERCLASS,DOMFAIL,num); return(-1); } else m->mstat++; /* one more timeout */ if (domwait < 20) /* exponential backoff */ domwait <<= 1; Snewns(); /* rotate to next nameserver */ qinit(); netulisten(997); /* pick a return port */ sendom(m->hname,Sns->hostip,num); /* try UDP */ Stimerset(SCLASS,UDPTO,num,domwait); /* time out more slowly */ return(num); } /*********************************************************************/ /* ddextract * extract the ip number from a response message. * returns the appropriate status code and if the ip number is available, * copies it into mip */ ddextract(qp,mip) struct useek *qp; unsigned char *mip; { uint16 i,j,nans,rcode; struct rrpart *rrp; uint8 *p,space[260]; nans = intswap(qp->h.ancount); /* number of answers */ rcode = DRCODE & intswap(qp->h.flags); /* return code for this message*/ if (rcode > 0) return(rcode); if (nans > 0 && /* at least one answer */ (intswap(qp->h.flags) & DQR)) { /* response flag is set */ p = (uint8 *)&qp->x; /* where question starts */ i = unpackdom(space,p,qp); /* unpack question name */ /* spec defines name then QTYPE + QCLASS = 4 bytes */ p += i+4; /* * at this point, there may be several answers. We will take the first * one which has an IP number. There may be other types of answers that * we want to support later. */ while (nans-- > 0) { /* look at each answer */ i = unpackdom(space,p,qp); /* answer name to unpack */ /* n_puts(space);*/ p += i; /* account for string */ rrp = (struct rrpart *)p; /* resource record here */ /* * check things which might not align on 68000 chip one byte at a time */ if (!*p && *(p+1) == DTYPEA && /* correct type and class */ !*(p+2) && *(p+3) == DIN) { movebytes(mip,rrp->rdata,4); /* save IP # */ return(0); /* successful return */ } movebytes(&j,&rrp->rdlength,2); /* 68000 alignment */ p += 10+intswap(j); /* length of rest of RR */ } } return(-1); /* generic failed to parse */ } /***********************************************************************/ static int son=1; Scwritemode(mode) int mode; { son = mode; return(0); } /***********************************************************************/ Scmode() { return(son); } /***********************************************************************/ static int tekon=1; Stekmode(mode) int mode; { tekon = mode; return(0); } /***********************************************************************/ Stmode() { return(tekon); } /***********************************************************************/ #ifdef PC static int rcpon=1; Srcpmode(mode) int mode; { rcpon = mode; if (rcpon) setrshd(); else unsetrshd(); return(0); } /***********************************************************************/ Srmode() { return(rcpon); } #endif /***********************************************************************/ static int ftpon=0; Sftpmode(mode) int mode; { if (ftpon && mode) return(-1); ftpon = mode; if (ftpon) setftp(); else unsetftp(); return(0); } /***********************************************************************/ Sfmode() { return(ftpon); } /***********************************************************************/ /* Snewcap * set a new capture file name */ Snewcap(s) char *s; { if (NULL == (Scon.capture = malloc(strlen(s)+1))) return(1); strcpy(Scon.capture,s); return(0); } /***********************************************************************/ /* Snewps * set a new ps file name */ Snewpsfile(s) char *s; { if (NULL == (Scon.psfile = malloc(strlen(s)+1))) return(1); strcpy(Scon.psfile,s); return(0); } /***********************************************************************/ /* Snewhpfile * set a new HPGL file name */ Snewhpfile(s) char *s; { if (NULL == (Scon.hpfile = malloc(strlen(s)+1))) return(1); strcpy(Scon.hpfile,s); return(0); } /***********************************************************************/ /* Snewtekfile * set a new tek file name */ Snewtekfile(s) char *s; { if (NULL == (Scon.tekfile = malloc(strlen(s)+1))) return(1); strcpy(Scon.tekfile,s); return(0); } /***********************************************************************/ /* Sopencap * returns a file handle to an open capture file * Uses the locally stored capture file name. */ FILE * Sopencap() { FILE *retfp; if (NULL == (retfp = fopen(Scon.capture,"ab"))) return(NULL); fseek(retfp,0L,2); /* seek to end */ return(retfp); } /**************************************************************************/ /* Stask * A higher level version of netsleep * * This manages the timer queue */ static int32 recent=0L; Stask() { long t; int i; netsleep(0); /* * Check the timer queue to see if something should be posted * First check for timer wraparound */ t = time(NULL); #ifdef PC if (t < recent) { i = Stfirst; while (i >= 0) { Stq[i].when -= WRAPTIME; i = Stq[i].next; } } #endif recent = t; /* save most recent time */ while (Stfirst >= 0 && t > Stq[Stfirst].when) { /* Q is not empty and timer is going off */ i = Stfirst; netputevent(Stq[i].eclass,Stq[i].event,Stq[i].idata); Stfirst = Stq[Stfirst].next; /* remove from q */ Stq[i].next = Stfree; Stfree = i; /* add to free list */ } } /**************************************************************************/ /* Stimerset * Sets an asynchronous timer which is checked in Stask() * usage: * Time is in seconds * Stimerset(class,event,dat,time) * int class,event,dat,time; * class,event,dat is the event which should be posted at the specified * time. Accuracy is dependent on how often Stask is called. */ Stimerset(class,event,dat,howlong) int class,event,dat,howlong; { int i,j,jlast,retval; int32 gooff; retval = 0; gooff = time(NULL) + howlong; if (Stfree < 0) { /* queue is full, post first event */ Stfree = Stfirst; Stfirst = Stq[Stfirst].next; Stq[Stfree].next = -1; netputevent(Stq[Stfree].eclass,Stq[Stfree].event,Stq[Stfree].idata); retval = -1; } Stq[Stfree].idata = dat; /* event to occur at that time */ Stq[Stfree].event = event; Stq[Stfree].eclass = class; Stq[Stfree].when = gooff; i = Stfree; /* remove from free list */ Stfree = Stq[i].next; if (Stfirst < 0) { /* if no queue yet */ Stfirst = i; Stq[i].next = -1; /* anchor active q */ } else if (gooff < Stq[Stfirst].when) { /* goes first on list */ Stq[i].next = Stfirst; /* at beginning of list */ Stfirst = i; } else { /* goes in middle */ j = jlast = Stfirst; /* search q from beginning */ while (gooff >= Stq[j].when && j >= 0) { jlast = j; j = Stq[j].next; } Stq[i].next = j; /* insert in q */ Stq[jlast].next = i; } return(retval); } /****************************************************************************/ /* Stimerunset * Remove all such timer events from the queue * They must match all three fields, event, class and optional data * */ Stimerunset(class,event,dat) unsigned char event,class; int dat; { int i,ilast,retval; retval = ilast = -1; i = Stfirst; while (i >= 0 ) { /* search list */ if (Stq[i].idata == dat && /* this one matches */ Stq[i].eclass == class && Stq[i].event == event) { retval = 0; /* found at least one */ /* * major bug fix -- if first element matched, old code could crash * fixed Spring 88 */ if (i == Stfirst) { Stfirst = Stq[i].next; /* first one matches */ Stq[i].next = Stfree; /* attach to free list */ Stfree = i; i = Stfirst; continue; /* start list over */ } else { Stq[ilast].next = Stq[i].next; /* remove this entry */ Stq[i].next = Stfree; /* attach to free list */ Stfree = i; i = ilast; } } ilast = i; i = Stq[i].next; } return(retval); } /****************************************************************************/ /* Scheckpass * Check the password file for the user, password combination * Returns valid or invalid */ Scheckpass(us,ps) char *us,*ps; { char buf[81],*p; FILE *fp; int i; if (NULL == (fp = fopen(Scon.pass,"r"))) return(0); while (NULL != fgets(buf,80,fp)) { p = strchr(buf,'\n'); *p = '\0'; /* remove \n */ p = strchr(buf,':'); /* find delimiter */ *p++ = '\0'; if (!strcmp(buf,us) && /* found user */ Scompass(ps,p)) { /* does password check ?*/ fclose(fp); return(1); } } fclose(fp); return(0); } /****************************************************************************/ /* Sneedpass * For other routines to call and find out if a password is required */ Sneedpass() { if (Scon.pass == NULL) return(0); return(1); } /****************************************************************************/ /* Scompass * compute and check the encrypted password */ Scompass(ps,en) char *ps,*en; { int ck; char *p,c; ck = 0; p = ps; while (*p) /* checksum the string */ ck += *p++; c = ck; while (*en) { if ((((*ps ^ c) | 32) & 127) != *en) /* XOR with checksum */ return(0); if (*ps) ps++; else c++; /* increment checksum to hide length */ en++; } return(1); } /****************************************************************************/ /* Sgetevent * gets events from the network and filters those for session related * activity. Returns any other events to the caller. */ Sgetevent(class,what,datp) int class,*what,*datp; { int retval; if (retval = netgetevent(SCLASS,what,datp)) { /* session event */ switch (retval) { case FTPACT: ftpd(0,*datp); break; #ifdef PC case RCPACT: /* give CPU to rsh for rcp */ rshd(0); break; #endif case UDPTO: /* name server not responding */ domto(*datp); break; case RETRYCON: if (0 < netopen2(*datp)) /* connection open yet? */ Stimerset(SCLASS,RETRYCON,*datp,4); /* 4 is a kludge */ break; default: break; } } Stask(); /* allow net and timers to take place */ if (!(retval = netgetevent(class,what,datp))) return(0); if (retval == CONOPEN) Stimerunset(CONCLASS,CONFAIL,*datp); /* kill this timer */ if ((*datp == 997) && (retval == UDPDATA)) { udpdom(); } else if ((*what == CONCLASS) && (Sptypes[*datp] >= 0)) { /* might be for session layer */ switch (Sptypes[*datp]) { case PFTP: rftpd(retval); break; case PDATA: ftpd(retval,*datp); break; #ifdef PC case PRCP: rshd(retval); break; #endif default: break; } } else return(retval); /* let higher layer have it */ return(0); } return(tekon); } /***********************************************************************/ #ifdef PC static int rcpon=1; Srcpmode(mode) int mode; { rcpon = mode; if (rcpon) setrshd(); else unsetrshd(); return(0); } /***********************************************************************/ Srmode() { return(rcpon); } #endif /******************************************************************source/pctools.c 644 144 13 21516 4267454335 7272 /* * pctools.c **************************************************************************** * * * part of: * * TCP/UDP/ICMP/IP Network kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * * those generic tool-type things that only work on PCs. * includes all hardware-level calls to Ethernet that are unique to the PC * * Function pointers for Ether calls */ #include "stdio.h" #include "protocol.h" #include "data.h" /* * defined in assembly language file for interrupt driven Ether buffering * */ unsigned char stat; /* status from last read */ char *bufpt,*bufend,*bufread,*buforg; int bufbig,buflim; /* * Declare each and every Ethernet driver. * To add a driver, pick a unique 2 char prefix and declare your own * routines. I want to keep the same parameters for EVERY driver. * If your driver needs additional parameters, then see netconfig() below * for an indication of where to put custom board code. */ extern int E1etopen(),E1getaddr(),E1setaddr(),E1recv(),E1xmit(),E1etupdate(),E1etclose(); extern int E3etopen(),E3getaddr(),E3setaddr(),E3recv(),E3xmit(),E3etupdate(),E3etclose(); extern int M5etopen(),M5getaddr(),M5recv(),M5xmit(),M5etupdate(),M5etclose(); extern int U1etopen(),U1getaddr(),U1recv(),U1xmit(),U1etupdate(),U1etclose(); extern int U2etopen(),U2getaddr(),U2recv(),U2xmit(),U2etupdate(),U2etclose(); extern int WDetopen(),WDgetaddr(),WDrecv(),WDxmit(),WDetupdate(),WDetclose(); extern int E2etopen(),E2getaddr(),E2recv(),E2xmit(),E2etupdate(),E2etclose(); static int (*etopen)()=NULL, /* open the device */ (*getaddr)()=NULL, /* get the Ether address */ (*setaddr)()=NULL, /* set the Ether address to use */ (*recv)()=NULL, /* load a packet from queue */ (*etupdate)()=NULL, /* update pointers in buffer */ (*etclose)()=NULL, /* shut down network */ (*xmit)()=NULL; /* transmit a packet */ /**********************************************************************/ /* statcheck * look at the connection status of the memory buffers to see if the * allocation schemes are working. Only used as a debug tool. */ statcheck() { int i; struct port *p; for (i=0; i<20; i++) { printf("\n%d > ",i); p = portlist[i]; if (p != NULL) printf("state: %d %5u %5u %10ld %5d %5d", p->state,intswap(p->tcpout.t.source), intswap(p->tcpout.t.dest),p->out.lasttime,p->rto, p->out.contain); } } /*************************************************************************/ /* config network parameters * Set IRQ and DMA parameters for initialization of the 3com adaptor */ static int nnirq=3,nnaddr=0xd000,nnioaddr=0x300; netparms(irq,address,ioaddr) int irq,address,ioaddr; { nnirq = irq; nnaddr = address; nnioaddr = ioaddr; return(0); } /**********************************************************************/ /* netconfig * load the function pointers for network access * Currently setaddr() is not used, so it isn't loaded. * * Note that netparms is called BEFORE netconfig. So if you have any * really special variables to set for your board that involve * irq,address and ioaddr, you can add calls to your special routines * in this section. * * Some drivers will do the interrupt driver and board initialization * in etopen() and some will do it in getaddr(). */ netconfig(s) char *s; { if (!strncmp(s,"ni5",3) || !strncmp(s,"mi",2)) { etopen = M5etopen; xmit = M5xmit; recv = M5recv; getaddr = M5getaddr; etupdate = M5etupdate; etclose = M5etclose; /* * special initialization call would go here */ } else if (!strncmp(s,"nicps",5)) { etopen = U2etopen; xmit = U2xmit; recv = U2recv; getaddr = U2getaddr; etupdate = U2etupdate; etclose = U2etclose; } else if (!strncmp(s,"nicpc",5) || !strncmp(s,"pcnic",5)) { etopen = U1etopen; xmit = U1xmit; recv = U1recv; getaddr = U1getaddr; etupdate = U1etupdate; etclose = U1etclose; } else if (!strncmp(s,"wd",2) || !strncmp(s,"800",3)) { etopen = WDetopen; xmit = WDxmit; recv = WDrecv; getaddr = WDgetaddr; etupdate = WDetupdate; etclose = WDetclose; } else if (!strncmp(s,"3c523",5) || !strncmp(s,"523",3)) { etopen = E2etopen; xmit = E2xmit; recv = E2recv; getaddr = E2getaddr; etupdate = E2etupdate; etclose = E2etclose; } else if (!strncmp(s,"r501",4)) { /* special reserve driver */ etopen = E3etopen; xmit = E3xmit; recv = E3recv; getaddr = E3getaddr; etupdate = E3etupdate; etclose = E3etclose; } else if (!strncmp(s,"3c",2) || 1) { /* default choice */ etopen = E1etopen; xmit = E1xmit; recv = E1recv; getaddr = E1getaddr; etupdate = E1etupdate; etclose = E1etclose; } } /**********************************************************************/ /* netarpme * send an arp to my address. arpinterpret will notice any response. * Checks for adapters which receive their own broadcast packets. */ netarpme(s) char *s; { if (etopen == U2etopen) return(0); if (etopen == U1etopen) return(0); reqarp(s); /* send it */ return(0); } /**********************************************************************/ initbuffer() { bufpt = bufread = buforg = raw; /* start at the beginning */ bufend = &raw[14500]; /* leave 2K breathing room, required */ buflim = 12000; /* another 2K breathing room */ (*getaddr)(nnmyaddr,nnaddr,nnioaddr); return(0); } /**********************************************************************/ /* demux * find the packets in the buffer, determine their lowest level * packet type and call the correct interpretation routines * * the 'all' parameter tells demux whether it should attempt to empty * the input packet buffer or return after the first packet is dealt with. * * returns the number of packets demuxed */ demux(all) int all; { uint16 getcode; int nmuxed; DLAYER *firstlook; nmuxed = 0; if (!etupdate) /* check that network is hooked up */ return(0); do { /* while all flag is on */ (*recv)(); /* NULL operation for 3COM */ if (bufbig > 0) { nmuxed++; firstlook = (DLAYER *)(bufread+2); /* where packet is */ getcode = firstlook->type; /* where does it belong? */ switch (getcode) { /* what to do with it? */ case EARP: case ERARP: arpinterpret(firstlook); /* handle ARP packet */ break; case EIP: ipinterpret(firstlook); break; default: break; } (*etupdate)(); /* update read pointers in buffer, free packet */ } else all = 0; } while (all); /* should we look for more to deal with? */ return(nmuxed); /* no packets anymore */ } /************************************************************************/ /* dlayersend * * usage: err = dlayersend(ptr,size) * err = 0 for successful, non-zero error code otherwise * ptr is to a dlayer packet header * size is the number of bytes total * * This particular dlayer routine is for Ethernet. It will have to be * replaced for any other dlayer. * * Ethernet addresses are resolved at higher levels because they will only * need to be resolved once per logical connection, instead of once per * packet. Not too layer-like, but hopefully modular. * */ dlayersend(ptr,size) DLAYER *ptr; unsigned size; { int ret; ret = (*xmit)(ptr,size); /* send it out, pass back return code */ /* xmit checks for size < 60 */ /* * automatic, immediate retry once */ if (ret) { if (ret == (*xmit)(ptr,size)) nnerror(100); /* post user error message */ } return(ret); } /***************************************************************************/ /* dlayerinit * Do machine dependent initializations of whatever hardware we have * (happens to be ethernet board here ) */ dlayerinit() { if (initbuffer() || !etopen) return(-1); return((*etopen)(nnmyaddr,nnirq,nnaddr,nnioaddr)); } dlayershut() { if (etclose) (*etclose)(); } /***************************************************************************/ /* pcgetaddr * return results from indirect getaddr call. * This is a pc-specific request for the 48-bit address which was added * so that the user program could print the value. */ pcgetaddr(s,x,y) char *s; int x,y; { if (getaddr) (*getaddr)(s,x,y); } U2etupdate; etclose = U2etclose; } else if (!strncmp(s,"nicpc",5) || !strncmp(s,"pcnic",5)) { etopen = U1etopen; xmit = U1xmit; recv = U1recv; getaddr = U1getaddr; source/tcp.c 644 144 13 41721 4267454337 6377 /* * TCP routines * **************************************************************************** * * * part of: * * TCP/UDP/ICMP/IP Network kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * Tim Krauskopf Fall 1986 * mods for int16/int32 2/88 */ #include "stdio.h" #include "protocol.h" #include "data.h" static int pnum; /************************************************************************ * tcpinterpret * This is called when a packet comes in, passes IP checksumming and is * of the TCP protocol type. Check and see if we are expecting it and * where the data should go. */ tcpinterpret(p,tlen) int tlen; TCPKT *p; { uint i,myport,hlen,hisport; struct port *prt; /* * checksum * First, fill the pseudo header with its fields, then run our * checksum to confirm it. * */ if (p->t.check) { movebytes(tcps.source,p->i.ipsource,8); /* move both addresses */ tcps.z = 0; tcps.proto = p->i.protocol; tcps.tcplen = intswap(tlen); /* byte-swapped length */ if (tcpcheck(&tcps,&p->t,tlen)) { /* compute checksum */ netposterr(400); return(2); } } /* * find the port which is associated with the incoming packet * First try open connections, then try listeners */ myport = intswap(p->t.dest); hisport = intswap(p->t.source); hlen = p->t.hlen >> 2; /* bytes offset to data */ for (i=0; iin.port == myport && prt->out.port == hisport) { pnum = i; return(tcpdo(prt,p,tlen,hlen)); } } /* * check to see if the incoming packet should go to a listener */ for (i=0; i < NPORTS; i++) { prt = portlist[i]; if (prt != NULL && !prt->out.port && prt->in.port == myport && (p->t.flags & TSYN)) { pnum = i; return(tcpdo(prt,p,tlen,hlen)); } } /* * no matching port was found to handle this packet, reject it */ tcpreset(p); /* tell them they are crazy */ if (!(p->t.flags & TSYN)) /* no error message if it is a SYN */ netposterr(407); /* invalid port for incoming packet */ return(1); /* no port matches */ } /**********************************************************************/ /* tcpdo * Looking at the port structure for the destination port, deliver * the incoming packet. */ tcpdo(prt,p,tlen,hlen) int tlen,hlen; struct port *prt; TCPKT *p; { switch (prt->state) { case SLISTEN: /* waiting for remote connection */ if (p->t.flags & TSYN) { /* receive SYN */ /* * remember anything important from the incoming TCP header */ prt->out.size = intswap(p->t.window); /* credit window */ prt->out.port = intswap(p->t.source); prt->in.nxt = longswap(p->t.seq) + 1; /* * set the necessary fields in the outgoing TCP packet */ prt->tcpout.t.dest = p->t.source; prt->tcpout.t.ack = longswap(prt->in.nxt); prt->tcpout.t.flags = TSYN | TACK; prt->tcpout.t.hlen = 24 << 2; /* * note that the maxmimum segment size is installed by 'netlisten()' * hence the header length is 24, not 20 */ /* * initialize all of the low-level transmission stuff (IP and lower) */ movebytes(prt->tcps.dest,p->i.ipsource,4); movebytes(prt->tcpout.i.ipdest,p->i.ipsource,4); movebytes(prt->tcpout.d.dest,p->d.me,DADDLEN); #ifdef MAC /* * look up address in the arp cache if using Atalk encapsulation */ if (!nnemac) { unsigned char *pc; pc = getdlayer(p->i.ipsource); if (pc != NULL) movebytes(prt->tcpout.d.dest,pc,DADDLEN); else return(0); /* no hope this time */ } #endif tcpsend(prt,4); prt->state = SSYNR; /* syn received */ } break; case SSYNR: if (!(p->t.flags & TACK)) { tcpsend(prt,4); break; /* not the right one */ } prt->tcpout.t.hlen = 20 << 2; prt->out.lasttime = time(NULL); /* don't need response */ prt->out.nxt++; /* count SYN as sent */ prt->out.ack = longswap(p->t.ack); /* starting ACK value */ prt->out.size = intswap(p->t.window); /* allowed window */ prt->tcpout.t.flags = TACK; /* starting ACK flag */ prt->state = SEST; /* drop through to established */ netputevent(CONCLASS,CONOPEN,pnum); checkmss(prt,p,hlen); /* see if MSS option is there */ /* fall through */ case SEST: /* normal data transmission */ /* * check and accept a possible piggybacked ack */ ackcheck(prt,p,pnum); estab1986(prt,p,tlen,hlen); return(0); case SSYNS: /* check to see if it ACKS correctly */ /* remember that tcpout is pre-set-up */ if (p->t.flags & TACK) { /* It is ACKING us */ if (longswap(p->t.ack) != prt->out.nxt) { netposterr(401); return(1); } } if (p->t.flags & TRESET) { netposterr(507); prt->state = SCLOSED; netputuev(CONCLASS,CONCLOSE,pnum); return(1); } if (p->t.flags & TSYN) { /* need to send ACK */ prt->tcpout.t.flags = TACK; prt->in.nxt = longswap(p->t.seq) + 1; prt->tcpout.t.ack = longswap(prt->in.nxt); prt->out.ack = longswap(p->t.ack); prt->out.size = intswap(p->t.window); /* credit window */ prt->out.lasttime = 0L; if (p->t.flags & TACK) { prt->state = SEST; netputevent(CONCLASS,CONOPEN,pnum); checkmss(prt,p,hlen); } else prt->state = SSYNR; /* syn received */ } break; case SCWAIT: ackcheck(prt,p,pnum); if (!prt->in.contain) { prt->tcpout.t.flags = TFIN | TACK; prt->out.lasttime = 0L; prt->state = SLAST; } break; case SLAST: /* check ack of FIN, or reset to see if we are done */ if ((p->t.flags & TRESET) || (longswap(p->t.ack) == prt->out.nxt+1)) prt->state = SCLOSED; break; case SFW1: /* waiting for ACK of FIN */ /* throw away data */ prt->in.nxt = longswap(p->t.seq)+tlen-hlen; if (p->t.flags & TRESET) prt->state = SCLOSED; else if (longswap(p->t.ack) != prt->out.nxt+1) { if (p->t.flags & TFIN) { /* got FIN, no ACK for mine */ prt->in.nxt++; /* account for FIN byte */ prt->tcpout.t.ack = longswap(prt->in.nxt); prt->tcpout.t.flags = TACK; /* final byte has no FIN flag */ prt->out.lasttime = 0L; /* cause last ACK to be sent */ prt->state = SCLOSING; } else { prt->tcpout.t.ack = longswap(prt->in.nxt); prt->tcpout.t.flags = TACK | TFIN; prt->out.lasttime = 0L; } } else if (p->t.flags & TFIN) { /* ACK and FIN */ prt->in.nxt++; /* account for his FIN flag */ prt->out.nxt++; /* account for my FIN */ prt->tcpout.t.ack = longswap(prt->in.nxt); prt->tcpout.t.flags = TACK; /* final byte has no FIN flag */ prt->out.lasttime = 0L; /* cause last ACK to be sent */ prt->state = STWAIT; /* we are done */ } else { /* got ACK, no FIN */ prt->out.nxt++; /* account for my FIN byte */ prt->tcpout.t.flags = TACK; /* final pkt has no FIN flag */ prt->state = SFW2; } break; case SFW2: /* want FIN */ prt->in.nxt = longswap(p->t.seq)+tlen-hlen; if (p->t.flags & TRESET) prt->state = SCLOSED; else if (p->t.flags & TFIN) { /* we got FIN */ prt->in.nxt++; /* count his FIN byte */ prt->tcpout.t.ack = longswap(prt->in.nxt); prt->out.lasttime = 0L; /* cause last ACK to be sent */ prt->state = STWAIT; } break; case SCLOSING: /* want ACK of FIN */ if (p->t.flags & TRESET) prt->state = SCLOSED; else if (!ackcheck(prt,p,pnum)) { prt->out.nxt++; /* account for my FIN byte */ prt->state = STWAIT; /* time-wait state next */ } break; case STWAIT: /* ack FIN again? */ if (p->t.flags & TRESET) prt->state = SCLOSED; if (p->t.flags & TFIN) /* only if he wants it */ prt->out.lasttime = 0L; if (prt->out.lasttime && (prt->out.lasttime + WAITTIME < time(NULL))) prt->state = SCLOSED; break; case SCLOSED: prt->in.port = prt->out.port = 0; break; default: netposterr(403); /* unknown tcp state */ break; } return(0); } /**********************************************************************/ /* checkmss * Look at incoming SYN,ACK packet and check for the options field * containing a TCP Maximum segment size option. If it has one, * then set the port's internal value to make sure that it never * exceeds that segment size. */ checkmss(prt,p,hlen) int hlen; struct port *prt; TCPKT *p; { unsigned int i; /* * check header for maximum segment size option */ if (hlen > 20 && p->x.options[0] == 2 && p->x.options[1] == 4) { movebytes(&i,&p->x.options[2],2); /* swapped value of maxseg */ i = intswap(i); if (i < prt->sendsize) /* we have our own limits too */ prt->sendsize = i; } } /**********************************************************************/ /* tcpreset * Send a reset packet back to sender * Use the packet which just came in as a template to return to * sender. Fill in all of the fields necessary and dlayersend it back. */ tcpreset(t) TCPKT *t; { uint tport; struct pseudotcp xxx; if (t->t.flags & TRESET) /* don't reset a reset */ return(1); /* * swap TCP layer portions for sending back */ if (t->t.flags & TACK) { t->t.seq = t->t.ack; /* ack becomes next seq # */ t->t.ack = 0L; /* ack # is 0 */ } else { t->t.ack = longswap(longswap(t->t.seq)+t->i.tlen-sizeof(IPLAYER)); t->t.seq = 0L; } t->t.flags = TRESET; tport = t->t.source; /* swap port #'s */ t->t.source = t->t.dest; t->t.dest = tport; t->t.hlen = 20 << 2; /* header len */ t->t.window = 0; /* * create pseudo header for checksum */ xxx.z = 0; xxx.proto = t->i.protocol; xxx.tcplen = intswap(20); movebytes(xxx.source,t->i.ipsource,4); movebytes(xxx.dest,t->i.ipdest,4); t->t.check = 0; t->t.check = tcpcheck(&xxx,&t->t,sizeof(struct tcph)); /* * IP and data link layers */ movebytes(t->i.ipdest,t->i.ipsource,4); /* machine it came from */ movebytes(t->i.ipsource,nnipnum,4); t->i.tlen = intswap(sizeof(IPLAYER)+sizeof(TCPLAYER)); t->i.ident = nnipident++; t->i.ttl = 30; t->i.check = 0; t->i.check = ipcheck(&t->i,10); movebytes(t->d.dest,t->d.me,DADDLEN); /* data link address */ movebytes(t->d.me,blankd.me,DADDLEN); /* my address */ return(dlayersend(t,sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(TCPLAYER))); } /***************************************************************************/ /* tcpsend * transmits a TCP packet. * * For IP: * sets ident,check,totallen * For TCP: * sets seq and window from port information, * fills in the pseudo header and computes the checksum. * Assumes that all fields not filled in here are filled in by the * calling proc or were filled in by makeport(). * (see all inits in protinit) * */ tcpsend(pport,dlen) int dlen; struct port *pport; { struct port *p; p = pport; if (p == NULL) { netposterr(404); return(-1); } /* * do IP header first */ p->tcpout.i.ident = intswap(nnipident++); p->tcpout.i.tlen = intswap(sizeof(struct iph)+sizeof(struct tcph) + dlen); p->tcpout.i.check = 0; /* install checksum */ p->tcpout.i.check = ipcheck(&p->tcpout.i,10); /* * do TCP header */ p->tcpout.t.seq = longswap(p->out.nxt); /* bytes swapped */ /* * if the port has some credit limit, use it instead of large * window buffer. Generally demanded by hardware limitations. */ if (p->credit < p->in.size) p->tcpout.t.window = intswap(p->credit); else p->tcpout.t.window = intswap(p->in.size); /* window size */ /* * prepare pseudo-header for checksum */ p->tcps.tcplen = intswap(dlen+sizeof(TCPLAYER)); p->tcpout.t.check = 0; p->tcpout.t.check = tcpcheck(&p->tcps,&p->tcpout.t,dlen+sizeof(struct tcph)); p->out.lasttime = time(NULL); return(dlayersend(&p->tcpout, sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(TCPLAYER)+dlen)); } /***************************************************************************/ /* ackcheck * take an incoming packet and see if there is an ACK for the outgoing * side. Use that ACK to dequeue outgoing data. */ ackcheck(p,t,pnum) TCPKT *t; struct port *p; int pnum; { uint32 ak; int32 rttl; int i; if ((t->t.flags & TRESET) && (t->t.seq == p->tcpout.t.ack)) { netposterr(405); p->state = SCLOSED; netputuev(CONCLASS,CONCLOSE,pnum); return(1); } if (!(t->t.flags & TACK)) /* check ACK flag */ return(1); /* if no ACK, no go */ p->out.size = intswap(t->t.window); /* allowable transmission size */ /* * rmqueue any bytes which have been ACKed, update p->out.nxt to the * new next seq number for outgoing. Update send window. * */ ak = longswap(t->t.ack); /* other side's ACK */ /* * Need to add code to check for wrap-around of sequence space * for ak. ak - p->out.ack may be affected by sequence wraparound. * If you have good, efficient code for this, please send it to me. * * If ak is not increasing (above p->out.nxt) then we should assume * that it is a duplicate packet or one of those stupid keepalive * packets that 4.2 sends out. */ if (ak > p->out.nxt) { rmqueue(&p->out,(int)(ak - p->out.ack)); /* take off of queue */ p->out.nxt = ak; p->out.ack = ak; /* * Check to see if this acked our most recent transmission. If so, adjust * the RTO value to reflect the newly measured RTT. This formula reduces * the RTO value so that it gradually approaches the most recent round * trip measurement. When a packet is retransmitted, this value is * doubled (exponential backoff). */ rttl = time(NULL) - p->out.lasttime; if (!p->out.contain && /* just now emptied queue */ rttl < (long)(MAXRTO) && p->rto >= MINRTO) { i = (int)(rttl); i = ((p->rto-MINRTO)*3 + i + 1) >> 2; /* smoothing function */ p->rto = i+MINRTO; } if (p->out.size > 0) p->out.lasttime = 0L; /* forces xmit */ return(0); } return(1); } /***************************************************************************/ /* estab1986 * take a packet which has arrived for an established connection and * put it where it belongs. */ estab1986(prt,pkt,tlen,hlen) struct port *prt; TCPKT *pkt; int tlen,hlen; { int dlen; uint32 sq,want; dlen = tlen-hlen; if (dlen <= 0) { /* only an ACK packet */ checkfin(prt,pkt); /* might still have FIN */ return(0); } /* * see if we want this packet, or is it a duplicate? */ sq = longswap(pkt->t.seq); want = prt->in.nxt; if (sq != want) { /* we may want it, may not */ if (sq < want && sq+dlen >= want) { /* overlap */ hlen += want-sq; /* offset desired */ dlen -= want-sq; /* skip this much */ } else { /* tough it */ prt->out.lasttime = 0L; /* make the ACK time out */ return(-1); } } /* * If we have room in the window, update the ACK field values */ if (prt->in.size >= dlen) { prt->in.nxt += dlen; /* new ack value */ prt->tcpout.t.ack = longswap(prt->in.nxt); prt->in.size -= dlen; /* new window size */ prt->out.lasttime = 0L; /* force timeout for ACK */ enqueue(&prt->in,pkt->x.data+hlen-20,dlen); netputuev(CONCLASS,CONDATA,pnum); /* tell user about it */ prt->in.lasttime = time(NULL); } else { /* no room in input buffer */ prt->out.lasttime = 0L; /* re-ack old sequence value */ } /* * Check the FIN bit to see if this connection is closing */ checkfin(prt,pkt); return(0); } /***************************************************************************/ /* checkfin * Check the FIN bit of an incoming packet to see if the connection * should be closing, ACK it if we need to. * Half open connections immediately, automatically close. We do * not support them. As soon as the incoming data is delivered, the * connection will close. */ checkfin(prt,pkt) struct port *prt; TCPKT *pkt; { if (pkt->t.flags & TFIN) { /* fin bit found */ prt->in.nxt++; /* count the FIN byte */ prt->state = SCWAIT; /* close-wait */ prt->tcpout.t.ack = longswap(prt->in.nxt); /* set ACK in packet */ prt->credit = 0; prt->out.lasttime = 0L; /* cause ACK to be sent */ netputuev(CONCLASS,CONCLOSE,pnum); /* * At this point, we know that we have received all data that the other * side is allowed to send. Some of that data may still be in the * incoming queue. As soon as that queue empties, finish off the TCP * close sequence. We are not allowing the user to utilize a half-open * connection, but we cannot close before the user has received all of * the data from the incoming queue. */ if (!prt->in.contain) { /* data remaining? */ prt->tcpout.t.flags = TFIN | TACK; tcpsend(prt,0); prt->state = SLAST; } } } for maximum segment size option */ if (hlen > source/ip.c 644 144 13 30305 4267454341 6210 /* * IP.C * IP level routines, including ICMP * also includes a basic version of UDP, not generalized yet * **************************************************************************** * * * part of: * * TCP/IP kernel for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * Revision history: * * 10/87 Initial source release, Tim Krauskopf * 2/88 typedefs of integer lengths, TK */ #include "stdio.h" #include "protocol.h" #include "data.h" uint16 ipcheck(),tcpcheck(); /***************************************************************************/ /* ipinterpret * Called by the reception routine with a new IP packet. Check the checksum, * addressing and protocol type and call appropriate routines. */ ipinterpret(p) IPKT *p; { int hischeck,iplen,i; /* * We cannot handle fragmented IP packets yet, return an error */ if (p->i.frags & 0x20) { netposterr(304); return(1); } /* * checksum verification of IP header */ if (p->i.check) { /* no IP checksumming if check=0 */ if (ipcheck(&p->i.versionandhdrlen,(p->i.versionandhdrlen & 0x0f) << 1)) { netposterr(300); /* bad IP checksum */ return(1); /* drop packet */ } } /* * check to make sure that the packet is for me. * Throws out all packets which are not directed to my IP address. * * This code is incomplete. It does not pass broadcast IP addresses up * to higher layers. It used to report packets which were incorrectly * addressed, but no longer does. Needs proper check for broadcast * addresses. */ if (!comparen(nnipnum,p->i.ipdest,4)) { /* potential non-match */ return(1); /* drop packet */ } /* * Extract total length of packet */ iplen = intswap(p->i.tlen); /* * See if there are any IP options to be handled. * We don't understand IP options, post a warning to the user and drop * the packet. */ i = (p->i.versionandhdrlen & 0x0f)<<2; if (i > 20) { netposterr(302); /* packet with options */ return(1); } switch (p->i.protocol) { /* which protocol to handle this packet? */ case PROTUDP: return(udpinterpret(p,iplen-i)); break; case PROTTCP: return(tcpinterpret(p,iplen-i)); /* pass tcplen on to TCP */ case PROTICMP: return(icmpinterpret(p,iplen-i)); default: netposterr(303); return(1); } return(0); } #ifdef NNDEBUG ipdump(p) IPKT *p; { uint16 iplen,iid; iid = intswap(p->i.ident); iplen = intswap(p->i.tlen); puts("found IP packet:"); printf("Version+hdr: %x service %d tlen %u \n", p->i.versionandhdrlen,p->i.service,iplen); printf("Ident: %u frags: %4x ttl: %d prot: %d \n", iid,p->i.frags,p->i.ttl,p->i.protocol); printf("addresses: s: %d.%d.%d.%d t: %d.%d.%d.%d \n", p->i.ipsource[0],p->i.ipsource[1],p->i.ipsource[2],p->i.ipsource[3], p->i.ipdest[0],p->i.ipdest[1],p->i.ipdest[2],p->i.ipdest[3]); puts("\n"); } /***************************************************************************/ /* ipsend THIS ROUTINE HAS NOT BEEN TESTED, NEVER USED! * * generic send of an IP packet according to parameters. Use of this * procedure is discouraged. Terribly inefficient, but may be useful for * tricky or diagnostic situations. Unused for TCP. * * usage: ipsend(data,ident,prot,options,hdrlen) * data is a pointer to the data to be sent * ident is the 16 bit identifier * prot is the protocol type, PROTUDP or PROTTCP or other * hlen is in bytes, total header length, 20 is minimum * dlen is the length of the data field, in bytes * who is ip address of recipient * options must be included in hlen and hidden in the data stream */ ipsend(data,dlen,iid,iprot,who,hlen) unsigned char *data,iprot,*who; int iid,dlen,hlen; { int iplen; if (dlen > 512) dlen = 512; iplen = hlen+dlen; /* total length of packet */ blankip.i.tlen = intswap(iplen); /* byte swap */ blankip.i.versionandhdrlen = 0x40 | (hlen>>2); blankip.i.ident = intswap(iid); /* byte swap */ blankip.i.protocol = iprot; blankip.i.check = 0; /* set to 0 before calculating */ movebytes(blankip.i.ipdest,who,4); movebytes(blankip.d.me,myaddr,DADDLEN); movenbytes(blankip.x.data,data,dlen); /* might be header options data */ blankip.i.check = ipcheck(&blankip.i.versionandhdrlen,hlen>>1); /* checks based on words */ /* resolve knowledge of Ethernet hardware addresses */ /* * This is commented out because I know that this procedure is broken! * If you use it, debug it first. dlayersend(&blankip,iplen+14); */ return(0); } #endif /****************************************************************************/ /* icmpinterpret * interpret the icmp message that just came in */ icmpinterpret(p,icmplen) ICMPKT *p; int icmplen; { uint i,cksum,hisck; IPLAYER *iptr; i = p->c.type; netposterr(600 + i); /* provide info for higher layer user */ if (p->c.check) { /* ignore if chksum = 0 */ if (ipcheck(&p->c,icmplen>>1)) { netposterr(699); return(-1); } } switch (i) { case 8: /* ping request sent to me */ p->c.type = 0; /* echo reply type */ neticmpturn(p,icmplen); /* send back */ break; case 5: /* ICMP redirect */ iptr = (IPLAYER *)p->data; netputuev(ICMPCLASS,IREDIR,0); /* event to be picked up */ movebytes(nnicmpsave,iptr->ipdest,4); /* dest address */ movebytes(nnicmpnew,&p->c.part1,4); /* new gateway */ break; default: break; } return(0); } /****************************************************************************/ /* udpinterpret * take incoming UDP packets and make them available to the user level * routines. Currently keeps the last packet coming in to a port. * * Limitations: * Can only listen to one UDP port at a time. Only saves the last packet * received on that port. * Port numbers should be assigned like TCP ports are (future). */ udpinterpret(p,ulen) UDPKT *p; int ulen; { uint hischeck,mycheck; /* * did we want this data ? If not, then let it go, no comment * If we want it, copy the relevent information into our structure */ if (intswap(p->u.dest) != ulist.listen) return(1); /* * first compute the checksum to see if it is a valid packet */ hischeck = p->u.check; p->u.check = 0; if (hischeck) { movebytes(tcps.source,p->i.ipsource,8); tcps.z = 0; tcps.proto = p->i.protocol; tcps.tcplen = intswap(ulen); mycheck = tcpcheck(&tcps,&p->u,ulen); if (hischeck != mycheck) { netposterr(700); return(2); } p->u.check = hischeck; /* put it back */ } ulen -= 8; /* account for header */ if (ulen > UMAXLEN) /* most data that we can accept */ ulen = UMAXLEN; movebytes(ulist.who,p->i.ipsource,4); movebytes(ulist.data,p->data,ulen); ulist.length = ulen; ulist.stale = 0; netputuev(USERCLASS,UDPDATA,ulist.listen); /* post that it is here */ return(0); } /****************************************************************************/ /* neturead * get the data from the UDP buffer * Returns the number of bytes transferred into your buffer, -1 if none here * This needs work. */ neturead(buffer) char *buffer; { if (ulist.stale) return(-1); movebytes(buffer,ulist.data,ulist.length); ulist.stale = 1; return(ulist.length); } /***************************************************************************/ /* netulisten * Specify which UDP port number to listen to. * Can only listen to one at a time. */ netulisten(port) int port; { ulist.listen = port; } /***************************************************************************/ /* netusend * send some data out in a UDP packet * uses the preinitialized data in the port packet ulist.udpout * * returns 0 on okay send, nonzero on error */ netusend(machine,port,retport,buffer,n) unsigned char *machine,*buffer; unsigned int port,retport; int n; { unsigned char *pc; if (n > UMAXLEN) n = UMAXLEN; /* * make sure that we have the right dlayer address */ if (!comparen(machine,ulist.udpout.i.ipdest,4)) { pc = netdlayer(machine); if (pc == NULL) return(-2); movebytes(ulist.udpout.d.dest,pc,DADDLEN); movebytes(ulist.udpout.i.ipdest,machine,4); movebytes(ulist.tcps.dest,machine,4); } ulist.udpout.u.dest = intswap(port); ulist.udpout.u.source = intswap(retport); ulist.tcps.tcplen = ulist.udpout.u.length = intswap(n+sizeof(UDPLAYER)); movenbytes(ulist.udpout.data,buffer,n); /* * put in checksum */ ulist.udpout.u.check = 0; ulist.udpout.u.check = tcpcheck(&ulist.tcps,&ulist.udpout.u,n+sizeof(UDPLAYER)); /* * iplayer for send */ ulist.udpout.i.tlen = intswap(n+sizeof(IPLAYER)+sizeof(UDPLAYER)); ulist.udpout.i.ident = intswap(nnipident++); ulist.udpout.i.check = 0; ulist.udpout.i.check = ipcheck(&ulist.udpout.i,10); /* * send it */ return(dlayersend(&ulist.udpout, sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(UDPLAYER)+n)); } #ifdef notneeded /***************************************************************************/ /* neticmpsend * Not currently used. Has not been tested since 9/87. * Do not assume this works (TK) * * send out an icmp packet, probably to do a ping operation * * returns 0 on okay send, nonzero on error */ neticmpsend(machine,type,code,buffer,n) unsigned char *machine,*buffer,type,code; int n; { unsigned char *pc; if (n > ICMPMAX) n = ICMPMAX; /* * make sure that we have the right dlayer address * * this may be re-entrant, needs checking. Okay, as long as this compare * is false when called from netsleep() routines! * When called from user routines, we are okay. */ if (!comparen(machine,blankicmp.i.ipdest,4)) { pc = netdlayer(machine); if (pc == NULL) return(-2); movebytes(blankicmp.d.dest,pc,DADDLEN); movebytes(blankicmp.i.ipdest,machine,4); /* movebytes(ulist.tcps.dest,machine,4); */ } /* * prepare ICMP portion */ blankicmp.c.type = type; blankicmp.c.code = code; movenbytes(&blankicmp.data,buffer,n); blankicmp.c.check = 0; blankicmp.c.check = ipcheck(&blankicmp.c,(sizeof(ICMPLAYER)+n)>>1); /* * iplayer for send */ blankicmp.i.tlen = intswap(n+sizeof(IPLAYER)+sizeof(ICMPLAYER)); blankicmp.i.ident = intswap(nnipident++); blankicmp.i.check = 0; blankicmp.i.check = ipcheck(&blankicmp.i,10); /* * send it * * debug this routine before using */ return(dlayersend(&blankicmp, sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(ICMPLAYER)+n)); } #endif /***************************************************************************/ /* neticmpturn * * send out an icmp packet, probably in response to a ping operation * interchanges the source and destination addresses of the packet, * puts in my addresses for the source and sends it * * does not change any of the ICMP fields, just the IP and dlayers * returns 0 on okay send, nonzero on error */ neticmpturn(p,ilen) ICMPKT *p; int ilen; { unsigned char *pc; /* * reverse the addresses, dlayer and IP layer */ if (comparen(p->d.me,broadaddr,DADDLEN)) return(0); movebytes(p->d.dest,p->d.me,DADDLEN); #ifdef MAC /* * look up address in the arp cache if we are using AppleTalk * encapsulation. */ if (!nnemac) { pc = getdlayer(p->i.ipsource); if (pc != NULL) movebytes(p->d.dest,pc,DADDLEN); else return(0); /* no hope this time */ } #endif movebytes(p->i.ipdest,p->i.ipsource,4); movebytes(p->d.me,nnmyaddr,DADDLEN); movebytes(p->i.ipsource,nnipnum,4); /* * prepare ICMP checksum */ p->c.check = 0; p->c.check = ipcheck(&p->c,ilen>>1); /* * iplayer for send */ p->i.ident = intswap(nnipident++); p->i.check = 0; p->i.check = ipcheck(&p->i,10); /* * send it */ return(dlayersend(p,sizeof(DLAYER)+sizeof(IPLAYER)+ilen)); } who is ip address of recipient * options must be included in hlen and hidden in the data stream */ ipsend(data,dlen,iid,iprot,who,hlen) unsigned char *data,iprot,*who; int iid,dlen,hlen; { int iplen; if (dlen > 512) dlen = 512; iplen = hlen+dlen; /* total length of packet */ blankisource/rspc.c 644 144 13 23266 4267454342 6560 /* RSpc * real screen interface for Gaige Paulsen's * Virtual screen driver * * Tim Krauskopf * * Date Notes * -------------------------------------------- * 11/25/86 Start -TKK */ #include "stdio.h" #include "whatami.h" #include "windat.h" #include "vskeys.h" unsigned char *malloc(); static int lastatt=255,lastw=255, /* last attribute value written to screen*/ thevis=0; /* which window is visible */ /*************************************************************************/ RSbell(w) int w; { /* * might add something to indicate which window */ n_sound(1000,12); /* PC bell routine */ } RSvis(w) int w; { thevis = w; /* this is visible window */ } RSinitall() {} RSinsstring() {} RSdelchars() {} RSbufinfo() {} RSdrawsep() {} RSmargininfo() {} RSdelcols() {} RSinscols() {} RScursoff(w) int w; { /* do nothing, MAC routine */ } RScurson(w,y,x) int w,x,y; { if (w != thevis) return(0); /* not visible */ /* * this is really the cursor positioning routine. If cursor is turned off, * then it needs to be turned back on. */ n_cur(x,y); /* add code to save cursor position for a given window */ } RSdraw(w,y,x,a,len,ptr) int w,x,y,a,len; char *ptr; { int i; if (w != thevis) { /* indicate that something happened */ x = n_row(); y = n_col(); if (screens[w]->sstat != '*') { if (screens[w]->sstat == 47) screens[w]->sstat = 92; else screens[w]->sstat = 47; } statline(); n_cur(x,y); /* restore cursor where belongs */ return(0); /* not visible */ } /* * call my own draw routine */ if (w != lastw || a != lastatt) /* need to parse attribute bit */ RSsetatt(a,w); n_cur(x,y); if (VSisgrph(lastatt)) { for (i=0; icolors[1]; else if (VSisrev(a)) c = screens[w]->colors[2]; else c = screens[w]->colors[0]; if (VSisblnk(a)) c |= 128; /* set blink bit */ if (VSisbold(a)) c |= 8; /* set brightness bit */ n_color(c); lastatt = a; lastw = w; } RSdellines(w,t,b,n,select) int w,t,b,n,select; { int c; if (w != thevis || n < 1) return(0); /* not visible */ c = n_color(screens[w]->colors[0]); n_scrup(n,t,0,b,79); n_color(c); } RSerase(w,y1,x1,y2,x2) int w,x1,y1,x2,y2; { int c; if (w != thevis) return(0); /* not visible */ c = n_color(screens[w]->colors[0]); n_scrup(0,x1,y1,x2,y2); n_color(c); } RSinslines(w,t,b,n,select) int w,t,b,n,select; { int c; if (w != thevis || n < 1) return(0); /* not visible */ c = n_color(screens[w]->colors[0]); n_scrdn(n,t,0,b,79); n_color(c); } RSsendstring(w,ptr,len) int w,len; char *ptr; { netwrite(screens[w]->pnum,ptr,len); } /* * VT100 code still needed, waiting for Gaige's routines */ #include "nkeys.h" int transtable[] = { /* Graphics translation set */ 32, 4, 177, 9, 12, 13, 10, 248, 241, 10, 10, 217, 191, 218, 192, 197, 196, 196, 196, 196, 95, 195, 180, 193, 194, 179, 243, 242, 227, 168, 156, 250, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; /***************************************************************************/ /* translate: * Takes a character and converts it to the IBM PC extended character * set which, in the transtable array, is mapped to the VT100 graphics * characters, with minor conflicts. */ translate(ch) int ch; { if (ch > 94) return(transtable[ ch - 95 ]); else return(ch); } /* Keyboard translations from the PC to VT100 * Tim Krauskopf Sept. 1986 * * original: ISP 1984 */ /***************************************************************************/ /* takes a key value and sends it to the TCP port in 'pnum' * First, translates all special keys to VT100 key sequences. */ /* see vskeys.h for keys in this trans table */ unsigned char vttrans[128] = { 19,20,21,22, 16,15, 17,11,0,18,0, 4, 0, 1,2,0, 0,0,0,0,0,0,0,0,0,0,0, 3, 0,0, 17, 0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 6,7,8,9,10,11,12,13,14,5, 0,0,0, 16, 15, 0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; vt100key(c) int c; { int i; if (c < 128) { netwrite(current->pnum,&c,1); return(0); } else { i = vttrans[c-128]; if (i) VSkbsend(current->vs,i+128,!(current->echo)); else { switch(c) { case HOME: netwrite(current->pnum,"\010",1); break; case ENDKEY: VSkbsend(current->vs,VSK4,0); VSkbsend(current->vs,VSK2,0); break; case CTRLHOME: VSkbsend(current->vs,VSF1,0); VSkbsend(current->vs,VSK5,0); break; case CTRLEND: VSkbsend(current->vs,VSF1,0); VSkbsend(current->vs,VSK4,0); break; case PGUP: VSkbsend(current->vs,VSK5,0); VSkbsend(current->vs,VSK8,0); break; case PGDN: VSkbsend(current->vs,VSK4,0); VSkbsend(current->vs,VSK8,0); break; case CTRLPGUP: VSkbsend(current->vs,VSK5,0); VSkbsend(current->vs,VSK7,0); break; case CTRLPGDN: VSkbsend(current->vs,VSK4,0); VSkbsend(current->vs,VSK7,0); break; case F9: netwrite(current->pnum,"\032exit\r\n",7); break; default: /* key is not mapped to anything */ break; } } } return(0); } /***********************************************************************/ /* non-blocking RSgets() * This routine will continually add to a string that is re-submitted * until a special character is hit. It never blocks. * * As long as editing characters (bksp, Ctrl-U) and printable characters * are pressed, this routine will update the string. When any other special * character is hit, that character is returned. */ char bk[] = {8,' ',8}; RSgets(w,s,lim) int w; char *s; int lim; { int c,count,i; char *save; count = strlen(s); save = s; s += count; while (0 < (c = n_chkchar())) { switch (c) { /* allow certain editing chars */ case 8: /* backspace */ if (count) { VSwrite(w,bk,3); count--; /* one less character */ s--; /* move pointer backward */ } break; case 21: for (i=0; i < s-save; i++) { VSwrite(w,bk,3); } s = save; break; case 13: case 9: *s = '\0'; /* terminate the string */ return(c); default: if (count == lim) { /* to length limit */ RSbell(); *s = '\0'; /* terminate */ return(0); } if (c > 31 && c < 127) { VSwrite(w,&c,1); *s++ = c; /* add to string */ count++; /* length of string */ } else { if (c > 0 && c < 27) { c += 64; VSwrite(w,"^",1); VSwrite(w,&c,1); c -= 64; } *s = '\0'; /* terminate the string */ return(c); } break; } } *s = '\0'; /* terminate the string */ return(c); } /***********************************************************************/ /* non-blocking gets() * This routine will call netsleep while waiting for keypresses during * a gets. Replaces the library gets. * * As long as editing characters (bksp, Ctrl-U) and printable characters * are pressed, this routine will continue. When any other special * character is hit, NULL is returned. the return key causes a normal return. */ char *nbgets(s,lim) char *s; int lim; { int c,count,i; char *save; count = 0; save = s; while (1) { c = n_chkchar(); if (c <= 0) { Stask(); /* keep communications going */ c = 0; } switch (c) { /* allow certain editing chars */ case 8: /* backspace */ if (count) { n_putchar(c); n_putchar(' '); n_putchar(c); count--; /* one less character */ s--; /* move pointer backward */ } break; case 13: /* carriage return, = ok */ n_puts(""); *s = '\0'; /* terminate the string */ return(save); /* return ok */ break; case 21: for (i=0; i < s-save; i++) { n_putchar(8); n_putchar(' '); n_putchar(8); } s = save; break; case 0: /* do nothing */ break; default: if (c > 31 && c < 127) { if (count < lim) { n_putchar(c); *s++ = c; /* add to string */ count++; /* length of string */ } } else { n_puts(""); *s = '\0'; /* terminate the string */ return(NULL); } break; } } } /************************************************************************/ /* nbgetch * check the keyboard for a character, don't block to wait for it, * but don't return to the caller until it is there. */ nbgetch() { int c; while (0 >= (c = n_chkchar())) /* there is a key? */ Stask(); /* no key yet, update everything */ return(c); } /************************************************************************/ /* nbget * demux at least one packet each time, check the keyboard for a key, * return any key pressed to the caller. */ nbget() { int c; demux(0); /* only one packet */ if (0 >= (c = n_chkchar())) return(-1); /* no key ready */ return(c); } /***********************************************************************/ /* ftpstart * update status line with new file length remaining */ ftpstart(dir,buf) char dir,*buf; { int r,c,cl; long int fpos; r = n_row(); c = n_col(); cl = n_color(current->colors[0]); if (dir) dir = '<'; else dir = '>'; Sftpname(&buf[100]); /* get file name */ Sftpstat(&fpos); /* get position in file */ n_cur(24,36); sprintf(buf,"FTP %c %14s %10lu",dir,&buf[100],fpos); if (scmode()) n_cheat(buf,strlen(buf)); else n_draw(buf,strlen(buf)); n_color(cl); n_cur(r,c); return(0); } want it, copy the relevent information into our structure */ if (intswap(p->u.dest) != ulist.listen) return(1); /* * first compute the checksum to see if it is a valid packet */ hischeck = p->u.check; p->u.check = 0; if (hischeck) { movebytes(tcps.source,p->i.ipsource,8); tcps.z = 0; tcps.proto = p->i.protocol; source/bkgr.c 644 144 13 70613 4267454345 6537 /* * Background procedures for rcp and ftp * 11/86 Tim Krauskopf * 1988 rewritten several times * National Center for Supercomputing Applications * **************************************************************************** * * * part of: * * Network utilities for NCSA Telnet * * by Tim Krauskopf * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * **************************************************************************** * */ #include "stdio.h" #include "fcntl.h" #include "whatami.h" #include "hostform.h" #define HTELNET 23 #define HRSHD 514 #define HFTP 21 #define BUFFERS 8000 #define PATHLEN 256 #ifdef PC #define RCPSEGSIZE 1024 #define EOLCHAR 10 #ifdef MSC #define O_RAW O_BINARY #endif #else #define MACBINARY #ifdef MACBINARY #include "MacBinary.h" int MacBinary=1; MBFile *MBopen(), *mbfp; #endif #define RCPSEGSIZE 512 #define O_RAW O_RDONLY #define EOLCHAR 13 #endif int32 atol(),lseek(); char *firstname(),*nextname(),*nextfile; static int ftpenable=0, /* is file transfer enabled? */ rcpenable=0, /* is rcp enabled? */ ftpdata=-1, /* port for ftp data connection */ fnum=-1, /* port number for incoming ftp */ rsnum=-1, /* port number for incoming rshell */ rserr=-1; /* port number for rshd() stderr */ static unsigned char xs[BUFFERS+10], /* buffer space for file transfer */ pathname[PATHLEN], /* space to keep path names */ newfile[PATHLEN], /* current file being received */ myuser[17], /* user name on my machine */ hisuser[17], /* user name on his machine */ waitchar; /* character waiting for from net */ static int curstate = -1, /* state machine for background processes */ retstate = 200, /* to emulate a subroutine call */ ftpstate = 0, /* state of the ftp data transfer */ isdir=0, /* flag for rcp target pathname */ waitpos=0, /* marker for gathering strings from net */ cnt=0, /* number of characters from last netread() */ fh=0, /* file handle when transfer file is open */ ftpfh=0, /* file handle for ftp data */ rc=0, /* telnet flag */ xp=0, /* general pointer */ towrite=0, /* file transfer pointer */ len=0; /* file transfer length */ static long int filelen=0L; /* length of current file for transfer */ static char mungbuf[1024],crfound=0; extern char Sptypes[NPORTS]; /* flags for port #'s */ #define PFTP 1 #define PRCP 2 #define PDATA 3 #ifdef PC #define ga() while (!netwrite(rsnum,"",1)) netsleep(0) /************************************************************************/ /* unsetrshd * remove the acceptance of rshd calls (rcp) */ unsetrshd() { netclose(rsnum); rsnum = -1; rcpenable = 0; } /************************************************************************/ setrshd() { int i; /* * set up to receive a rsh call connection */ if (rsnum >= 0) return(0); curstate = 199; /* waiting for connection */ i = netsegsize(RCPSEGSIZE); rsnum = netlisten(HRSHD); netsegsize(i); if (rsnum >= 0) Sptypes[rsnum] = PRCP; rcpenable = 1; } /************************************************************************/ /* rshell * take an incoming rshell request and service it. Designed to handle * rcp primarily. */ rshd(code) int code; { int i,j; if (!rcpenable) return(0); switch (curstate) { case 199: /* wait to get started */ if (code != CONOPEN) break; curstate = 0; netputuev(SCLASS,RCPACT,rsnum); /* keep us alive */ break; /* * in effect, this is a subroutine that captures network traffic while * waiting for a specific character to be received */ case 50: while (0 < (cnt = netread(rsnum,&xs[waitpos],1))) { if (xs[waitpos] == waitchar) { curstate = retstate; netputuev(SCLASS,RCPACT,rsnum); /* keep us alive */ break; } else waitpos += cnt; } netpush(rsnum); break; case 51: /* for recursion, passes straight through */ break; case 0: /* waiting for first string */ retstate = 1; curstate = 50; waitchar = 0; waitpos = 0; netputuev(SCLASS,RCPACT,rsnum); /* keep us alive */ break; case 1: /* we have received stderr port number */ i = atoi(xs); /* port number */ curstate = 51; #ifdef notneeded /* * caution, netrespond calls netsleep() * which will call this routine * careful with the synchronicity! */ if (i) /* zero means, don't bother */ rserr = netrespond(i,rsnum,1); /* respond to rsh */ else #else if (i) { cnt = -1; /* abort it all, we don't take rsh */ break; } else #endif rserr = -1; retstate = 2; curstate = 50; waitpos = 0; waitchar = 0; break; case 2: /* get user name, my machine */ strncpy(myuser,xs,16); retstate = 3; curstate = 50; waitpos = 0; waitchar = 0; break; case 3: /* get user name, his machine */ strncpy(hisuser,xs,16); /* ftransinfo(hisuser); */ retstate = 4; curstate = 50; waitchar = 0; waitpos = 0; break; case 4: /* ftransinfo(xs);*/ /* * ACK receipt of command line */ if (rserr >= 0) netwrite(rserr,&xp,1); /* send null byte */ else { ga(); /* send NULL on main connection */ } if (!strncmp(xs,"rcp ",4)) { /* * rcp will be using wildcards, target must be a directory */ if (!strncmp(&xs[4],"-d -t",5)) { strncpy(pathname,&xs[10],PATHLEN); if (direxist(pathname)) { /* ftransinfo("no directory by that name ");*/ netwrite(rsnum,"\001 No dir found ",16); netpush(rsnum); cnt = -1; break; } isdir = 1; retstate = 20; curstate = 50; waitchar = '\012'; waitpos = 0; ga(); /* ready for them to start */ break; } /* * target could be a directory or a complete file spec */ if (!strncmp(&xs[4],"-t",2)) { strncpy(pathname,&xs[7],PATHLEN); if (!direxist(pathname)) isdir = 1; else isdir = 0; retstate = 20 ; curstate = 50; waitchar = '\012'; waitpos = 0; ga(); /* ready for rcp to start */ break; } /* * rcp is requesting me to transfer file(s) (or giving directory name) */ if (!strncmp(&xs[4],"-f",2)) { strncpy(pathname,&xs[7],PATHLEN); /* * direxist returns whether the path spec refers to a directory, and if * it does, prepares it as a prefix. Therefore, if it is a dir, we append * a '*' to it to wildcard all members of the directory. * Firstname() takes a file spec (with wildcards) and returns a pointer * to a prepared ACTUAL file name. nextname() returns successive ACTUAL * filenames based on firstname(). */ if (!direxist(pathname)) { i = strlen(pathname); pathname[i] = '*'; /* all members of directory*/ pathname[++i] = '\0'; } nextfile = firstname(pathname); if (nextfile == NULL) { /* ftransinfo(" file or directory not found ");*/ netwrite(rsnum,"\001 File not found ",18); netpush(rsnum); cnt = -1; } else { /* wait for other side to be ready */ retstate = 30; curstate = 50; waitchar = 0; waitpos = 0; } break; } } break; case 20: xs[waitpos] = '\0'; /* add terminator */ /* * get working values from command line just received * open file for receive */ if (xs[0] != 'C' || xs[5] != ' ') { /* ftransinfo(" Cannot parse filename line "); */ netwrite(rsnum,"\001 Problem with file name ",26); cnt = -1; break; } filelen = atol(&xs[6]); for (i = 6; xs[i] != ' '; i++) if (!xs[i]) { /* ftransinfo(" premature EOL ");*/ netwrite(rsnum,"\001 Problem with file name ",26); cnt = -1; break; } strcpy(newfile,pathname); /* path spec for file */ if (isdir) /* add file name for wildcards */ strcat(newfile,&xs[++i]); if (0 > (fh = creat(newfile,O_RAW))) { netwrite(rsnum,"\001 Cannot open file for write ",29); cnt = -1; break; } netputevent(USERCLASS,RCPBEGIN,-1); ga(); /* start sending the file to me */ xp = len = 0; curstate = 21; /* receive file, fall through */ break; case 21: do { /* wait until xs is full before writing to disk */ if (len <= 0) { if (xp) { write(fh,xs,xp); xp = 0; } if (filelen > (long)BUFFERS) len = BUFFERS; else len = (int)filelen; } cnt = netread(rsnum,&xs[xp],len); filelen -= (long)cnt; len -= cnt; xp += cnt; /* printf(" %ld %d %d %d ",filelen,len,xp,cnt); n_row(); n_puts(""); */ if (filelen <= 0L || cnt < 0) { write(fh,xs,xp); /* write last block */ close(fh); fh = 0; /* wait for NULL byte at end after closing file */ curstate = 50; retstate = 22; waitchar = 0; waitpos = 0; break; } } while (cnt > 0); break; case 22: /* cause next sequence of bytes to be saved as next filename to transfer */ ga(); /* tell other side, I am ready */ waitchar = '\012'; waitpos = 0; curstate = 50; retstate = 20; break; /* * transfer file(s) to the sun via rcp */ case 30: if (0 > (fh = open(nextfile,O_RAW))) { netwrite(rsnum,"\001 File not found ",19); /* ftransinfo("Cannot open file to transfer: "); ftransinfo(nextfile); */ cnt = -1; break; } netputevent(USERCLASS,RCPBEGIN,-1); filelen = lseek(fh,0L,2); /* how long is file? */ lseek(fh,0L,0); /* back to beginning */ for (i=0,j=-1; nextfile[i] ; i++) if (nextfile[i] == '\\') j = i; sprintf(xs,"C0755 %lu %s\012",filelen,&nextfile[j+1]); netwrite(rsnum,xs,strlen(xs)); /* send info to other side */ /* ftransinfo(xs); check it */ retstate = 31; curstate = 50; waitchar = 0; waitpos = 0; towrite = xp = 0; break; case 31: /* * we are in the process of sending the file */ netputuev(SCLASS,RCPACT,rsnum); /* keep us alive */ if (towrite <= xp) { towrite = read(fh,xs,BUFFERS); xp = 0; filelen -= (long)towrite; } i = netwrite(rsnum,&xs[xp],towrite-xp); if (i > 0) xp += i; /* printf(" %d %d %d %ld\012",i,xp,towrite,filelen); n_row(); */ /* * done if: the file is all read from disk and all sent * or other side has ruined connection */ if ((filelen <= 0L && xp >= towrite) || netest(rsnum)) { close(fh); fh = 0; nextfile = nextname(); /* case of wildcards */ ga(); netputuev(SCLASS,RCPACT,rsnum); if (nextfile == NULL) retstate = 32; else retstate = 30; curstate = 50; waitchar = 0; waitpos = 0; } break; case 32: cnt = -1; break; case 5: break; default: break; } /* * after reading from connection, if the connection is closed, * reset up shop. */ if (cnt < 0) { if (fh > 0) { close(fh); fh = 0; } curstate = 5; cnt = 0; netclose(rsnum); rsnum = -1; netputevent(USERCLASS,RCPEND,-1); setrshd(); /* reset for next transfer */ } } #endif /***********************************************************************/ /***********************************************************************/ /***********************************************************************/ /************************************************************************/ /* ftp section * This should be extracted from rcp so that it compiles cleanly */ #define CRESP(A) netwrite(fnum, messs[(A)], strlen(messs[(A)])) #define FASCII 0 #define FIMAGE O_RAW #define FAMODE 0 #define FIMODE 1 #define FMMODE 2 /* Mac Binary, when ready */ static int rfstate, portnum[8], ftpfilemode=FASCII, /* how to open ze file */ ftptmode=FAMODE; /* how to transfer ze file on net */ static uint16 fdport; setftp() { /* * set up to receive a telnet connection for ftp commands */ rfstate = 0; ftpstate = 0; fnum = netlisten(HFTP); ftpenable = 1; if (fnum >= 0) /* signal that events should be caught */ Sptypes[fnum] = PFTP; strcpy(myuser,"unknown"); /* set unknown user name */ } unsetftp() { rfstate = 0; ftpstate = 0; netclose(fnum); fnum = -1; ftpenable = 0; } /***********************************************************************/ /* * resident ftp server -- enables initiation of ftp without a username * and password, as long as this telnet is active at the same time * Now checks for the need of passwords. */ static char *messs[] = { #ifdef PC "220 PC Resident FTP server, ready \015\012", #else "220 Macintosh Resident FTP server, ready \015\012", #endif "451 Error in processing list command \015\012", "221 Goodbye \015\012", /*2*/ "200 This space intentionally left blank < > \015\012", "150 Opening connection \015\012", "226 Transfer complete \015\012", /*5*/ "200 Type set to A, ASCII transfer mode \015\012", "200 Type set to I, binary transfer mode \015\012", "500 Command not understood \015\012", /*8*/ "200 Okay \015\012", "230 User logged in \015\012", "550 File not found \015\012", /*11*/ "501 Directory not present or syntax error\015\012", "250 Chdir okay\015\012", "257 \"", "\" is the current directory \015\012", /*15*/ "501 File not found \015\012", "504 Parameter not accepted, not implemented\015\012", "200 Stru F, file structure\015\012", "200 Mode S, stream mode\015\012", /*19*/ "202 Allocate and Account not required for this server\015\012", "501 Cannot open file to write, check for valid name\015\012", "530 USER and PASS required to activate me\015\012", "331 Password required\015\012", /*23 */ "530 Login failed\015\012", "200 MacBinary Mode enabled\015\012", "200 MacBinary Mode disabled\015\012", /*26 */ "552 Disk write error, probably disk full\015\012", "214-NCSA Telnet FTP server, supported commands:\015\012", " USER PORT RETR ALLO PASS STOR CWD XCWD XPWD LIST NLST\015\012", #ifdef MAC " HELP QUIT MODE TYPE STRU ACCT NOOP MACB\015\012", /*30*/ " MACB is MacBinary and must be done with TYPE I\015\012", #else " HELP QUIT MODE TYPE STRU ACCT NOOP\015\012", " A Macintosh version of NCSA Telnet is also available.\015\012", #endif "214 Direct comments and bugs to telbug@ncsa.uiuc.edu\015\012", ""}; rftpd(code) int code; { int i; if (!ftpenable) return(0); netpush(fnum); switch (rfstate) { case 0: if (code != CONOPEN) break; ftpfilemode = FASCII; ftptmode = FAMODE; netputevent(USERCLASS,FTPCOPEN,-1); rfstate = 1; /* drop through */ case 1: CRESP(0); netgetftp(portnum,fnum); /* get default ftp information */ for (i=0; i<4; i++) /* copy IP number */ hisuser[i] = portnum[i]; fdport = portnum[6]*256+portnum[7]; waitpos = 0; waitchar = '\012'; rfstate = 50; /* note skips over */ if (Sneedpass()) retstate = 3; /* check pass */ else retstate = 5; /* who needs one ? */ break; case 3: /* check for passwords */ case 4: waitpos = 0; waitchar = '\012'; rfstate = 50; if (!strncmp("USER",xs,4)) { strncpy(myuser,&xs[5],16); /* keep user name */ netputevent(USERCLASS,FTPUSER,-1); CRESP(23); retstate = 4; /* wait for password */ break; } if (!strncmp("PASS",xs,4)) { if (Scheckpass(myuser,&xs[5])) { netputevent(USERCLASS,FTPPWOK,-1); CRESP(10); retstate = 5; } else { netputevent(USERCLASS,FTPPWNO,-1); CRESP(24); retstate = 3; } break; } if (!strncmp("QUIT",xs,4)) { CRESP(2); cnt = -1; } else CRESP(22); retstate = 3; /* must have password first */ break; /* * interpret commands that are received from the other side */ case 5: #ifdef PC for (i=4; i< strlen(xs); i++) if (xs[i] == '/') /* flip slashes */ xs[i] = '\\'; #endif /* * set to a safe state to handle recursion * wait for another command line from client * */ rfstate = 50; retstate = 5; waitchar = '\012'; waitpos = 0; if (!strncmp(xs,"LIST",4) || !strncmp(xs,"NLST",4)) { if ((strlen(xs) < 6) || xs[5] == '.') strcpy(xs,"LIST *"); nextfile = firstname(&xs[5]); /* find first name */ if (nextfile == NULL) { CRESP(16); } else { ftpgo(); /* open the connection */ fdport = portnum[6]*256+portnum[7]; /* reset to def */ if (ftpdata >= 0) Sptypes[ftpdata] = PDATA; ftpstate = 40; /* ready to transmit */ CRESP(4); netputevent(USERCLASS,FTPLIST,-1); } } else if (!strncmp(xs,"CWD",3)) { if (chgdir(&xs[4])) /* failed */ CRESP(12); else /* success */ CRESP(13); } else if (!strncmp(xs,"STOR",4)) { #ifdef MACBINARY if ((mbfp = MBopen(&xs[5],0,MB_WRITE + (((!MacBinary) || (ftptmode == FAMODE)) ? MB_DISABLE : 0 ))) == 0L) { CRESP(21); break; } else ftpfh = 12; #else if (0 > (ftpfh = creat(&xs[5],ftpfilemode))) { CRESP(21); break; } #endif ftpstate = 0; strncpy(newfile,&xs[5],PATHLEN-1); ftpgo(); /* open connection */ fdport = portnum[6]*256+portnum[7]; /* reset to def */ if (ftpdata >= 0) Sptypes[ftpdata] = PDATA; CRESP(4); ftpstate = 30; /* ready for data */ } else if (!strncmp(xs,"RETR",4)) { #ifdef MACBINARY if ((mbfp = MBopen( &xs[5], 0, MB_READ + ((!MacBinary) || (ftptmode == FAMODE)) ? MB_DISABLE : 0)) == 0L) { CRESP(11); break; } ftpfh = 12; #else if (0 > (ftpfh = open(&xs[5],ftpfilemode))) { CRESP(11); break; } #endif strncpy(newfile,&xs[5],PATHLEN-1); ftpgo(); /* open connection */ fdport = portnum[6]*256+portnum[7]; /* reset to def */ ftpstate = 20; /* ready for data */ if (ftpdata >= 0) Sptypes[ftpdata] = PDATA; CRESP(4); } else if (!strncmp(xs,"TYPE",4)) { if (toupper(xs[5]) == 'I') { ftpfilemode = FIMAGE; ftptmode = FIMODE; CRESP(7); } else if (toupper(xs[5]) == 'A') { ftpfilemode = FASCII; ftptmode = FAMODE; CRESP(6); } else CRESP(17); } #ifdef MACBINARY else if (!strncmp(xs,"MACB",4)) { if (toupper(xs[5]) == 'E') { MacBinary = 1; CRESP(25); } else { MacBinary = 0; CRESP(26); } DisplayMacBinary(); /* post an event ? */ } #endif else if (!strncmp(xs,"PORT",4)) { /* * get the requested port number from the command given */ sscanf(&xs[5],"%d,%d,%d,%d,%d,%d",&portnum[0],&portnum[1], &portnum[2],&portnum[3],&portnum[4],&portnum[5]); fdport = portnum[4]*256+portnum[5]; CRESP(3); } else if (!strncmp(xs,"QUIT",4)) { CRESP(2); rfstate = 60; netputuev(CONCLASS,CONDATA,fnum); /* post back to me */ } else if (!strncmp(xs,"XPWD",4) || !strncmp(xs,"PWD",3)) { CRESP(14); /* start reply */ dopwd(xs,1000); /* get directory */ netwrite(fnum,xs,strlen(xs)); /* write dir name */ CRESP(15); /* finish reply */ } else if (!strncmp(xs,"USER",4)) { strncpy(myuser,&xs[5],16); /* keep user name */ netputevent(USERCLASS,FTPUSER,-1); /* confirm log in without password */ CRESP(10); } else if (!strncmp(xs,"STRU",4)) { /* only one stru allowed */ if (xs[5] == 'F') CRESP(18); else CRESP(17); } else if (!strncmp(xs,"MODE",4)) { /* only one mode allowed */ if (xs[5] == 'S') CRESP(19); else CRESP(17); } else if (!strncmp(xs,"ALLO",4) || !strncmp(xs,"ACCT",4)) CRESP(20); else if (!strncmp(xs,"HELP",4)) { for (i=28; i<33; i++) CRESP(i); } else if (!strncmp(xs,"NOOP",4)) CRESP(9); else /* command not understood */ CRESP(8); break; /* * subroutine to wait for a particular character */ case 50: while (0 < (cnt = netread(fnum,&xs[waitpos],1))) { if (xs[waitpos] == waitchar) { rfstate = retstate; while (xs[waitpos] < 33) /* find end of string */ waitpos--; xs[++waitpos] = '\0'; /* put in terminator */ for (i=0; i<4; i++) /* want upper case */ xs[i] = toupper(xs[i]); break; } else waitpos += cnt; } break; case 60: /* wait for message to get through */ /* or connection is broken */ /* printf(" %d,%d",netpush(fnum),netest(fnum));*/ if (!netpush(fnum) || netest(fnum)) cnt = -1; else netputuev(CONCLASS,CONDATA,fnum); /* post back to me */ break; default: break; } if (cnt < 0) { if (ftpfh > 0) { #ifdef MACBINARY MBclose( mbfp ); #else close(ftpfh); #endif ftpfh = 0; } if (ftpdata > 0) { netclose(ftpdata); netputevent(USERCLASS,FTPEND,-1); } rfstate = 100; ftpstate = 0; cnt = 0; netclose(fnum); netputevent(USERCLASS,FTPCLOSE,-1); fnum = -1; ftpdata = -1; setftp(); /* reset it */ } } /***********************************************************************/ /* ftpgo * open the FTP data connection to the remote host */ ftpgo() { int savest; struct machinfo *m; xs[0] = portnum[0]; xs[1] = portnum[1]; xs[2] = portnum[2]; xs[3] = portnum[3]; netfromport(20); /* ftp data port */ if (NULL == (m = Slookip(xs))) { /* use default entry */ if (NULL == (m = Shostlook("default"))) return(0); savest = m->mstat; m->mstat = HAVEIP; movebytes(m->hostip,xs,4); ftpdata = Snetopen(m,fdport); m->mstat = savest; movebytes(m->hostip,"\0\0\0\0",4); return(0); } ftpdata = Snetopen(m,fdport); } /*********************************************************************/ /* * FTP receive and send file functions */ static int fcnt=0; ftpd(code,curcon) int code,curcon; { int i; if (curcon != ftpdata) /* wrong event, was for someone else */ return(0); switch (ftpstate) { default: break; case 40: /* list file names in current dir */ if (code == CONFAIL) /* something went wrong */ fcnt = -1; if (code != CONOPEN) /* waiting for connection to open */ break; ftpstate = 41; /* * send the "nextfile" string and then see if there is another file * name to send */ case 41: netputuev(SCLASS,FTPACT,ftpdata); netpush(ftpdata); i = strlen(nextfile); if (i != netwrite(ftpdata,nextfile,i)) { CRESP(1); fcnt = -1; break; } netwrite(ftpdata,"\015\012",2); if (NULL == (nextfile = nextname())) { /* normal end */ ftpstate = 22; /* push data through */ } break; case 30: if (code == CONFAIL) /* something went wrong */ fcnt = -1; if (code != CONOPEN) /* waiting for connection to open */ break; ftpstate = 31; crfound = 0; len = xp = 0; filelen = 0L; netputevent(USERCLASS,FTPBEGIN,-2); break; case 31: /* * file has already been opened, take everything from the connection * and place into the open file: ftpfh */ do { /* wait until xs is full before writing to disk */ if (len <= 2000) { if (xp) { #ifdef MACBINARY if (0 > MBwrite(mbfp, xs, xp)) { netclose(ftpdata); fcnt = -1; CRESP(27); break; } #else if (0 > write(ftpfh,xs,xp)) { /* disk full err */ netclose(ftpdata); fcnt = -1; CRESP(27); break; } #endif xp = 0; } len = BUFFERS; /* expected or desired len to go */ } if (ftptmode == FAMODE) fcnt = Sfread(ftpdata,&xs[xp],len); else fcnt = netread(ftpdata,&xs[xp],len); if (fcnt >= 0) { len -= fcnt; xp += fcnt; filelen += fcnt; } /* printf(" %d %d %d \012",len,xp,fcnt); n_row(); */ if (fcnt < 0) { #ifdef MACBINARY if (0 > MBwrite( mbfp, xs, xp)) { CRESP(27); break; } MBclose( mbfp ); #else if (0 > write(ftpfh,xs,xp)) { /* disk full check */ CRESP(27); break; } close(ftpfh); #endif ftpfh = 0; CRESP(5); } } while (fcnt > 0); break; case 20: if (code == CONFAIL) /* something went wrong */ fcnt = -1; if (code != CONOPEN) /* waiting for connection to open */ break; ftpstate = 21; #ifndef MACBINARY filelen = lseek(ftpfh,0L,2); /* how long is file? */ lseek(ftpfh,0L,0); /* back to beginning */ #endif towrite = 0; xp = 0; netputevent(USERCLASS,FTPBEGIN,-1); case 21: /* * transfer file(s) to the other host via ftp request * file is already open = ftpfh */ netputuev(SCLASS,FTPACT,ftpdata); if (towrite <= xp) { i = BUFFERS; #ifdef MACBINARY towrite = MBread( mbfp, xs, i); #else towrite = read(ftpfh,xs,i); #endif xp = 0; } if (towrite <= 0 || netest(ftpdata)) { /* we are done */ ftpstate = 22; break; } if (ftptmode == FAMODE) i = Sfwrite(ftpdata,&xs[xp],towrite-xp); else i = netwrite(ftpdata,&xs[xp],towrite-xp); /* printf(" %d %d %d \012",i,xp,towrite); n_row(); */ if (i > 0) { xp += i; filelen -= i; if (filelen < 0L) filelen = 0L; } break; case 22: /* wait for data to be accepted */ netputuev(SCLASS,FTPACT,ftpdata); fcnt = netpush(ftpdata); /* will go negative on err */ if (!fcnt || netest(ftpdata)) fcnt = -1; if (fcnt < 0) CRESP(5); break; case 0: break; } /* end of switch */ /* * after reading from connection, if the connection is closed, * reset up shop. */ if (fcnt < 0) { if (ftpfh > 0) { close(ftpfh); ftpfh = 0; } ftpstate = 0; fcnt = 0; if (ftpdata >= 0) { netclose(ftpdata); netputevent(USERCLASS,FTPEND,-1); ftpdata = -1; } } } /***************************************************************************/ /* Sfwrite * Write an EOL translated buffer into netwrite. * Returns the number of bytes which were processed from the incoming * buffer. Uses its own 1024 byte buffer for the translation (with Sfread). */ Sfwrite(pnum,buf,nsrc) int pnum,nsrc; char *buf; { int i,ndone,nout,lim; char *p,*q; ndone = 0; while (ndone < nsrc) { if (0 > ( i = netroom(pnum))) return(-1); if (i < 1024) /* not enough room to work with */ return(ndone); /* * process up to 512 source bytes for output (could produce 1K bytes out) */ if (nsrc - ndone > 512) lim = 512; else lim = nsrc-ndone; p = buf + ndone; /* where to start this block */ q = mungbuf; /* where munged stuff goes */ for (i=0; i < lim; i++) { if (*p == EOLCHAR) { *q++ = 13; *q++ = 10; p++; } else *q++ = *p++; } ndone += lim; /* # of chars processed */ nout = q-mungbuf; /* # of chars new */ netwrite(pnum,mungbuf,nout); /* send them on their way */ } return(ndone); } /* * important note: for Sfread, nwant must be 256 bytes LARGER than the amount * which will probably be read from the connection. * Sfread will stop anywhere from 0 to 256 bytes short of filling nwant * number of bytes. */ Sfread(pnum,buf,nwant) int pnum,nwant; char *buf; { int i,ndone,lim; char *p,*q; if (nwant < 1024) return(-1); ndone = 0; while (ndone < nwant - 1024) { /* bugfix 6/88 - TK (added -1024) */ if (0 >= (lim = netread(pnum,mungbuf,1024))) { if (ndone || !lim) /* if this read is valid, but no data */ return(ndone); else return(-1); /* if connection is closed for good */ } p = mungbuf; q = buf + ndone; /* printf("\012 lim=%d done=%d want=%d",lim,ndone,nwant); n_row(); */ for (i=0; i < lim; i++) { if (crfound) { if (*p == 10) *q++ = EOLCHAR; else if (*p == 0) *q++ = 13; /* CR-NUL means CR */ crfound = 0; } else if (*p == 13) crfound = 1; else *q++ = *p; /* copy the char */ p++; } ndone = q-buf; /* count chars ready */ } return(ndone); } /***********************************************************************/ /* Sftpname and Sftpuser and Sftphost * record the name of the file being transferred, to use in the status * line updates */ Sftpname(s) char *s; { strcpy(s,newfile); } Sftpuser(user) char *user; { strcpy(user,myuser); /* user name entered to log in */ } Sftphost(host) char *host; { movebytes(host,hisuser,4); /* IP address of remote host */ } Sftpstat(byt) long *byt; { *byt = filelen; } #ifdef MACBINARY MBstat() { return(0); } #endif xs[i] = toupper(xs[i]); break; } else waitpos += cnt; } break; case 60: /* wait fosource/ncsaio.asm 644 144 13 73146 4267454371 7427 ; ; ncsaio.asm ; Support for BIOS calls in NCSA Telnet ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;**************************************************************************** TITLE NCSAIO -- LOW-LEVEL I/O FOR SANE HARDWARE HANDLING ;Microsoft EQU 1 Lattice EQU 1 ifndef Microsoft ifndef Lattice if2 %out %out ERROR: You have to specify "/DMicrosoft" OR "/DLattice" on the %out MASM command line to determine the type of assembly. %out endif end endif endif ; ; From original code by Tim Krauskopf 1984-1985 ; ; Modified and ported to Lattice C, Sept. 1986 ; ifdefs for Microsoft C, June 1987 ; Tim Krauskopf ; ; National Center for Supercomputing Applications ; NAME NIO ; ; Macros for reading and writing I/O ports ; MOUT MACRO REG,STUFF ; one byte to the given I/O register MOV DX,REG MOV AL,STUFF OUT DX,AL ENDM ; MIN MACRO REG ; get one byte to al MOV DX,REG IN AL,DX ENDM ; ; Internal data ; X EQU 6 ; for the large model programs ifdef Microsoft DGROUP group _DATA _DATA segment public 'DATA' assume DS:DGROUP public _dtaptr ; pointer to dta location _dtaptr dw 0000h ; DTA address for me _dtads dw 0000h ; DS for DTA else INCLUDE DOS.MAC DSEG public dtaptr ; pointer to dta location dtaptr dw 0000h ; DTA address for me dtads dw 0000h ; DS for DTA endif att db 7 ; current default attribute topl db 00 ; top line of current window botl db 24 ; bottom line of current window leftc db 00 ; left side of current window rightc db 79 ; right side of current window row db 00 ; cursor position row col db 00 ; cursor position col wrap db 00 ; wrap at end or not regen dw 0b000h ; default to color screen ; ; In this implementation, all special and function keys are translated ; to the following values. ; tr db 3 dup (0) db 0dbh db 11 dup(0) db 09bh,0d1h,0d7h,0c5h,0d2h,0d4h,0d9h,0d5h,0c9h,0cfh,0d0h db 4 dup(0) db 0c1h,0d3h,0c4h,0c6h,0c7h,0c8h,0cah,0cbh,0cch db 5 dup(0) db 0dah,0d8h,0c3h,0d6h,0c2h,0ceh,0cdh db 8 dup(0) db 80h,81h,82h,83h,84h,85h,86h,87h,88h,89h db 0,0 db 8ah,8dh,9ah,0,8bh,0,9bh,0,8ch,8eh,9ch,9dh,9eh db 90h,91h,92h,93h,94h,95h,96h,97h,98h,99h db 0a0h,0a1h,0a2h,0a3h,0a4h,0a5h,0a6h,0a7h,0a8h,0a9h db 0e0h,0e1h,0e2h,0e3h,0e4h,0e5h,0e6h,0e7h,0e8h,0e9h db 0aeh,0abh,0bbh,0ach,0bch,0aah db 0b0h,0b1h,0b2h,0b3h,0b4h,0b5h,0b6h,0b7h,0b8h,0b9h db 0bdh,0beh,0bah db 124 dup(0) ifdef Microsoft _DATA ends else ENDDS endif ; ; ; ; The subroutines to call from C ; ifdef Microsoft _TEXT segment public 'CODE' PUBLIC _n_color,_n_upscroll,_n_dnscroll,_n_wrap,_n_erase,_n_getchar PUBLIC _n_cur,_n_row,_n_col,_n_clear,_n_window,_n_putchar,_n_chkchar PUBLIC _n_savewin,_n_restwin,_n_puts,_n_sound,_n_findfirst,_n_findnext PUBLIC _n_draw,_n_scrup,_n_scrdn,_n_cheat,_n_scrlck,_n_clicks assume CS:_TEXT else PSEG PUBLIC n_color,n_upscro,n_dnscro,n_wrap,n_erase,n_getcha PUBLIC n_cur,n_row,n_col,n_clear,n_window,n_putcha,n_chkcha PUBLIC n_savewi,n_restwi,n_puts,n_sound,n_findfi,n_findne PUBLIC n_draw,n_scrup,n_scrdn,n_cheat,n_scrlck,n_clicks endif ;/***************************************************************/ ; scrlck ; returns whether scroll lock is on or not ; ifdef Microsoft _n_scrlck proc far else n_scrlck proc far endif push bp mov ax,0200h ; get shift states int 16h ; keyboard int and al,010h ; scroll lock state bit xor ah,ah pop bp ret ifdef Microsoft _n_scrlck endp else n_scrlck endp endif ;/***************************************************************/ ; ; Change the value of my current color ; ifdef Microsoft _n_color proc far else n_color proc far endif push bp mov bp,sp xor ax,ax mov dl,[bp+x] ; parameter, one byte mov al,att ; return old color mov att,dl pop bp ret ifdef Microsoft _n_color endp else n_color endp endif ;/*****************************************************************/ ; Determine the current type of display ; ; ask the BIOS which screen is currently active ; ifdef Microsoft _n_which proc far else n_which proc far endif push bp mov ah,15 ; get screen mode int 10h ; call video BIOS cmp al,7 ; is it mono? jz K1 ; yes mov ax,0b800h ; no, set for color jmp K2 K1: mov ax,0b000h K2: mov regen,ax pop bp ret ifdef Microsoft _n_which endp else n_which endp endif ; ;/***************************************************************/ ; ; Set whether word wrap will be on or not ; usage: n_wrap(flag); ; ifdef Microsoft _n_wrap proc far else n_wrap proc far endif push bp mov bp,sp mov dl,[bp+x] ; parameter, one byte flag mov al,wrap ; return old color mov wrap,dl pop bp ret ifdef Microsoft _n_wrap endp else n_wrap endp endif ;/***************************************************************/ ; ; Move the cursor somewhere ; ifdef Microsoft _n_cur proc far else n_cur proc far endif push bp mov bp,sp mov dh,[bp+x] ; row position mov dl,[bp+x+2] ; column position mov row,dh ; save a copy for me mov col,dl ; save column too mov ax,0200h ; ah=2 function call xor bx,bx ; video page 0 int 10h ; set cursor position pop bp ret ifdef Microsoft _n_cur endp else n_cur endp endif ;/***************************************************************/ ; ; set the window boundaries ; ; usage: n_window(ulrow,ulcol,lrrow,lrcol); ; sets window size for future operations. ; ; ifdef Microsoft _n_window proc far else n_window proc far endif push bp mov bp,sp mov al,[bp+x] ; upper left x mov topl,al mov al,[bp+x+2] ; column position mov leftc,al ; save a copy for me mov al,[bp+x+4] mov botl,al ; bottom row mov al,[bp+x+6] mov rightc,al ; keep this one too ifdef Microsoft call _n_which ; what kind of screen? else call n_which ; what kind of screen? endif pop bp ret ifdef Microsoft _n_window endp else n_window endp endif ;/***************************************************************/ ; ; erase portion of the screen ; ; usage: n_erase(ulrow,ulcol,lrrow,lrcol); ; ; ifdef Microsoft _n_erase proc far else n_erase proc far endif push bp mov bp,sp mov ch,[bp+x] ; upper left x mov cl,[bp+x+2] ; column position mov dh,[bp+x+4] mov dl,[bp+x+6] xor ax,ax ; clear area mov ah,6 ; scroll up function mov bh,att ; attribute for erasing is same as print int 10h ; call BIOS pop bp ret ifdef Microsoft _n_erase endp else n_erase endp endif ;/***************************************************************/ ; scroll up and down ; given the number of lines to scroll and the dimensions of the ; box to scroll. ; ifdef Microsoft _n_scrup proc far else n_scrup proc far endif push bp mov bp,sp mov ch,[bp+x+2] ; upper left x mov cl,[bp+x+4] ; column position mov dh,[bp+x+6] mov dl,[bp+x+8] mov al,[bp+x] ; number of lines to scroll mov ah,6 ; scroll up function mov bh,att ; attribute for blank line is same as print int 10h ; call BIOS pop bp ret ifdef Microsoft _n_scrup endp else n_scrup endp endif ifdef Microsoft _n_scrdn proc far else n_scrdn proc far endif push bp mov bp,sp mov ch,[bp+x+2] ; upper left x mov cl,[bp+x+4] ; column position mov dh,[bp+x+6] mov dl,[bp+x+8] mov al,[bp+x] ; number of lines to scroll mov ah,7 ; scroll down function mov bh,att ; attribute for blank line is same as print int 10h ; call BIOS pop bp ret ifdef Microsoft _n_scrdn endp else n_scrdn endp endif ;/***************************************************************/ ; ; Find the cursor position, return the row value ; ifdef Microsoft _n_row proc far else n_row proc far endif push bp mov ax,0300h ; video find cursor function xor bx,bx int 10h ; find cursor mov row,dh mov col,dl ; save col for next routine xor ax,ax mov al,dh ; return row location pop bp ret ifdef Microsoft _n_row endp else n_row endp endif ;/****************************************************************************/ ;----------------------------------------------------------- ; This routine was submitted by David T. Burhans, Jr. ; It was obtained from an RBBS-PC electronic bulleton board ; dedicated to the "C" language [(703) 321-7003]. ;----------------------------------------------------------- ; ; SOUND - This routine produces a tone of a specified frequency ; and duration on the speaker. ; The frequency in Hertz is moved into DI (21 to 65535 Hertz) ; and the duration in hundredths of a second, is moved into BX ; (0 to 65535). ; The routine is called with the following sequence: ; ; Unsigned frequency, duration; ; ; n_sound(frequency, duration); ; ifdef Microsoft _n_sound proc far else n_sound proc far endif push bp mov bp,sp mov di,[bp+X] ; Frequency mov bx,[bp+X+2] ; Duration cmp di, 21H ; See if freq. is below minimum jb done ; If less than limit, then ; clean up and return mov al,0B6H ; Write timer mode register out 43H, al mov dx, 14H ; Timer divisor = mov ax, 4F38H ; 1331000/Frequency div di out 42H,al ; Write timer 2 count low byte mov al,ah out 42H,al ; Write timer 2 count high byte in al,61H ; Get current Port B setting mov ah,al ; and save it in AH or al,3 ; Turn speaker on out 61H,al hold: mov cx, 2801 spkr_on: loop spkr_on dec bx ; Speaker-on count expired? jnz hold ; If not, keep speaker on mov al,ah ; Otherwise, recover value of port out 61H,al done: mov sp,bp ; Restore locals from stack. pop bp ; Restore caller's bp ret ; Return to caller ifdef Microsoft _n_sound endp else n_sound endp endif ;/***************************************************************/ ; ; Find the column position of the cursor. ; Must be preceded by a n_row call!!!! ; Will work when these routines already know the cursor position ; ifdef Microsoft _n_col proc far else n_col proc far endif xor ah,ah mov al,col ; return col location ret ifdef Microsoft _n_col endp else n_col endp endif ;/***************************************************************/ ; n_clear ; Clear what we think is the current window ; ifdef Microsoft _n_clear proc far else n_clear proc far endif push bp mov al,0 ; clear window mov ah,6 ; scroll up function mov ch,topl ; top row to clear mov cl,leftc ; left column mov dh,botl ; bottom row to clear mov dl,rightc ; right side to clear mov bh,att ; attribute to use for blanks int 10h ; call BIOS pop bp ret ifdef Microsoft _n_clear endp else n_clear endp endif ;/***************************************************************/ ; n_upscroll ; Scroll up what we think is the current window, n lines ; ifdef Microsoft _n_upscroll proc far else n_upscro proc far endif push bp mov bp,sp mov al,[bp+x] ; number of lines to scroll mov ah,6 ; scroll up function mov ch,topl ; top row to clear mov cl,leftc ; left column mov dh,botl ; bottom row to clear mov dl,rightc ; right side to clear mov bh,att ; attribute to use int 10h ; call BIOS pop bp ret ifdef Microsoft _n_upscroll endp else n_upscro endp endif ;/***************************************************************/ ; n_dnscroll ; Scroll up what we think is the current window, n lines ; ifdef Microsoft _n_dnscroll proc far else n_dnscro proc far endif push bp mov bp,sp mov al,[bp+x] ; number of lines to scroll mov ah,7 ; scroll down function mov ch,topl ; top row to clear mov cl,leftc ; left column mov dh,botl ; bottom row to clear mov dl,rightc ; right side to clear mov bh,att ; attribute to use for blank lines int 10h ; call BIOS pop bp ret ifdef Microsoft _n_dnscroll endp else n_dnscro endp endif ;/***************************************************************/ ; n_putchar(letter) ; puts onto screen at current cursor location ; ifdef Microsoft _n_putchar proc far else n_putcha proc far endif push bp mov bp,sp mov al,[bp+x] ; char to write xor bx,bx ; set page number for all cursor addresses cmp al,10 ; line feed jnz nxt mov al,row ; get current cursor row position cmp al,botl jl noscr ; within window area mov ax,1 ; scroll up one line push ax ifdef Microsoft call _n_upscroll ; scrolls it up one else call n_upscro ; scrolls it up one endif pop ax ; take parameter off of stack ; mov al,leftc ; get left side value ; mov col,al ; set cursor to left jmp putcur noscr: ; mov al,leftc ; return cursor to left side ; mov col,al inc row jmp putcur ; set new cursor position, return nxt: cmp al,7 ; ctrl-G, bell jnz nxt2 ; ; handle bell with a call to a special routine ; mov ax,12 ; duration of tone push ax mov ax,1000 ; frequency of tone push ax ifdef Microsoft call _n_sound else call n_sound endif add sp,4 ; remove params from stack here2: pop bp ret nxt2: cmp al,13 ; cr, home the cursor jnz trytab mov al,leftc ; new cursor position mov col,al jmp putcur trytab: cmp al,9 ; tab character jnz notcrlf ; ready for regular character ; expand tabs mov dl,col ; get cursor position mov cl,3 shr dl,cl ; divide cursor position by 8 inc dl ; increment cursor position shl dl,cl ; multiply back by 8 mov col,dl ; check to see if past right side mov dl,col ; get where the cursor has moved to cmp dl,rightc ; over the side yet? ja tabover ; set the new position jmp putcur tabover: mov dl,leftc mov col,dl inc row ; to next row mov dl,row ; what row? cmp dl,botl jg tabnos ; we are okay jmp putcur tabnos: dec row ; need to scroll mov ax,1 push ax ifdef Microsoft call _n_upscroll ; scroll window up one line else call n_upscro ; scroll window up one line endif pop ax mov al,leftc ; reset cursor position to left mov col,al jmp putcur notcrlf: cmp al,8 ; backspace jnz regchar mov al,col ; where is cursor? cmp al,leftc ; is at left of screen? jz here2 dec col ; decrement cursor position jmp putcur ; okay, move cursor where it belongs regchar: mov cx,1h ; number of repetitions = 1 mov bl,att ; attribute of char mov ah,9 ; write char int 10h inc col ; move cursor over one. mov dl,col ; get current position cmp dl,rightc ; is at right side of screen? jle putcur ; no, char can just be put out, cursor moved. ; ; check word-wrap because we are at edge of window ; mov dl,wrap or dl,dl ; 0 = no wrap, 1 = wrap jz nowrap mov dl,leftc ; cursor will wrap around mov col,dl ; save the new column position inc row ; to next row mov dl,botl cmp row,dl ; do we need to scroll? jng putcur ; no, okay for next row, normal wrap ; scroll screen up one push ax mov ax,1 push ax ifdef Microsoft call _n_upscroll ; scroll window up one line else call n_upscro ; scroll window up one line endif pop ax pop ax ; get the character back dec row ; scrolled up one jmp putcur ; don't wrap nowrap: dec col putcur: mov dh,row mov dl,col mov ah,2h xor bx,bx int 10h ; set cursor position escapehere: pop bp ret tele: mov bl,3 ; send whatever char mov ah,14 int 10h ret ifdef Microsoft _n_putchar endp else n_putcha endp endif ;/**********************************************************************/ ; draw ; place characters on the screen-- checking for bounds, etc. all done ; at a higher level. ; ; n_draw(s,len) ; char*s ; int len; ; ifdef Microsoft _n_draw proc far else n_draw proc far endif push bp mov bp,sp push es ; ; for now, assume that the cursor location has already been set ; (cursor is there) ; mov si,[bp+x] ; pointer to string mov ax,[bp+x+2] ; ds of string mov es,ax mov di,[bp+x+4] ; # of characters mov dh,row ; row where cursor is loopdraw: mov al,es:[si] ; get character to write from es:si inc si mov cx,1h ; number of repetitions = 1 xor bh,bh ; display page 0 mov bl,att ; attribute of char mov ah,9 ; write char int 10h inc col ; move the cursor over mov dl,col mov ah,2h xor bx,bx int 10h ; set cursor position dec di ; check counter jnz loopdraw ; go through to required count pop es pop bp ret ifdef Microsoft _n_draw endp else n_draw endp endif ;/**********************************************************************/ ; cheat ; put characters on the screen as per n_draw, but write directly ; to display memory. Don't even check for retrace ; ifdef Microsoft _n_cheat proc far else n_cheat proc far endif push bp mov bp,sp push es push ds ; ; for now, assume that the cursor location has already been set ; (cursor is there) ; mov ax,regen mov es,ax ; set up for segment where screen is mov al,row ; row where chars go xor ah,ah mov bl,160 mul bl ; ax now equals start of row xor bh,bh mov bl,col shl bl,1 ; col*2 add ax,bx ; add in column offset mov di,ax ; store in di (es:di) mov cx,[bp+x+4] ; # of characters add col,cl ; column position of cursor mov ah,att ; attribute for chars mov si,[bp+x] ; pointer to string mov bx,[bp+x+2] ; ds of string mov ds,bx ; ; do the move ; cld lpcheat: lodsb ; get next byte stosw ; store byte and attribute loop lpcheat pop ds pop es pop bp ret ifdef Microsoft _n_cheat endp else n_cheat endp endif ;/**********************************************************************/ ; ; keyboard handling ; the interrupt and codes for the keyboard interface. keyboard equ 16h ;interrupt 16 to deal with keyboard getc equ 0 ;code for reading a character checkc equ 1 ;code for keyboard status ;********************************************************************** ; ; get a translated character from the keyboard. ; Why hasn't someone else written this first? ; Tim Krauskopf ; ifdef Microsoft _n_getchar proc far else n_getcha proc far endif ;return the next character pressed ; translate if necessary push bp mov ah,getc ;ask for a keyboard character int keyboard or al,al jnz notextend ; is it an extended char? mov bx,offset dgroup:tr ; convert special key mov al,ah xlatb ; translate through table notextend: mov ah,0 ; char returns in al pop bp ret ifdef Microsoft _n_getchar endp else n_getcha endp endif ;********************************************************************** ; Check for character present at the keyboard ; If there is one available, return it, if not, return -1 ; ; translate any extended characters ; ifdef Microsoft _n_chkchar proc far else n_chkcha proc far endif push bp mov ah,checkc int keyboard mov ax,-1 jz nokey ifdef Microsoft call _n_getchar ;get the coded character else call n_getcha ;get the coded character endif nokey: pop bp ret ifdef Microsoft _n_chkchar endp else n_chkcha endp endif ;/***************************************************************/ ; savewin ; copy the current window into a buffer ; ; usage: n_savewindow(buffer); ; ifdef Microsoft _n_savewin proc far else n_savewi proc far endif push bp mov bp,sp push es push ds mov ax,[bp+x+2] ; ds of buffer mov es,ax mov di,[bp+x] ; pointer to buffer cld ; ; store parameters in first, for recall later ; mov al,row ; cursor position: row,col stosb mov al,col stosb mov al,topl ; window boundaries stosb mov al,leftc stosb mov al,botl stosb mov al,rightc stosb ; store in buffer too ; ; calculate amount to move ; ; ; calculate number of lines to move ; mov al,topl ; x1 mov bl,botl ; x2 sub bl,al ; x2 = x2-x1 inc bl ; add 1 line mov [bp+x],bl ; keep it somewhere safe ; ; calculate screen offset = 160*x1+2*y1; ; ; al has topl in it xor ah,ah mov bl,160 mul bl ; how many chars in x1 lines, in ax xor ch,ch mov cl,leftc ; chars to left side shl cl,1 ; *2 counts attributes add ax,cx ; add them mov si,ax ; place into source index ; ; find number of characters to move each time ; mov cl,rightc sub cl,leftc ; y2 = y2-y1 inc cl ; add 1 mov [bp+x+1],cl ; safe place again ; ; find number to add to wrap around for each line ; mov bl,cl ; # of chars moved each time shl bl,1 ; chars*2 mov al,160 sub al,bl ; 160-chars*2 mov [bp+x+2],al ; safe place to keep it ; ; mov ax,regen ; where source segment is (screen) mov ds,ax mov dx,03dah ; color card status port xor bh,bh mov bl,[bp+x] ; howmany lines to move cmp ax,0b000h ; check for mono card jnz getline mov dx,03bah ; change status port value getline: mov cl,[bp+x+1] ; # of chars to move getchar: ; in al,dx ; get status byte ; test al,1 ; horizontal retrace ; jnz getchar ; not there yet ;ison: in al,dx ; test al,1 ; jz ison ; still on, try again movsw ; now is time, move the word loop getchar ; do another until this line is done ; dec bx jz endkeep ; done, we are out of here mov al,[bp+x+2] xor ah,ah add si,ax ; go to next line, skip 160-n*2 bytes jmp getline endkeep: pop ds pop es pop bp ret ifdef Microsoft _n_savewin endp else n_savewi endp endif ;/***************************************************************/ ; restwindow ; restore the contents of the window to the screen ; ifdef Microsoft _n_restwin proc far else n_restwi proc far endif push bp mov bp,sp push es push ds mov ax,[bp+x+2] ; ds of buffer push ax ; will pop to ds later mov es,ax ; used to restore variable values mov si,[bp+x] ; pointer to buffer cld ; ; get back stored variables ; mov al,es:[si] ; get cursor row mov row,al inc si mov al,es:[si] mov col,al inc si mov al,es:[si] ; window boundaries mov topl,al inc si mov al,es:[si] mov leftc,al inc si mov al,es:[si] mov botl,al inc si mov al,es:[si] mov rightc,al inc si ; now we are ready for data ; ; calculate amount to move ; ; ; calculate number of lines to move ; mov al,topl ; x1 mov bl,botl ; x2 sub bl,al ; x2 = x2-x1 inc bl ; add 1 line mov [bp+x],bl ; keep it somewhere safe ; ; calculate screen offset = 160*x1+2*y1; ; ; al has topl in it xor ah,ah mov bl,160 mul bl ; how many chars in x1 lines, in ax xor ch,ch mov cl,leftc ; chars to left side shl cl,1 ; *2 counts attributes add ax,cx ; add them mov di,ax ; place into source index ; ; find number of characters to move each time ; mov cl,rightc sub cl,leftc ; y2 = y2-y1 inc cl ; add 1 mov [bp+x+1],cl ; safe place again ; ; find number to add to wrap around for each line ; mov bl,cl ; # of chars moved each time shl bl,1 ; chars*2 mov al,160 sub al,bl ; 160-chars*2 mov [bp+x+2],al ; safe place to keep it ; ; mov ax,regen ; screen will receive this data mov es,ax pop ds ; set ds to where data is coming from mov dx,03dah ; color card status port xor bh,bh mov bl,[bp+x] ; howmany lines to move cmp ax,0b000h ; check for mono card jnz rgetline mov dx,03bah ; change status port value rgetline: mov cl,[bp+x+1] ; # of chars to move rgetchar: ; in al,dx ; get status byte ; test al,1 ; horizontal retrace ; jnz rgetchar ; not there yet ;rison: in al,dx ; test al,1 ; jz rison ; still on, try again movsw ; now is time, move the word loop rgetchar ; do another until this line is done ; dec bx jz rendkeep ; done, we are out of here mov al,[bp+x+2] xor ah,ah add di,ax ; go to next line, skip 160-n*2 bytes jmp rgetline rendkeep: pop ds mov dh,row ; load stored cursor position mov dl,col mov ah,2h xor bx,bx int 10h ; set cursor position to saved value pop es pop bp ret ifdef Microsoft _n_restwin endp else n_restwi endp endif ;************************************************************************ ; n_puts ; Window-compatible puts, uses n_putchar and also translates ; \n to CRLF ; ; usage: identical to puts() ; ifdef Microsoft _n_puts proc far else n_puts proc far endif push bp mov bp,sp nextc: push ds mov dx,[bp+x+2] ; ds of string mov ds,dx mov bx,[bp+x] ; ptr to string mov al,[bx] ; get character xor ah,ah ; clear ah pop ds or al,al ; is it end of string? jz donest inc word ptr [bp+x] ; increment pointer for next one cmp al,10 ; newline? jnz dochar mov al,13 push ax ifdef Microsoft call _n_putchar else call n_putcha endif pop ax mov ax,10 dochar: push ax ifdef Microsoft call _n_putchar else call n_putcha endif pop ax ; take off of the stack jmp nextc donest: mov ax,13 ; CR push ax ifdef Microsoft call _n_putchar else call n_putcha endif pop ax mov ax,10 ; LF push ax ifdef Microsoft call _n_putchar else call n_putcha endif pop ax pop bp ret ifdef Microsoft _n_puts endp else n_puts endp endif ;********************************************************************** ; ; find first ; make dos find file names according to wildcards ; n_findfirst(filename,attr) ; char *filename; int attr; ; ifdef Microsoft _n_findfirst proc far else n_findfi proc far endif push bp mov bp,sp push es push ds mov ah,02fh ; DOS function get DTA int 21h ifdef Microsoft mov _dtaptr,bx ; squirrel a copy for me mov ax,es mov _dtads,ax else mov dtaptr,bx ; squirrel a copy for me mov ax,es mov dtads,ax endif mov ax,[bp+x+2] ; ds of filename ptr mov ds,ax mov dx,[bp+x] ; ptr part of filename mov cx,[bp+x+4] ; attribute to search for mov ah,04eh ; find matching file DOS call int 21h jc badret ; ax already contains error code xor ax,ax badret: pop ds pop es pop bp ret ifdef Microsoft _n_findfirst endp else n_findfi endp endif ; ; n_findnext() ; will find entries that follow findfirst ; no need to respecify file name ; ifdef Microsoft _n_findnext proc far else n_findne proc far endif push bp mov ah,04fh ; find next DOS call int 21h jc nbadret xor ax,ax nbadret: pop bp ret ifdef Microsoft _n_findnext endp else n_findne endp endif ; ; get the number of timer clicks from BIOS ; ifdef Microsoft _n_clicks proc far ; must be declared long n_clicks() else n_clicks proc far ; must be declared long n_clicks() endif mov ah,0 int 1ah ifdef Microsoft mov ax,dx ; MSC uses AX-lo, DX-hi mov dx,cx ; return values from interrupt 1A else mov ax,cx ; Lattice uses AX-hi, BX-lo mov bx,dx ; Lattice returns in BX, MSC in DX and switched endif ret ifdef Microsoft else endif ifdef Microsoft _n_clicks endp else n_clicks endp endif ifdef Microsoft _TEXT ends else endps endif end ; keep it somewhere safe ; ; calculate screen offset = 160*x1+2*y1; ; ; al has topl in it xor ah,ah mov bl,160 mul bl ; how many chars in x1 lines, in ax xor ch,ch mov cl,leftc ; chars to left side shl cl,1 ; *2 counts attributes add ax,cx ; add them mov si,ax ; place into source index ; ; fsource/ipasm.asm 644 144 13 16707 4267454372 7265 ; ; TCP/IP support routines ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;**************************************************************************** ; NAME IPASM INCLUDE DOS.MAC SETX PSEG PUBLIC IPCHECK,TCPCHECK,MOVEBYTE,LONGSWAP,INTSWAP,DBG ; ; Routines for general use by the communications programs ; ; ;************************************************************************ ; DBG ; provides a synch point for debugging ; dbg proc far nop nop nop ret dbg endp ; ;************************************************************************* ; Internet header checksum ; header checksum is calculated for a higher level program to verify ; ; USAGE: ipcheck(ptr) ; ; this proc knows that the IP header length is found in the first byte ; IPCHECK PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] ; ds for input data MOV DS,AX MOV SI,[BP+X] ; pointer to data MOV CX,[BP+X+4] ; count of words to test XOR BX,BX CLC CHKSUM: LODSW ; get next word ADC BX,AX ; keep adding LOOP CHKSUM ; til' done ADC BX,0 ; adds the carry bit in ; NOT BX ; take one more 1-complement MOV AX,BX POP DS POP BP RET IPCHECK ENDP ; ; TCP checksum, has two parts, including support for a pseudo-header ; ; usage: tcpcheck(psptr,tcpptr,tcplen) ; char *psptr,*tcpptr; pointers to pseudo header and real header ; int tcplen length of tcp packet in checksum ; TCPCHECK PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] ; ds for input data for pseudo-hdr MOV DS,AX MOV SI,[BP+X] ; pointer to data MOV CX,6 ; length of p-hdr in words XOR BX,BX ; clear to begin CLC PCHKSUM: LODSW ; get next word ADC BX,AX ; keep adding LOOP PCHKSUM ; til' done ADC BX,0 ; adds the carry bit in ; ; NOW THE REAL THING ; MOV AX,[BP+X+6] ; ds of real stuff MOV DS,AX MOV SI,[BP+X+4] ; pointer MOV CX,[BP+X+8] ; count of bytes to test MOV DX,CX ; keep a copy SHR CX,1 ; divide by two, round down CLC RCHKSUM: LODSW ADC BX,AX ; add to previous running sum LOOP RCHKSUM ADC BX,0 ; add the last carry in again AND DX,1 ; odd # of bytes? JZ NOTODD LODSB ; get that last byte XOR AH,AH ; clear the high portion ADD BX,AX ; add the last one in ADC BX,0 ; add the carry in, too NOTODD: NOT BX ; take one more 1-complement MOV AX,BX POP DS POP BP RET TCPCHECK ENDP ; ;******************************************************************** ; New movebytes ; Move an arbitrary number of bytes from one location to another. ; Thanks to Russell Nelson for his contribution to this code. ; ; Usage: ; movebytes(to,from,count) ; char *to,*from; ; int16 count ; moves < 64K from one 4 byte pointer to another. Does not handle ; overlap, but does copy quickly. ; MOVEBYTE PROC FAR PUSH BP MOV BP,SP PUSH DS PUSH ES MOV AX,[BP+X+2] ; WHERE TO PUT IT MOV ES,AX MOV AX,[BP+X+6] ; WHERE TO GET IT MOV DS,AX MOV SI,[BP+X+4] ; FROM PTR MOV DI,[BP+X] ; TO PTR MOV CX,[BP+X+8] ; HOW MANY TO MOVE shr cx,1 ; make into a word count jnc dowords movsb ; move the odd byte dowords: rep movsw POP ES POP DS POP BP RET MOVEBYTE ENDP ; ;************************************************************************* ; longswap ; swap the bytes of a long integer from PC ; order (reverse) to in-order. This will work both ways. ; returns the new long value ; usage: ; l2 = longswap(l) ; long l; ; LONGSWAP PROC FAR PUSH BP MOV BP,SP MOV BX,[BP+X+2] ; high bytes of the long int MOV AX,[BP+X] ; low bytes of the long int ; ; GET THE DATA ; XCHG AH,AL ; SWAP THEM, these are now low XCHG BH,BL ; swap the others POP BP RET LONGSWAP ENDP ; ;************************************************************************* ; INTSWAP ; swap the bytes of an integer, returns the swapped integer ; ; usage: i = intswap(i); ; INTSWAP PROC FAR MOV BX,SP MOV AX,SS:[BX+4] ; CAN IT BE DONE? XCHG AH,AL RET ; PUSH BP ; MOV BP,SP ; MOV AX,[BP+X] ; GET THE INT ; XCHG AH,AL ; SWITCH THEM, THAT'S IT ; POP BP ; RET INTSWAP ENDP ; ;************************************************************************** ; ; Routines to install and deinstall a timer routine which calls ; netsleep(0); ; The timer is set to go off every 1/2 second to check for packets ; in the incoming packet buffer. We use the user-hook into the system ; timer which occurs every 1/18th of a second. ; ; TIMEINT EQU 4*1CH ; User hook to timer int EXTRN netsleep:FAR ; C routine which gets called from handler PUBLIC TINST,TDEINST ;************************************************************************* ; ; Take out the timer interrupt handler, restore previous value ; TDEINST PROC FAR MOV CX,CS:TIP ; get old ip from save spot MOV DX,CS:TCS ; get old cs from save spot MOV BX,TIMEINT ; interrupt in table for timer PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX CLI MOV [BX],CX ; store old ip into the table INC BX INC BX ; move pointer in interrupt table MOV [BX],DX ; store old cs into the table STI POP DS RET TDEINST ENDP ; ; ; install the timer interrupt handler, the handler is technically ; part of this procedure. ; TINST PROC FAR XOR AX,AX MOV CS:TENTER,AL ; clear this flag MOV CS:TMYDS,DS ; store for use by handler MOV BX,TIMEINT ; interrupt in table for timer (1C) PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX MOV AX,OFFSET THAND ; where the handler is CLI MOV DX,[BX] ; keep copy of the ip MOV [BX],AX ; store ip into the table INC BX INC BX ; move pointer in interrupt table MOV CX,[BX] ; keep copy of the cs, too MOV AX,CS MOV [BX],AX ; store new cs into the table STI POP DS MOV CS:TIP,DX ; store them away MOV CS:TCS,CX RET ; ; Code segment addressable data for keeping track of the interrupt handler ; stuff ; TMYDS DW 00H ; the data segment for this assembly code TICNT DB 0 ; counter of 1/18ths sec TENTER DB 00 TIP DW 00 TCS DW 00 ; ; The handler itself. ; THAND: ; not a public name, only handles ints STI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI CLD ; all moves will be forward MOV AL,CS:TENTER OR AL,AL JNZ TIME2 MOV AL,1 MOV CS:TENTER,AL ; set flag to indicate busy INC CS:TICNT MOV AL,CS:TICNT ; counter for us AND AL,7 ; see if # mod 8 = 0 JNZ TSKIP ; skip 7 out of 8 times ; mov al,60h ; EOI for timer int out 20h,al ; let lower interrupts in ; ; SET UP CORRECT DS ; MOV DS,CS:TMYDS ; get correct ds ; ; do we have to set up our own stack here? ; xor ax,ax PUSH AX CALL netsleep POP AX TSKIP: XOR AL,AL MOV CS:TENTER,AL ; reenter flag, done now TIME2: POP SI POP DI POP DX POP CX POP BX POP AX POP ES POP DS ; ; forward to any other existing routines ; JMP DWORD PTR CS:TIP TINST ENDP ENDPS END NAME IPASM INCLUDE DOS.MAC SETX PSEG PUBLIC IPCHECKsource/dos.mac 644 144 13 6025 4267454373 6672 .XLIST PAGE 58,132 ;** ; ; This macro library defines the operating environment for the 8086 L ; memory model, which allows 1M bytes of data and 1M bytes of program. ; ;** MSDOS EQU 2 ;** ; ; The following symbols define the 8086 memory mode being used. Set LPROG ; to 1 for a large program segment (greater than 64K-bytes), and set LDATA ; to 1 for a large data segment. Set COM to 1 to generate .COM files ; instead of .EXE files. Note that if COM is not zero, then LPROG and ; LDATA must be 0. ; ;** COM EQU 0 LPROG EQU 1 LDATA EQU 1 ;** ; ; The following symbols are established via LPROG and LDATA as follows: ; ; S8086 set for small model (small prog, small data) ; D8086 set for model with large data, small prog ; P8086 set for model with large prog, small data ; L8086 set for large model ; ;** IF (LPROG EQ 0) AND (LDATA EQ 0) S8086 EQU 1 D8086 EQU 0 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG EQ 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 1 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA EQ 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 1 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 0 L8086 EQU 1 ENDIF ;** ; ; The DSEG and PSEG macros are defined to generate the appropriate GROUP ; and SEGMENT statements for the memory model being used. The ENDDS and ; ENDPS macros are then used to end the segments. ; ;** DSEG MACRO DGROUP GROUP DATA DATA SEGMENT WORD PUBLIC 'DATA' ASSUME DS:DGROUP ENDM ENDDS MACRO DATA ENDS ENDM IF S8086 PSEG MACRO PGROUP GROUP PROG PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:PGROUP ENDM ENDPS MACRO PROG ENDS ENDM ENDIF IF D8086 PSEG MACRO CGROUP GROUP CODE CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CGROUP ENDM ENDPS MACRO CODE ENDS ENDM ENDIF IF P8086 PSEG MACRO _CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:_CODE ENDM ENDPS MACRO _CODE ENDS ENDM ENDIF IF L8086 PSEG MACRO _PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:_PROG ENDM ENDPS MACRO _PROG ENDS ENDM ENDIF ;** ; ; The BEGIN and ENTRY macros establish appropriate function entry points ; depending on whether NEAR or FAR program addressing is being used. The ; only difference between the two is that BEGIN generates a PROC operation ; to start a segment. ; BEGIN MACRO NAME ; begin a function PUBLIC NAME IF LPROG NAME PROC FAR ELSE NAME PROC NEAR ENDIF ENDM ENTRY MACRO NAME PUBLIC NAME IF LPROG NAME LABEL FAR ELSE NAME LABEL NEAR ENDIF ENDM ;** ; ; The following symbols are defined to help set up a STRUC defining the ; stack frame: ; ; CPSIZE -> code pointer size (2 or 4) ; DPSIZE -> data pointer size (2 or 4) ; ; These wouldn't be necessary if it were possible to use macros or even ; conditionals within a STRUC. ; IF LPROG CPSIZE EQU 4 ELSE CPSIZE EQU 2 ENDIF IF LDATA DPSIZE EQU 4 ELSE DPSIZE EQU 2 ENDIF ; ; The SETX macro sets the symbol X to 4 if LPROG is 0 or to 6 otherwise. ; X can then be used to skip past the BP and return address save area ; in the stack frame when accessing the function arguments. ; SETX MACRO IF LPROG X EQU 6 ELSE X EQU 4 ENDIF ENDM .LIST OT BX ; take one more 1-complement MOV AX,BX POP DS POP BP RET TCPCHECK ENDP ; ;******************************************************************** ; New movebytes ; Move an arbitrary number of bytes from one location to another. ; Thanks to Russell Nelson for his contribution to this code. ; ; Usage: ; movebytes(to,from,count) ; char *to,*from; ; int16 count ; moves < 64K from one 4 byte pointer to another. Does not handle ; overlap, but does copy quickly. ; MOVEsource/tek/tekstor.h 644 144 13 1255 4267454556 10055 /* tekstor.h Declarations to allow use of tekstor.c */ struct handle { char *pool; unsigned int poolsize; struct handle *next; }; typedef struct handle HANDLE, *HANDLEP; typedef struct { HANDLE *firsth, /* beginning of list */ *thish, /* used for scanning thru data */ *lasth; /* last handle in store */ int thiselnum, /* number of currently-viewing element */ lastelnum; /* number of last element in store */ /* element numbers are in [0..(poolsize-1)] */ } STORE, *STOREP; #ifndef STORMASTER extern STOREP newstore(); #endif #ifndef STORMASTER extern void freestore(); #endif #ifndef STORMASTER extern int addstore(), topstore(), nextitem(), unstore(); #endif source/tek/vgfont.h 644 144 13 4051 4267454556 7662 char *VGfont[95] = { "", /* space */ /* stroked font designed by Aaron Contorer */ /* ! thru / */ /* ! " */ "4d1t1h1b1f3w5t", "4w4t4d4b", /* # $ % */ "2w8h4w8f2e8b4d8t", "4d8t1x3d6f1v1b1n6h1n1b1v6f", "8y6a2f2b2h2t6c2f2b2h2t", /* & */ "8d6r2f2t4h4v4b4h4y", /* ' */ "2w3e3y", /* ( ) */ "4d2r4t2y", "4d2y4t2r", /* * + , - */ "4d8t1x3d6v6w6n", "4d2w4t2x3a6h", "2c3y", "3w8h", /* . */ "2d2h2t2f2b", "8y", /* 0-9 */ "2d4h2y4t2r4f2v4b2n2a8y", "2d4h2a8t2v", "8w6h2n2v4f2v2b8h", "6h2y2r4f4d2y2r6f", "6e2w8b2e8f6y", "6h2y2r6f4t8h", "2w2y4h2n2v4f2r4t2y4h", "8y8f", "2d4h2y2r4f2v2n4w2r2y4h2n2v", "2d4h2y4t2r4f2v2n4h2y", /* : thru @ */ /* : */ "2d2h2t2f2b4w2h2t2f2b", /* ; */ "2c4y2w2t2f2b2h", "4d4r4y", "1w8h3w8f", "4y4r", "4d1h1t1f1b2w2t3h1y2t1r6f1v", "7w1y6h1n6b1v2f4t1x1r3f1v2b1n3h1y", /* A-Z */ "4t4y4n4b2w8f", "6h2y2r6f6d2y2r6f8b", "6d2e2v4f2r4t2y4h2n", "6h2y4t2r6f8b", "8d8f8t8h4x8f", "8t8h4x8f", "7e1d1r5f2v4b2n4h2y2t3f", "8t4x8h4w8b", "8h4a8t4a8h", "2w2n4h2y6t", /* K */ "8t4x4h4y4z4n", "8d8f8t", "8t4n4y8b", "8t8n8t", "2d4h2y4t2r4f2v4b2n", "8t7h1n2b1v7f", "2d2r4t2y4h2n4b2v4f2e4n", /* R */ "8t7h1n2b1v7f4d4n", "6h2y2r4f2r2y6h", "4d8t4a8h", "8w6b2n4h2y6t", "8w4b4n4y4t", "8w8b4y4n8t", "8y8a8n", "4d4t4r4c4y", "8w8h8v8h", /* [ thru ` */ "6d4f8t4h", "8w8n", "2d4h8t4f", "4w4y4n", "8h", "8w2d3n", /* a */ "1w2t1y6h1n1t1x2b1n1q1v6f1r", "4w7h1n2b1v7f8t", "8d7f1r2t1y7h", "8e8b7f1r2t1y7h", "2w8h1t1r6f1v2b1n6h", /* f */ "4w7h1d3w1r4f1v7b", "1d6h1y2t1r6f1v2b1n7h4w5b1v6f1r", "8t5x1y6h1n3b", "4d4t3w1t1h1b1f", "2n4h2y4t3w1t1f1b1h", /* k */ "2d8w8b2w1h2y2z2n", /* l */ "5d1f1r7t", "4t1x1y2h1n3b3w1y2h1n3b", "4w1h4b3w1y5h1n3b", "1d6h1y2t1r6f1v2b1n", /* p */ "2x6t1x1y6h1n2b1v6f1r", "1d6h1y2t1r6f1v2b1n7d4w6b", "4w1h4b3w1y5h1n", "1d6h1y1r6f1r1y6h", "4w7h4w5a7b1n4h1y", "4w3b1n5h1y1c1f4t", /* v */ "4w4n4y", "4w2b2n2y2n2y2t", "2d4y4a4n", "2x2d6y4z4r", "2w2e4h4v4h", /* end of lower-case letters */ /* { thru ~ */ "8d3f1r2t1r2f2d1y2t1y3h", "4d8t", "3h1y2t1y2h2a1r2t1r3f", "6w2y2n2y" }; 086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 0 L8086 EQU 1 ENDIF ;** ; ; The DSEG and PSEG macros are defined to generate the appropriate GROUP ; and SEGMENT statements for the memory model being used. The ENDDS and ; ENDPS macros are then used to end the segments. ; ;** DSEG MACRO DGROUP GROUP DATA DATA SEGMENT WORD PUBLIC 'DATA' ASSUME DS:DGROUP ENDM ENDDS MACRO DATA ENDS ENDM IF S8086 PSEG MACRO PGROUP GROUP PROG source/tek/vgtek.h 644 144 13 7176 4267454557 7513 /* output devices */ #define MAXRG 7 #define DEVNULL 0 #define EGA 1 #define PS 2 #define HERCULES 3 #define NO9 4 #define CGA 5 #define HP 6 #define PREDCOUNT 50 extern int RGEinit(), RGEpoint(), RGEdrawline(), RGEnewwin(), RGEclrscr(), RGEpagedone(), RGEdataline(), RGEpencolor(), RGEcharmode(), RGEshowcur(), RGElockcur(), RGEhidecur(), RGEclose(), RGEuncover(), RGEinfo(), RGEgmode(), RGEtmode(), RGEbell(), RG0init(), RG0point(), RG0drawline(), RG0newwin(), RG0clrscr(), RG0pagedone(), RG0dataline(), RG0pencolor(), RG0charmode(), RG0showcur(), RG0lockcur(), RG0hidecur(), RG0close(), RG0uncover(), RG0info(), RG0gmode(), RG0tmode(), RG0bell(), RGPinit(), RGPpoint(), RGPdrawline(), RGPnewwin(), RGPclrscr(), RGPpagedone(), RGPdataline(), RGPpencolor(), RGPcharmode(), RGPshowcur(), RGPlockcur(), RGPhidecur(), RGPclose(), RGPuncover(), RGPinfo(), RGPgmode(), RGPtmode(), RGPbell(), RGHinit(), RGHpoint(), RGHdrawline(), RGHnewwin(), RGHclrscr(), RGHpagedone(), RGHdataline(), RGHpencolor(), RGHcharmode(), RGHshowcur(), RGHlockcur(), RGHhidecur(), RGHclose(), RGHuncover(), RGHinfo(), RGHgmode(), RGHtmode(), RGHbell(), RG9init(), RG9point(), RG9drawline(), RG9newwin(), RG9clrscr(), RG9pagedone(), RG9dataline(), RG9pencolor(), RG9charmode(), RG9showcur(), RG9lockcur(), RG9hidecur(), RG9close(), RG9uncover(), RG9info(), RG9gmode(), RG9tmode(), RG9bell(), RGCinit(), RGCpoint(), RGCdrawline(), RGCnewwin(), RGCclrscr(), RGCpagedone(), RGCdataline(), RGCpencolor(), RGCcharmode(), RGCshowcur(), RGClockcur(), RGChidecur(), RGCclose(), RGCuncover(), RGCinfo(), RGCgmode(), RGCtmode(), RGCbell(), RGHPinit(), RGHPpoint(), RGHPdrawline(), RGHPnewwin(), RGHPclrscr(), RGHPpagedone(), RGHPdataline(), RGHPpencolor(), RGHPcharmode(), RGHPshowcur(), RGHPlockcur(), RGHPhidecur(), RGHPclose(), RGHPuncover(), RGHPinfo(), RGHPgmode(), RGHPtmode(), RGHPbell(), ; extern char *RGEdevname(), *RG0devname(), *RGPdevname(), *RGHdevname(), *RG9devname(), *RGCdevname(), *RGHPdevname(), *RGEPdevname(); static RGLINK RG[MAXRG] = { RG0init, RG0point, RG0drawline, RG0newwin, RG0clrscr, RG0pagedone, RG0dataline, RG0pencolor, RG0charmode, RG0showcur, RG0lockcur, RG0hidecur, RG0close, RG0uncover, RG0info, RG0gmode, RG0tmode, donothing, RG0devname, RG0bell, RGEinit, RGEpoint, RGEdrawline, RGEnewwin, RGEclrscr, RGEpagedone, RGEdataline, RGEpencolor, RGEcharmode, RGEshowcur, RGElockcur, RGEhidecur, RGEclose, RGEuncover, RGEinfo, RGEgmode, RGEtmode, donothing, RGEdevname, RGEbell, RGPinit, RGPpoint, RGPdrawline, RGPnewwin, RGPclrscr, RGPpagedone, RGPdataline, RGPpencolor, RGPcharmode, RGPshowcur, RGPlockcur, RGPhidecur, RGPclose, RGPuncover, RGPinfo, RGPgmode, RGPtmode, donothing, RGPdevname, RGPbell, RGHinit, RGHpoint, RGHdrawline, RGHnewwin, RGHclrscr, RGHpagedone, RGHdataline, RGHpencolor, RGHcharmode, RGHshowcur, RGHlockcur, RGHhidecur, RGHclose, RGHuncover, RGHinfo, RGHgmode, RGHtmode, donothing, RGHdevname, RGHbell, RG9init, RG9point, RG9drawline, RG9newwin, RG9clrscr, RG9pagedone, RG9dataline, RG9pencolor, RG9charmode, RG9showcur, RG9lockcur, RG9hidecur, RG9close, RG9uncover, RG9info, RG9gmode, RG9tmode, donothing, RG9devname, RG9bell, RGCinit, RGCpoint, RGCdrawline, RGCnewwin, RGCclrscr, RGCpagedone, RGCdataline, RGCpencolor, RGCcharmode, RGCshowcur, RGClockcur, RGChidecur, RGCclose, RGCuncover, RGCinfo, RGCgmode, RGCtmode, donothing, RGCdevname, RGCbell, RGHPinit, RGHPpoint, RGHPdrawline, RGHPnewwin, RGHPclrscr, RGHPpagedone, RGHPdataline, RGHPpencolor, RGHPcharmode, RGHPshowcur, RGHPlockcur, RGHPhidecur, RGHPclose, RGHPuncover, RGHPinfo, RGHPgmode, RGHPtmode, donothing, RGHPdevname, RGHPbell, }; GET IT MOV DS,AX MOV SI,[BP+X+4] ; FROM PTR MOV DI,[BP+X] ; TO PTR MOV CX,[BP+X+8] ; HOW MANY TO MOVE shr cx,1 ; make into a word count jnc dowords movsb ; move the odd byte dowords: rep movsw POP ES POP DS POP BP RET MOVEBYTE ENDP ; ;************************************************************************* ; longswap ; swap the bytes of a long integer from PC source/tek/tekmain.c 644 144 13 2411 4267454507 7774 #define DEVNULL 0 #define EGA 1 #define POSTSCRIPT 2 #define HERCULES 3 #define NO9 4 #define CGA 5 #define HP 6 #define EP 7 #define ELO 8 #define BUFSIZE 6000 #include #include char testbuf[BUFSIZE]; /* text input buffer */ FILE *plot; writeln(c) char *c; { fprintf(plot,"%s",c); } charout(c) char c; { fputc(c,plot); } main(argc,argv) int argc; char *argv[]; { int w1; VGinit(); plot=fopen("tekout.x","wb"); /* RGPoutfunc(writeln); RGHPoutfunc(writeln); RGEPoutfunc(charout);*/ if (argc < 3) w1=VGnewwin(HERCULES); else w1=VGnewwin(argv[2][0] - 48); /* 2nd param is device number */ VGuncover(w1); VGpage(w1); if (argc==1) showfile(w1,"file1.tek"); else showfile(w1,argv[1]); /* 1st param is filepath */ /* VGpage(w1); drawall(w1); VGpage(w1); VGredraw(w1,w1); */ VGtmode(w1); VGclose(w1); } /* end main() */ static drawall(vw) /* redraw the whole thing incrementally */ { int i; while (!(i=VGpred(vw,vw))); } static showfile(vw,fname) int vw; char *fname; /* Read the file named by fname and display it in the window. */ { int datalen,i; FILE *fp = fopen(fname,"ra"); if (fp != NULL) { while ((datalen = fread(testbuf,1,BUFSIZE,fp)) > 0) { for (i=0; i /* used for debugging only */ #include /* used for CGA init call */ #define TRUE 1 #define FALSE 0 #define INXMAX 4096 #define INYMAX 4096 #define MAGNIFY 1000 #define SCRNXHI 639 #define SCRNYHI 199 #define MAXRW 32 /* max. number of real windows */ extern char *malloc(); static int CGAactive; /* number of currently CGAactive window */ static char *CGAvidram; static char CGApbit[SCRNXHI]; static unsigned char power2[8] = {1,2,4,8,16,32,64,128}; static char *CGAname = "Color Graphics Adaptor, 640 x 200"; static int CGAxmax=640; static int CGAymax=200; static int CGAbytes=80; /* number of bytes per line */ /* Current status of an CGA window */ struct CGAWIN { char inuse; /* true if window in use */ int pencolor, rotation, size; int winbot,winleft,wintall,winwide; /* position of the window in virtual space */ }; static struct CGAWIN CGAwins[MAXRW]; CGAsetup() /* prepare variables for use in other functions */ { int x; CGApbit[0]=128; CGApbit[1]=64; CGApbit[2]=32; CGApbit[3]=16; CGApbit[4]=8; CGApbit[5]=4; CGApbit[6]=2; CGApbit[7]=1; for (x=8; x<=SCRNXHI; x++) CGApbit[x]=CGApbit[x & 7]; CGAvidram=(char *)0xB8000; } RGCgmode() /* go into CGA graphics mode */ { union REGS *in, *out; in = (union REGS *)malloc(sizeof(union REGS)); out = (union REGS *)malloc(sizeof(union REGS)); in->x.ax = 6; /* 640x200 BW */ int86(0x10,in,out); free(in); free(out); } RGCtmode() /* go into CGA 80x25 color text mode */ { union REGS *in, *out; in = (union REGS *)malloc(sizeof(union REGS)); out = (union REGS *)malloc(sizeof(union REGS)); in->x.ax = 3; /* 80x25 color text */ int86(0x10,in,out); free(in); free(out); CGAactive = -1; } RGCdrawline(w,x0,y0,x1,y1) int w,x0,y0,x1,y1; /* draw a line from (x0,y0) to (x1,y1) */ /* uses Bresenham's Line Algorithm */ { int x,y,dx,dy,d,temp, dx2,dy2, /* 2dx and 2dy */ direction; /* +1 or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish line */ if (w != CGAactive) return; x0 = (int) ((long) x0 * CGAxmax / INXMAX); y0 = CGAymax - 1 - (int) ((long) y0 * CGAymax / INYMAX); x1 = (int) ((long) x1 * CGAxmax / INXMAX); y1 = CGAymax - 1 - (int) ((long) y1 * CGAymax / INYMAX); if (abs(y1-y0) > abs(x1-x0)) { /* transpose vertical-ish to horizontal-ish */ temp=x1; x1=y1; y1=temp; temp=x0; x0=y0; y0=temp; transpose=TRUE; } else transpose=FALSE; /* make sure line is left to right */ if (x1 < x0) { temp=x1; x1=x0; x0=temp; temp=y1; y1=y0; y0=temp; } /* SPECIAL CASE: 1 POINT */ if (x1==x0 && y1==y0) { CGAvidram[(y1&1)*8192 + (y1>>1) *80+(x1>>3)] |= CGApbit[x1]; return(0); } /* ANY LINE > 1 POINT */ x=x0; y=y0; dx=x1-x0; if (y1 >= y0) { dy=y1-y0; direction=1; } else { dy=y0-y1; direction= -1; } dx2=dx<<1; dy2=dy<<1; d = (dy<<1) - dx; if (transpose) { /* CASE OF VERTICALISH (TRANSPOSED) LINES */ while (x <= x1) { if (y>=0 && y=0 && x>1) *80+(y>>3)] |= CGApbit[y]; while (d >= 0) { y += direction; d -= dx2; } d += dy2; x++; } } else { /* CASE OF HORIZONTALISH LINES */ while (x <= x1) { if (x>=0 && x=0 && y>1) *80+(x>>3)] |= CGApbit[x]; while (d >= 0) { y += direction; d -= dx2; } d += dy2; x++; } } /* end horizontalish */ } /* end CGArasline() */ RGCclrscr(w) int w; /* Clear the screen. */ { if (w == CGAactive) { CGAsetup(); RGCgmode(); } } int RGCnewwin() /* Set up a new window; return its number. Returns -1 if cannot create window. */ { int w=0; while (w>3)] |= CGApbit[x2]; } } RGCpagedone(w) int w; /* Do whatever has to be done when the drawing is all done. (For printers, that means eject page.) */ { /* do nothing for CGA */ } RGCdataline(w,data,count) int w,count; char *data; /* Copy 'count' bytes of data to screen starting at current cursor location. */ { /* Function not supported yet. */ } RGCpencolor(w,color) int w,color; /* Change pen color to the specified color. */ { /* Function not supported. */ } RGCcharmode(w,rotation,size) /* Set description of future device-supported graphtext. Rotation=quadrant. */ { /* No rotatable device-supported graphtext is available on CGA. */ } /* Not yet supported: */ RGCshowcur() {} RGClockcur() {} RGChidecur() {} static int abs(x) int x; { if (x>=0) return(x); else return(-x); } RGCbell(w) int w; /* Ring bell in window w */ { if (w == CGAactive) putchar(7); } char *RGCdevname() /* return name of device that this RG supports */ { return(CGAname); } RGCinit() /* initialize all RGC variables */ { int i; CGAsetup(); for (i=0; i /* used for debugging only */ #include /* used for HGC init call */ #define TRUE 1 #define FALSE 0 #define INXMAX 4096 #define INYMAX 4096 #define MAGNIFY 1000 #define SCRNXHI 719 #define SCRNYHI 347 #define MAXHERC 32 /* max. number of Hercules windows */ /* Hercules control */ #define INDXPORT 0x3b4 #define CTRLPORT 0x3b8 #define DATAPORT 0x3b5 #define CONFPORT 0x3bf #define SCRN_ON 8 #define GRPH 0x02 #define TEXT 0x20 /* graphics */ static unsigned char HGCgtable[12] = { 0x35, 0x2d, 0x2e, 0x07, 0x5b, 0x02, 0x57, 0x57, 0x02, 0x03, 0x00, 0x00 }; /* text */ static unsigned char HGCttable[12] = { 0x61, 0x50, 0x52, 0x0f, 0x19, 0x06, 0x19, 0x19, 0x02, 0x0d, 0x0b, 0x0c }; extern char *malloc(); static int HGCactive; /* number of currently HGCactive window */ static char *HGCram; static char HGCpbit[SCRNXHI]; static unsigned char power2[8] = {1,2,4,8,16,32,64,128}; static char *HGCname = "Hercules High-Resolution Graphics"; static int HGCxmax=720; static int HGCymax=348; /* Current status of an HGC window */ struct HGCWIN { char inuse; /* true if window in use */ int pencolor, rotation, size; int winbot,winleft,wintall,winwide; /* position of the window in virtual space */ }; static struct HGCWIN HGCwin[MAXHERC]; HGCsetup() /* prepare variables for use in other functions */ { int x; HGCpbit[0]=128; HGCpbit[1]=64; HGCpbit[2]=32; HGCpbit[3]=16; HGCpbit[4]=8; HGCpbit[5]=4; HGCpbit[6]=2; HGCpbit[7]=1; for (x=8; x<=SCRNXHI; x++) HGCpbit[x]=HGCpbit[x & 7]; HGCram=(char *)0xB0000; } RGHgmode() /* go into HGC graphics mode */ { char *hdata = HGCgtable; long *video; /* long does 4 chars at a time */ char port; int memloc; /* set video chips */ outp(CTRLPORT,GRPH | SCRN_ON); outp(CONFPORT,0x01); for (port=0; port<12; port++) { outp(INDXPORT,port); outp(DATAPORT,*(hdata++)); } /* clear video buffer */ video = (long *)HGCram; for (memloc=0; memloc<8191; memloc++) *(video++) = 0; } RGHtmode() /* go into HGC text mode, compatible with IBM Monochrome */ { char *hdata = HGCttable; long *video; /* long does 4 chars at a time */ char port; int memloc; /* set video chips */ outp(CTRLPORT,TEXT | SCRN_ON); outp(CONFPORT,0x01); for (port=0; port<12; port++) { outp(INDXPORT,port); outp(DATAPORT,*(hdata++)); } /* clear video buffer */ video = (long *)HGCram; for (memloc=0; memloc<1000; memloc++) *(video++) = 0; HGCactive = -1; } static HGCrasline(w,x0,y0,x1,y1) int w,x0,y0,x1,y1; /* draw a line from (x0,y0) to (x1,y1) */ /* uses Bresenham's Line Algorithm */ { int x,y,dx,dy,d,temp, dx2,dy2, /* 2dx and 2dy */ direction; /* +1 or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish line */ x0 = (int) ((long) x0 * HGCxmax / INXMAX); y0 = HGCymax- 1 - (int) ((long) y0 * HGCymax / INYMAX); x1 = (int) ((long) x1 * HGCxmax / INXMAX); y1 = HGCymax -1 - (int) ((long) y1 * HGCymax / INYMAX); if (abs(y1-y0) > abs(x1-x0)) { /* transpose vertical-ish to horizontal-ish */ temp=x1; x1=y1; y1=temp; temp=x0; x0=y0; y0=temp; transpose=TRUE; } else transpose=FALSE; /* make sure line is left to right */ if (x1 < x0) { temp=x1; x1=x0; x0=temp; temp=y1; y1=y0; y0=temp; } /* SPECIAL CASE: 1 POINT */ if (x1==x0 && y1==y0) { HGCram[0x2000 * (y1%4) + 90*(y1 / 4) + (x1 / 8)] |= HGCpbit[x1]; return(0); } /* ANY LINE > 1 POINT */ x=x0; y=y0; dx=x1-x0; if (y1 >= y0) { dy=y1-y0; direction=1; } else { dy=y0-y1; direction= -1; } dx2=dx<<1; dy2=dy<<1; d = (dy<<1) - dx; if (transpose) { /* CASE OF VERTICALISH (TRANSPOSED) LINES */ while (x <= x1) { if (y>=0 && y=0 && x= 0) { y += direction; d -= dx2; } d += dy2; x++; } } else { /* CASE OF HORIZONTALISH LINES */ while (x <= x1) { if (x>=0 && x=0 && y= 0) { y += direction; d -= dx2; } d += dy2; x++; } } /* end horizontalish */ } /* end HGCrasline() */ RGHclrscr(w) int w; /* Clear the screen. */ { if (w == HGCactive) { HGCsetup(); RGHgmode(); } } int RGHnewwin() /* Set up a new window; return its number. Returns -1 if cannot create window. */ { int w=0; while (w=0) return(x); else return(-x); } RGHbell(w) int w; /* Ring bell in window w */ { if (w == HGCactive) putchar(7); } char *RGHdevname() /* return name of device that this RG supports */ { return(HGCname); } RGHinit() /* initialize all RGH variables */ { int i; HGCsetup(); for (i=0; i /* used for debugging only */ #include /* used for EGA init call */ #define TRUE 1 #define FALSE 0 #define INXMAX 4096 #define INYMAX 4096 #define SCRNXHI 639 #define SCRNYHI 349 #define MAXRW 32 /* max. number of real windows */ extern char *malloc(); static int EGAactive; /* number of currently EGAactive window */ static char *EGAname = "Enhanced Graphics Adaptor, 640 x 350"; #define EGAxmax 640 #define EGAymax 350 static int EGAbytes=80; /* number of bytes per line */ /* Current status of an EGA window */ struct EGAWIN { char inuse; /* true if window in use */ int pencolor, rotation, size; int winbot,winleft,wintall,winwide; /* position of the window in virtual space */ }; static struct EGAWIN EGAwins[MAXRW]; EGAsetup() /* prepare variables for use in other functions */ { } RGEgmode() /* go into EGA graphics mode */ { union REGS *in, *out; in = (union REGS *)malloc(sizeof(union REGS)); out = (union REGS *)malloc(sizeof(union REGS)); in->x.ax = 0x10; int86(0x10,in,out); free(in); free(out); } RGEtmode() /* go into EGA 80x25 color text mode */ { union REGS *in, *out; in = (union REGS *)malloc(sizeof(union REGS)); out = (union REGS *)malloc(sizeof(union REGS)); in->x.ax = 3; int86(0x10,in,out); free(in); free(out); EGAactive = -1; } RGEclrscr(w) int w; /* Clear the screen. */ { if (w == EGAactive) { EGAsetup(); RGEgmode(); } } int RGEnewwin() /* Set up a new window; return its number. Returns -1 if cannot create window. */ { int w=0; while (w=0) return(x); else return(-x); } RGEdrawline(w,x0,y0,x1,y1) int w,x0,y0,x1,y1; /* draw a line from (x0,y0) to (x1,y1) */ /* uses Bresenham's Line Algorithm */ { int x,y,dx,dy,d,temp, dx2,dy2, /* 2dx and 2dy */ direction; /* +1 or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish line */ if (w != EGAactive) return; x0 = (int) (((long) x0 * EGAxmax) / INXMAX); y0 = EGAymax - 1 - (int) ((EGAymax*(long) y0) / INYMAX); x1 = (int) (((long) x1 * EGAxmax) / INXMAX); y1 = EGAymax - 1 - (int) ((EGAymax*(long) y1) / INYMAX); if (abs(y1-y0) > abs(x1-x0)) { /* transpose vertical-ish to horizontal-ish */ temp=x1; x1=y1; y1=temp; temp=x0; x0=y0; y0=temp; transpose=TRUE; } else transpose=FALSE; /* make sure line is left to right */ if (x1 < x0) { temp=x1; x1=x0; x0=temp; temp=y1; y1=y0; y0=temp; } /* SPECIAL CASE: 1 POINT */ if (x1==x0 && y1==y0) { EGAset (x1, y1, EGAwins[w].pencolor); return(0); } /* ANY LINE > 1 POINT */ x=x0; y=y0; dx=x1-x0; if (y1 >= y0) { dy=y1-y0; direction=1; } else { dy=y0-y1; direction= -1; } dx2=dx<<1; dy2=dy<<1; d = (dy<<1) - dx; if (transpose) { /* CASE OF VERTICALISH (TRANSPOSED) LINES */ while (x <= x1) { if (y>=0 && y=0 && x= 0) { y += direction; d -= dx2; } d += dy2; x++; } } else { /* CASE OF HORIZONTALISH LINES */ while (x <= x1) { if (x>=0 && x=0 && y= 0) { y += direction; d -= dx2; } d += dy2; x++; } } /* end horizontalish */ } /* end RGEdrawline() */ RGEbell(w) int w; /* Ring bell in window w */ { if (w == EGAactive) putchar(7); } char *RGEdevname() /* return name of device that this RG supports */ { return(EGAname); } RGEinit() /* initialize all RGE variables */ { int i; EGAsetup(); for (i=0; i /* used for debugging only */ #include /* used for NO9 init call */ #define TRUE 1 #define FALSE 0 #define INXMAX 4096 #define INYMAX 4096 #define MAGNIFY 1000 #define SCRNXHI 511 #define SCRNYHI 479 #define MAXWIN 32 /* max. number of windows */ extern char *malloc(); static int NO9active; /* number of currently NO9active window */ static char *NO9ram; static int *NO9bank; /* bank control registers */ static unsigned char power2[8] = {1,2,4,8,16,32,64,128}; static char *NO9name = "Number Nine, 512 x 480"; static int NO9xmax=512; static int NO9ymax=480; /* Current status of an NO9 window */ struct NO9WIN { char inuse; /* true if window in use */ int pencolor, rotation, size; int winbot,winleft,wintall,winwide; /* position of the window in virtual space */ }; static struct NO9WIN NO9win[MAXWIN]; NO9setup() /* prepare variables for use in other functions */ { NO9ram=(char *)0xA0000; NO9bank = (int *)0xC0705; } static clrbuf(bank) unsigned int bank; { *NO9bank = bank; memset(NO9ram,0,0xFFFF); NO9ram[0xFFFF]=0; } RG9gmode() /* go into NO9 graphics mode -- not yet implemented */ { clrbuf(0x0000); clrbuf(0x00ff); clrbuf(0xff00); clrbuf(0xffff); } RG9tmode() /* go into NO9 text mode -- not supported */ { } RG9drawline(w,x0,y0,x1,y1) int w,x0,y0,x1,y1; /* draw a line from (x0,y0) to (x1,y1) */ /* uses Bresenham's Line Algorithm */ { int x,y,dx,dy,d,temp, dx2,dy2, /* 2dx and 2dy */ direction; /* +1 or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish line */ if (w != NO9active) return; x0 = (int) ((long) x0 * NO9xmax / INXMAX); y0 = NO9ymax - 1 - (int) ((long) y0 * NO9ymax / INYMAX); x1 = (int) ((long) x1 * NO9xmax / INXMAX); y1 = NO9ymax - 1 - (int) ((long) y1 * NO9ymax / INYMAX); if (abs(y1-y0) > abs(x1-x0)) { /* transpose vertical-ish to horizontal-ish */ temp=x1; x1=y1; y1=temp; temp=x0; x0=y0; y0=temp; transpose=TRUE; } else transpose=FALSE; /* make sure line is left to right */ if (x1 < x0) { temp=x1; x1=x0; x0=temp; temp=y1; y1=y0; y0=temp; } /* SPECIAL CASE: 1 POINT */ if (x1==x0 && y1==y0) { NO9point(x1,y1); return(0); } /* ANY LINE > 1 POINT */ x=x0; y=y0; dx=x1-x0; if (y1 >= y0) { dy=y1-y0; direction=1; } else { dy=y0-y1; direction= -1; } dx2=dx<<1; dy2=dy<<1; d = (dy<<1) - dx; if (transpose) { /* CASE OF VERTICALISH (TRANSPOSED) LINES */ while (x <= x1) { if (y>=0 && y=0 && x= 0) { y += direction; d -= dx2; } d += dy2; x++; } } else { /* CASE OF HORIZONTALISH LINES */ while (x <= x1) { if (x>=0 && x=0 && y= 0) { y += direction; d -= dx2; } d += dy2; x++; } } /* end horizontalish */ } /* end NO9rasline() */ RG9clrscr(w) int w; /* Clear the screen. */ { if (w == NO9active) { NO9setup(); RG9gmode(); } } int RG9newwin() /* Set up a new window; return its number. Returns -1 if cannot create window. */ { int w=0; while (w=0) return(x); else return(-x); } RG9bell(w) int w; /* Ring bell in window w */ { if (w == NO9active) putchar(7); } char *RG9devname() /* return name of device that this RG supports */ { return(NO9name); } RG9init() /* initialize all RG9 variables */ { int i; NO9setup(); for (i=0; i a pointer to a "pool" of storage memory created by malloc() > an int stating the number of bytes in the pool > a pointer to the next handle IDEAS FOR IMPROVEMENT: Store pool as part of handle, rather than pointed to by handle */ #define MINPOOL 0x0200 /* smallest allowable pool */ #define MAXPOOL 0x2000 /* largest allowable pool */ #include #define TRUE 1 #define FALSE 0 extern char *malloc(); #define STORMASTER #include "tekstor.h" STOREP newstore() /* Create a new, empty store and return a pointer to it. Returns NULL if not enough memory to create a new store. */ { STOREP s; s=(STOREP) malloc(sizeof(STORE)); if (s==NULL) { return(NULL); } else { s->lasth = s->thish = s->firsth = (HANDLEP) malloc(sizeof(HANDLE)); if (s->firsth==NULL) { free(s); return(NULL); } else { s->firsth->pool = malloc(MINPOOL); if (s->firsth->pool==NULL) { free(s->firsth); free(s); return(NULL); } else { s->lastelnum = s->thiselnum = -1; s->firsth->poolsize = MINPOOL; s->firsth->next = NULL; } } } return(s); } void freestore(s) STOREP s; /* Frees all pools and other memory space associated with store s. */ { HANDLEP h,h2; h = s->firsth; while (h != NULL) { h2 = h; free(h->pool); h = h->next; free(h2); } free(s); } int addstore(s,d) STOREP s; char d; /* Adds character d to the end of store s. Returns 0 if successful, -1 if unable to add character (no memory). */ { int n; /* temp storage */ int size; HANDLEP h; n = ++(s->lastelnum); size = s->lasth->poolsize; if (n < s->lasth->poolsize) { s->lasth->pool[n] = d; } else { /* Pool full; allocate a new one. */ if (sizelastelnum)--; return(-1); } else { h->pool = malloc(size); if (h->pool==NULL) { free(h); (s->lastelnum)--; return(-1); } else { h->poolsize = size; h->next = NULL; s->lasth->next = h; s->lasth = h; s->lastelnum = 0; h->pool[0] = d; } } } /* end of new pool allocation */ return(0); } /* end addstore() */ topstore(s) STOREP s; /* Reset stats so that a call to nextitem(s) will be retrieving the first item in store s. */ { s->thish = s->firsth; s->thiselnum = -1; } int nextitem(s) STOREP s; /* Increment the current location in store s. Then return the character at that location. Returns -1 if no more characters. */ { HANDLEP h; if (s->thish==s->lasth && s->thiselnum==s->lastelnum) return(-1); else { h = s->thish; if (++(s->thiselnum) < s->thish->poolsize) { return((int)(s->thish->pool[s->thiselnum])); } else { /* move to next pool */ h = h->next; s->thish = h; s->thiselnum = 0; return((int)(h->pool[0])); } } } /* end nextitem() */ int unstore(s) STOREP s; /* Removes ("pops") the last item from the specified store. Returns that item (in range 0-255), or returns -1 if there are no items in the store. */ { HANDLEP nextolast; if (s->lastelnum > -1) { /* last pool not empty */ return((int)(s->lasth->pool[(s->lastelnum)--])); } else { /* last pool empty */ if (s->lasth == s->firsth) return(-1); else { /* move back one pool */ nextolast = s->firsth; while (nextolast->next != s->lasth) nextolast = nextolast->next; free(nextolast->next); s->lasth = nextolast; s->lastelnum = nextolast->poolsize - 2; if (s->thish == nextolast->next) { s->thish = nextolast; s->thiselnum = s->lastelnum; } nextolast->next = NULL; return((int)(nextolast->pool[s->lastelnum+1])); } } } } RG9point(w,x,y) int w,x,y; /* set pixel at lsource/tek/vgtek.c 644 144 13 64675 4267454520 7523 /* vgtek.c by Aaron Contorer 1987 for NCSA bugfixes by Tim Krauskopf 1988 Takes Tektronix codes as input; sends output to real graphics devices. CHANGES TO MAKE: Create a function to make sure a window is attached to a real window. Calling program will call this whenever switching between active windows. Pass virtual window number to RG driver so it can call back. */ #define FALSE 0 #define TRUE 1 #define MAXVG 20 /* maximum number of VG windows */ /* temporary states */ #define HIY 0 /* waiting for various pieces of coordinates */ #define EXTRA 1 #define LOY 2 #define HIX 3 #define LOX 4 #define DONE 5 /* not waiting for coordinates */ #define ENTERVEC 6 /* entering vector mode */ #define CANCEL 7 /* done but don't draw a line */ #define RS 8 /* RS - incremental plot mode */ #define ESCOUT 9 /* when you receive an escape char after a draw command */ #define CMD0 50 /* got esc, need 1st cmd letter */ #define SOMEL 51 /* got esc L, need 2nd letter */ #define IGNORE 52 /* ignore next char */ #define SOMEM 53 /* got esc M, need 2nd letter */ #define IGNORE2 54 #define INTEGER 60 /* waiting for 1st integer part */ #define INTEGER1 61 /* waiting for 2nd integer part */ #define INTEGER2 62 /* waiting for 3rd (last) integer part */ #define COLORINT 70 #define GTSIZE0 75 #define GTSIZE1 76 #define SOMET 80 #define JUNKARRAY 81 #define STARTDISC 82 #define DISCARDING 83 /* output modes */ #define ALPHA 0 #define DRAW 1 #define MARK 3 #define TEMPDRAW 101 #define TEMPMOVE 102 #define TEMPMARK 103 /* stroked fonts */ #define CHARWIDE 51 /* total horz. size */ #define CHARTALL 76 /* total vert. size */ #define CHARH 10 /* horz. unit size */ #define CHARV 13 /* vert. unit size */ /* RG coordinate space dimensions */ #define RGXSIZE 4096 #define RGYSIZE 4096 typedef struct { int (*init)(), (*point)(), (*drawline)(), (*newwin)(), (*clrscr)(), (*pagedone)(), (*dataline)(), (*pencolor)(), (*charmode)(), (*showcur)(), (*lockcur)(), (*hidecur)(), (*close)(), (*uncover)(), (*info)(), (*gmode)(), (*tmode)(), (*gin)(); char *(*devname)(); int (*bell)(); } RGLINK; extern char *malloc(); int donothing(); struct VGWINTYPE { int RGdevice, RGnum; char mode,modesave; /* current output mode */ char loy,hiy,lox,hix,ex,ey; /* current graphics coordinates */ char nloy,nhiy,nlox,nhix,nex,ney; /* new coordinates */ int curx,cury; /* current composite coordinates */ int winbot,wintop,winleft,winright,wintall,winwide; /* position of window in virutal space */ int textcol; /* text starts in 0 or 2048 */ int intin; /* integer parameter being input */ int pencolor; /* current pen color */ int fontnum,charx,chary; /* char size */ int count; /* for temporary use in special state loops */ }; #include #include "vgtek.h" #include "vgfont.h" #include "tekstor.h" static struct VGWINTYPE VGwin[MAXVG]; /* virtual window descriptors */ static char state[MAXVG],savstate[MAXVG]; /* save state in a parallel array for speed */ static STOREP VGstore[MAXVG]; /* the store where data for this widow is kept */ static char storing[MAXVG]; /* are we currently saving data from this window */ static int drawing[MAXVG]; /* redrawing or not? */ #define NUMSIZES 6 /* number of char sizes */ static int charxset[NUMSIZES] = {56,51,34,31,112,168}; static int charyset[NUMSIZES] = {88,82,53,48,176,264}; /*******************************************************************/ static donothing() { } static fontnum(vw,n) int vw,n; /* Set font for window 'vw' to size 'n'. Sizes are 0..3 in Tek 4014 standard. Sizes 4 & 5 are used internally for Tek 4105 emulation. */ { if (n<0 || n>=NUMSIZES) return(-1); VGwin[vw].fontnum=n; VGwin[vw].charx=charxset[n]; VGwin[vw].chary=charyset[n]; return(0); } static storexy(vw,x,y) int vw,x,y; /* set graphics x and y position */ { VGwin[vw].curx = x; VGwin[vw].cury = y; } static joinup(hi,lo,e) int hi,lo,e; /* returns the number represented by the 3 pieces */ { return (((hi /* & 31 */ ) << 7) | ((lo /* & 31 */ ) << 2) | (e /* & 3 */)); } /* end joinup() */ static newcoord(vw) int vw; /* Replace x,y with nx,ny */ { VGwin[vw].hiy = VGwin[vw].nhiy; VGwin[vw].hix = VGwin[vw].nhix; VGwin[vw].loy = VGwin[vw].nloy; VGwin[vw].lox = VGwin[vw].nlox; VGwin[vw].ey = VGwin[vw].ney; VGwin[vw].ex = VGwin[vw].nex; VGwin[vw].curx = joinup(VGwin[vw].nhix,VGwin[vw].nlox,VGwin[vw].nex); VGwin[vw].cury = joinup(VGwin[vw].nhiy,VGwin[vw].nloy,VGwin[vw].ney); } static linefeed(vw) /* Perform a linefeed & cr (CHARTALL units) in specified window. */ { /* int y=joinup(VGwin[vw].hiy,VGwin[vw].loy,VGwin[vw].ey);*/ int y=VGwin[vw].cury; int x; if (y>VGwin[vw].chary) y -= VGwin[vw].chary; else { y= 3119 - VGwin[vw].chary; VGwin[vw].textcol = 2048 - VGwin[vw].textcol; } x = VGwin[vw].textcol; storexy(vw,x,y); } #ifdef DEBUG static drawc(vw,c) int vw; char c; /* character to draw */ { putchar(c); } #else static drawc(vw,c) int vw; char c; /* character to draw */ /* Draw a stroked character at the current cursor location. Uses simple 8-directional moving, 8-directional drawing. */ { int x,y,savex,savey,strokex,strokey; int n; /* number of times to perform command */ char *pstroke; /* pointer into stroke data */ int hmag,vmag; if (c==10) { linefeed(vw); return(0); } if (c==7) { (*RG[VGwin[vw].RGdevice].bell) (VGwin[vw].RGnum); unstore(VGstore[vw]); return(0); } savey=y=VGwin[vw].cury; savex=x=VGwin[vw].curx; if (c==8) { if (savex<=VGwin[vw].textcol) return(0); savex -= VGwin[vw].charx; if (savex < VGwin[vw].textcol) savex = VGwin[vw].textcol; VGwin[vw].cury=savey; VGwin[vw].curx=savex; return(0); } hmag = VGwin[vw].charx / 10; vmag = VGwin[vw].chary / 10; if (3119 - savey < VGwin[vw].chary) { savey=y= 3119 - VGwin[vw].chary; } if (c<32 || c>126) return(0); c -= 32; pstroke = VGfont[c]; while (*pstroke) { strokex=x; strokey=y; n = (*(pstroke++) - 48); /* run length */ c = *(pstroke++); /* direction code */ switch(c) { /* horizontal movement: positive = right */ case 'e': case 'd': case 'c': case 'y': case 'h': case 'n': x += n * hmag; break; case 'q': case 'a': case 'z': case 'r': case 'f': case 'v': x -= n * hmag; } switch(c) { /* vertical movement: positive = up */ case 'q': case 'w': case 'e': case 'r': case 't': case 'y': y += n * vmag; break; case 'z': case 'x': case 'c': case 'v': case 'b': case 'n': y -= n * vmag; } switch(c) { /* draw or move */ case 'r': case 't': case 'y': case 'f': case 'h': case 'v': case 'b': case 'n': clipvec (vw,strokex,strokey,x,y); break; } } /* end while not at end of string */ /* Update cursor location to next char position */ if (savex + 2 * VGwin[vw].chary <= 4096) savex += VGwin[vw].charx; else { savey -= VGwin[vw].chary; if (savey<0) { savey= 3119 - VGwin[vw].chary; VGwin[vw].textcol = 2048 - VGwin[vw].textcol; } savex = VGwin[vw].textcol; } VGwin[vw].cury=savey; VGwin[vw].curx=savex; } /* end drawc() */ #endif static clipt(p,q,t0,t1) float p,q,*t0,*t1; /* To be called only by clipvec() */ { float r; int accept = TRUE; if (p<0.0) { r = q/p; if (r>*t1) accept = FALSE; else if (r>*t0) *t0 = r; } else if (p>0.0) { r = q/p; if (r<*t0) accept = FALSE; else if (r<*t1) *t1 = r; } else if (q<0.0) accept = FALSE; return(accept); } #ifdef DEBUG clipvec(vw,xa,ya,xb,yb) int vw; int xa,ya,xb,yb; { printf("%d,%d to %d,%d\n",xa,ya,xb,yb); } #else static clipvec(vw,xa,ya,xb,yb) int vw; int xa,ya,xb,yb; /* Draw a vector in vw's window from x0,y0 to x1,y1. Zoom the vector to the current visible window, and clip it before drawing it. Uses Liang-Barsky algorithm from ACM Transactions on Graphics, Vol. 3, No. 1, January 1984, p. 7. */ { int t,b,l,r; float x0,y0,x1,y1,t0,t1,deltay,deltax; struct VGWINTYPE *vp; vp = &VGwin[vw]; t=vp->wintop; b=vp->winbot; l=vp->winleft; r=vp->winright; /* totally visible */ if (xa<=r && xb<=r && xa>=l && xb>=l && ya<=t && yb<=t && ya>=b && yb>=b) { (*RG[vp->RGdevice].drawline) (vp->RGnum, (int) ((long)(xa - l) * RGXSIZE / vp->winwide), (int) ((long)(ya- b) * RGYSIZE / vp->wintall), (int) ((long)(xb - l) * RGXSIZE / vp->winwide), (int) ((long)(yb- b) * RGYSIZE / vp->wintall)); return; } /* trivially invisible */ if ((xa>r && xb>r) || (xat && yb>t)) { return; } /* the clipping algorithm */ x0=(float) xa; y0=(float) ya; x1=(float) xb; y1=(float) yb; t0=0.0; t1=1.0; deltax = x1-x0; if(clipt(-deltax, x0 - l, &t0, &t1)) { if (clipt(deltax, r - x0, &t0, &t1)) { deltay = y1 - y0; if (clipt(-deltay, y0 - b, &t0, &t1)) { if (clipt(deltay, t - y0, &t0, &t1)) { if (t1<1.0) { x1 = x0 + t1*deltax; y1 = y0 + t1*deltay; } if (t0>0.0) { x0 += t0*deltax; y0 += t0*deltay; } /* draw the line, it is at least partially visible */ (*RG[vp->RGdevice].drawline) (vp->RGnum, (int) ((long)((int)x0 - l) * RGXSIZE / vp->winwide), (int) ((long)((int)y0- b) * RGYSIZE / vp->wintall), (int) ((long)((int)x1 - l) * RGXSIZE / vp->winwide), (int) ((long)((int)y1- b) * RGYSIZE / vp->wintall)); } } } } /* end if */ } /* end clipvec() */ #endif /******************************************************* ******************************************************** All routines given below may be called by the user program. No routines given above may be called from the user program. ******************************************************** *******************************************************/ VGinit() /* Initialize the whole VG environment. Should be called ONCE at program startup before using the VG routines. */ { int i; for (i=0; i> 5) & 0x03; value=c & 0x1f; goagain=FALSE; switch(state[vw]) { case HIY: /* beginning of a vector */ vp->nhiy = vp->hiy; vp->nhix = vp->hix; vp->nloy = vp->loy; vp->nlox = vp->lox; vp->ney = vp->ey; vp->nex = vp->ex; switch(cmd) { case 0: if (value == 27) { /* escape sequence */ state[vw] = ESCOUT; savstate[vw] = HIY; } else if (value < 27) { /* ignore */ break; } else { state[vw]=CANCEL; goagain=TRUE; } break; case 1: /* hiy */ vp->nhiy=value; state[vw]=EXTRA; break; case 2: /* lox */ vp->nlox=value; state[vw]=DONE; break; case 3: /* extra or loy */ vp->nloy=value; state[vw]=LOY; break; } break; case ESCOUT: if (value != 13 && value != 10 && value != 27 && value != '~') {/* skip all EOL-type characters */ state[vw] = savstate[vw]; goagain = TRUE; } break; case EXTRA: /* got hiy; expecting extra or loy */ switch(cmd) { case 0: if (value == 27) { /* escape sequence */ state[vw] = ESCOUT; savstate[vw] = EXTRA; } else if (value < 27) { /* ignore */ break; } else { state[vw]=DONE; goagain=TRUE; } break; case 1: /* hix */ vp->nhix=value; state[vw]=LOX; break; case 2: /* lox */ vp->nlox=value; state[vw]=DONE; break; case 3: /* extra or loy */ vp->nloy=value; state[vw]=LOY; break; } break; case LOY: /* got extra or loy; next may be loy or something else */ switch(cmd) { case 0: if (value == 27) { /* escape sequence */ state[vw] = ESCOUT; savstate[vw] = LOY; } else if (value < 27) { /* ignore */ break; } else { state[vw]=DONE; goagain=TRUE; } break; case 1: /* hix */ vp->nhix=value; state[vw]=LOX; break; case 2: /* lox */ vp->nlox=value; state[vw]=DONE; break; case 3: /* this is loy; previous loy was really extra */ vp->ney = (vp->nloy >> 2) & 3; vp->nex = (vp->nlox) & 3; vp->nloy=value; state[vw]=HIX; break; } break; case HIX: /* hix or lox */ switch(cmd) { case 0: if (value == 27) { /* escape sequence */ state[vw] = ESCOUT; savstate[vw] = HIX; } else if (value < 27) { /* ignore */ break; } else { state[vw]=DONE; goagain=TRUE; } break; case 1: /* hix */ vp->nhix=value; state[vw]=LOX; break; case 2: /* lox */ vp->nlox=value; state[vw]=DONE; break; } break; case LOX: /* must be lox */ switch(cmd) { case 0: if (value == 27) { /* escape sequence */ state[vw] = ESCOUT; savstate[vw] = LOX; } else if (value < 27) { /* ignore */ break; } else { state[vw]=DONE; goagain=TRUE; } break; case 2: vp->nlox=value; state[vw]=DONE; break; } break; case ENTERVEC: if (c==7) vp->mode = DRAW; if (c < 27) break; state[vw] = HIY; vp->mode = TEMPMOVE; vp->modesave = DRAW; goagain = TRUE; break; case RS: switch (c) { case ' ': /* pen up */ vp->modesave = vp->mode; vp->mode = TEMPMOVE; break; case 'P': /* pen down */ vp->mode = DRAW; break; case 'D': /* move up */ vp->cury++; break; case 'E': vp->cury++; vp->curx++; break; case 'A': vp->curx++; break; case 'I': vp->curx++; vp->cury--; break; case 'H': vp->cury--; break; case 'J': vp->curx--; vp->cury--; break; case 'B': vp->curx--; break; case 'F': vp->cury++; vp->curx--; break; case 27: savstate[vw] = RS; state[vw] = ESCOUT; break; default: /* storexy(vw,vp->curx,vp->cury);*/ state[vw] = CANCEL; goagain = TRUE; break; } if (vp->mode == DRAW) clipvec(vw,vp->curx,vp->cury,vp->curx,vp->cury); #ifdef DEBUG printf("RS: %d,%d\n",vp->curx,vp->cury); #endif break; case CMD0: /* get 1st letter of cmd */ switch(c) { case 29: /* GS, start draw */ state[vw] = DONE; goagain = TRUE; break; case '8': fontnum(vw,0); state[vw]=DONE; break; case '9': fontnum(vw,1); state[vw]=DONE; break; case ':': fontnum(vw,2); state[vw]=DONE; break; case ';': fontnum(vw,3); state[vw]=DONE; break; case 12: /* form feed = clrscr */ VGpage(vw); VGclrstor(vw); break; case 'L': state[vw] = SOMEL; break; case 'K': state[vw] = IGNORE; break; case 'M': state[vw] = SOMEM; break; case 'T': state[vw] = SOMET; break; case 26: (*RG[vp->RGdevice].gin)(vp->RGnum); unstore(VGstore[vw]); unstore(VGstore[vw]); break; case 10: case 13: case 27: case '~': savstate[vw] = DONE; state[vw] = ESCOUT; break; /* completely ignore these after ESC */ default: state[vw] = DONE; } /* end switch */ break; case SOMET: /* Got ESC T; now handle 3rd char. */ switch(c) { case 'G': /* set surface color map */ state[vw]=INTEGER; savstate[vw]=JUNKARRAY; break; case 'F': /* set dialog area color map */ state[vw]=JUNKARRAY; break; default: state[vw]=DONE; } break; case JUNKARRAY: /* This character is the beginning of an integer array to be discarded. Get array size. */ savstate[vw]=STARTDISC; state[vw]=INTEGER; break; case STARTDISC: /* Begin discarding integers. */ vp->count = vp->intin + 1; goagain=TRUE; state[vw]=DISCARDING; break; case DISCARDING: /* We are in the process of discarding an integer array. */ goagain=TRUE; if (!(--(vp->count))) state[vw]=DONE; else if (vp->count==1) { state[vw]=INTEGER; savstate[vw]=DONE; } else { state[vw]=INTEGER; savstate[vw]=DISCARDING; } break; case INTEGER: if (c & 0x40) { vp->intin = c & 0x3f; state[vw] = INTEGER1; } else { vp->intin = c & 0x0f; if (!(c & 0x10)) vp->intin *= -1; state[vw] = savstate[vw]; } break; case INTEGER1: if (c & 0x40) { vp->intin = (vp->intin << 6) | (c & 0x3f); state[vw] = INTEGER2; } else { vp->intin = (vp->intin << 4) | (c & 0x0f); if (!(c & 0x10)) vp->intin *= -1; state[vw] = savstate[vw]; } break; case INTEGER2: vp->intin = (vp->intin << 4) | (c & 0x0f); if (!(c & 0x10)) vp->intin *= -1; state[vw] = savstate[vw]; break; case IGNORE: /* ignore next char; it's not supported */ state[vw]=DONE; break; case IGNORE2: /* ignore next 2 chars */ state[vw]=IGNORE; break; case SOMEL: /* now process 2nd letter */ switch(c) { case 'F': /* move */ vp->modesave = vp->mode; vp->mode = TEMPMOVE; state[vw] = HIY; break; case 'G': /* draw */ vp->modesave = vp->mode; vp->mode = TEMPDRAW; state[vw] = HIY; break; case 'H': /* marker */ vp->modesave = vp->mode; vp->mode = TEMPMARK; state[vw] = HIY; break; default: state[vw]=DONE; } /* end switch */ break; case SOMEM: switch(c) { case 'C': /* set graphtext size */ savstate[vw]=GTSIZE0; state[vw]=INTEGER; break; case 'L': /* set line index */ savstate[vw]=COLORINT; state[vw]=INTEGER; break; default: state[vw]=DONE; } /* end switch */ break; case COLORINT: /* set line index; have integer */ vp->pencolor=vp->intin; (*RG[vp->RGdevice].pencolor) (vp->RGnum,vp->intin); state[vw]=CANCEL; goagain=TRUE; /* we ignored current char; now process it */ break; case GTSIZE0: /* discard the first integer; get the 2nd */ state[vw]=INTEGER; /* get the important middle integer */ savstate[vw]=GTSIZE1; goagain=TRUE; break; case GTSIZE1: /* integer is the height */ if (vp->intin < 88) fontnum(vw,0); else if (vp->intin < 149) fontnum(vw,4); else fontnum(vw,5); state[vw]=INTEGER; /* discard last integer */ savstate[vw]=DONE; goagain=TRUE; break; case DONE: /* ready for anything */ switch(c) { case 31: vp->mode=ALPHA; state[vw]=CANCEL; break; case 30: state[vw]=RS; break; case 28: if (cmd) { vp->mode=MARK; state[vw]=HIY; } break; case 29: state[vw]=ENTERVEC; break; case 27: state[vw]=CMD0; break; default: if (vp->mode==ALPHA) { drawc(vw,c); state[vw]=CANCEL; } else if (vp->mode==DRAW && cmd) { state[vw]=HIY; goagain=TRUE; } else if (vp->mode==MARK && cmd) { state[vw]=HIY; goagain=TRUE; } else if (vp->mode == DRAW && (c == 13 || c == 10)) { /* break drawing mode on CRLF */ vp->mode=ALPHA; state[vw]=CANCEL; } else state[vw] = CANCEL; /* do nothing */ } /* end switch(c) */ } /* end switch(state) */ if (state[vw]==DONE) { if (vp->mode==TEMPMOVE) { vp->mode=vp->modesave; newcoord(vw); } else if (vp->mode==DRAW || vp->mode==TEMPDRAW) { clipvec(vw,vp->curx,vp->cury, joinup(vp->nhix,vp->nlox,vp->nex), joinup(vp->nhiy,vp->nloy,vp->ney)); if (vp->mode==TEMPDRAW) vp->mode=vp->modesave; newcoord(vw); } else if (vp->mode==MARK || vp->mode==TEMPMARK) { /* draw marker */ newcoord(vw); } } /* end if done */ if (state[vw]==CANCEL) state[vw]=DONE; } while (goagain); /*** END OF MAIN LOOP ***/ } /* end VGdraw() */ VGpage(vw) int vw; /* Clear screen and have a few other effects: - Return graphics to home position (0,3071) - Switch to alpha mode This is a standard Tek command; don't look at me. */ { #ifndef DEBUG (*RG[VGwin[vw].RGdevice].clrscr)(VGwin[vw].RGnum); (*RG[VGwin[vw].RGdevice].pencolor)(VGwin[vw].RGnum,1); #endif VGwin[vw].mode=ALPHA; state[vw] = DONE; VGwin[vw].textcol=0; fontnum(vw,0); storexy(vw,0,3071); } /* end page */ VGpred(vw,dest) int vw,dest; /* Redraw window 'vw' in pieces to window 'dest'. Must call this function repeatedly to draw whole image. Only draws part of the image at a time, to yield CPU power. Returns 0 if needs to be called more, or 1 if the image is complete. Another call would result in the redraw beginning again. User should clear screen before beginning redraw. */ { int data; STOREP st=VGstore[vw]; int count=0; if (drawing[vw]) { /* wasn't redrawing */ topstore(st); drawing[vw] = 0; /* redraw incomplete */ } while ( ++count < PREDCOUNT && ((data=nextitem(st)) != -1) ) { VGdraw(dest,data); } if (data == -1) drawing[vw] = 1; /* redraw complete */ return(drawing[vw]); } VGstopred(vw,dest) /* Abort VGpred redrawing of specified window. Must call this routine if you decide not to complete the redraw. */ { drawing[vw]=1; } VGredraw(vw,dest) int vw,dest; /* Redraw the contents of window 'vw' to window 'dest'. Does not yield CPU until done. User should clear the screen before calling this, to avoid a messy display. */ { int data; STOREP st=VGstore[vw]; topstore(st); while ((data=nextitem(st)) != -1) { VGdraw(dest,data); } } VGzoom(vw,x0,y0,x1,y1) /* Set new borders for zoom/pan region. x0,y0 is lower left; x1,y1 is upper right. User should redraw after calling this. */ { VGwin[vw].winbot=y0; VGwin[vw].winleft=x0; VGwin[vw].wintop=y1; VGwin[vw].winright=x1; VGwin[vw].wintall = y1-y0+1; VGwin[vw].winwide = x1-x0+1; VGgiveinfo(vw); } VGwhatzoom(vw,px0,py0,px1,py1) int vw,*px0,*py0,*px1,*py1; { *py0 = VGwin[vw].winbot; *px0 = VGwin[vw].winleft; *py1 = VGwin[vw].wintop; *px1 = VGwin[vw].winright; } VGzcpy(src,dest) /* Set zoom/pan borders for window 'dest' equal to those for window 'src'. User should redraw window 'dest' after calling this. */ { VGzoom(dest,VGwin[src].winleft, VGwin[src].winbot, VGwin[src].winright, VGwin[src].wintop); } VGclose(vw) /* Close virtual window. Release its real graphics device and its store. */ { (*RG[VGwin[vw].RGdevice].close)(VGwin[vw].RGnum); freestore(VGstore[vw]); VGwin[vw].RGdevice = -1; } VGwrite(vw,data,count) int vw, count; char *data; /* Draw the data pointed to by 'data' of length 'count' on window vw, and add it to the store for that window. This is THE way for user program to pass Tektronix data. */ { char *c = data; char *end = &(data[count]); if (VGwin[vw].RGdevice == -1 || vw >= MAXVG || vw < 0) return(-1); /* window not open */ if (storing[vw]) { while (c != end) { if (*c == 24) /* ASC CAN character */ return(c-data+1); addstore(VGstore[vw], *c); VGdraw(vw,*c++); } } else { while (c != end) { if (*c == 24) return(c-data+1); else VGdraw(vw,*c++); } } return(count); } VGgiveinfo(vw) int vw; /* Send interesting information about the virtual window down to its RG, so that the RG can make VG calls and display zoom values */ { (*RG[VGwin[vw].RGdevice].info)(VGwin[vw].RGnum, vw, VGwin[vw].winbot, VGwin[vw].winleft, VGwin[vw].wintop, VGwin[vw].winright); } char * VGrgname(rgdev) int rgdev; /* Return a pointer to a human-readable string which describes the specified real device */ { return(*RG[rgdev].devname)(); } VGgmode(rgdev) int rgdev; /* Put the specified real device into graphics mode */ { #ifndef DEBUG (*RG[rgdev].gmode)(); #endif } VGtmode(rgdev) int rgdev; /* Put the specified real device into text mode */ { (*RG[rgdev].tmode)(); } VGgindata(vw,x,y,c,a) int vw,x,y; char c; char *a; /* Translate data for output as GIN report. User indicates VW number and x,y coordinates of the GIN cursor. Coordinate space is 0-4095, 0-4095 with 0,0 at the bottom left of the real window and 4095,4095 at the upper right of the real window. 'c' is the character to be returned as the keypress. 'a' is a pointer to an array of 5 characters. The 5 chars must be transmitted by the user to the remote host as the GIN report. */ { long x2,y2; x2 = ((x * VGwin[vw].winwide) / RGXSIZE + VGwin[vw].winleft) >> 2; y2 = ((y * VGwin[vw].wintall) / RGYSIZE + VGwin[vw].winbot) >> 2; a[0] = c; a[1] = 0x20 | ((x2 & 0x03E0) >> 5); a[2] = 0x20 | (x2 & 0x001F); a[3] = 0x20 | ((y2 & 0x03E0) >> 5); a[4] = 0x20 | (y2 & 0x001F); } x0f; if (!(c & 0x10)) vp->intin *= -1; state[vw] = savstatesource/tek/rgep.c 644 144 13 14011 4267454521 7314 /* rgep.c by Aaron Contorer for NCSA graphics routines for drawing on Epson printer Input coordinate space = 0..4095 by 0..4095 MUST BE COMPILED WITH LARGE MEMORY MODEL! */ #include /* used for debugging only */ #define TRUE 1 #define FALSE 0 #define INXMAX 4096 #define INYMAX 4096 #define SCRNXHI 719 #define ROWS 90 #define SCRNYHI 929 #define LINEBYTE 930 #define MAXRW 1 /* max. number of real windows */ extern char *malloc(); static int EPSactive; /* number of currently active window */ static char EPSpbit[SCRNXHI]; static unsigned char power2[8] = {1,2,4,8,16,32,64,128}; static char *EPSname = "Epson, IBM, or compatible printer"; static int EPSxmax=720; static int EPSymax=LINEBYTE; static int EPSbytes=80; /* number of bytes per line */ static unsigned char *EPSram[ROWS]; static int (*EPSoutfunc)(); /* Current status of an EPS window */ struct EPSWIN { char inuse; /* true if window in use */ int pencolor, rotation, size; int winbot,winleft,wintall,winwide; /* position of the window in virtual space */ }; static struct EPSWIN EPSwins[MAXRW]; EPSsetup() /* prepare variables for use in other functions */ { int x; EPSpbit[0]=128; EPSpbit[1]=64; EPSpbit[2]=32; EPSpbit[3]=16; EPSpbit[4]=8; EPSpbit[5]=4; EPSpbit[6]=2; EPSpbit[7]=1; for (x=8; x<=SCRNXHI; x++) EPSpbit[x]=EPSpbit[x & 7]; } RGEPgmode() /* go into EPS graphics mode */ { } RGEPtmode() /* go into EPS text mode */ { } RGEPclrscr(w) int w; /* Clear the screen. */ { if (w == EPSactive) { EPSsetup(); } } int RGEPnewwin() /* Set up a new window; return its number. Returns -1 if cannot create window. */ { int w=0; int x,y; for (x=0; x=0) return(x); else return(-x); } EPSpoint(x,y) /* Set bit at x,y */ { (EPSram[x>>3]) [y] |= EPSpbit[x]; } RGEPdrawline(w,x0,y0,x1,y1) int w,x0,y0,x1,y1; /* draw a line from (x0,y0) to (x1,y1) */ /* uses Bresenham's Line Algorithm */ { int x,y,dx,dy,d,temp, dx2,dy2, /* 2dx and 2dy */ direction; /* +1 or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish line */ if (w != EPSactive) return; x0 = (int) ((long) x0 * EPSxmax / INXMAX); y0 = (int) ((long) y0 * EPSymax / INYMAX); x1 = (int) ((long) x1 * EPSxmax / INXMAX); y1 = (int) ((long) y1 * EPSymax / INYMAX); if (abs(y1-y0) > abs(x1-x0)) { /* transpose vertical-ish to horizontal-ish */ temp=x1; x1=y1; y1=temp; temp=x0; x0=y0; y0=temp; transpose=TRUE; } else transpose=FALSE; /* make sure line is left to right */ if (x1 < x0) { temp=x1; x1=x0; x0=temp; temp=y1; y1=y0; y0=temp; } /* SPECIAL CASE: 1 POINT */ if (x1==x0 && y1==y0) { EPSpoint(x1,y1); return(0); } /* ANY LINE > 1 POINT */ x=x0; y=y0; dx=x1-x0; if (y1 >= y0) { dy=y1-y0; direction=1; } else { dy=y0-y1; direction= -1; } dx2=dx<<1; dy2=dy<<1; d = (dy<<1) - dx; if (transpose) { /* CASE OF VERTICALISH (TRANSPOSED) LINES */ while (x <= x1) { if (y>=0 && y=0 && x= 0) { y += direction; d -= dx2; } d += dy2; x++; } } else { /* CASE OF HORIZONTALISH LINES */ while (x <= x1) { if (x>=0 && x=0 && y= 0) { y += direction; d -= dx2; } d += dy2; x++; } } /* end horizontalish */ } /* end RGEPdrawline() */ RGEPbell(w) int w; /* Ring bell in window w */ { } char *RGEPdevname() /* return name of device that this RG supports */ { return(EPSname); } static signore(s) char *s; /* Ignore the string pointer passed here. */ { } RGEPinit() /* initialize all RGEP variables */ { int i; EPSsetup(); for (i=0; i>8); /* send a line of bit image data */ for (y=0; y frame push ds ; save ds push es ; save es ; mov ax,seg EGAset_data ; ax -> data segment ; mov ds,ax ; ds -> data segment mov ax,seg E_refresh ; ax -> refresh segment mov es,ax ; es -> refresh segment mov dx,E_graph_ad ; dx -> graphics address register mov al,E_bit_mask_i ; bit mask index -> al out dx,al ; select bit mask register mov dx,E_bit_mask ; dx -> bit mask register mov cx,[bp+x] ; x -> cx and cx,7 ; keep bit number in byte mov al,80h ; 1 -> al shr al,cl ; shift mask bit to right bit out dx,al ; set mask mov dx,E_seq_ad ; dx -> sequencer address register mov al,E_map_mask_i ; Map mask index -> al out dx,al ; select map mask mov dx,E_map_mask ; dx -> map mask register mov al,0fh ; mask all planes out dx,al ; write enable all planes mov bx,[bp+x] ; x -> bx shr bx,1 ; / 2 shr bx,1 ; / 2 shr bx,1 ; / 2 mov ax,[bp+y] ; y -> ax mul k80 ; compute row address add bx,ax ; byte address -> bx mov al,es:[bx] ; latch byte to get other bits mov al,0 ; 0 -> al mov es:[bx],al ; clear all planes of right bit mov dx,E_seq_ad ; dx -> sequencer address register mov al,E_map_mask_i ; Map mask index -> al out dx,al ; select map mask mov dx,E_map_mask ; dx -> map mask register mov al,[bp+color] ; color -> al out dx,al ; write enable planes for right color mov al,es:[bx] ; latch byte to get other bits mov al,0ffh ; 1 in right bit -> al mov es:[bx],al ; set right bit in all color planes mov dx,E_seq_ad ; dx -> sequencer address register mov al,E_map_mask_i ; Map mask index -> al out dx,al ; select map mask mov dx,E_map_mask ; dx -> map mask register mov al,0fh ; mask all planes out dx,al ; write enable all planes mov dx,E_graph_ad ; dx -> graphics address register mov al,E_bit_mask_i ; bit mask index -> al out dx,al ; select bit mask register mov dx,E_bit_mask ; dx -> bit mask register mov al,0ffh ; ff -> al out dx,al ; set mask to all bits pop es ; restore es pop ds ; restore ds pop bp ; restore bp ret ; return ifdef Microsoft _EGAset endp EGAset_text ends else EGAset endp ENDPS endif end or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish linsource/tek/dos.mac 644 144 13 6025 4267454567 7462 .XLIST PAGE 58,132 ;** ; ; This macro library defines the operating environment for the 8086 L ; memory model, which allows 1M bytes of data and 1M bytes of program. ; ;** MSDOS EQU 2 ;** ; ; The following symbols define the 8086 memory mode being used. Set LPROG ; to 1 for a large program segment (greater than 64K-bytes), and set LDATA ; to 1 for a large data segment. Set COM to 1 to generate .COM files ; instead of .EXE files. Note that if COM is not zero, then LPROG and ; LDATA must be 0. ; ;** COM EQU 0 LPROG EQU 1 LDATA EQU 1 ;** ; ; The following symbols are established via LPROG and LDATA as follows: ; ; S8086 set for small model (small prog, small data) ; D8086 set for model with large data, small prog ; P8086 set for model with large prog, small data ; L8086 set for large model ; ;** IF (LPROG EQ 0) AND (LDATA EQ 0) S8086 EQU 1 D8086 EQU 0 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG EQ 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 1 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA EQ 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 1 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 0 L8086 EQU 1 ENDIF ;** ; ; The DSEG and PSEG macros are defined to generate the appropriate GROUP ; and SEGMENT statements for the memory model being used. The ENDDS and ; ENDPS macros are then used to end the segments. ; ;** DSEG MACRO DGROUP GROUP DATA DATA SEGMENT WORD PUBLIC 'DATA' ASSUME DS:DGROUP ENDM ENDDS MACRO DATA ENDS ENDM IF S8086 PSEG MACRO PGROUP GROUP PROG PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:PGROUP ENDM ENDPS MACRO PROG ENDS ENDM ENDIF IF D8086 PSEG MACRO CGROUP GROUP CODE CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CGROUP ENDM ENDPS MACRO CODE ENDS ENDM ENDIF IF P8086 PSEG MACRO _CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:_CODE ENDM ENDPS MACRO _CODE ENDS ENDM ENDIF IF L8086 PSEG MACRO _PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:_PROG ENDM ENDPS MACRO _PROG ENDS ENDM ENDIF ;** ; ; The BEGIN and ENTRY macros establish appropriate function entry points ; depending on whether NEAR or FAR program addressing is being used. The ; only difference between the two is that BEGIN generates a PROC operation ; to start a segment. ; BEGIN MACRO NAME ; begin a function PUBLIC NAME IF LPROG NAME PROC FAR ELSE NAME PROC NEAR ENDIF ENDM ENTRY MACRO NAME PUBLIC NAME IF LPROG NAME LABEL FAR ELSE NAME LABEL NEAR ENDIF ENDM ;** ; ; The following symbols are defined to help set up a STRUC defining the ; stack frame: ; ; CPSIZE -> code pointer size (2 or 4) ; DPSIZE -> data pointer size (2 or 4) ; ; These wouldn't be necessary if it were possible to use macros or even ; conditionals within a STRUC. ; IF LPROG CPSIZE EQU 4 ELSE CPSIZE EQU 2 ENDIF IF LDATA DPSIZE EQU 4 ELSE DPSIZE EQU 2 ENDIF ; ; The SETX macro sets the symbol X to 4 if LPROG is 0 or to 6 otherwise. ; X can then be used to skip past the BP and return address save area ; in the stack frame when accessing the function arguments. ; SETX MACRO IF LPROG X EQU 6 ELSE X EQU 4 ENDIF ENDM .LIST ad ; dx -> graphics address register mov al,E_bit_mask_i ; bit mask index -> al out dx,al ; select bit mask register mov dx,E_bit_mask ; dx -> bit mask register mov al,0ffh ; ff -> al out dx,al ; set mask to all bits pop es ; restore es pop ds ; restore ds pop bp ; restore bp ret ; return ifdef Microsoft _EGAset endp EGAset_text ends else EGAset endp ENDPS endif end or -1, used for slope */ char transpose; /* true if switching x and y for vertical-ish linsource/enet/net501.asm 644 144 13 55510 4267454605 10115 ; 3COM 3C501 driver code ; Tim Krauskopf ; ; Thanks to Bruce Orchard for mods to allow more I/O addresses and INTs ; 5/18/88 ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;**************************************************************************** ; TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for interrupt-driven Ethernet I/O on the PC ; ; Will read and write packets from the 2K packet buffer on the ; Etherlink card. Provides hooks for higher layer protocols. ; NAME NET INCLUDE DOS.MAC SETX ; ; Equates for controlling the 3COM board ; ICTRL EQU 020H ; 8259 interrupt control register IMASK EQU 021H ; 8259 interrupt mask register ENDOFI EQU 020H ; end-of-interrupt ; ; Controller registers ; O_EADDR EQU 0H ; Network address for hardware checking ; takes six bytes, this is the address that the Ethernet board will ; match to find packets on the net. (0-5h) ; O_EREC EQU 6H ; Receive status (read) ; Receive command (write) O_ESEND EQU 7H ; Transmit status (read) ; Transmit command (write) O_EGLOW EQU 8H ; General purpose pointer for R/W to ; packet buffer, low byte O_EGHI EQU 9H ; high byte, total of 11 bits for 2K buffer O_ERLOW EQU 0AH ; Receive pointer, set by board (read) low byte ; Receive buffer pointer clear (write) O_ERHI EQU 0BH ; high byte of Receive pointer O_EPROM EQU 0CH ; PROM window address O_XXX EQU 0DH ; ?? O_EAUX EQU 0EH ; Auxiliary Status (read) ; Aux Command (write) O_EBUF EQU 0FH ; Buffer window (where to I/O to net) ; ; Transmit command options ; what conditions we wish to be interrupted on after transmit ; EDTUNDER EQU 01H ; Detect underflow (bad CRC), not used EDTCOLL EQU 02H ; Detect collision on xmit EDTC16 EQU 04H ; Detect 16 consecutive collisions (net down) EDTOK EQU 08H ; Detect successful transmission ; other 4 bits unused in this reg EXMITNORM EQU 00H ; Normal use for interrupt-driven XMIT ; ; Transmit status results ; ; Use same values as commands, 08h means OK to xmit again ; ;***************************** ; Receive Command Options ; ; If something is not detected for, the receiver automatically discards ; those packets. ; EDTOVER EQU 01H ; Detect Overflow condition EDTFCS EQU 02H ; Detect FCS error, bad CRC on packet EDTDRB EQU 04H ; Detect dribble errors and accept them EDTSHORT EQU 08H ; Detect short frames (< 60 bytes) EDTEOF EQU 10H ; Detect no overflow (end-of-frame found) EGOOD EQU 20H ; Accept good frames ; four values legal for the last two bits: ECLOSE EQU 00H ; Turn off receiver EGETALL EQU 40H ; Get all packets, no matter what address EBROAD EQU 80H ; get those for me or for broadcast EMULTI EQU 0C0H ; get those for me or for multicast EWANT EQU 0A0h ; EGOOD OR EBROAD ; which packets we will look for on net ; ; Receive Status results ; ; errors are not detected unless asked for...otherwise the board just ; won't take bad packets off of the net. ; ERROVER EQU 01H ; overflow error ERRFCS EQU 02H ; FCS (checksum) error ERRDRB EQU 04H ; Dribble error ERRSHORT EQU 08H ; Short frame error ENOOVER EQU 10H ; Received without overflow error ; means that we didn't miss any by being slow ;EGOOD EQU 20H ; as above, we received a valid frame ; undefined 40h ESTALE EQU 80H ; stale receive condition, already read me ; ; Aux command register ; EIRE EQU 01H ; interrupt request enable (no DMA) new boards EBADFCS EQU 02H ; create bad checksum for testing only ; ; Next two bits tell who has access to packet buffer ; EBUS EQU 00H ; System bus has control of buffer EXMIT EQU 04H ; Transmit packet in buffer, auto kick ; back to recieve status EGETEM EQU 08H ; Receive state, look for packets ELOOP EQU 0CH ; Transmit and loopback into xmit buffer ; 10H unused EDMA EQU 20H ; Starts a DMA xfer ERIDE EQU 40H ; Interrupt and DMA enable ERESET EQU 80H ; Reset the Ethernet board ; ; Aux status register ; ERBUSY EQU 01H ; Receive busy, receiver looking for packets ; 02H ; echos command status EBADFCS ; 04,08h ; echos command status for EXMIT,EGETEM,EBUS EDMADONE EQU 10H ; goes to one when DMA finishes ; 20H ; echos DMA request bit ; 40h ; echos RIDE bit EXBUSY EQU 80H ; for polled xmit, goes to 0 when xmit is done ; ; ; Macros for in and out ; MOUT MACRO REG,STUFF ; one byte to the given I/O register MOV DX,REG MOV AL,STUFF OUT DX,AL ENDM ; MOUTW MACRO REG,LO,HI ; two bytes to the I/O double port MOV DX,REG MOV AL,LO OUT DX,AL INC DX MOV AL,HI OUT DX,AL ENDM ; MIN MACRO REG ; get one byte to al MOV DX,REG IN AL,DX ENDM DSEG ; PUBLIC STAT,BUFPT,BUFORG,BUFEND,BUFREAD,BUFBIG,BUFLIM,OFFS ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have ; ; BASEA DW ? ; Base I/O address on PC I/O bus EADDR DW ? ; Network address for hardware checking ; takes six bytes, this is the address that the Ethernet board will ; match to find packets on the net. (0-5h) ; EREC DW ? ; Receive status (read) ; Receive command (write) ESEND DW ? ; Transmit status (read) ; Transmit command (write) EGLOW DW ? ; General purpose pointer for R/W to ; packet buffer, low byte EGHI DW ? ; high byte, total of 11 bits for 2K buffer ERLOW DW ? ; Receive pointer, set by board (read) low byte ; Receive buffer pointer clear (write) ERHI DW ? ; high byte of Receive pointer EPROM DW ? ; PROM window address EAUX DW ? ; Auxiliary Status (read) ; Aux Command (write) EBUF DW ? ; Buffer window (where to I/O to net) SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H OLDMASK DB 00H ; save interrupt controller mask DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off ; ; use variables to access IRQ3 or IRQ5 ; 3 is COM2, 5 is LPT2 ; CINTNUM db 3 INTNUM DB 0BH ; Defaults to IRQ3, interrupt handler 0bh WHICHINT DW 4*0BH ; ETOPEN can change these values TURNOFF DB 08H TURNON DB 0F7H ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC E1RECV,E1ETOPEN,E1ETCLOS,E1GETADD PUBLIC E1SETADD,E1XMIT,E1ETUPDA ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,addr,ioaddr) ; char s[6]; ethernet address ; int irq,addr,ioaddr; ; interrupt number, base mem address (unused) and ; i/o address to use ; ; E1ETOPEN PROC FAR PUSH BP MOV BP,SP ; ; set up all of the I/O register values in memory ; MOV AX,[BP+X+8] ; i/o address -> ax MOV BASEA,AX ; save base address MOV BX,AX ; Base address -> BX ADD BX,O_EADDR ; + offset of Ethernet address MOV EADDR,BX ; store address of Ethernet address MOV BX,AX ; Base address -> BX ADD BX,O_EREC ; + offset of receive command/status MOV EREC,BX ; store address of receive command status MOV BX,AX ; Base address -> BX ADD BX,O_ESEND ; + offset of transmit command/status MOV ESEND,BX ; store address of transmit command status MOV BX,AX ; Base address -> BX ADD BX,O_EGLOW ; + offset of general pointer low byte MOV EGLOW,BX ; store address of general pointer low byte MOV BX,AX ; Base address -> BX ADD BX,O_EGHI ; + offset of general pointer high byte MOV EGHI,BX ; store address of general pointer high byte MOV BX,AX ; Base address -> BX ADD BX,O_ERLOW ; + offset of receive pointer low byte MOV ERLOW,BX ; store address of receive pointer low byte MOV BX,AX ; Base address -> BX ADD BX,O_ERHI ; + offset of receive pointer high byte MOV ERHI,BX ; store address of receive pointer high byte MOV BX,AX ; Base address -> BX ADD BX,O_EPROM ; + offset of PROM window MOV EPROM,BX ; store address of PROM window MOV BX,AX ; Base address -> BX ADD BX,O_EAUX ; + offset of auxiliary command/status MOV EAUX,BX ; store address of auxiliary command/status MOV BX,AX ; Base address -> BX ADD BX,O_EBUF ; + offset of buffer window MOV EBUF,BX ; store address of buffer window ; ; check the parameters for interrupt and dma ; MOV AX,[BP+X+4] ; interrupt number or ax,ax JG IOK1 ; if 0 or negative, use 3 mov ax,3 IOK1: CMP AX,7 ; too big? JNG IOK2 ; yes: use 3 mov ax,3 IOK2: MOV CINTNUM,AL ; save 8259 interrupt number ADD AX,8 ; convert to 8086 vector number MOV INTNUM,AL ; save vector number SHL AX,1 ; * 2 SHL AX,1 ; * 2 = vector address MOV WHICHINT,AX ; save vector address MOV CL,CINTNUM ; interrupt number -> cl MOV AX,1 ; 1 -> ax SHL AX,CL ; make interrupt mask MOV TURNOFF,AL ; store interrupt disable mask NOT AX ; make enable mask MOV TURNON,AL ; store interrupt enable mask ; ; DMA not used for 3C501 ; MOUT EAUX,ERESET ; reset the board MOUT EAUX,0 ; Clear the reset bit, otherwise keeps resetting ; ; install the interrupt handler ; CALL IINST ; do the patching of the interrupt table ; ; set up the net address ; MOV DX,EADDR ; get base i/o reg for setting address PUSH DS ; save mine MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; MOV CX,6 CLD SADDR: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR ; do 6 times POP DS ; get back DS of local data ; ; enable interrupts here with interrupt handler ; already set up. ; MOUT ESEND,0 ; xmit command = 0 for no interrupts IN AL,DX MOUT EREC,EWANT ; Set receiver for which packets we want IN AL,DX ; reset 'stale' MOUT ERLOW,0 ; Clear the receive buffer pointer CLI MOUT EAUX,EGETEM+ERIDE ; Set for receive, interrupts MIN IMASK ; get current int enable mask MOV BL,AL ; save a copy AND AL,TURNON ; force bit for etherlink board off OUT DX,AL ; put back the byte, IRQ enabled STI AND BL,TURNOFF ; isolate this bit only from oldmask MOV OLDMASK,BL ; save it ; POP BP XOR AX,AX RET E1ETOPEN ENDP ; ;****************************************************************** ; SETADDR ; set the Ethernet address on the board to 6 byte ID code ; ; usage: setaddr(s,basea,ioa); ; char s[6]; ethernet address to use ; int basea; shared memory base address (unused) ; int ioa; io address for board (unused) ; E1SETADD PROC FAR ret ; PUSH BP ; MOV BP,SP ; PUSH DS ; MOV AX,[BP+X+2] ; MOV DS,AX ; MOV SI,[BP+X] ; address of buffer to read ; ; MOV CX,6 ; MOV DX,EADDR ; get base i/o reg for setting address ; CLD SADDR2: ; LODSB ; get next one ; OUT DX,AL ; send it ; INC DX ; next position ; LOOP SADDR2 ; do 6 times ; ; POP DS ; POP BP ; RET E1SETADD ENDP ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; int address; ; int ioaddr; (unused here) mem address and ioaddress to use ; E1GETADD PROC FAR PUSH BP MOV BP,SP PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready ; ; ; set up all of the I/O register values in memory ; MOV AX,[BP+X+6] ; i/o address -> ax MOV BASEA,AX ; save base address MOV BX,AX ; Base address -> BX ADD BX,O_EADDR ; + offset of Ethernet address MOV EADDR,BX ; store address of Ethernet address MOV BX,AX ; Base address -> BX ADD BX,O_EREC ; + offset of receive command/status MOV EREC,BX ; store address of receive command status MOV BX,AX ; Base address -> BX ADD BX,O_ESEND ; + offset of transmit command/status MOV ESEND,BX ; store address of transmit command status MOV BX,AX ; Base address -> BX ADD BX,O_EGLOW ; + offset of general pointer low byte MOV EGLOW,BX ; store address of general pointer low byte MOV BX,AX ; Base address -> BX ADD BX,O_EGHI ; + offset of general pointer high byte MOV EGHI,BX ; store address of general pointer high byte MOV BX,AX ; Base address -> BX ADD BX,O_ERLOW ; + offset of receive pointer low byte MOV ERLOW,BX ; store address of receive pointer low byte MOV BX,AX ; Base address -> BX ADD BX,O_ERHI ; + offset of receive pointer high byte MOV ERHI,BX ; store address of receive pointer high byte MOV BX,AX ; Base address -> BX ADD BX,O_EPROM ; + offset of PROM window MOV EPROM,BX ; store address of PROM window MOV BX,AX ; Base address -> BX ADD BX,O_EAUX ; + offset of auxiliary command/status MOV EAUX,BX ; store address of auxiliary command/status MOV BX,AX ; Base address -> BX ADD BX,O_EBUF ; + offset of buffer window MOV EBUF,BX ; store address of buffer window ; ; MOV BX,0 ; start location MOV CX,EPROM ; address window GADDR: CLD MOUTW EGLOW,BL,BH ; set gp to the right value MIN CX ; get value from prom address window STOSB ; put into given buffer INC BX ; next position CMP BX,6 JNZ GADDR ; do 6 times POP ES POP BP xor ax,ax RET E1GETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down, remove the interrupt handler ; ; usage: etclose(); ; ; E1ETCLOS PROC FAR CLI MOUT EAUX,ERESET ; Turn off all pendings, cause reset MOUT EAUX,0 ; Turn off reset ; ; ; mask out IRQ on interrupt controller ; MIN IMASK ; get current mask OR AL,TURNOFF ; force that bit on OUT DX,AL ; send it back to controller STI CALL DEINST ; restore old interrupt handler MOV BL,OLDMASK ; get back saved setting of irq NOT BL ; flip it CLI MIN IMASK AND AL,BL ; restore setting of that bit OUT DX,AL STI xor ax,ax RET E1ETCLOS ENDP ; ;************************************************************************ ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; The 3COM 3C501 version uses interrupts, so this routine is a NOP ; for this board. ; ; usage: recv(); ; E1RECV PROC FAR RET ; for compatibility with other drivers E1RECV ENDP ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; E1XMIT PROC FAR PUSH BP MOV BP,SP MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes MOV CX,AX ; save a copy, might be less than 60, ok CMP AX,60 ; minimum length for Ether JNB OKLEN MOV AX,60 ; make sure size at least 60 OKLEN: MOV BX,2048 ; total length of buffer SUB BX,AX ; offset of for buffer pointer to start MOV DI,BX ; save a copy of the buffer pointer ; ; TAKE CONTROL OF THE INPUT BUFFER ; MOUT EAUX,EBUS+ERIDE ; take buffer away from receiver MOUT ERLOW,0 ; clear receive pointer for next read MOUTW EGLOW,BL,BH ; set the general purpose pointer MOV DX,EBUF ; window to packet buffer PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX CLD FILLBUF: LODSB ; get value to go into buffer OUT DX,AL ; put it into buffer (autoincrement) LOOP FILLBUF ; do whole count POP DS ; ; packet is in buffer, ready to be sent ; TRYAGAIN: MOV BX,DI ; retrieve copy of offset pointer MOUTW EGLOW,BL,BH ; set the general purpose pointer (again) ; MOUT EAUX,EXMIT+ERIDE ; tell the board to send it and start receiving NOTDONEX: MIN EAUX ; waiting for transmit to finish AND AL,EXBUSY ; is it done yet? JNZ NOTDONEX ; no, wait some more MOV CX,0 ; return value, ok MIN ESEND ; get xmit status MOV BL,AL ; save status AND AL,EDTOK ; was it ok? JNZ DONEX ; yes, successful xmit ; ; handle the possible errors, return 1 on coll16 ; coll16 generally means that the network has failed ; MOV AL,BL ; get copy of status back AND AL,EDTC16 ; check collision 16 JNZ RET16 ; yes, network probably down MOV AL,BL ; get copy back again AND AL,EDTCOLL ; check for collision status JZ UNK ; no, unknown problem MOUT EAUX,EBUS+ERIDE ; collision, reset buffer control JMP TRYAGAIN ; go for it UNK: MOV CX,2 ; unknown problem return code JMP DONEX RET16: MOV CX,1 ; failure return DONEX: MOUT EREC,EWANT ; reset receive register filter necessary MIN EAUX AND AL,ERBUSY ; is it still in receive state or done? JNZ DONEMIT ; not ready now, return instead MOV AL,INTNUM CMP AL,0BH ; two choices of int to call JNZ TRYNINT INT 0BH ; we do have a packet, read it JMP DONEMIT TRYNINT: CMP AL,0DH JNZ DONEMIT INT 0DH DONEMIT: MOV AX,CX ; put return in ax POP BP RET E1XMIT ENDP ; ;************************************************************************* ; Interrupt Handler ; installation and deinstallation ; ; the handler takes the receive packet out of the input buffer ; DEINST PROC NEAR MOV CX,SAVEIP ; get old ip from save spot MOV DX,SAVECS ; get old cs from save spot MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX CLI MOV [BX],CX ; store old ip into the table INC BX INC BX ; move pointer in interrupt table MOV [BX],DX ; store old cs into the table STI POP DS RET DEINST ENDP ; IINST PROC NEAR MOV CS:MYDS,DS ; store for use by handler MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX MOV AX,OFFSET IHAND ; where the handler is CLI MOV DX,[BX] ; keep copy of the ip MOV [BX],AX ; store ip into the table INC BX INC BX ; move pointer in interrupt table MOV CX,[BX] ; keep copy of the cs, too MOV AX,CS MOV [BX],AX ; store new cs into the table STI POP DS MOV SAVEIP,DX ; store them away MOV SAVECS,CX RET MYDS DW 00H ; the data segment for this assembly code ICNT DB 00H IHAND: ; not a public name, only handles ints STI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI CLD ; all moves will be forward ; ; SET UP CORRECT DS ; MOV DS,CS:MYDS ; get correct ds MOV AX,word ptr [BUFPT+2] ; buffer's ds MOV DI,BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. turn off receiver ; MOUT EAUX,EBUS+ERIDE ; refuse to read more packets until restarted MIN EREC ; must clear interrupt MOV AL,1 ; set flag MOV DEAF,AL ; we are now deaf, read routine must restart JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,BUFORG ; wrap to here MOV BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; OKAYREAD: ; IREADONE: MOUT EAUX,EBUS+ERIDE ; turn off receive, give buffer to bus MOUTW EGLOW,0,0 ; clear general purpose pointer for read MIN EREC ; get status to al, clears read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length OR BX,BX ; check for non-zero JZ STOPINT ; no packet MOV AX,BX ; save length in buffer, before packet STOSW MOV DX,EBUF ; window to the data IDOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP IDOBYTES ; ; ; DI now contains updated value for BUFPT, BX contains size of packet ; MOV BUFPT,DI ; it is here, now MOV AX,BUFBIG ; total amount of stuff in buffer ADD AX,BX INC AX INC AX ; to cover the length value MOV BUFBIG,AX ; after adding in current packet size ; ; signs that something is actually happening - used for debugging ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC CS:ICNT ; MOV Al,CS:ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux ENDINT: MOUT ICTRL,ENDOFI ; signal end of interrupt POP DI POP DX POP CX POP BX POP AX POP ES POP DS IRET IINST ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; ; usage: etupdate(); ; E1ETUPDA PROC FAR PUSH ES MOV AX,word ptr [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; two more for length value ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened MOUT ERLOW,0 ; reset receive buffer ptr MOUT EAUX,EGETEM+ERIDE ; turn on receiver ALIVE: POP ES RET E1ETUPDA ENDP ENDPS END The 3COM 3C501 version uses interrupts, so this routine is a NOP ; for this board. ; ; usage: recv(); ; E1RECV PROC FAR RET ; for compatibility with other drivers E1RECV ENsource/enet/nov3com.asm 644 144 13 57765 4267454607 10505 ; 3COM 3C501 driver code ; Tim Krauskopf ; with mods by Dave Thomson, Carleton University ; ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;* Novell compatibility modifications by Dave Thomson * ;* Copyright (c) 1987 ;* Carleton University * ;* Department of Computer Science * ;* Ottawa CANADA * ;* K1S 5B6 * ;* * ;**************************************************************************** ; TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for interrupt-driven Ethernet I/O on the PC ; ; Will read and write packets from the 2K packet buffer on the ; Etherlink card. Provides hooks for higher layer protocols. ; NAME NET INCLUDE DOS.MAC SETX ; ; Equates for controlling the 3COM board ; ICTRL EQU 020H ; 8259 interrupt control register IMASK EQU 021H ; 8259 interrupt mask register BASEA EQU 300H ; Base I/O address on PC I/O bus ENDOFI EQU 020H ; end-of-interrupt ; ; Controller registers ; EADDR EQU BASEA+0H ; Network address for hardware checking ; takes six bytes, this is the address that the Ethernet board will ; match to find packets on the net. (0-5h) ; EREC EQU BASEA+6H ; Receive status (read) ; Receive command (write) ESEND EQU BASEA+7H ; Transmit status (read) ; Transmit command (write) EGLOW EQU BASEA+8H ; General purpose pointer for R/W to ; packet buffer, low byte EGHI EQU BASEA+9H ; high byte, total of 11 bits for 2K buffer ERLOW EQU BASEA+0AH ; Receive pointer, set by board (read) low byte ; Receive buffer pointer clear (write) ERHI EQU BASEA+0BH ; high byte of Receive pointer EPROM EQU BASEA+0CH ; PROM window address ; DH ?? EAUX EQU BASEA+0EH ; Auxiliary Status (read) ; Aux Command (write) EBUF EQU BASEA+0FH ; Buffer window (where to I/O to net) ; ; Transmit command options ; what conditions we wish to be interrupted on after transmit ; EDTUNDER EQU 01H ; Detect underflow (bad CRC), not used EDTCOLL EQU 02H ; Detect collision on xmit EDTC16 EQU 04H ; Detect 16 consecutive collisions (net down) EDTOK EQU 08H ; Detect successful transmission ; other 4 bits unused in this reg EXMITNORM EQU 00H ; Normal use for interrupt-driven XMIT ; ; Transmit status results ; ; Use same values as commands, 08h means OK to xmit again ; ;***************************** ; Receive Command Options ; ; If something is not detected for, the receiver automatically discards ; those packets. ; EDTOVER EQU 01H ; Detect Overflow condition EDTFCS EQU 02H ; Detect FCS error, bad CRC on packet EDTDRB EQU 04H ; Detect dribble errors and accept them EDTSHORT EQU 08H ; Detect short frames (< 60 bytes) EDTEOF EQU 10H ; Detect no overflow (end-of-frame found) EGOOD EQU 20H ; Accept good frames ; four values legal for the last two bits: ECLOSE EQU 00H ; Turn off receiver EGETALL EQU 40H ; Get all packets, no matter what address EBROAD EQU 80H ; get those for me or for broadcast EMULTI EQU 0C0H ; get those for me or for multicast EWANT EQU 0A0h ; EGOOD OR EBROAD ; which packets we will look for on net ; ; Receive Status results ; ; errors are not detected unless asked for...otherwise the board just ; won't take bad packets off of the net. ; ERROVER EQU 01H ; overflow error ERRFCS EQU 02H ; FCS (checksum) error ERRDRB EQU 04H ; Dribble error ERRSHORT EQU 08H ; Short frame error ENOOVER EQU 10H ; Received without overflow error ; means that we didn't miss any by being slow ;EGOOD EQU 20H ; as above, we received a valid frame ; undefined 40h ESTALE EQU 80H ; stale receive condition, already read me ; ; Aux command register ; EIRE EQU 01H ; interrupt request enable (no DMA) new boards EBADFCS EQU 02H ; create bad checksum for testing only ; ; Next two bits tell who has access to packet buffer ; EBUS EQU 00H ; System bus has control of buffer EXMIT EQU 04H ; Transmit packet in buffer, auto kick ; back to recieve status EGETEM EQU 08H ; Receive state, look for packets ELOOP EQU 0CH ; Transmit and loopback into xmit buffer ; 10H unused EDMA EQU 20H ; Starts a DMA xfer ERIDE EQU 40H ; Interrupt and DMA enable ERESET EQU 80H ; Reset the Ethernet board ; ; Aux status register ; ERBUSY EQU 01H ; Receive busy, receiver looking for packets ; 02H ; echos command status EBADFCS ; 04,08h ; echos command status for EXMIT,EGETEM,EBUS EDMADONE EQU 10H ; goes to one when DMA finishes ; 20H ; echos DMA request bit ; 40h ; echos RIDE bit EXBUSY EQU 80H ; for polled xmit, goes to 0 when xmit is done ; ; ; Macros for in and out ; MOUT MACRO REG,STUFF ; one byte to the given I/O register MOV DX,REG MOV AL,STUFF OUT DX,AL ENDM ; MOUTW MACRO REG,LO,HI ; two bytes to the I/O double port MOV DX,REG MOV AL,LO OUT DX,AL INC DX MOV AL,HI OUT DX,AL ENDM ; MIN MACRO REG ; get one byte to al MOV DX,REG IN AL,DX ENDM DSEG PUBLIC STAT,BUFPT,BUFORG,BUFEND,BUFREAD,BUFBIG,BUFLIM,OFFS STAT DB 00H ; last status from read BUFBIG DW 00H ; buffer space used BUFLIM DW 05000H ; buffer space limit BUFPT DW 00000H ; where buffer pointer is, initialized safely BUFDS DW 0a000H ; where buffer is, ds BUFORG DW 00000H ; start of buffer space BUFDS2 DW 0a000H ; another ds BUFEND DW 06000H ; end limit of allowable buffer space BUFDS3 DW 0a000H BUFREAD DW 00000H ; where the read pointer is BUFDS4 DW 0a000H ;DAVE - additions for Novell compatibility VECTOR DB 4 DUP (0) ;used to chain to previously installed driver CHAINING DW 0 ;flag to indicate if we are chaining EN_IP EQU 800H EN_ARP EQU 806H SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H OLDMASK DB 00H ; save interrupt controller mask DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off ; ; use variables to access IRQ3 or IRQ5 ; 3 is COM2, 5 is LPT2 ; ;WHICHINT EQU 4*0Bh ; Interrupt for interrupt I/O on IRQ3 ;WHICHINT EQU 4*0Dh ; Interrupt for interrupt I/O on IRQ5 ;TURNOFF EQU 08H ; IRQ3 bit mask for 8259 controller (1<<3) ;TURNOFF EQU 020H ; IRQ5 bit mask for 8259 controller (1<<5) ;TURNON EQU 0F7H ; IRQ3 enable bit mask for 8259 controller ;TURNON EQU 0DFH ; IRQ5 enable bit mask for 8259 controller INTNUM DB 0BH ; Defaults to IRQ3, interrupt handler 0bh WHICHINT DW 4*0BH ; ETOPEN can change these values TURNOFF DB 08H TURNON DB 0F7H ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC RECV,ETOPEN,ETCLOSE,GETADDR,SETADDR,XMIT,ETUPDATE ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,dma) ; char s[6]; ethernet address ; int irq,dma; interrupt number and dma channel to use ; ETOPEN PROC FAR PUSH BP MOV BP,SP ;DAVE - determine if the board should be reset i.e. is it already hooked MOV AH,30H INT 21H ;get DOS version CMP AL,3 ;must be 3.xx JE ET_VERSION_OK1 ET_VERSION_ERROR: MOV AX,0FFFFH RET ET_VERSION_OK1: CMP AH,10 ;3.10? JE ET_DOS310 CMP AH,20 JE ET_DOS320 ;3.20? JMP ET_VERSION_ERROR ET_DOS310: MOV DX,0F000H ;this vector defaults to rom segment under 3.10 JMP ET_GOT_SEGMENT ET_DOS320: MOV DX,0070H ;default is segment 0070H under dos 3.20 ET_GOT_SEGMENT: PUSH DX ;save the segment for later checking agains vector ; ; check the parameters for interrupt and dma ; MOV AX,[BP+X+4] ; interrupt number CMP AL,5 ; If not 5, then use 3 JNZ USE3 MOV INTNUM,0DH ; Interrupt # for handler for IRQ5 MOV WHICHINT,4*0DH ; Interrupt handler location in vector table MOV TURNOFF,020H ; mask for interrupt controller for IRQ5 MOV TURNON,0DFH ; opposite mask, for interrupt controller USE3: MOV AX,[BP+X+6] ; dma channel to use CMP AL,3 ; if not 3, then use 1 JNZ USE1 ; ; Add DMA values for channel 3 here later ; USE1: ;DAVE - save mask in case we don't go through initialization code ; MIN IMASK ; get current int enable mask MOV OLDMASK,AL ;DAVE - interrupts must be off for the entire installation ; in case the card is in use by another driver ; CLI CALL IINST ; DO THE PATCHING OF THE INTERRUPT TABLE POP DX CMP DX,SAVECS ;compare the default segment with the actual segment ;the vector pointed to before we hooked it JNE ET_CHAINING ;vector already hooked - don't initialize it MOUT EAUX,ERESET ; reset the board MOUT EAUX,0 ; Clear the reset bit, otherwise keeps resetting ; ; install the interrupt handler ; ; ; set up the net address ; PUSH DS ; save mine MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; MOV CX,6 MOV DX,EADDR ; get base i/o reg for setting address CLD SADDR: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR ; do 6 times POP DS ; get back DS of local data ; ; enable interrupts here with interrupt handler ; already set up. ; MOUT ESEND,0 ; xmit command = 0 for no interrupts IN AL,DX MOUT EREC,EWANT ; Set receiver for which packets we want IN AL,DX ; reset 'stale' MOUT ERLOW,0 ; Clear the receive buffer pointer ;DAVE - interrupts already off ; CLI MOUT EAUX,EGETEM+ERIDE ; Set for receive, interrupts MIN IMASK ; get current int enable mask MOV OLDMASK,AL MOV BL,AL ; save a copy AND AL,TURNON ; force bit for etherlink board off OUT DX,AL ; put back the byte, IRQ enabled ;DAVE - zero chaining flag ; XOR AX,AX MOV CHAINING,AX ;set chaining flag JMP ET_SKIP_INIT ET_CHAINING: MOV AX,1 MOV CHAINING,AX ;set chaining flag ET_SKIP_INIT: STI ;DAVE - it appears we don't need this code and it will interfere with the ; chaining case ; AND BL,TURNOFF ; isolate this bit only from oldmask ; MOV OLDMASK,BL ; save it ; POP BP XOR AX,AX RET ETOPEN ENDP ; ;****************************************************************** ; SETADDR ; set the Ethernet address on the board to 6 byte ID code SETADDR PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; address of buffer to read ; MOV CX,6 MOV DX,EADDR ; get base i/o reg for setting address CLD SADDR2: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR2 ; do 6 times POP DS POP BP RET SETADDR ENDP ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s); ; char s[6]; will get six bytes from the PROM ; GETADDR PROC FAR PUSH BP MOV BP,SP PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready ; MOV BX,0 ; start location MOV CX,EPROM ; address window GADDR: CLD MOUTW EGLOW,BL,BH ; set gp to the right value MIN CX ; get value from prom address window STOSB ; put into given buffer INC BX ; next position CMP BX,6 JNZ GADDR ; do 6 times POP ES POP BP RET GETADDR ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down ; ETCLOSE PROC FAR ;DAVE - interrupts must be off for duration of close ; CLI ; MOUT EAUX,ERESET ; Turn off all pendings, cause reset ; MOUT EAUX,0 ; Turn off reset ; ; Make sure that DMA transfers are not outstanding (later) ; ; ; mask out IRQ on interrupt controller ; ; MIN IMASK ; get current mask ; OR AL,TURNOFF ; force that bit on ; OUT DX,AL ; send it back to controller ; STI CALL DEINST ; restore old interrupt handler ; MOV BL,OLDMASK ; get back saved setting of irq ; NOT BL ; flip it ; CLI ;should be already off ; MIN IMASK ; AND AL,BL ; restore setting of that bit MOV AL,OLDMASK ;DAVE - restore to old mask OUT DX,AL STI ;DAVE - turn interrupts on RET ETCLOSE ENDP ; ;************************************************************************ ; Receive ; This version is unused. It is a polled receive which is nearly ; useless with this board. It hasn't been debugged yet, either. ; get those packets into a buffer ; ; usage: size = recv(where); ; char *where; at least 2048 bytes of room ; returns # bytes in packet, -1 if no packet available ; RECV PROC FAR PUSH BP MOV BP,SP PUSH ES MOV AX,[BP+X+2] ; get new es value MOV ES,AX MOV DI,[BP+X] ; set di for later movement ; MOV CX,10 ; give it a few tries FINDONE: MIN EAUX ; get status to al MOV STAT,AL AND AL,ERBUSY ; is it still in receive state or done? JZ READONE ; done, can read it LOOP FINDONE MOV AX,-1 ; no packet yet, return POP ES POP BP RET ; READONE: MOUT EAUX,EBUS ; turn off receive, give buffer to bus MOUTW EGLOW,0,0 ; clear general purpose pointer for read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length MOV DX,EBUF ; window to the data DOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP DOBYTES MIN EREC ; get status to al, clears read MOV STAT,AL ; KEEP LAST STATUS BYTE ; ; set up to read the next packet from the net ; MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux MOV AX,BX ; return value is # of bytes POP ES POP BP RET RECV ENDP ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; XMIT PROC FAR PUSH BP MOV BP,SP ;DAVE - we must turn interrupts of for the duration of the transmit so that ; other drivers won't try to access the card ; CLI PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes MOV CX,AX ; save a copy, might be less than 60, ok CMP AX,60 ; minimum length for Ether JNB OKLEN MOV AX,60 ; make sure size at least 60 OKLEN: MOV BX,2048 ; total length of buffer SUB BX,AX ; offset of for buffer pointer to start MOV DI,BX ; save a copy of the buffer pointer ; ; TAKE CONTROL OF THE INPUT BUFFER ; MOUT EAUX,EBUS+ERIDE ; take buffer away from receiver MOUT ERLOW,0 ; clear receive pointer for next read MOUTW EGLOW,BL,BH ; set the general purpose pointer MOV DX,EBUF ; window to packet buffer CLD FILLBUF: LODSB ; get value to go into buffer OUT DX,AL ; put it into buffer (autoincrement) LOOP FILLBUF ; do whole count POP DS ; ; PACKET IS IN BUFFER, READY TO BE SENT ; TRYAGAIN: MOV BX,DI ; retrieve copy of offset pointer MOUTW EGLOW,BL,BH ; set the general purpose pointer (again) ; MOUT EAUX,EXMIT+ERIDE ; tell the board to send it and start receiving NOTDONEX: MIN EAUX ; waiting for transmit to finish AND AL,EXBUSY ; is it done yet? JNZ NOTDONEX ; no, wait some more MOV CX,0 ; return value, ok MIN ESEND ; get xmit status MOV BL,AL ; save status AND AL,EDTOK ; was it ok? JNZ DONEX ; yes, successful xmit ; ; handle the possible errors, return 1 on coll16 ; coll16 generally means that the network has failed ; MOV AL,BL ; get copy of status back AND AL,EDTC16 ; check collision 16 JNZ RET16 ; yes, network probably down MOV AL,BL ; get copy back again AND AL,EDTCOLL ; check for collision status JZ UNK ; no, unknown problem MOUT EAUX,EBUS+ERIDE ; collision, reset buffer control JMP TRYAGAIN ; go for it UNK: MOV CX,2 ; unknown problem return code JMP DONEX RET16: MOV CX,1 ; failure return DONEX: MOUT EREC,EWANT ; reset receive register filter necessary MIN EAUX AND AL,ERBUSY ; is it still in receive state or done? JNZ DONEMIT ; not ready now, return instead ; MOV AL,INTNUM ; CMP AL,0BH ; two choices of int to call ; JNZ TRYNINT ; INT 0BH ; we do have a packet, read it ; JMP DONEMIT ;TRYNINT: ; CMP AL,0DH ; JNZ DONEMIT ; INT 0DH DONEMIT: MOV AX,CX ; put return in ax ;DAVE - turn interrupts back on ; STI POP BP RET XMIT ENDP ; ;************************************************************************* ; INTERRUPT HANDLER, INSTALLATION AND DEINSTALLATION ; IHAND ; take the receive packet out of the input buffer ; ;DAVE - modified to not turn interrupts on - should be off before the call ; the call ; DEINST PROC NEAR MOV CX,SAVEIP ; get old ip from save spot MOV DX,SAVECS ; get old cs from save spot MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX CLI MOV [BX],CX ; store old ip into the table INC BX INC BX ; move pointer in interrupt table MOV [BX],DX ; store old cs into the table ;DAVE - interrupts must be off the entire deinstall process ; ; STI POP DS RET DEINST ENDP ; ;DAVE - modified to not turn interrupts on - must be off before call IINST PROC NEAR MOV CS:MYDS,DS ; store for use by handler MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX MOV AX,OFFSET IHAND ; where the handler is CLI MOV DX,[BX] ; keep copy of the ip MOV [BX],AX ; store ip into the table INC BX INC BX ; move pointer in interrupt table MOV CX,[BX] ; keep copy of the cs, too MOV AX,CS MOV [BX],AX ; store new cs into the table ;DAVE - interrupts must be off for the entire install process ; ; STI POP DS MOV SAVEIP,DX ; store them away MOV SAVECS,CX ;DAVE - save the CS:DX in the jump vector in the correct order ; LEA SI,VECTOR MOV WORD PTR [SI],DX MOV WORD PTR [SI+2],CX RET MYDS DW 00H ; the data segment for this assembly code ICNT DB 00H IHAND: ; not a public name, only handles ints ;DAVE - mosified the way the "deaf" mode works to set a flag but keep the ; card in receive mode so that we can still pass packets we don't ; want to other installed drivers (if any) ; ;DAVE - interrupts must be off - we must have exclusive access to the card ; for the duration of the service routine ; ; STI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI CLD ; all moves will be forward ; ; SET UP CORRECT DS ; MOV DS,CS:MYDS ; get correct ds MOV AX,BUFDS ; buffer's ds MOV DI,BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. turn off receiver ; ; MOUT EAUX,EBUS+ERIDE ; refuse to read more packets until restarted MIN EREC ; must clear interrupt ;DAVE - "deaf" mode no longer turns the card off ; MOV AL,1 ; SET FLAG MOV DEAF,AL ; WE ARE NOW DEAF, READ ROUTINE MUST RESTART CHAIN: MOV AX,CHAINING OR AX,AX JZ STOPINT ;not chaining so reset receive mode and exit LEA SI,VECTOR PUSHF CALL DWORD PTR [SI] ;chain to previously installed driver ;we assume that the other driver leaves the ;card in receive mode JMP ENDINT ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,BUFORG ; wrap to here MOV BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; OKAYREAD: ; MOV CX,10 ; give it a few tries IFINDONE: ; MIN EAUX ; get status to al ; AND AL,ERBUSY ; is it still in receive state or done? ; JZ IREADONE ; done, can read it ; LOOP IFINDONE ; MIN EREC ; AND AL,ESTALE ; JZ IREADONE ; jmp ireadone ; MOV AX,0 ; no packet yet, spurious int, return ; STOSB ; MIN EREC ; clear interrupt condition ; MIN ESEND ; in case it was an xmit spurious int ; JMP STOPINT ; IREADONE: MOUT EAUX,EBUS+ERIDE ; turn off receive, give buffer to bus ; MOUTW EGLOW,0,0 ; clear general purpose pointer for read ;DAVE - we now want a first peek at offset 12-13 the ethernet type field ; MOUTW EGLOW,12,0 ; clear general purpose pointer for read MIN EREC ; get status to al, clears read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length OR BX,BX ; check for non-zero JZ STOPINT ; no packet MOV AX,BX ; SAVE LENGTH IN BUFFER, BEFORE PACKET STOSW ;DAVE - check the ethernet type to see if we are interested in it ; MOV DX,EBUF ; window to the data IN AL,DX MOV AH,AL IN AL,DX PUSH AX ;type in AX ;set up the card for the packet read in case we want it ; MOUTW EGLOW,0,0 ; clear general purpose pointer for read MOV DX,EBUF ; window to the data POP AX ;get type back CMP AX,EN_IP ;IP packet? JE IDOBYTES ;if so get it CMP AX,EN_ARP ;ARP? JE IDOBYTES JMP CHAIN ;otherwise chain to the previously installed driver IDOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP IDOBYTES ; ; ; DI now contains updated value for BUFPT, BX contains size of packet ; MOV BUFPT,DI ; IT IS HERE, NOW MOV AX,BUFBIG ; TOTAL AMOUNT OF STUFF IN BUFFER ADD AX,BX INC AX INC AX ; TO COVER THE LENGTH VALUE MOV BUFBIG,AX ; AFTER ADDING IN CURRENT PACKET SIZE ; ; signs that something is actually happening ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC CS:ICNT ; MOV Al,CS:ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux ENDINT: MOUT ICTRL,ENDOFI ; signal end of interrupt POP SI POP DI POP DX POP CX POP BX POP AX POP ES POP DS IRET IINST ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; ; ;DAVE - this routine no longer restarts the card - it is always on ; thgis routine just resets the "deaf" flag ; ETUPDATE PROC FAR PUSH ES MOV AX,BUFDS ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened ;DAVE - already on ; ; MOUT ERLOW,0 ; reset receive buffer ptr ; MOUT EAUX,EGETEM+ERIDE ; turn on receiver ALIVE: POP ES RET ETUPDATE ENDP ENDPS END nerally measource/enet/net523.asm 644 144 13 51707 4267454612 10123 ; 3COM 3C523 driver code ; Tim Krauskopf ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;**************************************************************************** ; TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for Ethernet I/O on the PC ; ; Tim Krauskopf ; 9/1/87 Ungermann-Bass driver started, PC bus ; 9/14/87 MICOM NI5210 driver started, PC bus ; 4/22/88 3COM 3C523 driver adapted from MICOM 82586 driver, MCA bus ; ; NAME MNET INCLUDE DOS.MAC SETX ; ; CA macro ; DOCA MACRO FLGS mov dx,IOADDR add dx,IOC mov al,FLGS+CA out dx,al mov al,FLGS jmp $+2 jmp $+2 jmp $+2 out dx,al ENDM ; ; Equates for controlling the 3c523 board ; ; I/O addresses, seventh address controls everything ; ; First six addresses are the EPROM board Ether address (read) ; IOC EQU 6 ; ; Register bits ; GO EQU 83h ; no reset plus bank select CA EQU 40h ; CA bit LBK EQU 20h ; loopback bit ZRAM EQU 03h ; memory bank enable ; ; other bits are interrupts, not needed ; ; ; Structure elements specific to the Intel 82586 chip ; BDBASE EQU 1874+0c000h ; base address for 30 buffer descriptors BUFBASE EQU 2174+0c000h ; base address for 30 200 byte buffers BDSTAT EQU 0 ; status word in BD BDLINK EQU 2 ; 16pointer to next BD BDPTR EQU 4 ; 24pointer to actual buffer BDSIZE EQU 8 ; size of the buffer ; SCB EQU 10+0c000h ; system control block base SSTAT EQU 0 ; status word for SCB SCOM EQU 2 ; command word in SCB SCBL EQU 4 ; 16pointer to command block list SRFA EQU 6 ; 16pointer to receive frame list SERRS EQU 8 ; 4 words of error counts ; FDBASE EQU 1214+0c000h ; base addr for 30 frame descriptors FDSTAT EQU 0 ; status word for frame FDEOL EQU 2 ; end of FD list flag FDLINK EQU 4 ; 16pointer to next FD FDPTR EQU 6 ; 16pointer to list of BD's ; TSTAT EQU 0 ; status word for xmit TCOM EQU 2 ; command to transmit TLINK EQU 4 ; 16pointer to next command (always ffff) TPTR EQU 6 ; 16pointer to xmit TBD TTRIES EQU 8 ; number of transmit retries ; SCPTR EQU 03ff6h+0c000h ; hardwired address for SCP ISCPTR EQU 03feeh+0c000h ; my address for ISCP, points to SCB CCBPTR EQU 26+0c000h ; offset of configure command block TCBPTR EQU 44+0c000h ; xmit CB offset TBDPTR EQU 60+0c000h ; xmit BD offset TBUFPTR EQU 68+0c000h ; xmit buffer offset ; ; Data segment ; DSEG ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have ICNT DB 00h SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H LFPP DB 00h ; Full Page pointer DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off FIRSTFD dw FDBASE ; start of FD queue LASTFD DW 0 ; end of the FD chain LASTBD DW 0 ; end of the BD chain IOADDR DW 02300h ; I/O address for card (POS set) UBASE DW 0bc00h ; base segment for board (POS set) ; ; data for configuring and setting up the MICOM board ; ; chip always looks at SCP for config info which points to ISCP for the ; pointer to the CONTROL BLOCK which handles everything from there. ; Kind of indirect, but it works. ; SCP DB 0 ; bus use flag DB 5 DUP(0) ; unused DW ISCPTR ; 24pointer to ISCP offset DW 0 ; high part ; ; Intermediate SCP ; ISCP DW 1 ; busy flag DW SCB ; 16pointer to SCB DW 0,0 ; base for all 16 pointers, lo, hi ; board is hardwired to 0 for these values ; ; Configuration block for 82586, this comprises one config command ; Parameters taken from MICOM driver ; CBCONF DW 0 ; status word DW 8002H ; end of command list + configure command DW 0ffffh ; link to next command (not used) DW 080CH ; fifo=8, byte count=C DW 2E00H ; important! Addr (AL) not inserted on the fly! DW 6000H ; IFS = 60h DW 0F200H ; retry=F, slot time=200h DW 0 ; flags, set to 1 for promiscuous DW 40H ; min frame length=40h ; ; CB for xmit, followed by BD for xmit, copied together ; TCB DW 0 ; status word DW 08004H ; command word for xmit + EL DW 0ffffh ; no command link DW TBDPTR ; 16pointer to xmit BD DW 0,0,0,0 ; no addressing used here ; ; BD template for xmit TBD DW 0 DW 0 ; next BD pointer, unused DW TBUFPTR ; 24pointer to xmit buffer DW 0 ; high part of pointer ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC E2RECV,E2ETOPEN,E2ETCLOS,E2GETADD,E2XMIT,E2ETUPDA ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,address,ioaddr) ; char s[6]; ethernet address ; int irq; (unused, we don't use no interrupts) ; int address base mem address ; int ioaddr io base address ; E2ETOPEN PROC FAR PUSH BP MOV BP,SP push es mov ax,[bp+X+6] ; second parameter sub ax,0c00h ; set back to match 82586 addressing mov UBASE,ax ; base address ; mov ax,UBASE ; get a copy (temporary) mov es,ax ; set to base address mov ax,[bp+X+8] ; get I/O address mov IOADDR,ax ; save a copy ; mov ax,IOADDR ; get a copy (temporary) ; ; Set the POS bit for card enable? ; The section which hasn't been written here should query the POS, ; set the card enable bit, and look up the base and ioaddrs so that ; the user won't have to install them in the configuration file. ; That's why we have PS/2s - but I got lazy (tk) ; ; ; Bank select, so that the memory will be available ; Bit settings also leave interrupts off, don't need them ; mov dx,ax ; IO reg base add dx,IOC ; control register mov al,23h ; reset, plus enable bank 3, plus LBK out dx,al ; ; Initialize 82586 ; DOCA ZRAM+LBK ; hit the board with a reset ; ; Install 16K SCP ; nop nop nop mov si,offset SCP ; get pre-set values mov di,SCPTR ; offset for 16K board mov cx,5 ; 5 words rep movsw ; install SCP ; ; Intermediate SCP ; mov si,offset ISCP ; addr of pre-set values mov di,ISCPTR mov cx,4 ; 4 words rep movsw ; install ISCP ; ; configure 82586 ; mov si,offset CBCONF ; configure command mov di,CCBPTR ; where command will reside mov cx,9 rep movsw ; copy to board ; ; issue the configure command ; mov ax,word ptr es:[SCB+SSTAT] ; get old status and ax,0f000h or ax,0100h mov word ptr es:[SCB+SCOM],ax ; do-command command mov word ptr es:[SCB+SSTAT],0 ; status word mov word ptr es:[CCBPTR],0 ; status word mov word ptr es:[SCB+SCBL],CCBPTR ; where conf command is mov word ptr es:[SCB+SERRS],0 ; zero errs field mov word ptr es:[SCB+SERRS+2],0 ; zero errs field mov word ptr es:[SCB+SERRS+4],0 ; zero errs field mov word ptr es:[SCB+SERRS+6],0 ; zero errs field DOCA LBK+GO ; kick CA DOCA LBK+GO ; kick CA xor cx,cx ; timeout waitconf: mov ax,word ptr es:[CCBPTR] ; get status word test ax,08000h loopz waitconf jnz confok mov ax,-1 ; timeout problem pop es pop bp ret ; confok: and ax,0f000h ; ack flags mov word ptr es:[SCB+SCOM],ax ; ack bits DOCA LBK+GO xor cx,cx ; timeout waitcack: mov ax,word ptr es:[SCB+SCOM] ; get command word or ax,ax loopnz waitcack jz cackok mov ax,-1 ; timeout problem pop es pop bp ret ; cackok: ; ; Next step, load our address into the board ; reuses the space that the configure command used, with different command ; mov di,CCBPTR ; start of config command block xor ax,ax stosw ; zero status word for commmand mov ax,8001h ; IA setup command + EL stosw xor ax,ax dec ax stosw ; set link value to -1 (unused) ; move my addr onto board location inside IA command ; es:di is already set ; PUSH DS ; save my DS MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; movsw movsw movsw POP DS ; get back DS of local data ; ; start the IA setup command, ; mov word ptr es:[SCB+SCOM],0100h ; do-command command mov word ptr es:[SCB+SSTAT],0 ; clear status DOCA LBK+GO ; kick CA, with loopback xor cx,cx ; timeout waitia: mov ax,word ptr es:[SCB+SSTAT] ; get status word or ax,ax loopz waitia jnz iaok mov ax,-2 ; timeout problem pop es pop bp ret ; iaok: and ax,0f000h ; ack flags mov word ptr es:[SCB+SCOM],ax ; ack bits DOCA LBK+GO xor cx,cx ; timeout waitiack: mov ax,word ptr es:[SCB+SCOM] ; get command word or ax,ax loopnz waitiack jz iackok mov ax,-1 ; timeout problem pop es pop bp ret ; iackok: ; ; IA sent, setup all of the other data structures on the board ; start with xmit command descriptors ; mov si,offset TCB ; template for xmit mov di,TCBPTR ; where it goes on board mov cx,12 ; copies CB and BD for xmit rep movsw ; ; Set up frame and buffer descriptors, 30 each ; mov cx,30 ; # of FDs mov di,FDBASE ; base addr for FDs fdloop: xor ax,ax mov bx,di ; save pointer stosw ; clear status wd stosw ; clear EL field add bx,22 ; points to next one mov es:[di],bx ; put in link ptr inc di inc di dec ax stosw ; clear BD ptr to -1 add di,14 loop fdloop ; sub di,20 ; point back to last EL field mov ax,08000h ; end of list stosw ; put into last FD sub di,4 ; back to beginning of last FD mov LASTFD,di ; save the pointer mov word ptr es:[di+FDLINK],FDBASE ; make list circular, from last to first ; mov ax,BDBASE ; first BD mov word ptr es:[FDBASE+FDPTR],ax ; put it in the first FD frame ; ; now BDs mov cx,30 mov di,BDBASE ; start of BD area mov dx,BUFBASE ; start of buffer area bdloop: xor ax,ax mov bx,di ; save pointer stosw ; zero status field add bx,10 ; point to next record mov es:[di],bx ; put in link ptr inc di inc di mov es:[di],dx ; address of buffer, lo part inc di inc di stosw ; zero out high part mov ax,200 stosw ; store length field add dx,ax ; add in length of buffer, updates ptr loop bdloop ; sub di,2 ; back to last BD size field mov ax,08000h+200 ; end of list + 200 stosw ; mark end of list sub di,8 ; back to last BDLINK field mov ax,BDBASE stosw ; put link to beginning of list here sub di,4 ; back to beginning of last BD mov LASTBD,di ; save pointer to end of list ; ; minor detail, but important ; Change SCB command block pointer to setup for xmit commands ; = only commands needed when operational ; mov word ptr es:[SCB+SCBL],TCBPTR ; where xmit command is ; ; configure to connect to network ; ; mov dx,IOADDR ; add dx,IOC ; enable network ; mov al,GO ; network setting ; out dx,al ; ; ; Start the RU, doesn't need CB, only SCB parms. ; command, to start receiving ; mov word ptr es:[SCB],0 ; clear status word mov word ptr es:[SCB+SRFA],FDBASE ; set to frame descriptors mov word ptr es:[SCB+SCOM],010h ; start RU DOCA GO ; kick CA ; ; don't wait for response, we are done ; xor ax,ax POP ES POP BP RET E2ETOPEN ENDP ; ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; E2GETADD PROC FAR PUSH BP MOV BP,SP mov dx,[BP+X+6] ; get board's base io addr PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready ; mov cx,6 CLD getonee: in al,dx ; get a byte of the EPROM address STOSB ; put it away inc dx ; next register loop getonee ; go back for rest xor ax,ax POP ES POP BP RET E2GETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down if necessary ; MICOM board never interrupts, so we can leave it running. ; Who cares, right? ; E2ETCLOS PROC FAR RET E2ETCLOS ENDP ; ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; E2XMIT PROC FAR PUSH BP MOV BP,SP push es mov ax,ubase mov es,ax ; base for board PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV DX,[BP+X+4] ; count of bytes MOV CX,DX ; save a copy, might be less than 60, ok CMP DX,60 ; minimum length for Ether JNB OKLEN MOV DX,60 ; make sure size at least 60 OKLEN: mov di,TBUFPTR ; start of xmit buffer ; ; check for previous xmit xwait: mov bx,word ptr es:[SCB+SCOM] ; is command zeroed yet? or bx,bx jnz xwait ; not there yet, wait for it ; ; move the data rep movsb ; copy into buffer ; ; put the correct size into the TDB ; or dx,08000h ; end of frame bit flag mov word ptr es:[TBDPTR],dx ; store it mov word ptr es:[TCBPTR],0 ; zero status wd mov word ptr es:[TCBPTR+TCOM],08004h; xmit command in TCB mov word ptr es:[SCB+SCOM],0100h ; execute command ; test bx,0100h ; suspended? ; jz nosus ; mov word ptr es:[SCB+SCOM],0400h ; stop command ;nosus: pop ds ; get back my ds DOCA GO ; kick CA xor ax,ax pop es POP BP RET E2XMIT ENDP ; ; ;*********************************************************************** ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; The 3COM 3C501 version uses interrupts, so this routine is a NOP ; for this board. ; ; usage: recv(); ; ; E2RECV proc far push bp push es MOV AX,word ptr [BUFPT+2] ; buffer's ds MOV DI,BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. ; JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,BUFORG ; wrap to here MOV BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; Add 2 to allow for space to put the size value ; OKAYREAD: inc di inc di ; room for size word mov bx,FIRSTFD ; get addr of first FD in list mov dx,ubase ; base for board push ds mov ds,dx ; ; CKFRAME: mov ax,[bx] ; status word of frame test ax,08000h ; frame written? jnz IREADONE ; yes, read it in pop ds call rust ; restore receiver if necessary jmp STOPINT ; ; we have a frame, read it in ; IREADONE: test ax,02000h ; check frame OK bit jnz frameok ; ok, read it mov cx,ds pop ds call rust ; preserves cx, restart RU if necessary jmp freespace ; not OK, just free the frame frameok: mov si,[bx+FDPTR] ; get pointer to buffer descriptor copybuf: ; es:di is already set to receive packet mov dx,si ; save a copy of current BD ptr mov ax,[si] ; get status and count word for BD push ax ; save for EOF bit test ax,04000h ; is count field there? jnz okcount pop ax mov cx,ds pop ds jmp freespace ; free the frame, etc okcount: xor cx,cx mov cl,al ; 200 bytes is largest this can be mov si,[si+BDPTR] ; get offset of data rep movsb ; copy the bytes from this packet segment mov si,dx ; get back current BD ptr pop ax ; get back EOF bit test ax,08000h ; check bit jnz ptrupdate ; free the frame, no more data here mov si,[si+BDLINK] ; go to next BD in list jmp copybuf ; copy the next buffer ; ; ptrupdate: ; ; DI now contains updated value for BUFPT ; mov cx,ds ; save board segment pop ds mov bx,BUFPT ; get where size field for this packet goes mov ax,di ; where pointer is now sub ax,bx ; where is was then, difference is size MOV DX,BUFBIG ; total amount of stuff in buffer ADD DX,AX ; add in size of this packet MOV BUFBIG,DX ; after adding in current packet size dec ax dec ax ; account for length field mov [bx],ax ; put the accumulated size there MOV BUFPT,DI ; it is here, now ; freespace: ; ; we are done with the frame, do the list management ; cx=DS for board DS=DGROUP ; mov bx,FIRSTFD ; the frame we are working with mov di,LASTBD ; where end of BD list is now mov es,cx ; reload board segment mov si,es:[bx+FDPTR] ; first BD in frame list nextbd: mov cx,es:[si] ; count word for BD, EOF bit test cx,08000h ; EOF bit, if set, save si in lastbd jnz dolastbd mov word ptr es:[si],0 ; clear status word, EOF bit cmp si,LASTBD ; see if we are wrapping jz dolastbd ; yes, just undo it mov si,es:[si+BDLINK] ; follow link jmp nextbd dolastbd: mov LASTBD,si ; store last known BD mov word ptr es:[si+BDSIZE],08000h+200 ; end of list here mov word ptr es:[si],0 ; clear status word, EOF bit ; size field for not end of list mov word ptr es:[di+BDSIZE],200 ; remove old end-of-list ; ; update the FD list flags, new end-of-list ; mov di,LASTFD ; get old end-of-list mov word ptr es:[bx+FDEOL],08000h ; store new EOL mov word ptr es:[bx],0 ; clear status word for frame mov word ptr es:[di+FDEOL],0 ; zero old one mov LASTFD,bx ; update stored pointer mov si,es:[bx+FDLINK] ; where next fd is mov FIRSTFD,si ; store that info for next time ; mov bx,LASTBD ; last of the BD list ; mov ax,es:[bx+BDLINK] ; get link field from lastBD ; mov es:[si+FDPTR],ax ; store into next frame ; ; signs that something is actually happening - for debugging ; ; MOV AX,0B800H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC ICNT ; MOV al,ICNT ; character ; STOSB ; ; ; set up to read the next packet from the net ; STOPINT: ENDINT: pop es POP BP RET E2RECV ENDP rust proc near ; re-start receiver ; ; check to see if the receiver went off because of no resources ; and restart receiver if necessary ; ds=dgroup, no other requirements ; push es mov ax,ubase mov es,ax mov ax,es:[SCB] ; status word for SCB and ax,070h ; receiver status cmp al,020h ; receiver has no resources jnz hasres ; ; setup lists for starting the RU on the chip ; we know that there isn't anything in the buffer that we want ; mov bx,FIRSTFD ; get first FD on free list (assume free) mov word ptr es:[SCB+SRFA],bx ; put into SCB mov si,LASTBD ; pointer to a BD, end of chain mov ax,word ptr es:[si+BDLINK] ; pointer to next BD mov word ptr es:[bx+FDPTR],ax ; set to start of BDs ; ; ; Start the RU, doesn't need CB, only SCB parms. ; command, to start receiving again ; mov word ptr es:[SCB],0 ; clear status word mov word ptr es:[SCB+SCOM],010h ; start RU DOCA GO hasres: pop es ret rust endp ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; E2ETUPDA PROC FAR PUSH ES MOV AX,word ptr [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened ALIVE: POP ES RET E2ETUPDA ENDP ENDPS END . Count is the length of packet < 2048 ; ; checks for source/enet/net5210.asm 644 144 13 50465 4267454614 10203 ; MICOM NI 5210 driver code ; Tim Krauskopf ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;**************************************************************************** ; TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for Ethernet I/O on the PC ; ; Tim Krauskopf ; 9/1/87 Ungermann-Bass driver started, PC bus ; 9/14/87 MICOM NI5210 driver started, PC bus ; 4/88 Modified for combined drivers, version 2.2 ; ; NAME MNET INCLUDE DOS.MAC SETX ; ; Equates for controlling the MICOM board ; ; I/O addresses, writing anything in AL trips these gates ; ; First six addresses are the EPROM board Ether address (read) ; IORESET EQU 0 ; reset the board IOCA EQU 1 ; execute command which is in SCB IODIS EQU 2 ; disable network connect IOENA EQU 3 ; enable network IOINTON EQU 4 ; disable interrupts, '586 thinks it still ints IOINTOF EQU 5 ; enable interrupts ; ; Structure elements specific to the Intel 82586 chip ; BDBASE EQU 1874 ; base address for 30 buffer descriptors BUFBASE EQU 2174 ; base address for 30 200 byte buffers BDSTAT EQU 0 ; status word in BD BDLINK EQU 2 ; 16pointer to next BD BDPTR EQU 4 ; 24pointer to actual buffer BDSIZE EQU 8 ; size of the buffer ; SCB EQU 10 ; system control block base SSTAT EQU 0 ; status word for SCB SCOM EQU 2 ; command word in SCB SCBL EQU 4 ; 16pointer to command block list SRFA EQU 6 ; 16pointer to receive frame list SERRS EQU 8 ; 4 words of error counts ; FDBASE EQU 1214 ; base addr for 30 frame descriptors FDSTAT EQU 0 ; status word for frame FDEOL EQU 2 ; end of FD list flag FDLINK EQU 4 ; 16pointer to next FD FDPTR EQU 6 ; 16pointer to list of BD's ; TSTAT EQU 0 ; status word for xmit TCOM EQU 2 ; command to transmit TLINK EQU 4 ; 16pointer to next command (always ffff) TPTR EQU 6 ; 16pointer to xmit TBD TTRIES EQU 8 ; number of transmit retries ; SCPTR EQU 01ff6h ; hardwired address for SCP ISCPTR EQU 01feeh ; my address for ISCP, points to SCB CCBPTR EQU 26 ; offset of configure command block TCBPTR EQU 44 ; xmit CB offset TBDPTR EQU 60 ; xmit BD offset TBUFPTR EQU 68 ; xmit buffer offset ; ; Data segment ; DSEG ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have ICNT DB 00h SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H LFPP DB 00h ; Full Page pointer DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off FIRSTFD dw FDBASE ; start of FD queue LASTFD DW 0 ; end of the FD chain LASTBD DW 0 ; end of the BD chain IOADDR DW 0360h ; I/O address for card (jumpers) UBASE DW 0d000h ; base segment for board (jumper set) ; ; data for configuring and setting up the MICOM board ; ; chip always looks at SCP for config info which points to ISCP for the ; pointer to the CONTROL BLOCK which handles everything from there. ; Kind of indirect, but it works. ; SCP DB 1 ; bus use flag DB 5 DUP(0) ; unused DW ISCPTR ; 24pointer to ISCP offset DW 0 ; high part ; ; Intermediate SCP ; ISCP DW 1 ; busy flag DW SCB ; 16pointer to SCB DW 0,0 ; base for all 16 pointers, lo, hi ; board is hardwired to 0 for these values ; ; Configuration block for 82586, this comprises one config command ; Parameters taken from MICOM driver ; CBCONF DW 0 ; status word DW 8002H ; end of command list + configure command DW 0ffffh ; link to next command (not used) DW 080CH ; fifo=8, byte count=C DW 2E00H ; important! Addr (AL) not inserted on the fly! DW 6000H ; IFS = 60h DW 0F200H ; retry=F, slot time=200h DW 0 ; flags, set to 1 for promiscuous DW 40H ; min frame length=40h ; ; CB for xmit, followed by BD for xmit, copied together ; TCB DW 0 ; status word DW 08004H ; command word for xmit + EL DW 0ffffh ; no command link DW TBDPTR ; 16pointer to xmit BD DW 0,0,0,0 ; no addressing used here ; ; BD template for xmit TBD DW 0 DW 0 ; next BD pointer, unused DW TBUFPTR ; 24pointer to xmit buffer DW 0 ; high part of pointer ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC M5RECV,M5ETOPEN,M5ETCLOS,M5GETADD,M5XMIT,M5ETUPDA ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,address,ioaddr) ; char s[6]; ethernet address ; int irq; (unused, we don't use no interrupts) ; int address base mem address ; int ioaddr io base address ; M5ETOPEN PROC FAR PUSH BP MOV BP,SP push es mov ax,[bp+X+6] ; second parameter mov UBASE,ax ; base address ; mov ax,UBASE ; get a copy (temporary) mov es,ax ; set to base address mov ax,[bp+X+8] ; get I/O address mov IOADDR,ax ; save a copy ; mov ax,IOADDR ; get a copy (temporary) ; ; check for correct EPROM location ; mov dx,ax ; i/o address add dx,6 in al,dx mov bl,al ; assemble pattern to check inc dx in al,dx mov bh,al cmp bx,05500h ; pattern known to be there in ROM jz goinit mov ax,-1 pop es pop bp ret goinit: ; ; Initialize MICOM 5210 ; ; Install 8K SCP, we only use 8K bytes no matter what ; mov si,offset SCP ; get pre-set values mov di,SCPTR mov cx,5 ; 5 words rep movsw ; install SCP ; ; Install 16K SCP ; mov si,offset SCP ; get pre-set values mov di,SCPTR+02000h ; offset for 16K board mov cx,5 ; 5 words rep movsw ; install SCP ; ; Intermediate SCP ; mov si,offset ISCP ; addr of pre-set values mov di,ISCPTR mov cx,4 ; 4 words rep movsw ; install ISCP ; ; Turn off interrupts, I don't want them ; mov dx,IOADDR ; base for IO control add dx,IOINTOF ; turn ints off out dx,al ; any value in ax sub dx,IOINTOF ; return to base address = reset out dx,al ; reset the chip ; ; Issue a CA to initialize the chip after reset ; inc dx out dx,al ; CA ; ; Disconnect from network inc dx ; one greater than CA out dx,al ; ; configure 82586 ; mov si,offset CBCONF ; configure command mov di,CCBPTR ; where command will reside mov cx,9 rep movsw ; copy to board ; ; issue the configure command ; mov word ptr es:[SCB+SCOM],0100h ; do-command command mov word ptr es:[SCB+SCBL],CCBPTR ; where conf command is mov word ptr es:[SCB+SERRS],0 ; zero errs field mov word ptr es:[SCB+SERRS+2],0 ; zero errs field mov word ptr es:[SCB+SERRS+4],0 ; zero errs field mov word ptr es:[SCB+SERRS+6],0 ; zero errs field mov dx,IOADDR add dx,IOCA ; CA out dx,al ; send it xor cx,cx ; timeout waitconf: mov ax,word ptr es:[CCBPTR] ; get status word test ax,08000h ; is command complete? loopz waitconf jnz confok mov ax,-1 ; timeout problem pop es pop bp ret ; confok: ; ; Next step, load our address into the board ; reuses the space that the configure command used, with different command ; mov di,CCBPTR ; start of config command block xor ax,ax stosw ; zero status word for commmand mov ax,8001h ; IA setup command + EL stosw xor ax,ax dec ax stosw ; set link value to -1 (unused) ; move my addr onto board location inside IA command ; es:di is already set ; PUSH DS ; save my DS MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; movsw movsw movsw POP DS ; get back DS of local data ; ; start the IA setup command, ; mov word ptr es:[SCB+SCOM],0100h ; do-command command mov dx,IOADDR add dx,IOCA ; CA out dx,al ; send it xor cx,cx ; timeout waitia: mov ax,word ptr es:[CCBPTR] ; get status word test ax,08000h ; is command complete? loopz waitia jnz iaok mov ax,-2 ; timeout problem pop es pop bp ret ; iaok: ; ; IA sent, setup all of the other data structures on the board ; start with xmit command descriptors ; mov si,offset TCB ; template for xmit mov di,TCBPTR ; where it goes on board mov cx,12 ; copies CB and BD for xmit rep movsw ; ; Set up frame and buffer descriptors, 30 each ; mov cx,30 ; # of FDs mov di,FDBASE ; base addr for FDs fdloop: xor ax,ax mov bx,di ; save pointer stosw ; clear status wd stosw ; clear EL field add bx,22 ; points to next one mov es:[di],bx ; put in link ptr inc di inc di dec ax stosw ; clear BD ptr to -1 add di,14 loop fdloop ; sub di,20 ; point back to last EL field mov ax,08000h ; end of list stosw ; put into last FD sub di,4 ; back to beginning of last FD mov LASTFD,di ; save the pointer mov word ptr es:[di+FDLINK],FDBASE ; make list circular, from last to first ; mov ax,BDBASE ; first BD mov word ptr es:[FDBASE+FDPTR],ax ; put it in the first FD frame ; ; now BDs mov cx,30 mov di,BDBASE ; start of BD area mov dx,BUFBASE ; start of buffer area bdloop: xor ax,ax mov bx,di ; save pointer stosw ; zero status field add bx,10 ; point to next record mov es:[di],bx ; put in link ptr inc di inc di mov es:[di],dx ; address of buffer, lo part inc di inc di stosw ; zero out high part mov ax,200 stosw ; store length field add dx,ax ; add in length of buffer, updates ptr loop bdloop ; sub di,2 ; back to last BD size field mov ax,08000h+200 ; end of list + 200 stosw ; mark end of list sub di,8 ; back to last BDLINK field mov ax,BDBASE stosw ; put link to beginning of list here sub di,4 ; back to beginning of last BD mov LASTBD,di ; save pointer to end of list ; ; minor detail, but important ; Change SCB command block pointer to setup for xmit commands ; = only commands needed when operational ; mov word ptr es:[SCB+SCBL],TCBPTR ; where xmit command is ; ; configure to connect to network ; mov dx,IOADDR add dx,IOENA ; enable network out dx,al ; any al value ; ; Start the RU, doesn't need CB, only SCB parms. ; command, to start receiving ; mov word ptr es:[SCB],0 ; clear status word mov word ptr es:[SCB+SRFA],FDBASE ; set to frame descriptors mov word ptr es:[SCB+SCOM],010h ; start RU mov dx,IOADDR inc dx ; issue CA out dx,al ; ; don't wait for response, we are done ; xor ax,ax POP ES POP BP RET M5ETOPEN ENDP ; ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; M5GETADD PROC FAR PUSH BP MOV BP,SP mov dx,[BP+X+6] ; get board's base io addr ; mov dx,IOADDR ; (temporary) PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready ; mov cx,6 CLD getonee: in al,dx ; get a byte of the EPROM address STOSB ; put it away inc dx ; next register loop getonee ; go back for rest xor ax,ax POP ES POP BP RET M5GETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down if necessary ; MICOM board never interrupts, so we can leave it running. ; Who cares, right? ; M5ETCLOS PROC FAR RET M5ETCLOS ENDP ; ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; M5XMIT PROC FAR PUSH BP MOV BP,SP push es mov ax,ubase mov es,ax ; base for board PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV DX,[BP+X+4] ; count of bytes MOV CX,DX ; save a copy, might be less than 60, ok CMP DX,60 ; minimum length for Ether JNB OKLEN MOV DX,60 ; make sure size at least 60 OKLEN: mov di,TBUFPTR ; start of xmit buffer ; ; check for previous xmit xwait: mov bx,word ptr es:[SCB+SCOM] ; is command zeroed yet? or bx,bx jnz xwait ; not there yet, wait for it ; ; move the data rep movsb ; copy into buffer ; ; put the correct size into the TDB ; or dx,08000h ; end of frame bit flag mov word ptr es:[TBDPTR],dx ; store it mov word ptr es:[TCBPTR],0 ; zero status wd mov word ptr es:[TCBPTR+TCOM],08004h; xmit command in TCB mov word ptr es:[SCB+SCOM],0100h ; execute command ; test bx,0100h ; suspended? ; jz nosus ; mov word ptr es:[SCB+SCOM],0400h ; stop command ;nosus: pop ds ; get back my ds mov dx,IOADDR inc dx out dx,al ; issue CA to get it going xor ax,ax pop es POP BP RET M5XMIT ENDP ; ; ;*********************************************************************** ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; The 3COM 3C501 version uses interrupts, so this routine is a NOP ; for this board. ; ; usage: recv(); ; ; M5RECV proc far push bp push es MOV AX,word ptr [BUFPT+2] ; buffer's ds MOV DI,BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. ; JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,BUFORG ; wrap to here MOV BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; Add 2 to allow for space to put the size value ; OKAYREAD: inc di inc di ; room for size word mov bx,FIRSTFD ; get addr of first FD in list mov dx,ubase ; base for board push ds mov ds,dx ; ; CKFRAME: mov ax,[bx] ; status word of frame test ax,08000h ; frame written? jnz IREADONE ; yes, read it in pop ds call rust ; restore receiver if necessary jmp STOPINT ; ; we have a frame, read it in ; IREADONE: test ax,02000h ; check frame OK bit jnz frameok ; ok, read it mov cx,ds pop ds call rust ; preserves cx, restart RU if necessary jmp freespace ; not OK, just free the frame frameok: mov si,[bx+FDPTR] ; get pointer to buffer descriptor copybuf: ; es:di is already set to receive packet mov dx,si ; save a copy of current BD ptr mov ax,[si] ; get status and count word for BD push ax ; save for EOF bit test ax,04000h ; is count field there? jnz okcount pop ax mov cx,ds pop ds jmp freespace ; free the frame, etc okcount: xor cx,cx mov cl,al ; 200 bytes is largest this can be mov si,[si+BDPTR] ; get offset of data rep movsb ; copy the bytes from this packet segment mov si,dx ; get back current BD ptr pop ax ; get back EOF bit test ax,08000h ; check bit jnz ptrupdate ; free the frame, no more data here mov si,[si+BDLINK] ; go to next BD in list jmp copybuf ; copy the next buffer ; ; ptrupdate: ; ; DI now contains updated value for BUFPT ; mov cx,ds ; save board segment pop ds mov bx,BUFPT ; get where size field for this packet goes mov ax,di ; where pointer is now sub ax,bx ; where is was then, difference is size MOV DX,BUFBIG ; total amount of stuff in buffer ADD DX,AX ; add in size of this packet MOV BUFBIG,DX ; after adding in current packet size dec ax dec ax ; account for length field mov [bx],ax ; put the accumulated size there MOV BUFPT,DI ; it is here, now ; freespace: ; ; we are done with the frame, do the list management ; cx=DS for board DS=DGROUP ; mov bx,FIRSTFD ; the frame we are working with mov di,LASTBD ; where end of BD list is now mov es,cx ; reload board segment mov si,es:[bx+FDPTR] ; first BD in frame list nextbd: mov cx,es:[si] ; count word for BD, EOF bit test cx,08000h ; EOF bit, if set, save si in lastbd jnz dolastbd mov word ptr es:[si],0 ; clear status word, EOF bit cmp si,LASTBD ; see if we are wrapping jz dolastbd ; yes, just undo it mov si,es:[si+BDLINK] ; follow link jmp nextbd dolastbd: mov LASTBD,si ; store last known BD mov word ptr es:[si+BDSIZE],08000h+200 ; end of list here mov word ptr es:[si],0 ; clear status word, EOF bit ; size field for not end of list mov word ptr es:[di+BDSIZE],200 ; remove old end-of-list ; ; update the FD list flags, new end-of-list ; mov di,LASTFD ; get old end-of-list mov word ptr es:[bx+FDEOL],08000h ; store new EOL mov word ptr es:[bx],0 ; clear status word for frame mov word ptr es:[di+FDEOL],0 ; zero old one mov LASTFD,bx ; update stored pointer mov si,es:[bx+FDLINK] ; where next fd is mov FIRSTFD,si ; store that info for next time ; mov bx,LASTBD ; last of the BD list ; mov ax,es:[bx+BDLINK] ; get link field from lastBD ; mov es:[si+FDPTR],ax ; store into next frame ; ; signs that something is actually happening - for debugging ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC ICNT ; MOV al,ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: ENDINT: pop es POP BP RET M5RECV ENDP rust proc near ; re-start receiver ; ; check to see if the receiver went off because of no resources ; and restart receiver if necessary ; ds=dgroup, no other requirements ; push es mov ax,ubase mov es,ax mov ax,es:[SCB] ; status word for SCB and ax,070h ; receiver status cmp al,020h ; receiver has no resources jnz hasres ; ; setup lists for starting the RU on the chip ; we know that there isn't anything in the buffer that we want ; mov bx,FIRSTFD ; get first FD on free list (assume free) mov word ptr es:[SCB+SRFA],bx ; put into SCB mov si,LASTBD ; pointer to a BD, end of chain mov ax,word ptr es:[si+BDLINK] ; pointer to next BD mov word ptr es:[bx+FDPTR],ax ; set to start of BDs ; ; ; Start the RU, doesn't need CB, only SCB parms. ; command, to start receiving again ; mov word ptr es:[SCB],0 ; clear status word mov word ptr es:[SCB+SCOM],010h ; start RU mov dx,IOADDR inc dx ; issue CA out dx,al hasres: pop es ret rust endp ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; M5ETUPDA PROC FAR PUSH ES MOV AX,word ptr [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened ALIVE: POP ES RET M5ETUPDA ENDP ENDPS END Ethernet size limit of 60 and handles them ; M5XMIT PROC FAR PUSH BP MOV BP,SP push es mov ax,ubase mov es,ax ; base for board PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,Asource/enet/netub.asm 644 144 13 32205 4267454616 10214 ; ; Driver for Ungermann-Bass (IBM) NIC board ; Tim Krauskopf ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;**************************************************************************** TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for interrupt-driven Ethernet I/O on the PC ; ; Tim Krauskopf ; National Center for Supercomputing Applications ; 9/1/87 Ungermann-Bass driver started, PC bus ; 4/88 Modified for combined - version 2.2 ; ; NAME NET INCLUDE DOS.MAC SETX ; ; Equates for controlling the UB board ; EADDR EQU 010h ; where EPROM address is RBUF EQU 04000h ; where receive buffers start TBUF EQU 07000h ; where transmit buffers start RPIDX EQU 02100h ; receive page index starts here ; ; Registers on UB board ; TXINIT EQU 02080h ; initiate transmit (read) TSAMSB EQU 02080h ; transmit high start address TSALSB EQU 02081h ; transmit low start address CLRPAV EQU 02081h ; Clear packet available (read) INTSTAT EQU 02082h ; interrupt and transmit status INTCTL EQU 02082h ; interrupt control EPPPAV EQU 02083h ; packet avail, and empty page ptr FPP EQU 02083h ; full page ptr, high bit clear=no interrupts ; ; EDLC registers ; TSTAT EQU 02180h ; transmit status, write ff to clear TMASK EQU 02181h ; transmit mask, which ints can occur RSTAT EQU 02182h ; rec status, write ff to clear RMASK EQU 02183h ; rec mask, which ints can occur TMODE EQU 02184h ; trans mode, high nyb=# of collisions ; enable loopback, write 00 ; disable loopback, write 02 RMODE EQU 02185h ; rec mode, see list ERESET EQU 02186h ; set 080h to reset, set 0 to clear MADDR EQU 02188h ; write my eaddr here,+1,+2,3,4,5 ; ; Transmit status that we are interested in ; TDONE EQU 01 ; transmission done (when set) TOK EQU 02 ; transmission done ok ; ; mask for the packet available bit ; PAV EQU 080h ; packet is available EPP EQU 07fh ; inverse of PAV ; ; Receive modes ; PROMI EQU 03 ; promiscuous receive ROFF EQU 00 ; don't receive LMULT EQU 01 ; limited multicasts, bcast, mine MULT EQU 02 ; mult, bcast, mine ; ; Data segment ; DSEG ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have ICNT DB 00h SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H LFPP DB 00h ; Full Page pointer DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off UBASE DW 0b800h ; base segment for Ungermann-Bass board (jumper set) ; ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC U1RECV,U1ETOPEN,U1ETCLOS,U1GETADD,U1XMIT,U1ETUPDA ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,address,ioaddr) ; char s[6]; ethernet address ; int irq; (unused, we don't use no interrupts) ; int address base mem address ; int ioaddr (unused for this board) io base address ; U1ETOPEN PROC FAR PUSH BP MOV BP,SP push es mov ax,[bp+X+6] ; parameter for mov UBASE,ax ; base address mov es,ax ; set to base address ; ; reset the board by ; reset board, won't work until I set it back mov byte ptr es:[ERESET],080h ; ; loopback ; xor ax,ax mov byte ptr es:[TMODE],al ; ; turn off receive mov byte ptr es:[RMODE],al mov al,byte ptr es:[CLRPAV] ; clear PAV ; ; clear interrupt control xor ax,ax mov byte ptr es:[INTCTL],al ; ; rmask mov byte ptr es:[RMASK],al ; ; tmask mov byte ptr es:[TMASK],al ; ; rstat mov byte ptr es:[RSTAT],0ffh ; clear status ; ; tstat mov byte ptr es:[TSTAT],0ffh ; clear status ; ; move my addr onto board PUSH DS ; save my DS MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; MOV CX,6 MOV di,MADDR ; get base i/o reg for setting address CLD REP MOVSB ; LOAD MY ADDR POP DS ; get back DS of local data ; ; write epp to fpp and clear PAV mov al,byte ptr es:[EPPPAV] and al,07fh ; mask PAV off mov byte ptr es:[FPP],al ; no ints, set fpp=epp mov al,byte ptr es:[CLRPAV] ; ; ; set board back on from reset, causes xmit to loopback mov es:[TSAMSB],0ff0fh ; set to zero length xmit mov byte ptr es:[ERESET],00 ; ; wait for it waitforit: mov al,byte ptr es:[INTSTAT] ; status of xmit test al,TDONE jz waitforit ; ; turn off loopback mov byte ptr es:[TMODE],0ah ; turn on transmit ; ; write epp to fpp and clear PAV mov al,byte ptr es:[EPPPAV] and al,07fh ; mask PAV off mov byte ptr es:[FPP],al ; no ints, set fpp=epp mov LFPP,al ; save copy of FPP mov al,byte ptr es:[CLRPAV] ; ; set to receive certain types of packets mov al,MULT ; which mode mov byte ptr es:[RMODE],al ; set into reg xor ax,ax POP ES POP BP RET U1ETOPEN ENDP ; ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; int address; segment base address ; U1GETADD PROC FAR PUSH BP MOV BP,SP mov dx,[bp+X+4] ; get board's base addr PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready MOV SI,EADDR ; get ether address ptr ; PUSH DS mov ds,dx ; put base addr in DS mov cx,6 CLD REP MOVSB POP DS POP ES POP BP RET U1GETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down ; U1ETCLOS PROC FAR RET U1ETCLOS ENDP ; ; ;************************************************************************ ; U1XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; U1XMIT PROC FAR PUSH BP MOV BP,SP push es mov ax,ubase mov es,ax ; base for board PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes MOV CX,AX ; save a copy, might be less than 60, ok CMP AX,60 ; minimum length for Ether JNB OKLEN MOV AX,60 ; make sure size at least 60 OKLEN: mov di,TBUF ; start of xmit buffer MOV BX,2047 ; total length of buffer-1 SUB BX,AX ; offset from beginning of buffer ADD DI,BX ; add in to get buffer pointer MOV BX,DI ; make a copy ; ; check for previous xmit xwait: mov al,byte ptr es:[INTSTAT] and al,3 ; check TXOK and TXDONE cmp al,3 jnz xwait ; not there yet, wait for it ; ; move the data rep movsb ; copy into buffer ; ; set address regs and send it mov byte ptr es:[TSTAT],0fh ; clear xmit status bits mov byte ptr es:[TSALSB],bl ; address regs mov byte ptr es:[TSAMSB],bh mov al,byte ptr es:[TXINIT] ; start xmit xor ax,ax pop ds pop es POP BP RET U1XMIT ENDP ; ; ;*********************************************************************** ; RECV ; Get whatever packets are on the board ; U1RECV proc far push bp push es MOV AX,WORD PTR [BUFPT+2] ; buffer's ds MOV DI,BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. ; JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,BUFORG ; wrap to here MOV BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; OKAYREAD: inc di inc di ; leave space for length of packet mov bl,LFPP ; local copy of FPP value xor bh,bh ; clear high byte mov dx,ubase ; base for board push ds mov ds,dx mov al,byte ptr ds:[EPPPAV] ; see if packet is available test al,PAV ; see if bit is set jnz IREADONE pop ds JMP STOPINT IREADONE: mov dx,0 ; size of packet is zero step2: mov cx,0 ; size of packet segment is zero mov si,bx ; save start of packet segment page # in si step3: push bx ; save LFPP value add bx,RPIDX ; get into page index mov al,byte ptr ds:[bx] ; get page index byte pop bx xor ah,ah push ax ; save end of packet marker and al,07fh ; mask off last packet bit inc al ; size is value+1 add cx,ax ; making total size of packet segment inc bl ; increment page pointer cmp bl,05fh jng nopwrap mov bl,0 ; wrap around at 60h (96) nopwrap: mov al,byte ptr ds:[EPPPAV] ; have we read all used pages? and al,EPP cmp al,bl ; is EPP = LFPP? jne notend mov al,byte ptr ds:[CLRPAV] ; clear packet available to make board contin. notend: pop ax ; get back end-of packet marker test al,080h ; is it end of packet? jnz dopack ; no, get next page of packet or bl,bl ; test to see if packet wraps around end jnz step3 ; keep adding to size dopack: ; move the packet into our local buffer ds:si to es:di ; cx is size, bl is page #, al is saving EOP mark, dx is accumulated size ; of packet push ax push bx ; save incremented bl mov bx,si ; get back saved value from before increment xor al,al ; clear low byte, load high byte mov ah,bl ; multiply *256, then divide by 2 = shr ax,1 ; multiply * 128 add ax,RBUF ; add offset of rec buffer mov si,ax ; this is where data resides add dx,cx ; add in to size of packet rep movsb ; copy the packet or portion of packet pop bx ; get back incremented bl mov byte ptr ds:[FPP],bl ; write full page pointer where it is now pop ax test al,080h ; was this the end of the packet? jz step2 ; if not, read next section of packet pop ds ; work with local data again ; ; ; DI now contains updated value for BUFPT, BX contains size of packet ; mov LFPP,bl ; store local fpp where we need it mov bx,BUFPT ; get where size field for this packet goes mov [bx],dx ; put the accumulated size there MOV BUFPT,DI ; IT IS HERE, NOW MOV AX,BUFBIG ; TOTAL AMOUNT OF STUFF IN BUFFER ADD AX,DX ; add in size of this packet INC AX INC AX ; TO COVER THE LENGTH VALUE MOV BUFBIG,AX ; AFTER ADDING IN CURRENT PACKET SIZE ; ; signs that something is actually happening ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC ICNT ; MOV al,ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: ENDINT: pop es POP BP RET U1RECV ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; U1ETUPDA PROC FAR PUSH ES MOV AX,WORD PTR [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened ALIVE: POP ES RET U1ETUPDA ENDP ENDPS END ints, set fpp=epp mov al,byte ptr es:[CLRPAV] ; ; ; set board back on from reset, causes xmit to loopback mov es:[TSAMSB],0ff0fh ; set to zero length xmit mov byte ptr es:[ERESET],00 ; ; wait for it waitforit: mov al,byte ptr es:[INTSTAT] ; status of xmit test al,TDONE jz waitforit ; ; turn off loopback mov byte ptr es:[TMODE],0ah ; turn on transmit ; ; write epp tsource/enet/net3com.asm 644 144 13 53204 4267454620 10444 ; 3COM 3C501 driver code ; Tim Krauskopf ; ; This version is virtually unused, replaced by the driver version ; which handles all of the different ioaddrs and interrupts. ; ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;**************************************************************************** ; TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for interrupt-driven Ethernet I/O on the PC ; ; Will read and write packets from the 2K packet buffer on the ; Etherlink card. Provides hooks for higher layer protocols. ; NAME NET INCLUDE DOS.MAC SETX ; ; Equates for controlling the 3COM board ; ICTRL EQU 020H ; 8259 interrupt control register IMASK EQU 021H ; 8259 interrupt mask register BASEA EQU 300H ; Base I/O address on PC I/O bus ENDOFI EQU 020H ; end-of-interrupt ; ; Controller registers ; EADDR EQU BASEA+0H ; Network address for hardware checking ; takes six bytes, this is the address that the Ethernet board will ; match to find packets on the net. (0-5h) ; EREC EQU BASEA+6H ; Receive status (read) ; Receive command (write) ESEND EQU BASEA+7H ; Transmit status (read) ; Transmit command (write) EGLOW EQU BASEA+8H ; General purpose pointer for R/W to ; packet buffer, low byte EGHI EQU BASEA+9H ; high byte, total of 11 bits for 2K buffer ERLOW EQU BASEA+0AH ; Receive pointer, set by board (read) low byte ; Receive buffer pointer clear (write) ERHI EQU BASEA+0BH ; high byte of Receive pointer EPROM EQU BASEA+0CH ; PROM window address ; DH ?? EAUX EQU BASEA+0EH ; Auxiliary Status (read) ; Aux Command (write) EBUF EQU BASEA+0FH ; Buffer window (where to I/O to net) ; ; Transmit command options ; what conditions we wish to be interrupted on after transmit ; EDTUNDER EQU 01H ; Detect underflow (bad CRC), not used EDTCOLL EQU 02H ; Detect collision on xmit EDTC16 EQU 04H ; Detect 16 consecutive collisions (net down) EDTOK EQU 08H ; Detect successful transmission ; other 4 bits unused in this reg EXMITNORM EQU 00H ; Normal use for interrupt-driven XMIT ; ; Transmit status results ; ; Use same values as commands, 08h means OK to xmit again ; ;***************************** ; Receive Command Options ; ; If something is not detected for, the receiver automatically discards ; those packets. ; EDTOVER EQU 01H ; Detect Overflow condition EDTFCS EQU 02H ; Detect FCS error, bad CRC on packet EDTDRB EQU 04H ; Detect dribble errors and accept them EDTSHORT EQU 08H ; Detect short frames (< 60 bytes) EDTEOF EQU 10H ; Detect no overflow (end-of-frame found) EGOOD EQU 20H ; Accept good frames ; four values legal for the last two bits: ECLOSE EQU 00H ; Turn off receiver EGETALL EQU 40H ; Get all packets, no matter what address EBROAD EQU 80H ; get those for me or for broadcast EMULTI EQU 0C0H ; get those for me or for multicast EWANT EQU 0A0h ; EGOOD OR EBROAD ; which packets we will look for on net ; ; Receive Status results ; ; errors are not detected unless asked for...otherwise the board just ; won't take bad packets off of the net. ; ERROVER EQU 01H ; overflow error ERRFCS EQU 02H ; FCS (checksum) error ERRDRB EQU 04H ; Dribble error ERRSHORT EQU 08H ; Short frame error ENOOVER EQU 10H ; Received without overflow error ; means that we didn't miss any by being slow ;EGOOD EQU 20H ; as above, we received a valid frame ; undefined 40h ESTALE EQU 80H ; stale receive condition, already read me ; ; Aux command register ; EIRE EQU 01H ; interrupt request enable (no DMA) new boards EBADFCS EQU 02H ; create bad checksum for testing only ; ; Next two bits tell who has access to packet buffer ; EBUS EQU 00H ; System bus has control of buffer EXMIT EQU 04H ; Transmit packet in buffer, auto kick ; back to recieve status EGETEM EQU 08H ; Receive state, look for packets ELOOP EQU 0CH ; Transmit and loopback into xmit buffer ; 10H unused EDMA EQU 20H ; Starts a DMA xfer ERIDE EQU 40H ; Interrupt and DMA enable ERESET EQU 80H ; Reset the Ethernet board ; ; Aux status register ; ERBUSY EQU 01H ; Receive busy, receiver looking for packets ; 02H ; echos command status EBADFCS ; 04,08h ; echos command status for EXMIT,EGETEM,EBUS EDMADONE EQU 10H ; goes to one when DMA finishes ; 20H ; echos DMA request bit ; 40h ; echos RIDE bit EXBUSY EQU 80H ; for polled xmit, goes to 0 when xmit is done ; ; ; Macros for in and out ; MOUT MACRO REG,STUFF ; one byte to the given I/O register MOV DX,REG MOV AL,STUFF OUT DX,AL ENDM ; MOUTW MACRO REG,LO,HI ; two bytes to the I/O double port MOV DX,REG MOV AL,LO OUT DX,AL INC DX MOV AL,HI OUT DX,AL ENDM ; MIN MACRO REG ; get one byte to al MOV DX,REG IN AL,DX ENDM DSEG ; PUBLIC STAT,BUFPT,BUFORG,BUFEND,BUFREAD,BUFBIG,BUFLIM,OFFS ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have ; ; ;STAT DB 00H ; last status from read ;BUFBIG DW 00H ; buffer space used ;BUFLIM DW 05000H ; buffer space limit ;BUFPT DW 00000H ; where buffer pointer is, initialized safely ;BUFDS DW 0a000H ; where buffer is, ds ;BUFORG DW 00000H ; start of buffer space ;BUFDS2 DW 0a000H ; another ds ;BUFEND DW 06000H ; end limit of allowable buffer space ;BUFDS3 DW 0a000H ;BUFREAD DW 00000H ; where the read pointer is ;BUFDS4 DW 0a000H SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H OLDMASK DB 00H ; save interrupt controller mask DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off ; ; use variables to access IRQ3 or IRQ5 ; 3 is COM2, 5 is LPT2 ; ;WHICHINT EQU 4*0Bh ; Interrupt for interrupt I/O on IRQ3 ;WHICHINT EQU 4*0Dh ; Interrupt for interrupt I/O on IRQ5 ;TURNOFF EQU 08H ; IRQ3 bit mask for 8259 controller (1<<3) ;TURNOFF EQU 020H ; IRQ5 bit mask for 8259 controller (1<<5) ;TURNON EQU 0F7H ; IRQ3 enable bit mask for 8259 controller ;TURNON EQU 0DFH ; IRQ5 enable bit mask for 8259 controller INTNUM DB 0BH ; Defaults to IRQ3, interrupt handler 0bh WHICHINT DW 4*0BH ; ETOPEN can change these values TURNOFF DB 08H TURNON DB 0F7H ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC E3RECV,E3ETOPEN,E3ETCLOS,E3GETADD PUBLIC E3SETADD,E3XMIT,E3ETUPDA ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,addr,ioaddr) ; char s[6]; ethernet address ; int irq,addr,ioaddr; ; interrupt number, base mem address (unused) and ; i/o address to use (currently unused) ; ; E3ETOPEN PROC FAR PUSH BP MOV BP,SP MOUT EAUX,ERESET ; reset the board MOUT EAUX,0 ; Clear the reset bit, otherwise keeps resetting ; ; check the parameters for interrupt and dma ; MOV AX,[BP+X+4] ; interrupt number CMP AL,5 ; If not 5, then use 3 JNZ USE3 MOV INTNUM,0DH ; Interrupt # for handler for IRQ5 MOV WHICHINT,4*0DH ; Interrupt handler location in vector table MOV TURNOFF,020H ; mask for interrupt controller for IRQ5 MOV TURNON,0DFH ; opposite mask, for interrupt controller USE3: MOV AX,[BP+X+6] ; dma channel to use CMP AL,3 ; if not 3, then use 1 JNZ USE1 ; ; Add DMA values for channel 3 here later ; probably never going to use DMA for this board - unreliable so far ; USE1: ; ; install the interrupt handler ; CALL IINST ; do the patching of the interrupt table ; ; set up the net address ; PUSH DS ; save mine MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; MOV CX,6 MOV DX,EADDR ; get base i/o reg for setting address CLD SADDR: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR ; do 6 times POP DS ; get back DS of local data ; ; enable interrupts here with interrupt handler ; already set up. ; MOUT ESEND,0 ; xmit command = 0 for no interrupts IN AL,DX MOUT EREC,EWANT ; Set receiver for which packets we want IN AL,DX ; reset 'stale' MOUT ERLOW,0 ; Clear the receive buffer pointer CLI MOUT EAUX,EGETEM+ERIDE ; Set for receive, interrupts MIN IMASK ; get current int enable mask MOV BL,AL ; save a copy AND AL,TURNON ; force bit for etherlink board off OUT DX,AL ; put back the byte, IRQ enabled STI AND BL,TURNOFF ; isolate this bit only from oldmask MOV OLDMASK,BL ; save it ; POP BP XOR AX,AX RET E3ETOPEN ENDP ; ;****************************************************************** ; SETADDR ; set the Ethernet address on the board to 6 byte ID code ; ; usage: setaddr(s,basea,ioa); ; char s[6]; ethernet address to use ; int basea; shared memory base address (unused) ; int ioa; io address for board (unused) ; E3SETADD PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; address of buffer to read ; MOV CX,6 MOV DX,EADDR ; get base i/o reg for setting address CLD SADDR2: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR2 ; do 6 times POP DS POP BP RET E3SETADD ENDP ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; int address; ; int ioaddr; (unused here) mem address and ioaddress to use ; E3GETADD PROC FAR PUSH BP MOV BP,SP PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready ; MOV BX,0 ; start location MOV CX,EPROM ; address window GADDR: CLD MOUTW EGLOW,BL,BH ; set gp to the right value MIN CX ; get value from prom address window STOSB ; put into given buffer INC BX ; next position CMP BX,6 JNZ GADDR ; do 6 times POP ES POP BP RET E3GETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down, remove the interrupt handler ; ; usage: etclose(); ; ; E3ETCLOS PROC FAR CLI MOUT EAUX,ERESET ; Turn off all pendings, cause reset MOUT EAUX,0 ; Turn off reset ; ; ; mask out IRQ on interrupt controller ; MIN IMASK ; get current mask OR AL,TURNOFF ; force that bit on OUT DX,AL ; send it back to controller STI CALL DEINST ; restore old interrupt handler MOV BL,OLDMASK ; get back saved setting of irq NOT BL ; flip it CLI MIN IMASK AND AL,BL ; restore setting of that bit OUT DX,AL STI RET E3ETCLOS ENDP ; ;************************************************************************ ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; The 3COM 3C501 version uses interrupts, so this routine is a NOP ; for this board. ; ; usage: recv(); ; E3RECV PROC FAR RET ; for compatibility with other drivers E3RECV ENDP ifdef unused_code ; This version is unused. It is a polled receive which is nearly ; useless with this board. It hasn't been debugged yet, either. ; char *where; at least 2048 bytes of room ; returns # bytes in packet, -1 if no packet available PUSH BP MOV BP,SP PUSH ES MOV AX,[BP+X+2] ; get new es value MOV ES,AX MOV DI,[BP+X] ; set di for later movement ; MOV CX,10 ; give it a few tries FINDONE: MIN EAUX ; get status to al MOV STAT,AL AND AL,ERBUSY ; is it still in receive state or done? JZ READONE ; done, can read it LOOP FINDONE MOV AX,-1 ; no packet yet, return POP ES POP BP RET ; READONE: MOUT EAUX,EBUS ; turn off receive, give buffer to bus MOUTW EGLOW,0,0 ; clear general purpose pointer for read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length MOV DX,EBUF ; window to the data DOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP DOBYTES MIN EREC ; get status to al, clears read MOV STAT,AL ; KEEP LAST STATUS BYTE ; ; set up to read the next packet from the net ; MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux MOV AX,BX ; return value is # of bytes POP ES POP BP RET E3RECV ENDP endif ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; E3XMIT PROC FAR PUSH BP MOV BP,SP PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes MOV CX,AX ; save a copy, might be less than 60, ok CMP AX,60 ; minimum length for Ether JNB OKLEN MOV AX,60 ; make sure size at least 60 OKLEN: MOV BX,2048 ; total length of buffer SUB BX,AX ; offset of for buffer pointer to start MOV DI,BX ; save a copy of the buffer pointer ; ; TAKE CONTROL OF THE INPUT BUFFER ; MOUT EAUX,EBUS+ERIDE ; take buffer away from receiver MOUT ERLOW,0 ; clear receive pointer for next read MOUTW EGLOW,BL,BH ; set the general purpose pointer MOV DX,EBUF ; window to packet buffer CLD FILLBUF: LODSB ; get value to go into buffer OUT DX,AL ; put it into buffer (autoincrement) LOOP FILLBUF ; do whole count POP DS ; ; packet is in buffer, ready to be sent ; TRYAGAIN: MOV BX,DI ; retrieve copy of offset pointer MOUTW EGLOW,BL,BH ; set the general purpose pointer (again) ; MOUT EAUX,EXMIT+ERIDE ; tell the board to send it and start receiving NOTDONEX: MIN EAUX ; waiting for transmit to finish AND AL,EXBUSY ; is it done yet? JNZ NOTDONEX ; no, wait some more MOV CX,0 ; return value, ok MIN ESEND ; get xmit status MOV BL,AL ; save status AND AL,EDTOK ; was it ok? JNZ DONEX ; yes, successful xmit ; ; handle the possible errors, return 1 on coll16 ; coll16 generally means that the network has failed ; MOV AL,BL ; get copy of status back AND AL,EDTC16 ; check collision 16 JNZ RET16 ; yes, network probably down MOV AL,BL ; get copy back again AND AL,EDTCOLL ; check for collision status JZ UNK ; no, unknown problem MOUT EAUX,EBUS+ERIDE ; collision, reset buffer control JMP TRYAGAIN ; go for it UNK: MOV CX,2 ; unknown problem return code JMP DONEX RET16: MOV CX,1 ; failure return DONEX: MOUT EREC,EWANT ; reset receive register filter necessary MIN EAUX AND AL,ERBUSY ; is it still in receive state or done? JNZ DONEMIT ; not ready now, return instead MOV AL,INTNUM CMP AL,0BH ; two choices of int to call JNZ TRYNINT INT 0BH ; we do have a packet, read it JMP DONEMIT TRYNINT: CMP AL,0DH JNZ DONEMIT INT 0DH DONEMIT: MOV AX,CX ; put return in ax POP BP RET E3XMIT ENDP ; ;************************************************************************* ; Interrupt Handler ; installation and deinstallation ; ; the handler takes the receive packet out of the input buffer ; DEINST PROC NEAR MOV CX,SAVEIP ; get old ip from save spot MOV DX,SAVECS ; get old cs from save spot MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX CLI MOV [BX],CX ; store old ip into the table INC BX INC BX ; move pointer in interrupt table MOV [BX],DX ; store old cs into the table STI POP DS RET DEINST ENDP ; IINST PROC NEAR MOV CS:MYDS,DS ; store for use by handler MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX MOV AX,OFFSET IHAND ; where the handler is CLI MOV DX,[BX] ; keep copy of the ip MOV [BX],AX ; store ip into the table INC BX INC BX ; move pointer in interrupt table MOV CX,[BX] ; keep copy of the cs, too MOV AX,CS MOV [BX],AX ; store new cs into the table STI POP DS MOV SAVEIP,DX ; store them away MOV SAVECS,CX RET MYDS DW 00H ; the data segment for this assembly code ICNT DB 00H IHAND: ; not a public name, only handles ints STI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI CLD ; all moves will be forward ; ; SET UP CORRECT DS ; MOV DS,CS:MYDS ; get correct ds MOV AX,word ptr [BUFPT+2] ; buffer's ds MOV DI,BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. turn off receiver ; MOUT EAUX,EBUS+ERIDE ; refuse to read more packets until restarted MIN EREC ; must clear interrupt MOV AL,1 ; set flag MOV DEAF,AL ; we are now deaf, read routine must restart JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,BUFORG ; wrap to here MOV BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; OKAYREAD: ; MOV CX,10 ; give it a few tries IFINDONE: ; MIN EAUX ; get status to al ; AND AL,ERBUSY ; is it still in receive state or done? ; JZ IREADONE ; done, can read it ; LOOP IFINDONE ; MIN EREC ; AND AL,ESTALE ; JZ IREADONE ; jmp ireadone ; MOV AX,0 ; no packet yet, spurious int, return ; STOSB ; MIN EREC ; clear interrupt condition ; MIN ESEND ; in case it was an xmit spurious int ; JMP STOPINT ; IREADONE: MOUT EAUX,EBUS+ERIDE ; turn off receive, give buffer to bus MOUTW EGLOW,0,0 ; clear general purpose pointer for read MIN EREC ; get status to al, clears read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length OR BX,BX ; check for non-zero JZ STOPINT ; no packet MOV AX,BX ; save length in buffer, before packet STOSW MOV DX,EBUF ; window to the data IDOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP IDOBYTES ; ; ; DI now contains updated value for BUFPT, BX contains size of packet ; MOV BUFPT,DI ; it is here, now MOV AX,BUFBIG ; total amount of stuff in buffer ADD AX,BX INC AX INC AX ; to cover the length value MOV BUFBIG,AX ; after adding in current packet size ; ; signs that something is actually happening - used for debugging ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC CS:ICNT ; MOV Al,CS:ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux ENDINT: MOUT ICTRL,ENDOFI ; signal end of interrupt POP DI POP DX POP CX POP BX POP AX POP ES POP DS IRET IINST ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; ; usage: etupdate(); ; E3ETUPDA PROC FAR PUSH ES MOV AX,word ptr [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; two more for length value ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened MOUT ERLOW,0 ; reset receive buffer ptr MOUT EAUX,EGETEM+ERIDE ; turn on receiver ALIVE: POP ES RET E3ETUPDA ENDP ENDPS END t) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; E3XMIT PROC FAR PUSH BP MOV BP,SP PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[Bsource/enet/netzyp.asm 644 144 13 27706 4267454622 10437 ; Driver for Ungermann-Bass NICps/2 Zypher interface ; Tim Krauskopf ; ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;**************************************************************************** TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Tim Krauskopf ; National Center for Supercomputing Applications ; 9/1/87 Ungermann-Bass driver started, PC bus ; 12/28/87 UB NIC driver started, Zypher interface ; ; NAME NET INCLUDE DOS.MAC INCLUDE ZYPDEFS.INC SETX ; ; ; Data segment ; DSEG ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H LFPP DB 00h ; Full Page pointer DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off ; ; ; Zypher definitions to work with ; Init_ZCB LABEL WORD db Initialize_Cmd db 0 ; Status db 0 ; Result db 0 ; Report_Code dw 0 ; Options dd 0 ; Post_Routine dw 2 dup (0) dw 0 ; Modes dw 1100 ; Max_Xmt_Length dw 1 ; Num_Xmt_Buffers dw 1514 ; Max_Rcv_Size dw 12 ; Num_Rcv_Buffers dw 0 ; Max_Multicast_Addresses dw 7 dup (0) ; reserved dw 0 ; Acquired Modes dw 0 ; Acquired Max_Xmt_Length dw 0 ; Acquired Num_Xmt_Buffers dw 0 ; Acquired Max_Rcv_Size dw 0 ; Acquired Num_Rcv_Buffers dw 0 ; Acquired Max_Multicast_Addresses dw 3 dup (0) ; reserved Stat_ZCB LABEL WORD db Status_Cmd db 0 ; Status db 0 ; Result db 0 ; Report_Code dw 0 ; Options dd 0 ; Post_Routine dw 2 dup (0) dw 0 ; state dw 0 ; modes dw 0 ; Max_Xmt_Length dw 0 ; Act Num_Xmt_Buffers dw 0 ; Act Max_Rcv_Size dw 0 ; Act Num_Rcv_Buffers db 6 dup (0) ; unique ID dw 0,0 ; total xmts dw 0,0 ; total rcvs dw 0,0 ; CRC errors dw 0,0 ; ALN errors dw 0,0 ; RSC errors dw 0,0 ; OVR errors dw 12 dup (0) ; reserved Xmt_ZCB LABEL WORD db Transmit_Cmd db 0 ; Status db 0 ; Result db 0 ; Report_Code dw 0 ; Options dd 0 dw 2 dup (0) dw 0 ; Xmt_Length dw 0,0 ; Xmt_Buffer dw 0 ; hardware status db 0,0 ; Xmt_Bfr_ID and an unused byte dw 0,0 ; Xmt_Bfr_Address (on-card transmit buffer address) Cancel_ZCB LABEL WORD db Cancel_Receives_Cmd db 0 ; Status db 0 ; Result db 0 ; Report_Code dw 0 ; Options dd 0 ; Post_Routine dw 2 dup (0) Recv_ZCB LABEL WORD db Receive_Cmd db 0 ; Status db 0 ; Result db 0 ; Report_Code dw 0 ; Options dd 0 dw 2 dup (0) ; db SIZE ZCB_Header dup (0) ;ZCB_Rcv_Mode db 0 db 0 ;ZCB_Rcv_Status db 0 db 0 ;ZCB_Rcv_Buffer_Size dw 0 ; Size of user's buffer. dw 1514 ; always this much room ;ZCB_Rcv_Buffer_Address dd 0 ; Address of user's buffer. dd 0 ;ZCB_Rcv_Data_Length dw 0 ; Bytes copied to user's buffer. dw 0 ;ZCB_Rcv_Frame_Count dw 0 ; Count of as-yet-uncopied bytes left; dw 0 ; in frame. ;ZCB_Rcv_Hdwr_Status dw 0 ; Status reported by 82586. dw 0 ;ZCB_Rcv_Frame_ID dw 0 ; Frame ID for "incremental mode". dw 0 ;ZCB_Rcv_Bfr_Ptr dw 0,0 ; Address of next as-yet-uncopied byte dw 0,0 ; of frame in on-card receive buffer. ;ZCB_Rcv_Bfr_Count dw 0 ; Count of as-yet-uncopied bytes in dw 0 ; current on-card receive buffer. ;ZCB_Rcv_Descriptor dw 0,0 ; Address of 82586 RBD (Receive Buffer dw 0,0 ; Descriptor) for current on-card ; receive buffer. ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC U2RECV,U2ETOPEN,U2ETCLOS,U2GETADD,U2XMIT,U2ETUPDA ZYP_Entry LABEL DWORD dw 0, 0D000h ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,addr,ioaddr) ; char s[6]; ethernet address ; int irq,addr,ioaddr; ; interrupt number, base mem address and ; i/o address to use ; ; U2ETOPEN PROC FAR PUSH BP MOV BP,SP push es mov ax,ds mov es,ax ; set to base address for NCB ; ; Set base address for board into ZYP_Entry ; mov ax,[X+BP+6] ; base address for board as installed mov word ptr cs:[ZYP_Entry+2],ax ; store ; ; MOV BX,offset Init_ZCB CALL ZYP_Entry Init_wait: CMP [BX].ZCB_Status,0FFh JE Init_wait ; ; call ZYP receive ; mov bx,offset Recv_ZCB mov ax,word ptr [BUFPT+2] ; where packet should arrive mov di,word ptr [BUFPT] inc di inc di ; address of packet into NCB mov word ptr [bx].ZCB_Rcv_Buffer_Address,di mov word ptr [bx].ZCB_Rcv_Buffer_Address+2,ax CALL ZYP_Entry xor ax,ax getout: POP ES POP BP RET U2ETOPEN ENDP ; ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; int address; ; int ioaddr; mem address and ioaddress to use ; ; U2GETADD PROC FAR PUSH BP MOV BP,SP PUSH ES ; save mine mov ax,ds mov es,ax ; ; Set base address for board into ZYP_Entry ; mov ax,[X+BP+4] ; base address for board as installed mov word ptr cs:[ZYP_Entry+2],ax ; store ; ; MOV BX,offset Init_ZCB CALL ZYP_Entry Init_wt: CMP [BX].ZCB_Status,0FFh JE Init_wt ; CMP [BX].ZCB_Result,Initialization_Complete ; JNE oh_well MOV BX,offset Stat_ZCB CALL ZYP_Entry addr_wait: CMP [BX].ZCB_Status,0FFh JE addr_wait CMP [BX].ZCB_Result,Status_Complete JE get_addr oh_well: mov ax,-1 jmp nomore get_addr: MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready MOV SI,BX add si,ZCB_Stat_Unique_ID ; mov cx,3 CLD rep movsw ; copy address xor ax,ax nomore: POP ES POP BP RET U2GETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down ; U2ETCLOS PROC FAR push bp push es mov ax,ds mov es,ax MOV BX,offset Cancel_ZCB CALL ZYP_Entry canwait: CMP [BX].ZCB_Status,0FFh JE canwait pop es pop bp RET U2ETCLOS ENDP ; ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; U2XMIT PROC FAR PUSH BP MOV BP,SP push es MOV CX,[BP+X+2] ; ds for buffer MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes cmp ax,1100 jle oklen mov ax,1100 ; maximum for me oklen: ; ; place into Xmit parms ; MOV BX,offset Xmt_ZCB ; Start the 1st transmit, using mov word ptr [bx].ZCB_Xmt_Data_Address,si mov word ptr [bx].ZCB_Xmt_Data_Address+2,CX mov [bx].ZCB_Xmt_Data_Length,ax mov ax,ds mov es,ax ; base for ZCB block CALL ZYP_Entry waitstat: CMP [BX].ZCB_Status,0FFh JE waitstat xor ax,ax ; CMP [BX].ZCB_Result,Initialization_Complete ; JE xmitok ; mov al,[BX].ZCB_Result xmitok: ; ; signs that something is actually happening ; ; push ax ; MOV AX,0B800H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC ICNT ; MOV al,ICNT ; character ; STOSB ; pop ax pop es POP BP RET U2XMIT ENDP ; ; ;*********************************************************************** ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; usage: recv(); ; ; U2RECV proc far push bp push es ; mov bx,offset Recv_ZCB CMP [BX].ZCB_Status,0FFh ; status byte for outstanding read request JNE newpkt ; ; no packet yet, skip ; pop es pop bp ret ; no packet arrival yet newpkt: mov dx,[bx].ZCB_Rcv_Data_Length ; length of recieved packet mov bx,word ptr [BUFPT] ; get where size field for this packet goes mov [bx],dx ; put the accumulated size there add bx,dx ; add length to bufpt inc bx inc bx MOV word ptr [BUFPT],bx ; it is here, now MOV AX,word ptr [BUFBIG] ; total amount of stuff in buffer ADD AX,DX ; add in size of this packet INC AX INC AX ; to cover the length value MOV word ptr [BUFBIG],AX ; after adding in current packet size ; ; signs that something is actually happening ; ; MOV AX,0B800H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC ICNT ; MOV al,ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; ; ; get ready for next packet ; MOV aX,word ptr [BUFPT+2] ; buffer's ds MOV DI,word ptr [BUFPT] ; where buffer is mov es,ax ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. ; JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,word ptr [BUFEND] ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,word ptr [BUFORG] ; wrap to here MOV word ptr [BUFPT],AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; OKAYREAD: inc di inc di ; leave space for length of packet ; ; ; call receive again ; mov bx,offset Recv_ZCB ; address of packet into NCB mov word ptr [bx].ZCB_Rcv_Buffer_Address,di mov word ptr [bx].ZCB_Rcv_Buffer_Address+2,es mov ax,ds mov es,ax CALL ZYP_Entry STOPINT: ENDINT: pop es POP BP RET U2RECV ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; U2ETUPDA PROC FAR PUSH ES MOV AX,word ptr [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,word ptr [BUFREAD] ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,word ptr [BUFEND] ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened ALIVE: POP ES RET U2ETUPDA ENDP ; ENDPS END eported by 82586. dw 0 ;ZCB_Rcv_Frame_ID dw 0 ; Frame ID source/enet/net8003.asm 644 144 13 35737 4267454623 10213 ; WD8003E driver code ; Tim Krauskopf ;**************************************************************************** ;* * ;* * ;* part of NCSA Telnet * ;* by Tim Krauskopf, VT100 by Gaige Paulsen, Tek by Aaron Contorer * ;* * ;* National Center for Supercomputing Applications * ;* 152 Computing Applications Building * ;* 605 E. Springfield Ave. * ;* Champaign, IL 61820 * ;* * ;* * ;**************************************************************************** ; TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for interrupt-driven Ethernet I/O on the PC ; ; Reads and writes from the 8K buffer on the WD card. ; Started 4/11/88 ; NAME NET8003 INCLUDE DOS.MAC INCLUDE NET8003.INC SETX ; ; macros for writing to WD board ; ;*********************************************************************** ; ; Macros, from example driver ; ;*********************************************************************** ; ; MACRO rd_wd8 ; Reads port specified on macro call. Leaves byte in AL. ; rd_wd8 MACRO port mov DX, WDBASE add DX, port ; DX contains address of port in AL, DX ; AL contains data read from port ENDM ; ; MACRO wr_wd8 ; Writes byte in AL to port specified on macro call. ; wr_wd8 MACRO port mov DX, WDBASE add DX, port ; DX contains address of port out DX, AL ; AL contains data to be written to port ENDM ; DSEG ; PUBLIC STAT,BUFPT,BUFORG,BUFEND,BUFREAD,BUFBIG,BUFLIM,OFFS ; ; The pointers below are actually DWORDs but we access them two ; bytes at a time. ; EXTRN STAT:BYTE ; last status from read EXTRN BUFPT:WORD ; current buffer pointer EXTRN BUFORG:WORD ; pointer to beginning of buffer EXTRN BUFEND:WORD ; pointer to end of buffer EXTRN BUFREAD:WORD ; pointer to where program is reading EXTRN BUFBIG:WORD ; integer, how many bytes we have EXTRN BUFLIM:WORD ; integer, max bytes we can have ; ; ;STAT DB 00H ; last status from read ;BUFBIG DW 00H ; buffer space used ;BUFLIM DW 05000H ; buffer space limit ;BUFPT DW 00000H ; where buffer pointer is, initialized safely ;BUFDS DW 0a000H ; where buffer is, ds ;BUFORG DW 00000H ; start of buffer space ;BUFDS2 DW 0a000H ; another ds ;BUFEND DW 06000H ; end limit of allowable buffer space ;BUFDS3 DW 0a000H ;BUFREAD DW 00000H ; where the read pointer is ;BUFDS4 DW 0a000H WDBASE DW 00h ; base ioaddr WDADD DW 00h ; base shared mem addr DEAF DB 00H ; when we can't handle any more packets OFFS DW 00H ; how many times the handler was turned off ; ENDDS ; ; ; ; The subroutines to call from C ; PSEG PUBLIC WDRECV,WDETOPEN,WDETCLOS,WDGETADD PUBLIC WDSETADD,WDXMIT,WDETUPDA ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,addr,ioaddr) ; char s[6]; ethernet address ; int irq,addr,ioaddr; ; interrupt number (unused), base mem address and ; i/o address to use ; WDETOPEN PROC FAR PUSH BP MOV BP,SP mov AX,[BP+X+8] ; install ioaddr mov WDBASE,AX mov AX,[BP+X+6] ; install shared mem addr mov WDADD,AX ; ; put shared mem address into memory select register ; mov cl,9 shr ax,cl ; adapt for MSR reg wr_wd8 0 ; ship it to offset 0 (MSR) ; ; portions adapted from WD8STAR2.ASM example driver ; Initializations as recommended by manufacturer and National Semi ; cld ; Clear direction flag for movs... ; ; initial the LAN Controller register ; ; program for page 0 mov AL, MSK_PG0 + MSK_RD2 wr_wd8 CMDR ; initial DCR data configuration mov AL, MSK_BMS + MSK_FT10 ; select FIFO threshold = 8 bytes wr_wd8 DCR ; clr RBCR0,1 xor AL, AL wr_wd8 RBCR0 wr_wd8 RBCR1 ; initial RCR to monitor mode mov AL, MSK_MON wr_wd8 RCR ; disable the rxer ; initial TCR xor AL, AL wr_wd8 TCR ; normal operation ; initial rev buffer ring mov AL, STOP_PG wr_wd8 PSTOP ; init PSTOP mov AL, STRT_PG wr_wd8 PSTART ; init PSTART to the 1st page of ring wr_wd8 BNRY ; init BNRY ; clr ISR by 1's mov AL, -1 ; write FF wr_wd8 ISR ; initial IMR mov AL, 00h ; ***NCSA Telnet does not ; ***need interrupts on wr_wd8 IMR ; enable interrupt ; program for page 1 mov AL, MSK_PG1 + MSK_RD2 wr_wd8 CMDR ; initial physical addr mov DX, WDBASE ; get board io base push DS mov ax,[bp+X+2] ; get seg from parms mov ds,ax mov CX, BPNA ; should be 6 for Ethernet mov BX, [BP+X] ; ptr to adr in BX add DX, PAR0 ; i/o address of PAR0 in DX lopa: mov AL, [BX] ; get 1 byte into AL out DX, AL ; write to PAR inc BX inc DX loop lopa pop DS ; initial multicast filter, write all 0's into MAR0 - MAR7 mov CX, 8 mov DX, WDBASE add DX, MAR0 ; i/o address of MAR0 in DX xor AL, AL lopb: out DX, AL inc DX loop lopb ; initial CURR = PSTART + 1 mov AL, STRT_PG + 1 wr_wd8 CURR ; program for page 0 mov AL, MSK_PG0 + MSK_RD2 wr_wd8 CMDR ; put 8390 on line mov AL, MSK_STA + MSK_RD2 ; activate 8390 wr_wd8 CMDR ; program RCR to normal operation (MSK_AB, no MSK_AM) mov AL, MSK_AB ; accept broadcast wr_wd8 RCR ; XOR AX,AX POP BP RET WDETOPEN ENDP ; ;****************************************************************** ; SETADDR ; set the Ethernet address on the board to 6 byte ID code ; ; usage: setaddr(s,basea,ioa); ; char s[6]; ethernet address to use ; int basea; shared memory base address ; int ioa; io address for board ; WDSETADD PROC FAR PUSH BP MOV BP,SP ; ; not used for this board, set during etopen ; POP BP RET WDSETADD ENDP ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s,address,ioaddr); ; char s[6]; will get six bytes from the PROM ; int address; ; int ioaddr; mem address and ioaddress to use ; WDGETADD PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] ; SEG of where to put info MOV DS,AX MOV BX,[BP+X] ; address of where to put info mov cx,6 mov dx,[BP+X+6] ; ioaddr for board, offset 0 for PROM addr getloop: in al,dx mov [bx],al ; store where we want inc dx inc bx loop getloop ; in al,dx cmp al,3 ; verification of board's existence XOR AX,AX jz noerr ; compare went ok mov ax,-1 ; error return noerr: POP DS POP BP RET WDGETADD ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down, remove the interrupt handler ; ; usage: etclose(); ; ; WDETCLOS PROC FAR RET WDETCLOS ENDP ; ;************************************************************************ ; Receive ; This is a CPU hook for boards that must be polled before we can ; deliver packets into the receive buffer. (i.e. no interrupts used) ; ; usage: recv(); ; WDRECV PROC FAR push bp push es ; ; check for data which can be read ; mov AL, MSK_PG1 + MSK_RD2 ; read CURR reg wr_wd8 CMDR rd_wd8 CURR mov BL, AL ; CURR in BL mov AL, MSK_PG0 + MSK_RD2 ; read BNRY reg wr_wd8 CMDR rd_wd8 BNRY ; BNRY in AL add AL, 1 ; start page of frm in AL cmp AL, STOP_PG ; check boundary jne go_cmp mov AL, STRT_PG go_cmp: cmp AL, BL jne gotone jmp end_rx ; buff ring empty gotone: ; ring not empty mov BH, AL xor BL, BL ; BX has the rx_frm pointer push BX ; save the frm ptr mov AX, WDADD ; shared mem base mov ES, AX ; ES has the shr seg paragraph mov AL, ES:[BX] ; AL has the status byte test AL, SMK_PRX ; if rx good jnz readit jmp fd_bnry ; rx error, drop frm by forward bnry readit: ; ; set up to read the next packet from the net ; ; ; get ready for next packet ; cld ; moves in fwd dir ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,BUFBIG ; how much stuff is in buffer MOV BX,BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. ; JMP fd_bnry ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DI,word ptr [BUFPT] ; where buffer is MOV DX,word ptr [BUFEND] ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,word ptr [BUFORG] ; wrap to here MOV word ptr [BUFPT],AX ; wrap-around MOV DI,AX ; di also OKAYREAD: ; ; ; start the copy of the new packet ; pointer to the shared memory offset is in BX ; At this offset, you will find: ; 1 byte - read status, usually 21h ; 1 byte - pointer, page # of next packet ; 2 bytes - length of data in packet, swapped for you already ; n bytes - that many bytes of Ethernet packet spread ; over n div 256 pages (allowing four lost bytes in first packet) ; ; pop si ; get packet pointer back into si push si ; restore for fd_bnry to read ; ; save regs while moving packet to buffer ; set up ds for buffer, even though we switch it later ; push es push ds MOV AX,word ptr [BUFPT+2] ; buffer's ds mov ds,ax ; ; here, DS:DI contains where we want to put the packet. ; newpkt: add si,2 ; offset for length field mov dx,es:[si] ; value of length of recd packet mov [di],dx ; put the accumulated size there inc si inc si inc di inc di ; now it is the data pointer ; ; ; Actually move the data ; DX has packet size in bytes ; ES:SI has the source pointer } need to switch ; DS:DI has the dest pointer } es and ds ; Remember, 256 byte pages wrap around at STOP_PG and there ; are max 252 bytes in the first page ; mov cx,dx cmp cx,252 jng shrt mov cx,252 ; first page len shrt: mov ax,ds mov bx,es mov ds,bx mov es,ax ; swap them mov bx,dx ; save a copy of data length mvpg: ; start of page move loop sub dx,cx shr cx,1 ; convert to words jnc iseven movsb ; move odd one if needed iseven: rep movsw ; move all words in one page cmp dx,0 ; how many left to move? jng donepg mov cx,dx cmp cx,256 jng shrtr mov cx,256 ; one more page shrtr: mov ax,si ; look at source page cmp ah,STOP_PG jl mvpg mov ah,STRT_PG ; wrap around at this page boundary mov si,ax ; put back in si for rest of packet jmp mvpg donepg: pop ds pop es ; put regs back so ES is shared mem ; ; update the pointer and length in the buffer ; DI already points just past end of data just placed there ; MOV word ptr [BUFPT],di ; it is here, now MOV AX,word ptr [BUFBIG] ; total amount of stuff in buffer ADD AX,BX ; add in size of this packet INC AX INC AX ; to cover the length value MOV word ptr [BUFBIG],AX ; after adding in current packet size ; ; ; signs that something is actually happening ; ; push es ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC cs:ICNT ; MOV al,cs:ICNT ; character ; STOSB ; pop es ; ; drop bad frame by forwarding the BNRY register ; or just normal BNRY update after frame read ; fd_bnry: ; drop frm by forward BNRY pop BX ; restore frm ptr in BX add BX, 1 mov AL, ES:[BX] ; next frm start page in AL sub AL, 1 ; new BNRY in AL cmp AL, STRT_PG ; check boundary jge wrbnry mov AL, STOP_PG - 1 wrbnry: wr_wd8 BNRY end_rx: pop es POP BP RET ; for compatibility with other drivers ICNT db 0 WDRECV ENDP ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; WDXMIT PROC FAR PUSH BP MOV BP,SP cld push es PUSH DS ; set up proper ds for the buffer ; mov dx,WDADD ; shared memory address in dx mov es,dx ; use es for this ; ; ; move packet into position, set up regs ; MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes MOV CX,AX ; save a copy, might be less than 60, ok CMP AX,60 ; minimum length for Ether JNB OKLEN MOV AX,60 ; make sure size at least 60 OKLEN: ; ; Copy packet into transmit buffer ; xmit buffer starts at offset zero in shared mem ; push ax ; xmit size here, CX has data size mov di,0 ; set di to start of xmit buffer, 0 page shr cx,1 jnc evenx movsb evenx: rep movsw ; copy all data into xmit buf ; ; set up xmit length registers ; pop ax pop ds ; get back DS for wr_wd8 macro ; set up TBCR0,1 wr_wd8 TBCR0 ; lower byte to TBCR0 mov AL, AH wr_wd8 TBCR1 ; higher byte to TBCR1 ; set page number to page 0 ; xor al,al ; page number ; set up TPSR wr_wd8 TPSR ; write start page into TPSR ; issue tx command mov AL, MSK_TXP + MSK_RD2 wr_wd8 CMDR ; start xmit ; ; ; check to see if the last packet xmitted ok ; xor cx,cx waitxmit: rd_wd8 CMDR ; command register test al,MSK_TXP ; xmit bit jz oktogo ; xmit is finished loop waitxmit ; waiting for xmit to complete mov ax,-1 jmp getout oktogo: xor ax,ax ; ; go back for more ; getout: pop es POP BP RET WDXMIT ENDP ; ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; ; usage: etupdate(); ; WDETUPDA PROC FAR PUSH ES MOV AX,word ptr [BUFPT+2] ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; two more for length value ADD BX,DX ; increment bufread by size of packet MOV CX,BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,BUFORG ; wrap to here NOWRAPRD: MOV BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC OFFS ; keep count how many times this happened ; ; turn receiver back on ; ALIVE: POP ES RET WDETUPDA ENDP ENDPS END sage: etclose(); ; ; WDETCLOS PRsource/enet/net8003.inc 644 144 13 22602 4267454625 10171 SUBTTL WD8STAR2.EQU - StarLAN equates COMMENT \ /************************************************************************ * WD8STAR2.EQU * * * * Provides constants and data definitions for WD8STAR2.ASM file * * ( For WD8003 StarLAN PC Adapter Board ). * * * * CONTENTS: * * -------- * * external definition * * I/O port offset definition * * I/O register mask definition * * miscellaneous constants definition * * structure definition * * * * NOTE: * * ---- * * variable definition and reserved patch area are in asm file now * * * * HISTORY: * * ------- * * * * * *************************************************************************\ ;***************************************************************************** ; ; StarLAN controller board offsets ; IO port definition (BASE in WD8_base) ;***************************************************************************** W83CREG EQU 00h ; 8003 control register W83CREG EQU 00h ; 8003 status register ADDROM EQU 08h ; LAN Address ROM ; 8390 LAN Controller (page0) register offset for read and write CMDR EQU 10h ; command register for read & write CLDA0 EQU 11h ; current local dma addr 0 for read PSTART EQU 11h ; page start register for write CLDA1 EQU 12h ; current local dma addr 1 for read PSTOP EQU 12h ; page stop register for write BNRY EQU 13h ; boundary reg for rd and wr TSR EQU 14h ; tx status reg for rd TPSR EQU 14h ; tx start page start reg for wr NCR EQU 15h ; number of collision reg for rd TBCR0 EQU 15h ; tx byte count 0 reg for wr FIFO EQU 16h ; FIFO for rd TBCR1 EQU 16h ; tx byte count 1 reg for wr ISR EQU 17h ; interrupt status reg for rd and wr CRDA0 EQU 18h ; current remote dma address 0 for rd RSAR0 EQU 18h ; remote start address reg 0 for wr CRDA1 EQU 19h ; current remote dma address 1 for rd RSAR1 EQU 19h ; remote start address reg 1 for wr RBCR0 EQU 1Ah ; remote byte count reg 0 for wr RBCR1 EQU 1Bh ; remote byte count reg 1 for wr RSR EQU 1Ch ; rx status reg for rd RCR EQU 1Ch ; rx configuration reg for wr CNTR0 EQU 1Dh ; tally cnt 0 for frm alg err for rd TCR EQU 1Dh ; tx configuration reg for wr CNTR1 EQU 1Eh ; tally cnt 1 for crc err for rd DCR EQU 1Eh ; data configuration reg for wr CNTR2 EQU 1Fh ; tally cnt 2 for missed pkt for rd IMR EQU 1Fh ; interrupt mask reg for wr ; 8390 LAN Controller (page1) register offset for read and write PAR0 EQU 11h ; physical addr reg 0 for rd and wr PAR1 EQU 12h ; physical addr reg 1 for rd and wr PAR2 EQU 13h ; physical addr reg 2 for rd and wr PAR3 EQU 14h ; physical addr reg 3 for rd and wr PAR4 EQU 15h ; physical addr reg 4 for rd and wr PAR5 EQU 16h ; physical addr reg 5 for rd and wr CURR EQU 17h ; current page reg for rd and wr MAR0 EQU 18h ; multicast addr reg 0 fro rd and WR MAR1 EQU 19h ; multicast addr reg 1 fro rd and WR MAR2 EQU 1Ah ; multicast addr reg 2 fro rd and WR MAR3 EQU 1Bh ; multicast addr reg 3 fro rd and WR MAR4 EQU 1Ch ; multicast addr reg 4 fro rd and WR MAR5 EQU 1Dh ; multicast addr reg 5 fro rd and WR MAR6 EQU 1Eh ; multicast addr reg 6 fro rd and WR MAR7 EQU 1Fh ; multicast addr reg 7 fro rd and WR ;*********************************************************************** ; ; 8003 control register operations ;*********************************************************************** MSK_RESET EQU 80h ; reset LAN controller MSK_ENASH EQU 40h ; enable PC access to shared mem MSK_DECOD EQU 3Fh ; ???? memory decode bits, corresponding ; to SA 18-13. SA 19 assumed to be 1 ;*********************************************************************** ; ; 8390 CMDR MASK ;*********************************************************************** MSK_STP EQU 01h ; software reset, take 8390 off line MSK_STA EQU 02h ; activate the 8390 NIC MSK_TXP EQU 04h ; initial txing of a frm MSK_RD2 EQU 20h ; abort remote DMA MSK_PG0 EQU 00h ; select register page 0 MSK_PG1 EQU 40h ; select register page 1 ;*********************************************************************** ; ; 8390 ISR & IMR MASK ;*********************************************************************** MSK_PRX EQU 01h ; rx with no error MSK_PTX EQU 02h ; tx with no error MSK_RXE EQU 04h ; rx with error MSK_TXE EQU 08h ; tx with error MSK_OVW EQU 10h ; overwrite warning MSK_CNT EQU 20h ; MSB of one of the tally counters is set MSK_RDC EQU 40h ; remote dma completed MSK_RST EQU 80h ; reset state indicator ;*********************************************************************** ; ; 8390 DCR MASK ;*********************************************************************** MSK_WTS EQU 01h ; word transfer mode selection MSK_BOS EQU 02h ; byte order selection MSK_LAS EQU 04h ; long addr selection MSK_BMS EQU 08h ; burst mode selection MSK_ARM EQU 10h ; atuoinitialize remote MSK_FT00 EQU 00h ; burst lrngth selection MSK_FT01 EQU 20h ; burst lrngth selection MSK_FT10 EQU 40h ; burst lrngth selection MSK_FT11 EQU 60h ; burst lrngth selection ;*********************************************************************** ; ; 8390 RCR MASK ;*********************************************************************** MSK_SEP EQU 01h ; save error pkts MSK_AR EQU 02h ; accept runt pkt MSK_AB EQU 04h ; accept broadcast MSK_AM EQU 08h ; accept multicast MSK_PRO EQU 10h ; promiscuous physical ; accept all pkt with physical adr MSK_MON EQU 20h ; monitor mode ;*********************************************************************** ; ; 8390 TCR MASK ;*********************************************************************** MSK_CRC EQU 01h ; inhibit CRC, do not append crc MSK_LB01 EQU 06h ; encoded loopback control MSK_ATD EQU 08h ; auto tx disable MSK_OFST EQU 10h ; collision offset enable ;*********************************************************************** ; ; 8390 RSR MASK ;*********************************************************************** SMK_PRX EQU 01h ; rx without error SMK_CRC EQU 02h ; CRC error SMK_FAE EQU 04h ; frame alignment error SMK_FO EQU 08h ; FIFO overrun SMK_MPA EQU 10h ; missed pkt SMK_PHY EQU 20h ; physical/multicase address SMK_DIS EQU 40h ; receiver disable. set in monitor mode SMK_DEF EQU 80h ; deferring ;*********************************************************************** ; ; 8390 TSR MASK ;*********************************************************************** SMK_PTX EQU 01h ; tx without error SMK_DFR EQU 02h ; non deferred tx SMK_COL EQU 04h ; tx collided SMK_ABT EQU 08h ; tx aboort because of excessive collisions SMK_CRS EQU 10h ; carrier sense lost SMK_FU EQU 20h ; FIFO underrun SMK_CDH EQU 40h ; collision detect heartbeat SMK_OWC EQU 80h ; out of window collision ;*********************************************************************** ; ; Miscellaneous Constants ;*********************************************************************** ; ; PIC (8259) Information ; EOI EQU 20h ; End Of Interrupt INTA00 EQU 20h ; 8259 port INTA01 EQU 21h ; 8259 port ; ; Buffer Length and Field Definition Info ; BPNA EQU 6 ; Bytes Per Network Address MIN_DATA EQU 52 + BPNA ; 52 bytes data + 6 bytes address TX_BLK_LEN EQU 2 ; Offset to Dest Address in TX Block TX_BLK_ADD EQU BPNA ; Length of TX block dest addr field TX_HDR_LEN EQU TX_BLK_LEN + TX_BLK_ADD ; Offset to Data in TX Block RX_TRAIL EQU 02 ; Trailer def for Received Frame Status RX_HDR_LEN EQU 12 ; Offset to Data in Received Frame ;*********************************************************************** ; ; shared memory constant definition ;*********************************************************************** ; for rcv buff ring of shr mem STRT_PG EQU 6 ; start at page 6 STOP_PG EQU 32 ; end at page 31 ; for tx buff of shr mem TB_SIZE EQU 2 ; number of tb buff in shr mem TB_PGNO EQU 3 ; number of pages in one tb buff ;*********************************************************************** ; ; Structure definitions ;*********************************************************************** ; ; MAC LAYER STATS ; mstats struc ; MAC Layer Statistics t0 dd 0 ; number of attempts to transmit txok dd 0 ; number of successful transmissions txbad dd 0 ; number of failed transmits collsn dd 0 ; number of collisions occured lostcrs dd 0 ; number of times the CRS was lost during tx lostcts dd 0 ; lost CTS underrun dd 0 ; number of underrun errors rxrd dd 0 ; number of times the receiver was ready to receive rxok dd 0 ; number of mpdus rxed w/o error rxnrd dd 0 ; number rx's aborted for receive processing crcerr dd 0 ; number of mpdus received with CRC error overrun dd 0 ; number of overrun errors algerr dd 0 ; number of alignment errors srtfrm dd 0 ; number of short frame receiver error rxnom dd 0 ; number of receives lost due to lack of mem rxblkd dd 0 ; NOT USED ; The following are not used right now.... ex_lockup dd 0 ; number of times the execution unit of the chip was in lockup situation ia_corrup dd 0 ; number of times the IA is corrupted spur_int dd 0 ; # of spurious interrupts mstats ENDS ; ; BUFFER DESCRIPTOR ; bufp struc ; Buffer Descriptor Block bd_next dw 0 ; Pointer to next block in chain bd_prev dw 0 ; Pointer to previous block in chain bd_global dw 0 ; bd_addr dw 0 ; Address of buffer associated w/this block bd_seq dd 2 DUP (?) ; bd_info dw 0 ; bufp ENDS the shared memory offset is in BX ; At this offset, you will find: ; 1 byte - read status, usually 21h ; 1 byte - pointsource/enet/zypdefs.inc 644 144 13 36052 4267454627 10562 ;************************************************************************ ;* * ;* ZYPDEFS.INC -- ZYPHER Definitions * ;* * ;* Copyright (C) Ungermann-Bass, Inc. 1986, 1987. * ;* All rights reserved. * ;* * ;************************************************************************ ;* Revision History * ;* ---------------- * ;* 03/16/87 JF Added "Get_Xmt_Bfr", "Write_Xmt_Bfr", "Send_ * ;* Xmt_Bfr", and "Release_Xmt_Bfr" definitions. * ;* 03/19/87 JF Added definitions for "incremental mode" of * ;* "Receive" command. * ;* 03/29/87 JF Added "Schedule" command definitions. * ;* 04/02/87 JF Added "Set_Multicast" command definitions. * ;* * ;************************************************************************ ;************************************************ ;* 82586 System Configuration Pointer * ;************************************************ SCP_ STRUC SCP_SysBus db 0 ; System data bus width. db 0 ; [Unused by us.] dw 0 ; " dw 0 ; " SCP_ISCP_Address dw 0 ; Low order 16 bits of ISCP address. SCP_ISCP_Address_MSB db 0 ; High order 6 bits of ISCP address. db 0 SCP_ ENDS ;************************************************ ;* 82586 Intermediate System Control Pointer * ;************************************************ ISCP_ STRUC ISCP_Busy db 0 ; Initialization-in-progress flag. db 0 ; [Unused.] ISCP_SCB_Offset dw 0 ; Offset of SCB from SCB Base. ISCP_SCB_Base dw 0 ; Low order bits of SCB base address. ISCP_SCB_Base_MSB db 0 ; High order bits of SCB base address. db 0 ; [Unused.] ISCP_ ENDS ;************************************************ ;* 82586 System Control Block * ;************************************************ SCB_ STRUC SCB_Status dw 0 ; STAT, CUS, and RUS. SCB_Command dw 0 ; ACK, CUC, RESET, and RUC. SCB_CBL_offset dw 0 ; Command Block List. SCB_RFA_offset dw 0 ; Receive Frame Area. SCB_CRCERRS dw 0 ; Cyclic Redundancy Check. SCB_ALNERRS dw 0 ; Alignment. SCB_RSCERRS dw 0 ; Resource. SCB_OVRNERRS dw 0 ; Overrun. SCB_ ENDS SCB_CUS_Ready_bit equ 0200h SCB_RUS_Ready_bit equ 0040h RUS_No_Resources equ 0020h CUC_Start_Command equ 0100h RUC_Start_Command equ 0010h CX_bit equ 8000h CNA_bit equ 2000h ;************************************************ ;* 82586 Transmit Command Block * ;************************************************ TCB_ STRUC TCB_Status dw 0 ; C, B, and other status. TCB_Command dw 0 ; EL, S, I, and CMD. TCB_next_TCB dw 0 ; Pointer to next TCB (always FFFF). TCB_1st_TBD dw 0 ; Address of active TBD, or FFFF. TCB_ ENDS TCB_Completion_Status equ BYTE PTR TCB_Status + 1 TCB_Complete_bit equ 80h TCB_Complete_and_No_Error_bits equ 80h+20h TCB_No_Error_bit equ 20h TCB_Hang_bit equ 8 TCB_EL_bit equ 8000h CB_Status equ WORD PTR 0 CB_C_bit equ 8000h CB_OK_bit equ 2000h ;************************************************ ;* 82586 Transmit Buffer Descriptors * ;************************************************ TBD STRUC TBD_EOF_and_Length dw 0 ; EOF bit and byte count. TBD_next_TBD dw 0 ; Pointer to next TBD (always FFFF). TBD_Buffer dw 0 ; 16 LSBs of the buffer address. TBD_Buffer_MSB db 0 ; 4 MSBs of the buffer address. TBD_State db 0 ; [Used as the buffer's state.] TBD_Link dw 0 ; [Used to link TBDs on queues.] TBD_Index db 0 ; [This TBD's TBD_Table index.] db 0 ; [Unused.] TBD_ZCB_Address dw 0,0 ; [Holds address of a Transmit_ZCB.] TBD ENDS TBD_Bfr_Ptr equ TBD_ZCB_Address TBD_Bfr_Room_Left equ TBD_ZCB_Address+2 Max_TBDs EQU 4 TBD_EOF_bit equ 80h TBD_Length_bits equ 3FFFh Multicast_byte equ BYTE PTR 0 Multicast_bit equ 1 TBD_State_Free equ 0 ; Not in use -- on "Free_TBD_Queue". TBD_State_User equ 1 ; Assigned to user for filling with data. TBD_State_Wait equ 2 ; Ready to transmit -- on "Waiting_TBD_Queue". TBD_State_Xmtg equ 3 ; Being transmitted now. ;************************************************ ;* 82586 Receive Frame Descriptors * ;************************************************ RFD STRUC RFD_Status dw 0 ; C, B, and other status bits of this RFD. db 0 ; Unused. RFD_EOL db 0 ; End-of-List status byte. RFD_next_RFD dw 0 ; Link to next RFD. RFD_1st_RBD dw 0 ; Pointer to the first RBD. RFD ENDS RFD_Complete_bit equ 8000h RFD_Complete_and_No_Error_bits equ 8000h+2000h RFD_EOL_bit equ 80h ;************************************************ ;* 82586 Receive Buffer Descriptors * ;************************************************ RBD STRUC RBD_EOF_F_and_Length dw 0 ; EOF & F bits and rec'vd byte count. RBD_next_RBD dw 0 ; Pointer to next RBD. RBD_Buffer dw 0 ; 16 LSBs of the buffer address. RBD_Buffer_MSB db 0 ; 4 MSBs of the buffer address. RBD_State db 0 ; [Used as the buffer's state.] RBD_EOL_and_Size dw 0 ; EOL status and buffer size. RBD_Link dw 0 ; [Used to link RBDs on queues.] RBD_Frame_Length dw 0 ; [Total length of received frame.] RBD_Rcv_Hdwr_Status dw 0 ; [Status from RFD.] RBD ENDS RBD_EOF_and_F equ BYTE PTR RBD_EOF_F_and_Length + 1 RBD_EOL equ BYTE PTR RBD_EOL_and_Size + 1 RBD_EOF_bit equ 80h RBD_F_bit equ 40h RBD_EOL_bit equ 80h RBD_Size_bits equ 3fffh RBD_Length_bits equ 3fffh ;************************************************ ;* 82586 Configure Command Block * ;************************************************ CFG_ STRUC CFG_Status dw 0 CFG_Command dw 0 CFG_next_CB dw 0 CFG_Byte_Count db 0 CFG_FIFO_Limit db 0 CFG_BF_and_SRDY_ARDY db 0 CFG_LPBK_PREAM_AL_and_LEN db 0 CFG_BOF_ACR_and_PRIO db 0 CFG_Interframe_Spacing db 0 CFG_Slot_Time_Low db 0 CFG_Retries_and_Slot_Time db 0 CFG_BTSTF_TONOCRS_etc db 0 CFG_CS_and_CD_things db 0 CFG_Min_Frame_Length db 0 db 0 CFG_ ENDS Bit_Stuffing_bit EQU 40h TONO_CRS_bit EQU 08h BIT_STUFF_and_TONO_CRS_bits EQU 48h Both_Loopback_bits EQU 0C0h External_Loopback_bit EQU 80h Internal_Loopback_bit EQU 40h Save_Bad_Frames_bit EQU 80h Alternative_Backoff_bit EQU 80h ;************************************************ ;* 82586 Set Individual Address Command Block * ;************************************************ SET_IA_ STRUC IA_Status dw 0 IA_Command dw 0 IA_next_CB dw 0 IA_Address db 6 dup (0) SET_IA_ ENDS ;************************************************ ;* 82586 Multicast Setup Command Block * ;************************************************ MCB_ STRUC db 0 MCB_Status db 0 ; C, B, OK, and A status bits. MCB_Command dw 0 ; EL, S, I, and CMD. MCB_next_CB dw 0 ; Pointer to next Command Block. MCB_Count dw 0 ; Count of Multicast Address bytes. MCB_ ENDS MCB_Addresses equ BYTE PTR MCB_Count + 2 MCB_Complete_bit equ 80h ;************************************************ ;* ZYPHER Command Blocks * ;************************************************ ;******************************************************** ;* Command Block Header -- Common to all Commands * ;******************************************************** ZCB_Header STRUC ZCB_Command db 0 ZCB_Status db 0 ZCB_Result db 0 ZCB_Report_Code db 0 ZCB_Options dw 0 ZCB_Post_Routine dd 0 ZCB_Link dw 2 dup (0) ZCB_Header ENDS ZCB_Post_Routine_offset equ WORD PTR ZCB_Post_Routine ZCB_Post_Routine_segment equ WORD PTR ZCB_Post_Routine + 2 ;************************************ ;* Transmit Command Block * ;************************************ Transmit_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Xmt_Data_Length dw 0 ZCB_Xmt_Data_Address dd 0 ZCB_Xmt_Hdwr_Status dw 0 ZCB_Xmt_Bfr_ID db 0,0 ZCB_Xmt_Bfr_Address dw 0,0 Transmit_ZCB ENDS ;************************************ ;* Receive Command Block * ;************************************ Receive_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Rcv_Mode db 0 ZCB_Rcv_Status db 0 ZCB_Rcv_Buffer_Size dw 0 ; Size of user's buffer. ZCB_Rcv_Buffer_Address dd 0 ; Address of user's buffer. ZCB_Rcv_Data_Length dw 0 ; Bytes copied to user's buffer. ZCB_Rcv_Frame_Count dw 0 ; Count of as-yet-uncopied bytes left ; in frame. ZCB_Rcv_Hdwr_Status dw 0 ; Status reported by 82586. ZCB_Rcv_Frame_ID dw 0 ; Frame ID for "incremental mode". ZCB_Rcv_Bfr_Ptr dw 0,0 ; Address of next as-yet-uncopied byte ; of frame in on-card receive buffer. ZCB_Rcv_Bfr_Count dw 0 ; Count of as-yet-uncopied bytes in ; current on-card receive buffer. ZCB_Rcv_Descriptor dw 0,0 ; Address of 82586 RBD (Receive Buffer ; Descriptor) for current on-card ; receive buffer. Receive_ZCB ENDS Rcv_Mode_Incremental EQU 1 ;************************************ ;* Initialize Command Block * ;************************************ Initialize_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Init_Modes dw 0 ; Desired modes of operation. ZCB_Max_Xmt_Length dw 0 ; Desired on-card xmit buffer size. ZCB_Num_Xmt_Buffers dw 0 ; " number of on-card xmit bfrs. ZCB_Max_Rcv_Size dw 0 ; " on-card recv buffer size. ZCB_Num_Rcv_Buffers dw 0 ; " number of on-card recv bfrs. ZCB_Max_Mcast_Addresses dw 0 ; " number of multicast addresses. dw 3 dup (0) ; [Reserved.] dw 2 dup (0) ; [Reserved.] ZCB_82586_CFG_Address dd 0 ; Offset & Segment of 82586 CFG data. ZCB_Acq_Modes dw 0 ; Acquired modes of operation. ZCB_Acq_Max_Xmt_Length dw 0 ; Acquired on-card xmit buffer size. ZCB_Acq_Num_Xmt_Buffers dw 0 ; " number of on-card xmit bfrs. ZCB_Acq_Max_Rcv_Size dw 0 ; " on-card recv buffer size. ZCB_Acq_Num_Rcv_Buffers dw 0 ; " number of on-card recv bfrs. ZCB_Acq_Max_Mcast_Addresses dw 0 ; " number of multicast addrs. dw 3 dup (0) ; [Reserved.] Initialize_ZCB ENDS ;**************************************************** ;* User-Supplied 82586 Configuration Data Block * ;**************************************************** ; If the "User_Configured_82586" bit is set in the "ZCB_Init_Modes" field ; of the "Initialize_ZCB", and the "ZCB_82586_CFG_Address" field is not ; equal to (0, 0), the contents of the "ZCB_82586_CFG_Address" field will ; be assumed to be the address of a block of 14 bytes with the format given ; below. If the "UCFG_Signature" word contains 429Ah (i.e., 82586 decimal, ; truncated to 16 bits), and the "UCFG_Byte_Count" field contains 12, the ; 12 bytes of the block starting with "UCFG_Byte_Count" will be used for ; the "command specific" part of the CONFIGURE commands given to the 82586, ; in place of the 82586 configuration data built into the ROM. The user- ; specified configuration will override the broadband/baseband decision that ; has been made by "Determine_Baseband_or_Broadband". User_CFG STRUC UCFG_Signature dw 0 UCFG_Byte_Count db 0 UCFG_FIFO_Limit db 0 UCFG_BF_and_SRDY_ARDY db 0 UCFG_LPBK_PREAM_AL_and_LEN db 0 UCFG_BOF_ACR_and_PRIO db 0 UCFG_Interframe_Spacing db 0 UCFG_Slot_Time_Low db 0 UCFG_Retries_and_Slot_Time db 0 UCFG_BTSTF_TONOCRS_etc db 0 UCFG_CS_and_CD_things db 0 UCFG_Min_Frame_Length db 0 db 0 User_CFG ENDS ;************************************ ;* Status Command Block * ;************************************ Status_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Stat_State dw 0 ZCB_Stat_Modes dw 0 ; Current modes of operation. ZCB_Stat_Max_Xmt_Length dw 0 ; Current on-card xmit buffer size. ZCB_Stat_Num_Xmt_Buffers dw 0 ; " number of on-card xmit bfrs. ZCB_Stat_Max_Rcv_Size dw 0 ; " on-card recv buffer size. ZCB_Stat_Num_Rcv_Buffers dw 0 ; " number of on-card recv bfrs. ZCB_Stat_Unique_ID db 6 dup (0) ZCB_Stat_Total_Xmts dw 2 dup (0) ZCB_Stat_Total_Rcvs dw 2 dup (0) ZCB_Stat_CRC_Errors dw 2 dup (0) ZCB_Stat_ALN_Errors dw 2 dup (0) ZCB_Stat_RSC_Errors dw 2 dup (0) ZCB_Stat_OVR_Errors dw 2 dup (0) dw 12 dup (0); [Reserved.] Status_ZCB ENDS ;******************************************** ;* Setup-Scheduling Command Block * ;******************************************** Scheduling_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Sched_Routine dw 0,0 ZCB_Sched_Options dw 0 ZCB_Sched_Interval dw 0 Scheduling_ZCB ENDS Time_Elapsed equ 1 Frame_Arrived equ 2 Receive_Completed equ 4 Transmit_Completed equ 8 Explicit_Sched_Request equ 100h Sched_Options_bits equ Time_Elapsed+Frame_Arrived+Receive_Completed+Transmit_Completed+Explicit_Sched_Request ;******************************************** ;* Set-Multicast-Address Command Block * ;******************************************** Multicast_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Multicast_Address db 6 dup (0) Multicast_ZCB ENDS ;************************************************ ;* ZYPHER Command Codes * ;************************************************ Initialize_Cmd EQU 40h Transmit_Cmd EQU 41h Receive_Cmd EQU 42h Status_Cmd EQU 43h Cancel_Receives_Cmd EQU 44h Get_Xmt_Bfr_Cmd EQU 45h Write_Xmt_Bfr_Cmd EQU 46h Send_Xmt_Bfr_Cmd EQU 47h Release_Xmt_Bfr_Cmd EQU 48h Read_Frame_Data_Cmd EQU 49h Next_Rcv_Bfr_Cmd EQU 4Ah Flush_Frame_Cmd EQU 4Bh Setup_Scheduling_Cmd EQU 4Ch Schedule_Cmd EQU 4Dh Enable_Multicast_Cmd EQU 4Eh Disable_Multicast_Cmd EQU 4Fh Disable_All_Multicasts_Cmd EQU 50h Min_Command_Code EQU Initialize_Cmd Max_Command_Code EQU Disable_All_Multicasts_Cmd ;************************************************ ;* ZYPHER Result Codes * ;************************************************ Xmt_Length_Error equ 10h ; Data length too long or too short. Xmt_Address_Error equ 11h ; Data starts at odd address. Unknown_Command_Error equ 12h ; Undefined command code in ZCB. Unimplemented_yet_Error equ 13h ; As-yet-unimplemented command code. Xmt_Completion_Success equ 14h ; Transmission successfully completed. Xmt_Completion_Error equ 15h ; Transmission completed with error. Rcv_Completion_Success equ 16h ; Frame received successfully. Rcv_Frame_Truncated_Error equ 17h ; Receive buffer was too small. Initialization_Complete equ 18h ; Initialization completed. Status_Complete equ 19h ; Status completed. Not_Yet_Initialized equ 1Ah Already_Initialized equ 1Bh Command_Cancelled equ 1Ch Cancel_Complete equ 1Dh Xmt_Bfr_Assigned equ 1Eh ; "Get_Xmt_Bfr_Cmd" completed. Xmt_Bfr_not_Assigned equ 1Fh Xmt_Bfr_Released equ 20h ; "Release_Xmt_Bfr_Cmd" completed. Xmt_Bfr_Written equ 21h ; "Write_Xmt_Bfr_Cmd" completed. More_Data_Available equ 22h No_More_Rcv_Bfrs equ 23h Frame_ID_Invalid equ 24h Flush_Complete equ 25h Frame_Table_Overflow equ 26h Scheduling_Started equ 27h Scheduling_Cancelled equ 28h Schedule_Done equ 29h Multicast_Setup_Done equ 2Ah Too_Many_Multicast_Addresses equ 2Bh ;************************************************ ;* System State Bits * ;************************************************ Initialized EQU 8000h Ready_to_Receive EQU 1000h Interrupt_Entry_Installed EQU 0800h Media_Test_Done EQU 0002h Media_Determined EQU 0001h ;************************************************ ;* System Modes Bits * ;************************************************ Broadcasts_Disabled EQU 0800h Promiscuous_Mode EQU 0400h Accept_Bad_Frames EQU 0200h Broadband_Mode EQU 0100h User_Configured_82586 EQU 0080h ;************************************************ ;* ZE-NIC Control and Status Register bits * ;************************************************ CSR_Reset EQU 1 CSR_Network_Loopback EQU 2 CSR_Timer_Enable EQU 4 CSR_82586_Enable EQU 8 CSR_Soft_Interrupt EQU 20h CSR_Timer_Interrupt EQU 40h CSR_82586_Interrupt EQU 80h ;_segment equ WORD PTR ZCB_Post_Routine + 2 ;************************************ ;* Transmit Command Block * ;************************************ Transmit_ZCB STRUC db SIZE ZCB_Header dup (0) ZCB_Xmt_Data_Length dw 0 ZCB_Xmt_Data_Address dd 0 ZCB_Xmt_Hdwr_Status dw 0 ZCB_Xmt_Bfr_ID db 0,0 ZCB_Xmt_Bfr_Address dw 0,0 Transmit_ZCB ENDS ;************************************ ;* Receive Command Block * ;*************************source/enet/dos.mac 644 144 13 6025 4267454627 7627 .XLIST PAGE 58,132 ;** ; ; This macro library defines the operating environment for the 8086 L ; memory model, which allows 1M bytes of data and 1M bytes of program. ; ;** MSDOS EQU 2 ;** ; ; The following symbols define the 8086 memory mode being used. Set LPROG ; to 1 for a large program segment (greater than 64K-bytes), and set LDATA ; to 1 for a large data segment. Set COM to 1 to generate .COM files ; instead of .EXE files. Note that if COM is not zero, then LPROG and ; LDATA must be 0. ; ;** COM EQU 0 LPROG EQU 1 LDATA EQU 1 ;** ; ; The following symbols are established via LPROG and LDATA as follows: ; ; S8086 set for small model (small prog, small data) ; D8086 set for model with large data, small prog ; P8086 set for model with large prog, small data ; L8086 set for large model ; ;** IF (LPROG EQ 0) AND (LDATA EQ 0) S8086 EQU 1 D8086 EQU 0 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG EQ 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 1 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA EQ 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 1 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 0 P8086 EQU 0 L8086 EQU 1 ENDIF ;** ; ; The DSEG and PSEG macros are defined to generate the appropriate GROUP ; and SEGMENT statements for the memory model being used. The ENDDS and ; ENDPS macros are then used to end the segments. ; ;** DSEG MACRO DGROUP GROUP DATA DATA SEGMENT WORD PUBLIC 'DATA' ASSUME DS:DGROUP ENDM ENDDS MACRO DATA ENDS ENDM IF S8086 PSEG MACRO PGROUP GROUP PROG PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:PGROUP ENDM ENDPS MACRO PROG ENDS ENDM ENDIF IF D8086 PSEG MACRO CGROUP GROUP CODE CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CGROUP ENDM ENDPS MACRO CODE ENDS ENDM ENDIF IF P8086 PSEG MACRO _CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:_CODE ENDM ENDPS MACRO _CODE ENDS ENDM ENDIF IF L8086 PSEG MACRO _PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:_PROG ENDM ENDPS MACRO _PROG ENDS ENDM ENDIF ;** ; ; The BEGIN and ENTRY macros establish appropriate function entry points ; depending on whether NEAR or FAR program addressing is being used. The ; only difference between the two is that BEGIN generates a PROC operation ; to start a segment. ; BEGIN MACRO NAME ; begin a function PUBLIC NAME IF LPROG NAME PROC FAR ELSE NAME PROC NEAR ENDIF ENDM ENTRY MACRO NAME PUBLIC NAME IF LPROG NAME LABEL FAR ELSE NAME LABEL NEAR ENDIF ENDM ;** ; ; The following symbols are defined to help set up a STRUC defining the ; stack frame: ; ; CPSIZE -> code pointer size (2 or 4) ; DPSIZE -> data pointer size (2 or 4) ; ; These wouldn't be necessary if it were possible to use macros or even ; conditionals within a STRUC. ; IF LPROG CPSIZE EQU 4 ELSE CPSIZE EQU 2 ENDIF IF LDATA DPSIZE EQU 4 ELSE DPSIZE EQU 2 ENDIF ; ; The SETX macro sets the symbol X to 4 if LPROG is 0 or to 6 otherwise. ; X can then be used to skip past the BP and return address save area ; in the stack frame when accessing the function arguments. ; SETX MACRO IF LPROG X EQU 6 ELSE X EQU 4 ENDIF ENDM .LIST DATA EQU 1 ;** ; ; The following symbols are established via LPROG and LDATA as follows: ; ; S8086 set for small model (small prog, small data) ; D8086 set for model with large data, small prog ; P8086 set for model with large prog, small data ; L8086 set for large model ; ;** IF (LPROG EQ 0) AND (LDATA EQ 0) S8086 EQU 1 D8086 EQU 0 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG EQ 0) AND (LDATA NE 0) S8086 EQU 0 D8086 EQU 1 P8086 EQU 0 L8086 EQU 0 ENDIF IF (LPROG NE 0) AND (LDATA EQ source/enet/nov3com.doc 644 144 13 3200 4267455160 10414 Note: These instructions and the currently posted NOV3COM driver only work with the source code to version 2.1. Minor modifications will be required to work with version 2.2. (tk) This hardware driver co-exists with Novell network software. It allows you to FTP files from TCP/IP hosts to your Novell network disk. It was contributed by Dave Thompson of Carleton University. He modified the driver for the 3COM 3C501 board (NET3COM.ASM) to produce NOV3COM.ASM. The instructions below were taken from the original note from Dave Thompson. The only modifications I have made are to the net3com.asm file. The new file is called nov3com.asm and works with or without novell and should work with other well behaved network software. Novell is initially not that well behaved - it bashes its ethernet card interrupt vector back every few seconds so it has to be patched. Instructions for the patch follow. The new version of the ethernet driver peeks at the type field of the packet and calls the novell handler if it is not IP or ARP. This allows "simultaneous" use of the card by the two drivers i.e. you can FTP a file from a server to/from your novell drive. Dave Patch to the anet3.com novell shell. We have 2 versions of anet3.com - instructions for both follow. Advanced NetWare V2.0a xxxx = 7923 Advanced/SFT NetWare V2.0a++ xxxx=81DD DEBUG ANET3.COM Axxxx RET NNEWANET3.COM W QUIT There is no difference in operation with this new shell other than the fact that it doesn't bash its vector back every few seconds. Just link in NOV3COM.ASM instead of NET3COM.ASM and run it with the patched Novell software and you should be running. DE CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:CGROUP ENDM ENDPS MACRO CODE ENDS ENDM ENDIF IF P8086 PSEG MACRO _CODE SEGMENT BYTE PUBLIC 'CODE' ASSUME CS:_CODE ENDM ENDPS MACRO _CODE ENDS ENDM ENDIF IF L8086 PSEG MACRO _PROG SEGMENT BYTE PUBLIC 'PROG' ASSUME CS:_PROG ENDM ENDPS MACRO _PROG ENDS ENDM ENDIF ;** ; ; The BEGIN and ENTRY macros establish appropriate funsource/ftp/ftppi.h 644 144 13 6665 4267454677 7530 #define NCMDS 48 #define QMARK 1 #define BANG 2 #define ASCII 3 #define BELL 4 #define BGET 5 #define BINARY 6 #define BPUT 7 #define BYE 8 #define CD 9 #define CLOSE 10 #define DEL 11 #define DEBUG 12 #define DIR 13 #define GET 14 #define GLOB 15 #define HASH 16 #define HELP 17 #define INTERACTIVE 18 #define LCD 19 #define LLS 20 #define LS 21 #define MDELETE 22 #define MDIR 23 #define MGET 24 #define MKDIR 25 #define MLS 26 #define MODE 27 #define MPUT 28 #define NONINTERACTIVE 29 #define OPEN 30 #define PROMPT 31 #define PUT 32 #define PWD 33 #define QUIT 34 #define QUOTE 35 #define RECV 36 #define REMOTEHELP 37 #define RENAME 38 #define RM 39 #define RMDIR 40 #define SEND 41 #define SENDPORT 42 #define SLASHFLIP 43 #define STATUS 44 #define STRUCT 45 #define TYPE 46 #define USER 47 #define VERBOSE 48 static char *ftp_cmdlist[] = { "?", "!", "ascii", "bell", "bget", "binary", "bput", "bye", "cd", "close", "delete", "debug", "dir", "get", "glob", "hash", "help", "interactive", "lcd", "lls", "ls", "mdelete", "mdir", "mget", "mkdir", "mls", "mode", "mput", "noninteractive", "open", "prompt", "put", "pwd", "quit", "quote", "recv", "remotehelp", "rename", "rm", "rmdir", "send", "sendport", "slashflip", "status", "struct", "type", "user", "verbose" }; static char *helpstrings[] = { "? print local help information", "! escape to the shell", "ascii set ascii transfer type", "bell beep when command completed - toggle", "bget get a file in binary mode", "binary set binary transfer type", "bput put a file in binary mode", "bye terminate ftp session and exit", "cd change remote working directory", "close terminate ftp session", "delete delete remote file - inquires if prompting is on", "debug toggle/set debugging mode", "dir list contents of remote directory", "get receive file", "glob toggle metacharacter expansion of local file names", "hash toggle printing `#' for each buffer transferred", "help print local help information", "interactive turn on prompting for multiple commands", "lcd change local working directory", "ls nlist contents of remote directory", "lls list contents of local directory", "mdelete delete multiple files", "mdir list contents of multiple remote directories", "mget get multiple files", "mkdir make directory on the remote machine", "mls nlist contents of multiple remote directories", "mode set file transfer mode", "mput send multiple files", "noninteractive turn off prompting on multiple commands", "open connect to remote tftp", "prompt toggle interactive prompting on multiple commands", "put send one file", "pwd print working directory on remote machine", "quit terminate ftp session and exit", "quote send arbitrary ftp command", "recv receive file", "remotehelp get help from remote server", "rename rename file", "rm remove file", "rmdir remove directory on the remote machine", "send send one file", "sendport toggle use of PORT cmd for each data connection", "slashflip toggle changing / to \ on outgoing commands", "status show current status", "struct set file transfer structure", "type set file transfer type", "user send new user information", "verbose toggle verbose mode" };ilable equ 22h No_More_Rcv_Bfrs equ 23h Frame_ID_Invalid equ 24h Flush_Compsource/ftp/ftpbin.c 644 144 13 143267 4267454707 7715 /* * User FTP * 6/8/87 **************************************************************************** * * * by Tim Krauskopf and Swami Natarajan * * * * National Center for Supercomputing Applications * * 152 Computing Applications Building * * 605 E. Springfield Ave. * * Champaign, IL 61820 * * * * * **************************************************************************** */ #include "stdio.h" #include "fcntl.h" #include "nkeys.h" #include "ctype.h" #include "hostform.h" #include "whatami.h" #define FASCII 0 #define FIMAGE 1 #define HFTP 21 #define FALSE 0 #define TRUE 1 #define SUCCESS 2 #define ERROR -1 #define NONE -2 #define ABORT -3 #define INCOMPLETE -4 #define AMBIGUOUS -5 #define HAVEDATA 4 int xp=0, /* general pointer */ towrite=0, /* file transfer pointer */ len=0, /* file transfer length */ ftpnum, /* current command port */ ftpdata=-1, /* current data port */ ftpfh, /* file handle for ftp */ ftpstate=0, /* state for background process */ fcnt = 0, /* counter for ftpd */ ftpfilemode=0, /* file open mode for transfer */ foundbreak=0, /* cntrl-break pending */ connected=0, /* not connected */ debug=0, /* debug level */ hash=0, /* hash mark printing */ sendport=1, /* to send ports or not */ verbose=1, /* informative messages */ bell=0, /* sound bell */ autologin=1, /* login on connect */ prompt=1, /* check on multiple commands */ glob=1, /* expand wildcards */ slashflip=1, /* change \ to / */ capture=0, /* capture data or not */ fromtty=1; /* default input from tty */ FILE *fromfp=NULL; /* file pointer for input */ unsigned int curftpprt = 0, /* port to use */ ftpport(); unsigned char destname[50], /* who to connect to */ s[550], /* temporary string */ captlist[2001], /* response string */ ftpcommand[200], /* command to execute */ *neterrstring(), *malloc(); char printline[100]; /* line to display */ static int breakstop(),userftpd(); static char *stpblk(),*stptok(); #define BUFFERS 8000 /* size of buffer */ #define READSIZE 128 /* how much to read */ static unsigned char xs[BUFFERS+10]; /* buffer space for file transfer */ long int time(), start=0L, /* timing var */ filelength=0L; /* length of current file for transfer */ /************************************************************************/ /* main - main procedure. Displays opening message, parses arguments, /* initializes network, reads user commands and executes them, and /* cleans up. /************************************************************************/ main(argc,argv) int argc; char *argv[]; { int i,c; static char Configfile[50]="config.tel"; static char fromfile[20]=""; onbreak(&breakstop); /* break handler */ /* * initialize network */ destname[0] = '\0'; /* destination unknown */ /* parse arguments */ for (i=1; i "); /* prompt */ c = ftpgets(ftpcommand,200,1); /* read cmd from user */ } while (c != AF3); /* Alt-F3 aborts */ netclose(ftpnum); /* close command connection */ netshut(); /* terminate network stuff */ return(0); } /************************************************************************/ /* ftpgets - read a line from the keyboard /* returns ABORT if aborted, non-zero on success /************************************************************************/ ftpgets(s,lim,echo) char *s; /* where to put the line */ int lim,echo; /* max chars to read, echo? */ { int c,count,i; char *save, *ret; count = 0; /* none read */ save = s; /* beginning of line */ if (foundbreak) { foundbreak = 0; *s = '\0'; nputs(""); return(ABORT); } if (!fromtty) { if (fromfp==NULL) { ret = fgets(s,lim,stdin); } else { ret = fgets(s,lim,fromfp); } if (ret==NULL) { nputs("EOF or error on read from file\n"); if (connected) { ftpdo("QUIT",""); connected = FALSE; } netshut(); exit(1); } s[strlen(s)-1] = '\0'; /* remove newline */ if (echo && fromfp) nputs(s); return (strlen(s)); } while (1) { if (foundbreak) { /* abort */ s = save; /* reset line */ *s = '\0'; /* null line */ nputs(""); /* newline */ foundbreak = 0; /* break processed */ return(ABORT); } c = n_chkchar(); /* char available to read? */ if (c <= 0) { /* none available */ checkevent(); /* check event queue */ c = 0; } switch (c) { /* allow certain editing chars */ case 8: /* backspace */ if (count) { if (echo) { nputchar(c); nputchar(' '); nputchar(c); } count--; /* one less character */ s--; /* move pointer backward */ } break; case 13: /* carriage return, = ok */ nputs(""); /* newline */ *s = '\0'; /* terminate the string */ return(c); /* return ok */ break; case 21: /* kill line */ for (i=0; i < s-save; i++) { /* length of line */ if (echo) { /* erase */ nputchar(8); nputchar(' '); nputchar(8); } } s = save; /* reset line */ break; case 0: /* do nothing */ break; default: /* not special char */ if (c > 31 && c < 127) { /* printable */ if (echo) nputchar(c); /* display */ *s++ = c; /* add to string */ count++; /* length of string */ } else /* acts as eol */ return(c); /* value of special char */ if (count == lim) { /* to length limit */ *s = '\0'; /* terminate */ return(c); } break; } } } /************************************************************************/ /* dumpcon * take everything from a connection and send it to the screen * return -1 on closed connection, else 0, 1 if paused /************************************************************************/ dumpcon(cnum) int cnum; { int cnt; if (fromtty && n_scrlck()) return(TRUE); /* if paused, nothing to do */ do { cnt = netread(cnum,s,64); /* get some from queue */ telnet(cnt); /* display on screen, etc.*/ /* demux all packets */ } while (cnt > 0); return(cnt); /* 0 normally, -1 if connection closed */ } /************************************************************************/ /* telnet * filter telnet options on incoming data /************************************************************************/ telnet(cnt) int cnt; { int i; for (i=0; i < cnt; i++) { /* put on screen */ if (s[i] & 128) { /* if over ASCII 128 */ sprintf(printline," %d ",s[i]); /* show as number */ nputs(printline); } else nputchar(s[i]); } return(TRUE); } #include "ftppi.h" /* list of commands, help strings */ /********************************************************************/ /* FTP PI * Protocol interpreter for user interface commands * Will permit any command to be abbreviated uniquely. * Recognizes commands, translates them to the protocol commands to be * sent to the other server, and uses userftpd, the daemon, to do data * transfers. /************************************************************************/ ftppi(command) char *command; { int cmdno,i; char cmdname[20],word[50],line[100],answer[20],ofilename[50]; char *p,*firstname(),*nextname(); struct machinfo *mp,*Sgethost(); if (debug>1) { /* print command */ sprintf(printline,"command: %s",command); nputs(printline); } /* get command number */ if (!getword(command,cmdname)) return(FALSE); /* get command name */ /* removes first word from command */ lowercase(cmdname); cmdno = finduniq(cmdname,ftp_cmdlist,NCMDS); /* search cmdlist for prefix */ if (cmdno==AMBIGUOUS) { /* not unique abbreviation */ nputs("?Ambiguous command"); return(FALSE); } if (cmdno==NONE) { /* not a prefix of any command */ nputs("?Invalid command"); return(FALSE); } /* change \ to / and check if command output redirected */ if (cmdno!=BANG) { /* don't alter shell escape */ if (cmdno!=LLS) /* do not flip slashes for LLS */ checkoredir(command,ofilename,slashflip); /* check redirection, flip \ */ else checkoredir(command,ofilename,FALSE); /* check redirection */ } /* process commands */ switch (cmdno) { case QMARK: case HELP: if (!command[0]) { /* no argument */ nputs("Commands may be abbreviated. Commands are:\n"); /* display command list */ printline[0] = '\0'; for (i=0; i0) debug = i; /* level */ else if (getword(command,word)) { /* scan arg */ lowercase(word); if (!strcmp(word,"off")) debug = FALSE; else if (!strcmp(word,"on")) debug = TRUE; else debug = !debug; } else debug = !debug; if (debug) { sprintf(printline,"Debugging on (debug=%d).",debug); nputs(printline); } else nputs("Debugging off."); return(TRUE); case GLOB: /* wildcard expansion */ if (getword(command,word)) { lowercase(word); if (!strcmp(word,"off")) glob = FALSE; else if (!strcmp(word,"on")) glob = TRUE; else glob = !glob; } else glob = !glob; if (glob) nputs("Globbing on."); else nputs("Globbing off."); return(TRUE); case HASH: /* hash mark printing */ if (getword(command,word)) { lowercase(word); if (!strcmp(word,"off")) hash = FALSE; else if (!strcmp(word,"on")) hash = TRUE; else hash = !hash; } else hash = !hash; if (hash) nputs("Hash mark printing on (1024 bytes/hash mark)."); else nputs("Hash printing off."); return(TRUE); case INTERACTIVE: /* prompting on multiple transfers */ prompt = TRUE; nputs("Interactive mode on."); return(TRUE); case LCD: /* change local directory */ if (command[1]==':') { /* if disk specified */ chgdsk(tolower(command[0])-'a'); strcpy(command,&command[2]); } if (*(stpblk(command)) && chdir(command)) { /* CD */ nputs("Unable to change directory"); } getdir(0,line); /* current directory */ sprintf(printline,"Local directory now %s",line); nputs(printline); return(TRUE); case LLS: /* local DIR */ sprintf(line,"DIR %s",command); system(line); return(TRUE); case NONINTERACTIVE: /* turn off interactive prompting */ prompt = FALSE; nputs("Interactive mode off."); return(TRUE); case OPEN: /* open connection to host */ if (connected) { nputs("Already connected."); return(FALSE); } while (!(*(stpblk(command)))) { /* no argument */ putstring("To: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } getword(command,destname); /* host name */ mp = Sgethost(destname); /* get host info */ if (foundbreak) { /* abort */ foundbreak = FALSE; return(FALSE); } if (mp==NULL) { /* try domain serving */ Sdomain(destname); while (mp==NULL) switch (checkevent()) { case ABORT: return(FALSE); /* abort */ case DOMFAIL: printerr(); sprintf(printline,"Unknown host: %s\n",destname); nputs(printline); return(FALSE); case DOMOK: mp = Slooknum(ftpnum); /* get host info */ break; default: break; } } if (sscanf(command,"%d",&i)>0) /* port number specified */ ftpnum = Snetopen(mp,i); else { ftpnum = Snetopen(mp,HFTP); /* default port */ } if (foundbreak) { /* abort */ foundbreak = FALSE; return(FALSE); } if (ftpnum<0) { /* error on open */ printerr(); sprintf(printline,"Unable to connect to %s",destname); nputs(printline); return(FALSE); } ftpreplies(ftpnum,&i); /* response from other end */ if (foundbreak) { foundbreak = FALSE; return(FALSE); } connected = TRUE; if (autologin) { /* execute login command */ strcpy(command,"user"); ftppi(command); } return(TRUE); case PROMPT: /* interactive prompting */ if (getword(command,word)) { lowercase(word); if (!strcmp(word,"off")) prompt = FALSE; else if (!strcmp(word,"on")) prompt = TRUE; else prompt = !prompt; } else prompt = !prompt; if (prompt) nputs("Interactive mode on."); else nputs("Interactive mode off."); return(TRUE); case SENDPORT: /* send PORT commands for each transfer */ if (getword(command,word)) { lowercase(word); if (!strcmp(word,"off")) sendport = FALSE; else if (!strcmp(word,"on")) sendport = TRUE; else sendport = !sendport; } else sendport = !sendport; if (sendport) nputs("Use of PORT cmds on."); else nputs("Use of PORT cmds off."); return(TRUE); case SLASHFLIP: /* change \ to / */ if (getword(command,word)) { lowercase(word); if (!strcmp(word,"off")) slashflip = FALSE; else if (!strcmp(word,"on")) slashflip = TRUE; else slashflip = !slashflip; } else slashflip = !slashflip; if (slashflip) nputs("Slash translation on."); else nputs("Slash translation off."); return(TRUE); case STATUS: /* display status info */ if (connected) { sprintf(printline,"Connected to %s",destname); nputs(printline); } if (ftpfilemode==FASCII) nputs("Transfer mode is ascii."); else nputs("Transfer mode is binary."); if (bell) nputs("Bell on."); else nputs("Bell off."); if (debug) { sprintf(printline,"Debugging on. (Debug=%d)",debug); nputs(printline); } else nputs("Debugging off."); if (glob) nputs("Filename globbing on."); else nputs("Filename globbing off."); if (hash) nputs("Hash-mark printing on."); else nputs("Hash-mark printing off."); if (prompt) nputs("Interactive prompting on."); else nputs("Interactive prompting off."); if (sendport) nputs("Sending of port commands on."); else nputs("Sending of PORT cmds off."); if (slashflip) nputs("Flipping \\ to / on."); else nputs("Flipping \\ to / off."); if (verbose) nputs("Verbose mode on."); else nputs("Verbose mode off."); if (connected) { /* send STAT command */ nputs("\nRemote status:"); ftpdo("STAT",ofilename); } return(TRUE); case VERBOSE: /* display informative messages */ if (getword(command,word)) { lowercase(word); if (!strcmp(word,"off")) verbose = FALSE; else if (!strcmp(word,"on")) verbose = TRUE; else verbose = !verbose; } else verbose = !verbose; if (verbose) nputs("Verbose mode on."); else nputs("Verbose mode off."); return(TRUE); default: /* The other commands valid only if connected */ if (!connected) { nputs("Not connected."); return(FALSE); } switch (cmdno) { case ASCII: /* transfer mode */ ftpdo("TYPE A",ofilename); return(TRUE); case BGET: /* get file in binary mode */ i = ftpfilemode; /* save current mode */ if (i==FASCII) ftpdo("TYPE I",ofilename); while (!(*(stpblk(command)))) { putstring("File: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } sprintf(line,"RETR %s",command); ftpdo(line,ofilename); /* get file */ if (i==FASCII) ftpdo("TYPE A",ofilename); /* restore mode */ return(TRUE); case BINARY: /* binary mode */ ftpdo("TYPE I",ofilename); return(TRUE); case BPUT: /* put file in binary mode */ i = ftpfilemode; if (i==FASCII) ftpdo("TYPE I",ofilename); while (!(*(stpblk(command)))) { /* if no arg */ putstring("File: "); /* get from user */ if (ftpgets(command,100,1)==ABORT) return(FALSE); } sprintf(line,"STOR %s",command); ftpdo(line,ofilename); if (i==FASCII) ftpdo("TYPE A",ofilename); return(TRUE); case CD: /* change remote directory */ while (!(*(stpblk(command)))) { /* if no arg, get from user */ putstring("To: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); /* abort */ } getword(command,word); if (!strcmp(word,"..")) { /* special case */ i = ftpdo("CDUP",ofilename); if (i!=ERROR) return(TRUE); /* if CDUP understood */ nputs("Trying again..."); i = ftpdo("XCUP",ofilename); /* try alternative */ if (i!=ERROR) return(TRUE); nputs("Trying again..."); /* else try usual CD */ } sprintf(line,"CWD %s",word); /* try CWD */ i = ftpdo(line,ofilename); if (i!=ERROR) return(TRUE); nputs("Trying again..."); sprintf(line,"XCWD %s",word); /* try XCWD */ ftpdo(line,ofilename); return(TRUE); case CLOSE: /* drop connection */ ftpdo("QUIT",ofilename); connected = FALSE; return(TRUE); case DEL: case RM: getword(command,word); while (!word[0]) { /* get arg from user */ putstring("File: "); if (ftpgets(word,100,1)==ABORT) return(FALSE); /* abort */ } if (prompt) { /* check interactively */ sprintf(printline,"Delete %s? ",word); putstring(printline); ftpgets(answer,20,1); if (tolower(*(stpblk(answer)))!='y') return(TRUE); } sprintf(line,"DELE %s",word); ftpdo(line,ofilename); return(TRUE); case DIR: /* get list of remote files */ i = ftpfilemode; /* save mode */ if (i==FIMAGE) ftpdo("TYPE A",ofilename); if (getword(command,word)) { /* do DIR */ sprintf(line,"LIST %s",word); ftpdo(line,ofilename); } else ftpdo("LIST",ofilename); if (i==FIMAGE) ftpdo("TYPE I",ofilename); return(TRUE); case GET: case RECV: /* get remote file */ while (!(*(stpblk(command)))) { /* if no arg */ putstring("File: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); /* abort */ } sprintf(line,"RETR %s",command); ftpdo(line,ofilename); return(TRUE); case LS: /* get remote file list - short */ i = ftpfilemode; if (i==FIMAGE) ftpdo("TYPE A",ofilename); if (getword(command,word)) { sprintf(line,"NLST %s",word); ftpdo(line,ofilename); } else ftpdo("NLST",ofilename); if (i==FIMAGE) ftpdo("TYPE I",ofilename); return(TRUE); case MDELETE: while (!(*(stpblk(command)))) { /* no arg */ putstring("Files: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); /* abort */ } while (getword(command,word)) { /* for each arg */ if (glob) { /* wildcard expansion */ sprintf(line,"NLST %s",word); capture = TRUE; ftpdo(line,ofilename); /* put exapnsion in captlist */ capture = FALSE; } else strcpy(captlist,word); /* captlist has name(s) now */ while (getnname(captlist,word)) { /* for each name */ if (prompt) { /* check */ sprintf(printline,"mdelete %s? ",word); putstring(printline); if (ftpgets(answer,20,1)==ABORT) { /* abort */ command[0] = '\0'; /* no more processing */ break; /* quit immediately */ } if (tolower(*(stpblk(answer)))!='y') continue; } sprintf(line,"DELE %s",word); /* delete */ ftpdo(line,ofilename); } } return(TRUE); case MDIR: /* remote multiple DIR */ i = ftpfilemode; /* save mode */ if (i==FIMAGE) ftpdo("TYPE A",ofilename); while (!(*(stpblk(command)))) { /* no arg */ putstring("Directories: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); /* abort */ } while (getword(command,word)) { /* for each arg */ if (glob) { /* expand wildcards */ sprintf(line,"NLST %s",word); capture = TRUE; ftpdo(line,ofilename); capture = FALSE; } else strcpy(captlist,word); while (getnname(captlist,word)) { /* for each name */ if (prompt) { /* check */ sprintf(printline,"mdir %s? ",word); putstring(printline); if (ftpgets(answer,20,1)==ABORT) { /* abort */ command[0] = '\0'; /* no more processing */ break; /* quit immediately */ } if (tolower(*(stpblk(answer)))!='y') continue; } sprintf(line,"LIST %s",word); /* DIR */ ftpdo(line,ofilename); } } if (i==FIMAGE) ftpdo("TYPE I",ofilename); return(TRUE); case MGET: /* get multiple files */ getword(command,line); while (!line[0]) { /* no arg */ putstring("Files: "); if (ftpgets(line,100,1)==ABORT) return(FALSE); /* abort */ } while (getword(line,word)) { /* for each arg */ if (glob) { /* expand wildcards */ sprintf(command,"NLST %s",word); capture = TRUE; ftpdo(command,ofilename); capture = FALSE; } else strcpy(captlist,word); while (getnname(captlist,word)) { /* for each name */ if (prompt) { /* check */ sprintf(printline,"mget %s? ",word); putstring(printline); if (ftpgets(answer,20,1)==ABORT) { /* abort */ command[0] = '\0'; /* no more processing */ break; /* quit immediately */ } if (tolower(*(stpblk(answer)))!='y') continue; } sprintf(command,"RETR \"%s\"",word); ftpdo(command,ofilename); } } return(TRUE); case MKDIR: /* create directory */ while (!(*(stpblk(command)))) { /* no arg */ putstring("Directory: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); /* abort */ } sprintf(line,"XMKD %s",command); /* try XMKD */ i = ftpdo(line,ofilename); if (i!=ERROR) return(TRUE); nputs("Trying again..."); sprintf(line,"MKD %s",command); /* else try MKD */ ftpdo(line,ofilename); return(TRUE); case MLS: i = ftpfilemode; if (i==FIMAGE) ftpdo("TYPE A",ofilename); while (!(*(stpblk(command)))) { /* no arg */ putstring("Directories: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); /* abort */ } while (getword(command,word)) { /* for each arg */ if (glob) { /* exapnd wildcards */ sprintf(line,"NLST %s",word); capture = TRUE; ftpdo(line,ofilename); capture = FALSE; } else strcpy(captlist,word); while (getnname(captlist,word)) { /* for each name */ if (prompt) { /* check */ sprintf(printline,"mls %s? ",word); putstring(printline); if (ftpgets(answer,20,1)==ABORT) { /* abort */ command[0] = '\0'; /* no more processing */ break; /* quit immediately */ } if (tolower(*(stpblk(answer)))!='y') continue; } sprintf(line,"NLST %s",word); /* DIR */ ftpdo(line,ofilename); } } if (i==FIMAGE) ftpdo("TYPE I",ofilename); return(TRUE); case MODE: /* set stream mode */ getword(command,word); lowercase(word); if (strncmp(word,"stream",strlen(word))) nputs("We only support stream mode, sorry."); else nputs("Mode is stream."); return(TRUE); case MPUT: /* put multiple files */ getword(command,line); while (!line[0]) { /* no arg */ putstring("Files: "); if (ftpgets(line,100,1)==ABORT) return(FALSE); /* abort */ } p = NULL; /* no names expanded yet */ while (getword(line,word)) { /* for each arg */ do { /* for each name */ if (glob) { /* local wildcard expansion */ if (p==NULL) { /* if no expansions yet */ p = firstname(word); /* get first name */ if (p==NULL) { /* if no expansions */ sprintf(printline,"No match for %s",word); nputs(printline); continue; } } else { /* not first name */ p = nextname(word); /* get next name */ if (p==NULL) continue; /* if no names, next arg */ } } else p = word; /* no expansion */ if (prompt) { /* check */ sprintf(printline,"mput %s? ",p); putstring(printline); if (ftpgets(answer,20,1)==ABORT) { /* abort */ command[0] = '\0'; /* no more processing */ break; /* quit immediately */ } if (tolower(*(stpblk(answer)))!='y') continue; } sprintf(command,"STOR \"%s\"",p); /* name may have spl chars */ ftpdo(command,ofilename); } while (glob && p!=NULL); /* Not last expansion */ } return(TRUE); case PUT: case SEND: /* put file */ while (!(*(stpblk(command)))) { /* no args */ putstring("File: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } sprintf(line,"STOR %s",command); /* put file */ ftpdo(line,ofilename); return(TRUE); case PWD: i = ftpdo("XPWD",ofilename); /* try XPWD */ if (i!=ERROR) return(TRUE); nputs("Trying again..."); ftpdo("PWD",ofilename); /* else try PWD */ return(TRUE); case QUOTE: while (!(*(stpblk(command)))) { /* no arg */ putstring("Command: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } ftpdo(command,ofilename); /* send command */ return(TRUE); case REMOTEHELP: /* get help */ if (*(stpblk(command))) { /* for specific command */ sprintf(line,"HELP %s",command); ftpdo(line,ofilename); } else ftpdo("HELP",ofilename); /* generic help */ return(TRUE); case RENAME: /* rename remote file */ while (!(*(stpblk(command)))) { /* no arg */ putstring("From: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } getword(command,word); sprintf(line,"RNFR %s",word); ftpdo(line,ofilename); /* send rename from name */ while (!(*(stpblk(command)))) { /* no second arg */ putstring("To: "); if (ftpgets(command,100,1)==ABORT) { ftpdo("ABOR",ofilename); return(FALSE); } } sprintf(line,"RNTO %s",command); /* send rename to name */ ftpdo(line,ofilename); return(TRUE); case RMDIR: /* remove remote dir */ while (!(*(stpblk(command)))) { /* no arg */ putstring("Directory: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } sprintf(line,"XRMD %s",command); /* try XRMD */ i = ftpdo(line,ofilename); if (i!=ERROR) return(TRUE); nputs("Trying again..."); sprintf(line,"RMD %s",command); /* try RMD */ ftpdo(line,ofilename); return(TRUE); case STRUCT: /* set structure type - only file */ getword(command,word); lowercase(word); if (strncmp(word,"file",strlen(word))) nputs("We only support file structure, sorry."); else nputs("Structure is file."); return(TRUE); case TYPE: /* set transfer type */ if (!getword(command,word)) { /* no arg, just show */ if (ftpfilemode==FASCII) nputs("Transfer type is ascii."); else nputs("Transfer type is binary."); } lowercase(word); if (!strncmp(word,"ascii",strlen(word))) ftpdo("TYPE A",ofilename); else if (!strncmp(word,"binary",strlen(word)) || !strncmp(word,"image",strlen(word))) ftpdo("TYPE I",ofilename); else { sprintf(printline,"Unrecognized type: %s",word); nputs(printline); } return(TRUE); case USER: /* login to remote machine */ if (!(*(stpblk(command)))) { /* null response to prompt ok */ putstring("Username: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } sprintf(line,"USER %s",command); /* username */ if (ftpdo(line,ofilename)==TRUE) return(TRUE); putstring("Password: "); if (ftpgets(word,20,0)==ABORT) return(FALSE); /* no echoing */ sprintf(line,"PASS %s",word); /* password */ if (ftpdo(line,ofilename)==INCOMPLETE) { /* if account needed */ do { putstring("Account: "); if (ftpgets(command,100,1)==ABORT) return(FALSE); } while (!(*(stpblk(command)))); sprintf(line,"ACCT %s",command); ftpdo(line,ofilename); } return(TRUE); default: /* unknown command */ sprintf(printline,"***Program error: Unknown command no: %d",cmdno); nputs(printline); break; } } } /************************************************************************/ /* ftpdo * Do whatever command is sent from the user interface using * userftpd, the background file handler * Returns code from ftpreplies /************************************************************************/ ftpdo(s,ofile) char *s,*ofile; { int i,rcode; char name[50],name2[50]; for (i=0; i<4; i++) { s[i] = toupper(s[i]); /* command to upper case */ } if (!strncmp(s,"STOR",4)) { /* put file */ getword(&s[5],name); /* first arg - local file */ if (!s[5]) strcpy(&s[5],name); /* if only one argument */ else { getword(&s[5],name2); /* second arg - removes quotes etc. */ strcpy(&s[5],name2); /* copy back into command */ } if (0 > (ftpfh = open(name,O_RAW))) { /* open local file */ nputs(" Cannot open file to transfer."); return(-1); } ftpdata = netlisten(ftpport()); /* open data connection */ ftpstate = 20; } else if (!strncmp(s,"RETR",4)) { /* get file */ getword(&s[5],name); /* remote file */ if (s[5]) { /* two args present */ getword(&s[5],name2); /* local file */ ftpfh = creat(name2,O_RAW); /* open local file */ } else ftpfh = creat(name,O_RAW); /* local name same as remote */ if (ftpfh<0) { sprintf(printline,"Cannot open file to receive: %s",name); nputs(printline); return(-1); } strcpy(&s[5],name); /* put remote name back into command */ ftpdata = netlisten(ftpport()); /* open data connection */ ftpstate = 30; } else if (!strncmp(s,"LIST",4) || !strncmp(s,"NLST",4)) { if (capture) captlist[0] = '\0'; /* where to put incoming data */ ftpdata = netlisten(ftpport()); /* data connection */ ftpstate = 40; } else if (!strncmp(s,"TYPE",4)) { if (toupper(s[5]) == 'I') ftpfilemode = FIMAGE; /* remember mode */ else if (toupper(s[5]) == 'A') ftpfilemode = FASCII; } dumpcon(ftpnum); /* clear command connection */ netpush(ftpnum); netwrite(ftpnum,s,strlen(s)); /* send command */ netwrite(ftpnum,"\r\n",2); /* terminates command */ if (!capture && ofile[0]) { /* command redirected */ if ((ftpstate!=20) && (ftpstate!=30)) { /* not get or put */ if (0 > (ftpfh = open(ofile,O_CREAT|O_APPEND|O_WRONLY,S_IWRITE))) nputs(" Cannot open output file."); else if (ftpdata > -1) { ftpstate = 30; /* act as get, since data goes into file */ } else { close(ftpfh); ftpfh = 0; } } } if (debug) { sprintf(printline,"---> %s",s); /* show command sent */ nputs(printline); if (debug>=7) { for (i=0; i-1) netclose(ftpdata); if (ftpfh!=0) close(ftpfh); ftpdata = -1; ftpfh = 0; } return(i); } /************************************************************************/ /* ftpport * return a new port number so that we don't try to re-use ports * before the mandatory TCP timeout period. (lifetime of a packet) * use a time-based initial port selection scheme. /************************************************************************/ long int time(); /* global */ unsigned int ftpport() { unsigned int i,rcode; unsigned char hostnum[5]; char sendline[60]; /* for port command */ if (!sendport) { /* default port */ return(HFTP-1); } if (curftpprt < 40000) { /* restart cycle */ i = (unsigned int)time(NULL); curftpprt = 40000 + (i & 0x3fff); } i = curftpprt--; /* get port, update for next time */ netgetip(hostnum); /* get my ip number */ sprintf(sendline,"PORT %d,%d,%d,%d,%d,%d\r\n",hostnum[0],hostnum[1],hostnum[2], hostnum[3],i/256,i&255); /* full port number */ if (debug>1) nputs(sendline); netpush(ftpnum); /* empty command connection */ netwrite(ftpnum,sendline,strlen(sendline)); /* send PORT command */ /* check result of command, make sure port was okay */ /* return 0 on error */ /* ????? */ dumpcon(ftpnum); ftpreplies(ftpnum,&rcode); /* get response */ return(i); /* port number */ } /************************************************************************/ /* ftpreplies * get responses to commands to server * return TRUE on successful completion, FALSE on transient negative * completion, INCOMPLETE if more commands needed for operation, * NONE on lost connection, ABORT on user abort and ERROR on failure * /************************************************************************/ ftpreplies(cnum,rcode) int cnum,*rcode; { int cnt,ev,j=0; while (1) { cnt = rgetline(cnum); /* get line form remote host */ if (cnt==NONE) return(NONE); /* lost connection */ if (cnt==ABORT) { /* user abort */ netpush(cnum); netwrite(cnum,"ABOR\r\n",6); /* send abort */ return(ABORT); } if (!sscanf(s,"%d",rcode)) *rcode = -1; /* continuation line */ if ((*rcode/100)==2) { /* positive completion */ dumpcon(ftpnum); /* clear command connection */ while (ftpdata>=0) { /* wait till transfers complete */ ev = checkevent(); if (ev==NONE) return(NONE); /* lost connection */ if (ev==ABORT) break; /* user abort */ if (ev==HAVEDATA) dumpcon(ftpnum); /* msg on command connection */ } } if (verbose || (*rcode==-1) || (*rcode>500)) telnet(cnt); /* informative/error msg or display on */ if (j) if (*rcode==j) j=0; /* end of continuation */ else continue; else if (s[3]=='-') { /* line with continuations */ j = *rcode; /* remember end code */ continue; } switch(*rcode/100) { /* first digit */ case 1: /* preliminary */ continue; case 2: /* positive completion */ return(TRUE); break; case 3: /* intermediate */ return(INCOMPLETE); break; case 4: /* transient negative completion */ return(FALSE); break; case 5: /* Permanent negative completion */ return(ERROR); break; default: nputs("Server response not understood. Terminating command\n"); return(ERROR); } } } /************************************************************************/ /* rgetline - get a line from remote server * return ABORT on user ABORT, NONE on lost connection, * length of received line on success * /************************************************************************/ rgetline(cnum) int cnum; { int cnt,i=0,ev; while (1) { ev = checkevent(); switch (ev) { case ABORT: /* user abort */ case NONE: /* lost connection */ return(ev); case HAVEDATA: if (fromtty && n_scrlck()) cnt = 0; /* if paused, nothing to do */ else while (1) { cnt = netread(cnum,&s[i],1); /* get some from queue */ if (!cnt) break; /* nothing available */ if (s[i++]=='\n') { /* end of line */ s[i] = '\0'; return(i); /* return line length */ } } break; default: /* ignore other events */ break; } } } /************************************************************************/ /* breakstop * handle cntrl-break /************************************************************************/ breakstop() { foundbreak = 1; return(0); } /************************************************************************/ /* userftpd * FTP receive and send file functions /************************************************************************/ userftpd() { int i,r1,r2; double rate; static int stopcapture=FALSE; /* bytes xferred % 1024 */ static long tbytes; switch (ftpstate) { default: /* unknown */ break; case 40: /* start LIST */ if (!netest(ftpdata)) { start = time(NULL); /* current time */ tbytes = 0; /* received so far */ ftpstate = 41; } break; case 41: /* get started */ do { if (capture && !stopcapture) { /* into captlist */ fcnt = netread(ftpdata,xs,READSIZE); if (strlen(captlist)+fcnt>=2000) { /* full */ if (!fromtty || !n_scrlck()) nputs(&xs[2001-strlen(captlist)]); /* display excess chars */ strncat(captlist,xs,2000-strlen(captlist)); nputs("Error: capture list too long"); stopcapture = TRUE; } else strncat(captlist,xs,fcnt); /* append */ } else { if (fromtty && n_scrlck()) break; /* paused */ fcnt = netread(ftpdata,xs,READSIZE); for (i=0; i < fcnt; i++) nputchar(xs[i]); /* display */ } if (fcnt>0) tbytes += fcnt; /* how much */ } while (fcnt>0); /* till no more input */ break; case 30: /* receive */ if (!netest(ftpdata)) { /* connection made */ start = time(NULL); ftpstate = 31; len = xp = 0; tbytes = 0; } break; case 31: /* * file has already been opened, take everything from the connection * and place into the open file: ftpfh */ do { /* wait until xs is full before writing to disk */ if (len <= 0) { if (xp) { write(ftpfh,xs,xp); xp = 0; } len = BUFFERS; /* expected or desired len to go */ } /* how much to read */ if (len < READSIZE) i = len; else i = READSIZE; fcnt = netread(ftpdata,&xs[xp],i); if (fcnt>0) { /* adjust counts */ len -= fcnt; xp += fcnt; tbytes += fcnt; } if (debug>1) { sprintf(printline,"len %d xp %d fcnt %d",len,xp,fcnt); nputs(printline); } if (fcnt < 0) { /* connection closed */ write(ftpfh,xs,xp); /* write last block */ if (ftpfilemode == FASCII) { write(ftpfh,"\032",1); /* EOF char */ } close(ftpfh); ftpfh = 0; } if (hash) { /* hash mark printing */ for (i=((tbytes-fcnt)%1024+fcnt)/1024; i; i--) nputchar('#'); } } while (fcnt > 0); break; case 20: /* send */ if (!netest(ftpdata)) { /* connection made */ start = time(NULL); ftpstate = 21; filelength = lseek(ftpfh,0L,2); /* how long is file? */ lseek(ftpfh,0L,0); /* back to beginning */ if (ftpfilemode == FASCII) { filelength--; /* leave off ctrl-Z */ } towrite = 0; xp = 0; tbytes = 0; } break; case 21: /* * transfer file(s) to the other host via ftp request * file is already open = ftpfh */ if (towrite <= xp) { /* need to read again */ if (filelength < (long)BUFFERS) i = (int)filelength; else i = BUFFERS; towrite = read(ftpfh,xs,i); xp = 0; } i = netwrite(ftpdata,&xs[xp],towrite-xp); if (i > 0) { /* send successful, adjust counts */ xp += i; filelength -= i; tbytes += i; } if (debug>1) { sprintf(printline,"i %d xp %d towrite %d",i,xp,towrite); nputs(printline); } if (hash) { /* hash printing */ for (r1=((tbytes-i)%1024+i)/1024; r1; r1--) nputchar('#'); } /* * done if: the file is all read from disk and all sent * or other side has ruined connection */ if ((filelength <= 0L && xp >= towrite) || netest(ftpdata)) { ftpstate = 22; } break; case 22: /* send done */ /* wait for other side to accept everything and then close */ if ( 0 >= (r1=netpush(ftpdata))) fcnt = -1; if (debug>1) { sprintf(printline,"fcnt %d r1 %d\n",fcnt,r1); nputs(printline); } break; } /* end of switch */ /* * after reading from connection, if the connection is closed, * reset up shop. */ if (fcnt < 0) { /* connection lost */ if (ftpfh > 0) { /* close file */ close(ftpfh); ftpfh = 0; } ftpstate = 0; /* done */ fcnt = 0; i = (int) (time(NULL)-start); /* how long to transfer */ if (!i) rate = ((double) tbytes) / 1024.0; else rate = ((double) tbytes) / (i * 1024.0); r1 = (int) rate; /* integer part of rate */ r2 = (int) ((rate - (double) r1) * 1000); sprintf(printline,"Transferred %ld bytes in %d seconds (%d.%03d Kbytes/sec)",tbytes,i,r1,r2); if (hash) nputs(""); if (verbose) nputs(printline); netclose(ftpdata); /* close connection */ ftpdata = -1; if (bell) nputchar(7); } return(TRUE); } /************************************************************************/ /* getword: remove a word from a string. Things within quotes are * assumed to be one word. * return TRUE on success, FALSE on end of string /************************************************************************/ getword(string,word) char *string,*word; { char *p,*q; int i=0; if (debug>4) { sprintf(printline,"getword: string is %s",string); nputs(printline); } p = stpblk(string); /* skip leading blanks */ if (!(*p)) { /* no words in string */ word[0] = '\0'; return(FALSE); } if (*p=='!') { /* ! is a word */ word[0] = *p; word[1] = '\0'; strcpy(string,++p); return(TRUE); } if (*p=='\"') { /* word delimited by quotes */ while (p[++i] && p[i]!='\"') word[i-1] = p[i]; word[i-1] = '\0'; if (!p[i]) nputs("Missing \". Assumed at end of string."); else i++; q = p+i; } else q = stptok(p, word, 50, " \t\r\n"); /* get word, max len 50 */ p = stpblk(q); /* remove trailing blanks */ strcpy(string,p); /* remove extracted stuff */ return(TRUE); } /************************************************************************/ /* lowercase: convert a string to lowercase * /************************************************************************/ lowercase(word) char *word; { int i; for (i=0; word[i]=tolower(word[i]); i++); return(TRUE); } /************************************************************************/ /* checkoredir: check for output redirection. If the command contains a * >, assume a filename follows and extract it. Remove the redirection * from the original command. * Also change \ to / * return TRUE if redirection specified, FALSE otherwise /************************************************************************/ checkoredir(command,filename,slashflip) char *command,*filename; int slashflip; { int i; filename[0] = '\0'; for (i=0; (command[i]!='>'); i++) { /* process command part */ if (slashflip && command[i] == '\\') command[i] = '/'; if (!command[i]) return(FALSE); /* no redirection */ } getword(&command[i+1],filename); /* get redirected filename */ command[i] = '\0'; return(TRUE); } /************************************************************************/ /* getdir: get current directory. Finds current drive and current path * on drive, returns a string. * /************************************************************************/ getdir(drive,path) int drive; char *path; { char partpath[64]; if (!drive) drive = getdsk(); /* current disk */ getcd(drive+1,partpath); /* current dir */ sprintf(path,"%c:\\%s",'A'+drive,partpath); return(TRUE); } /************************************************************************/ /* finduniq: find name that is a unique prefix of one of the entries in * a list. Return position of the entry, NONE if none, AMBIGUOUS if more * than one. * /************************************************************************/ finduniq(name,list,listsize) char *name, *list[]; int listsize; { int i,j=NONE,len; len = strlen(name); for (i=0; i or /************************************************************************/ getnname(string,word) char *string,*word; { char *s; s = string; while ((*string=='\n') || (*string=='\r')) string++; /* skip initial newlines */ if (!(*string)) return(FALSE); /* end of captlist */ while ((*string!='\n') && (*string!='\r') && (*string)) *(word++) = *(string++); while ((*string=='\n') || (*string=='\r')) string++; /* skip trailing newline */ *word = '\0'; strcpy(s,string); return(TRUE); } /***************************************************************************/ /* dosescape * escape to dos for processing * put the connections to automated sleep while in DOS /************************************************************************/ dosescape() { int i; nputs("Warning, some programs will interfere with network communication and can"); nputs("cause lost connections. Do not run any network programs from this DOS shell."); nputs("Type 'EXIT' to return to FTP"); /* * invoke a put-to-sleep routine which calls netsleep every 8/18ths of a sec */ tinst(); i = system("command"); /* call DOS */ tdeinst(); if (i < 0) { nputs("\n\nError loading COMMAND.COM"); nputs("Make sure COMMAND.COM is specified under COMSPEC."); nputs("It must also be in a directory which is in your PATH statement."); } if (fromtty) n_row(); return(0); } static int scrsetup; nputs(line) char *line; { if (!scrsetup) { scrsetup = 1; if (fromtty) { n_clear(); /* clear screen */ n_wrap(1); /* cursor positioning */ n_cur(0,0); n_color(2); /* set color to green */ } nputs(" National Center for Supercomputing Applications"); nputs(" FTP version 1.04 7/5/88\n"); } if (fromtty) n_puts(line); else puts(line); return(TRUE); } nputchar(ch) char ch; { if (fromtty) n_putchar(ch); else putchar(ch); return(TRUE); } ftpdata))) fcnt = -1; if (debug>1) { sprintf(printline,"fcnt %d r1 %d\n",fcnt,r1); nputs(printline); } break; } /* end of switch */ /* * after reading from connection, if the connection is closed, * reset up shop. */ if (fcnt < 0) { /* connection lost */ if (ftpfh > 0) { /* close file */ source/vs/vsdata.h 644 144 13 6365 4267455063 7512 /* * %W% (NCSA) %G% * * Virtual Screen Kernel Data/Structure Definitions * (vsdata.h) * * National Center for Supercomputing Applications * by Gaige B. Paulsen * * This file contains the control and interface calls for the NCSA * Virtual Screen Kernel. * * Version Date Notes * ------- ------ --------------------------------------------------- * 0.01 861102 Initial coding -GBP * 2.1 871130 NCSA Telnet 2.1 -GBP * 2.2 880715 NCSA Telnet 2.2 -GBP * */ #define MAXWID 132 /* The absolute maximum number of chars/line */ struct VSline { struct VSline *next, /* Pointer to next line */ *prev; /* Pointer to previous line */ char *text; /* Text for the line */ }; typedef struct VSline VSline; struct VSscrn { VSline *scrntop, /* top of the current screen */ *buftop, /* top of the current buffer */ *vistop, /* top of the visible area of screen */ *attrst[24], /* pointer to each of the 24 attribute lines */ *linest[24]; /* pointer to each of the 24 real screen lines */ int maxlines, /* maximum number of lines to save off top */ numlines, /* number of lines currently saved off top */ allwidth, /* real maximum width for this window */ maxwidth, /* current maximum width for this window */ savelines, /* save lines off top? 0=no */ ESscroll, /* Scroll screen when ES received */ attrib, /* current attibute */ x,y, /* current cursor positon */ Px,Py,Pattrib, /* saved cursor position and attribute */ VSIDC, /* Insert/delete character mode 0=draw line */ DECAWM, /* Auto Wrap Mode 0=off */ DECCKM, /* Cursor Key Mode */ DECPAM, /* keyPad Application Mode */ G0,G1, /* Character set identifiers */ charset, /* Character set mode */ IRM, /* Insert/Replace Mode */ escflg, /* Current Escape level */ top, bottom, /* Vertical bounds of the screen */ Rtop,Rbottom, /* Vertical bounds of Region */ Rleft,Rright, /* Horizontal bounds of Region */ parmptr; /* pointer to current parm */ int parms[6]; /* Ansi Parameters */ char *tabs; /* pointer to array for tab settings */ }; typedef struct VSscrn VSscrn; struct VSscrndata { VSscrn *loc; /* Location of the Screen record for this scrn */ int stat; /* status of this screen (0=Uninitialized, */ /* 1=In Use */ /* 2=Inited, but not IU */ }; typedef struct VSscrndata VSscrndata; #ifdef VSMASTER VSscrn *VSIw; int VSIwn; #else extern VSscrn *VSIw; extern int VSIwn; #endif word delimited by quotes */ while (p[++i] && p[i]!='\"') word[i-1] = p[i]; word[i-1] = '\0'; if (!p[i]) nputs("Missing \". Assumed at end of string."); else i++; q = p+i; } else q = stptok(p, word, 50, " \t\r\n"); /* get word, max len 50 */ p = ssource/vs/vsem.c 644 144 13 25413 4267455063 7210 #ifdef lint static char *SCCSid = "%W% (NCSA) %G%"; #endif /* * * Virtual Screen Kernel Emulation Routines * (vsem.c) * * National Center for Supercomputing Applications * by Gaige B. Paulsen * * This file contains the private emulation calls for the NCSA * Virtual Screen Kernel. * * Version Date Notes * ------- ------ --------------------------------------------------- * 0.01 861102 Initial coding -GBP * 0.10 861111 Added/Modified VT emulator -GBP * 0.50 861113 First compiled edition -GBP * 2.1 871130 NCSA Telnet 2.1 -GBP * 2.2 880715 NCSA Telnet 2.2 -GBP */ #include "vsdata.h" #include "vskeys.h" VSem(c,ctr) register int ctr; register unsigned char *c; { register int i,sx; register int escflg; int insert,ocount,attrib,wrapit,extra,offend; char *acurrent,*current,*start; escflg= VSIw->escflg; while (ctr>0) { while ( (escflg==0) && (ctr>0) && (*c<32) ) { switch(*c) { case 0x1b: escflg++; break; case 0x0e: if (VSIw->G1) VSIw->attrib=VSgraph(VSIw->attrib); else VSIw->attrib=VSnotgraph(VSIw->attrib); VSIw->charset=1; break; case 0x0f: if (VSIw->G0) VSIw->attrib=VSgraph(VSIw->attrib); else VSIw->attrib=VSnotgraph(VSIw->attrib); VSIw->charset=0; break; case 0x07: RSbell(VSIwn); break; case 0x08: VSIw->x--; if (VSIw->x<0) VSIw->x=0; break; case 0x0c: VSIindex(); break; case 0x09: /* Later change for versatile tabbing */ VSItab(); break; case 0x0a: VSIindex(); break; case 0x0d: VSIw->x=0; break; case 0x0b: VSIindex(); break; #ifdef CISB case 0x10: bp_DLE( c, ctr); ctr = 0; break; case 0x05: bp_ENQ(); break; #endif CISB } c++;ctr--; } while( (ctr>0) && (escflg==0) && (*c>=32) ) { /* the following line is important, both are * chars */ current = start = &VSIw->linest[VSIw->y]->text[VSIw->x]; acurrent = &VSIw->attrst[VSIw->y]->text[VSIw->x]; attrib = VSIw->attrib; insert = VSIw->IRM; /* boolean */ ocount = VSIw->x; wrapit=0;offend=0;extra=0; sx=VSIw->x; if ( VSIw->x > VSIw->maxwidth ) { if ( VSIw->DECAWM ) { VSIw->x=0; VSIindex(); } else VSIw->x=VSIw->maxwidth; current = start = &VSIw->linest[VSIw->y]->text[VSIw->x]; acurrent = &VSIw->attrst[VSIw->y]->text[VSIw->x]; ocount = VSIw->x; sx=VSIw->x; } while( (ctr>0) && (*c>=32) && (offend==0)) { if (insert) VSIinschar(1); *current = *c; *acurrent=attrib; c++; ctr--; if (VSIw->x < VSIw->maxwidth) {acurrent++; current++; VSIw->x++; } else { if (VSIw->DECAWM) { VSIw->x++; offend=1; } else { VSIw->x=VSIw->maxwidth; extra=1; } } } if (insert) { VSIinsstring(VSIw->x-ocount+offend+extra,start); /* actually just decides which RS to use */ } else VSIdraw(VSIwn,sx,VSIw->y,VSIw->attrib,VSIw->x-ocount+offend+extra,start); } while( (ctr>0) && (escflg==1) ) { switch(*c) { case 0x08: VSIw->x--; if (VSIw->x<0) VSIw->x=0; break; case '[': VSIapclear(); escflg++; break; case '7': VSIsave(); escflg = 0; break; case '8': VSIrestore(); escflg = 0; break; case 'c': VSIreset(); break; case 'D': VSIindex(); escflg = 0; break; case 'E': VSIw->x=0; VSIindex(); escflg = 0; break; case 'M': VSIrindex(); escflg = 0; break; case '>': VSIw->DECPAM=0; escflg = 0; break; case '=': VSIw->DECPAM=1; escflg = 0; break; case 'Z': VTsendident(); escflg = 0; break; case '#': escflg=3; break; case '(': escflg=4; break; case ')': escflg=5; break; case 'H': VSIw->tabs[VSIw->x]='x'; escflg=0; break; #ifdef CISB case 'I': bp_ESC_I(); break; #endif CISB default: escflg = 0; break; } c++;ctr--; } while ( (escflg==2) && (ctr>0) ) { switch(*c) { case 0x08: VSIw->x--; if (VSIw->x<0) VSIw->x=0; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (VSIw->parms[VSIw->parmptr]<0) VSIw->parms[VSIw->parmptr]=0; VSIw->parms[VSIw->parmptr] *= 10; VSIw->parms[VSIw->parmptr] += *c-'0'; break; case '?': VSIw->parms[VSIw->parmptr++] = -2; break; case ';': VSIw->parmptr++; break; case 'A': if (VSIw->parms[0]<1) VSIw->y--; else VSIw->y-=VSIw->parms[0]; if ( VSIw->y < VSIw->top ) VSIw->y=VSIw->top; VSIrange(); escflg = 0; break; case 'B': if (VSIw->parms[0]<1) VSIw->y++; else VSIw->y+=VSIw->parms[0]; if ( VSIw->y > VSIw->bottom ) VSIw->y=VSIw->bottom; VSIrange(); escflg = 0; break; case 'C': if (VSIw->parms[0]<1) VSIw->x++; else VSIw->x+=VSIw->parms[0]; VSIrange(); if (VSIw->x > VSIw->maxwidth) VSIw->x = VSIw->maxwidth; escflg = 0; break; case 'D': if (VSIw->parms[0]<1) VSIw->x--; else VSIw->x-=VSIw->parms[0]; VSIrange(); escflg = 0; break; case 'f': case 'H': VSIw->x=VSIw->parms[1]-1; VSIw->y=VSIw->parms[0]-1; VSIrange(); escflg = 0; break; case 'K': switch(VSIw->parms[0]) { case -1: case 0: VSIeeol(); break; case 1: VSIebol(); break; case 2: VSIel(-1); break; default: escflg=0; break; } escflg = 0; break; case 'J': switch(VSIw->parms[0]) { case -1: case 0: VSIeeos(); break; case 1: VSIebos(); break; case 2: VSIes(); break; default: escflg=0; break; } escflg = 0; break; case 'm': { int temp=0; while (temp<=VSIw->parmptr) { if (VSIw->parms[temp]<1) VSIw->attrib&=128; else VSIw->attrib|=1<<(VSIw->parms[temp]-1); temp++; } } escflg = 0; break; case 'q': escflg = 0; break; case 'c': VTsendident(); escflg = 0; break; case 'n': switch(VSIw->parms[0]) { case 5: VTsendstat(); break; case 6: VTsendpos(); break; } escflg = 0; break; case 'L': if(VSIw->parms[0]<1) VSIw->parms[0]=1; VSIinslines(VSIw->parms[0],-1); escflg = 0; break; case 'M': if(VSIw->parms[0]<1) VSIw->parms[0]=1; VSIdellines(VSIw->parms[0],-1); escflg = 0; break; case 'P': if(VSIw->parms[0]<1) VSIw->parms[0]=1; VSIdelchars(VSIw->parms[0]); escflg = 0; break; case 'r': if (VSIw->parms[0]<0) VSIw->top=0; else VSIw->top=VSIw->parms[0]-1; if (VSIw->parms[1]<0) VSIw->bottom=23; else VSIw->bottom=VSIw->parms[1]-1; if (VSIw->top<0) VSIw->top=0; if (VSIw->top>22) VSIw->top=22; if (VSIw->bottom<1) VSIw->bottom=23; if (VSIw->bottom>23) VSIw->bottom=23; VSIw->x=0; VSIw->y=0; escflg = 0; break; case 'h': VSIsetoption(1); escflg = 0; break; case 'l': VSIsetoption(0); escflg = 0; break; case 'g': if (VSIw->parms[0]==3) VSItabclear(); else if (VSIw->parms[0]==0 || VSIw->parms[0]<0) VSIw->tabs[VSIw->x]=' '; escflg =0; break; default: /* Dag blasted strays... */ escflg=0; break; } c++; ctr--; } while ( (escflg==3) && (ctr>0) ) { /* # Handling */ switch(*c) { case 0x08: VSIw->x--; if (VSIw->x<0) VSIw->x=0; break; case '8': VTalign(); escflg=0; break; default: escflg=0; break; } c++; ctr--; } while ( (escflg==4) && (ctr>0) ) { /* ( Handling */ switch(*c) { case 0x08: VSIw->x--; if (VSIw->x<0) VSIw->x=0; break; case 'A': case 'B': case '1': VSIw->G0=0; if ( !VSIw->charset ) VSIw->attrib=VSnotgraph(VSIw->attrib); escflg=0; break; case '0': case '2': VSIw->G0=1; if ( !VSIw->charset ) VSIw->attrib=VSgraph(VSIw->attrib); escflg=0; break; default: escflg=0; break; } c++; ctr--; } while ( (escflg==5) && (ctr>0) ) { /* ) Handling */ switch(*c) { case 0x08: VSIw->x--; if (VSIw->x<0) VSIw->x=0; break; case 'A': case 'B': case '1': VSIw->G1=0; if ( VSIw->charset ) VSIw->attrib=VSnotgraph(VSIw->attrib); escflg=0; break; case '0': case '2': VSIw->G1=1; if ( VSIw->charset ) VSIw->attrib=VSgraph(VSIw->attrib); escflg=0; break; default: escflg=0; break; } c++; ctr--; } if ((escflg>2) && (ctr>0)) {escflg=0; c++; ctr--;} } VSIw->escflg=escflg; } ]; ocount = VSIw->x; sx=VSIw->x; } while( (ctr>0) && (*c>=32) && (offend==0)) { if (insert) VSIinschar(1); *current = *c; *acurrent=attrib; c++; ctr--; source/vs/vsinit.h 644 144 13 150 4267455063 7506 /* %W% (NCSA) %G% One define with the # of lines to allocate by default */ #define VSDEFLINES 24 it.h× vsinterf.cØ vsintern.cÙˆvskeys.hsource/vs/vsinterf.c 644 144 13 44336 4267455063 10103 #ifdef lint static char *SCCSid = "%W% (NCSA) %G%"; #endif /* * * Virtual Screen Kernel Interface * (vsinterf.c) * * by Gaige B. Paulsen * * This file contains the control and interface calls for the NCSA * Virtual Screen Kernal. * * VSinit(maxwidth) - Initialize the VSK * VSnewscreen(maxlines,scrnsave) - Initialize a new screen. * VSdetatch(w) - Detach screen w * VSredraw(w,x1,y1,x2,y2) - redraw region for window w * VSwrite(w,ptr,len) - write text @ptr, length len * VSclear(w) - clear w's real screen * VSkbsend(w,k,echo) - send keycode k's rep. out window w (w/echo if req.) * VSclearall(w) - clear w's real and saved screen * VSreset(w) - reset w's emulator (as per TERM) * VSgetline(w,y) - get a ptr to w's line y * VSsetrgn(w,x1,y1,x2,y2) - set local display region * VSscrolback(w,n) - scrolls window w back n lines * VSscrolforward(w,n) - scrolls window w forward n lines * VSscrolleft(w,n) - scrolls window w left n columns * VSscrolright(w,n) - scrolls window w right n columns * VSscrolcontrol(w,scrlon,offtop) - sets scroll vars for w * VSgetrgn(w,&x1,&y1,&x2,&y2) - returns set region * VSsnapshot(w) - takes a snapshot of w * * Version Date Notes * ------- ------ --------------------------------------------------- * 0.01 861102 Initial coding -GBP * 0.10 861113 Added some actual program to this file -GBP * 0.15 861114 Initiated Kludge Operation-GBP * 0.50 861123 Parameters added to VSnewscreen -GBP * 0.90 870203 Added the kbsend routine -GBP * 2.1 871130 NCSA Telnet 2.1 -GBP * 2.2 880715 NCSA Telnet 2.2 -GBP * */ #define VSMASTER #include "vsdata.h" #include "vskeys.h" #include "vsinit.h" #ifdef DEBUGMAC pascal void DEBUGGER() = 0xa9ff; #endif DEBUGMAC int VSmax=0,VSinuse=0; /* Internal variables for use in managing windows */ VSscrndata *VSscreens; VSline *malloc(); int VSinit(max) int max; { int i; RSinitall(); VSmax=max; VSIwn=0; if((VSscreens=(VSscrndata *)malloc(max*sizeof(VSscrndata)))==0L) return(-2); for(i=0;i=VSmax) return(-1); VSIwn=0; while((VSIwn=VSmax) return(-1); if (VSscreens[VSIwn].stat==2) { VSIw=VSscreens[VSIwn].loc; if (VSIw==0L) return(-7); } else if ((VSscreens[VSIwn].loc=VSIw=(VSscrn *)malloc(sizeof(VSscrn)))==0L) { VSscreens[VSIwn].loc=0L; return(-2); } if ( VSscreens[VSIwn].stat !=2 ) { VSIw->maxlines=maxlines; VSIw->numlines=VSDEFLINES; } VSIw->maxwidth=maxwid-1; VSIw->allwidth=maxwid-1; VSIw->savelines=screensave; VSIw->attrib=0; VSIw->x=0; VSIw->y=0; VSIw->charset=0; VSIw->G0=0; VSIw->G1=1; VSIw->VSIDC=IDC; VSIw->DECAWM=0; VSIw->DECCKM=0; VSIw->DECPAM=0; VSIw->IRM=0; VSIw->escflg=0; VSIw->top=0; VSIw->bottom=23; VSIw->parmptr=0; VSIw->Rtop=0; VSIw->Rleft=0; VSIw->Rright=79; VSIw->Rbottom=23; VSIw->ESscroll=1; VSinuse++; if (VSscreens[VSIwn].stat==2) { VSscreens[VSIwn].stat=1; VSiclrbuf(); VSItabinit(); return(VSIwn); } VSscreens[VSIwn].stat=1; VSIw->tabs = (char *)malloc(maxwid); VSIw->buftop=last=malloc(sizeof(VSline)); if((last->text=(char *)malloc(maxwid))==0L) return(-2); last->next=0L; last->prev=0L; for(i=0; i< (24+VSDEFLINES); i++) { tt=malloc(sizeof(VSline)); if((tt->text=(char *)malloc(maxwid))==0L) return(-2); tx= tt->text; for(j=0; j<=VSIw->allwidth;j++) *tx++=' '; /* Clear out the blumin' line */ tt->next=0L; tt->prev=last; last->next=tt; last=tt; } VSIw->scrntop= last; for (i=23; i>0; i--) VSIw->scrntop = VSIw->scrntop->prev; /* Go to right place... */ VSIw->attrst[0]=last=malloc(sizeof(VSline)); if((last->text=(char *)malloc(maxwid))==0L) return(-2); last->next=0; last->prev=0; for(i=0; i<24; i++) { tt=malloc(sizeof(VSline)); if((tt->text=(char *)malloc(maxwid))==0L) return(-2); tt->next=0L; tt->prev=last; last->next=tt; last=tt; } VSIlistndx(VSIw->scrntop, VSIw->attrst[0]); /*Assign lists */ VSIw->attrst[ 0]->prev= VSIw->attrst[23]; /* Make attr circ. */ VSIw->attrst[23]->next= VSIw->attrst[ 0]; VSiclrbuf(); VSItabinit(); VSIw->vistop=VSIw->scrntop; if (VSIw->linest[23]->next != 0L) return(-42); return(VSIwn); } VSdestroy(w) int w; { VSline *p,*oldp; if (VSvalids(w)!=0) return(-3); p=VSIw->attrst[0]; do { free(p->text); oldp=p; p=p->next; free(oldp); } while ( (p!=0L) && (p!=VSIw->attrst[ 0]) ); p=VSIw->buftop; do { free(p->text); oldp=p; p=p->next; free(oldp); } while ( (p!=0L) && (p!=VSIw->buftop) ); free( VSIw->tabs); free( VSIw); VSscreens[w].stat=0; VSIwn = -1; VSinuse--; /* SCA '87 */ return(0); } #ifdef DEBUGPC int VSckconsist(out,verbose) int out,verbose; { VSline *topa,*topt,*a,*t; int line, i; for (i=0; iattrst[ 0]->prev; topt=VSIw->linest[ 0]->prev; a = VSIw->attrst[0]->next; t= VSIw->linest[0]->next; if (topa && out) printf(" Attrib thinks its circular\n"); if (topa != VSIw->attrst[23]->next) printf("***********BUT IT'S NOT*************\n"); for (line=1; line<24; line++) { if (a != VSIw->attrst[ line]) { if (out) printf(" Attrib line %d is wrong !\n", line); else return(-1); } a = a->next; if ( !a) { if (out) printf(" Attrib line %d is NULL\n", line); else if (!out && line!=23) return(-2); } } if (topt && out) printf(" Linest thinks its circular\n"); if (VSIw->linest[23]->next) printf(" More than 23 lines.... \n"); for (line=1; line<24; line++) { if (t != VSIw->linest[ line]) { if (out) printf(" Linest line %d is wrong !\n", line); else return (-3); } t = t->next; if ( !t) { if (out) printf(" Linest line %d is NULL\n", line); else if (line!=23) return(-4); } } if (VSIw->numlines >0) { if (out) printf(" Thinks that there is scrollback of %d lines ", VSIw->numlines); t= VSIw->linest[23]->next; line=0; while ( t!=0L && t!=VSIw->buftop) { t=t->next; line++; } if (out) printf(" [ Actual is %d ]\n", line); if (out && t==0L) printf(" Lines end in a null\n"); if (out && t==VSIw->buftop) printf(" Lines end in a wraparound\n"); } else if (out) printf(" There is no scrollback"); break; case 2: if (out && verbose) printf("Screen %d detached\n",i); break; default: if (out) printf("Screen %d invalid stat\n",i); break; } } return(0); } #endif DEBUGPC #ifdef USEDETATCH VSdetatch(w) int w; { if (VSscreens[w].stat!=1) return(-1); VSscreens[w].stat=2; VSIwn = -1; VSinuse--; /* SCA '87 */ } #else VSdetatch(w) int w; { VSdestroy(w); } #endif VSiclrbuf() { int j,i; char *ta,*tx; for(i=0; i<24; i++) { ta = &VSIw->attrst[i]->text[0]; tx = &VSIw->linest[i]->text[0]; for(j=0; j<=VSIw->allwidth;j++) { *ta++=0; *tx++=' '; } } } VSredraw(w,x1,y1,x2,y2) int w,x1,y1,x2,y2; { char *pt,*pa; int cc,cm; char lc,la; VSline *yp; int y; int ox,tx1,tx2,ty1,ty2, tn = -1,offset; if (VSvalids(w)!=0) return(-3); VSIcuroff(w); x1+= VSIw->Rleft; x2+=VSIw->Rleft; /* Make local coords global again */ y1+= VSIw->Rtop; y2+=VSIw->Rtop; if (x2<0) x2=0; if (x1<0) x1=0; if (x2>VSIw->maxwidth) x2=VSIw->maxwidth; if (x1>VSIw->maxwidth) x1=VSIw->maxwidth; if (y2<-VSIw->maxlines) y2= -VSIw->maxlines; if (y1<-VSIw->maxlines) y1= -VSIw->maxlines; if (y2>23) y2=23; if (y1>23) y1=23; tx1=x1; tx2=x2; ty1=y1; ty2=y2; /* Set up VSIclip call */ if (!VSIclip (&tx1,&ty1,&tx2,&ty2, &tn, &offset) ) RSerase(w,tx1,ty1,tx2,ty2); /* Erase the offending area */ if ( y1 <0) { yp = VSIw->vistop; y= y1 - VSIw->Rtop; while ( y-- >0) yp=yp->next; /* Get pointer to top line we need */ } y=y1; while ( (y<0) && ( y<=y2) ) { ox=tx1=x1; tx2=x2; ty1=ty2=y; tn = -1; if ( !VSIclip( &tx1,&ty1, &tx2, &ty2, &tn, &offset) ) RSdraw( w, tx1,ty1,0,tn, &yp->text[ox+offset]); yp=yp->next; y++; } while ( y<= y2) { pt= &VSIw->linest[y]->text[x2]; pa= &VSIw->attrst[y]->text[x2]; cm=x2; cc=1; lc = *pt; la = *pa; while (cm >=x1) { if ((lc==' ') &&(la==0)) { while( (cm>x1) && (*(pt-1)==' ') && (*(pa-1)==0) ) { pa--;pt--;cm--;cc++;} pa--;pt--; cm--;cc=1; lc= *pt; la= *pa; continue; } while( (cm>x1) && (la==*(pa-1)) ) {pa--;pt--;cm--;cc++;} if (cm>=x1) VSIdraw(w,cm,y,la,cc,pt); pa--;pt--; cm--;cc=1; lc= *pt;la= *pa; } y++; } VSIcurson(w,VSIw->x,VSIw->y, 0); /* Don't force move */ tx1=ty1=0; tn=132; if (!VSIclip( &tx1, &ty1,&tx2,&ty2, &tn, &offset) ) RSdrawsep( w, ty1, 1); /* Draw Separator */ return(0); } VSwrite(w,ptr,len) int w,len; char *ptr; { if (VSvalids(w)!=0) return(-3); VSIcuroff(w); VSem(ptr,len); VSIcurson(w,VSIw->x,VSIw->y, 1); /* Force Move */ return(0); } VSclear(w) int w; { if (VSvalids(w)!=0) return(-3); VSIes(); VSIw->x=VSIw->y=0; VSIcurson(w,VSIw->x,VSIw->y, 1); /* Force Move */ return(0); } VSpossend(w,x,y,echo) int w,x,y,echo; { static char VSkbax[]="\033O ", /* prefix for auxiliary code*/ VSkban[]="\033[ "; /* prefix for arrows normal */ char *vskptr; if (VSvalids(w)!=0) return(-3); if (VSIw->DECPAM && VSIw->DECCKM) vskptr = VSkbax; else vskptr = VSkban; if (x<0 ||y<0 ||x>VSIw->maxwidth ||y>24) return(-10); x -= VSIw->x; y -= VSIw->y; vskptr[2]='B'; while( y>0) { y--; RSsendstring(w,vskptr,3); } vskptr[2]='A'; while( y<0) { y++; RSsendstring(w,vskptr,3); } vskptr[2]='C'; while( x>0) { x--; RSsendstring(w,vskptr,3); } vskptr[2]='D'; while( x<0) { x++; RSsendstring(w,vskptr,3); } if (echo) { VSIcuroff(w); VSIw->x = x; VSIw->y = y; VSIcurson(w,VSIw->x,VSIw->y, 1); /* Force Move */ } } char VSkbsend(w,k, echo) int w, echo; unsigned char k; { static char VSkbkn[]="\033O ", /* prefix for keypad normal */ VSkbax[]="\033O ", /* prefix for auxiliary code*/ VSkban[]="\033[ ", /* prefix for arrows normal */ VSkbfn[]="\033O" ; /* prefix for function keys */ char *vskptr; int vskplen; if (VSvalids(w)!=0) return(-3); if (kVSLT) && (kDECPAM)) { RSsendstring(w,&VSIkpxlate[0][k-VSUP],1); if (k==VSKE) RSsendstring(w,"\012",1); return(0); } if (VSIw->DECPAM && VSIw->DECCKM) { vskptr= VSkbax; vskplen=3; } /* aux kp mode */ else if (kx,VSIw->y, 1); /* Force Move */ return(0); } char *VSgetline(w,y) int w,y; { if (VSvalids(w)!=0) return( (char *) -3); return(VSIw->linest[y]->text); /* Need to add code for scrolled back lines */ } VSsetrgn(w,x1,y1,x2,y2) int w,x1,y1,x2,y2; { int n,offset; if (VSvalids(w)!=0) return(-3); VSIw->Rbottom = VSIw->Rtop +(y2-y1); /* Reduce window size first */ if ( x2> VSIw->maxwidth ) { n=x2-VSIw->maxwidth; /* Get the maximum width */ if ( (x1-n) <0) n=x1; /* never a pos. offset from the left */ x1 -= n; /* Adjust left */ x2 -= n; /* Adjust right */ } if ( VSIw->Rleft !=x1 ) { /* If the left margin changes then do scroll immediately */ n = x1 -VSIw->Rleft; /* Because LM change, TM change and Size change are the */ if (n >0) VSscrolright( w,n); /* Only valid ways to call this proc */ else VSscrolleft(w, -n); } else RSmargininfo( w, VSIw->maxwidth- (x2-x1), x1); VSIw->Rleft= x1; /* Same with the margins */ VSIw->Rright = x2; if (VSIw->Rbottom>23) { n=VSIw->Rbottom-23; } else n = VSIw->Rtop -y1; if ( n > 0) VSscrolback(w,n); /* Go forward */ else { if (n <0) VSscrolforward(w,-n); /* And backward on command */ else { x1=y1=1;n=132; if (!VSIclip( &x1, &y1,&x2,&y2, &n, &offset) ) RSdrawsep( w, n,1); /* Draw Separator */ RSbufinfo( w, VSIw->numlines, VSIw->Rtop, VSIw->Rbottom); } } return(0); } VSscrolback(w,in) int w,in; { int sn,x1,y1,x2,y2,n; n=in; if (VSvalids(w)!=0) return(-3); if (VSIw->numlines < (n-VSIw->Rtop) ) /* Check against bounds */ n= VSIw->Rtop+VSIw->numlines; if (n<=0) return(0); /* Dont be scrollin' no lines.... */ VSIcuroff(w); VSIw->Rtop= VSIw->Rtop-n; /* Make the new region */ VSIw->Rbottom=VSIw->Rbottom-n; sn=n; while (sn-->0) { #ifdef DEBUGMAC if (VSIw->vistop->prev == 0L) DEBUGGER(); #endif DEBUGMAC VSIw->vistop=VSIw->vistop->prev; } sn=VSIw->Rbottom-VSIw->Rtop; RSbufinfo( w, VSIw->numlines, VSIw->Rtop, VSIw->Rbottom); if (n<24) { RSinslines(w,0,sn,n, 0); /* Dont be destructive */ VSIcurson(w,VSIw->x, VSIw->y, 0); /* Dont force move */ VSredraw(w,0,0,VSIw->maxwidth,n-1); } else VSredraw(w,0,0,VSIw->maxwidth,sn); return(0); } VSscrolforward(w,n) int w,n; { int sn,x1,y1,x2,y2; if (VSvalids(w)!=0) return(-3); if ( VSIw->Rtop+n > (23-(VSIw->Rbottom - VSIw->Rtop))) /* Check against bounds */ n= 23-(VSIw->Rbottom - VSIw->Rtop) -VSIw->Rtop; if (n<=0) return(0); /* Dont be scrollin' no lines.... */ VSIcuroff(w); VSIw->Rtop= n+VSIw->Rtop; /* Make the new region */ VSIw->Rbottom=n+VSIw->Rbottom; sn=n; while (sn-->0) VSIw->vistop=VSIw->vistop->next; sn=VSIw->Rbottom-VSIw->Rtop; RSbufinfo( w, VSIw->numlines, VSIw->Rtop, VSIw->Rbottom); if (n<24) { RSdellines(w,0,sn,n,0); /* Dont be destructive */ VSIcurson(w,VSIw->x, VSIw->y, 0); /* Dont force move */ VSredraw(w,0,(sn+1)-n,VSIw->maxwidth, sn); } else VSredraw(w,0,0,VSIw->maxwidth, sn); return(0); } VSscrolright(w,n) int w,n; { int sn,x1,y1,x2,y2, lmmax; if (VSvalids(w)!=0) return(-3); lmmax= (VSIw->maxwidth-(VSIw->Rright - VSIw->Rleft)); if ( VSIw->Rleft+n > lmmax) /* Check against bounds */ n= lmmax- VSIw->Rleft; if (n==0) return; /* Do nothing if appropriate */ VSIcuroff(w); VSIw->Rleft +=n; /* Make new region */ VSIw->Rright +=n; sn=VSIw->Rbottom-VSIw->Rtop; RSmargininfo( w, lmmax, VSIw->Rleft); RSdelcols(w,n); VSIcurson(w,VSIw->x,VSIw->y, 0); /* Don't force move */ VSredraw(w, (VSIw->Rright-VSIw->Rleft)-n, 0, (VSIw->Rright - VSIw->Rleft), sn); } VSscrolleft(w,n) int w,n; { int sn,x1,y1,x2,y2, lmmax; if (VSvalids(w)!=0) return(-3); lmmax= (VSIw->maxwidth-(VSIw->Rright - VSIw->Rleft)); if ( VSIw->Rleft-n < 0) /* Check against bounds */ n= VSIw->Rleft; if (n==0) return; /* Do nothing if appropriate */ VSIcuroff(w); VSIw->Rleft -=n; /* Make new region */ VSIw->Rright -=n; sn=VSIw->Rbottom-VSIw->Rtop; RSmargininfo( w, lmmax, VSIw->Rleft); RSinscols(w,n); VSIcurson(w,VSIw->x,VSIw->y, 0); /* Don't force move */ VSredraw(w,0,0,n, sn); } VSscrolcontrol(w,scrolon, offtop) int w,scrolon; { if (VSvalids(w)!=0) return(-3); if (scrolon>-1) VSIw->savelines=scrolon; if (offtop >-1) VSIw->ESscroll =offtop; return(0); } VSgetrgn(w,x1,y1,x2,y2) int w,*x1,*y1,*x2,*y2; { if (VSvalids(w)!=0) return(-3); *x1=VSIw->Rleft; *y1=VSIw->Rtop; *x2=VSIw->Rright; *y2=VSIw->Rbottom; return(0); } VSsnapshot(w) int w; { if (VSvalids(w)!=0) return(-3); return(0); } VSvalids(w) int w; { if (VSinuse==0) return(-5); /* -5=no ports in use */ if (VSIwn==w) return(0); /* Currently set to that window */ if ((w>VSmax) || (w<0)) return(-6); /* blown out the top of the stuff */ VSIwn=w; if (VSscreens[w].stat!=1) return(-3);/* not currently active */ VSIw=VSscreens[w].loc; if (VSIw==0L) return(-3); /* no space allocated */ return(0); } VSmaxwidth(w) int w; { if (VSvalids(w)!=0) return(-3); return(VSIw->maxwidth); } VSline *VSIGetLineStart(w,y1) { VSline *ptr; int n; if (VSvalids(w)!=0) return( (VSline *) -3); if (y1>=0) return( VSIw->linest[y1]); n= y1 - VSIw->Rtop; /* Number of lines from VISTOP to scroll forward */ ptr=VSIw->vistop; while (n>0) { n--; ptr=ptr->next; } while (n<0) { n++; ptr=ptr->prev; } return( ptr); } char *VSIstrcopy( src, len, dest, table) char *src, *dest; int len, table; { char *p, *tempp; int tblck; p=src+len-1; while((*p==' ') && (p>=src)) p--; if (p=table) { *tempp++ = '\011'; dest = tempp; } } } return(dest); } long VSgettext(w, x1, y1, x2, y2, charp, max, EOLS, table) int w,x1,y1,x2,y2; char *charp, *EOLS; long max; int table; { int EOLlen; int lx,ly, /* Upper bounds of selection */ ux,uy; /* Lower bounds of selection */ int maxwid; char *origcp; VSline *t; if (VSvalids(w)!=0) return(-3); EOLlen= strlen(EOLS); maxwid= VSIw->maxwidth; origcp= charp; if ( y1 < -VSIw->numlines) { y1= - VSIw->numlines; x1= -1; } if ( y1 == y2) { t=VSIGetLineStart(w,y1); if ( x1 < x2 ) { ux=x1; uy= y1; lx=x2; ly=y2; } /* Order the lower and */ else { ux=x2; uy= y2; lx=x1; ly=y1; } /* upper bounds */ charp = VSIstrcopy( &t->text[ux+1], lx-ux, charp, table); if (lx==maxwid) *charp++=*EOLS; } else { if ( y1< y2 ) { ux=x1; uy= y1; lx=x2; ly=y2; } /* Order the lower and */ else { ux=x2; uy= y2; lx=x1; ly=y1; } /* upper bounds */ t=VSIGetLineStart(w, uy); charp= VSIstrcopy( &t->text[ux+1], maxwid-ux, charp, table); *charp++=*EOLS; uy++; t=t->next; while (uy text,maxwid+1,charp, table); *charp++=*EOLS; t=t->next; uy++; } if (ly>23) lx=maxwid; charp=VSIstrcopy( t->text, lx+1, charp, table); if (lx>=maxwid ) *charp++=*EOLS; } return( charp - origcp ); } kbkn[]="\033O ", /* prefix for keypad normal */ VSkbax[]="\033O ", /* prefix for auxiliary code*/ VSkban[]="\033[ ", /* prefix for arrows normal */ VSkbfn[]="\033O" ; /* prefix for function keys */ char *vskptr; int vskplen; if (VSvalids(w)!=0) return(-3); if (kattrst[x] #define VL(x) VSIw->linest[x] #define vtp VSIw->top #define btm VSIw->bottom #define VSIclrattrib 0 #define Rrt VSIw->Rright #define Rlt VSIw->Rleft #define Rtp VSIw->Rtop #define Rbm VSIw->Rbottom char *malloc(); VSIclip( x1, y1, x2, y2, n, offset) int *x1, *x2, *y1, *y2, *n, *offset; { if (*n >=0) { *x2= *x1+*n-1; *y2= *y1; }; if (( *x1 > Rrt) || ( *y2 < Rtp)) return (-1); if ( *x2 > Rrt) *x2= Rrt; if ( *y2> Rbm) *y2= Rbm; *x1 -= Rlt; *x2 -= Rlt; *y1 -= Rtp; *y2 -= Rtp; *offset = - *x1; if (*offset <0) *offset=0; if (*x1<0) *x1=0; if (*y1<0) *y1=0; *n = *x2 - *x1 +1; if (( *n <= 0) || ( *y2 - *y1 <0 ) ) return(-1); return(0); } VSIcdellines( w, top, bottom, n, scrolled) int w, top, bottom, n,scrolled; { int x1=0,x2=VSIw->maxwidth, tn = -1,offset; if (VSIclip( &x1, &top, &x2, &bottom, &tn,&offset) ) return(-1); tn =bottom-top; if (tn maxwidth, tn= -1,offset; if (VSIclip( &x1, &top, &x2, &bottom, &tn, &offset) ) return(-1); tn =bottom-top; if (tn =23) VSsetrgn(VSIwn,Rlt,23-x2,Rrt,23); else { if (y>0) VSscrolforward(VSIwn, y ); else VSscrolback(VSIwn, -y); } x=ox; y=oy; /* Restore from previous */ n=1; /* if (!VSIclip(&x,&y,&x2,&y2,&n, &offset) ) RScurson( w, x, y); */ } } VSIcuroff( w) int w; { int x=VSIw->x, y=VSIw->y, x2,y2,n=1,offset; if (!VSIclip(&x,&y,&x2,&y2,&n, &offset) ) RScursoff(w); } VSIreset() { VSIw->top=0; VSIw->bottom=23; VSIw->parmptr=0; VSIw->escflg=0; VSIw->DECAWM=0; VSIw->DECCKM=0; VSIw->DECPAM=0; VSIw->IRM=0; VSIw->attrib=0; VSIw->x=0; VSIw->y=0; VSIw->charset=0; VSIes(); VSItabinit(); } VSItabinit() { int x=0; VSItabclear(); while(x<=VSIw->allwidth) { VSIw->tabs[x]='x';x+=8;} VSIw->tabs[VSIw->allwidth]='x'; } VSItabclear() { int x=0; while (x<=VSIw->allwidth) { VSIw->tabs[x]=' '; x++; } } VSIlistmove(TD,BD,TI,BI) VSline *TD,*BD,*TI,*BI; { if (TD->prev!=0L) TD->prev->next=BD->next; /* Maintain circularity */ if (BD->next!=0L) BD->next->prev=TD->prev; TD->prev=TI; /* Place the nod in its new home */ BD->next=BI; if (TI!=0L) TI->next=TD; /* Ditto prev->prev */ if (BI!=0L) BI->prev=BD; } VSIlistndx(ts,as) VSline *ts,*as; { int i; for(i=0; i<24;i++) { AL(i)=as; VL(i)=ts; ts=ts->next; as=as->next; } } VSIdellines(n,s) int n,s; { int i,j, attop= (VSIw->vistop == VSIw->scrntop); char *ta,*tt; VSline *as,*ts,*TD,*BD,*TI,*BI,*itt,*ita; if (s<0) s=VSIw->y; if ( s+n-1 > VSIw->bottom) n = VSIw->bottom -s +1; ts=VL(0)->prev; TD=VL(s); BD=VL(s+n-1); TI=VL(VSIw->bottom); BI=TI->next; itt=TD; if ((TI!=BD)) VSIlistmove(TD,BD,TI,BI); if (s==0 || n>23) as=AL( n); else as=AL(0); TD=AL(s); BD=AL(s+n-1); TI=AL(VSIw->bottom); BI=TI->next; if (TD!=BI && TI!=BD) VSIlistmove(TD,BD,TI,BI); ita=TD; for(i=0; itext; tt=itt->text; for(j=0; j<=VSIw->allwidth; j++) { *tt++=' '; *ta++=VSIw->attrib; } ita=ita->next; itt=itt->next; } VSIw->scrntop = ts->next; if (attop) VSIw->vistop = VSIw->scrntop; VSIlistndx(ts->next,as); VSIcdellines(VSIwn,s, VSIw->bottom,n, -1); /* Destroy selection area if this is called */ } VSIinslines(n,s) int n,s; { int i,j, attop= (VSIw->vistop == VSIw->scrntop); char *ta,*tt; VSline *as,*ts,*TD,*BD,*TI,*BI,*itt,*ita; if (s<0) s=VSIw->y; if ( s+n-1 > VSIw->bottom) n = VSIw->bottom -s +1; ts=VL(0)->prev; BI=VL(s); TI=BI->prev; TD=VL(VSIw->bottom-n+1); BD=VL(VSIw->bottom); itt=TD; if (TD!=BI) VSIlistmove(TD,BD,TI,BI); if (s==0 || n>23) as=AL( VSIw->bottom-n+1); else as=AL(0); BI=AL(s); TI=BI->prev; TD=AL(VSIw->bottom-n+1); BD=AL(VSIw->bottom); if (TD!=BI && TI!=BD) VSIlistmove(TD,BD,TI,BI); ita=TD; for(i=0; itext; ta=ita->text; for(j=0; j<=VSIw->allwidth; j++) { *tt++=' '; *ta++=VSIw->attrib; } itt=itt->next; ita=ita->next; } VSIw->scrntop = ts->next; if (attop) VSIw->vistop = VSIw->scrntop; VSIlistndx(ts->next,as); VSIcinslines(VSIwn, s,VSIw->bottom, n, -1); /* Destroy selection area if this is called tooo */ } VSIwrapnow( xp,yp) int *xp, *yp; { if (VSIw->x > VSIw->maxwidth) { VSIw->x=0; VSIindex(); } *xp=VSIw->x; *yp=VSIw->y; } VSIeeol() { char *tt,*ta; int x1=VSIw->x, y1=VSIw->y, x2=VSIw->maxwidth, y2=VSIw->y, n= -1,offset; int i; VSIwrapnow( &x1, &y1); y2=y1; ta = &AL(y1)->text[x1]; tt = &VL(y1)->text[x1]; for(i=VSIw->allwidth-x1+1;i>0; i--) { *ta++=VSIw->attrib; *tt++=' '; } if ( !VSIclip( &x1,&y1,&x2,&y2,&n, &offset)) RSerase(VSIwn,x1,y1,x2,y2); } VSIdelchars(x) int x; { int i; int x1=VSIw->x, y1=VSIw->y, x2=VSIw->maxwidth, y2=VSIw->y, n= -1,offset; char *tempa,*temp; VSIwrapnow( &x1, &y1); y2=y1; if (x>VSIw->maxwidth) x=VSIw->maxwidth; tempa=VSIw->attrst[y1]->text; temp =VSIw->linest[y1]->text; for(i=x1; i<=VSIw->maxwidth-x;i++) { temp[i]=temp[x+i]; tempa[i]=tempa[x+i]; } for(i=VSIw->maxwidth-x+1;i<=VSIw->allwidth; i++) { temp[i]=' '; tempa[i]=VSIw->attrib; } if ( !VSIclip( &x1,&y1,&x2,&y2,&n, &offset)) { if (VSIw->VSIDC) RSdelchars(VSIwn, x1, y1, x); else RSdraw(VSIwn, x1, y1,VSIw->attrib, n, &VSIw->linest[y1]->text[x1]); } } VSIindex() { if( ++VSIw->y > VSIw->bottom) { VSIw->y=VSIw->bottom; VSIscroll(); } } VSline *VSInewline() { VSline *t2; char *t; if ((t = malloc( VSIw->allwidth+1)) !=0L) { if ((t2 = (VSline *)malloc(sizeof(VSline))) !=0L) t2->text=t; else { free(t); return(0L); } /* Return on bad malloc */ } else return(0L); /* Return on bad malloc */ t2->next=t2->prev=0L; return(t2); } VSIscroll() { char *temp,*tempa; VSline *tmp; int i, attop; int tx1,tx2,ty1,ty2, tn,offset; tx1=ty1=0; tn=132; if (!VSIclip( &tx1, &ty1,&tx2,&ty2, &tn, &offset) ) RSdrawsep( VSIwn, ty1, 1); /* Draw Separator */ if ( (!VSIw->savelines) || (VSIw->top !=0) || (VSIw->bottom !=23)) VSIdellines(1,VSIw->top); else { if (( VL(23)->next == 0L) && ( VSIw->numlines < VSIw->maxlines) && (tmp = VSInewline()) !=0L ) { VL(23)->next = tmp; /* ADD A LINE */ tmp->prev= VL(btm); VSIw->numlines++; RSbufinfo(VSIwn, VSIw->numlines,VSIw->Rtop, VSIw->Rbottom); } else { if ( VL(btm)->next==0L) { VL(btm)->next =VSIw->buftop; /* Make it circular */ VSIw->buftop->prev= VL(btm); } if ( VL(btm)->next == VSIw->buftop) VSIw->buftop=VSIw->buftop->next;/* if we are in old terr. loop*/ } attop= (VSIw->vistop == VSIw->scrntop); VSIw->scrntop=VSIw->scrntop->next; VSIlistndx( VSIw->scrntop, AL(1)); if (VSIcdellines(VSIwn,VSIw->Rtop, VSIw->Rbottom,1,1) ) { /* Dont destroy select */ /* If we did not show on screen then we mush do dis */ if (VSIw->Rtop > -VSIw->numlines) { /* If we are not at the top..... */ VSIw->Rtop--; VSIw->Rbottom--; /* Then we should move down */ } else { /* But if we're at the top.... */ /* The region remains the same .. */ VSIw->vistop=VSIw->vistop->next;/* and move the vistop as */ RSdellines( VSIwn, 0,Rbm-Rtp,1,1);/* we also delete the top line */ } /* well.... (but don't destroy selection) */ } else { VSIw->vistop=VSIw->vistop->next; } tempa=AL(23)->text; temp =VL(23)->text; for(i=0;i<=VSIw->allwidth; i++) { temp[i]=' '; tempa[i]=VSIw->attrib; } } tx1=ty1=0; tn=132; if (!VSIclip( &tx1, &ty1,&tx2,&ty2, &tn, &offset) ) RSdrawsep( VSIwn, ty1, 1); /* Draw Separator */ } VSIscroff() { char *temp,*tempa; VSline *tmp; int i, attop; int tx1,tx2,ty1,ty2, tn,offset; if ( VL(23)->next != 0L) { for (i = 0; i<24; i++) { if ( VL(23)->next == VSIw->buftop) VSIw->buftop=VSIw->buftop->next;/* if we are in old terr. loop*/ VSIw->scrntop=VSIw->scrntop->next; VSIlistndx( VSIw->scrntop, AL(1)); } attop= (VSIw->vistop == VSIw->scrntop); } else { for (i=0; i<24; i++) { if (( VSIw->numlines < VSIw->maxlines) && (tmp = VSInewline()) !=0L ) { VL(23)->next = tmp; /* ADD A LINE */ tmp->prev= VL(23); VSIw->numlines++; } else { if ( VL(23)->next==0L) { VL(23)->next =VSIw->buftop; /* Make it circular */ VSIw->buftop->prev= VL(23); } if ( VL(23)->next == VSIw->buftop) VSIw->buftop=VSIw->buftop->next;/* if we are in old terr. loop*/ } VSIw->scrntop=VSIw->scrntop->next; VSIlistndx( VSIw->scrntop, AL(1)); } attop= (VSIw->vistop == VSIw->scrntop); RSbufinfo(VSIwn, VSIw->numlines,VSIw->Rtop, VSIw->Rbottom); } #ifdef NOT_NEEDED_IF_WE_WILL_ERASE_ANYWAY tempa=AL(23)->text; temp =VL(23)->text; for(i=0;i<=VSIw->allwidth; i++) { temp[i]=' '; tempa[i]=VSIw->attrib; } #endif } VSIrindex() { if(--VSIw->y < VSIw->top) { VSIw->y=VSIw->top; VSIinslines(1,VSIw->top); } } VSIebol() { char *tt,*ta; int x1=0, y1=VSIw->y, x2=VSIw->x, y2=VSIw->y, n= -1, offset; int i; VSIwrapnow( &x2, &y1); y2=y1; ta = &AL(y1)->text[0]; tt = &VL(y1)->text[0]; for(i=0;i<=x2; i++) { *ta++=VSIclrattrib; *tt++=' '; } if ( !VSIclip( &x1,&y1,&x2,&y2,&n,&offset)) RSerase(VSIwn,x1,y1,x2,y2); } VSIel(s) int s; { char *tt,*ta; int x1=0, y1=s, x2=VSIw->maxwidth, y2=s, n= -1,offset; int i; if (s<0) { VSIwrapnow( &x1, &y1); s=y2=y1; x1=0; } ta = &AL(s)->text[0]; tt = &VL(s)->text[0]; for(i=0;i<=VSIw->allwidth; i++) { *ta++=VSIclrattrib; *tt++=' '; } if ( !VSIclip( &x1,&y1,&x2,&y2,&n, &offset)) RSerase(VSIwn,x1,y1,x2,y2); } VSIelo(s) { char *tt,*ta; int i,j; /* VSIwrapnow( &i, &j); */ if (s<0) s=VSIw->y; ta = &AL(s)->text[0]; tt = &VL(s)->text[0]; for(i=0;i<=VSIw->allwidth; i++) { *ta++=VSIclrattrib; *tt++=' '; } } VSIeeos() { int i; int x1=0, y1=VSIw->y+1, x2=VSIw->maxwidth, y2=23, n= -1,offset; VSIwrapnow( &x1, &y1); y1++; x1=0; i=y1; if ( !VSIclip( &x1,&y1,&x2,&y2,&n, &offset)) RSerase(VSIwn,x1,y1,x2,y2); VSIeeol(); while(i<24) { VSIelo(i); i++; } if (VSIw->y<23 && (VSIw->x <= VSIw->maxwidth)) if ( !VSIclip( &x1,&y1,&x2,&y2,&n,&offset)) RSerase(VSIwn,x1,y1,x2,y2); } VSIebos() { int i; int x1, y1, x2=VSIw->maxwidth, y2, n= -1,offset; VSIwrapnow( &x1, &y1); y2=y1-1; x1=0; y1=0; VSIebol(); i=0; while(i< (y2+1) ) { /* Equiv of VSIw->y */ VSIelo(i); i++; } if (y2>=0) /* Equiv of VSIw->y >0 */ if ( !VSIclip( &x1,&y1,&x2,&y2,&n, &offset)) RSerase(VSIwn,x1,y1,x2,y2); } VSIes() { int i; int x1=0, y1=0, x2=VSIw->maxwidth, y2=23, n= -1,offset; if (VSIw->ESscroll) VSIscroff(); for(i=0;i<24;i++) VSIelo(i); if ( !VSIclip( &x1,&y1,&x2,&y2,&n, &offset)) RSerase(VSIwn,x1,y1,x2,y2); VSIw->vistop = VSIw->scrntop; } VSIrange() /* check and resolve range errors on x and y */ { int wrap=0; if (VSIw->DECAWM) wrap=1; if (VSIw->x<0) VSIw->x=0; if (VSIw->x>(VSIw->maxwidth+wrap)) VSIw->x=VSIw->maxwidth+wrap; if (VSIw->y<0) VSIw->y=0; if (VSIw->y>23) VSIw->y=23; } VTsendpos() { char tempbuf[9]; int x=VSIw->x, y=VSIw->y; if (x>VSIw->maxwidth) { x=0;y++; } if (y>23) y=23; sprintf(tempbuf,"\033[%d;%dR",y+1,x+1); RSsendstring(VSIwn,tempbuf,8); } VTsendstat() { RSsendstring(VSIwn,"\033[0n",4); } VTsendident() { #ifdef VT100RESP if (VSIw->allwidth >80) RSsendstring(VSIwn,"\033[?4;6c",7); else RSsendstring(VSIwn,"\033[?1;6c",7); #endif VT100RESP RSsendstring(VSIwn, "\033[?6c",5); } VTalign() { char *tt; int i,j; VSIes(); /* erase the screen */ for (j=0; j<23; j++) { tt = &VL(j)->text[0]; for(i=0;i<=VSIw->maxwidth; i++) { *tt++='E'; } } VSredraw( VSIwn, 0, 0, (VSIw->Rright - VSIw->Rleft), (VSIw->Rbottom - VSIw->Rtop) ); } VSIapclear() { VSIw->parmptr=6; while(VSIw->parmptr-->0) VSIw->parms[VSIw->parmptr] = -1; VSIw->parmptr=0; } VSIsetoption(toggle) int toggle; { int WindWidth= VSIw->Rright - VSIw->Rleft; switch(VSIw->parms[0]) { case -2: switch(VSIw->parms[1]){ case 1: VSIw->DECCKM=toggle; break; case 3: VSIw->x=VSIw->y=0; /* Clear the screen, mama! */ VSIes(); if (toggle) { /* 132 column mode */ VSIw->maxwidth=VSIw->allwidth; } else { VSIw->maxwidth= 79; } RSmargininfo( VSIwn, VSIw->maxwidth- WindWidth, VSIw->Rleft); break; case 7: VSIw->DECAWM=toggle; break; default: break; } break; case 4: VSIw->IRM=toggle; break; default: break; } } VSItab() { if (VSIw->x>=VSIw->maxwidth) { VSIw->x=VSIw->maxwidth; return(0); } VSIw->x++; while( (VSIw->tabs[VSIw->x]!='x') && (VSIw->xmaxwidth)) VSIw->x++; return(0); } VSIinschar(x) int x; { int i,j; char *tempa,*temp; VSIwrapnow( &i, &j); tempa=VSIw->attrst[VSIw->y]->text; temp =VSIw->linest[VSIw->y]->text; for(i=VSIw->maxwidth-x; i>=VSIw->x; i--) { temp[x+i]=temp[i]; tempa[x+i]=tempa[i]; } for(i=VSIw->x;ix+x; i++) { temp[i]=' '; tempa[i]=VSIclrattrib; } } VSIinsstring(len,start) int len; char *start; { if (VSIw->VSIDC) RSinsstring(VSIwn,VSIw->x-len,VSIw->y,VSIw->attrib,len,start); else RSdraw(VSIwn,VSIw->x-len,VSIw->y,VSIw->attrib, VSIw->maxwidth-VSIw->x+len+1,start); } VSIsave() { VSIw->Px=VSIw->x; VSIw->Py=VSIw->y; VSIw->Pattrib=VSIw->attrib; } VSIrestore() { VSIw->x=VSIw->Px; VSIw->y=VSIw->Py; VSIrange(); VSIw->attrib=VSinattr(VSIw->Pattrib); } VSIdraw( VSIwn, x, y, a, len, c) int VSIwn,x,y,a,len; char *c; { int oy=y,x2,y2,offset; #ifdef OLDM if (y<0) { #endif if ( !VSIclip( &x, &y, &x2, &y2, &len, &offset) ) RSdraw( VSIwn, x, y, a, len, c+(offset) ); #ifdef OLDM /* This code moved to cursor for testing */ } else { if ( !VSIclip( &x, &y, &x2, &y2, &len,&offset) ) RSdraw( VSIwn, x, y, a, len, c+(offset) ); else { x2= Rbm-Rtp; if (x2 >=23) VSsetrgn(VSIwn,Rlt,23-x2,Rrt,23); else { if (y>0) VSscrolforward(VSIwn, y - x2 ); else VSscrolback(VSIwn, y); } } } #endif OLDM } nt tx1,tx2,ty1,ty2, tn,offset; tx1=ty1=0; tn=132; if (!VSIclip( &tx1, &ty1,&tx2,&ty2, &tn, &offset) ) RSdrawsep( VSIwn, ty1, 1); /* Draw Separator */ if ( (!VSIw->savelines) || (VSIw->top !=0) || (VSIw->bottom !=23)) VSIdellines(1,VSIw->top); else { if (( VL(23)->next == 0L) && ( VSIw->numlines < VSIw->maxlines) && (tmp = VSInewline(source/vs/vskeys.h 644 144 13 4140 4267455063 7541 /* * %W% (NCSA) %G% * * Virtual Screen Kernel Keys and Attribute Definitions * (vskeys.c) * * National Center for Supercomputing Applications * by Gaige B. Paulsen * * This file contains equates used by the program for specification of * special Keyboard characters and definition of the Attribute byte. * * Version Date Notes * ------- ------ --------------------------------------------------- * 0.01 861102 Initial coding -GBP * 2.1 871130 NCSA Telnet 2.1 -GBP * 2.2 880715 NCSA Telnet 2.2 -GBP * */ #define VSUP 129 /* Up Arrow */ #define VSDN 130 /* Down Arrow */ #define VSRT 131 /* Right Arrow */ #define VSLT 132 /* Left Arrow */ #define VSK0 133 /* Keypad 0 */ #define VSK1 134 /* Keypad 1 */ #define VSK2 135 /* Keypad 2 */ #define VSK3 136 /* Keypad 3 */ #define VSK4 137 /* Keypad 4 */ #define VSK5 138 /* Keypad 5 */ #define VSK6 139 /* Keypad 6 */ #define VSK7 140 /* Keypad 7 */ #define VSK8 141 /* Keypad 8 */ #define VSK9 142 /* Keypad 9 */ #define VSKC 143 /* Keypad , */ #define VSKM 144 /* Keypad - */ #define VSKP 145 /* Keypad . */ #define VSKE 146 /* Keypad Enter */ #define VSF1 147 /* Function 1 */ #define VSF2 148 /* Function 2 */ #define VSF3 149 /* Function 3 */ #define VSF4 150 /* Function 4 */ #ifdef VSMASTER char VSIkpxlate[2][23] = { "ABCD0123456789,-.\15PQRS", "ABCDpqrstuvwxylmnMPQRS" }; #else extern char *VSIkpxlate; #endif /* * Definition of attribute bits in the Virtual Screen * * 0 - Bold * 1 - * 2 - * 3 - Underline * 4 - Blink * 5 - * 6 - Reverse * 7 - Graphics character set * */ #define VSisbold(x) (x & 0x01) #define VSisundl(x) (x & 0x08) #define VSisblnk(x) (x & 0x10) #define VSisrev(x) (x & 0x40) #define VSisgrph(x) (x & 0x80) #define VSinattr(x) (x & 0xd9) #define VSgraph(x) (x | 0x80) #define VSnotgraph(x) (x & 0x7F) 6c",7); else RSsendstring(VSIwn,"\033[?1;6c",7); #endif VT100RESP RSsendstring(VSIwn, "\033[?6c",5); } VTalign() { char *tt; int i,j; VSIes(); /* erase the screen */ for (j=0; j<23; j++) { tt = &VL(j)->text[0]; for(i=0;i<=VSIw->maxwidth; i++) { *tt++='E'; } } VSredraw( VSIwn, 0, 0, (VSIw->Rright - VSIw->Rleft), (VSIw->Rbottom - VSIw->Rtop) ); } VSIapclear() { VSIw->parmptr=6; while(VSIsource/msc/mipasm.asm 644 144 13 16637 4267455437 10231 ; ; Changes to get this from NET.ASM ; 1. X EQU 6 ; 2. replace "include DOS.MAC" ; 3. replace DSEG, PSEG and endings ; 4. put underscores in front of all public identifiers ; 5. fill out some identifiers to full length ; 6. Check returns of long pointers and longs to use DX-low, AX-hi ; NAME NET ; INCLUDE DOS.MAC ; SETX X EQU 6 ; ; The subroutines to call from C ; _TEXT segment public 'CODE' assume CS:_TEXT PUBLIC _IPCHECK,_TCPCHECK,_MOVEBYTES,_MOVENBYTES,_LONGSWAP,_INTSWAP ; ; Routines for general use by the communications programs ; ; ;************************************************************************* ; Internet header checksum ; header checksum is calculated for a higher level program to verify ; ; USAGE: ipcheck(ptr) ; ; this proc knows that the IP header length is found in the first byte ; _IPCHECK PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] ; ds for input data MOV DS,AX MOV SI,[BP+X] ; pointer to data MOV CX,[BP+X+4] ; count of words to test XOR BX,BX CLC CHKSUM: LODSW ; get next word ADC BX,AX ; keep adding LOOP CHKSUM ; til' done ADC BX,0 ; adds the carry bit in ; NOT BX ; take one more 1-complement MOV AX,BX POP DS POP BP RET _IPCHECK ENDP ; ; TCP checksum, has two parts, including support for a pseudo-header ; ; usage: tcpcheck(psptr,tcpptr,tcplen) ; char *psptr,*tcpptr; pointers to pseudo header and real header ; int tcplen length of tcp packet in checksum ; _TCPCHECK PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] ; ds for input data for pseudo-hdr MOV DS,AX MOV SI,[BP+X] ; pointer to data MOV CX,6 ; length of p-hdr in words XOR BX,BX ; clear to begin CLC PCHKSUM: LODSW ; get next word ADC BX,AX ; keep adding LOOP PCHKSUM ; til' done ADC BX,0 ; adds the carry bit in ; ; NOW THE REAL THING ; MOV AX,[BP+X+6] ; ds of real stuff MOV DS,AX MOV SI,[BP+X+4] ; pointer MOV CX,[BP+X+8] ; count of bytes to test MOV DX,CX ; keep a copy SHR CX,1 ; divide by two, round down CLC RCHKSUM: LODSW ADC BX,AX ; add to previous running sum LOOP RCHKSUM ADC BX,0 ; add the last carry in again AND DX,1 ; odd # of bytes? JZ NOTODD LODSB ; get that last byte XOR AH,AH ; clear the high portion ADD BX,AX ; add the last one in ADC BX,0 ; add the carry in, too NOTODD: CMP BX,0ffffH ; don't not it to 0 (special case: all 1's) JZ NONOT NOT BX ; take one more 1-complement NONOT: MOV AX,BX POP DS POP BP RET _TCPCHECK ENDP ; ;*********************************************************************8 ; movebytes ; ONLY MOVES EVEN NUMBERS OF BYTES!!!! WILL DROP THE LAST BYTE OF ; ODD BYTES! ; use movenbytes for arbitrary byte moves ; usage: ; movebytes(to,from,n); n must be even ; _MOVEBYTES PROC FAR PUSH BP MOV BP,SP PUSH DS PUSH ES MOV AX,[BP+X+2] ; WHERE TO PUT IT MOV ES,AX MOV AX,[BP+X+6] ; WHERE TO GET IT MOV DS,AX MOV SI,[BP+X+4] ; FROM PTR MOV DI,[BP+X] ; TO PTR MOV CX,[BP+X+8] ; HOW MANY TO MOVE SHR CX,1 ; DIVIDE BY TWO WORDS REP MOVSW ; MOVE THEM POP ES POP DS POP BP RET _MOVEBYTES ENDP ;*********************************************************************8 ; movenbytes ; use movenbytes for arbitrary byte moves of any count ; usage: ; movenbytes(to,from,n); ; _MOVENBYTES PROC FAR PUSH BP MOV BP,SP PUSH DS PUSH ES MOV AX,[BP+X+2] ; WHERE TO PUT IT MOV ES,AX MOV AX,[BP+X+6] ; WHERE TO GET IT MOV DS,AX MOV SI,[BP+X+4] ; FROM PTR MOV DI,[BP+X] ; TO PTR MOV CX,[BP+X+8] ; HOW MANY TO MOVE OR CX,CX ; COULD BE NONE TO MOVE JZ ENDMOVE CMP CX,1 ; SEE IF THERE IS JUST ONE BYTE JZ LASTONE MOV BX,CX ; MAKE A COPY TO KEEP AND BX,1 ; SEE IF IT IS ODD JNZ ODDN SHR CX,1 ; DIVIDE BY TWO WORDS REP MOVSW ; MOVE THEM POP ES POP DS POP BP RET ODDN: SHR CX,1 ; DIVIDE BY TWO REP MOVSW LASTONE: MOVSB ; MOVE ODD ONE ENDMOVE: POP ES POP DS POP BP RET _MOVENBYTES ENDP ; ;************************************************************************* ; longswap ; swap the bytes of a long integer from PC ; order (reverse) to in-order. This will work both ways. ; returns the new long value ; usage: ; l2 = longswap(l) ; long l; ; _LONGSWAP PROC FAR PUSH BP MOV BP,SP MOV AX,[BP+X+2] ; high bytes of the long int MOV DX,[BP+X] ; low bytes of the long int ; ; GET THE DATA ; XCHG AH,AL ; SWAP THEM, these are now low XCHG DH,DL ; swap the others POP BP RET _LONGSWAP ENDP ; ;************************************************************************* ; INTSWAP ; swap the bytes of an integer, returns the swapped integer ; ; usage: i = intswap(i); ; _INTSWAP PROC FAR MOV BX,SP MOV AX,SS:[BX+4] ; CAN IT BE DONE? XCHG AH,AL RET ; PUSH BP ; MOV BP,SP ; MOV AX,[BP+X] ; GET THE INT ; XCHG AH,AL ; SWITCH THEM, THAT'S IT ; POP BP ; RET _INTSWAP ENDP ; ;************************************************************************** ; ; Routines to install and deinstall a timer routine which calls ; netsleep(0); ; The timer is set to go off every 1/2 second to check for packets ; in the incoming packet buffer. We use the user-hook into the system ; timer which occurs every 1/18th of a second. ; ; TIMEINT EQU 4*1CH ; User hook to timer int EXTRN _netsleep:FAR ; C routine which gets called from handler PUBLIC _TINST,_TDEINST ;************************************************************************* ; ; Take out the timer interrupt handler, restore previous value ; _TDEINST PROC FAR MOV CX,CS:TIP ; get old ip from save spot MOV DX,CS:TCS ; get old cs from save spot MOV BX,TIMEINT ; interrupt in table for timer PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX CLI MOV [BX],CX ; store old ip into the table INC BX INC BX ; move pointer in interrupt table MOV [BX],DX ; store old cs into the table STI POP DS RET _TDEINST ENDP ; ; ; install the timer interrupt handler, the handler is technically ; part of this procedure. ; _TINST PROC FAR XOR AX,AX MOV CS:TENTER,AL ; clear this flag MOV CS:TMYDS,DS ; store for use by handler MOV BX,TIMEINT ; interrupt in table for timer (1C) PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX MOV AX,OFFSET THAND ; where the handler is CLI MOV DX,[BX] ; keep copy of the ip MOV [BX],AX ; store ip into the table INC BX INC BX ; move pointer in interrupt table MOV CX,[BX] ; keep copy of the cs, too MOV AX,CS MOV [BX],AX ; store new cs into the table STI POP DS MOV CS:TIP,DX ; store them away MOV CS:TCS,CX RET ; ; Code segment addressable data for keeping track of the interrupt handler ; stuff ; TMYDS DW 00H ; the data segment for this assembly code TICNT DB 0 ; counter of 1/18ths sec TENTER DB 00 TIP DW 00 TCS DW 00 ; ; The handler itself. ; THAND: ; not a public name, only handles ints STI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI PUSH SI CLD ; all moves will be forward MOV AL,CS:TENTER OR AL,AL JNZ TIME2 MOV AL,1 MOV CS:TENTER,AL ; set flag to indicate busy INC CS:TICNT MOV AL,CS:TICNT ; counter for us AND AL,7 ; see if # mod 8 = 0 JNZ TSKIP ; skip 7 out of 8 times ; ; SET UP CORRECT DS ; MOV DS,CS:TMYDS ; get correct ds ; ; do we have to set up our own stack here? ; xor ax,ax PUSH AX CALL _netsleep POP AX TSKIP: XOR AL,AL MOV CS:TENTER,AL ; reenter flag, done now TIME2: POP SI POP DI POP DX POP CX POP BX POP AX POP ES POP DS ; ; forward to any other existing routines ; JMP DWORD PTR CS:TIP _TINST ENDP _TEXT ends END bottom); } #ifdef NOT_NEEDED_IF_WE_WILL_ERASE_ANYWAY tempa=AL(23)->text; temp =VL(23)->text;source/msc/tmake 644 144 13 4033 4267455450 7223 # # Note: Append this line to 'stdio.h' # #include "io.h" # ip.obj : ip.c msc /AL ip.c,ip,nul ; user.obj : user.c msc /AL user.c,user,nul ; rspc.obj : rspc.c windat.h msc /AL rspc.c,rspc,nul ; dlayer.obj : dlayer.c msc /AL dlayer.c,dlayer,nul ; tools.obj : tools.c msc /AL tools.c ; bkgr.obj : bkgr.c msc /AL -DMSC bkgr.c ; util.obj : util.c msc /AL util.c ; pctools.obj : pctools.c msc /AL pctools.c ; tcp.obj : tcp.c msc /AL tcp.c ; pcutil.obj : pcutil.c msc /AL pcutil.c ; protinit.obj : protinit.c msc /AL protinit.c ; look.obj : look.c windat.h msc /AL -DMSC look.c ; vsinterf.obj : vsinterf.c msc /AL vsinterf.c ; vsem.obj : vsem.c msc /AL vsem.c ; vsintern.obj : vsintern.c msc /AL vsintern.c ; rg0.obj : rg0.c msc rg0,rg0 /AL; rge.obj : rge.c msc rge,rge /AL; rgh.obj : rgh.c msc rgh,rgh /AL; rg9.obj : rg9.c msc rg9,rg9 /AL; rgp.obj : rgp.c msc rgp,rgp /AL; rgc.obj : rgc.c msc rgc,rgc /AL; rghp.obj : rghp.c msc rghp,rghp /AL; vgtek.obj : vgtek.c msc vgtek,vgtek /AL; tekstor.obj : tekstor.c msc tekstor,tekstor /AL; net.obj : mnet3com.asm \masm\masm mnet3com,net ; ipasm.obj : mipasm.asm \masm\masm mipasm,ipasm ; ncsaio.obj : ncsaio.asm \masm\masm ncsaio ; tcp1m.lib : tcp.obj ip.obj dlayer.obj tools.obj pctools.obj net.obj protinit.obj user.obj ipasm.obj lib tcp1m.lib +tcp +ip +net +dlayer +tools +pctools +protinit +user +ipasm; sessm.lib : util.obj pcutil.obj bkgr.obj lib sessm.lib +util +pcutil +bkgr ; vs.lib : vsinterf.obj vsem.obj vsintern.obj lib vs.lib +vsinterf +vsintern +vsem ; tek.lib : vgtek.obj tekstor.obj lib tek.lib +vgtek +tekstor +rg0 +rge +rgc +rgh +rgp +rghp +rg9 ; look.exe : look.obj ncsaio.obj tek.lib sessm.lib tcp1m.lib vs.lib link /stack:4096 look+ncsaio+RSPC,look,nul,tek+tcp1m+sessm+vs ; telpass.obj : telpass.c msc /AL telpass,telpass,nul ; telpass.exe : telpass.obj link telpass+ncsaio,telpass,nul ; minitel.obj : minitel.c whatami.h hostform.h msc /AL minitel,minitel,nul ; minitel.exe : minitel.obj link minitel+ncsaio,minitel,nul,tcp1m+sessm ; HKSUM: LODSW ADC BX,AX ; add to previous running sum LOOP RCHKSUM ADC BX,0 ; add the last carry in again AND DX,1 ; odd # of bytes? JZ NOTODD LODSB ; get that last byte XOR AH,AH ; clear the high portion ADD BX,AX ; add the last one in ADC BX,0 ; add the carry in, too NOTODD: CMP BX,0ffffH ; don't not it to 0 (special case: all 1's) JZ NONOT NOT BX ; take one more 1-complement NONOT: MOV AX,BX POP DS POP BP RET _TCPCHECK ENDP ; ;**********************source/msc/mnet3com.asm 644 144 13 47210 4267455461 10454 TITLE NETSUPPORT -- LOW LEVEL DRIVERS FOR ETHERNET ; ; Assembler support for interrupt-driven Ethernet I/O on the PC ; ; Will read and write packets from the 2K packet buffer on the ; Etherlink card. Provides hooks for higher layer protocols. ; ; ; Changes to get this from NET.ASM ; 1. X EQU 6 ; 2. replace "include DOS.MAC" ; 3. replace DSEG, PSEG and endings ; 4. put underscores in front of all public identifiers ; 5. fill out some identifiers to full length ; 6. Check returns of long pointers and longs to use DX-low, AX-hi ; NAME NET ; INCLUDE DOS.MAC ; SETX X EQU 6 ; ; Equates for controlling the 3COM board ; ICTRL EQU 020H ; 8259 interrupt control register IMASK EQU 021H ; 8259 interrupt mask register BASEA EQU 300H ; Base I/O address on PC I/O bus ENDOFI EQU 020H ; end-of-interrupt ; ; Controller registers ; EADDR EQU BASEA+0H ; Network address for hardware checking ; takes six bytes, this is the address that the Ethernet board will ; match to find packets on the net. (0-5h) ; EREC EQU BASEA+6H ; Receive status (read) ; Receive command (write) ESEND EQU BASEA+7H ; Transmit status (read) ; Transmit command (write) EGLOW EQU BASEA+8H ; General purpose pointer for R/W to ; packet buffer, low byte EGHI EQU BASEA+9H ; high byte, total of 11 bits for 2K buffer ERLOW EQU BASEA+0AH ; Receive pointer, set by board (read) low byte ; Receive buffer pointer clear (write) ERHI EQU BASEA+0BH ; high byte of Receive pointer EPROM EQU BASEA+0CH ; PROM window address ; DH ?? EAUX EQU BASEA+0EH ; Auxiliary Status (read) ; Aux Command (write) EBUF EQU BASEA+0FH ; Buffer window (where to I/O to net) ; ; Transmit command options ; what conditions we wish to be interrupted on after transmit ; EDTUNDER EQU 01H ; Detect underflow (bad CRC), not used EDTCOLL EQU 02H ; Detect collision on xmit EDTC16 EQU 04H ; Detect 16 consecutive collisions (net down) EDTOK EQU 08H ; Detect successful transmission ; other 4 bits unused in this reg EXMITNORM EQU 00H ; Normal use for interrupt-driven XMIT ; ; Transmit status results ; ; Use same values as commands, 08h means OK to xmit again ; ;***************************** ; Receive Command Options ; ; If something is not detected for, the receiver automatically discards ; those packets. ; EDTOVER EQU 01H ; Detect Overflow condition EDTFCS EQU 02H ; Detect FCS error, bad CRC on packet EDTDRB EQU 04H ; Detect dribble errors and accept them EDTSHORT EQU 08H ; Detect short frames (< 60 bytes) EDTEOF EQU 10H ; Detect no overflow (end-of-frame found) EGOOD EQU 20H ; Accept good frames ; four values legal for the last two bits: ECLOSE EQU 00H ; Turn off receiver EGETALL EQU 40H ; Get all packets, no matter what address EBROAD EQU 80H ; get those for me or for broadcast EMULTI EQU 0C0H ; get those for me or for multicast EWANT EQU 0A0h ; EGOOD OR EBROAD ; which packets we will look for on net ; ; Receive Status results ; ; errors are not detected unless asked for...otherwise the board just ; won't take bad packets off of the net. ; ERROVER EQU 01H ; overflow error ERRFCS EQU 02H ; FCS (checksum) error ERRDRB EQU 04H ; Dribble error ERRSHORT EQU 08H ; Short frame error ENOOVER EQU 10H ; Received without overflow error ; means that we didn't miss any by being slow ;EGOOD EQU 20H ; as above, we received a valid frame ; undefined 40h ESTALE EQU 80H ; stale receive condition, already read me ; ; Aux command register ; EIRE EQU 01H ; interrupt request enable (no DMA) new boards EBADFCS EQU 02H ; create bad checksum for testing only ; ; Next two bits tell who has access to packet buffer ; EBUS EQU 00H ; System bus has control of buffer EXMIT EQU 04H ; Transmit packet in buffer, auto kick ; back to recieve status EGETEM EQU 08H ; Receive state, look for packets ELOOP EQU 0CH ; Transmit and loopback into xmit buffer ; 10H unused EDMA EQU 20H ; Starts a DMA xfer ERIDE EQU 40H ; Interrupt and DMA enable ERESET EQU 80H ; Reset the Ethernet board ; ; Aux status register ; ERBUSY EQU 01H ; Receive busy, receiver looking for packets ; 02H ; echos command status EBADFCS ; 04,08h ; echos command status for EXMIT,EGETEM,EBUS EDMADONE EQU 10H ; goes to one when DMA finishes ; 20H ; echos DMA request bit ; 40h ; echos RIDE bit EXBUSY EQU 80H ; for polled xmit, goes to 0 when xmit is done ; ; ; Macros for in and out ; MOUT MACRO REG,STUFF ; one byte to the given I/O register MOV DX,REG MOV AL,STUFF OUT DX,AL ENDM ; MOUTW MACRO REG,LO,HI ; two bytes to the I/O double port MOV DX,REG MOV AL,LO OUT DX,AL INC DX MOV AL,HI OUT DX,AL ENDM ; MIN MACRO REG ; get one byte to al MOV DX,REG IN AL,DX ENDM DGROUP group _DATA _DATA segment public 'DATA' assume DS:DGROUP PUBLIC _STAT,_BUFPT,_BUFORG,_BUFEND,_BUFREAD,_BUFBIG,_BUFLIM,_OFFS _STAT DB 00H ; last status from read _BUFBIG DW 00H ; buffer space used _BUFLIM DW 05000H ; buffer space limit _BUFPT DW 00000H ; where buffer pointer is, initialized safely _BUFDS DW 0a000H ; where buffer is, ds _BUFORG DW 00000H ; start of buffer space _BUFDS2 DW 0a000H ; another ds _BUFEND DW 06000H ; end limit of allowable buffer space _BUFDS3 DW 0a000H _BUFREAD DW 00000H ; where the read pointer is _BUFDS4 DW 0a000H SAVECS DW 00H ; where to save the old interrupt ptr SAVEIP DW 00H OLDMASK DB 00H ; save interrupt controller mask DEAF DB 00H ; when we can't handle any more packets _OFFS DW 00H ; how many times the handler was turned off ; ; use variables to access IRQ3 or IRQ5 ; 3 is COM2, 5 is LPT2 ; ;WHICHINT EQU 4*0Bh ; Interrupt for interrupt I/O on IRQ3 ;WHICHINT EQU 4*0Dh ; Interrupt for interrupt I/O on IRQ5 ;TURNOFF EQU 08H ; IRQ3 bit mask for 8259 controller (1<<3) ;TURNOFF EQU 020H ; IRQ5 bit mask for 8259 controller (1<<5) ;TURNON EQU 0F7H ; IRQ3 enable bit mask for 8259 controller ;TURNON EQU 0DFH ; IRQ5 enable bit mask for 8259 controller INTNUM DB 0BH ; Defaults to IRQ3, interrupt handler 0bh WHICHINT DW 4*0BH ; ETOPEN can change these values TURNOFF DB 08H TURNON DB 0F7H _DATA ends ; ; ; ; The subroutines to call from C ; _TEXT segment public 'CODE' assume CS:_TEXT PUBLIC _RECV,_ETOPEN,_ETCLOSE,_GETADDR,_SETADDR,_XMIT,_ETUPDATE ;****************************************************************** ; ETOPEN ; Initialize the Ethernet board, set receive type. ; ; usage: etopen(s,irq,dma) ; char s[6]; ethernet address ; int irq,dma; interrupt number and dma channel to use ; _ETOPEN PROC FAR PUSH BP MOV BP,SP MOUT EAUX,ERESET ; reset the board MOUT EAUX,0 ; Clear the reset bit, otherwise keeps resetting ; ; check the parameters for interrupt and dma ; MOV AX,[BP+X+4] ; interrupt number CMP AL,5 ; If not 5, then use 3 JNZ USE3 MOV INTNUM,0DH ; Interrupt # for handler for IRQ5 MOV WHICHINT,4*0DH ; Interrupt handler location in vector table MOV TURNOFF,020H ; mask for interrupt controller for IRQ5 MOV TURNON,0DFH ; opposite mask, for interrupt controller USE3: MOV AX,[BP+X+6] ; dma channel to use CMP AL,3 ; if not 3, then use 1 JNZ USE1 ; ; Add DMA values for channel 3 here later ; USE1: ; ; install the interrupt handler ; CALL IINST ; DO THE PATCHING OF THE INTERRUPT TABLE ; ; set up the net address ; PUSH DS ; save mine MOV AX,[BP+X+2] ; get new one MOV DS,AX ; set new one MOV SI,[BP+X] ; get pointer, ds:si is ready ; MOV CX,6 MOV DX,EADDR ; get base i/o reg for setting address CLD SADDR: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR ; do 6 times POP DS ; get back DS of local data ; ; enable interrupts here with interrupt handler ; already set up. ; MOUT ESEND,0 ; xmit command = 0 for no interrupts IN AL,DX MOUT EREC,EWANT ; Set receiver for which packets we want IN AL,DX ; reset 'stale' MOUT ERLOW,0 ; Clear the receive buffer pointer CLI MOUT EAUX,EGETEM+ERIDE ; Set for receive, interrupts MIN IMASK ; get current int enable mask MOV BL,AL ; save a copy AND AL,TURNON ; force bit for etherlink board off OUT DX,AL ; put back the byte, IRQ enabled STI AND BL,TURNOFF ; isolate this bit only from oldmask MOV OLDMASK,BL ; save it ; POP BP XOR AX,AX RET _ETOPEN ENDP ; ;****************************************************************** ; SETADDR ; set the Ethernet address on the board to 6 byte ID code _SETADDR PROC FAR PUSH BP MOV BP,SP PUSH DS MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; address of buffer to read ; MOV CX,6 MOV DX,EADDR ; get base i/o reg for setting address CLD SADDR2: LODSB ; get next one OUT DX,AL ; send it INC DX ; next position LOOP SADDR2 ; do 6 times POP DS POP BP RET _SETADDR ENDP ; ;******************************************************************* ; GETADDR ; get the Ethernet address off of the board ; ; usage: getaddr(s); ; char s[6]; will get six bytes from the PROM ; _GETADDR PROC FAR PUSH BP MOV BP,SP PUSH ES ; save mine MOV AX,[BP+X+2] ; get new one MOV ES,AX ; set new one MOV DI,[BP+X] ; get pointer, es:di is ready ; MOV BX,0 ; start location MOV CX,EPROM ; address window GADDR: CLD MOUTW EGLOW,BL,BH ; set gp to the right value MIN CX ; get value from prom address window STOSB ; put into given buffer INC BX ; next position CMP BX,6 JNZ GADDR ; do 6 times POP ES POP BP RET _GETADDR ENDP ; ;*********************************************************************** ; ETCLOSE ; shut it down ; _ETCLOSE PROC FAR CLI MOUT EAUX,ERESET ; Turn off all pendings, cause reset MOUT EAUX,0 ; Turn off reset ; ; Make sure that DMA transfers are not outstanding (later) ; ; ; mask out IRQ on interrupt controller ; MIN IMASK ; get current mask OR AL,TURNOFF ; force that bit on OUT DX,AL ; send it back to controller STI CALL DEINST ; restore old interrupt handler MOV BL,OLDMASK ; get back saved setting of irq NOT BL ; flip it CLI MIN IMASK AND AL,BL ; restore setting of that bit OUT DX,AL STI RET _ETCLOSE ENDP ; ;************************************************************************ ; Receive ; This version is unused. It is a polled receive which is nearly ; useless with this board. It hasn't been debugged yet, either. ; get those packets into a buffer ; ; usage: size = recv(where); ; char *where; at least 2048 bytes of room ; returns # bytes in packet, -1 if no packet available ; _RECV PROC FAR PUSH BP MOV BP,SP PUSH ES MOV AX,[BP+X+2] ; get new es value MOV ES,AX MOV DI,[BP+X] ; set di for later movement ; MOV CX,10 ; give it a few tries FINDONE: MIN EAUX ; get status to al MOV _STAT,AL AND AL,ERBUSY ; is it still in receive state or done? JZ READONE ; done, can read it LOOP FINDONE MOV AX,-1 ; no packet yet, return POP ES POP BP RET ; READONE: MOUT EAUX,EBUS ; turn off receive, give buffer to bus MOUTW EGLOW,0,0 ; clear general purpose pointer for read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length MOV DX,EBUF ; window to the data DOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP DOBYTES MIN EREC ; get status to al, clears read MOV _STAT,AL ; KEEP LAST STATUS BYTE ; ; set up to read the next packet from the net ; MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux MOV AX,BX ; return value is # of bytes POP ES POP BP RET _RECV ENDP ; ;************************************************************************ ; XMIT ; send a packet to Ethernet ; Is not interrupt driven, just call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; _XMIT PROC FAR PUSH BP MOV BP,SP PUSH DS ; set up proper ds for the buffer MOV AX,[BP+X+2] MOV DS,AX MOV SI,[BP+X] ; offset for buffer MOV AX,[BP+X+4] ; count of bytes MOV CX,AX ; save a copy, might be less than 60, ok CMP AX,60 ; minimum length for Ether JNB OKLEN MOV AX,60 ; make sure size at least 60 OKLEN: MOV BX,2048 ; total length of buffer SUB BX,AX ; offset of for buffer pointer to start MOV DI,BX ; save a copy of the buffer pointer ; ; TAKE CONTROL OF THE INPUT BUFFER ; MOUT EAUX,EBUS+ERIDE ; take buffer away from receiver MOUT ERLOW,0 ; clear receive pointer for next read MOUTW EGLOW,BL,BH ; set the general purpose pointer MOV DX,EBUF ; window to packet buffer CLD FILLBUF: LODSB ; get value to go into buffer OUT DX,AL ; put it into buffer (autoincrement) LOOP FILLBUF ; do whole count POP DS ; ; PACKET IS IN BUFFER, READY TO BE SENT ; TRYAGAIN: MOV BX,DI ; retrieve copy of offset pointer MOUTW EGLOW,BL,BH ; set the general purpose pointer (again) ; MOUT EAUX,EXMIT+ERIDE ; tell the board to send it and start receiving NOTDONEX: MIN EAUX ; waiting for transmit to finish AND AL,EXBUSY ; is it done yet? JNZ NOTDONEX ; no, wait some more MOV CX,0 ; return value, ok MIN ESEND ; get xmit status MOV BL,AL ; save status AND AL,EDTOK ; was it ok? JNZ DONEX ; yes, successful xmit ; ; handle the possible errors, return 1 on coll16 ; coll16 generally means that the network has failed ; MOV AL,BL ; get copy of status back AND AL,EDTC16 ; check collision 16 JNZ RET16 ; yes, network probably down MOV AL,BL ; get copy back again AND AL,EDTCOLL ; check for collision status JZ UNK ; no, unknown problem MOUT EAUX,EBUS+ERIDE ; collision, reset buffer control JMP TRYAGAIN ; go for it UNK: MOV CX,2 ; unknown problem return code JMP DONEX RET16: MOV CX,1 ; failure return DONEX: MOUT EREC,EWANT ; reset receive register filter necessary MIN EAUX AND AL,ERBUSY ; is it still in receive state or done? JNZ DONEMIT ; not ready now, return instead MOV AL,INTNUM CMP AL,0BH ; two choices of int to call JNZ TRYNINT INT 0BH ; we do have a packet, read it JMP DONEMIT TRYNINT: CMP AL,0DH JNZ DONEMIT INT 0DH DONEMIT: MOV AX,CX ; put return in ax POP BP RET _XMIT ENDP ; ;************************************************************************* ; INTERRUPT HANDLER, INSTALLATION AND DEINSTALLATION ; IHAND ; take the receive packet out of the input buffer ; DEINST PROC NEAR MOV CX,SAVEIP ; get old ip from save spot MOV DX,SAVECS ; get old cs from save spot MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX CLI MOV [BX],CX ; store old ip into the table INC BX INC BX ; move pointer in interrupt table MOV [BX],DX ; store old cs into the table STI POP DS RET DEINST ENDP ; IINST PROC NEAR MOV CS:MYDS,DS ; store for use by handler MOV BX,WHICHINT ; interrupt in table for 3com board PUSH DS XOR AX,AX ; system interrupt table MOV DS,AX MOV AX,OFFSET IHAND ; where the handler is CLI MOV DX,[BX] ; keep copy of the ip MOV [BX],AX ; store ip into the table INC BX INC BX ; move pointer in interrupt table MOV CX,[BX] ; keep copy of the cs, too MOV AX,CS MOV [BX],AX ; store new cs into the table STI POP DS MOV SAVEIP,DX ; store them away MOV SAVECS,CX RET MYDS DW 00H ; the data segment for this assembly code ICNT DB 00H IHAND: ; not a public name, only handles ints STI PUSH DS PUSH ES PUSH AX PUSH BX PUSH CX PUSH DX PUSH DI CLD ; all moves will be forward ; ; SET UP CORRECT DS ; MOV DS,CS:MYDS ; get correct ds MOV AX,_BUFDS ; buffer's ds MOV DI,_BUFPT ; where buffer is MOV ES,AX ; ; check for buffer overrun or catching up with reader ; ; implicit 64K max buffer, should stop before 64K anyway ; MOV AX,_BUFBIG ; how much stuff is in buffer MOV BX,_BUFLIM ; what is our size limit? CMP AX,BX JNA ISROOM ; we are ok ; ; no room at the Inn. turn off receiver ; MOUT EAUX,EBUS+ERIDE ; refuse to read more packets until restarted MIN EREC ; must clear interrupt MOV AL,1 ; SET FLAG MOV DEAF,AL ; WE ARE NOW DEAF, READ ROUTINE MUST RESTART JMP ENDINT ; can't do much, we lose packets until restarted ; ; wrap pointer around at end, we know that we have room ; ISROOM: MOV DX,_BUFEND ; right before 2K safety area CMP DX,DI ; see if pointer is over limit JA OKAYREAD ; we are not at wrap-around MOV AX,_BUFORG ; wrap to here MOV _BUFPT,AX ; wrap-around MOV DI,AX ; di also ; ; here, DI contains where we want to put the packet. ; OKAYREAD: ; MOV CX,10 ; give it a few tries IFINDONE: ; MIN EAUX ; get status to al ; AND AL,ERBUSY ; is it still in receive state or done? ; JZ IREADONE ; done, can read it ; LOOP IFINDONE ; MIN EREC ; AND AL,ESTALE ; JZ IREADONE ; jmp ireadone ; MOV AX,0 ; no packet yet, spurious int, return ; STOSB ; MIN EREC ; clear interrupt condition ; MIN ESEND ; in case it was an xmit spurious int ; JMP STOPINT ; IREADONE: MOUT EAUX,EBUS+ERIDE ; turn off receive, give buffer to bus MOUTW EGLOW,0,0 ; clear general purpose pointer for read MIN EREC ; get status to al, clears read MOV DX,ERLOW ; receive buffer pointer IN AL,DX MOV CL,AL ; save low byte INC DX IN AL,DX MOV CH,AL ; save high byte MOV BX,CX ; save another copy of the length OR BX,BX ; check for non-zero JZ STOPINT ; no packet MOV AX,BX ; SAVE LENGTH IN BUFFER, BEFORE PACKET STOSW MOV DX,EBUF ; window to the data IDOBYTES: IN AL,DX ; get a byte STOSB ; save it to es:di LOOP IDOBYTES ; ; ; DI now contains updated value for BUFPT, BX contains size of packet ; MOV _BUFPT,DI ; IT IS HERE, NOW MOV AX,_BUFBIG ; TOTAL AMOUNT OF STUFF IN BUFFER ADD AX,BX INC AX INC AX ; TO COVER THE LENGTH VALUE MOV _BUFBIG,AX ; AFTER ADDING IN CURRENT PACKET SIZE ; ; signs that something is actually happening ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC CS:ICNT ; MOV Al,CS:ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux ENDINT: MOUT ICTRL,ENDOFI ; signal end of interrupt POP DI POP DX POP CX POP BX POP AX POP ES POP DS IRET IINST ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; _ETUPDATE PROC FAR PUSH ES MOV AX,_BUFDS ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,_BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,_BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,_BUFORG ; wrap to here NOWRAPRD: MOV _BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,_BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV _BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,_BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC _OFFS ; keep count how many times this happened MOUT ERLOW,0 ; reset receive buffer ptr MOUT EAUX,EGETEM+ERIDE ; turn on receiver ALIVE: POP ES RET _ETUPDATE ENDP _TEXT ends END t call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; _XMIT PROC FAR PUSH BP MOV BP,SP PUSH DS ; set up prsource/msc/readme.msc4.0 644 144 13 652 4267455710 10344 The files in the MSC directory are the examples from the 2.1 release of the source code. 2.2 has NOT been compiled under MSC 4.0 yet. Use these files and the makefile as an example to help you get started on the port. ASM: Need underscore in front of names. Change the registers that pass back LONG values or pointers. Change the static frame buffer pointers in RGH.C and RGE.C and other RGs. Tim Krauskopf 6/15/88 source/compile.bat 644 144 13 3533 4267463753 7546 rem Compiling NCSA Telnet with Lattice C 3.X compiler. rem Compiler must be in \LC on the local hard disk and in the path. rem MASM must be in the path. rem Default dir must be set to telnet source directory. rem Requires ENET,TEK and VS subdirectories in place. rem Take out all occurrences of -qd: if you don't have a d: ramdisk or disk. rem pause set lib=\lc\l set include=\lc rem Compiling Tektronix routines . . . cd tek masm egaset ; lc -ml -qd: vgtek lc -ml -qd: tekstor lc -ml -qd: r*.c erase tek.lib oml tek.lib r vgtek tekstor rgh rge rgp rg0 rg9 rgc rghp egaset copy tek.lib .. cd .. rem Done with Tek rem Starting Virtual screen directory cd vs lc -ml -qd: vsinterf lc -ml -qd: vsintern lc -ml -qd: vsem erase vs.lib oml vs.lib r vsinterf vsintern vsem copy vs.lib .. cd .. rem finished VS directory rem Assembling enet drivers cd enet masm net3com ; masm net501 ; masm net523 ; masm netub ; masm net5210 ; masm net8003 ; masm netzyp ; cd .. rem finished enet rem Compiling TELBIN.EXE lc -ml -qd: ip lc -ml -qd: dlayer lc -ml -qd: confile lc -ml -qd: protinit lc -ml -qd: tools lc -ml -qd: rspc lc -ml -qd: tcp lc -ml -qd: user lc -ml -qd: pctools lc -ml -qd: bkgr lc -ml -qd: look lc -ml -qd: util lc -ml -qd: pcutil masm ipasm.asm ; masm ncsaio ; rem Library creation and linking . . . oml tcp.lib r tcp user pctools tools protinit dlayer ip ipasm oml tcp.lib r enet\net3com enet\net501 enet\net523 enet\net8003 enet\net5210 enet\netub enet\netzyp oml sess.lib r bkgr util pcutil confile link \lc\l\c+look+ncsaio+rspc,telbin,nul,lc+lcm+tcp+vs+sess+tek rem Done with TELBIN.EXE rem Telpass.exe lc -ml -qd: telpass link \lc\l\c+telpass+ncsaio,telpass,nul,lc rem Done with TELPASS rem Starting ftp client copy ftp\ftppi.h copy ftp\ftpbin.c lc -ml -qd: ftpbin.c link \lc\l\c+ftpbin+ncsaio,ftpbin,nul,lc+lcm+tcp+sess rem finished ftp client cket out of the input buffer ; DEINST PROC NEAR MOV CX,SAVEIP ; get old ip from save spot MOV DX,SAVECS ; get old cs from save spot MOV BX,WHICHINT ; interrupt insource/readme.src 644 144 13 464 4267464114 7344 Download all of the source code into the correct subdirectories and then invoke COMPILE.BAT. Note the restrictions posted at the beginning of COMPILE.BAT. Use ASCII mode transfer to download to the PC. Please let me know if there are any problems with the posted files. Tim Krauskopf timk@ncsa.uiuc.edu user.c!þ protinit.c"  confile.c" pcutil.c" look.c"util.c" pctools.c"tcp.c"ip.c"rspc.c"bkgr.c" ncsaio.asize of packet ; MOV _BUFPT,DI ; IT IS HERE, NOW MOV AX,_BUFBIG ; TOTAL AMOUNT OF STUFF IN BUFFER ADD AX,BX INC AX INC AX ; TO COVER THE LENGTH VALUE MOV _BUFBIG,AX ; AFTER ADDING IN CURRENT PACKET SIZE ; ; signs that something is actually happening ; ; MOV AX,0B000H ; screen ; MOV ES,AX ; MOV DI,3998 ; lower right corner ; INC CS:ICNT ; MOV Al,CS:ICNT ; character ; STOSB ; ; set up to read the next packet from the net ; STOPINT: MOUT ERLOW,0 ; clear receive buffer pointer MOUT EAUX,EGETEM+ERIDE ; set receive bit in aux ENDINT: MOUT ICTRL,ENDOFI ; signal end of interrupt POP DI POP DX POP CX POP BX POP AX POP ES POP DS IRET IINST ENDP ; ;************************************************************************* ; ETUPDATE ; update pointers and/or restart receiver when read routine has ; already removed the current packet ; _ETUPDATE PROC FAR PUSH ES MOV AX,_BUFDS ; establish data segment to buffer MOV ES,AX ; put that in es ; MOV BX,_BUFREAD ; where read pointer is now MOV DX,ES:[BX] ; get size of this packet INC DX INC DX ; TWO MORE FOR LENGTH VALUE ADD BX,DX ; increment bufread by size of packet MOV CX,_BUFEND ; right before 2K safety area CMP BX,CX ; see if pointer is over limit JB NOWRAPRD ; we are not at wrap-around MOV BX,_BUFORG ; wrap to here NOWRAPRD: MOV _BUFREAD,BX ; buffer pointer has been updated ; ; DECREMENT TOTAL BUFFER SIZE ; CLI ; keep interrupt handler from bothering dec MOV CX,_BUFBIG ; size before removing packet SUB CX,DX ; remove size of current packet MOV _BUFBIG,CX ; put it back STI ; ; IF RECEIVER IS ON, THEN CHECKING BUFLIM IS UNNECESSARY. ; MOV AL,DEAF ; is the receiver turned off? OR AL,AL ; 0 = reading, 1 = deaf JZ ALIVE ; ; CHECK FOR ROOM IN THE BUFFER, IF THERE IS, TURN ON RECEIVER ; MOV AX,_BUFLIM ; what is our limit? CMP CX,AX ; compare to limit JA ALIVE ; not really alive, but can't turn on yet XOR AL,AL MOV DEAF,AL ; reset flag INC _OFFS ; keep count how many times this happened MOUT ERLOW,0 ; reset receive buffer ptr MOUT EAUX,EGETEM+ERIDE ; turn on receiver ALIVE: POP ES RET _ETUPDATE ENDP _TEXT ends END t call it when you need it. ; ; usage: xmit(packet,count) ; char *packet; ; int count; ; ; Takes a packet raw, Ethernet packets start with destination address, ; and puts it out onto the wire. Count is the length of packet < 2048 ; ; checks for packets under the Ethernet size limit of 60 and handles them ; _XMIT PROC FAR PUSH BP MOV BP,SP PUSH DS ; set up prsource/msc/readme.msc4.0 644 144 13 652 4267455710 10344