// // IPX communications-based distributed Mandelbrot fractal generator // // IPX Based on Riceware's QuickBASIC IPX library // // Version 2.0 07/26/01.003 by Andy "Fox" Gruenenwald // // 2.0 7/26/01.003 Packet listening logic re-done so 486 wouldn't miss packets // 2.0 7/25/01.001 unused packet data area zeroed out for earier packet sniffing // 2.0 7/31/00.002 very slight mods since 1997 to kill null pointer error and streamline #define TRUE 1 #define FALSE 0 #include #include #include #include #include #include #include char *midstr(char *d, char *s, int start, int num); // Like BASIC's MID$ char *makehex(char *d, char *s, int len); // convert char[] to hex void EncodeData(unsigned int Itter, unsigned int Len, unsigned char Completed); void ClearStatusLine(void); void UpdateStatus(char *s); char IPXInstalled(void); // Checks for IPX.COM void GetLocalAddress(void); // get this machine's address void OpenSocket(unsigned int s); void SendPacket(void); void TurnAround(void); void RelenquishControl(void); void SocketListen(void); char MonitorIncommingPackets(char *dps); void CloseSocket(unsigned int s); void IPXCancelR(void); void IPXCancelS(void); void IPXDisconnect(void); //---------------------------------------------------------------------- // IPX structures // // This is the Event Control Block Structure. //---------------------------------------------------------------------- struct ECBStructure { unsigned int LinkAddressOff; // Link address offset unsigned int LinkAddressSeg; // Link address segment unsigned int ESRAddressOff; // ESRA address offset unsigned int ESRAddressSeg; // ESRA address segment unsigned char InUse; unsigned char CompCode; unsigned int SockNum; unsigned char IPXWorkSpc[4]; unsigned char DrvWorkSpc[12]; unsigned char ImmAdd[6]; unsigned int FragCount; unsigned int FragAddressOff; // fragment address offset unsigned int FragAddressSeg; // fragment address segment unsigned int FragSize; }; //---------------------------------------------------------------------- // Structure for a complete IPX network address //---------------------------------------------------------------------- struct FullNetAddress { unsigned char Network[4]; unsigned char Node[6]; unsigned int Socket; }; //---------------------------------------------------------------------- // IPX Packet Header Structure. //---------------------------------------------------------------------- struct IPXPacket { unsigned int Checksum; unsigned int Length; unsigned char Control; unsigned char PacketType; unsigned char DestNet[4]; unsigned char DestNode[6]; unsigned int DestSocket; unsigned char SourNet[4]; unsigned char SourNode[6]; unsigned int SourSocket; unsigned char Datagram[546]; // send/receive up to 546 bytes/packet }; //---------------------------------------------------------------------- // Define variables used for IPX functions //---------------------------------------------------------------------- struct IPXPacket IPXS, IPXR; struct ECBStructure ECBS, ECBR; struct FullNetAddress Disconnect, LocalAddress; unsigned int SocketR, SocketS, IPX_Status, SocketNumberReturned; unsigned int SocketTemp, Response; //---------------------------------------------------------------------- // Define other variables //---------------------------------------------------------------------- FILE *CfgFile, *TraceFile, *LogFile, *DataFile; char *epos, *sp; // pointers for cfg line pars char *ts; // temp string for string ops char hexnet[9],hexnode[13]; char StatusString[80]; // used to report prog. status char CfgParam[40], CfgVal[40], CfgLine[81]; int grdrv, grmod; // used for graphics system char *strbuf; unsigned char ServerMode, Logging, Trace; // Flags set in config file unsigned char PerformCalculations, DoSound; // more flags from cfg file unsigned char ProgramAbort, NeedWork; // Program boolean flags unsigned char UserAbort; // goes true if user hits ESC unsigned char NetTries, tries; // General retry loop counter unsigned char CloseFirst; // close sockets before opening? int ResendDelay; // General delay variable unsigned char bval[3]; // used for encoding line data //---------------------------------------------------------------------- // Mandelbrot calculation-specific variables //---------------------------------------------------------------------- int Xresolution, Yresolution; // pixel resolution of fract. double MandelX, MandelY, Magnification;// Mandelbrot parameters double Xscale, Yscale; // Mandelbrot parameters int MaxItterations; // Mandelbrot parameters int WorkX, WorkY; // current working coord. double Xcorner, Ycorner, Xmult, Ymult; // Used to find upper left double AC, BC, AZ, BZ, A1, B1; int RunI, RunL, RunBytes, i; // used for RLE encoding int WorkLines[4096][2]; // max of 4096 scan lines, // [y,1] = assigned or not // (0=no, 1=yes) // [y,2] = current x pos unsigned char WorkCompleted; // set when image is done unsigned char LineBuffer[500]; // buffer for line being calc'd unsigned char LineCompleted; // goes true when work line done /* *********************************************************************** * * * PACKET DESCRIPTIONS AND FORMATS * * * * [:] * * * * is a 5character code that identifies a packet * * is a variable length number of bytes whoes contents is * * specific to the type of packet. * * * * Packet ID C--S Packet description Data * * --------- ---- --------------------------- -------------- * * LFSRV --> Looking for server none * * SRVID <-- Server identification none * * REQWP --> Request work parameters none * * PDATA <-- Parameter data * * * * * * REQWK --> Request for work none * * WDATA <-- Work assignment packet or <-1> * * LDATA --> Line data from assignment **see below** * * LINOK <-- Line data received ok none * * * *********************************************************************** */ // Format of line data packets: // // LINED:,{1rrliiii}{1iiiiiii}{1iiiiccc){1ccccccc} // // rr : 1 - 3 : run length, of 1 to 3 pixels, 0 - use rl byte // l : 0 - 1 : 0 - no more data for line, 1 - more to come // c : 0 - 1023 : RLE count // i : 0 - 32767: itteration count for this RLE segment // // Allows for itterations up to 32,767 and run lengths up to 1,023 //====================================================================== // // Misc. string functions required for silly things // //====================================================================== char *midstr(char *d, char *s, int start, int num) { int len,offset; len=strlen(s); if(len>11), // byte 0: {1rrLiiii} 0x80^((Itter&0x07F0)>>4)); // byte 1: {1iiiiiii} strcat(LineBuffer,bval); if(RB>0) sprintf(bval,"%c",0x80^((Itter&0x0F)<<3)); // byte 2: {1iiii000} else sprintf(bval,"%c%c", 0x80^((Itter&0x0F)<<3)^((Len&0x0380)>>7), // byte 2: {1iiiiccc} 0x80^(Len&0x7F)); // byte 3: {1ccccccc} strcat(LineBuffer,bval); } //====================================================================== // // Display functions // //====================================================================== void ClearStatusLine(void) { setcolor(0); // Blank out line by printing 70 lace characters outtextxy(8,464,"лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл"); } void UpdateStatus(char *s) { struct date d; struct time t; ClearStatusLine(); setcolor(15); outtextxy(8,464,s); if(Logging) { LogFile=fopen("IPX_DIST.LOG","at"); getdate(&d); gettime(&t); fprintf(LogFile,"%02d-%02d-%02d %02d:%02d:%02d %s\n", d.da_mon, d.da_day, d.da_year, t.ti_hour, t.ti_min, t.ti_sec,s); fclose(LogFile); } } //====================================================================== // // IPX functions - good stuff // //====================================================================== char IPXInstalled(void) { struct REGPACK regs; regs.r_ax=0x7A00; intr(0x2F,®s); return((regs.r_ax&0x00FF)==0xFF?TRUE:FALSE); // return TRUE if al==0xFF, IPX is here } void GetLocalAddress(void) { struct REGPACK regs; regs.r_bx=0x09; regs.r_es=FP_SEG((unsigned long far)&LocalAddress); regs.r_si=FP_OFF((unsigned long far)&LocalAddress); intr(0x7A,®s); } void OpenSocket(unsigned int s) { struct REGPACK regs; regs.r_ax=0; regs.r_bx=0; regs.r_dx=s; intr(0x7A,®s); IPX_Status=(regs.r_ax&0x00FF); // grab the al part of ax. SocketNumberReturned=regs.r_dx; // // Completion status: // 00 successful // FF open already // FE socket table is full } void SendPacket(void) { struct REGPACK regs; time_t timer; timer=time(NULL); if(Trace) { TraceFile=fopen("IPX_DIST.NET","at"); makehex(hexnet,IPXS.DestNet,sizeof(IPXR.DestNet)), makehex(hexnode,IPXS.DestNode,sizeof(IPXR.DestNode)); fprintf(TraceFile,"S %s:%s %s\n",hexnet,hexnode,IPXS.Datagram); fclose(TraceFile); } regs.r_bx = 0x03; regs.r_es = FP_SEG((unsigned long far)&ECBS); regs.r_si = FP_OFF((unsigned long far)&ECBS); intr(0x7A,®s); // // Error codes: // 00 sent // FC canceled // FD malformed packet // FE no listener (undelivered) // FF hardware failure // // Wait for send channel to clear // do { RelenquishControl(); } while((ECBS.InUse!=0x00)&&(time(NULL)-timer<2)); // Allow 2 seconds to die if(ECBS.InUse!=0x00) { sprintf(StatusString,"Error sending packet!"); UpdateStatus(StatusString); ProgramAbort=TRUE; } SocketListen(); } // This is a simple routine to set up the sending packet destination // address to whoever sent the most recent packet. void TurnAround(void) { memcpy(IPXS.DestNet, IPXR.SourNet, sizeof(IPXR.SourNet)); memcpy(IPXS.DestNode, IPXR.SourNode, sizeof(IPXR.SourNode)); IPXS.DestSocket = SocketS; IPXS.SourSocket = SocketR; memcpy(ECBS.ImmAdd, ECBR.ImmAdd, sizeof(ECBR.ImmAdd)); } void RelenquishControl(void) { struct REGPACK regs; regs.r_ax=0; regs.r_bx=0x0A; intr(0x7A,®s); } void SocketListen(void) { struct REGPACK regs; regs.r_bx=0x04; regs.r_es=FP_SEG((unsigned long far)&ECBR); regs.r_si=FP_OFF((unsigned long far)&ECBR); intr(0x7A,®s); // ECBR.CompCode is completion code: // // Completion codes: // 00 received // FC canceled // FD packet overflow // FF socket was closed // FE Listening } char MonitorIncommingPackets(char *dps) { unsigned int DataLine, RunLength, itter, index; char DesiredPacket; char PktID[6], Data[6]; char *sp; time_t timer; DesiredPacket = FALSE; timer=time(NULL); // ***** Logic note for packet listening ***** // // Normally we would put a SocketListen() here and that would be the only // one. But I had to design this program to work with slower computers // which would (and did) miss packets if that technique were used. So, the // new concept is "always listen when possible." which means there is a // SocketListen() at the end of SendPacket(). Regardless, the 486-33 can // only be used as a server with the PII-333 as it still can't catch the // SRVID packet quick enough to start up. do { // // Wait for the packet to arrive. If one does, // the INUSE flag will go from &HFE to &H00. // if(ECBR.InUse==0) { if(Trace) { TraceFile=fopen("IPX_DIST.NET","at"); makehex(hexnet,IPXR.SourNet,sizeof(IPXR.SourNet)), makehex(hexnode,IPXR.SourNode,sizeof(IPXR.SourNode)); fprintf(TraceFile,"R %s:%s %s\n",hexnet,hexnode,IPXR.Datagram); fclose(TraceFile); } // Sound if we got a packet if(DoSound) { sound(300); delay(10); nosound(); } strncpy(PktID,IPXR.Datagram,5); // store first 5 chars (packet ID) PktID[5]='\0'; // properly terminate it if(strcmp(PktID,dps)==0) DesiredPacket=TRUE; else { if(ServerMode) { // // process alternate packets that, as a server, we might receive // if(strcmp(PktID,"REQWP")==0) // is someone looking for params? { TurnAround(); // Set up sending packet with receiver info memset(&IPXS.Datagram,0x00,sizeof(IPXS.Datagram)); sprintf(IPXS.Datagram,"PDATA:P1%dP2%dP3%fP4%fP5%fP6%d", Xresolution, Yresolution, MandelX, MandelY, Magnification, MaxItterations); SendPacket(); makehex(hexnet,IPXR.SourNet,sizeof(IPXR.SourNet)), makehex(hexnode,IPXR.SourNode,sizeof(IPXR.SourNode)); sprintf(StatusString,"Parameter data sent to %s:%s.",hexnet,hexnode); UpdateStatus(StatusString); } if(strcmp(PktID,"LFSRV")==0) // is someone looking for us? { TurnAround(); // Set up sending packet with receiver info memset(&IPXS.Datagram,0x00,sizeof(IPXS.Datagram)); sprintf(IPXS.Datagram,"SRVID"); SendPacket(); makehex(hexnet,IPXR.SourNet,sizeof(IPXR.SourNet)), makehex(hexnode,IPXR.SourNode,sizeof(IPXR.SourNode)); sprintf(StatusString,"Server identification packet sent to %s:%s.",hexnet,hexnode); UpdateStatus(StatusString); } if(strcmp(PktID,"REQWK")==0) // is someone looking for work? { // // find next row to work on and assign to inquiring node // index=0; while(WorkLines[index][1]!=0&&index> 5); if(RunLength==0) RunLength = ((*(sp+2)&0x07) << 7) + (*(sp+3)&0x7F); itter = ((*sp&0x0F) << 18) + ((*(sp+1)&0x7F) << 4) + ((*(sp+2)&0x78) >> 3); if(itter> Reading configuration file...\n"); if ((CfgFile = fopen("IPX_DIST.CFG","rt")) != NULL) { do { fgets(CfgLine,81,CfgFile); if((CfgLine[0]!=';')&&(strlen(CfgLine)>2)) { strupr(CfgLine); // make uppercase sp=CfgLine; // point to beginning of cfg line epos=strchr(CfgLine,'='); // point to equal sign position strncpy(CfgParam,CfgLine,(epos-sp)-1); // extrace parameter name CfgParam[(epos-sp)-1]='\0'; // terminate the param string strcpy(CfgVal,epos+2); // extract parameter value CfgVal[strlen(CfgVal)-1]='\0'; // change newline char to null if(strcmp(CfgParam,"SERVER")==0) if(strcmp(CfgVal,"YES")==0) ServerMode=TRUE; else ServerMode=FALSE; if(strcmp(CfgParam,"CALCULATE")==0) if(strcmp(CfgVal,"YES")==0) PerformCalculations=TRUE; else PerformCalculations=FALSE; if(strcmp(CfgParam,"LOGGING")==0) if(strcmp(CfgVal,"YES")==0) Logging=TRUE; else Logging=FALSE; if(strcmp(CfgParam,"TRACE")==0) if(strcmp(CfgVal,"YES")==0) Trace=TRUE; else Trace=FALSE; if(strcmp(CfgParam,"CLOSE_FIRST")==0) if(strcmp(CfgVal,"YES")==0) CloseFirst=TRUE; else CloseFirst=FALSE; if(strcmp(CfgParam,"SOUND")==0) if(strcmp(CfgVal,"YES")==0) DoSound=TRUE; else DoSound=FALSE; if(strcmp(CfgParam,"RECEIVE_SOCKET")==0) SocketR=atoi(CfgVal); if(strcmp(CfgParam,"SEND_SOCKET")==0) SocketS=atoi(CfgVal); if(strcmp(CfgParam,"NET_TRIES")==0) NetTries=atoi(CfgVal); if(strcmp(CfgParam,"RESEND_DELAY")==0) ResendDelay=atoi(CfgVal); if(strcmp(CfgParam,"X_RES")==0) Xresolution=atoi(CfgVal); if(strcmp(CfgParam,"Y_RES")==0) Yresolution=atoi(CfgVal); if(strcmp(CfgParam,"MANDEL_X")==0) MandelX=atof(CfgVal); if(strcmp(CfgParam,"MANDEL_Y")==0) MandelY=atof(CfgVal); if(strcmp(CfgParam,"MAGNIFICATION")==0) Magnification=atof(CfgVal); if(strcmp(CfgParam,"MAX_ITTERATIONS")==0) MaxItterations=atoi(CfgVal); } } while(!feof(CfgFile)); fclose(CfgFile); } else { printf(" Configuration file not found. Program aborted.\n"); exit(1); } printf(">> Initializing...\n"); //---------------------------------------------------------------------- // If logging is turned on delete current log file //---------------------------------------------------------------------- if(Logging) { printf(">> Deleting current log file...\n"); remove("IPX_DIST.LOG"); } //---------------------------------------------------------------------- // If server mode delete current data file //---------------------------------------------------------------------- if(ServerMode) { printf(">> Deleting current data file...\n"); remove("IPX_DIST.DAT"); } //---------------------------------------------------------------------- // If tracing is turned on delete current trace file //---------------------------------------------------------------------- if(Trace) { printf(">> Deleting current net trace file...\n"); remove("IPX_DIST.NET"); } //---------------------------------------------------------------------- // Clear scan line array //---------------------------------------------------------------------- memset(WorkLines, 0x00, sizeof(WorkLines)); //---------------------------------------------------------------------- // Check to see if IPX.COM is loaded //---------------------------------------------------------------------- if(!IPXInstalled()) { textcolor(14); cprintf(" IPX.COM is not installed. Program aborted.\n"); textcolor(7); exit(1); } // If we are a server swap the receive and send sockets if(ServerMode) { SocketTemp=SocketS; SocketS=SocketR; SocketR=SocketTemp; } //---------------------------------------------------------------------- // Open send socket //---------------------------------------------------------------------- if(CloseFirst) CloseSocket(SocketS); OpenSocket(SocketS); if(IPX_Status==0xFE) { textcolor(14); cprintf(" No send socket available. Change IPX_DIST.CFG and try again.\n"); textcolor(7); exit(1); } else { if(IPX_Status!=0x00) { textcolor(14); cprintf(" Unknown error opening socket %d. Status: %d\n",SocketS, IPX_Status); textcolor(7); exit(1); } else { printf(">> Opened socket %d.\n",SocketS); } } //---------------------------------------------------------------------- // Open receive socket //---------------------------------------------------------------------- if(SocketR!=SocketS) { if(CloseFirst) CloseSocket(SocketR); OpenSocket(SocketR); if(IPX_Status==0xFE) { textcolor(14); cprintf(" No receive socket available. Change IPX_DIST.CFG and try again.\n"); textcolor(7); exit(1); } else { if(IPX_Status!=0x00) { textcolor(14); cprintf(" Unknown error opening socket %d. Status: %d\n",SocketR, IPX_Status); textcolor(7); exit(1); } else { printf(">> Opened socket %d.\n",SocketR); } } } delay(1000); //---------------------------------------------------------------------- // Initialize IPX packet header data //---------------------------------------------------------------------- grdrv=VGA; grmod=VGAHI; // Set to 640x480x16 initgraph(&grdrv, &grmod,""); // initialize graphics screen.... oooh. ClearStatusLine(); GetLocalAddress(); // Find machine's address IPXS.Checksum = 0; // Will be set by IPX.COM IPXS.Length = sizeof(IPXS); // Size, with all fragments IPXS.Control = '\0'; // Set by IPX.COM IPXS.PacketType = '\0'; // Zero equals "unknown" memset(IPXS.DestNet,0x00,sizeof(IPXS.DestNet)); // default network as 00000000 memset(IPXS.DestNode,0xFF,sizeof(IPXS.DestNode)); // broadcast FFFFFFFFFFFF IPXS.DestSocket = SocketS; // Broadcast on the send socket memcpy(IPXS.SourNet,LocalAddress.Network, sizeof(LocalAddress.Network)); memcpy(IPXS.SourNode,LocalAddress.Node, sizeof(LocalAddress.Node)); IPXS.SourSocket = 0x0740; // Random. Not needed. ECBS.LinkAddressOff = 0; // No link event either ECBS.LinkAddressSeg = 0; ECBS.ESRAddressOff = 0; // No event service request ECBS.ESRAddressSeg = 0; ECBS.SockNum = SocketS; // Broadcast on the send socket memset(ECBS.ImmAdd,0xFF,sizeof(ECBS.ImmAdd)); // Nearest Network ECBS.FragCount = 1; // One fragment ECBS.FragAddressOff = FP_OFF(&IPXS); // point fragment address to IPXR ECBS.FragAddressSeg = FP_SEG(&IPXS); ECBS.FragSize = sizeof(IPXS); // Length of IPX send block ECBR.LinkAddressOff = 0; // No link address ECBR.LinkAddressSeg = 0; ECBR.ESRAddressOff = 0; // No event service request ECBR.ESRAddressSeg = 0; ECBR.SockNum = SocketR; // Our LISTEN socket ECBR.FragCount = 1; // One fragment ECBR.FragAddressOff = FP_OFF(&IPXR); // point fragment address to IPXR ECBR.FragAddressSeg = FP_SEG(&IPXR); ECBR.FragSize = sizeof(IPXR); // Length of IPX listen block ProgramAbort = FALSE; UserAbort = FALSE; if(ServerMode) SocketListen(); // We will now begin listening // for packets if(!ServerMode) // Where's the server???? { //---------------------------------------------------------------------- // Send broadcast packet to find server //---------------------------------------------------------------------- memset(&IPXS.Datagram,0x00,sizeof(IPXS.Datagram)); sprintf(IPXS.Datagram,"LFSRV"); // send "Looking For SeRVer" packet tries = 0; do { sprintf(StatusString,"Sending broadcast message to find server."); UpdateStatus(StatusString); SendPacket(); // // If the INUSE flag went from &HFF to something // other than zero, there was an error; probably // a hardware error. // if(ECBS.InUse!=0) { closegraph(); printf("Send error: %0X\n",ECBS.InUse); CloseSocket(SocketS); if(SocketR!=SocketS) CloseSocket(SocketR); exit(1); } Response=MonitorIncommingPackets("SRVID"); // listen for "SeRVer ID" pkt if(Response) { // // from now on only send to this node // memcpy(IPXS.DestNet, IPXR.SourNet, sizeof(IPXR.SourNet)); memcpy(IPXS.DestNode, IPXR.SourNode, sizeof(IPXR.SourNode)); memcpy(ECBS.ImmAdd, ECBR.ImmAdd, sizeof(ECBR.ImmAdd)); makehex(hexnet,IPXR.SourNet,sizeof(IPXR.SourNet)), makehex(hexnode,IPXR.SourNode,sizeof(IPXR.SourNode)); sprintf(StatusString,"Server responded from %s:%s",hexnet,hexnode); UpdateStatus(StatusString); } tries++; // // check keyboard for user abort // if(kbhit()) if(getch()==27) UserAbort=TRUE; } while((tries-1) { WorkY=i; // set work line to what server sent WorkX=0; // start at beginning of line RunI=0; RunL=0; LineCompleted=FALSE; sprintf(LineBuffer,"LINED:%d,",WorkY); sprintf(StatusString,"Work data received: Line %d.",WorkY); UpdateStatus(StatusString); NeedWork=FALSE; } else { WorkCompleted=TRUE; sprintf(StatusString,"Server reports Mandelbrot work is complete.\n"); UpdateStatus(StatusString); } } tries++; } while (tries490) { if(ServerMode) { //-------------------------------------------------------------- // We are a server so write our data to disk //-------------------------------------------------------------- DataFile=fopen("IPX_DIST.DAT","at"); fprintf(DataFile,"%s\n",LineBuffer+6); fclose(DataFile); if(LineCompleted) // if we actually completed the line, NeedWork=TRUE; // flag that we need more work. else // otherwise start another buffer up { sprintf(LineBuffer,"LINED:%d,",WorkY); } sprintf(StatusString,"Data for line %d processed locally.",WorkY); UpdateStatus(StatusString); } else { //-------------------------------------------------------------- // We are not a server so send Mr. server our data //-------------------------------------------------------------- memset(&IPXS.Datagram,0x00,sizeof(IPXS.Datagram)); strcpy(IPXS.Datagram,LineBuffer); // copy line buffer to IPX out tries=0; do { sprintf(StatusString,"Sending data for line %d to server.",WorkY); UpdateStatus(StatusString); SendPacket(); Response=MonitorIncommingPackets("LINOK"); if(Response) // if server received line ok if(LineCompleted) // and we are done with this line NeedWork=TRUE; // flag that we need more work. else { sprintf(LineBuffer,"LINED:%d,",WorkY); } tries++; } while(tries> Closed socket %d\n",SocketS); if(SocketR!=SocketS) { IPXCancelR(); CloseSocket(SocketR); printf(">> Closed socket %d\n",SocketR); } }