pcbridged.c1006440004230000000000004204105372434132012136 037777777luigisystem 1 0 /* * PCbridge managing daemon. * taken from bootpd sources * Options are: * -c command send a command * 0 = dump data segment (256 bytes) * 1 = dump filtering table (256 bytes) * 2 = clush filtering table * -o offset send offset (for cmd 0 and 1) * -e ethaddr send the offset corresponding to ethaddr (for cmd 1) * -t get traffic data * -l filename log data to file * -s use as standalone, otherwise use with inetd * -s is useless at the moment * -d increase debugging verbosity */ #define MYNET 0xffffffff /* insert here you net. broadcast address */ #include #include #include #include #include #include #include #include #ifdef SUNOS40 # include # include #endif #include #include #include #include #include #include #include #if 1 /* #ifdef SYSLOG */ # include #endif /* * Externals, forward declarations, and global variables */ #define byte char #define TRUE 1 #define FALSE 0 #define MAXHADDRLEN 6 #define MAXPORTS 5 #define NUMCOUNTERS 8 struct bdg_if_addr { unsigned char a[8]; }; struct counter { unsigned char ct[8]; }; struct filters { char mask[6]; }; /*** all data here are in little-endian format ***/ struct pcbridge_data { char magic[16]; char if_found[8]; struct bdg_if_addr if_addr[8]; struct counter stat[3+MAXPORTS*NUMCOUNTERS]; short video_seg; char loop_counters[8]; unsigned char filt[16*6+2]; short table_data_off; struct bdg_if_addr table[64]; }; #define MIN_SIZE (sizeof(struct pcbridge_data)-8-16*6-2-2-512) struct bridges { unsigned char hwaddr[6]; char *bdg_name; char *port_name; } known_bridges[1024]; int n_known_bridges=0; void usage(); void report(); void readtable(); void cleanup(); /* * IP port numbers for client and server obtained from /etc/services */ #define PCBRIDGE_SPORT 2299 #define PCBRIDGE_CPORT 2298 u_short pcbridge_port; struct servent *servp; /* * Internet socket and interface config structures */ unsigned thecommand=0xff; unsigned short theoffset=0; struct sockaddr_in sin; struct sockaddr_in cmdsock; struct sockaddr_in from; /* Packet source */ struct ifreq ifreq[10]; /* Holds interface configuration */ struct ifconf ifconf; /* Int. config ioctl block (pnts to ifreq) */ /* * General */ char *logfilename=NULL; FILE *logfile; int traffic=FALSE; int standalone = TRUE; /* set to FALSE to use with inetd */; int debug = 3; /* Debugging flag */ int s; /* Socket file descriptor */ byte buf[2048]; /* Receive packet buffer */ /* struct timeval tp; /* Time (extra baggage) */ unsigned long thecounter(struct counter i) { return ( ( ( ( (i.ct[5]<<8)+i.ct[4])<<8) + i.ct[3])<<8) + i.ct[2]; } char *stat_string[]= { "Rx packets", "Fwd all ", "Tx packets", "Dropped ", "Local drop", "Rx bridge ", "Rx bytes ", "Bad rx pkt", NULL}; int addrcmp(char *a, unsigned char *ha) { int i; char *b= (char *)ha; for (i=0;i<6;i++) if (*a++ != *b++) return 0 ; return 1; } int dump_packet(struct pcbridge_data *p, int n) { int i,j,stat; char string[80]; struct bdg_if_addr h; char *name=NULL, *port=NULL; char *port_names[MAXPORTS]; unsigned char *q; if (traffic && logfile) { for (i=1;i<=MAXPORTS;i++) { if (p->if_found[i]) { h= (p->if_addr[i]); fprintf(logfile, "TIME %6lx PORT %d-%02x:%02x:%02x:%02x:%02x:%02x RXBYTES %12u\n", thecounter(p->stat[2]),i, h.a[0],h.a[1],h.a[2],h.a[3],h.a[4],h.a[5], thecounter(p->stat[2+5*6+i]) ); fflush(logfile); } } } if (traffic) return; /* printf("Packet size %d\n",n); */ j=p->if_found[0]; h= (p->if_addr[j]); for (i=0;imagic,name, port, j,h.a[0],h.a[1],h.a[2],h.a[3],h.a[4],h.a[5]); printf(" Loops 0x%lx -- Hash Table Collisions %ld -- Time %06lx\n", thecounter(p->stat[0]), thecounter(p->stat[1]),thecounter(p->stat[2])); for (i=1;i<=MAXPORTS;i++) { port_names[i-1]="???"; if (p->if_found[i]) { h= (p->if_addr[i]); for (j=0;jif_found[0] ? '*':' ', port_names[i-1], i, h.a[0],h.a[1],h.a[2],h.a[3],h.a[4],h.a[5], string); } } for (q= &(p->filt[0]); *q; q += 6) { printf("[%04x..%04x[: %s outside, %s inside\n", q[2]+q[3]*256, q[4]+q[5]*256, (q[0]==1 ? "keep" : (q[0]==2 ? "discard" : "--")), (q[1]==1 ? "keep" : (q[1]==2 ? "discard" : "--")) ); } printf("\n%10s",""); for (j=0;j<5; j++) { printf("%14s",p->if_found[j+1]?port_names[j]:""); } printf("\n%10s",""); for (j=0;j<5; j++) { printf(p->if_found[j+1]?" == Port %d ==":"",j+1); } printf("\n"); for (i=0;i<8;i++) { printf(stat_string[i]); for (j=0;j<5; j++) { if (p->if_found[j+1]) printf("%14u",thecounter(p->stat[3+5*i+j])); else printf("%14s",""); } printf("\n"); } { unsigned short off=p->table_data_off; printf("Table offset %4x\n",off); for (i=0; i<64;i++) { struct bdg_if_addr bdg; bdg= p->if_addr[p->if_found[0]]; h= (p->table[i]); if ( h.a[6] != 255 ) { printf("[Entry %4d] %02x:%02x:%02x:%02x:%02x:%02x port %d - %d\n", i, h.a[0],h.a[1],h.a[2],h.a[3],h.a[4],h.a[5], h.a[6],h.a[7]); if (logfile) { fprintf(logfile, "IF %02x:%02x:%02x:%02x:%02x:%02x %4x PORT %d %2x SRC %02x:%02x:%02x:%02x:%02x:%02x TIME %6lx\n", h.a[0],h.a[1],h.a[2],h.a[3],h.a[4],h.a[5], off + i*8 - 0x200 /* patch for bad EPROM 2.64 */, h.a[6],h.a[7], bdg.a[0],bdg.a[1],bdg.a[2],bdg.a[3],bdg.a[4],bdg.a[5], thecounter(p->stat[2]) ); fflush(logfile); } } } } printf("\n"); } /* * Initialization such as command-line processing is done and then the main * server loop is started. */ main(argc, argv) int argc; char **argv; { struct timeval actualtimeout, *timeout; int n, tolen, fromlen; int nfound, readfds; timeout = &actualtimeout; /* * Read switches. */ for (argc--, argv++; argc > 0; argc--, argv++) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 't': /* traffic */ traffic=TRUE; break; case 'c': /* command */ argc--; argv++; thecommand=atoi(argv[0]); if (thecommand>2) thecommand=0; break; case 'o': /* offset */ argc--; argv++; sscanf(argv[0],"%i",&theoffset); break; case 'e': /* offset */ {int b2,b3,b4,b5; argc--; argv++; sscanf(argv[0],"%*x:%*x:%x:%x:%x:%x", &b2, &b3, &b4, &b5); theoffset= (((b2 ^ b4) + ((b3 ^ b5)<<8) )<<3) & 0xffff; } break; case 'l': /* logging */ argc--; argv++; logfilename=argv[0]; logfile=fopen(logfilename,"w+"); if (logfile==NULL) { fprintf(stderr,"Can't open logfile %s\n",logfilename); } break; case 'd': debug++; break; case 's': standalone = TRUE; break; default: fprintf(stderr, "pcbridged: unknown switch: -%c\n", argv[0][1]); usage(); break; } } else { usage(); } } if (standalone) { /* * Go into background and disassociate from controlling terminal. */ if (debug < 3) { if (fork()) exit(0); for (n = 0; n < 10; n++) (void) close(n); (void) open("/", O_RDONLY); (void) dup2(0, 1); (void) dup2(0, 2); n = open("/dev/tty", O_RDWR); if (n >= 0) { ioctl(n, TIOCNOTTY, (char *) 0); (void) close(n); } } /* * Nuke any timeout value */ timeout = NULL; } #ifdef SYSLOG /* * Initialize logging. */ #ifndef LOG_CONS #define LOG_CONS 0 /* Don't bother if not defined... */ #endif #ifndef LOG_DAEMON #define LOG_DAEMON 0 #endif openlog("pcbridged", LOG_PID | LOG_CONS, LOG_DAEMON); syslog(LOG_INFO, "%s", Version); #endif readtable(); if (standalone) { /* * Get us a socket. */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { report(LOG_ERR, "socket: %s\n", "can't get socket" ); exit(1); } /* * Get server's listening port number */ servp = getservbyname("pcbridge", "udp"); if (servp) { pcbridge_port = ntohs((u_short) servp->s_port); } else { report(LOG_ERR, "udp/pcbridge: unknown service -- assuming port %d\n", PCBRIDGE_SPORT); pcbridge_port = (u_short) PCBRIDGE_SPORT; } /* * Bind socket to pcbridge port. */ sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(pcbridge_port); if (bind(s, &sin, sizeof(sin)) < 0) { report(LOG_ERR, "bind: %s\n", "can't bind to pcbridge_port"); exit(1); } } else { /* * Assume socket was passed to us from inetd */ s = 0; tolen = sizeof(sin); bzero((char *) &sin, tolen); if (getsockname(s, &sin, &tolen) < 0) { report(LOG_ERR, "getsockname: %s\n","failed" ); exit(1); } pcbridge_port = ntohs(sin.sin_port); } { int c=1; setsockopt(s, SOL_SOCKET, SO_BROADCAST, &c, sizeof(c) ); } /* * Determine network configuration. */ ifconf.ifc_len = sizeof(ifreq); ifconf.ifc_req = ifreq; if ((ioctl(s, SIOCGIFCONF, (caddr_t) &ifconf) < 0) || (ifconf.ifc_len <= 0)) { report(LOG_ERR, "ioctl: %s\n", "failed"); exit(1); } /* * Set up signals to clean up on exit. -mah */ signal(SIGSEGV, cleanup); signal(SIGBUS , cleanup); signal(SIGINT, cleanup); signal(SIGILL, cleanup); signal(SIGQUIT, cleanup); signal(SIGFPE, cleanup); signal(SIGIOT, cleanup); /* * Set up signals to read or dump the table. */ if ((int) signal(SIGHUP, cleanup) < 0) { report(LOG_ERR, "signal: %s\n", "cleanup..."); exit(1); } /*** if necessary, send out command ***/ if (thecommand != 0xff) { sendcmd(thecommand,theoffset); thecommand = 0xff; } /* * Process incoming requests. */ for (;;) { struct pcbridge_data *pcd=(struct pcbridge_data *)buf; readfds = 1 << s; nfound = select(s + 1, &readfds, NULL, NULL, timeout); if (nfound < 0) { if (errno != EINTR) { report(LOG_ERR, "select: %s\n", "not eintr"); } continue; } if (!(readfds & (1 << s))) { report(LOG_INFO, "exiting after %ld minutes of inactivity\n", actualtimeout.tv_sec / 60); exit(0); } fromlen = sizeof(from); n = recvfrom(s, buf, sizeof(buf), 0, &from, &fromlen); if (n <= 0) { continue; } if (n < MIN_SIZE) { if (debug) { report(LOG_INFO, "received short packet\n"); } continue; } dump_packet(pcd,n); /* process data */ } } char screen[80*24]; /* * Print "usage" message and exit */ void usage() { fprintf(stderr, "usage: pcbridged [-d] [-s]\n"); fprintf(stderr, "\t -d\tincrease debug verbosity\n"); fprintf(stderr, "\t -s\trun standalone (without inetd)\n"); exit(1); } /* * Process BOOTREQUEST packet. * * (Note, this version of the bootpd.c server never forwards * the request to another server. In our environment the * stand-alone gateways perform that function.) * * (Also this version does not interpret the hostname field of * the request packet; it COULD do a name->address lookup and * forward the request there.) */ /* * This call checks read access to a file. It returns 0 if the file given * by "path" exists and is publically readable. A value of -1 is returned if * access is not permitted or an error occurs. Successful calls also * return the file size in bytes using the long pointer "filesize". * * The read permission bit for "other" users is checked. This bit must be * set for tftpd(8) to allow clients to read the file. */ /* * Send a reply packet to the client. 'forward' flag is set if we are * not the originator of this reply packet. */ struct bdgcmd { unsigned char magic[4]; /* 'L' 'R' .... */ unsigned short cmd[2]; } thecmd; sendcmd(unsigned char cmd, unsigned short off) { struct bdgcmd *bp = &thecmd; struct in_addr dst; struct sockaddr_in to; thecmd.magic[0]='L'; /* magic string */ thecmd.magic[1]='R'; thecmd.cmd[0]=htons(cmd); thecmd.cmd[1]=htons(off); to = sin; to.sin_port = htons(PCBRIDGE_CPORT); /* * If the client IP address is specified, use that * else if gateway IP address is specified, use that * else make a temporary arp cache entry for the client's NEW * IP/hardware address and use that. */ dst.s_addr=htonl(MYNET); to.sin_addr = dst; if (sendto(s, bp, sizeof(struct bdgcmd), 0, &to, sizeof(to)) < 0) { fprintf(stderr, "sendto: %s\n", "failed <0"); } else { fprintf(stderr, "sendto: succeeds\n"); } } /* * Compare function to determine whether two hardware addresses are * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE * otherwise. * * This function is used when retrieving elements from the hardware address * hash table. */ /* * Convert a hardware address to an ASCII string. */ char *haddrtoa(haddr, htype) register byte *haddr; byte htype; { static char haddrbuf[2 * MAXHADDRLEN + 1]; register char *bufptr; register unsigned count; bufptr = haddrbuf; for (count = MAXHADDRLEN; count > 0; count--) { sprintf(bufptr, "%02X", (unsigned) (*haddr++ & 0xFF)); bufptr += 2; } return (haddrbuf); } /* * This routine reports errors and such via stderr and syslog() if * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs * from being scattered throughout the code. * * The syntax is identical to syslog(3), but %m is not considered special * for output to stderr (i.e. you'll see "%m" in the output. . .). Also, * control strings should normally end with \n since newlines aren't * automatically generated for stderr output (whereas syslog strips out all * newlines and adds its own at the end). */ /*VARARGS2*/ void report(priority, fmt, p0, p1, p2, p3, p4) int priority; char *fmt; { /* * Print the message */ if (debug > 2) { fprintf(stderr, "*** prbridge: "); fprintf(stderr, fmt, p0, p1, p2, p3, p4); fprintf(stderr, "\n\n"); } #ifdef SYSLOG syslog(priority, fmt, p0, p1, p2, p3, p4); #endif } /* * shutdown the socket on weird signals * otherwise it wont go away on pmaxes * and we get an 'address already in use' on next try * -mah */ void cleanup(sig) int sig; { shutdown(s,2); report(LOG_ERR, "cleanup: got signal %d\n", sig); exit(sig); } int ishex(char c) { if (c>='0' && c<='9') return (c-'0'); else if (c>='a' && c<='f') return (c-'a'+10); else if (c>='A' && c<='F') return (c-'A'+10); else return(-1); } void readtable() { char linebuf[256]; char *p; FILE *f; int i; unsigned char ha[6]; char *bdg_name, *bdg_port; f=fopen("pcbridgetab","r"); if (f==NULL) f=fopen("/etc/pcbridgetab","r"); if (f==NULL) return; while (p=fgets(linebuf,sizeof(linebuf),f)) { while (*p && *p!='#' && *p!='\n') p++; *p='\0'; /* skip comments */ for (p=linebuf;*p && (*p==' ' || *p=='\t'); p++); /* skip blanks */ if (*p) { printf("Found %s\n",p); for (i=0;i<6;i++) { int c1,c2; c1=ishex(*p++); if (c1<0) { fprintf(stderr, "Invalid first digit (%d) %d\n",i,c1); break; } c2=ishex(*p); if (c2== -1 ) { if (*p!=':' && *p!=' ' && *p!='\t') { fprintf(stderr, "Invalid second digit\n"); break; } else { c2=c1; c1=0; } } else p++; known_bridges[n_known_bridges].hwaddr[i]=c1*16+c2; if (i<5) while (*p && ishex(*p)<0) p++; } if (i==6) { /* successful end */ char *p1; char *p2; while (*p && (*p ==' ' || *p=='\t')) p++; p1=p; while (*p && *p !=' ' && *p!='\t') p++; if (*p) { *p++='\0'; while (*p && (*p ==' ' || *p=='\t')) p++; p2= p; while (*p && *p !=' ' && *p!='\t') p++; *p='\0'; } else p2=NULL; known_bridges[n_known_bridges].bdg_name= (char *)malloc(1+strlen(p1)); strcpy(known_bridges[n_known_bridges].bdg_name,p1); if (p2) { known_bridges[n_known_bridges].port_name= (char *)malloc(1+strlen(p2)); strcpy(known_bridges[n_known_bridges].port_name,p2); } else known_bridges[n_known_bridges].port_name=NULL; fprintf(stderr," bdg: %s port: %s\n", known_bridges[n_known_bridges].bdg_name, known_bridges[n_known_bridges].port_name); n_known_bridges++; } } } fclose(f); } ': /* traffic */ traffic=TRUE; break; case 'c': /* command */ argc--; argv++; thecommand=atoi(argv[0]); if (thecommand>2) thecommand=0; break; case 'o': /* offset */ argc--; argv++; sscanf(argv[0],"%i",&theoffset); break; case 'e': /* offset */ {int b2,b3,b4,b5; argc--; argv++; sscanf(argv[0],"%*x:%*x:%x:%x:%x:%x", &b2, &b3, &b4, &b5); theoffset= (((b2 ^ b4) + ((b3 ^ b5)<<8) )<<3) & 0xffffpcbridgetab1006440004230000000000000246005371253721010211 037777777777 1 0 # Similarly to bootptab and others, this file has the following format # A dash # marks the start of comment # each line is one entry, with three fields # eth_address bridge_name bridge_port # An address can have multiple entries to assign multiple names # # A bridge can respond to the following commands: # # LOCATE -- query to tell which interface # a specific address is on. It requires some work to find out a map, # but in the end it shouldn't be difficult as we have already a tree. # Also serves as a PING # # FLUSH -- request to flush the bridging tables. May help, sometimes. # requires some rights to work on the bridge. # # DUMP -- dump internal tables to the host. As the table is large, # and we don't want to mantain protocols etc. in the bridge, we # may want to dump the table in small pieces, e.g. 512bytes or so, # with the page number specified by the host. # # 00:00:c0:01:ea:65 deit ---port1--- # main_port 00:00:c0:fe:f2:2f deit bartoli # pisolo 00:00:c0:ff:e9:65 deit labinfo # r6000 00:00:c0:a1:b0:67 deit deit_main 00:00:c0:96:b0:67 deit demarinis 00:00:c0:94:b0:67 deit_interdip deit 00:00:c0:cc:af:67 deit_interdip interdip 00:00:c0:c4:f1:2f pc-uprog port1 00:00:c0:d9:f1:2f pc-uprog port2 00:00:c0:9d:b0:67 cisid risc6000 00:00:c0:a9:b0:67 cisid empty 00:00:c0:90:b0:67 cisid interdip addr = INADDR_ANY; sin.sin_port = htons(pcbridge_port); if (bind(s, &sin, sizeof(sin)) < 0) { report(LOG_ERR, "bind: %s\n", "can't bind to pcbridge_port"); exit(1); } } else { /* * Assume image.c1006440004230000000000000440005372240611007235 037777777777 1 0 /* * (C) 1993 Luigi Rizzo, Dipartimento di Ingegneria dell'Informazione * Universita' di Pisa, Italy (luigi@iet.unipi.it) * * This program will read binary files ld.com and bridge.com * producing an image file (image.rom) for a 32K ROM * The image is made as follows: * * offset 0h: LD.COM (must be less than 128 bytes) * offset 100h: BRIDGE.COM (must be less than 32K) * The sum modulo 256 of the eprom must be 0 in order to be bootable. * image.c will patch a free location in order to insure this. * * This program is provided as an example only. We didn't try it * on DOS, where there might be problems with BINARY files. * You are welcome to make any changes that are necessary to * make it reliable (please report them, so that I can make them). * available. * */ #include #define IMAGE_SIZE 32768 #define LOADER "ld.com" #define BRIDGE "bridge.com" #define IMAGE "image.rom" #define ORG 0x100 #ifdef MSDOS # define RMODE "r+b" # define WMODE "w+b" #else # define RMODE "r" # define WMODE "w" #endif unsigned char image[IMAGE_SIZE]; main() { unsigned int i,sum; FILE *f; for (i=0;iORG) { fprintf(stderr,"Loader %s too big (%d instead of 256 bytes)\n", LOADER,i); exit(2); } fprintf(stderr,"%s: %d bytes\n",LOADER,i); fclose(f); f=fopen(BRIDGE,RMODE); if (f==NULL) { fprintf(stderr,"Can't open bridge file %s\n",BRIDGE); exit(3); } i=fread(image+ORG,1,IMAGE_SIZE-ORG,f); if (i>=IMAGE_SIZE-ORG) { fprintf(stderr,"Image %s too big (%d instead of %d bytes)\n", BRIDGE,i,IMAGE_SIZE-ORG); exit(4); } fprintf(stderr,"%s: %d bytes\n",BRIDGE,i); fclose(f); sum=0; for (i=0;i WDE_REAL_DEFINE idx I8250_REAL_DEFINE idx PKT_REAL_DEFINE idx ; C507_REAL_DEFINE idx ENDM real_start: mov AX, cs ;; initialize data segment mov DS, AX ;; not really needed... cld ;; direction forward - default IFDEF i8250_declared ;; save the command line .DATA comline_count db 0 ;; a place to put the command line comline db 127 dup (0) baud_rate_div dw 0 ;; used for remote bridging .CODE mov SI, 80h mov AX, cs mov ES, AX mov DI, offset comline_count mov CX, 40h rep movsw mov SI, offset comline ;; if this is a remote bridge ASCII_TO_BINARY ;; get the baud rate cmp AX, 0 jnz not_default mov AX, 19200 ;; 19.2 is the default not_default: shr AX, 1 ;; compute the baud rate div mov BX, AX mov AX, 57600 xor DX, DX div BX mov baud_rate_div, AX ENDIF ;; i8250_declared FOR idx,<1,2,3,4,5> ;; call definition code IF idx le num_dls ;; for existing IF only WDE_DEFINE idx BUFF_DEFINE %(idx*10+1) ;; this is for the serial IF QUEUE_DEFINE %(idx*10+2) BUFF_DEFINE %(idx*10+3) QUEUE_DEFINE %(idx*10+4) Q_IF_DEFINE idx SLIP_DEFINE idx I8250_DEFINE idx, serial_fail PKT_DEFINE idx, packet_fail ; C507_DEFINE idx, card_fail ENDIF ;; idx le num_dls ENDM .CODE mov bdg_flush_ptr,6 mov ax, cs add ax, 1000h mov bdg_table_ptr, AX ;; remember where the table is mov ES, AX xor DI, DI mov CX, 32768 ;; table size mov ax, 0ffffh ;; clean the table. No card has address FFFFFFFFFFFF rep stosw ;; default - forward call INIT_TAB ;; init digits table call PRINT_MASK ;; print mask call PRINT_CARDS ;; print working cards ;; start looking for packets ;; This is the brigde main loop, we want to go FAST! ;; init_ct: mov cx, 2048 ;; cx= # big_loops before increase BIGLOOP_CTR big_loop: push cx FOR idx,<1,2,3,4,5> ;; was 8 IF idx le num_dls BDG_IF_R_ACCESS idx ENDIF ;; idx le num_dls ENDM pop cx IFDEF flush_tables cmp cl,0 jne dont_flush mov es,bdg_table_ptr ;; flush about 16 entries per second mov bx, bdg_flush_ptr ;; wrap in 512 seconds mov byte ptr es:[bx],0ffh ;; flush entry add bx, 8 ;; don't care about wraparound mov bdg_flush_ptr, bx dont_flush: ENDIF ;; flush_tables dec cx jnz big_loop COUNT_CYCLE %BIGLOOP_CTR PRINT_COUNTERS jmp init_ct ;; never exit from here. card_fail: ;; print_reg , AX packet_fail: ;; print serial_fail: ;; print terminate: jmp terminate ;; include proc.asm END bridge_start ;; --------- end of file -------------- proved unreliable (i.e. the bridge hangs every now and then). This is due to a bug in the old 8390 controllers which is not properly dealt with by the bridge software. We didn't fix the bug, because we don't know much about it, and since our bridges with SMC cards have been working continuously for over 8 months, we're not too concerned about this problem. - the BRIDGE eprom, or a floppy disld.asm1006440004230000000000000253505366276467007144 037777777777 1 0 ;;**************************************************************************** ;; loader.asm ;;**************************************************************************** ;; ;; loader for eprom code ;; ;; 14apr93 Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it) ;; ;;**************************************************************************** EPROM_SIZE EQU 32 ;; size eprom, KB DEST_SEGMENT EQU 1000h ;; where the code is copied COM_ORG EQU 100h ;; start IP for .COM files .286 ;; 286 code .model tiny ;; for .COM .CODE ORG 100h the_eprom: DB 55h, 0aah DB 2*EPROM_SIZE ;; eprom size, in 512byte blocks jmp eprom_start; DB 0ffh ;; room for checksum DB 0ffh ;; room for checksum DB 0ffh ;; room for checksum DB 0ffh ;; room for checksum eprom_start: jmp newint19 ;; install as new INT19 handler newint19: mov ax,cs ;; establish addressing mov ds, ax ;; copy to RAM xor si,si ;; ds:si=source xor di,di ;; es:di dest;; mov ax,DEST_SEGMENT ;; dest. segment mov es,ax mov cx, EPROM_SIZE*1024;; cld rep movsb mov ax,DEST_SEGMENT mov ds,ax mov es,ax mov ss,ax mov sp,0fffeh sti ;; set other registers if needed push DEST_SEGMENT ;; prepare to jump... push COM_ORG retf END the_eprom ;; --------- end of file -------------- 299, every 10 seconds. A simple program, called pcbridged, will allow you to collect statistics. pcbridged has been taken from bootpd sources and modified to servebridge.com1007550004230000000000004640305372437477010001 037777777777 1 0 HGNBCBtHAh!2쨀tH 2@aHCBB"2ùHGNBCBtHAj!2쨀tH 2@aHCBB"2ù"HGNBCBtHAd!2쨀tH 2@a"HCBB"2ù*HG(NBCBtH  A%f !02:;7쨀tH >2<=1@237?a0*H1CB8B7"02=<ù2HGHNBCBtH@@AEp@!P2Z[W쨀tH ^2\]Q@RSW_aP2HQCBXBW"P2]\ÌȎ ]#IP3@@4AQ>Hu <@r`:u dHu fHuhHH2ۺЎ&'t!u&&g:w :r:v5:r@s (r:v|Iu ~IuI Ǻ &M&O~ :u2TIs VIuXIN&G;Hu&G;Hu &;Hu &G 辬I#t-Э;r;stuIu IuIr ݋P؃|3|VW&}t5uuu&=t&&݀H_^2 ؎THu VHuXH_^؎Ht إ& t|3|&}uttt&<uIu IuI <t'!Ht : H$uOuuԎ‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$Hu HuH<t')Ht :(H$uOuuȎ‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$lHu HuHY<t'1Ht 0:0H$uOuu̎‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0>Hu HuH+<t'9Ht P:8H$uOuu‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuI|Iu ~IuI݋Hu HuH>Hu#!Ht : H$uOuuԎ‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$Hu HuH>Hu#)Ht :(H$uOuuȎ‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$Hu HuH>Hu#1Ht 0:0H$uOuu̎‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0Hu HuH>Hu#9Ht P:8H$uOuu‹Hu HuHڸЎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIN&t,Iu .Iu0I,6 M<}?>Hu <@r`:u lHu nHupH H2ۺԎ&'t!u&&g:w :r:v5:r@s (r:vIu IuI Ǻ &M&O~ :u2\Is ^Iu`IN&G;Hu&G;Hu &;Hu &G 辬I#t-Э;r;stu Iu IuIr ݋P؃|3|VW&}t5uuu&=t&&݀!H_^2 ؎THu VHuXH_^؎!Ht إ& t|3|&}uttt&<u Iu IuI <t'Ht :H$uOuuЎ‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH<t')Ht :(H$uOuuȎ‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$lHu HuHY<t'1Ht 0:0H$uOuu̎‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0>Hu HuH+<t'9Ht P:8H$uOuu‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIIu IuI݋Hu HuH>Hu#Ht :H$uOuuЎ‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH>Hu#)Ht :(H$uOuuȎ‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$Hu HuH>Hu#1Ht 0:0H$uOuu̎‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0Hu HuH>Hu#9Ht P:8H$uOuu‹Hu HuHڸԎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIN&t4Iu 6Iu8Ix* M<}?>Hu <@r`:u tHu vHuxH(H2ۺȎ&'t!u&&g:w :r:v5:r@s (r:vIu IuI Ǻ &N&O~ :u2dIs fIuhIN&G;&Hu&G;$Hu &;"Hu &G 辬I#t-Э;r;stuIu IuIr ݋P؃|3|VW&}t5uuu&=t&&݀)H_^2 ؎THu VHuXH_^؎)Ht إ& t|3|&}uttt&<uIu IuI <t'Ht :H$uOuuЎ‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH<t'!Ht : H$uOuuԎ‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$lHu HuHY<t'1Ht 0:0H$uOuu̎‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0>Hu HuH+<t'9Ht P:8H$uOuu‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIIu IuI݋Hu HuH>Hu#Ht :H$uOuuЎ‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH>Hu#!Ht : H$uOuuԎ‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$Hu HuH>Hu#1Ht 0:0H$uOuu̎‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0Hu HuH>Hu#9Ht P:8H$uOuu‹Hu HuHڸȎظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIN&tIu@I N<}?>Hu 03<@r`07:u |Hu ~HuH0H2ۺ̎&'t!u&&g:w :r:v5:r@s (r:vIu IuI 0Ǻ3 &N&O~ :u2lIs nIupIN&G;.Hu&G;,Hu &;*Hu &G 辬I#t-Э;r;stuIu Iu Ir ݋P؃|3|VW&}t5uuu&=t&&݀1H_^2 ؎THu VHuXH_^؎1Ht إ& t|3|&}uttt&<uIu Iu I <t'Ht :H$uOuuЎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH<t'!Ht : H$uOuuԎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$lHu HuHY<t')Ht :(H$uOuuȎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$>Hu HuH+<t'9Ht P:8H$uOuu‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIIu IuI݋Hu HuH>Hu#Ht :H$uOuuЎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH>Hu#!Ht : H$uOuuԎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$Hu HuH>Hu#)Ht :(H$uOuuȎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$Hu HuH>Hu#9Ht P:8H$uOuu‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;@T T8HTźV2T$PHu HuIN&tDIu FIuHI 0N<}?3>Hu PS<@r`PW:u Hu HuH8H2ۺ&'t!u&&g:w :r:v5:r@s (r:vIu IuI PǺS &N&O~ :u2tIs vIuxIN&G;6Hu&G;4Hu &;2Hu &G 辬I#t-Э;r;stu$Iu &Iu(Ir ݋P؃|3|VW&}t5uuu&=t&&݀9H_^2 ؎THu VHuXH_^؎9Ht إ& t|3|&}uttt&<u$Iu &Iu(I <t'Ht :H$uOuuЎ‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH<t'!Ht : H$uOuuԎ‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$lHu HuHY<t')Ht :(H$uOuuȎ‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$>Hu HuH+<t'1Ht 0:0H$uOuu̎‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0Hu HuHIu IuI݋Hu HuH>Hu#Ht :H$uOuuЎ‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T THTź2$Hu HuH>Hu#!Ht : H$uOuuԎ‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T HTź2$Hu HuH>Hu#)Ht :(H$uOuuȎ‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ;T T(HTź2$Hu HuH>Hu#1Ht 0:0H$uOuu̎‹Hu HuHڸظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0Hu HuHN&tLIu NIuPI\ PN<}?SYIttLHu NHuPHLH$u<t>Hu2ؠ\H$:GuGGG|3|P:H$uOuuЎHu HuHGHAPR%EAAAA%AEZXHT THTź2$HU]+uH$X&H$0&<t>Hu2ؠ\H$:GuGGG|3|P: H$uOuuԎHu HuHGHAPR%EAAAA%AEZXHT T HTź2$!HU],u H$X&4!H$0&6u<t>Hud2ؠ\H$:GuGGhG|3|P:(H$uOuuȎHu HuHGHAPR%EAAAA%AEZXHT T(HTź2$)HU]-u(H$X&P)H$0&RY<t>HuH2ؠ\H$:GuGGLG|3|P0:0H$uOuu̎Hu HuHGHAPR%EAAAA%AEZXH T T0HTź624$01HU].u0H$X&l1H$0&n=<t>Hu,2ؠ\H$:GuGG0G|3|PP:8H$uOuuHu HuHGHAPR%EAAAA%AEZXH@T T8HTźV2T$P9HUp]/u8H$X&9H$0&"6\H]H.^H93-駿Ps sFA<|u<|uJH^I2N&&'NyùN$< r r00FFArÎI&&>tII&&>t3 ȎL8LGÀ>HtGI>HtG<>HtG/>HtG">HtGH HE:૸4૸૸૸૸&૸&$uc&*LRu[&uT& uM&G06JJ&G.ݎ<t)<t <t <t"hjˎP3 PعÊ3t 2Ѓ (E8T0Lh8Tp , H x  4 P l ( X t PCBRIDGE v.2.77|05 may 93 18:05|by Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it)|Dipartimento di Ingegneria dell'Informazione|Universita` di Pisa - via Diotisalvi 2, 56126 Pisa, Italy|(original sources by Vance Morrison, Northwestern University)|-=== Bridge Statistics ===|Loops|Tab colls|St I/O MEM|280 D000|2A0 D400|,300 C800|:320 CC00|H340 E000| Eth. addr.| Rx packets| Fwd all| Tx packets|Dropped|Local drop|Rx bridge|Rx bytes|Bad rx pkt|||Ȏظ@+;sO+APR%EAAAA%AEZXAPR%EAAAA%AEZXڋ; T T0HTź624$0Hu HuH>Hu#9Ht P:8Hld.com1007550004230000000000000006205372437477007133 037777777777 1 0 U@Ȏ33؎мhhu &G 辬I#t-Э;r;stuIu Iu Ir ݋P؃|3|VW&}t5uuu&=t&&݀1H_^2 ؎THu VHuXH_^؎1Ht إ& t|3|&}uttt&<uIu Iu I <t'Ht :H$uOuuЎ‹Hu HuHڸ̎ظ@+;sO+APR%EAAAA%AEZXAPR%EAbridge.inc1006440004230000000000002261405372240755007756 037777777777 1 0 ;;------------------------------------------------------------------- ;; File: bridge.inc ;; ;; Original Copyright (C) 1989 Northwestern University, Vance Morrison ;; Changes 05may93 Alessandro Fanelli and Luigi Rizzo (luigi@iet.unipi.it) ;; ;; ORIGINAL COPYRIGHT NOTICE: ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Northwestern University makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;; BDG_IF_R_ACCESS waits for the next packet to come from the board ;; associated with 'name'. Source and Destination address are searched ;; in the table and compared with the bridge's port address, causing ;; the following behaviour: ;; ;; Dest. port Resent to: ;; ============================================== ;; bridge Upper layer ;; this port Dropped ;; other port Destination port ;; not found All ports ;; multi/broadcast All ports and upper layer ;; ;; After processing, the packet is dropped and space freed. ;; ;; Registers: DS:always data segment ;; IN: -- ;; OUT: -- ;; CHANGE: AX, BX, CX, DX, BP, DI, SI, ES BDG_IF_R_ACCESS MACRO name LOCAL got_packet LOCAL forward_all LOCAL forward, not_me, drop LOCAL bridge_end ;; can become an external label LOCAL broadcast, collision, no_collision LOCAL end_filters, next_filter, outside ;; for statistics ;; Reg. Used: -- Free: ALL cmp if_found[name],0 ;; skip non existing interfaces jz bridge_end IF_R_ACCESS name, bridge_end ;; ES:BX=buffer, CX= pkt_len ;; Free: AX, DX, SI, DI, BP ;;------- check if packet is for the bridge --------------- mov current_rx_segment,ES mov AX, ES:[BX+4] cmp AX, if_address[8*name+4] jnz not_me mov AX, ES:[BX+2] cmp AX, if_address[8*name+2] jnz not_me mov AX, ES:[BX] cmp AX, if_address[8*name] jz got_packet ;; it is for me not_me: ;;------- an ordinary packet - must be bridged ------------ IFDEF use_filters ;; ------ filtering code ------------ ;; Remember, CX and ES:BX should be preserved mov ax, es:[BX+12] ;; packet type xchg ah,al ;; back to Intel (little endian) format mov bp,ax lea si, filters next_filter: lodsw and ax, ax jz end_filters; mov dx, ax ;; save type. DL= outside, DH=inside lodsw ;; DI=low boundary mov di, ax lodsw cmp bp, di jb outside cmp bp,ax jae outside xchg dh,dl ;; here is inside outside: cmp dl,1 ; KEEP je end_filters ;; keep packet cmp dl,2 ; DROP jne next_filter COUNT_CYCLE %(name + LOCAL_DROP_BASE_CTR) jmp drop end_filters: ENDIF ;;----------- filtering code -------------------- ;; Used: ES:BX = buffer address, CX= pkt_len ;; Free: AX, DX, SI, DI, BP ;; WARNING: DS is saved in BP, ES (was pkt-seg) goes to DS, BX saved in SI ;;---------------- find table entry -------------------- mov BP, DS ;; save DS mov SI, BX ;; save BX (buf pointer) mov AX, ES mov ES, bdg_table_ptr ;; ES = table mov DS, AX ;; now DS:SI = pkt_start ;; Used: DS:SI(=BX) = rx pkt address, CX= pkt_len, BP=data seg. ;; ES=bridge table ;; Free: AX, DX, DI add SI, 6 ;; SI= Source_Address mov DI, [SI+4] ;; compute hash address xor DI, [SI+2] shl DI, 3 ;; now ES:DI is the hash address ;;----------- check table collisions on source addresses ------------- push si ;; DS:SI=pkt(src addr), ES:DI=table push di cmp byte ptr es:[di+6],0ffh;; interface # jz no_collision ;; empty -> no collision, no loop cmpsw ;; ES:DI=table, DS:SI=pkt jnz collision ;; different -> collision, no loop cmpsw jnz collision ;; different -> collision, no loop cmpsw IFDEF no_loops ;;-------------- loop avoidance ------------------- jnz collision ;; different -> collision, no loop cmp byte ptr es:[di],name ;; found address, check source port je no_collision ;; same port, no loops ;; found a loop. Temporarily disable this interface, flush entry. mov byte ptr es:[di], 0ffh ;; flush entry mov ds, bp ;; restore DS=data seg or byte ptr if_address[8*name+7],IF_FLAG_DISABLE pop di pop si jmp drop ELSE jz no_collision ENDIF ;;------------- loop avoidance ----------------------------- collision: ;; collision in hash table, update counters mov ax, ds ;; save DS mov ds, bp ;; restore DS=data seg COUNT_CYCLE %COLLSN_CTR ;; counter for table collisions mov ds,ax ;; restore DS:SI Source_Address no_collision: pop di pop si ;;------------- drop packet if IF is disabled. ---------------------- ;; Here DS:SI=pkt+6, ES:DI=table entry, BP=data segment, BX=pkt offset,CX=len mov ax, ds ;; save pkt segment in AX mov ds,bp ;; restore DS=data segment test byte ptr if_address[8*name+7],IF_FLAG_DISABLE ;; status jnz drop ;; drop if disabled. Should count... mov ds,ax ;; restore packet segment ;;------------- now write the source address into the table. -------- movsw movsw movsw mov byte ptr es:[di], name ;; save source IF sub SI, 12 ;; DS:SI back to pkt beginning ;; Used: DS:SI(=BX) = rx pkt address, CX= pkt_len, BP=data seg. ;; ES=bridge table ;; Free: AX, DX, DI ;;-------------- check destination ----------------------------------- test byte ptr [SI], 1 ;; dst is a multicast address, forward all jnz broadcast ;; really the same as forward_all mov DI, [SI+4] ;; hash destination xor DI, [SI+2] shl DI, 3 cmp byte ptr es:[di+6],0ffh ;; empty entry ? jz forward_all ;; yes, must forward all cmpsw ;; search into table jnz forward_all ;; not in table cmpsw jnz forward_all ;; not in table cmpsw jnz forward_all ;; not in table ;; found destination mov DS, BP ;; restore DS to data segment mov AL, ES:[DI] ;; AL = dst card cmp AL, name ;; check for local destination jnz forward ;; forward if not COUNT_CYCLE %(name + LOCAL_DROP_BASE_CTR) ;; drop because local destination jmp drop forward: ;;----- forward to a specific interface (in AL) ---------------- mov BP, CX ;; BP,CX = pkt size, AL= destination IF ;; Reg. Used: DS= dataseg, BX = rx pkt address ;; Free: DX, SI, DI, ES ;;--------- old code - can be shortened ------------------ ;; The problem here is that macros expect a constant (idx) as argument, ;; while we'd like to use AL instead. ;; "name" is the source port, "idx" is the destination port FOR idx, <1,2,3,4,5> LOCAL next,drop1 IF (idx le num_dls) AND (idx ne name) cmp AL, idx ;; skip if not for this IF jnz next ;; now have found the if, no need to check if_found test byte ptr if_address[8*idx+7], IF_FLAG_DISABLE jnz drop1 ;; disabled ports don't work ;; BX=source pkt offset, BP=source pkt len ;; AX, DX, SI available IF_W_ACCESS idx, drop1 ;; get output buffer in ES:DI mov SI, BX ;; probably can do it outside COUNT_CYCLE %(idx + TXPKT_BASE_CTR) ;; update tx. ctr IF_COPY name ;; seg:SI -> ES:DI for CX bytes ;; CX is lost, as are SI and DI mov CX, BP ;; restore CX ;; AX, DX, SI, DI available IF_W_WRITE idx ;; forward data. Output buffer for card ;; is at fixed position, so pointers are not needed jmp drop drop1: ;; packet dropped -- IF not ready or disabled COUNT_CYCLE %(idx + DROP_BASE_CTR) jmp drop next: ENDIF ;; "if (idx le num_dls)..." ENDM ;; should only reach here if something goes wrong! ;; should really mark it, in counter %name COUNT_CYCLE %(name + BAD_RX_BASE) ;; test discard packet jmp drop ;;------------- end of forwarding code for specific IF ------------- ;;------------- forwarding code for all interfaces ----------------- broadcast: forward_all: mov DS, BP ;; restore DS (on entry here, it is in BP) mov BP, CX ;; save CX for later use COUNT_CYCLE %(name + RXALL_BASE_CTR) FOR idx, <1,2,3,4,5> LOCAL drop2, next IF (idx le num_dls) AND (idx ne name) ;; skip local address cmp if_found[idx], 0 jz next test byte ptr if_address[8*idx+7], IF_FLAG_DISABLE jnz drop2 ;; disabled ports don't work IF_W_ACCESS idx, drop2; ES:DI=packet ptr mov SI, BX COUNT_CYCLE %(idx + TXPKT_BASE_CTR) IF_COPY name ;; CX is lost, as are SI and DI mov CX, BP ;; AX, DX, SI, DI available IF_W_WRITE idx jmp next drop2: ;; skip this if, goto next COUNT_CYCLE %(idx + DROP_BASE_CTR) next: ENDIF ;; "if (idx le num_dls)..." ENDM ;;--------- end forwarding code for all interfaces ----------------- ;;---------- deal with possible broadcasts for the bridge --------- mov ES, current_rx_segment ;; Set ES to the buffer segment, which test byte ptr es:[bx],1 ;; BX points to the input PKT jz drop mov CX, BP ;; restore length. got_packet: ;; send the pkt up to the higher level protocols COUNT_CYCLE %(name + RXPKT_BRIDGE) ;; count # packets to bridge ;; CX=length. ES:BX=packet. BP=name. Either for me, or multicast. mov BP, name call process_pkt drop: IF_R_FREE name ;; free memory, no par. bridge_end: ENDM ;;****************************************************************** ;;------------- end of file ------------------- PR%EAAAA%AEZXAPR%EAif.inc1006440004230000000000001414205367021773007116 037777777777 1 0 ;;******************************************************************** ;; if.inc ;;******************************************************************** ;; ;; Copyright (C) 1989 Northwestern University, Vance Morrison ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Northwestern University makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;**************************************************************************** ;; ;; if.inc is a module that describes in INTERFACE (IF). An interface ;; basicly is a description of a network card that is reasonably card ;; independant ;; ;; if.inc is the dispatcher for the if object. Basically all this module does ;; is look up the name in its database, and based on its name, call the ;; correct routine for that card type (WD .. ) ;; ;;**************************************************************************** ;; ;; The functions provided by this file are ;; ;; IF_DECLARE name, real_if, type ;; IF_DECLARE makes the association between the IF object 'name' and ;; the object 'real_if' which actually supplies the routines. 'type' ;; is the prefix for the real if (eg. WD) ;; ;; IF_R_ACCESS interface, no_packet ;; IF_R_ACCESS waits for the next packet to come from the the board ;; associated with 'interface' and returns a pointer to the begining of ;; an ethernet packet in BX:ES. If there is no packet to read in, ;; this function jumps to 'no_packet' ;; ;; IF_R_FREE interface ;; After the client is through processing the packet returned by ;; IF_R_ACCESS, IF_R_FREE must be called to inform 'interface' that the ;; memory that the packet was in can be reused for future packets. ;; ;; IF_R_CONT name, ok ;; IF_R_CONT determines if the packet returned by R_READ in BX:ES ;; of length CX is continuous. If it is it jumps to 'ok' otherwise ;; it just returns ;; ;; IF_W_ACCESS interface, no_buffer ;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The ;; pointer is returned in DI:ES. If the ouptut buffer is busy, this ;; routine jump to 'no_buffer'. the output buffer is guarenteed to ;; be at least min (CX, 1514) (the MTU for Ethernet - 4 byte checksum) ;; bytes long. ;; ;; IF_W_WRITE interface ;; IF_W_WRITE actually signals the ethernet board to write a packet to ;; the ethernet. The packet is assumed to be in the buffer returned by ;; IF_W_ACCESS. CX is the length of the packet to send. ;; ;; IF_SET_ADDRESS interface ;; IF_SET_ADDRESS sets the hardware address to be the value ;; pointed to by SI. Note this function may be a no-op if the ;; hardware address cannot be set (ETHERNET for example) ;; ;; IF_COPY interface ;; IF_COPY copys a packet from the input buffer (pointed ;; to by SI and the segement register given in IF_DECLARE) to an output ;; buffer (pointed to by DI and dest_reg) of length CX. It assumes the ;; output buffer is contiguous. (and the caller shouldn't care if the ;; input buffer is contiguous) COPY updates the pointers SI and DI ;; to the end of the packet, and COPY could be called again if CX is not ;; the total packet length (Note that CX MUST be even if you care about ;; SI, and DI being updated properly) ;; ;; The variable provided by this module are (READ ONLY!!!) ;; ;; if_&name&_mtu ;; the maximum transmition unit ;; ;;**************************************************************************** ;;**************************************************************************** ;; IF_DECLARE name, real_if, type ;; IF_DECLARE MACRO name, real_if, type c_if_&name&_name = real_if c_if_&name&_type equ if name ne real_if ;; c_if_&name&_address = if_&real_if&_address c_if_&name&_mtu = if_&real_if&_mtu endif ENDM ;;****************************************************************************** ;; IF_R_ACCESS name, no_packet ;; IF_R_ACCESS MACRO name, no_packet HELPER macro myname, type, myno_packet type&_IF_R_ACCESS myname, myno_packet ENDM HELPER %c_if_&name&_name, %c_if_&name&_type, no_packet ENDM ;;****************************************************************************** ;; IF_R_FREE name ;; IF_R_FREE MACRO name HELPER macro myname, type type&_IF_R_FREE myname ENDM HELPER %c_if_&name&_name, %c_if_&name&_type ENDM ;;****************************************************************************** ;; IF_R_CONT name, ok ;; IF_R_CONT MACRO name, ok HELPER macro myname, type, myok type&_IF_R_CONT myname, myok ENDM HELPER %c_if_&name&_name, %c_if_&name&_type, ok ENDM ;;****************************************************************************** ;; IF_W_ACCESS name, no_buffer ;; IF_W_ACCESS MACRO name, no_buffer HELPER macro myname, type, myno_buffer type&_IF_W_ACCESS myname, myno_buffer ENDM HELPER %c_if_&name&_name, %c_if_&name&_type, no_buffer ENDM ;;****************************************************************************** ;; IF_W_WRITE interface ;; IF_W_WRITE MACRO name HELPER macro myname, type type&_IF_W_WRITE myname ENDM HELPER %c_if_&name&_name, %c_if_&name&_type ENDM ;;****************************************************************************** ;; IF_SET_ADDRESS interface ;; IF_SET_ADDRESS MACRO name HELPER macro myname, type type&_IF_SET_ADDRESS myname ENDM HELPER %c_if_&name&_name, %c_if_&name&_type ENDM ;;****************************************************************************** ;; IF_COPY interface ;; IF_COPY MACRO name HELPER macro myname, type type&_IF_COPY myname ENDM HELPER %c_if_&name&_name, %c_if_&name&_type ENDM ;; --------------- end of file ------------------------- jnz forward ;; forward if not COUNT_CYCLE %(name + LOCAL_DROP_BASE_CTR) ;; drop because local destination jmp drop forward: ;;----- forward to a specific interface (in AL) ---------------- mov BP, CX ;; BP,CX = pkt size, AL= destination IF ;; Reg. Used: DS= dataseg, BX = rx pkt address ;; Free: DX, SI, DI, ES ;;--------- old code - can be shortened ------------------ ;; Thmemory.inc1006440004230000000000001455105367022003010017 037777777777 1 0 ;;************************************************************ ;; memory.inc (memory and port utilities) ;;************************************************************ ;; ;; Copyright (C) 1990 Vance Morrison ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;****************************************************************************** ;; ;; In order to do fast 16 bit transfers between cards with shared memory ;; we need to tell each card that it can use all 16 bits. Unforunately, ;; we can't leave this on all the time (because this bit is shared among ;; hardward that may want it unasserted). Thus before each transfer, the ;; source and destination card need to be places in 16 bit mode. ;; ;; Because in PCroute determining the destination card is difficult, and ;; since it is unlikely that there will be configurations with a mix ;; of 8 and 16 bit shared memory cards, we take a simplified view. ;; Namely if any 8 bit shared memory boards exist, everyone does 8 bit ;; transfers all the time. If all boards are 16 bit, then before any copy, ;; all cards are placed in 16 bit mode. ;; of networking cards is not transparent (at least not with the WD8013EBT). ;; because of this, we need to toggle 16bit mode when it is necessary ;; (it is also necessary to do this with interupts disabled, which also ;; requires special processing). memory.inc provides functions that allow ;; this toggling to be done automatically. ;; ;;****************************************************************************** ;; ;; functions provided by this module are ;; ;; MEM_DECLARE_16BIT on_macro, off_macro ;; MEM_DECLARE_8BIT ;; MEM_DECLARE_NEED_INTS lantency ;; MEM_COPY ;; PORT_READ ;; PORT_WRITE ;; ;;****************************************************************************** mem_num_16bit = 0 mem_num_8bit = 0 mem_min_latency = -1 BIT8 = 0 BIT16 = 1 ;;****************************************************************************** ;; MEM_DECLARE_16BIT declares the fact that a 16 bit shared memory card ;; exists in the system. 'on_macro' turns 16 bit mode on for that card` ;; 'off_macro' turns it off. Interfaces that use main memory for buffering ;; need not be declared. Note that on and off macros CAN ONLY MODIFY ;; THE AX AND DX REGISTERS. 'id' is a number passed to the on and off ;; macros when they are called. MEM_DECLARE_16BIT MACRO on_macro, off_macro, id mem_num_16bit = mem_num_16bit + 1 HELPER MACRO num mem_16bit_&num&_on equ mem_16bit_&num&_off equ mem_16bit_&num&_id = id ENDM HELPER %mem_num_16bit ENDM ;;****************************************************************************** ;; MEM_DECLARE_NEED_INTS declares the fact that there is an interface ;; that uses interupts that need to be serviced quickly, so interupts ;; cannot remain disabled for long times. 'lantency' is the worst case ;; latency required measured in usec. For example a SLIP line at 19.2K ;; baud requires that interupts be serviced in 500 us. ;; MEM_DECLARE_NEED_INTS MACRO latency IF mem_min_latency gt latency ;; take the minimum mem_min_latency = latency ENDIF ENDM ;;****************************************************************************** ;; MEM_DECLARE_8BIT declares the fact that a 8 bit shared memory card ;; exists in the system. In this implementation, this means don't ever ;; use 16 bit mode at all. MEM_DECLARE_8BIT MACRO mem_num_8bit = 1 ENDM ;;****************************************************************************** ;; MEM_COPY does the copy operation. It requires ;; that SI and DI are EVEN. This function is esentially a 'rep movsw' ;; with added functionality that enables 16bit bus transfers if possible. ;; MEM_COPY MACRO inc CX shr CX, 1 IF (mem_num_8bit eq 0) and (mem_num_16bit gt 0) ;; use 16bit push AX push DX IF mem_min_latency lt 0 cli MEM_ALL_16BIT_ON rep movsw MEM_ALL_16BIT_OFF sti ELSE ;; really should copy in pieces, but I don't have ;; the time to code it. for now we just give up ;; on 16 bit access rep movsw ENDIF pop DX pop AX ELSE rep movsw ENDIF ENDM ;;**************************************************************************** ;; These macros call the stored on_macro and off_macro respectively MEM_ALL_16BIT_ON MACRO FOR idx,<1,2,3,4,5> IF idx le mem_num_16bit MEM_16BIT_ON idx ENDIF ENDM ENDM ;;**************************************************************************** MEM_ALL_16BIT_OFF MACRO FOR idx,<1,2,3,4,5> IF idx le mem_num_16bit MEM_16BIT_OFF idx ENDIF ENDM ENDM ;;**************************************************************************** MEM_16BIT_ON MACRO num HELPER MACRO name, myid name myid ENDM HELPER %mem_16bit_&num&_on, %mem_16bit_&num&_id ENDM ;;**************************************************************************** MEM_16BIT_OFF MACRO num HELPER MACRO name, myid name myid ENDM HELPER %mem_16bit_&num&_off, %mem_16bit_&num&_id ENDM ;;********************************************************************* ;; utility functions for port I/O ;; IN: -- ;; OUT: AL ;; CHNG: AL, DX PORT_READ MACRO port, if_io ;; read port, changes AL, DX mov DX, if_io+port in AL, DX ;; AL contains data read from port ENDM ;;********************************************************************* ;;********************************************************************* ;; IN: AL ;; OUT: -- ;; CHNG: DX PORT_WRITE MACRO port, if_io, data ;; write port, changes DX IFNB mov al,data ENDIF mov DX, if_io+port out DX, AL ;; AL contains data read from port ENDM ;;********************************************************************* ;; -------------------- end of file ------------------------ us) COPY updates the pointers SI and DI ;; to the end of the packet, and COPY could be called again if CX is not ;; the total packet length declare.inc1006440004230000000000000662405367021756010126 037777777777 1 0 ;;******************************************************************* ;; declare.inc ;;******************************************************************* COMMENT ^ Copyright (C) 1989 Northwestern University, Vance Morrison Permission to view, compile, modify for LOCAL (intra-organization) USE ONLY is hereby granted, provided that this copyright and permission notice appear on all copies. Any other use by permission only. Northwestern University makes no representations about the suitability of this software for any purpose. It is provided "as is" without expressed or implied warranty. See the copywrite notice file for complete details. ^ ;;******************************************************************** ;; ;; declare.inc is a 'header' file that constains the declarations ;; of all compile-time information that is shared among seperately ;; compiled pieces. ;; ;; AUTHOR: Vance Morrison ;; DATE: 4/28/89 ;;******************************************************************* ;; 30mar93 Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it) ;; up to 5 WD cards, with statistics ;;******************************************************************* ;;**************************************************************** ;; ;; start declaration section. Comment in your coices ;; the first WD8013EBT card WD_CARD 280H, 0D000H, 16, BIT16 ;; 16K, 16bit ;; the second WD8013EBT card WD_CARD 2A0H, 0D400H, 16, BIT16 ;; 16K, 16bit ;; the third WD8013EBT card WD_CARD 300H, 0C800H, 16, BIT16 ;; 16K, 16bit ;; the fourth WD8013EBT card WD_CARD 320H, 0CC00H, 16, BIT16 ;; 16K, 16bit ;; the fifth WD8013EBT card WD_CARD 340H, 0E000H, 16, BIT16 ;; 16K, 16bit ;; the first WD8003E Ethernet card or WD8003S(H) Starlan card ; WD_CARD 280h, 0D000H, 8, BIT8 ;; 8K, 8bit ;; a second WD8003E Ethernet card or WD8003S(H) Starlan card ; WD_CARD 2A0h, 0D400H, 8, BIT8 ;; 8K, 8bit ;; a serial card for a remote bridge. Must find a simpler macro ; I8250_DECLARE %cur_if, SLIP, %cur_if, IBM_COM1_PORT, IBM_COM1_IRQ, 0 ; tmp = cur_if*10 ;; generate temp names ; BUFF_DECLARE %(tmp+1), 3200 ;; the read buffer ; QUEUE_DECLARE %(tmp+2), 5, %(size qif_entry) ;; the read queue ; BUFF_DECLARE %(tmp+3), 10240 ;; the write buffer ; QUEUE_DECLARE %(tmp+4), 16, %(size qif_entry) ;; the write queue ; SLIP_DECLARE %cur_if,%cur_if,%(tmp+1),%(tmp+2),%(tmp+3),%(tmp+4) ; MY_SET_MTU %cur_if, 1518 ;; overide default MTU ; Q_IF_DECLARE %cur_if,%(tmp+1),%(tmp+2),%(tmp+3),%(tmp+4),SLIP,%cur_if ; IF_DECLARE %cur_if, %cur_if, Q ; cur_if = cur_if + 1 ;; a packet driver entry ; PKT_CARD 60H, 1 ;; ;; a second packet driver entry ; PKT_CARD 61H, 1 ;; ;; a 3C507 card entry 64K of memory at D0000H ; C507_DECLARE %cur_if, 280H, 0D000H, 10000H, 1 ;; name,io,seg,len,prom ; IF_DECLARE %cur_if, %cur_if, C507 ; cur_if = cur_if + 1 ;; a 3C507 card entry 64K of memory at C0000H ; C507_DECLARE %cur_if, 2A0H, 0C000H, 10000H, 1 ;; name,io,seg,len,prom ; IF_DECLARE %cur_if, %cur_if, C507 ; cur_if = cur_if + 1 bdg_1_forw_ip = 1 ; 0=no forward, 1=forward ;; end declaration section ;;------------- end of file ----------------- other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of thiswd.inc1006440004230000000000002647105367022047007135 037777777777 1 0 SUBTTL wd.inc COMMENT^ Provides constants and data definitions for WD80x3.inc 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: ------- XXxxx89 Original by Vance Morrison 01apr93 Changes by Luigi Rizzo ^ ;******************************************************************** ; ; WD80x3 controller board offsets ; IO port definition (BASE in WD8_base) ;******************************************************************** W83CREG EQU 00h ; 8003 control register W83CREG EQU 00h ; 8003 status register LAAR EQU 05h ; LA Address Register (write only) 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 RCVR 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, read and write PAR0 EQU 11h ; physical addr reg 0 PAR1 EQU 12h ; physical addr reg 1 PAR2 EQU 13h ; physical addr reg 2 PAR3 EQU 14h ; physical addr reg 3 PAR4 EQU 15h ; physical addr reg 4 PAR5 EQU 16h ; physical addr reg 5 CURR EQU 17h ; current page reg MAR0 EQU 18h ; multicast addr reg 0 MAR1 EQU 19h ; multicast addr reg 1 MAR2 EQU 1Ah ; multicast addr reg 2 MAR3 EQU 1Bh ; multicast addr reg 3 MAR4 EQU 1Ch ; multicast addr reg 4 MAR5 EQU 1Dh ; multicast addr reg 5 MAR6 EQU 1Eh ; multicast addr reg 6 MAR7 EQU 1Fh ; multicast addr reg 7 ;*********************************************************************** ; ; 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 ;*********************************************************************** ; ; LAAR register Masks LAN16ENB EQU 40h ; Enables 16bit shrd RAM for LAN MEM16ENB EQU 80h ; Enables 16bit shrd RAM for host LA19 EQU 01h ; Address line 19 for enabling ;*********************************************************************** ; ; 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 RCVR 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_LBm1 EQU 02h ; mode 1; internal loopback LPBK=0 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 shared memory 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 ;; --------------------- end of file ---------------------- 4h ; 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 wd80x3.inc1006440004230000000000004377505371700025007560 037777777777 1 0 ;;---------------- File: wd8003e.inc ------------------------ COMMENT^ Copyright (C) 1989 Northwestern University, Vance Morrison Permission to view, compile, and modify for LOCAL (intra-organization) USE ONLY is hereby granted, provided that this copyright and permission notice appear on all copies. Any other use by permission only. Northwestern University makes no representations about the suitability of this software for any purpose. It is provided "as is" without expressed or implied warranty. See the copywrite notice file for complete details. 17apr93 Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it) wd8003 holds the interface routines for the western digital ethernet card WD8003E or the starlan card WD8003S. Althougth this routine will work for any of the above cards, it has been optimized for the Ethernet card. The functions provided by this file are WD_CARD MACRO ioaddr, memseg, memsize_k, bit16 WDE_DEFINE name WDE_IF_R_ACCESS name, no_packet WDE_IF_R_CONT name, ok WDE_IF_R_FREE name WDE_IF_W_ACCESS name, no_buffer WDE_IF_W_WRITE name WDE_IF_SET_ADDRESS name WDE_IF_COPY name Variables set by this module c_wde_&name&_declared ;; one if this interface exists c_if_&name&_mtu ;; the maximum trans unit --------------------------------------------------------------- ^ include wd.inc ;;---------------------------------------------------------------- ;; data storage needed by this module wde_data STRUC wde_new_bndry DB 0 wde_data ENDS ;;****************************************************************************** ;;------------------------------------------------------------------- ;; WD_CARD ioaddr, memseg, memsize_k, 16bit ;; Called to define a new card. It calls two macros: ;; WDE_DECLARE which declares an interface object ;; IF_DECLARE which sets IF name and type for future use WD_CARD MACRO ioaddr, memseg, memsize_k, bit16 WDE_DECLARE %cur_if, ioaddr, memseg, 0H, 1, memsize_k*4, %bit16 ;; ;; mem_off=0, promiscuous , 256byte-pages, 16bit IF_DECLARE %cur_if, %cur_if, WDE cur_if = cur_if + 1 ENDM ;;-------------------------------------------------------------- ;;------------------------------------------------------------------- ;; IF_DECLARE name, io_address, shr_seg, shr_off ;; declares an interface object. 'io_address' is the address of the ;; start of the 8003E control registers. 'shr_seg' and ;; 'shr_off' is the address of the WD8003 card buffer ;; This address must be a multiple of 512. If 'promiscuous' is not ;; zero (or blank),the interface is configured so that every packet on ;; the ethernet is received. 'total_pg' if not blank, is the total ;; amount of pages of shared memory. (default = 32 pages = 8K) ;; if 'bits16' is non-blank and equal to 1, then the card is assumed ;; to be a WD8013EBT card. It it is 2, then it is a WD8013 card but ;; we have disabled 16bit transfers (some hardware doesn't like it) ;; ;; No code, only some definitions WDE_DECLARE MACRO name, io_addr, shr_seg, shr_off, promiscuous, total_pg, bits16 .ERRB .ERRB .ERRB .ERRB .DATA c_wde_&name&_declared = 1 c_wde_&name&_io = io_addr ;; set compile time values c_wde_&name&_shared_off = shr_off c_wde_&name&_shared_seg = shr_seg IF shr_seg lt 8000h .err Shared memory MUST be above 80000H ENDIF c_wde_&name&_stop_pg = STOP_PG IFNB c_wde_&name&_stop_pg = 0&total_pg ENDIF c_wde_&name&_promiscuous = 0 IFNB c_wde_&name&_promiscuous = 0&promiscuous ENDIF c_wde_&name&_bits16 = 0 IFNB c_wde_&name&_bits16 = 0&bits16 ENDIF IF c_wde_&name&_bits16 eq 1 ;; note these can only touch registers AX and DX WDE_16BIT_ON MACRO myname PORT_WRITE LAAR, c_wde_&myname&_io,LAN16ENB or LA19 or MEM16ENB ENDM WDE_16BIT_OFF MACRO myname PORT_WRITE LAAR, c_wde_&myname&_io,LAN16ENB or LA19 ENDM MEM_DECLARE_16BIT WDE_16BIT_ON, WDE_16BIT_OFF, name ELSE MEM_DECLARE_8BIT ENDIF c_if_&name&_mtu = 1514 externdef wde_&name&_data:wde_data externdef if_address:word .CODE externdef wde_&name&_real_define:near ENDM ;; WDE_DECLARE ;;****************************************************************************** ;;****************************************************************************** ;; WDE_DEFINE name ;; sets asside memory an name object and initializes it. This ;; routine is a no-op if 'name' was not declared ;; Registers: see wde_real_define ;; IN: -- ;; OUT: -- ;; CHANGE: AX, BX, CX, DX, SI ;; WDE_DEFINE MACRO name IFDEF c_wde_&name&_declared call wde_&name&_real_define ENDIF ENDM ;;****************************************************************************** ;; IF_REAL_DEFINE name ;; Registers: ;; IN: -- ;; OUT: -- ;; CHANGE: AX, BX, CX, DX, SI ;; ;; Should try to use BP as base to the data area for the card ;; so that can change this to be a real subroutine. ;; WDE_REAL_DEFINE MACRO name LOCAL loop1, loop2, loop3, around, resetwait LOCAL card_found .errb IFDEF c_wde_&name&_declared .DATA wde_&name&_data wde_data <> ;; create storage needed ;; Registers: NONE .CODE wde_&name&_real_define: ;; this is a global label ; look for card mov cx, 6 ;; get the ethernet address mov bx, OFFSET if_address+ 8*name mov si, OFFSET ct_&name&_ha+5 ;; for printing ethernet address mov dx, c_wde_&name&_io+ADDROM ;; point to the Ethernet address ROM mov ah,0 ;; for checksum loop1: ;; read hardware address in AL, DX add ah,al mov [BX], AL mov [si], AL ;; store in print order dec si inc DX inc BX loop loop1 in AL, DX ;; read two more bytes inc DX add ah,al in AL, DX add ah,al cmp ah, 0ffh ;; checksum jz card_found ret ;; card not found card_found: mov if_found[name],1 PORT_WRITE W83CREG, c_wde_&name&_io, MSK_RESET;; reset card PORT_WRITE W83CREG, c_wde_&name&_io, 0 ;; deassert ;; this sets bit 6 (0 justified) of register offset 0x05, it will enable ;; the lan controller to access shared RAM 16 bits at a time ;; In addition, this routine maintains address bit 19 ;; (previous cards assumed this bit high...we must do it manually) ;; note: this is a write only register and only exists on the WD8013 IF c_wde_&name&_bits16 eq 1 PORT_WRITE LAAR, c_wde_&name&_io,LAN16ENB+LA19 ;; set bit19 of address & 16 bit mode for card ENDIF ;; register 0 is the MSR (Memory base reg) -- also W83CREG ;; this will enable the on board ram PORT_WRITE W83CREG,c_wde_&name&_io,(c_wde_&name&_shared_seg+(c_wde_&name&_shared_off/16))/512 PORT_WRITE CMDR, c_wde_&name&_io, MSK_STP+MSK_PG0+MSK_RD2 ;; RESET,goto pg0 xor AL, AL ;; clear RBCR0,1 PORT_WRITE RBCR0, c_wde_&name&_io PORT_WRITE RBCR1, c_wde_&name&_io resetwait: ;; make sure reset is complete PORT_READ ISR, c_wde_&name&_io test AL, MSK_RST jz resetwait mov AL, MSK_BMS + MSK_FT10 ;; select FIFO threshold = 8 bytes IF c_wde_&name&_bits16 EQ 1 or AL, MSK_WTS ;; FOR 16 BIT OPERATION ENDIF PORT_WRITE DCR, c_wde_&name&_io xor AL, AL ;; turn off receiving PORT_WRITE RCVR, c_wde_&name&_io PORT_WRITE TCR, c_wde_&name&_io, MSK_LBm1 ;; enter loopback mode 1 PORT_WRITE PSTART,c_wde_&name&_io,STRT_PG ;; start page of input buffer PORT_WRITE PSTOP,c_wde_&name&_io,c_wde_&name&_stop_pg;; end pg of in buffer PORT_WRITE BNRY, c_wde_&name&_io, STRT_PG PORT_WRITE ISR, c_wde_&name&_io , -1 ;; clear all status bits PORT_WRITE IMR, c_wde_&name&_io,0 ;; no interupts PORT_WRITE CMDR,c_wde_&name&_io,MSK_STP+MSK_PG1+MSK_RD2 ;; page 1 mov CX, 6 ;; set the ethernet address mov BX, OFFSET if_address + 8*name mov DX, c_wde_&name&_io+PAR0 loop2: mov AL, [BX] ;; get 1 byte into AL out DX, AL ;; write to PAR inc BX inc DX loop loop2 IF c_wde_&name&_promiscuous NE 0 mov AL, 0FFH ELSE xor AL, AL ENDIF mov CX, 8 ;; set the multicast address to all 1/0's mov DX, c_wde_&name&_io+MAR0 loop3: out DX, AL inc DX loop loop3 PORT_WRITE CURR, c_wde_&name&_io, STRT_PG+1 ;; Set input pointer for queue PORT_WRITE CMDR, c_wde_&name&_io, MSK_STA + MSK_PG0 + MSK_RD2 ;; make sure we are on page 0 and start the NIC 8390 xor AL, AL ;; loopback off PORT_WRITE TCR, c_wde_&name&_io ;; set receiver mode IF c_wde_&name&_promiscuous ne 0 mov AL, MSK_AB+MSK_AM+MSK_PRO ;; promiscuous ELSE mov AL, MSK_AB ;; just broadcasts + to me ENDIF PORT_WRITE RCVR, c_wde_&name&_io RET endif ENDM ;;****************************************************************************** ;;****************************************************************************** ;; IF_R_ACCESS name, no_packet ;; IF_R_ACCESS waits for the next packet to come from the the board ;; associated with 'name' and returns a pointer to the begining of ;; an ethernet packet in BX:ES. CX holds the length of the packet ;; R_ACCESS jumps to 'no_packet' if there are no packets waiting to ;; be read in ;; Registers: ;; IN: -- ;; OUT: ES:BX=ptr to pkt, CX=len ;; CHANGE: AX, BX, CX, DX, ES ;; fast... WDE_IF_R_ACCESS MACRO name, no_packet LOCAL inside LOCAL good_packet LOCAL wrapped LOCAL new_wrapped LOCAL bad_packet LOCAL ok_status LOCAL good_length LOCAL truncate .errb PORT_WRITE CMDR,c_wde_&name&_io,MSK_PG0+MSK_RD2;; read BNRY reg. into AL PORT_READ BNRY, c_wde_&name&_io ;;REG: AL=BNRY inc AL ;; increment with wrap around cmp AL, c_wde_&name&_stop_pg jb inside ;; no need to wrap mov AL, STRT_PG inside: mov BH, AL ;; save it in BH PORT_WRITE CMDR, c_wde_&name&_io, MSK_PG1+MSK_RD2 ;; read CURR reg. into AL PORT_READ CURR, c_wde_&name&_io ;;REG: AL=CURR, BH=succ(BNRY) cmp AL, BH je no_packet COUNT_CYCLE %(RX_BASE_CTR+name) ;; count # pkt rx from name card mov byte ptr if_address[8*name+6],1 ; mark active... xor BL, BL ;; BX now holds pointer to packet mov DX, c_wde_&name&_shared_seg ;; ES = segment address mov ES, DX mov AH, ES:[BX+c_wde_&name&_shared_off] ;; get the status cmp AH, SMK_PRX ;; is it good jz ok_status cmp AH, SMK_PRX+SMK_PHY jnz bad_packet ok_status: mov AH, ES:[BX+1+c_wde_&name&_shared_off] ;; pointer to the next packet ;; sanity check on next packet pointer AH cmp BH, AL ;; is BNDRY+1 <= CURR? ja wrapped cmp AH, BH jb bad_packet cmp AH, AL jbe good_packet jmp bad_packet wrapped: cmp AH, BH jb new_wrapped cmp AH, c_wde_&name&_stop_pg jnb bad_packet jmp good_packet new_wrapped: cmp AH, STRT_PG jb bad_packet cmp AH, AL jbe good_packet bad_packet: COUNT_CYCLE %(BAD_RX_BASE+name) ;; set BNDRY = BNDRY+1 and try again PORT_WRITE CMDR, c_wde_&name&_io, MSK_PG0 + MSK_RD2 ;; page 0 PORT_WRITE BNRY, c_wde_&name&_io, BH ;; write the new BNRY register jmp no_packet ;; return bad status good_packet: mov wde_&name&_data.wde_new_bndry, AH ;; save it for R_FREE mov CX, ES:[BX+c_wde_&name&_shared_off+2] ;; load the length add BX, c_wde_&name&_shared_off+4 ;; BX point to begining of the packet sub CX, 4 cmp CX, 1536 ;; sanity check jle good_length cmp CH, CL jne truncate xor CH, CH ;; fix western digital bug jmp good_length truncate: mov CX, 1536 good_length: COUNT_CYCLE %(RX_BYTE_BASE+name), CX ;; XXX ENDM ;;****************************************************************************** ;;****************************************************************************** ;; IF_R_FREE name ;; After the client is through processing the packet returned by ;; IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the ;; memory that the packet was in can be reused for future packets. ;; ;; Registers: ;; IN: -- ;; OUT: -- ;; CHNG: AL, DX ;; Fast WDE_IF_R_FREE MACRO name LOCAL inside .errb PORT_WRITE CMDR, c_wde_&name&_io, MSK_PG0 + MSK_RD2 ;; page 0 mov AL, wde_&name&_data.wde_new_bndry ;; Retrieve NEW_BOUNDRY dec AL cmp AL, STRT_PG jge inside mov AL, c_wde_&name&_stop_pg-1 inside: ;; write the new BNRY register PORT_WRITE BNRY, c_wde_&name&_io ENDM ;;****************************************************************************** ;;****************************************************************************** ;; WDE_IF_R_CONT name, ok ;; IF_R_CONT determines if the packet returned by R_READ in BX:ES ;; of length CX is contiguous (ES is unused). If it is it jumps ;; to 'ok' otherwise ;; it just returns ;; ;; Registers: ;; IN: BX=pkt offset, CX=pkt len ;; OUT: cy set if contiguous ;; CHNG: AX ;; Fast WDE_IF_R_CONT MACRO name, ok .errb mov AX, BX add AX, CX cmp AX, OFFSET c_wde_&name&_shared_off+c_wde_&name&_stop_pg*256 jb ok ENDM ;;****************************************************************************** ;;****************************************************************************** ;; IF_W_ACCESS name, no_buffer ;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The ;; pointer is returned in DI:ES. If the ouptut buffer is busy, this ;; routine will jump to 'no_buffer'. The output buffer min(CX, 1536) ;; bytes long ;; ;; It would be nice to be able to use a register as a name -- LR ;; Must preserve BX, SI, BP ;; Registers: ;; IN: -- ;; OUT: ES:DI out buffer address ;; CHNG: AL, DX, DI, ES ;; Busy waiting WDE_IF_W_ACCESS MACRO name, no_buffer LOCAL wait_loop .errb mov DX, c_wde_&name&_io + CMDR mov DI, 15000 ;; so we don't wait forever ; old value di=65000 mov AL, byte ptr if_address[8*name+6] ;; status... and al,1 ;; packet transmitted byte jnz wait_loop ;; was successful mov DI, 1024 ;; short wait if not wait_loop: dec DI jz no_buffer in al,dx test AL, MSK_TXP jnz wait_loop mov DI, c_wde_&name&_shared_off ;; return DI:ES pointer mov DX, c_wde_&name&_shared_seg mov ES, DX ENDM ;;****************************************************************************** ;;****************************************************************************** ;; IF_W_WRITE name ;; IF_W_WRITE actually signals the ethernet board to write a packet to ;; the ethernet. The packet is assumed to be in the buffer returned by ;; IF_W_ACCESS. CX is the length of the packet to send. ;; ;; It would be nice to be able to use a register as a name -- LR ;; ;; Registers: ;; IN: CX=length ;; OUT: -- ;; CHNG: AL, DX ;; Very fast. ;; ;; Note: this could be changed by entering with the base c_wde_&name&_io in DX WDE_IF_W_WRITE MACRO name .errb mov SI, c_wde_&name&_io lea dx, [si+CMDR] mov al, MSK_PG0 + MSK_RD2 ;; make sure we are in register page 0 out dx,al lea dx, [si+TSR] in al, dx mov byte ptr if_address[8*name+6],al ;; save tx status lea dx, [si+TBCR0] mov al,cl out dx,al ;; set length PORT_WRITE TBCR1, c_wde_&name&_io, CH xor AL, AL ;; tell card packet begins at page 0 PORT_WRITE TPSR, c_wde_&name&_io PORT_WRITE CMDR, c_wde_&name&_io, MSK_TXP + MSK_RD2 ;; send the packet ENDM ;;****************************************************************************** ;;****************************************************************************** ;; IF_SET_ADDRESS name ;; IF_SET_ADDRESS sets the hardware address to be the value ;; pointed to by SI. Note this function may be a no-op if the ;; hardware address cannot be set (ETHERNET for example) ;; ;; WDE_IF_SET_ADDRESS MACRO name .err ;; we don't support setting ethernet addresses (yet) ENDM ;;****************************************************************************** ;;****************************************************************************** ;; IF_COPY name ;; IF_COPY copys a packet from the input buffer (pointed ;; to by SI and the immediate segment register given in IF_DECLARE) ;; to an output ;; buffer (pointed to by DI and dest_reg) of length CX. It assumes the ;; output buffer is contiguous. (and the caller shouln't care if the ;; input buffer is contiguous) ;; ;; IN: ? imm_seg:SI=src pkt, CX=len, ES:DI=dest ;; OUT: -- ;; CHNG: AX, CX, DX, SI, DI WDE_IF_COPY MACRO name local wrap, nowrap, done, no_time1, no_time2 .errb mov DX, DS ;; save DS mov AX, c_wde_&name&_shared_seg ;; immediate mov DS, AX mov AX, OFFSET c_wde_&name&_shared_off+c_wde_&name&_stop_pg*256 sub AX, SI ;; AX holds length to wrap line cmp AX, CX jae nowrap ;; new- dont wrap if AX >= packet lenght ;; jl wrap ;; original - wrap if AX less than packet lenght ;; MEM_COPY ;; new ;; jmp done ;; new wrap: xchg AX, CX ;; length is now length to wrap line sub AX, CX ;; AX holds remainder MEM_COPY mov SI, OFFSET c_wde_&name&_shared_off+STRT_PG*256 mov CX, AX nowrap: MEM_COPY done: mov DS, DX ;; restore DS ENDM ;;****************************************************************************** ;; --------------- end of file ---------------- ct.inc1006440004230000000000001770105372241603007122 037777777777 1 0 ;;---------- File: ct.inc --------------------------- ;; ;; 05may93 Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it) ;; ;; Statistics for pcbridge. They are both printed on the screen ;; and sent to the network on every active interface. ;; ;; Counter names. Cards # start from 1, so idx are base+1..base+MAX_CARDS ;; Counters are sequential for each field. MAX_CARDS EQU 5 ;; can't fit more on the screen... BIGLOOP_CTR EQU 0 ;; main counter COLLSN_CTR EQU 1 ;; table collision TIME_CTR EQU 2 ;; time-of-day RX_BASE_CTR EQU 2 RXALL_BASE_CTR EQU MAX_CARDS*1+2 ;; to max_cards*2+2-1 TXPKT_BASE_CTR EQU MAX_CARDS*2+2 ;;... DROP_BASE_CTR EQU MAX_CARDS*3+2 LOCAL_DROP_BASE_CTR EQU MAX_CARDS*4+2 RXPKT_BRIDGE EQU MAX_CARDS*5+2 RX_BYTE_BASE EQU MAX_CARDS*6+2 BAD_RX_BASE EQU MAX_CARDS*7+2 NUM_COUNTERS EQU 8 ;;---------------------------------------------------------------- .DATA ethaddr_base label word ;; used once to print card addresses FOR name, <1,2,3,4,5> pos_&name&_ha dw (9*80-2+14*name)*2 ;; positions for ethernet address ct_&name&_ha DW 3 DUP (0) ;; if_addr in print order ENDM last_stat_sent db 8 dup (0); last time sent statistics for i-th card table_offset dw 8 dup (0); last segment sent statistics for i-th card ;; The following data are also sent out to the net, thus they must ;; be contiguous. ;;===== Ethernet Header - 14 bytes - total 14 out_pkt dw 3 dup (0ffffh) ;; 00: dest, broadcast eth_src dw 3 dup (?) ;; 06: card address dw 8 ;; 12: IP type ;;===== IP header - 20 bytes - total 34 dw 45h ;; 14: VERS 4, IHL 5, TOS 0) dw (?) ;; 16: Total length (byte 14 to end) dw (?) ;; 18: Identification (random number) dw 0 ;; 20: Flags=0, frag.offset=0 dw (?) ;; 22: TTL(0x0f=15), Protocol (0x11=UDP) dw (?) ;; 24: IP header checksum (14 to 33incl.) dw 2 dup (0) ;; 26: Src address (0 ?) dw 2 dup (0ffffh) ;; 30: Dest address, broadcast ;;===== UDP Header - 8 bytes - total 42 dw (?) ;; 34: Source port dw (?) ;; 36: Destination port dw (?) ;; 38: UDP Length (byte 34 to end of data) dw 0 ;; 40: Chechsum (0 if not computed) ;;===== UDP DATA magic_string db 16 dup (?) ;; 42: filled up at run time port_id LABEL BYTE ;; 58: used for port id when sending out data if_found DB 8 dup (0) ;; 58: 1 if found, 0 if not if_address DW 8 dup (0eeeeh,0eeeeh,0eeeeh,0);;66: if_address ;; 6 bytes IF address, 1 TX status, 1 flags. 64bytes, total 130 IF_FLAG_DISABLE EQU 1 ;; RX/TX disabled on this if ;; IF_FLAG_TEMP_DISABLE EQU 2 ;; temporarily disabled ;; IF_FLAG_PERM_DISABLE EQU 4 ;; permanently disabled ;; Each counter is 6 bytes, preceded by 2 bytes indicating ;; the memory offset on a PC screen. counter_base dw (5*80+12)*2,0,0,0 ;; 130: bigloop counter. 8bytes, total 138 dw (6*80 + 12)*2,0,0,0 ;; 138: collisions. time_ctr_w label byte dw (5*80 + 26)*2,0,0,0 ;; 146: time ;; 154: FOR ctr, <1,2,3,4,5,6,7,8> ;; 8 counters per card FOR card, <1,2,3,4,5> ;; 5 cards dw ( 10*80 +80*ctr -2 + 14*card)*2, 0, 0, 0 ENDM ENDM ;; 320 bytes total 474 pos_vdrm dw 0b000h ;; 474: 2 bytes total 476 ;; the following 4 words are actually unused bdg_flush_ptr dw (0) unused_3 dw 0, 0, 0 ;; This is the filtering table, made of 16 3-word entries. ;; Filtering is made on the Ethernet TYPE fields. ;; Word 1 and 2 define a right-open interval (e.g. 800h,801h) ;; contains only 800h). Word 0 is divided into 2 bytes: ;; LSB tells what to do if type is outside the interval ;; MSB tells what to do if type is inside the interval ;; Values are: 1=bridge, 2=discard, 3=try next filter. ;; 00 means no more filters are needed. ;; The following will pass only IP and ARP. ;; If you put 0 in the first word, then all packets will be ;; bridged. filters label byte dw 103h, 800h, 801h ;; take IP. Zero 1st word for no filters dw 103h, 806h, 807h ;; take ARP dw 202h, 0, 0 ;; discard all the others dw 13 dup (0, 0, 0 ) ;; address filters. 96 bytes, total 572; dw 0; end of filter table_data_off dw (?) table_data dw 256 dup (?); 512 bytes of table data ;; End of sent data. eth_pkt_size equ $ - out_pkt ;; used later to compute checksums etc. ;;******************************************************************** .DATA? current_rx_segment dw (?) tab dw 256 dup(?) ;; a table of hex strings ;;---------------------------------------------------- COUNT_CYCLE MACRO name,size LOCAL done .CODE IFNB add word ptr counter_base[8*name+2],size ;; LSW jnc done ;; if no-overflow verify for printing ELSE inc word ptr counter_base[8*name+2] ;; LSW jnz done ;; if no-overflow verify for printing ENDIF inc word ptr counter_base[8*name+4] ;; 2SW jnz done inc word ptr counter_base[8*name+6];; MSW, no overflow in one year done: ENDM ;; COUNT_CYCLE ;;---------------------------------------------------- ;;---------------------------------------------------- PRINT_COUNTERS MACRO LOCAL go_out, no_time .CODE ;; mov es, pos_vdrm ;; not needed mov al, byte ptr counter_base[2] and al, 7 jz no_time FOR card, <1,2,3,4,5> ;; still needed for IF_W_ACCESS, IF_W_WRITE LOCAL next,cicla,no_stat IF card le num_dls cmp al,card jne next cmp if_found[card], 0 ;; if card 1 not found, dont print counter jz go_out xor ah,ah mov bx, ax ;; lr 16 apr 93 ;; card index mov al,time_ctr_w[2] and al, 0f0h ;; once every 10 second send packet cmp al, last_stat_sent[bx] je no_stat ;; already sent... ;;----- management code, send info on the network --------------- mov last_stat_sent[bx],al mov di, offset out_pkt ;; di points to output buffer call make_pkt ;; now copy table data ;; mov si, table_offset[2*card] ;; from... local to each card ;; mov table_data_off,si ;; lea di, table_data ;; to ;; mov cx, 256 ;; mov AX, bdg_table_ptr ;; push ds ;; pop es ;; es=data segment (packet) ;; mov ds,ax ;; ds=table segment ;; rep movsw ;; mov ax,es ;; restore ds ;; mov ds,ax ;; mov table_offset[2*card],si ;; from... local to each card!! ;; remember, I am on this side lea si, out_pkt[6] mov di, [si+4] ;; hash source address xor di, [si+2] shl DI, 3 ;; now ES:DI is the hash address mov ES, bdg_table_ptr movsw ;; now save source address/if in table movsw movsw mov AL, card ;; save source IF stosb IF_W_ACCESS card, no_stat ;; out ES:DI buffer COUNT_CYCLE %(card + TXPKT_BASE_CTR) ;; update tx. ctr mov SI, offset out_pkt mov CX, eth_pkt_size ;; size of packet MEM_COPY ;; DS:SI -> ES:DI for CX bytes ;; CX is lost, as well as SI and DI mov CX, eth_pkt_size ;; size of packet IF_W_WRITE card ;; forward data (buffer is at fixed position) mov byte ptr if_address[8*card+7],0 ; restore status ;;----------------- end management code ---------------------------- no_stat: mov bp,2+card ;; print all counters related to a card cicla: push bp call print_stat ;; also sets ES pop bp add bp,MAX_CARDS ;; that is is 5 cmp bp, 2+ MAX_CARDS*NUM_COUNTERS + card jnz cicla mov al,byte ptr if_address[8*card+6] ; status and al,1 add al,'X' ;; this is a hack. 'X' is bad, 'Y' is good mov es:[2*(8*80-2+14*card)],al ;; print status on screen mov al,byte ptr if_address[8*card+7] ; status and al,1 add al,'0' ;; display port status mov es:[2*(8*80-1+14*card)],al ;; print status on screen jmp go_out next: ENDIF ;; card le num_dls ENDM mov ah,2 int 1ah mov time_ctr_w[2],dh mov time_ctr_w[3],cl mov time_ctr_w[4],ch mov bp,2 CALL PRINT_STAT no_time: mov bp, 0 ;; print counter 0 CALL PRINT_STAT mov bp, 1 ;; print counter 1 CALL PRINT_STAT go_out: ENDM ;; PRINT_COUNTERS ;;-----------------------------end of file------------------------------------- support setting ethernet addresses (yet) ENDM ;;**********copyrt.inc1006440004230000000000000232105371762506010035 037777777777 1 0 ;;----------------------------------------------------------- ;; File: copyrt.inc ;; ;; 04may93 Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it) ;; .DATA ;; strings here are in the form ;; COL ROW Text '|' (terminator). terminator ascii code must be >80 ;; col, row start from 0. banner label byte db 0,0,"PCBRIDGE v.2.77|" db 0,1,"05 may 93 18:05|" db 20,0, "by Alessandro Fanelli, Luigi Rizzo (luigi@iet.unipi.it)|" db 25,1, "Dipartimento di Ingegneria dell'Informazione|" db 19,2, "Universita` di Pisa - via Diotisalvi 2, 56126 Pisa, Italy|" db 17,3,"(original sources by Vance Morrison, Northwestern University)|" db 45,06,'=== Bridge Statistics ===|' db 00,05,'Loops|' db 00,06,'Tab colls|' ;; collisions db 00,08,'St I/O MEM|' db 16,08,'280 D000|' db 30,08,'2A0 D400|' db 44,08,'300 C800|' db 58,08,'320 CC00|' db 72,08,'340 E000|' db 00,09,'Eth. addr.|' db 00,11,'Rx packets|' db 00,12,'Fwd all|' db 00,13,'Tx packets|' db 00,14,'Dropped|' db 00,15,'Local drop|' db 00,16,'Rx bridge|' db 00,17,'Rx bytes|' db 00,18,'Bad rx pkt|' db 00,00,'||' ; terminator ;;---------------------end of file-------------------------------------- 37777777777 1 0 buffer.inc1006440004230000000000001103705367021725007766 037777777777 1 0 ;;**************************************** ;; buffer.inc ;;**************************************** ;; ;; Copyright (C) 1989 Northwestern University, Vance Morrison ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Northwestern University makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;************************************************************************* ;; ;; buffer.inc implements a simple buffer allocator for use in a packet ;; queue. ;; ;; AUTHOR: Vance Morrison ;; DATE: 5/24/89 ;; ADDRESS: morrison@accuvax.nwu.edu ;;**************************************************************************** buff_data STRUC start_free DW ? ;; free space between these end_free DW ? buff_data ENDS ;;************************************************************************** ;; buff declare declares a new buffer manager. The buffer to be managed is ;; 'buff' of length 'len'. If 'buff' is blank, then BUFF_DEFINE allocates ;; the buffer space. Note that the total buffer space needs AT LEAST ;; 2-3 times greater than the biggest allocation for these routines to work ;; well. ;; BUFF_DECLARE MACRO name, len, buff .DATA buff_&name&_len = len IFNB buff_&name&_buff = buff ELSE global buff_&name&_my_space:byte buff_&name&_buff = buff_&name&_my_space buff_&name&_allocate = 1 ENDIF buff_&name&_buff_end = (buff_&name&_buff + len) global buff_&name&_data:buff_data .CODE ENDM ;;************************************************************************** ;; BUFF_DEFINE allocates memory and initializes the buffer manager ;; BUFF_DEFINE MACRO name ifdef buff_&name&_buff .DATA buff_&name&_data buff_data <> ifdef buff_&name&_allocate buff_&name&_my_space DB buff_&name&_len DUP (0) endif .CODE mov buff_&name&_data.start_free, offset buff_&name&_buff mov buff_&name&_data.end_free, offset buff_&name&_buff_end endif ENDM ;;************************************************************************** ;; BUFF_CHECK checks to see if CX bytes of contiguous memory is available ;; in the buffer. If it is it sets SI to the begining and DI to the end ;; of the buffer. Othersize it jumps to 'fail'. Note this routine does ;; NOT allocate the space (that is if BUFF_CHECK is called again it will ;; return the same space) BUFF_GET does the allocation ;; note that this behavior is guarenteed (successive BUFF_CHECKS will ;; return the same value even if BUFF_FREEs are done in between) ;; BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES MACRO name, fail local done mov SI, word ptr buff_&name&_data.start_free mov AX, word ptr buff_&name&_data.end_free mov DI, SI add DI, CX ;; first guess at SI:DI output cmp DI, AX jbe done ;; try wrap around cmp SI, AX jbe fail cmp DI, offset buff_&name&_buff_end jbe done mov SI, offset buff_&name&_buff mov DI, SI add DI, CX ;; first guess at SI:DI output cmp DI, AX ja fail done: ENDM ;;************************************************************************** ;; BUFF_GET allocates the space that was checked with BUFF_CHECK. DI ;; is assumed to be initialized by BUFF_CHECK. ;; BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES MACRO name mov word ptr buff_&name&_data.start_free, DI ENDM ;;************************************************************************** ;; BUFF_FREE releases the buffer allocated by BUFF_GET. DI is assumed to ;; be the same value as returned by BUFF_CHECK. IT ALSO RELEASES ALL ;; ALLOCATION MADE PREVIOUSLY TO THE ALLOCATION ASSOCIATED WITH DI (that ;; is we asume that if Buffer 1 was allocated before Buffer 2, then ;; buffer 1 will be freed before buffer 2. (this is true for queue allocations) ;; BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES MACRO name local done mov word ptr buff_&name&_data.end_free, DI cmp DI, word ptr buff_&name&_data.start_free ;; is the buffer all free? jnz done mov word ptr buff_&name&_data.start_free, offset buff_&name&_buff mov word ptr buff_&name&_data.end_free, offset buff_&name&_buff_end done: ENDM .CODE ;; mov es, pos_vdrm ;; not needed mov al, byte ptr counter_base[2] and al, 7 jz no_time FOR card, <1,2,3,4,5> ;; still needed for IF_W_ACCESS, IF_W_WRITE LOCAL next,cicla,no_stat IF card le num_dls cmp al,card jne next cmp if_found[card], 0 ;; if card 1 not found, dont print counter jz go_out xor ah,ah mov bx, ax ;; lr 16 apr 93 ;; card index mov al,time_ctr_w[2] and al, 0f0h ;; once every 10 second send packet cmp al, last_stat_sequeue.inc1006440004230000000000000676105367022030007637 037777777777 1 0 ;;**************************************************************************** ;; queue.inc queue.inc ;;**************************************************************************** ;; ;; Copyright (C) 1989 Northwestern University, Vance Morrison ;; ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Northwestern University makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;************************************************************************* ;; ;; queue.inc implements a simple, general purpose, fixed lenght queue. ;; ;; AUTHOR: Vance Morrison ;; DATE: 5/24/89 ;; ADDRESS: morrison@accuvax.nwu.edu ;;**************************************************************************** queue_data STRUC head DW ? tail DW ? queue_data ENDS ;;************************************************************************** ;; QUEUE_DECLARE declares a queue called 'name' that can hold 'len' entries ;; of size 'size' ;; QUEUE_DECLARE MACRO name, len, size .DATA queue_&name&_len = len + 1 queue_&name&_size = size global queue_&name&_data:queue_data global queue_&name&_entries:byte queue_&name&_end = queue_&name&_entries+(queue_&name&_size*queue_&name&_len) .CODE ENDM ;;************************************************************************** ;; QUEUE_DEFINE allocates the necessary memory and initializes the queue ;; QUEUE_DEFINE MACRO name ifdef queue_&name&_len .DATA queue_&name&_data queue_data <> queue_&name&_entries DB (queue_&name&_size * queue_&name&_len) dup (0) .CODE mov queue_&name&_data.head, offset queue_&name&_entries mov queue_&name&_data.tail, offset queue_&name&_entries endif ENDM ;;************************************************************************** ;; QUEUE_HEAD_out_SI returns the pointer to the head of the queue in SI. ;; if the queue is empty this macro jumps to 'empty' ;; QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES MACRO name, empty mov SI, word ptr queue_&name&_data.head cmp SI, word ptr queue_&name&_data.tail jz empty ENDM ;;************************************************************************** ;; QUEUE_DEQUEUE_in_SI removes the head entry pointed to by SI from the ;; queue. (SI was assumed to be inititialized by QUEUE_HEAD) ;; QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES MACRO name local no_wrap add SI, offset queue_&name&_size cmp SI, offset queue_&name&_end jb no_wrap mov SI, offset queue_&name&_entries no_wrap: mov word ptr queue_&name&_data.head, SI ENDM ;;************************************************************************** ;; QUEUE_ENQUEUE enqueues a buffer and returns its pointer in DI. The ;; entry can then be filled in as desired. ;; QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES MACRO name, full local no_wrap mov DI, queue_&name&_data.tail ;; increment with wrap mov AX, DI ;; save DI add DI, offset queue_&name&_size cmp DI, offset queue_&name&_end jb no_wrap mov DI, offset queue_&name&_entries no_wrap: cmp DI, word ptr queue_&name&_data.head jz full mov queue_&name&_data.tail, DI mov DI, AX ENDM ,14,'Dropped|' qif.inc1006440004230000000000001702605367022021007266 037777777777 1 0 ;;****************************************************************************** ;; qif.inc qif.inc ;;****************************************************************************** ;; ;; Copyright (C) 1990 Vance Morrison ;; ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;****************************************************************************** ;; qif.inc contains the interface driver for a QIF interface. This ;; software is responcible for packetizing/depacketizing the data for the ;; serial line and providing data transparency (byte stuffing) ;; ;; The functions provided by this file are ;; ;; Q_IF_DECLARE name, rbuff, rqueue, wbuff, wqueue, prefix, port ;; Q_IF_DEFINE name ;; Q_IF_R_ACCESS name, no_packet ;; Q_IF_R_CONT name, ok ;; Q_IF_R_FREE name ;; Q_IF_W_ACCESS name, no_buffer ;; Q_IF_W_WRITE name ;; Q_IF_SET_ADDRESS name ;; Q_IF_COPY name ;; ;; Variables set by this module ;; ;; qif_&name&_declared ;; one if this interface exists ;; ;; Variables used by this module ;; ;; if_&name&_mtu ;; the MTU of this interface ;; ;;****************************************************************************** ;;****************************************************************************** ;; data storage needed by this module qif_entry STRUC qif_q_start DW ? qif_q_end DW ? qif_q_len DW ? qif_entry ENDS qif_data STRUC qif_wpkt_start DW 0 qif_wpkt_end DW 0 qif_data ENDS ;;****************************************************************************** ;; IF_DECLARE name, rbuff, rqueue, wbuff, wqueue, prefix, port ;; rbuff and wbuff are the buffer sizes (in bytes for the read ;; and write buffers, and rqueue and wqueue are the number length ;; of the queue in packets. Whenever a high level write is performed ;; the routine _WRITE_const_BX_BP_ES is called. ;; Q_IF_DECLARE MACRO name, rbuff, rqueue, wbuff, wqueue, prefix, port .errb .errb .DATA qif_&name&_declared = 1 qif_&name&_rbuff = rbuff qif_&name&_rqueue = rqueue qif_&name&_wbuff = wbuff qif_&name&_wqueue = wqueue qif_&name&_prefix equ qif_&name&_port = port global qif_&name&_data:qif_data .CODE ENDM ;;****************************************************************************** ;; IF_DEFINE name ;; sets asside memory an name object and initializes it. This ;; routine is a no-op if 'name' was not declared. ;; Q_IF_DEFINE MACRO name ifdef qif_&name&_declared .DATA qif_&name&_data qif_data <> .CODE endif ENDM ;;****************************************************************************** ;; IF_R_ACCESS name, no_packet ;; IF_R_ACCESS checks for the next packet to come from the the board ;; associated with 'name' and returns a pointer to the begining of ;; an ethernet packet in BX:ES. CX holds the length of the packet ;; R_ACCESS jumps to 'no_packet' if there are no packets waiting to ;; be read in ;; Q_IF_R_ACCESS MACRO name, no_packet .errb QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %qif_&name&_rqueue, no_packet mov BX, [SI+qif_q_start] mov CX, [SI+qif_q_len] mov AX, DS mov ES, AX ENDM ;;****************************************************************************** ;; IF_R_FREE name ;; After the client is through processing the packet returned by ;; IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the ;; memory that the packet was in can be reused for future packets. ;; Q_IF_R_FREE MACRO name local done .errb cli mov AX, SI ;; save SI DI mov DX, DI QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %qif_&name&_rqueue, done mov DI, [SI+qif_q_end] BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %qif_&name&_rbuff QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES %qif_&name&_rqueue done: mov SI, AX ;; restore SI DI mov DI, DX sti ENDM ;;****************************************************************************** ;; Q_IF_R_CONT name, ok ;; IF_R_CONT determines if the packet returned by R_READ in BX:ES ;; of length CX is continuous. If it is it jumps to 'ok' otherwise ;; it just returns ;; Q_IF_R_CONT MACRO name, ok .errb jmp ok ;; is it always continuous ENDM ;;****************************************************************************** ;; IF_W_ACCESS name, no_buffer ;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The ;; pointer is returned in DI:ES. If the ouptut buffer is busy, this ;; routine will jump to 'no_buff'. The output buffer min(CX, mtu) ;; bytes long ;; Q_IF_W_ACCESS MACRO name, no_buff local lenOK, done, my_no_buff .errb cmp CX, if_&name&_mtu jbe lenOK mov CX, if_&name&_mtu lenOK: cli BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %qif_&name&_wbuff,my_no_buff BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %qif_&name&_wbuff sti mov word ptr qif_&name&_data.qif_wpkt_start, SI mov word ptr qif_&name&_data.qif_wpkt_end, DI mov DI, SI mov AX, DS mov ES, AX jmp done my_no_buff: sti jmp no_buff done: ENDM ;;****************************************************************************** ;; IF_W_WRITE name ;; IF_W_WRITE actually signals the ethernet board to write a packet to ;; the ethernet. The packet is assumed to be in the buffer returned by ;; IF_W_ACCESS. CX is the length of the packet to send. ;; Q_IF_W_WRITE MACRO name local done .errb cli QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES %qif_&name&_wqueue, done mov AX, word ptr qif_&name&_data.qif_wpkt_start mov [DI+qif_q_start], AX mov AX, word ptr qif_&name&_data.qif_wpkt_end mov [DI+qif_q_end], AX mov [DI+qif_q_len], CX ;start the low level processing, if necessary. done: CALL_WRITE_const_BX_BP_ES %qif_&name&_prefix, %qif_&name&_port sti ENDM CALL_WRITE_const_BX_BP_ES MACRO prefix, port prefix&_WRITE_const_BX_BP_ES port ENDM ;;****************************************************************************** ;; IF_SET_ADDRESS name ;; IF_SET_ADDRESS sets the hardware address to be the value ;; pointed to by SI. Note this function may be a no-op if the ;; hardware address cannot be set (ETHERNET for example) ;; Q_IF_SET_ADDRESS MACRO name .err ;; we don't support setting ethernet addresses ENDM ;;****************************************************************************** ;; IF_COPY name ;; IF_COPY copys a packet from the input buffer (pointed ;; to by SI and the segement register given in IF_DECLARE) to an output ;; buffer (pointed to by DI and ES) of length CX. It assumes the ;; output buffer is contiguous. (and the caller shouln't care if the ;; input buffer is contiguous) ;; Q_IF_COPY MACRO name .errb ;; since DS points to the data segment, we are ready to go!! inc CX shr CX,1 rep movsw ENDM ame&_len) .CODE ENDM ;;************************************************************************** ;; QUEUE_DEFINE allocates the necessary memory and initializes the queue ;; QUEUE_DEFINE MACRO name ifdef queue_&name&_len .DATA queue_&name&_data queue_data <> queue_&name&_entries DB (queue_&name&_size * queue_&name&_len) dup (0) .CODE mov queue_&name&_data.head, offset queue_&name&_entries mov queue_&name&_data.tail, offset queue_&name&_entries endif ENDM slip.inc1006440004230000000000002262305367022040007456 037777777777 1 0 ;;****************************************************************************** ;; slip.inc slip.inc ;;****************************************************************************** ;; ;; Copyright (C) 1989 Vance Morrison ;; ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;****************************************************************************** ;; slip.inc contains the interface driver for a SLIP interface. This ;; software is responcible for packetizing/depacketizing the data for the ;; serial line and providing data transparency (byte stuffing) ;; ;; The functions provided by this file are ;; ;; SLIP_DECLARE name, i8250, rbuff, rqueue, wbuff, wqueue ;; SLIP_DEFINE name ;; SLIP_CONSUME_in_AL_const_BX_BP_ES name ;; SLIP_WRITE_EMPTY_const_BX_BP_ES name ;; SLIP_WRITE_const_BX_BP_ES name ;; ;; This code assumes that an i8250 interface (WRITE and CAN_WRITE) ;; is available via the 'i8250' variable passed to SLIP_DECLARE ;; ;; slip_&name&_declared ;; one if this interface exists ;; if_&name&_mtu ;; the maximum transmition unit ;; if_&name&_address ;; physical address (dummy value) ;; ;;****************************************************************************** ;;****************************************************************************** ;; data storage needed by this module slip_data STRUC slip_rptr DW 0 slip_rstart DW 0 slip_rend DW 0 slip_resc DB 0 slip_rsyncing DB 0 slip_wptr DW 1 ; bigger than wend means start a new packet slip_wend DW 0 slip_drops DW 0 ; number of dropped packets slip_bytes_in DW 0 ; total number of bytes that have come in slip_bytes_out DW 0 ; total number of bytes that have went out slip_wesc DB 0 slip_w_idle DB 1 ;; flag set when 8250 write buff idle slip_data ENDS SLIP_ESC = 0DBH SLIP_END = 0C0H SLIP_ESC_ESC = 0DDH SLIP_ESC_END = 0DCH ;;****************************************************************************** ;; SLIP_DECLARE SLIP_DECLARE MACRO name, i8250, rbuff, rqueue, wbuff, wqueue .errb .errb .DATA slip_&name&_declared = 1 slip_&name&_i8250 = i8250 slip_&name&_rbuff = rbuff slip_&name&_rqueue = rqueue slip_&name&_wbuff = wbuff slip_&name&_wqueue = wqueue if_&name&_mtu = 1006 global slip_&name&_data:slip_data global if_&name&_address:byte .CODE ENDM ;;****************************************************************************** ;; SLIP_DEFINE name ;; sets asside memory the named object and initializes it. This ;; routine is a no-op if 'name' was not declared. It jumps to 'fail' ;; if there was an error in setup. ;; SLIP_DEFINE MACRO name ifdef slip_&name&_declared .DATA slip_&name&_data slip_data <> ;; create storage needed if_&name&_address DB 6 DUP (0) .CODE mov slip_&name&_data.slip_w_idle, 1 endif ENDM ;;****************************************************************************** ;; SLIP_WRITE_EMPTY_const_BX_BP_ES is called at interupt level whenever a ;; character can be written. SLIP_WRITE_EMPTY_const_BX_BP_ES MACRO name local done, write_char, no_char write_char: SLIP_GETQUEUE_out_AL_const_BX_BP_ES name, no_char inc slip_&name&_data.slip_bytes_out I8250_WRITE_in_AL_const_BX_BP_ES %slip_&name&_i8250 I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES %slip_&name&_i8250, done jmp write_char no_char: mov slip_&name&_data.slip_w_idle, 1 done: ENDM ;;****************************************************************************** ;; SLIP_WRITE is called whenever a high level write is ;; performed (something is placed in the queue). The call to this routine ;; acts as a 'trigger' that starts the writing if it is not already in ;; progress. SLIP_WRITE_const_BX_BP_ES MACRO name local done, write_char, no_char, cant_write cmp slip_&name&_data.slip_w_idle, 0 ;; are we idle jz done write_char: SLIP_GETQUEUE_out_AL_const_BX_BP_ES name, done inc slip_&name&_data.slip_bytes_out I8250_WRITE_in_AL_const_BX_BP_ES %slip_&name&_i8250 I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES %slip_&name&_i8250, cant_write jmp write_char cant_write: mov slip_&name&_data.slip_w_idle, 0 done: ENDM ;;****************************************************************************** ;; SLIP_CONSUME is called from the interupt routine whenever a new character ;; is available from this interface. The character is in AL. SLIP_CONSUME's ;; job is to place it in the packet queue. SLIP_CONSUME_in_AL_const_BX_BP_ES MACRO name inc slip_&name&_data.slip_bytes_in SLIP_PUTQUEUE_in_AL_const_const_BX_BP_ES name ; easy is't it? ENDM ;;****************************************************************************** ;; puts a chacacter read in for interface 'name' into the read queue SLIP_PUTQUEUE_in_AL_const_const_BX_BP_ES MACRO name local cont_packet, done, not_escaped, try_esc_esc, got_end, got_esc local drop, not_end, nobuff mov SI, slip_&name&_data.slip_rptr or SI, SI ;; rptr = 0 if 'between' packets jnz cont_packet cmp AL, SLIP_END jnz not_end mov slip_&name&_data.slip_rsyncing, 0 jmp done not_end: cmp slip_&name&_data.slip_rsyncing, 0 jnz done mov CX, if_&name&_mtu mov DX, AX BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %slip_&name&_rbuff,nobuff mov AX, DX mov slip_&name&_data.slip_rptr, SI mov slip_&name&_data.slip_rstart, SI mov slip_&name&_data.slip_rend, DI BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %slip_&name&_rbuff cont_packet: cmp AL, SLIP_END jz got_end cmp AL, SLIP_ESC jz got_esc ;; what to do if you have a regular character cmp slip_&name&_data.slip_resc, 0 jz not_escaped mov slip_&name&_data.slip_resc, 0 cmp AL, SLIP_ESC_END jnz try_esc_esc mov AL, SLIP_END jmp not_escaped try_esc_esc: cmp AL, SLIP_ESC_ESC jnz drop mov AL, SLIP_ESC not_escaped: mov CX, slip_&name&_data.slip_rstart add CX, if_&name&_mtu cmp SI, CX jae drop mov [SI], AL inc SI mov slip_&name&_data.slip_rptr, SI jmp done drop: mov DI,slip_&name&_data.slip_rend BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %slip_&name&_rbuff nobuff: mov slip_&name&_data.slip_rptr, 0 mov slip_&name&_data.slip_resc, 0 mov slip_&name&_data.slip_rsyncing, 1 inc slip_&name&_data.slip_drops jmp done got_esc: mov slip_&name&_data.slip_resc, 1 jmp done got_end: mov CX, SI mov AX, slip_&name&_data.slip_rstart sub CX, AX mov SI, AX QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES %slip_&name&_rqueue, drop mov [DI+0], SI ;; offset for start ptr mov DX, slip_&name&_data.slip_rend mov [DI+2], DX ;; offset for end ptr mov [DI+4], CX ;; offset for data len (may be less) mov slip_&name&_data.slip_rptr, 0 done: ENDM ;;****************************************************************************** ;; SLIP_GETQUEUE_out_AL_const_BX_BP_ES MACRO name, no_char local cont_packet, end_packet, done, normal_char, check_esc, update_ptr mov SI, slip_&name&_data.slip_wptr cmp SI, slip_&name&_data.slip_wend jb cont_packet jz end_packet QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %slip_&name&_wqueue, no_char mov DI, SI mov SI, [DI+0] ;; get start ptr mov slip_&name&_data.slip_wptr, SI mov AX, [DI+4] ;; get data len add AX, SI mov slip_&name&_data.slip_wend, AX mov AL, SLIP_END jmp done end_packet: mov AL, SLIP_END inc SI mov slip_&name&_data.slip_wptr, SI QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %slip_&name&_wqueue, done mov DI, [SI+2] ;; get end ptr BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %slip_&name&_wbuff QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES %slip_&name&_wqueue jmp done cont_packet: cmp slip_&name&_data.slip_wesc, 0 jz normal_char mov AL, slip_&name&_data.slip_wesc mov slip_&name&_data.slip_wesc, 0 jmp update_ptr normal_char: mov AL, [SI] cmp AL, SLIP_END jnz check_esc mov slip_&name&_data.slip_wesc, SLIP_ESC_END mov AL, SLIP_ESC jmp done check_esc: cmp AL, SLIP_ESC jnz update_ptr mov slip_&name&_data.slip_wesc, SLIP_ESC_ESC mov AL, SLIP_ESC jmp done update_ptr: inc SI mov slip_&name&_data.slip_wptr, SI done: ENDM ata.head, offset queue_&name&_entries mov queue_&name&_data.tail, offset queue_&name&_entries endif ENDM i8250.inc1006440004230000000000003526205367021765007276 037777777777 1 0 ;;***************************************************************************** ;; i8250.inc i8250.inc ;;***************************************************************************** ;; ;; Copyright (C) 1990 Vance Morrison ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;***************************************************************************** ;; this software essentially is responsible for setup of the 8250 and ;; getting individual chacters off the serial line. The routines here ;; where designed so that they will work with the 16550AF and take advantage ;; of that chip's read and write FIFOs. ;; ;; The functions provided by this file are ;; ;; I8250_DECLARE name, port_prefix, port, io_addr, irq, flow ;; I8250_DEFINE name, fail ;; I8250_WRITE_in_AL_const_BX_BP_ES MACRO name ;; I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES MACRO name not_ready ;; ;; the following 'upcall' routines will be called from interupt level ;; _CONSUME_in_AL_const_BX_BP_ES 'port' ;; _WRITE_EMPTY_const_BX_BP_ES 'port' ;; ;; i8250_&name&_declared ;; one if this interface exists ;; i8250_declared ;; one if ANY interface exists ;; ;;****************************************************************************** ;; 'standard' port numbers IBM_COM1_PORT = 3F8H IBM_COM2_PORT = 2F8H IBM_COM3_PORT = 3E8H IBM_COM4_PORT = 2E8H ;; and interupts IBM_COM1_IRQ = 4 IBM_COM2_IRQ = 3 ;; the 10 registers of the 8250 I8250_REG_THR = 0 ;; trans hold reg (LCR_DIVISOR = 0) (write only) I8250_REG_RDR = 0 ;; rec data reg (LCR_DIVISOR = 0) (read only) I8250_REG_DIVL = 0 ;; baud rate divisor (LCR_DIVISOR = 1) I8250_REG_DIVH = 1 ;; baud rate divisor (LCR_DIVISOR = 1) I8250_REG_IER = 1 ;; interupt enable (LCR_DIVISOR = 0) I8250_REG_IIR = 2 ;; interupt ident (read only) NS16550_REG_FIFO = 2 ;; FIFO control register for the NS16550AF chip I8250_REG_LCR = 3 ;; line control I8250_REG_MCR = 4 ;; modem control I8250_REG_LSR = 5 ;; line status I8250_REG_MSR = 6 ;; modem status NS16550_REG_SCR = 7 ;; Scratch register. ;; masks for the IER I8250_IER_DIN = 1H ;; interupt on data in I8250_IER_DOUT = 2H ;; interupt on data ready for out I8250_IER_ERR = 4H ;; interupt on data error I8250_IER_MODEM = 8H ;; interupt on modem status change ;; masks for the IIR I8250_IIR_NOINT = 1H ;; the rest of IIR valid only if this bit is 0 I8250_IIR_TYPE = 6H ;; type of highest priority interupt NS16550_IIR_CHECK = 0C0H ;; Both of these bits must be 1 if its a 16550AF ;; values for type field of the IIR (read only) I8250_TYPE_MODEM = 0H ;; notice they are shifted to the TYPE field I8250_TYPE_DOUT = 2H I8250_TYPE_DIN = 4H I8250_TYPE_ERR = 6H ;; masks for the FIFO reg (write only) NS16550_FIFO_ON = 1 ;; Enable the FIFOs NS16550_FIFO_FIN = 2 ;; Flush input FIFO NS16550_FIFO_FOUT = 4 ;; Flush input FIFO NS16550_FIFO_TRIG = 0C0H ;; bytes in input FIFO before interupt ;; values for the TRIG field of the FIFO reg NS16550_TRIG_1 = 000H ;; trigger on 1 byte NS16550_TRIG_4 = 040H ;; trigger on 4 bytes NS16550_TRIG_8 = 080H ;; trigger on 8 bytes NS16550_TRIG_14 = 0C0H ;; trigger on 14 bytes ;; masks for the LCR I8250_LCR_BITS = 3H ;; number of bits - 5 I8250_LCR_STOP = 4H I8250_LCR_PARITY = 38H I8250_LCR_BREAK = 40H ;; send a break I8250_LCR_DIVISOR = 80H ;; make reg 0,1 be the divisor register ;; values for the parity field of the LCR I8250_PARITY_IGN = 0H ;; notice they are shifted I8250_PARITY_ODD = 20H I8250_PARITY_EVEN = 30H I8250_PARITY_MARK = 28H I8250_PARITY_SPACE = 38H ;; masks for the MCR I8250_MCR_DTR = 1H ;; activate DTR line I8250_MCR_RTS = 2H ;; activate RTS line I8250_MCR_OUT1 = 4H ;; out1 Line I8250_MCR_OUT2 = 8H ;; out2 line I8250_MCR_LOOP = 10H ;; loopback ;; masks for the LSR I8250_LSR_DIN = 1H ;; data in read register I8250_LSR_OVR = 2H ;; data overun I8250_LSR_PERR = 4H ;; data parity error I8250_LSR_FERR = 8H ;; data framing error I8250_LSR_BREAK = 10H ;; break detected I8250_LSR_DOUT = 20H ;; Transmit buffer empty I8250_LSR_TSR = 40H ;; Transmit shift reg empty (no data on line) ;; masks for the MSR I8250_MSR_DELTA_CTS = 1H ;; CTS changed I8250_MSR_DELTA_DSR = 2H ;; DSR changed I8250_MSR_DELTA_RI = 4H ;; RI changed I8250_MSR_DELTA_DCD = 8H ;; DCD changed I8250_MSR_CTS = 10H ;; CTS active I8250_MSR_DSR = 20H ;; DSR active I8250_MSR_RI = 40H ;; RI active I8250_MSR_DCD = 80H ;; DCD active ;;***************************************************************************** ;; defs for the I8259 programable interupt controller IBM_I8259 = 20H ;; standard place on the IBM PC I8259_REG_CNTRL = 0 ;; the 8259 control register I8259_REG_MASKS = 1 ;; the interupt mask register ;; control register functions I8259_CNTRL_EOI = 20H ;; the End of Interupt command ;;***************************************************************************** ;; I8250_DECLARE declares your intent to use a 8250 serial controler at ;; address 'io_addr', using interupt 'irq'. When it gets characters it calls ;; _CONSUME_in_AL_const_BX_BP_ES 'port' and ;; _WRITE_EMPTY_const_BX_CX_DX_BP_SI_DI_ES 'port' when it ;; can write characters. If 'flow' is non-blank and = 1 ;; then hardware flow control using RTS-CTS is followed. ;; I8250_DECLARE MACRO name, port_prefix, port, io_addr, irq, flow .errb .errb .DATA i8250_declared = name i8250_&name&_port = port i8250_&name&_prefix equ i8250_&name&_declared = 1 i8250_&name&_io = io_addr i8250_&name&_irq = irq i8250_&name&_flow = 0 ifnb i8250_&name&_flow = 0&flow endif .CODE global i8250_data_seg:word global i8250_&name&_real_define_in_BX_out_AX:near ENDM ;;****************************************************************************** ;; I8250_DEFINE name, fail ;; sets asside memory an name object and initializes it. This ;; routine is a no-op if 'name' was not declared. It jumps to 'fail' ;; if there was an error in setup. BX contains the word that ;; is the baud rate divisor. (12 = 9600Baud, 6 = 19200Baud, etc) ;; I8250_DEFINE MACRO name, fail .errb .errb IFDEF i8250_&name&_declared mov BX, baud_rate_div call i8250_&name&_real_define_in_BX_out_AX or AX, AX jnz fail ENDIF ENDM ;;************************************************************************* ;; I8250_REAL_DEFINE is simply a the code that I8250_DEFINE jumps to. ;; this indirection is necessary to avoid macros getting too large. ;; I8250_REAL_DEFINE MACRO name local done .errb ifdef i8250_&name&_declared i8250_&name&_real_define_in_BX_out_AX: mov word ptr CS:i8250_data_seg, DS ;; make sure interupt routine OK ;; turn off my interupt PORT_READ I8259_REG_MASKS, IBM_I8259 and AL, (1 shl i8250_&name&_irq) PORT_WRITE I8259_REG_MASKS, IBM_I8259 I8250_SETUP_in_BX_out_AX name, done ;; turn on my interupt PORT_READ I8259_REG_MASKS, IBM_I8259 and AL, (not (1 shl i8250_&name&_irq)) PORT_WRITE I8259_REG_MASKS, IBM_I8259 sti ;; turn on interupts, if not already on xor AX, AX ;; return success done: ret if i8250_declared eq name ; define once for all ;;***************************************************************** ;; define the interupt handler .CODE i8250_data_seg: DW 0 ;; this location hold the data segment i8250_interupt: ;; the actual interupt handler cli push DS push AX push CX push DX push SI push DI mov DS, word ptr CS:i8250_data_seg FOR idx, <1,2,3,4,5> ;; check every chip I8250_IF_CHECK_const_BX_BP_ES idx ENDM mov AL, I8259_CNTRL_EOI ; tell the 8259 we are done PORT_WRITE I8259_REG_CNTRL, IBM_I8259 pop DI pop SI pop DX pop CX pop AX pop DS iret ; interupts flag restored on return endif endif ENDM ;;****************************************************************************** ;; I8250_SETUP does all the 'per chip' setup for a 8250 serial chip associated ;; with 'name'. if there is a failure 'fail' is jumped to, AX will have a ;; non-zero value if the 'fail' branch is taken. BX contains the word that ;; is the baud rate divisor. (12 = 9600Baud, 6 = 19200Baud) ;; I8250_SETUP_in_BX_out_AX MACRO name, fail local done, is_a_16550AF, is_a_8250 ifdef i8250_&name&_declared ; check to see that a serial port is there by reading 8250 register PORT_READ I8250_REG_LSR, i8250_&name&_io cmp AL, 0FFH ;; non-existant registers turn up as all 1s jz fail ;; set the baud rate divisor mov AL, I8250_LCR_DIVISOR PORT_WRITE I8250_REG_LCR,i8250_&name&_io mov AX, BX ;; get the baud rate PORT_WRITE I8250_REG_DIVL, i8250_&name&_io mov AL, AH PORT_WRITE I8250_REG_DIVH,i8250_&name&_io mov AL, (8-5) + I8250_PARITY_IGN ;; 8 bits no parity 1 stop bit PORT_WRITE I8250_REG_LCR, i8250_&name&_io if i8250_&name&_flow eq 1 mov AL, I8250_IER_DIN + I8250_IER_DOUT + I8250_IER_MODEM else mov AL, I8250_IER_DIN + I8250_IER_DOUT endif PORT_WRITE I8250_REG_IER, i8250_&name&_io ;; Turn on FIFO mode if this is a 16550AF mov AL, NS16550_FIFO_ON+NS16550_FIFO_FIN+NS16550_FIFO_FOUT+NS16550_TRIG_8 PORT_WRITE NS16550_REG_FIFO,i8250_&name&_io ;; make sure that this is a 16550AF not just a 16550 (which has a bug) PORT_READ I8250_REG_IIR, i8250_&name&_io and AL, NS16550_IIR_CHECK test AL, 80H ;; is it a 16550? jz is_a_8250 test AL, 40H ;; is it a 16550AF? jnz is_a_16550AF mov AL, 0 ;; turn off FIFO, the 16550 is buggy PORT_WRITE NS16550_REG_FIFO,i8250_&name&_io is_a_16550AF: is_a_8250: ;; set DTR and RTS for those modems that care ;; ON AN IBM PC OUT2 MUST BE SET FOR PROPER OPERATION mov AL, I8250_MCR_DTR + I8250_MCR_RTS + I8250_MCR_OUT2 PORT_WRITE I8250_REG_MCR, i8250_&name&_io cli xor AX, AX ;; load the interupt vector mov ES, AX mov DI, (i8250_&name&_irq+8)*4 mov BX, offset i8250_interupt mov AX, CS mov ES:[DI], BX mov ES:[DI+2], AX sti ; acknowledge any interupts that may have occured in the past PORT_READ I8250_REG_RDR, i8250_&name&_io PORT_READ I8250_REG_MSR, i8250_&name&_io PORT_READ I8250_REG_IIR, i8250_&name&_io endif ENDM ;;****************************************************************************** ;; I8250_IF_CHECK checks to see of interface 'name' has any characters to write ;; or to read and does the appropriate upcall if there is anthing to do. ;; I8250_IF_CHECK_const_BX_BP_ES MACRO name local done, try_write, try_read, done_write ifdef i8250_&name&_declared try_read: PORT_READ I8250_REG_LSR, i8250_&name&_io and AL, I8250_LSR_DIN ;; is there a character ready? jz try_write PORT_READ I8250_REG_RDR, i8250_&name&_io UPCALL_CONSUME_in_AL_const_BX_BP_ES %i8250_&name&_prefix, %i8250_&name&_port jmp try_read ;; keep trying until failure (for 16650) try_write: ; acknowledge the Tran Empty interupt PORT_READ I8250_REG_IIR, i8250_&name&_io I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES name, done_write UPCALL_WRITE_EMPTY_const_BX_BP_ES %i8250_&name&_prefix,%i8250_&name&_port done_write: ;; have all the interupts been processed? PORT_READ I8250_REG_IIR, i8250_&name&_io test AL, I8250_IIR_NOINT jz try_read ;; no, then try again until they are ;; Note that this final check for interupts is absolutely ;; necessary because the 8259 responds to EDGES, not LEVELs ;; only if we are absolutely sure the interupt line has gone ;; low (which would be the case if IIR has NOINT bit = 1) can ;; we be sure that the next interupt will generate an EDGE ;; the the 8259 will respond to. Without this check it is ;; possible for interupts from the 8250 to be lost causing ;; the code to hang done: endif ENDM UPCALL_CONSUME_in_AL_const_BX_BP_ES MACRO prefix, port prefix&_CONSUME_in_AL_const_BX_BP_ES port ENDM UPCALL_WRITE_EMPTY_const_BX_BP_ES MACRO prefix, port prefix&_WRITE_EMPTY_const_BX_BP_ES port ENDM ;;****************************************************************************** ;; W_CAN_WRITE jumps to 'not_ready' if the 8250 chip associated with ;; 'name' is not ready to recieve another byte. Otherwise it just returns. ;; I8250_CAN_WRITE_const_BX_CX_BP_SI_DI_ES MACRO name, not_ready .errb .errb ;; if we are doing flow control, we can only send if CTS is active if i8250_&name&_flow eq 1 PORT_READ I8250_REG_MSR, i8250_&name&_io and AL, I8250_MSR_CTS jz not_ready endif PORT_READ I8250_REG_LSR, i8250_&name&_io and AL, I8250_LSR_DOUT ;; is there a character ready? jz not_ready ENDM ;;****************************************************************************** ;; WRITE_in_AL writes the character in AL to the chip 'name'. It ;; will busy wait if necessary, however this routine is guarenteed not ;; to busy wait if it is called only when the WRITE_EMPTY upcall is issued. ;; I8250_WRITE_in_AL_const_BX_BP_ES MACRO name local wait_loop, ready .errb mov SI, AX ;; save AL xor CX, CX wait_loop: PORT_READ I8250_REG_LSR, i8250_&name&_io and AL, I8250_LSR_DOUT ;; is there a character ready? jnz ready dec CX ;; so we don't wait forever jnz wait_loop ready: mov AX, SI ;; restore AL PORT_WRITE I8250_REG_THR, i8250_&name&_io ENDM ;; ----------- end of file ---------- Transmit shift reg empty (no data on line) ;; masks for the MSR I8250_MSR_DELTA_CTS = 1H ;; CTS changed I8250_MSR_DELTA_DSR = 2H ;; DSR changed I8250_MSR_DELTA_RI = 4H ;; RI changed I8250_MSR_DELTA_DCD = 8H ;; DCD changed I8250_MSR_CTS = 10H ;; CTS active I8250_MSR_DSR = 20H ;; DSR active I8250_MSR_RI packet.inc1006440004230000000000002613305367022012007755 037777777777 1 0 ;;****************************************************************************** ;; packet.inc packet.inc ;;****************************************************************************** ;; ;; Copyright (C) 1989 Vance Morrison ;; ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;****************************************************************************** ;; packet.inc contains the interface driver for the the packet driver ;; (a la FTP software, drivers and info available from sun.soe.clarson.edu) ;; ;; Note that this interface driver is VERY inefficient. basically packets ;; are copyied once on read and once again on write. Thus this driver will ;; be at best 1/3 the speed of a native driver. Nevertheless, this speed ;; may be acceptable, (and besides it gets all those people who want 3Com ;; support off my back). There are other inefficiencies as well (saving ;; and restoring registers, waiting for packets to be sent etc), but even ;; so it will be usefull for low performance applications. ;; ;; Note that this these routines do not use any extended or high performance ;; packet driver calls. Only the basic calls are used ;; ;; The functions provided by this file are ;; ;; PKT_DECLARE name, driver_int, driver_class, prom ;; PKT_DEFINE name, fail ;; PKT_IF_R_ACCESS name, no_packet ;; PKT_IF_R_CONT name, ok ;; PKT_IF_R_FREE name ;; PKT_IF_W_ACCESS name, no_buffer ;; PKT_IF_W_WRITE name ;; PKT_IF_SET_ADDRESS name ;; PKT_IF_COPY name ;; ;; Variables set by this module ;; ;; pkt_&name&_declared ;; one if this interface exists ;; if_&name&_mtu ;; the maximum trans unit ;; ;;****************************************************************************** ;;****************************************************************************** ;; data storage needed by this module pkt_r_q_entry STRUC pkt_q_rstart DW ? pkt_q_rend DW ? pkt_r_q_entry ENDS pkt_data STRUC pkt_handle DW ? pkt_bend DW ? pkt_bstart DW ? pkt_data ENDS ;;****************************************************************************** ;; IF_DECLARE name, driver_int driver_class, promiscuous ;; declares an interface object. driver_int is the interupt number ;; to use to access the packet driver (60H-80H). driver_class is ;; the packet driver class of the card (Ethernet = 1) ;; PKT_DECLARE MACRO name, driver_int, driver_class, promiscuous .errb .errb .DATA pkt_&name&_declared = 1 pkt_&name&_drv_int = driver_int pkt_&name&_drv_class = driver_class pkt_&name&_rbuff = name*100+1 pkt_&name&_rqueue = name*100+2 pkt_&name&_promiscuous = 0 ifnb pkt_&name&_promiscuous = 0&promiscuous endif if_&name&_mtu = 1514 global pkt_&name&_wbuffer:byte global pkt_&name&_rbuffer:byte global pkt_&name&_data:pkt_data .CODE global pkt_&name&_data_seg:word global pkt_&name&_real_define:near BUFF_DECLARE %pkt_&name&_rbuff, 10240, pkt_&name&_rbuffer QUEUE_DECLARE %pkt_&name&_rqueue, 16, %(size pkt_r_q_entry) ENDM ;;****************************************************************************** ;; IF_DEFINE name, fail ;; sets asside memory an name object and initializes it. This ;; routine is a no-op if 'name' was not declared. It jumps to 'fail' ;; if there was an error in setup. ;; PKT_DEFINE MACRO name, fail ifdef pkt_&name&_declared BUFF_DEFINE %pkt_&name&_rbuff QUEUE_DEFINE %pkt_&name&_rqueue call pkt_&name&_real_define or AX, AX jnz fail endif ENDM PKT_REAL_DEFINE MACRO name local pkt_read_task, got_driver, got_address, size_check, no_room, done local set_prom .errb ifdef pkt_&name&_declared .DATA pkt_&name&_rbuffer DB 10240 dup (0) pkt_&name&_wbuffer DB 1536 dup (0) pkt_&name&_data pkt_data <> ;; create storage needed .CODE ;;***************************************************************** pkt_&name&_real_define: mov word ptr cs:pkt_&name&_data_seg, DS mov AH, 2 ;; access_type driver call mov AL, pkt_&name&_drv_class ;; driver class mov DL, 0 ;; wildcard number mov BX, 0FFFFH ;; wildcard Type xor CX, CX ;; get any packet type mov DI, CS ;; point to my packet reciever mov ES, DI mov DI, offset pkt_read_task stc PKT_CALL_DRIVER name jnc got_driver mov DL, DH xor DH, DH mov AX, DX ret got_driver: mov pkt_&name&_data.pkt_handle, AX mov BX, AX mov AH, 6 ;; get ethernet address mov DX, DS mov ES, DX mov DI, offset if_address + 8*name mov CX, 6 ;; length of address buffer stc PKT_CALL_DRIVER name jnc got_address mov DL, DH xor DH, DH mov AX, DX ret got_address: if pkt_&name&_promiscuous eq 1 mov BX, pkt_&name&_data.pkt_handle mov AH, 20 ;; set recieve mode mov CX, 6 ;; to recieve all packets stc PKT_CALL_DRIVER name jnc set_prom mov DL, DH xor DH, DH mov AX, DX ret set_prom: endif xor AX, AX sti ;; turn on interupts if they are not already on ret ;;********************************************************************* ;; 'interupt' routine pkt_&name&_data_seg: DW 0 pkt_read_task: push DS segcs mov DS, word ptr pkt_&name&_data_seg or AX, AX jz size_check QUEUE_ENQUEUE_out_DI_const_BX_CX_DX_BP_SI_ES %pkt_&name&_rqueue, done mov AX, pkt_&name&_data.pkt_bstart mov [DI+pkt_q_rstart], AX mov AX, pkt_&name&_data.pkt_bend mov [DI+pkt_q_rend], AX done: pop DS retf size_check: BUFF_CHECK_in_CX_out_SI_DI_const_BX_CX_DX_BP_ES %pkt_&name&_rbuff,no_room BUFF_GET_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %pkt_&name&_rbuff mov pkt_&name&_data.pkt_bstart, SI mov pkt_&name&_data.pkt_bend, DI mov DI, SI mov AX, DS mov ES, AX pop DS retf no_room: xor DI, DI mov ES, DI pop DS retf endif ENDM ;;****************************************************************************** ;; PKT_CLOSE ;; closes the packet driver gracefully ;; PKT_CLOSE MACRO name mov AH, 3 ;; CLOSE command mov BX, pkt_&name&_data.pkt_handle PKT_CALL_DRIVER name ENDM ;;****************************************************************************** ;; IF_R_ACCESS name, no_packet ;; IF_R_ACCESS checks for the next packet to come from the the board ;; associated with 'name' and returns a pointer to the begining of ;; an ethernet packet in BX:ES. CX holds the length of the packet ;; R_ACCESS jumps to 'no_packet' if there are no packets waiting to ;; be read in ;; PKT_IF_R_ACCESS MACRO name, no_packet .errb QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %pkt_&name&_rqueue, no_packet mov BX, [SI+pkt_q_rstart] mov CX, [SI+pkt_q_rend] sub CX, BX mov AX, DS mov ES, AX ENDM ;;****************************************************************************** ;; IF_R_FREE name ;; After the client is through processing the packet returned by ;; IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the ;; memory that the packet was in can be reused for future packets. ;; PKT_IF_R_FREE MACRO name local done .errb cli QUEUE_HEAD_out_SI_const_AX_BX_CX_DX_BP_DI_ES %pkt_&name&_rqueue, done mov DI, [SI+pkt_q_rend] BUFF_FREE_in_DI_const_AX_BX_CX_DX_BP_SI_DI_ES %pkt_&name&_rbuff QUEUE_DEQUEUE_in_SI_const_AX_BX_CX_DX_BP_DI_ES %pkt_&name&_rqueue done: sti ENDM ;;****************************************************************************** ;; PKT_IF_R_CONT name, ok ;; IF_R_CONT determines if the packet returned by R_READ in BX:ES ;; of length CX is continuous. If it is it jumps to 'ok' otherwise ;; it just returns ;; PKT_IF_R_CONT MACRO name, ok .errb jmp ok ;; is it always continuous ENDM ;;****************************************************************************** ;; IF_W_ACCESS name, no_buffer ;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The ;; pointer is returned in DI:ES. If the ouptut buffer is busy, this ;; routine will jump to 'no_buffer'. The output buffer min(CX, 1536) ;; bytes long ;; PKT_IF_W_ACCESS MACRO name, no_buffer .errb mov DI, offset pkt_&name&_wbuffer mov AX, DS mov ES, AX ENDM ;;****************************************************************************** ;; IF_W_WRITE name ;; IF_W_WRITE actually signals the ethernet board to write a packet to ;; the ethernet. The packet is assumed to be in the buffer returned by ;; IF_W_ACCESS. CX is the length of the packet to send. ;; PKT_IF_W_WRITE MACRO name .errb push BX push BP push ES mov AH, 4 ;; set_pkt command mov SI, offset pkt_&name&_wbuffer ;; the packet to send ;; DS is OK as it stands ;; CX is also fine as is PKT_CALL_DRIVER name pop ES pop BP pop BX ENDM ;;****************************************************************************** ;; IF_SET_ADDRESS name ;; IF_SET_ADDRESS_in_SI sets the hardware address to be the value ;; pointed to by SI. Note this function may be a no-op if the ;; hardware address cannot be set (ETHERNET for example) ;; PKT_IF_SET_ADDRESS MACRO name .err ;; we don't support setting ethernet addresses (yet) ENDM ;;****************************************************************************** ;; IF_COPY name ;; IF_COPY copys a packet from the input buffer (pointed ;; to by SI and the segement register given in IF_DECLARE) to an output ;; buffer (pointed to by DI and ES) of length CX. It assumes the ;; output buffer is contiguous. (and the caller shouln't care if the ;; input buffer is contiguous) ;; PKT_IF_COPY MACRO name .errb ;; since DS points to the data segment, we are ready to go!! inc CX shr CX,1 rep movsw ENDM ;;****************************************************************************** ;; PKT_CALL_DRIVER calls the drivers, but it also makes sure that the ;; segment registers are preserved. PKT_CALL_DRIVER MACRO name .errb push DS push SS int pkt_&name&_drv_int pop SS pop DS ENDM speed of a native driver. Nevertheless, this speed ;; may be acceptable, (and besides it gets all those people who want 3Com ;; support off my back). There are other inefficiencies as well (saving ;; and restoring registers, waiting for packets to be sent etc), but even ;; so it will be usefull for low performance applications. ;; ;; Note that this these routines do not use any extended or high performance ;; pac3c507.inc1006440004230000000000011034405366275370007265 037777777777 1 0 ;;****************************************************************************** ;; 3c507.inc 3c507.inc ;;****************************************************************************** ;; ;; Copyright (C) 1991 Vance Morrison ;; ;; ;; Permission to view, compile, and modify for LOCAL (intra-organization) ;; USE ONLY is hereby granted, provided that this copyright and permission ;; notice appear on all copies. Any other use by permission only. ;; ;; Vance Morrison makes no representations about the suitability ;; of this software for any purpose. It is provided "as is" without expressed ;; or implied warranty. See the copywrite notice file for complete details. ;; ;;****************************************************************************** ;; 3c507.inc holds the interface routines for the 3com etherlink 16 card. ;; ;; The functions provided by this file are ;; ;; C507_DECLARE name, io_address, seg, len, promiscuous ;; C507_DEFINE name, fail ;; C507_IF_R_ACCESS name, no_packet ;; C507_IF_R_CONT name, ok ;; C507_IF_R_FREE name ;; C507_IF_W_ACCESS name, no_buffer ;; C507_IF_W_WRITE name ;; C507_IF_SET_ADDRESS name ;; C507_IF_COPY name ;; ;; Variables set by this module ;; ;; c507_&name&_declared ;; one if this interface exists ;; if_&name&_address ;; the hardware address ;; if_&name&_mtu ;; the maximum trans unit ;; ;;****************************************************************************** ;; data storage needed by this module c507_data STRUC ;; these values are set at init only c507_base dw 0 ;; the offset value for shared memory c507_end_buff dw 0 ;; end of buffer block for packet data c507_start_buff dw 0 ;; begining of buffer block for packet data ;; set at init, and updated at FREE c507_last_rbd dw 0 ;; points to buff desc with the EOL marker c507_last_rfd dw 0 ;; points to frame desc with the EOL marker ;; set at R_ACCESS used at FREE c507_last_frame_rbd dw 0 ;; last buff descriptor in current frame c507_cur_frame dw 0 ;; pointer to current frame c507_cur_tcb dw 0 ;; current transmit command buffer c507_data ENDS ;;****************************************************************************** ;; C507_DECLARE name, io_address, seg, len, promiscuous ;; declares that there is a 3c507 ethernet card at 'io_address'. The ;; shared memory starts a the SEGMENT 'seg' and has length 'len'. ;; if 'promiscuous' is non-blank and non-zero then every packet on ;; the ethernet is returned. ;; c507_first = 0 ;; the first 3c507 card. C507_DECLARE MACRO name, io_address, seg, len, promiscuous .errb .errb .DATA c507_&name&_declared = 1 c507_&name&_io = io_address c507_&name&_seg = seg c507_&name&_len = len c507_&name&_promiscuous = 0 ifnb c507_&name&_promiscuous = 0&promiscuous endif if (name le c507_first) or (c507_first eq 0) c507_first = name endif if_&name&_mtu = 1514 global c507_&name&_data:c507_data global if_&name&_address:word .CODE global c507_&name&_real_define:near ENDM ;;****************************************************************************** ;; IF_DEFINE name ;; sets asside memory an name object and initializes it. This ;; routine is a no-op if 'name' was not declared ;; Note that if multiple 3C507 cards are present, they must be ;; the smallest named card must be defined first (since this card ;; turns on all the cards). AX holds a failure code if the initialization ;; fails. ;; C507_DEFINE MACRO name, fail IFDEF c507_&name&_declared call c507_&name&_real_define or AX, AX jnz fail ENDIF ENDM C507_REAL_DEFINE MACRO name local ret_code, rfd_loop, found_rfd, rbd_loop, found_rbd .errb ifdef c507_&name&_declared .DATA if_&name&_address DW 3 dup (0) c507_&name&_data c507_data <> .CODE c507_&name&_real_define: ;; put all card in run state (only done for first card) if name eq c507_first xor AX, AX PORT_WRITE C507_ID_PORT, 0 C507_SEND_ID_const_BX_BP_SI_DI_ES C507_ID_PORT xor AX, AX PORT_WRITE C507_ID_PORT, 0 endif ;; get the ethernet address mov SI, offset if_&name&_address C507_GET_ADDRESS_in_SI_const_BX_BP_DI_ES c507_&name&_io C507_INIT_SETUP_out_ES c507_&name&_io, c507_&name&_seg, c507_&name&_len, if_&name&_address, c507_&name&_promiscuous, ret_code mov c507_&name&_data.c507_base, ES ;; initialize my state variables mov SI, ES:[SCB+scb_rframes] mov c507_&name&_data.c507_cur_frame, SI mov DI, ES:[SI+rfd_rbd] ;; DI points to the first buffer descriptor, we ;; assume that this rbd points to the begining ;; of the large block of memory mov DX, ES:[DI+rbd_buff.offs] mov c507_&name&_data.c507_start_buff, DX ;; find the last element of the Buffer list rbd_loop: test ES:[DI+rbd_len], RBD_LEN_EOL jnz found_rbd mov DI, ES:[DI+rbd_next] jmp rbd_loop found_rbd: mov c507_&name&_data.c507_last_rbd, DI mov AX, ES:[DI+rbd_len] ;; DI points to the last buffer descriptor. we ;; assume that this rbd points to the end of the ;; large block of memory. and AX, RBD_LEN_MSK mov DX, ES:[DI+rbd_buff.offs] add DX, AX mov c507_&name&_data.c507_end_buff, DX ;; find the last element of the Frame list mov DI, ES:[SCB+scb_rframes] rfd_loop: test ES:[DI+rfd_cmd], RFD_CMD_EOL jnz found_rfd mov DI, ES:[DI+rfd_next] jmp rfd_loop found_rfd: mov c507_&name&_data.c507_last_rfd, DI mov c507_&name&_data.c507_cur_tcb, CU_TCMD1 xor AX, AX ret_code: ret endif ENDM ;;****************************************************************************** ;; IF_R_ACCESS name, no_packet ;; IF_R_ACCESS waits for the next packet to come from the the board ;; associated with 'name' and returns a pointer to the begining of ;; an ethernet packet in BX:ES. CX holds the length of the packet ;; R_ACCESS jumps to 'no_packet' if there are no packets waiting to ;; be read in ;; C507_IF_R_ACCESS MACRO name, no_packet local look_packet, found_packet, len_loop, last_buff .errb mov ES, c507_&name&_data.c507_base mov SI, c507_&name&_data.c507_cur_frame look_packet: mov AX, ES:[SI+rfd_status] test AX, RFD_STAT_DONE jz no_packet test AX, RFD_STAT_OK jnz found_packet mov SI, ES:[SI+rfd_next] jmp look_packet found_packet: mov c507_&name&_data.c507_cur_frame, SI mov SI, ES:[SI+rfd_rbd] mov BX, ES:[SI+rbd_buff.offs] ;; compute the length of the packet, as well as if it wrapped or not xor CX, CX len_loop: mov AX, ES:[SI+rbd_used_len] test AX, RBD_USED_LEN_EOF jnz last_buff and AX, RBD_USED_LEN_MSK add CX, AX mov SI, ES:[SI+rbd_next] jmp len_loop last_buff: and AX, RBD_USED_LEN_MSK add CX, AX mov c507_&name&_data.c507_last_frame_rbd, SI ENDM ;;****************************************************************************** ;; IF_R_FREE name ;; After the client is through processing the packet returned by ;; IF_R_ACCESS, IF_R_FREE must be called to inform 'name' that the ;; memory that the packet was in can be reused for future packets. ;; C507_IF_R_FREE MACRO name local ok_rbd, do_restart, no_restart, look_first_ndone, found_restart .errb mov DX, SI ;; save SI mov AX, ES xchg AX, c507_&name&_data.c507_base ;; load/save ES mov ES, AX mov SI, c507_&name&_data.c507_last_frame_rbd or ES:[SI+rbd_len], RBD_LEN_EOL mov SI, c507_&name&_data.c507_last_rbd and ES:[SI+rbd_len], (NOT RBD_LEN_EOL) mov SI, c507_&name&_data.c507_last_frame_rbd mov c507_&name&_data.c507_last_rbd, SI mov SI, c507_&name&_data.c507_cur_frame mov AX, SI mov ES:[SI+rfd_status], 0 or ES:[SI+rfd_cmd], RFD_CMD_EOL mov SI, ES:[SI+rfd_next] mov c507_&name&_data.c507_cur_frame, SI mov SI, c507_&name&_data.c507_last_rfd mov ES:[SI+rfd_cmd], 0 mov c507_&name&_data.c507_last_rfd, AX mov AX, ES:[SCB+scb_status] ;; is the reciever still recieving? and AX, SCB_STAT_RUS_MSK cmp AX, SCB_STAT_RUS_READY jz no_restart mov SI, c507_&name&_data.c507_cur_frame mov AX, 16 ;; only look forward 16 packets at most look_first_ndone: test ES:[SI+rfd_status], RFD_STAT_DONE jz found_restart mov SI, ES:[SI+rfd_next] dec AX jnz look_first_ndone found_restart: mov ES:[SCB+scb_rframes], SI mov ES:[SI+rfd_status], 0 cmp word ptr ES:[SI+rfd_rbd], 0FFFFH jnz ok_rbd mov SI, c507_&name&_data.c507_last_rfd mov AX, ES:[SI+rfd_rbd] mov SI, ES:[SCB+scb_rframes] mov ES:[SI+rfd_rbd], AX ok_rbd: mov ES:[SCB+scb_cmd], SCB_RUC_START ;; signal the 82586 PORT_WRITE c507_&name&_io, C507_IO_ATTN no_restart: mov SI, DX ;; restore SI mov AX, ES xchg AX, c507_&name&_data.c507_base ;; restore ES mov ES, AX ENDM ;;****************************************************************************** ;; C507_IF_R_CONT name, ok ;; IF_R_CONT determines if the packet returned by R_READ in BX:ES ;; of length CX is continuous. If it is it jumps to 'ok' otherwise ;; it just returns ;; C507_IF_R_CONT MACRO name, ok .errb mov AX, BX add AX, CX cmp AX, c507_&name&_data.c507_end_buff jb ok ENDM ;;****************************************************************************** ;; IF_W_ACCESS name, no_buffer ;; IF_W_ACCESS returns a pointer to an output buffer for a packet. The ;; pointer is returned in DI:ES. If the ouptut buffer is busy, this ;; routine will jump to 'no_buffer'. The output buffer min(CX, 1536) ;; bytes long ;; C507_IF_W_ACCESS MACRO name, no_buffer local wait_busy, buffer_free, com_accepted, wait_com_accept, nocarry .errb ;; we ignore CX and always return a buffer 1536 bytes long mov ES, c507_&name&_data.c507_base mov SI, c507_&name&_data.c507_cur_tcb cmp ES:[SI+cu_status], 0 ;; has the RU done ANYTHING with stat jnz com_accepted ;; it may be 0 if the CU hasn't even accepted the command ;; check for this and wait if necessary xor DX, DX ;; so we don't loop forever wait_com_accept: test ES:[SCB+scb_cmd], SCB_CMD_CUC_MSK jz com_accepted dec DX jnz wait_com_accept com_accepted: xor DX, DX ;; so we don't loop forever wait_busy: test ES:[SI+cu_status], CU_STAT_BUSY jz buffer_free dec DX jnz wait_busy buffer_free: mov SI, ES:[SI+cu_params+trans_tbd] mov DI, ES:[SI+tbd_buff.offs] ENDM ;;****************************************************************************** ;; IF_W_WRITE name ;; IF_W_WRITE actually signals the ethernet board to write a packet to ;; the ethernet. The packet is assumed to be in the buffer returned by ;; IF_W_ACCESS. CX is the length of the packet to send. ;; C507_IF_W_WRITE MACRO name local not_tcmd1, check_prev_com, no_prev_com, wait_busy, buffer_free .errb mov DX, ES ;; save ES mov ES, c507_&name&_data.c507_base mov DI, c507_&name&_data.c507_cur_tcb ;; make any previous command has been recognized before ;; sending this next one. xor AX, AX check_prev_com: test ES:[SCB+scb_cmd], SCB_CMD_CUC_MSK jz no_prev_com dec AX jnz check_prev_com no_prev_com: xor AX, AX wait_busy: test ES:[DI+cu_status], CU_STAT_BUSY jz buffer_free dec AX jnz wait_busy buffer_free: mov ES:[DI+cu_status], 0 mov ES:[DI+cu_cmd], CU_CMD_EOL+CU_CMD_TRANS ;; probably unnecessary mov ES:[DI+cu_params+trans_len], CX ;; probably unnecessary mov SI, ES:[DI+cu_params+trans_tbd] add CX, TBD_LEN_EOL mov ES:[SI+tbd_len], CX ;; send our transmit command mov SI, offset SCB mov ES:[SI+scb_status], 0 mov ES:[SI+scb_cuc], DI mov ES:[SI+scb_cmd], SCB_CUC_START PORT_WRITE c507_&name&_io, C507_IO_ATTN ;; update the tcb pointer to the 'other' command block mov AX, CU_TCMD1 cmp AX, DI jnz not_tcmd1 mov AX, CU_TCMD2 not_tcmd1: mov c507_&name&_data.c507_cur_tcb, AX mov ES, DX ;; restore ES ENDM ;;****************************************************************************** ;; IF_SET_ADDRESS name ;; IF_SET_ADDRESS sets the hardware address to be the value ;; pointed to by SI. Note this function may be a no-op if the ;; hardware address cannot be set (ETHERNET for example) ;; C507_IF_SET_ADDRESS MACRO name .err ;; we don't support setting ethernet addresses (yet) ENDM ;;****************************************************************************** ;; IF_COPY name ;; IF_COPY copys a packet from the input buffer (pointed ;; to by SI and the segement register given in IF_DECLARE) to an output ;; buffer (pointed to by DI and dest_reg) of length CX. It assumes the ;; output buffer is contiguous. (and the caller shouln't care if the ;; input buffer is contiguous) ;; C507_IF_COPY MACRO name LOCAL wrap, done .errb mov DX, DS ;; save DS mov AX, c507_&name&_data.c507_end_buff sub AX, SI ;; space till end cmp AX, CX jbe wrap inc CX shr CX, 1 mov DS, c507_&name&_data.c507_base rep movsw jmp done wrap: xchg AX, CX sub AX, CX mov DS, c507_&name&_data.c507_base inc CX shr CX, 1 rep movsw mov CX, AX mov DS, DX ;; remember this instruction rely on DS mov SI, c507_&name&_data.c507_start_buff mov DS, c507_&name&_data.c507_base inc CX shr CX, 1 rep movsw done: mov DS, DX ;; restore DS ENDM ;;****************************************************************************** ;; These macros and definitions are specific to the 3C507 C507_ID_PORT = 100H C507_IO_ADDR = 00H C507_IO_CTR = 06H C507_IO_INT_CLEAR = 0AH C507_IO_ATTN = 0BH C507_IO_ROM = 0DH C507_IO_RAM = 0EH C507_IO_INT = 0FH ;; values for teh IO_CTR reg C507_CTR_RUN = 080H ;; 1 = run 0 = reset C507_CTR_CA = 040H ;; obsolete way of to a chan attn C507_CTR_LOOP = 020H ;; put in loopback C507_CTR_LAD = 010H ;; LA address decode disable C507_CTR_INT = 008H ;; interupt is pending (read only) C507_CTR_IEN = 004H ;; interupt enable C507_CTR_PG_MSK = 003H ;; controls first 6 locations ;; values for the CTR_PG field C507_PG_3COM = 00H ;; page that has '*3com*' in it C507_PG_ETHER = 01H ;; page ethernet address in it ;;******************************************************************* ;; This macro sends the ID sequence that the 3Com 3C507 responds to. ;; to the I/O address 'port'. C507_SEND_ID_const_BX_BP_SI_DI_ES MACRO port local id_loop, no_xor mov CX, 0FFH mov AL, 0FFH id_loop: PORT_WRITE port, 0 shl AL, 1 jnc no_xor xor AL, 0E7H no_xor: loop id_loop ENDM ;;***************************************************************** ;; this routine gets the ethernet address from the 3Com I/O registers ;; and places it in the buffer pointed to by SI C507_GET_ADDRESS_in_SI_const_BX_BP_DI_ES MACRO port local eaddr_loop ;; get the ethernet address mov AL, C507_CTR_RUN+C507_PG_ETHER PORT_WRITE port, C507_IO_CTR mov CX, 6 mov DX, port+C507_IO_ADDR eaddr_loop: in AL, DX mov [SI], AL inc DX inc SI loop eaddr_loop ENDM ;;******************************************************************** ;; These definitions have more to do with the 82586 longptr struc ;; just allows me to get at 8086 long ptrs easily offs dw ? segm dw ? longptr ends ;;********************************************************************* ;; This structure is always at a fixed location, and points to ;; the system control pointer i82586_root struc ;; the root pointer root_bus dw 0 root_zero db 4 dup (0) root_scp dd ? i82586_root ends I82586_FIXED_ROOT = 0FFF6H ;; this is the fixed position ;;********************************************************************* ;; The system control pointer, in turn, points to the ;; system control block i82586_scp struc ;; the system control pointer scp_busy dw 0 ;; 82586 sets to 0 when reset complete scp_scb dw 0 ;; offset to system control block scp_base dd 0 ;; base for all 16 bit pointers i82586_scp ends ;; the system control block ;;********************************************************************* ;; The system control block is the heart of the communication ;; between the CPU and the 82586. The 82586 has a control ;; unit (CU) which you can issue commands to, and a recieve ;; unit (RU) that buffers packets. Commands to these units ;; are given from this structure. i82586_scb struc ;; the system control block scb_status dw 0 ;; RU and CU status scb_cmd dw 0 ;; RU and/or CU command scb_cuc dw 0 ;; CU command (list) scb_rframes dw 0 ;; buffers for RU scb_crc_errs dw 0 ;; CRC error count scb_aln_errs dw 0 ;; Alignment error count scb_rcs_errs dw 0 ;; Resource error (queue filled) scb_ovrn_errs dw 0 ;; Overruns (memory busy) i82586_scb ends ;; masks for the scb_status field SCB_STAT_CX = 08000H ;; command with interupt executed SCB_STAT_FR = 04000H ;; Frame recieved SCB_STAT_CNR = 02000H ;; command unit left active state SCB_STAT_RNR = 01000H ;; receive unit left active state SCB_STAT_CUS_MSK = 00700H ;; command status mask SCB_STAT_RUS_MSK = 00070H ;; reciever status mask ;; these are the values for teh RUS_MSK field SCB_STAT_RUS_IDLE = 00000H ;; reciever idle SCB_STAT_RUS_SUSP = 00010H ;; reciever suspended SCB_STAT_RUS_NORES = 00020H ;; reciever no_resources SCB_STAT_RUS_READY = 00040H ;; reciever ready ;; masks for the scb_cmd (set by CPU cleared by 82586) SCB_CMD_ACK_CX = 08000H ;; acks command executed SCB_CMD_ACK_FR = 04000H ;; acks frame received SCB_CMD_ACK_CNA = 02000H ;; acks CU not ready SCB_CMD_ACK_RNR = 01000H ;; acks RU not ready SCB_CMD_CUC_MSK = 00700H ;; CU command mask SCB_CMD_RST = 00080H ;; reset the 82586 SCB_CMD_RUC_MSK = 00070H ;; RU command mask ;; values for the CUC_MSK part of the scb_cmd field SCB_CUC_START = 00100H ;; start a CU command SCB_CUC_RESUME = 00200H SCB_CUC_SUSPEND = 00300H SCB_CUC_ABORT = 00400H ;; values for the RUC_MSK part of the scb_cmd field SCB_RUC_START = 00010H ;; start RU receiving packets SCB_RUC_RESUME = 00020H SCB_RUC_SUSPEND = 00030H SCB_RUC_ABORT = 00040H ;;********************************************************************* ;; the scb_cuc points to a list of commands for the CU ;; to execute. Each entry in the list has this format i82586_cu struc cu_status dw 0 ;; status of this command cu_cmd dw 0 ;; specifies the op cu_next dw 0 ;; next cmd in the list cu_params db 58 dup (0) ;; depends on particular cmd i82586_cu ends ;; values for the cu_status field CU_STAT_COMPLETE = 08000H ;; command is completed CU_STAT_BUSY = 04000H ;; command not complete CU_STAT_OK = 02000H ;; finished and OK ;; values for the cu_cmd field CU_CMD_CMD_MSK = 00007H ;; mask for the command CU_CMD_EOL = 08000H ;; last command list in list CU_CMD_SUSPEND = 04000H ;; suspend after completion CU_CMD_INT = 02000H ;; interupt after completion ;; values for the CMD_MSK parat of the cu_cmd field CU_CMD_NOOP = 0 ;; noop command CU_CMD_ADDRESS_SET = 1 ;; set ethernet address CU_CMD_CONFIG = 2 ;; configure ethernet params CU_CMD_MULTI_SET = 3 ;; set multicast addresses CU_CMD_TRANS = 4 ;; transmit packet CU_CMD_TDR = 5 ;; Time domain reflectometer CU_CMD_DUMP = 6 ;; dump internal state CU_CMD_DIAGNOSE = 7 ;; run diagnostics ;;******************************************************************* ;; these are the parameters of the TRAMSMIT command i82586_trans struc trans_tbd dw 0 ;; points to buffer descriptor trans_dst_addr db 6 dup (0) ;; address to send it to (not used) trans_len dw 0 ;; 802.3 length field (not used) i82586_trans ends ;; the first parameter of a transmit command points to a list ;; of transmit buffer discriptors that characterize the ;; packet to send i82586_tbd struc tbd_len dw 0 ;; length and EOL flag tbd_next dw 0 ;; pointer to next descriptor tbd_buff dd 0 ;; a long pointer to the buffer i82586_tbd ends ;; the bit fields bit in the tbd_len field TBD_LEN_EOL = 08000H ;; Last buff in list TBD_LEN_LEN_MSK = 03FFFH ;; this part is the length ;;******************************************************************* ;; the scb_rfd field points to a list of frame descriptors. ;; each of these describe a single packet i82586_rfd struc rfd_status dw 0 ;; recieve status rfd_cmd dw 0 ;; really just specifies EOL rfd_next dw 0 ;; next frame descriptor rfd_rbd dw 0 ;; pointer to buff descriptor rfd_dst_addr db 6 dup (0) ;; ethernet address of destination rfd_src_addr db 6 dup (0) ;; ethernet address of source rfd_len dw 0 ;; length of packet i82586_rfd ends RFD_STAT_DONE = 08000H RFD_STAT_CONSUMED = 04000H RFD_STAT_OK = 02000H RFD_STAT_CRC = 00800H RFD_STAT_ALN = 00400H RFD_STAT_RSC = 00200H RFD_STAT_OVRN = 00100H RFD_STAT_RUNT = 00080H RFD_CMD_EOL = 08000H ;; End of frame list ;; the rdb_buff field points to a buffer descriptor, which is ;; a unit of memory allocation. i82586_rbd struc rbd_used_len dw 0 ;; also hold some flag bits rbd_next dw 0 ;; next buffer descriptor rbd_buff dd 0 ;; pointer to actual memory rbd_len dw 0 ;; length of buffer i82586_rbd ends ;; masks for the rdb_used_len field RBD_USED_LEN_EOF = 08000H ;; Last buff in a frame list RBD_USED_LEN_VALID = 04000H ;; says the used_len field valid RBD_USED_LEN_MSK = 03FFFH ;; this part is the used length RBD_LEN_MSK = 03FFFH ;; this part is the used length RBD_LEN_EOL = 08000H ;; End of buffer list ;;*********************************************************************** ;; setup the chain of recieve buffers. SI points to a block ;; of memory 'len'*(SIZE i82586_rfd) bytes long. Note 1 < CX < 16K C507_SETUP_R_FRAMES_in_CX_SI_ES_const_AX_BP_ES MACRO local init_loop ;; create a circular list of receive buffers mov DX, SI ;; save original pointer mov DI, SI add DI, (SIZE i82586_rfd) dec CX init_loop: ;; set up a frame descriptor mov ES:[SI+rfd_status], 0 mov ES:[SI+rfd_cmd], 0 ;; says NOT end of list mov ES:[SI+rfd_next], DI mov ES:[SI+rfd_rbd], 0FFFFH add SI, (SIZE i82586_rfd) add DI, (SIZE i82586_rfd) dec CX jnz init_loop mov ES:[SI+rfd_cmd], RFD_CMD_EOL ;; logical end of list mov ES:[SI+rfd_next], DX ;; close the loop mov ES:[SI+rfd_rbd], 0FFFFH ENDM ;;*********************************************************************** ;; setup the chain of recieve buffers. BX points to the begining of ;; a block of memory 'len'* CX bytes long and SI points to a block ;; of memory 'len'*(SIZE i82586_rbd) bytes long. Note 1 < CX < 16K ;; it returns in SI the start of the list of buffer descriptors. C507_SETUP_R_BUFFS_in_BX_CX_SI_ES_out_SI_const_AX_BP_ES MACRO len local init_loop ;; create a circular list of receive buffers mov DX, SI ;; save original pointer mov DI, SI add DI, (SIZE i82586_rbd) dec CX init_loop: ;; set up a buffer descriptor mov ES:[SI+rbd_next], DI mov ES:[SI+rbd_buff.offs], BX mov ES:[SI+rbd_buff.segm], 0 mov ES:[SI+rbd_len], len add SI, (SIZE i82586_rbd) add DI, (SIZE i82586_rbd) add BX, len dec CX jnz init_loop mov ES:[SI+rbd_next], DX ;; close the loop mov ES:[SI+rbd_buff.offs], BX mov ES:[SI+rbd_buff.segm], 0 mov ES:[SI+rbd_len], len+RBD_LEN_EOL ;; logical end of list mov SI, DX ;; return the first one ENDM ;;*********************************************************************** ;; setup the data structure needed for the receiver. CX, ES:SI is the ;; len and begining of shared memory in which to set it up. It returns ;; in SI the pointer that belongs in the scb_rframes field of the system ;; control block 'len' is the length of a buffer C507_SETUP_R_MEM_in_CX_SI_ES_out_SI_const_ES MACRO len ;; since we allocate two buffers for every frame TOTAL_BUFF_LEN = len + (SIZE i82586_rbd) + (SIZE i82586_rfd) mov AX, CX sub AX, (SIZE i82586_rfd) ;; Want an extra frame desc xor DX, DX mov BX, TOTAL_BUFF_LEN div BX mov CX, AX ;; AX holds the number of buffers mov BX, SI ;; BX start of buffer space mov DX, len mul DX add SI, AX ;; SI start of buff desc mov BP, SI mov AX, CX mov DX, (SIZE i82586_rbd) mul DX add BP, AX ;; BP points to the start of frame desc mov AX, CX ;; CX holds the number of buffers inc AX ;; AX holds the number of frames C507_SETUP_R_BUFFS_in_BX_CX_SI_ES_out_SI_const_AX_BP_ES len mov CX, AX mov AX, SI ;; save pointer to start of buffer list mov SI, BP C507_SETUP_R_FRAMES_in_CX_SI_ES_const_AX_BP_ES mov SI, BP ;; return final pointer to frames mov ES:[SI+rfd_rbd], AX ;; link the frames to the buffs ENDM ;;*********************************************************************** ;; setup the data structures needed to communicate with the 82586. ;; SI:ES points to the shared memory window. Note that We only use ;; the memory ABOVE SI in the ES segment, thus if SI=C000 there is ;; a 4K window. In addition, some data structures are in 'standard' ;; positions that can be used directly. ;; SCP = 0FFE0H ;; place for system control ptr SCB = (SCP-16) ;; place for system control block CU_GCMD = (SCB-32) ;; generic command (32 bytes long) CU_TCMD1 = (CU_GCMD-32) ;; first trans command (32 bytes long) CU_TCMD2 = (CU_TCMD1-32) ;; second trans command (32 bytes long) CU_TBD1 = (CU_TCMD2-8) ;; first Transmit buffer descriptor CU_TBD2 = (CU_TBD1-8) ;; second Transmit buffer descriptor CU_TB1 = (CU_TBD2-1536);; first Trans buffer (1536 bytes long) CU_TB2 = (CU_TB1-1536) ;; second Trans buffer (1536 bytes long) START_RESERVED = CU_TB2 ;; start of fixed allocated memory LEN_R_BUFF = 256 ;; this is a optimization param ;;************************************************************************* ;; SETUP_MEM sets up all the shared memory data structures that the 82586 ;; uses SI:ES points the the begining of the memory. It is assumed that ;; the memory ranges is ES:SI to ES:FFFF ;; C507_SETUP_MEM_in_SI_ES_const_ES MACRO mov DI, SI ;; zero out memory, it makes reading xor AX, AX ;; dumps easier mov DX, SI shr DX, 1 mov CX, 8000H sub CX, DX rep stosw ;; setup ROOT mov BX, offset I82586_FIXED_ROOT mov ES:[BX+root_bus], 0 mov ES:[BX+root_scp.offs], offset SCP mov ES:[BX+root_scp.segm], 0 ;; setup SCP mov BX, offset SCP mov ES:[BX+scp_busy], 1 ;; set the busy bit mov ES:[BX+scp_scb], SCB mov ES:[BX+scp_base.offs], 0 mov ES:[BX+scp_base.segm], 0 ;; setup SCB mov BX, offset SCB mov ES:[BX+scb_cmd], 0 mov ES:[BX+scb_status], 0 mov ES:[BX+scb_crc_errs], 0 mov ES:[BX+scb_aln_errs], 0 mov ES:[BX+scb_rcs_errs], 0 mov ES:[BX+scb_ovrn_errs], 0 mov ES:[BX+scb_cuc], offset CU_GCMD ;; get the length of the rest of memory mov CX, START_RESERVED sub CX, SI C507_SETUP_R_MEM_in_CX_SI_ES_out_SI_const_ES LEN_R_BUFF mov BX, offset SCB mov ES:[BX+scb_rframes], SI ;; setup transmit buffers descriptors mov BX, CU_TCMD1 mov ES:[BX+cu_cmd], CU_CMD_EOL+CU_CMD_TRANS mov ES:[BX+cu_params+trans_tbd], CU_TBD1 mov BX, CU_TCMD2 mov ES:[BX+cu_cmd], CU_CMD_EOL+CU_CMD_TRANS mov ES:[BX+cu_params+trans_tbd], CU_TBD2 mov BX, CU_TBD1 mov ES:[BX+tbd_buff.offs], offset CU_TB1 mov ES:[BX+tbd_buff.segm], 0 mov BX, CU_TBD2 mov ES:[BX+tbd_buff.offs], offset CU_TB2 mov ES:[BX+tbd_buff.segm], 0 ENDM ;;*********************************************************************** ;; DO_CMD simply executes the command 'command'. It assumes that all ;; parameters to the command have been set up in the DO_CMD_PARAMS ;; structure below. This command waits for completion and jumps to ;; 'fail' if unsuccessful. (note that DO_CMD_PARAMS points to the ;; command specific part of the command. ;; Note also that this command should not be used if it is possible ;; that other commands are in progress DO_CMD_PARAMS = (CU_GCMD+6) C507_DO_CMD_in_ES_const_BP_SI_DI_ES MACRO command, port, fail local waitloop, done .errb mov BX, offset SCB mov ES:[BX+scb_status], 0 mov ES:[BX+scb_cuc], offset CU_GCMD mov ES:[BX+scb_cmd], SCB_CUC_START mov BX, offset CU_GCMD mov ES:[BX+cu_status], 0 mov ES:[BX+cu_cmd], CU_CMD_EOL+command ;; we assume that the rest of the command is set up ;; signal the 82586 PORT_WRITE port, C507_IO_ATTN xor CX, CX ;; don't wait forever waitloop: dec CX jz fail mov AX, ES:[BX+cu_status] test AX, CU_STAT_COMPLETE jz waitloop ;; wait for completion test AX, CU_STAT_OK jz fail ENDM ;;*********************************************************************** ;; do the intial setup of the 3C507 card C507_RESET_82586_in_ES MACRO port, fail local reset_wait, reset_done, reset_loop ;; reset the 82586 mov ES:[SCP+scp_busy], 1 ;; set the busy bit mov AL, 0 PORT_WRITE port, C507_IO_CTR nop ;; wait 10 clock cycles nop ;; just in case there was an interupt pending, clear it PORT_WRITE port, C507_IO_INT_CLEAR mov AL, C507_CTR_RUN+C507_PG_ETHER PORT_WRITE port, C507_IO_CTR nop nop PORT_WRITE port, C507_IO_ATTN mov CX, 256 reset_loop: cmp ES:[SCP+scp_busy], 0 jz reset_done loop reset_loop jmp fail reset_done: ENDM ;;*********************************************************************** ;; do the intial setup of the 3C507 card C507_INIT_SETUP_out_ES MACRO port, segment, len, addr, promiscuous, fail local eaddr_loop, done, diag_fail, config_fail, setaddr_fail local reset_fail, ru_on_fail, wait_ru_on, done_wait_ru .errb ;; adjust ES so that ES:SI point to begining and ES:FFFF the end mov AX, segment if ((len/16) ne 0) ;; This is a kludge because the assembler wraps 10000H sub AX, (1000H - (len / 16)) endif mov ES, AX mov SI, 10000H - len ;; test the memory just a bit mov ES:[SI], 5F75H mov DX, ES:[SI] mov AX, 0101H ;; signals a memory check error type 1 cmp DX, 5F75H jnz fail mov ES:[0FFFEH], 57A5H mov DX, ES:[0FFFEH] mov AX, 0102H ;; signals a memory check error type 2 cmp DX, 57A5H jnz fail C507_SETUP_MEM_in_SI_ES_const_ES ;; just to test the board is there, get the first char ;; of the '*3com*' identifier from the I/O ROM. mov AL, C507_CTR_RUN+C507_PG_3COM PORT_WRITE port, C507_IO_CTR PORT_READ port, C507_IO_ADDR mov DL, AL mov AX, 0201H ;; signals 82586 not there cmp DL, '*' ;; should be part of *3com*' string jnz fail C507_RESET_82586_in_ES port reset_fail ;; check the setup by doing a diag C507_DO_CMD_in_ES_const_BP_SI_DI_ES CU_CMD_DIAGNOSE, port, diag_fail ;; configure ethernet params mov BX, offset DO_CMD_PARAMS mov word ptr ES:[BX+0], 0080CH ; fifo=8 byte count=12 mov word ptr ES:[BX+2], 02E00H ; preamble=4, add_len=6, DONT ins headers mov word ptr ES:[BX+4], 06000H ; interframe spacing = 60h mov word ptr ES:[BX+6], 0F200H ; retry = 15, slot time = 200h if promiscuous eq 1 mov word ptr ES:[BX+8], 1 ; flags bit 1 means promiscuous else mov word ptr ES:[BX+8], 0 ; flags bit 1 means promiscuous endif mov word ptr ES:[BX+10], 0003CH ; minimum frame length = 60 C507_DO_CMD_in_ES_const_BP_SI_DI_ES CU_CMD_CONFIG, port, config_fail ;; set the ethernet address mov DI, offset DO_CMD_PARAMS mov SI, offset addr mov CX, 3 rep movsw C507_DO_CMD_in_ES_const_BP_SI_DI_ES CU_CMD_ADDRESS_SET, port, setaddr_fail mov BX, offset SCB mov ES:[BX+scb_status], 0 mov ES:[BX+scb_cmd], SCB_RUC_START ;; signal the 82586 PORT_WRITE port, C507_IO_ATTN xor CX, CX wait_ru_on: dec CX mov AX, ES:[BX+scb_status] and AX, SCB_STAT_RUS_MSK jnz done_wait_ru dec CX jnz wait_ru_on done_wait_ru: cmp AX, SCB_STAT_RUS_READY jz done ru_on_fail: mov AX, 0206H ;; failed to turn RU on jmp fail reset_fail: mov AX, 0202H ;; failed resetting the 82586 jmp fail diag_fail: mov AX, 0203H ;; signals 82586 failed noop test jmp fail config_fail: mov AX, 0204H ;; signals 82586 failed config jmp fail setaddr_fail: mov AX, 0205H ;; signals 82586 failed set addr jmp fail done: ENDM ;; ----------------- end of file ------------- mov ES:[SI+rbd_buff.offs], BX mov ES:[SI+rbd_buff.segm], 0 mov ES:[SI+rbd_len], len add SI, (SIZE i82586_rbd) add DI, (SIZE i82586_rbd) add BX, len dec CX jnz init_loop mov ES:[SI+rbd_next], DX ;; close the loopproc.asm1006440004230000000000001713005372243022007457 037777777777 1 0 ;;------------ proc.asm ----------- ;; ;; Misc. procedures for PCbridge ;; 05may93 Luigi Rizzo (luigi@iet.unipi.it) .CODE ;;------------------------------------------------------------- ;; print_string prints a set of strings using int10. Use '|' as ;; string terminator, empty string as end of strings ;; Registers ;; In: DS:SI (string pointer) ;; Out: -- ;; Chng: AL, CX, DX, SI, BP (INT 10h) ;; print_string PROC cicl: mov dx, [si] cmp dl, 80 ;; check off-limits columns jae fin cmp dh, 25 ;; check off-limits rows jae fin add si, 2 ;; read chars in the string mov bp, si mov cx, -1 legg: mov al, byte ptr [si] inc si inc cx cmp al,'|' ;; compare if char is delimiter parameter jne legg mov ax, 1300h int 10h mov al, byte ptr [si] cmp al,'|' ;; compare if char is end of procedure jne cicl fin: RET print_string ENDP .CODE ;;------------------------------------------------------------- ;; print_stat prints quickly a counter writing to video RAM ;; each counter is 8 bytes: 2 bytes pos, 6 bytes counter ;; Registers ;; In: DS:BP (counter index) ;; Out: ES=video segment ;; Chng: BX, SI, DI, BP, ES ;; print_stat PROC shl bp,3 ;; address counter add bp, offset counter_base print_stat_1 LABEL near ;; don't recompute BP mov bx,[bp] ;; bx holds Video offset add bp,2 ;; bp holds counter address mov si, 5 ;; si = counter bytes to display mov es, pos_vdrm back: mov al, [bp][si] ;; al = two digits exadecimal xor ah, ah add ax,ax mov di, ax ;; DI=2*AL mov ax, tab[di] ;; AX = ASCII code two digits to display mov es:[bx], al add bx, 2 mov es:[bx], ah add bx, 2 dec si jns back print_stat_end: ret PRINT_STAT ENDP .CODE ;;------------------------------------------------------------ ;; init_tab builds a table of hex strings ;; Registers: ;; In: -- ;; Out: -- ;; Change: all init_tab PROC ;; builds a table of hex strings mov cx,0 ;; 0..255 mov si, offset tab ;; base address init_tab_again: mov al,cl ;; high nibble first shr al,4 and al,0fh cmp al,10 jb good_h add al,7 good_h: mov ah,cl and ah,0fh cmp ah,10 jb good_l add ah,7 good_l: add ax,3030h mov [si],ax inc si inc si inc cx cmp cx,256 jb init_tab_again ret init_tab ENDP .CODE ;;---------------------------------------------------- ;; print_mask print the startup screen after cleaning page 0 ;; uses BIOS 10h ;; Registers: ;; In: -- ;; Out: -- ;; Change: all print_mask PROC mov es, pos_vdrm ;; es = video ram segment. First try mono mov byte ptr es:0, 0aah cmp byte ptr es:0, 0aah je found_vdrm mov pos_vdrm, 0b800h ;; then try color.. mov es, pos_vdrm mov byte ptr es:0, 0aah cmp byte ptr es:0, 0aah je found_vdrm ;; mov pos_vdrm,0 ;; not found... found_vdrm: mov cx, 800h ;; clear video memory xor di, di mov ax, 0e20h ;; bleu background, write ' ' rep stosw ;; write in video memory mov ax, cs ;; ES:si points to the string mov es, ax mov bx, 000eh ;; black background, yellow char mov si, offset banner CALL print_string push ds pop es mov si, offset banner+2; bridge name... mov di, offset magic_string mov cx,15 rep movsb mov byte ptr [di],0 ret print_mask ENDP ;; Print working cards .CODE print_cards PROC FOR idx, <1,2,3,4,5> ;; might be more... LOCAL no_work IFDEF c_wde_&idx&_declared or c507_&idx&_declared cmp if_found[idx], 0 jz no_work mov bp, offset pos_&idx&_ha CALL print_stat_1 no_work: ENDIF ;; wde_&idx&_declared.... ENDM ret print_cards ENDP PCBRIDGE_SPORT EQU 2299 PCBRIDGE_CPORT EQU 2298 .CODE ;;---------------------------------------------------- ;; make_pkt prepares a packet for output ;; In: DS=data segment, BX=Interface number (1...5), DI=out_pkt ;;Chg: AX, BX, CX, DI, SI, ES make_pkt PROC push ds pop es ;;---------- build ethernet header -------------- mov ax,0ffffh ;; broadcast stosw stosw stosw mov port_id,bl ;; port identity. Do it now, shl bx,3 lea si, if_address[bx] ;; start of eth. address movsw movsw movsw mov ax, 8h ;; htons(DOD_IP_TYPE) stosw ;;----------------- build ip header ---------------------- mov ax,45h ;; htons(0x4500: VERS 4, IHL 5, TOS 0) stosw mov ax,eth_pkt_size-14 ;; xchg ah,al stosw mov ax, 1234h ;; ID, sequential number xchg ah,al stosw mov ax,0 ;; flags, fragment offset stosw mov ax,110fh;; (ttl=0fh, protocol=0x11, UDP) stosw ETH_CHKSUM EQU (4500h+eth_pkt_size-14+1234h+0f11h);; mod 65535 mov ax,0ffffh-ETH_CHKSUM xchg ah,al stosw mov ax,0h ;; src. address stosw stosw mov ax,0ffffh ;; dest. address stosw stosw ;;----------------- build udp header ------------------ mov ax, PCBRIDGE_CPORT ;; src port. xchg ah,al stosw mov ax, PCBRIDGE_SPORT ;; dst port. xchg ah,al stosw mov ax,eth_pkt_size-34 ;; no PHYS and IP headers xchg ah,al stosw mov ax,0 ;; no checksum stosw ret make_pkt ENDP ;;------------------------------------------------------------ ;; process_pkt processes incoming packets. ;; Registers: ;; In: ES:BX buffer, CX=len, BP=input port index ;; Out: -- ;; Change: all process_pkt PROC cmp word ptr es:[bx+36], LOW(PCBRIDGE_CPORT)*256+HIGH(PCBRIDGE_CPORT) jne process_pkt_end cmp word ptr es:[bx+42], 'L'+ 'R'*256 ;; magic number jne process_pkt_end cmp byte ptr es:[bx+23], 11h ;; UDP type jne process_pkt_end cmp word ptr es:[bx+12], 8 ;; IP type jne process_pkt_end ;; have an IP pkt for me ;; bx+46= command. Command codes are: ;; 0: dump data segment (256 bytes, offset at bx+48) ;; 1: dump filter table (256 bytes, offset at bx+48) ;; 2: flush filter table ;; 3: cold reset of the bridge ;; bx+48 has memory address to dump mov ax, word ptr es:[bx+48] xchg ah,al ;; htons mov si, ax ;; source address mov table_data_off,si lea di,table_data ;; dest. address mov ax,word ptr es:[bx+46] xchg ah,al ;; htons mov bp, ds ;; save data segment mov es,bp ;; set dest. segment cmp al,0 ;; dump_data_seg jz dump_data_segment cmp al,1 ;; dump_table_seg jz dump_table_segment cmp al,2 ;; flush table jz flush_table cmp al,3 ;; reset bridge ;; jz reset_bdg ;; Uncomment this only if you really want it jmp process_pkt_end reset_bdg: push 0f000h push 0fff0h retf flush_table: mov es, bdg_table_ptr xor DI, DI mov CX, 32768 ;; table size mov ax, 0ffffh ;; clean the table. No card has address FFFFFFFFFFFF rep stosw ;; default - forward jmp process_pkt_end1 dump_table_segment: mov ax, bdg_table_ptr mov ds, ax ;; source is table dump_data_segment: mov cx, 256 rep movsw process_pkt_end1: mov ds,bp ;; restore ds process_pkt_end: ret process_pkt ENDP ;;------------------------------------------------------------ ;; inchksum computes checksum for a packet. Isn't used yet. ;; In: DS:SI ; buffer pointer, CX=count (bytes) ;; Out: AX=checksum ;; Change: AX, CX, DX, SI inchksum PROC mov bl,cl ; save low bit for the end shr cx,1 ; group into words xor dx,dx ; clear sum clc ;; in case xor doesn't do it chk_loop: lodsw adc dx,ax loop chk_loop adc dx,0 adc dx,0 and bl,1 jz chk_end xor ah,ah lodsb add dx,ax adc dx,0 adc dx,0 ;; is it really necessary ? chk_end: mov ax,dx ret inchksum ENDP ;; end of file stat.asm ;; --------- end of file -------------- ord ptr ES:[BX+0], 0080CH ; fifo=8 byte count=12 mov word ptr ES:[BX+2], 02E00H ; preamble=4, add_len=6, DONT ins headers mov word ptr ES:[BX+4], 06000H ; interframe spacing = 60h mov word ptr ES:[BX+6], 0F200H ; retry = 15, slot time = 200h if promiscuous eq 1 mov word ptr ES:[BX+8], 1 ; flags bit 1 means promiscuous else mov word ptr ES:[BX+8], 0 ; flags bit 1 means promiscuproc.asm1006440004230000000000001713005372243022007457 037777777777 1 0 ;;------------ proc.asm ----------- ;; ;; Misc. procedures for PCbridge ;; 05may93 Luigi Rizzo (luigi@iet.unipi.it) .CODE ;;------------------------------------------------------------- ;; print_string prints a set of strings using int10. Use '|' as ;; string terminator, empty string as end of strings ;; Registers ;; In: DS:SI (string pointer) ;; Out: -- ;; Chng: AL, CX, DX, SI, BP (INT 10h) ;; print_string PROC cicl: mov dx, [si] cmp dl, 80 ;; check off-limits columns jae fin cmp dh, 25 ;; check off-limits rows jae fin add si, 2 ;; read chars in the string mov bp, si mov cx, -1 legg: mov al, byte ptr [si] inc si inc cx cmp al,'|' ;; compare if char is delimiter parameter jne legg mov ax, 1300h int 10h mov al, byte ptr [si] cmp al,'|' ;; compare if char is end of procedure jne cicl fin: RET print_string ENDP .CODE ;;------------------------------------------------------------- ;; print_stat prints quickly a counter writing to video RAM ;; each counter is 8 bytes: 2 bytes pos, 6 bytes counter ;; Registers ;; In: DS:BP (counter index) ;; Out: ES=video segment ;; Chng: BX, SI, DI, BP, ES ;; print_stat PROC shl bp,3 ;; address counter add bp, offset counter_base print_stat_1 LABEL near ;; don't recompute BP mov bx,[bp] ;; bx holds Video offset add bp,2 ;; bp holds counter address mov si, 5 ;; si = counter bytes to display mov es, pos_vdrm back: mov al, [bp][si] ;; al = two digits exadecimal xor ah, ah add ax,ax mov di, ax ;; DI=2*AL mov ax, tab[di] ;; AX = ASCII code two digits to display mov es:[bx], al add bx, 2 mov es:[bx], ah add bx, 2 dec si jns back print_stat_end: ret PRINT_STAT ENDP .CODE ;;------------------------------------------------------------ ;; init_tab builds a table of hex strings ;; Registers: ;; In: -- ;; Out: -- ;; Change: all init_tab PROC ;; builds a table of hex strings