DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/ipx.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 
00022 #if C_IPX
00023 
00024 #include <string.h>
00025 #include <time.h>
00026 #include <stdio.h>
00027 #include "cross.h"
00028 #include "support.h"
00029 #include "cpu.h"
00030 #include "regs.h"
00031 #include "inout.h"
00032 #include "setup.h"
00033 #include "debug.h"
00034 #include "callback.h"
00035 #include "dos_system.h"
00036 #include "mem.h"
00037 #include "ipx.h"
00038 #include "ipxserver.h"
00039 #include "timer.h"
00040 #include "SDL_net.h"
00041 #include "programs.h"
00042 #include "pic.h"
00043 #include "control.h"
00044 #include "setup.h"
00045 
00046 #define SOCKTABLESIZE   150 // DOS IPX driver was limited to 150 open sockets
00047 
00048 struct ipxnetaddr {
00049         Uint8 netnum[4];   // Both are big endian
00050         Uint8 netnode[6];
00051 } localIpxAddr;
00052 
00053 Bit32u udpPort;
00054 bool isIpxServer;
00055 bool isIpxConnected;
00056 IPaddress ipxServConnIp;                        // IPAddress for client connection to server
00057 UDPsocket ipxClientSocket;
00058 int UDPChannel;                                         // Channel used by UDP connection
00059 Bit8u recvBuffer[IPXBUFFERSIZE];        // Incoming packet buffer
00060 
00061 static RealPt ipx_callback;
00062 
00063 SDLNet_SocketSet clientSocketSet;
00064 
00065 packetBuffer incomingPacket;
00066 
00067 static Bit16u socketCount;
00068 static Bit16u opensockets[SOCKTABLESIZE]; 
00069 
00070 static Bit16u swapByte(Bit16u sockNum) {
00071         return ((sockNum>> 8) | (sockNum << 8));
00072 }
00073 
00074 void UnpackIP(PackedIP ipPack, IPaddress * ipAddr) {
00075         ipAddr->host = ipPack.host;
00076         ipAddr->port = ipPack.port;
00077 }
00078 
00079 void PackIP(IPaddress ipAddr, PackedIP *ipPack) {
00080         ipPack->host = ipAddr.host;
00081         ipPack->port = ipAddr.port;
00082 }
00083 
00084 ECBClass *ECBList;  // Linked list of ECB's
00085 ECBClass* ESRList;      // ECBs waiting to be ESR notified
00086 
00087 #ifdef IPX_DEBUGMSG 
00088 Bitu ECBSerialNumber = 0;
00089 Bitu ECBAmount = 0;
00090 #endif
00091 
00092 
00093 ECBClass::ECBClass(Bit16u segment, Bit16u offset) {
00094         ECBAddr = RealMake(segment, offset);
00095         databuffer = 0;
00096         
00097 #ifdef IPX_DEBUGMSG
00098         SerialNumber = ECBSerialNumber;
00099         ECBSerialNumber++;
00100         ECBAmount++;
00101 
00102         LOG_IPX("ECB: SN%7d created.   Number of ECBs: %3d, ESR %4x:%4x, ECB %4x:%4x",
00103                 SerialNumber,ECBAmount,
00104                 real_readw(RealSeg(ECBAddr),
00105                 RealOff(ECBAddr)+6),
00106                 real_readw(RealSeg(ECBAddr),
00107                 RealOff(ECBAddr)+4),segment,offset);
00108 #endif
00109         isInESRList = false;
00110         prevECB = NULL;
00111         nextECB = NULL;
00112         
00113         if (ECBList == NULL)
00114                 ECBList = this;
00115         else {
00116                 // Transverse the list until we hit the end
00117                 ECBClass *useECB = ECBList;
00118                 
00119                 while(useECB->nextECB != NULL)
00120                         useECB = useECB->nextECB;
00121 
00122                 useECB->nextECB = this;
00123                 this->prevECB = useECB;
00124         }
00125 
00126         iuflag = getInUseFlag();
00127         mysocket = getSocket();
00128 }
00129 void ECBClass::writeDataBuffer(Bit8u* buffer, Bit16u length) {
00130         if(databuffer!=0) delete [] databuffer;
00131         databuffer = new Bit8u[length];
00132         memcpy(databuffer,buffer,length);
00133         buflen=length;
00134 
00135 }
00136 bool ECBClass::writeData() {
00137         Bitu length=buflen;
00138         Bit8u* buffer = databuffer;
00139         fragmentDescriptor tmpFrag;
00140         setInUseFlag(USEFLAG_AVAILABLE);
00141         Bit16u fragCount = getFragCount(); 
00142         Bitu bufoffset = 0;
00143         for(Bit16u i = 0;i < fragCount;i++) {
00144                 getFragDesc(i,&tmpFrag);
00145                 for(Bit16u t = 0;t < tmpFrag.size;t++) {
00146                         real_writeb(tmpFrag.segment, tmpFrag.offset + t, buffer[bufoffset]);
00147                         bufoffset++;
00148                         if(bufoffset >= length) {
00149                                 setCompletionFlag(COMP_SUCCESS);
00150                                 setImmAddress(&buffer[22]);  // Write in source node
00151                                 return true;
00152                         }
00153                 }
00154         }
00155         if(bufoffset < length) {
00156                 setCompletionFlag(COMP_MALFORMED);
00157                 return false;
00158         }
00159         return false;
00160 }
00161 
00162 Bit16u ECBClass::getSocket(void) {
00163         return swapByte(real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 0xa));
00164 }
00165 
00166 Bit8u ECBClass::getInUseFlag(void) {
00167         return real_readb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8);
00168 }
00169 
00170 void ECBClass::setInUseFlag(Bit8u flagval) {
00171         iuflag = flagval;
00172         real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8, flagval);
00173 }
00174 
00175 void ECBClass::setCompletionFlag(Bit8u flagval) {
00176         real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x9, flagval);
00177 }
00178 
00179 Bit16u ECBClass::getFragCount(void) {
00180         return real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 34);
00181 }
00182 
00183 void ECBClass::getFragDesc(Bit16u descNum, fragmentDescriptor *fragDesc) {
00184         Bit16u memoff = RealOff(ECBAddr) + 30 + ((descNum+1) * 6);
00185         fragDesc->offset = real_readw(RealSeg(ECBAddr), memoff);
00186         memoff += 2;
00187         fragDesc->segment = real_readw(RealSeg(ECBAddr), memoff);
00188         memoff += 2;
00189         fragDesc->size = real_readw(RealSeg(ECBAddr), memoff);
00190 }
00191 
00192 RealPt ECBClass::getESRAddr(void) {
00193         return RealMake(real_readw(RealSeg(ECBAddr),
00194                 RealOff(ECBAddr)+6),
00195                 real_readw(RealSeg(ECBAddr),
00196                 RealOff(ECBAddr)+4));
00197 }
00198 
00199 void ECBClass::NotifyESR(void) {
00200         Bit32u ESRval = real_readd(RealSeg(ECBAddr), RealOff(ECBAddr)+4);
00201         if(ESRval || databuffer) { // databuffer: write data at realmode/v86 time
00202                 // LOG_IPX("ECB: SN%7d to be notified.", SerialNumber);
00203                 // take the ECB out of the current list
00204                 if(prevECB == NULL) {   // was the first in the list
00205                         ECBList = nextECB;
00206                         if(ECBList != NULL) ECBList->prevECB = NULL;
00207                 } else {                // not the first
00208                         prevECB->nextECB = nextECB;
00209                         if(nextECB != NULL) nextECB->prevECB = prevECB;
00210                 }
00211 
00212                 nextECB = NULL;
00213                 // put it to the notification queue
00214                 if(ESRList==NULL) {
00215                         ESRList = this;
00216                         prevECB = NULL;
00217                 } else  {// put to end of ESR list
00218                         ECBClass* useECB = ESRList;
00219                 
00220                         while(useECB->nextECB != NULL)
00221                                 useECB = useECB->nextECB;
00222 
00223                         useECB->nextECB = this;
00224                         prevECB = useECB;
00225                 }
00226                 isInESRList = true;
00227                 PIC_ActivateIRQ(11);
00228         }
00229         // this one does not want to be notified, delete it right away
00230         else delete this;
00231 }
00232 
00233 void ECBClass::setImmAddress(Bit8u *immAddr) {
00234         for(Bit8u i=0;i<6;i++)
00235                 real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr)+28+i, immAddr[i]);
00236 }
00237 
00238 void ECBClass::getImmAddress(Bit8u* immAddr) {
00239         for(Bit8u i=0;i<6;i++)
00240                 immAddr[i] = real_readb(RealSeg(ECBAddr), RealOff(ECBAddr)+28+i);
00241 }
00242 
00243 ECBClass::~ECBClass() {
00244 #ifdef IPX_DEBUGMSG 
00245         ECBAmount--;
00246         LOG_IPX("ECB: SN%7d destroyed. Remaining ECBs: %3d", SerialNumber,ECBAmount);
00247 #endif
00248 
00249         if(isInESRList) {
00250                 // in ESR list, always the first element is deleted.
00251                 ESRList=nextECB;
00252         } else {
00253                 if(prevECB == NULL) {   // was the first in the list
00254                         ECBList = nextECB;
00255                         if(ECBList != NULL) ECBList->prevECB = NULL;
00256                 } else {        // not the first
00257                         prevECB->nextECB = nextECB;
00258                         if(nextECB != NULL) nextECB->prevECB = prevECB;
00259                 }
00260         }
00261         if(databuffer!=0) delete [] databuffer;
00262 }
00263 
00264 
00265 
00266 static bool sockInUse(Bit16u sockNum) {
00267         for(Bitu i=0;i<socketCount;i++) {
00268                 if (opensockets[i] == sockNum) return true;
00269         }
00270         return false;
00271 }
00272 
00273 static void OpenSocket(void) {
00274         Bit16u sockNum, sockAlloc;
00275         sockNum = swapByte(reg_dx);
00276 
00277         if(socketCount >= SOCKTABLESIZE) {
00278                 reg_al = 0xfe; // Socket table full
00279                 return;
00280         }
00281 
00282         if(sockNum == 0x0000) {
00283                 // Dynamic socket allocation
00284                 sockAlloc = 0x4002;
00285                 while(sockInUse(sockAlloc) && (sockAlloc < 0x7fff)) sockAlloc++;
00286                 if(sockAlloc > 0x7fff) {
00287                         // I have no idea how this could happen if the IPX driver
00288                         // is limited to 150 open sockets at a time
00289                         LOG_MSG("IPX: Out of dynamic sockets");
00290                 }
00291                 sockNum = sockAlloc;
00292         } else {
00293                 if(sockInUse(sockNum)) {
00294                         reg_al = 0xff; // Socket already open
00295                         return;
00296                 } 
00297         }
00298 
00299         opensockets[socketCount] = sockNum;
00300         socketCount++;
00301 
00302         reg_al = 0x00; // Success
00303         reg_dx = swapByte(sockNum);  // Convert back to big-endian
00304 }
00305 
00306 static void CloseSocket(void) {
00307         Bit16u sockNum, i;
00308         ECBClass* tmpECB = ECBList;
00309         ECBClass* tmp2ECB = ECBList;
00310 
00311         sockNum = swapByte(reg_dx);
00312         if(!sockInUse(sockNum)) return;
00313 
00314         for(i=0;i<socketCount-1;i++) {
00315                 if (opensockets[i] == sockNum) {
00316                         // Realign list of open sockets
00317                         memcpy(&opensockets[i], &opensockets[i+1], size_t(SOCKTABLESIZE - (i + 1)));
00318                         break;
00319                 }
00320         }
00321         --socketCount;
00322         
00323         // delete all ECBs of that socket
00324         while(tmpECB!=0) {
00325                 tmp2ECB = tmpECB->nextECB;
00326                 if(tmpECB->getSocket()==sockNum) {
00327                         tmpECB->setCompletionFlag(COMP_CANCELLED);
00328                         tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
00329                         delete tmpECB;
00330                 }
00331                 tmpECB = tmp2ECB;
00332         }
00333 }
00334 
00335 //static RealPt IPXVERpointer;
00336 
00337 static bool IPX_Multiplex(void) {
00338         if(reg_ax != 0x7a00) return false;
00339         reg_al = 0xff;
00340         SegSet16(es,RealSeg(ipx_callback));
00341         reg_di = RealOff(ipx_callback);
00342         
00343         //reg_bx = RealOff(IPXVERpointer);
00344         //reg_cx = RealSeg(ipx_callback);
00345         return true;
00346 }
00347 
00348 static void IPX_AES_EventHandler(Bitu param)
00349 {
00350         ECBClass* tmpECB = ECBList;
00351         ECBClass* tmp2ECB;
00352         while(tmpECB!=0) {
00353                 tmp2ECB = tmpECB->nextECB;
00354                 if(tmpECB->iuflag==USEFLAG_AESCOUNT && param==(Bitu)tmpECB->ECBAddr) {
00355                         tmpECB->setCompletionFlag(COMP_SUCCESS);
00356                         tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
00357                         tmpECB->NotifyESR();
00358                         // LOG_IPX("AES Notification: ECB S/N %d",tmpECB->SerialNumber);
00359                         return;
00360                 }
00361                 tmpECB = tmp2ECB;
00362         }
00363         LOG_MSG("!!!! Rouge AES !!!!" );
00364 }
00365 
00366 static void sendPacket(ECBClass* sendecb);
00367 
00368 static void handleIpxRequest(void) {
00369         ECBClass *tmpECB;
00370 
00371         switch (reg_bx) {
00372                 case 0x0000:    // Open socket
00373                         OpenSocket();
00374                         LOG_IPX("IPX: Open socket %4x", swapByte(reg_dx));
00375                         break;
00376                 case 0x0001:    // Close socket
00377                         LOG_IPX("IPX: Close socket %4x", swapByte(reg_dx));
00378                         CloseSocket();
00379                         break;
00380                 case 0x0002:    // get local target
00381                                                 // es:si
00382                                                 // Currently no support for multiple networks
00383 
00384                         for(Bit8u i = 0; i < 6; i++) 
00385                                 real_writeb(SegValue(es),reg_di+i,real_readb(SegValue(es),reg_si+i+4));
00386 
00387                         reg_cx=1;               // time ticks expected
00388                         reg_al=0x00;    //success
00389                         break;
00390 
00391                 case 0x0003:            // Send packet
00392                         tmpECB = new ECBClass(SegValue(es),reg_si);
00393                         if(!incomingPacket.connected) {
00394                                 tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
00395                                 tmpECB->setCompletionFlag(COMP_UNDELIVERABLE);
00396                                 delete tmpECB;  // not notify?
00397                                 reg_al = 0xff; // Failure
00398                         } else {
00399                                 tmpECB->setInUseFlag(USEFLAG_SENDING);
00400                                 //LOG_IPX("IPX: Sending packet on %4x", tmpECB->getSocket());
00401                                 reg_al = 0x00; // Success
00402                                 sendPacket(tmpECB);
00403                         }
00404 
00405                         break;
00406                 case 0x0004:  // Listen for packet
00407                         tmpECB = new ECBClass(SegValue(es),reg_si);
00408                         // LOG_IPX("ECB: SN%7d RECEIVE.", tmpECB->SerialNumber);
00409                         if(!sockInUse(tmpECB->getSocket())) {  // Socket is not open
00410                                 reg_al = 0xff;
00411                                 tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
00412                                 tmpECB->setCompletionFlag(COMP_HARDWAREERROR);
00413                                 delete tmpECB;
00414                         } else {
00415                                 reg_al = 0x00;  // Success
00416                                 tmpECB->setInUseFlag(USEFLAG_LISTENING);
00417                                 /*LOG_IPX("IPX: Listen for packet on 0x%4x - ESR address %4x:%4x",
00418                                         tmpECB->getSocket(),
00419                                         RealSeg(tmpECB->getESRAddr()),
00420                                         RealOff(tmpECB->getESRAddr()));*/
00421                         }
00422                         break;
00423 
00424                 case 0x0005:    // SCHEDULE IPX EVENT
00425                 case 0x0007:    // SCHEDULE SPECIAL IPX EVENT
00426                 {
00427                         tmpECB = new ECBClass(SegValue(es),reg_si);
00428                         // LOG_IPX("ECB: SN%7d AES. T=%fms.", tmpECB->SerialNumber,
00429                         //      (1000.0f/(1193182.0f/65536.0f))*(float)reg_ax);
00430                         PIC_AddEvent(IPX_AES_EventHandler,
00431                                 (1000.0f/(1193182.0f/65536.0f))*(float)reg_ax,(Bitu)tmpECB->ECBAddr);
00432                         tmpECB->setInUseFlag(USEFLAG_AESCOUNT);
00433                         break;
00434                 }
00435                 case 0x0006:    // cancel operation
00436                 {
00437                         RealPt ecbaddress = RealMake(SegValue(es),reg_si);
00438                         ECBClass* tmpECB= ECBList;
00439                         ECBClass* tmp2ECB;
00440                         while(tmpECB) {
00441                                 tmp2ECB=tmpECB->nextECB;
00442                                 if(tmpECB->ECBAddr == ecbaddress) {
00443                                         if(tmpECB->getInUseFlag()==USEFLAG_AESCOUNT)
00444                                                 PIC_RemoveSpecificEvents(IPX_AES_EventHandler,(Bitu)ecbaddress);
00445                                         tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
00446                                         tmpECB->setCompletionFlag(COMP_CANCELLED);
00447                                         delete tmpECB;
00448                                         reg_al=0;       // Success
00449                                         LOG_IPX("IPX: ECB canceled.");
00450                                         return;
00451                                 }
00452                                 tmpECB=tmp2ECB;
00453                         }
00454                         reg_al=0xff;    // Fail
00455                         break;
00456                 }
00457                 case 0x0008:            // Get interval marker
00458                         reg_ax = mem_readw(0x46c); // BIOS_TIMER
00459                         break;
00460                 case 0x0009:            // Get internetwork address
00461                 {
00462                         LOG_IPX("IPX: Get internetwork address %2x:%2x:%2x:%2x:%2x:%2x",
00463                                 localIpxAddr.netnode[5], localIpxAddr.netnode[4],
00464                                 localIpxAddr.netnode[3], localIpxAddr.netnode[2],
00465                                 localIpxAddr.netnode[1], localIpxAddr.netnode[0]);
00466 
00467                         Bit8u * addrptr = (Bit8u *)&localIpxAddr;
00468                         for(Bit16u i=0;i<10;i++)
00469                                 real_writeb(SegValue(es),reg_si+i,addrptr[i]);
00470                         break;
00471                 }
00472                 case 0x000a:            // Relinquish control
00473                         break;                  // Idle thingy
00474                 
00475                 case 0x000b:            // Disconnect from Target
00476                         break;                  // We don't even connect
00477                 
00478                 case 0x000d:            // get packet size
00479                         reg_cx=0;               // retry count
00480                         reg_ax=1024;    // real implementation returns 1024
00481                         break;
00482                 
00483                 case 0x0010:            // SPX install check
00484                         reg_al=0;               // SPX not installed
00485                         break;
00486 
00487                 case 0x001a:            // get driver maximum packet size
00488                         reg_cx=0;               // retry count
00489                         reg_ax=IPXBUFFERSIZE;   // max packet size: something near the 
00490                                                                         // ethernet packet size
00491                         break;
00492                 
00493                 default:
00494                         LOG_MSG("Unhandled IPX function: %4x", reg_bx);
00495                         break;
00496         }
00497 }
00498 
00499 // Entrypoint handler
00500 Bitu IPX_Handler(void) {
00501         handleIpxRequest();
00502         return CBRET_NONE;
00503 }
00504 
00505 // INT 7A handler
00506 Bitu IPX_IntHandler(void) {
00507         handleIpxRequest();
00508         return CBRET_NONE;
00509 }
00510 
00511 static void pingAck(IPaddress retAddr) {
00512         IPXHeader regHeader;
00513         UDPpacket regPacket;
00514 
00515         SDLNet_Write16(0xffff, regHeader.checkSum);
00516         SDLNet_Write16(sizeof(regHeader), regHeader.length);
00517         
00518         SDLNet_Write32(0, regHeader.dest.network);
00519         PackIP(retAddr, &regHeader.dest.addr.byIP);
00520         SDLNet_Write16(0x2, regHeader.dest.socket);
00521 
00522         SDLNet_Write32(0, regHeader.src.network);
00523         memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(regHeader.src.addr.byNode.node));
00524         SDLNet_Write16(0x2, regHeader.src.socket);
00525         regHeader.transControl = 0;
00526         regHeader.pType = 0x0;
00527 
00528         regPacket.data = (Uint8 *)&regHeader;
00529         regPacket.len = sizeof(regHeader);
00530         regPacket.maxlen = sizeof(regHeader);
00531         regPacket.channel = UDPChannel;
00532         
00533         SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
00534 }
00535 
00536 static void pingSend(void) {
00537         IPXHeader regHeader;
00538         UDPpacket regPacket;
00539         Bits result;
00540 
00541         SDLNet_Write16(0xffff, regHeader.checkSum);
00542         SDLNet_Write16(sizeof(regHeader), regHeader.length);
00543         
00544         SDLNet_Write32(0, regHeader.dest.network);
00545         regHeader.dest.addr.byIP.host = 0xffffffff;
00546         regHeader.dest.addr.byIP.port = 0xffff;
00547         SDLNet_Write16(0x2, regHeader.dest.socket);
00548 
00549         SDLNet_Write32(0, regHeader.src.network);
00550         memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(regHeader.src.addr.byNode.node));
00551         SDLNet_Write16(0x2, regHeader.src.socket);
00552         regHeader.transControl = 0;
00553         regHeader.pType = 0x0;
00554 
00555         regPacket.data = (Uint8 *)&regHeader;
00556         regPacket.len = sizeof(regHeader);
00557         regPacket.maxlen = sizeof(regHeader);
00558         regPacket.channel = UDPChannel;
00559         
00560         result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
00561         if(!result) {
00562                 LOG_MSG("IPX: SDLNet_UDP_Send: %s\n", SDLNet_GetError());
00563         }
00564 }
00565 
00566 static void receivePacket(Bit8u *buffer, Bit16s bufSize) {
00567         ECBClass *useECB;
00568         ECBClass *nextECB;
00569         Bit16u *bufword = (Bit16u *)buffer;
00570         Bit16u useSocket = swapByte(bufword[8]);
00571         IPXHeader * tmpHeader;
00572         tmpHeader = (IPXHeader *)buffer;
00573 
00574         // Check to see if ping packet
00575         if(useSocket == 0x2) {
00576                 // Is this a broadcast?
00577                 if((tmpHeader->dest.addr.byIP.host == 0xffffffff) &&
00578                         (tmpHeader->dest.addr.byIP.port == 0xffff)) {
00579                         // Yes.  We should return the ping back to the sender
00580                         IPaddress tmpAddr;
00581                         UnpackIP(tmpHeader->src.addr.byIP, &tmpAddr);
00582                         pingAck(tmpAddr);
00583                         return;
00584                 }
00585         }
00586 
00587         useECB = ECBList;
00588         while(useECB != NULL)
00589         {
00590                 nextECB = useECB->nextECB;
00591                 if(useECB->iuflag == USEFLAG_LISTENING && useECB->mysocket == useSocket) {
00592                         useECB->writeDataBuffer(buffer, (Bit16u)bufSize);
00593                         useECB->NotifyESR();
00594                         return;
00595                 }
00596                 useECB = nextECB;
00597         }
00598         LOG_IPX("IPX: RX Packet loss!");
00599 }
00600 
00601 static void IPX_ClientLoop(void) {
00602         int numrecv;
00603         UDPpacket inPacket;
00604         inPacket.data = (Uint8 *)recvBuffer;
00605         inPacket.maxlen = IPXBUFFERSIZE;
00606         inPacket.channel = UDPChannel;
00607 
00608         // Its amazing how much simpler UDP is than TCP
00609         numrecv = SDLNet_UDP_Recv(ipxClientSocket, &inPacket);
00610         if(numrecv) receivePacket(inPacket.data, inPacket.len);
00611 }
00612 
00613 
00614 void DisconnectFromServer(bool unexpected) {
00615         if(unexpected) LOG_MSG("IPX: Server disconnected unexpectedly");
00616         if(incomingPacket.connected) {
00617                 incomingPacket.connected = false;
00618                 TIMER_DelTickHandler(&IPX_ClientLoop);
00619                 SDLNet_UDP_Close(ipxClientSocket);
00620         }
00621 }
00622 
00623 static void sendPacket(ECBClass* sendecb) {
00624         Bit8u outbuffer[IPXBUFFERSIZE];
00625         fragmentDescriptor tmpFrag; 
00626         Bit16u i, fragCount,t;
00627         Bit16s packetsize;
00628         Bit16u *wordptr;
00629         Bits result;
00630         UDPpacket outPacket;
00631                 
00632         sendecb->setInUseFlag(USEFLAG_AVAILABLE);
00633         packetsize = 0;
00634         fragCount = sendecb->getFragCount(); 
00635         for(i=0;i<fragCount;i++) {
00636                 sendecb->getFragDesc(i,&tmpFrag);
00637                 if(i==0) {
00638                         // Fragment containing IPX header
00639                         // Must put source address into header
00640                         Bit8u * addrptr;
00641                         
00642                         // source netnum
00643                         addrptr = (Bit8u *)&localIpxAddr.netnum;
00644                         for(Bit16u m=0;m<4;m++) {
00645                                 real_writeb(tmpFrag.segment,tmpFrag.offset+m+18,addrptr[m]);
00646                         }
00647                         // source node number
00648                         addrptr = (Bit8u *)&localIpxAddr.netnode;
00649                         for(Bit16u m=0;m<6;m++) {
00650                                 real_writeb(tmpFrag.segment,tmpFrag.offset+m+22,addrptr[m]);
00651                         }
00652                         // Source socket
00653                         real_writew(tmpFrag.segment,tmpFrag.offset+28, swapByte(sendecb->getSocket()));
00654                         
00655                         // blank checksum
00656                         real_writew(tmpFrag.segment,tmpFrag.offset, 0xffff);
00657                 }
00658 
00659                 for(t=0;t<tmpFrag.size;t++) {
00660                         outbuffer[packetsize] = real_readb(tmpFrag.segment, tmpFrag.offset + t);
00661                         packetsize++;
00662                         if(packetsize>=IPXBUFFERSIZE) {
00663                                 LOG_MSG("IPX: Packet size to be sent greater than %d bytes.", IPXBUFFERSIZE);
00664                                 sendecb->setCompletionFlag(COMP_UNDELIVERABLE);
00665                                 sendecb->NotifyESR();
00666                                 return;
00667                         }
00668                 }
00669         }
00670         
00671         // Add length and source socket to IPX header
00672         wordptr = (Bit16u *)&outbuffer[0];
00673         // Blank CRC
00674         //wordptr[0] = 0xffff;
00675         // Length
00676         wordptr[1] = swapByte((Bit16u)packetsize);
00677         // Source socket
00678         //wordptr[14] = swapByte(sendecb->getSocket());
00679         
00680         sendecb->getFragDesc(0,&tmpFrag);
00681         real_writew(tmpFrag.segment,tmpFrag.offset+2, swapByte((Bit16u)packetsize));
00682         
00683 
00684         Bit8u immedAddr[6];
00685         sendecb->getImmAddress(immedAddr);
00686         // filter out broadcasts and local loopbacks
00687         // Real implementation uses the ImmedAddr to check wether this is a broadcast
00688 
00689         bool islocalbroadcast=true;
00690         bool isloopback=true;
00691 
00692         Bit8u * addrptr;
00693                         
00694         addrptr = (Bit8u *)&localIpxAddr.netnum;
00695         for(Bitu m=0;m<4;m++) {
00696                 if(addrptr[m]!=outbuffer[m+0x6])isloopback=false;
00697         }
00698         addrptr = (Bit8u *)&localIpxAddr.netnode;
00699         for(Bitu m=0;m<6;m++) {
00700                 if(addrptr[m]!=outbuffer[m+0xa])isloopback=false;
00701                 if(immedAddr[m]!=0xff) islocalbroadcast=false;
00702         }
00703         LOG_IPX("SEND crc:%2x",packetCRC(&outbuffer[0], packetsize));
00704         if(!isloopback) {
00705                 outPacket.channel = UDPChannel;
00706                 outPacket.data = (Uint8 *)&outbuffer[0];
00707                 outPacket.len = packetsize;
00708                 outPacket.maxlen = packetsize;
00709                 // Since we're using a channel, we won't send the IP address again
00710                 result = SDLNet_UDP_Send(ipxClientSocket, UDPChannel, &outPacket);
00711                 
00712                 if(result == 0) {
00713                         LOG_MSG("IPX: Could not send packet: %s", SDLNet_GetError());
00714                         sendecb->setCompletionFlag(COMP_HARDWAREERROR);
00715                         sendecb->NotifyESR();
00716                         DisconnectFromServer(true);
00717                         return;
00718                 } else {
00719                         sendecb->setCompletionFlag(COMP_SUCCESS);
00720                         LOG_IPX("Packet sent: size: %d",packetsize);
00721                 }
00722         }
00723         else sendecb->setCompletionFlag(COMP_SUCCESS);
00724 
00725         if(isloopback||islocalbroadcast) {
00726                 // Send packet back to ourselves.
00727                 receivePacket(&outbuffer[0],packetsize);
00728                 LOG_IPX("Packet back: loopback:%d, broadcast:%d",isloopback,islocalbroadcast);
00729         }
00730         sendecb->NotifyESR();
00731 }
00732 
00733 static bool pingCheck(IPXHeader * outHeader) {
00734         char buffer[1024];
00735         Bits result;
00736         UDPpacket regPacket;
00737         IPXHeader *regHeader;
00738         regPacket.data = (Uint8 *)buffer;
00739         regPacket.maxlen = sizeof(buffer);
00740         regPacket.channel = UDPChannel;
00741         regHeader = (IPXHeader *)buffer;
00742         
00743         result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
00744         if (result != 0) {
00745                 memcpy(outHeader, regHeader, sizeof(IPXHeader));
00746                 return true;
00747         }
00748         return false;
00749 }
00750 
00751 bool ConnectToServer(char const *strAddr) {
00752         int numsent;
00753         UDPpacket regPacket;
00754         IPXHeader regHeader;
00755         if(!SDLNet_ResolveHost(&ipxServConnIp, strAddr, (Bit16u)udpPort)) {
00756 
00757                 // Generate the MAC address.  This is made by zeroing out the first two
00758                 // octets and then using the actual IP address for the last 4 octets.
00759                 // This idea is from the IPX over IP implementation as specified in RFC 1234:
00760                 // http://www.faqs.org/rfcs/rfc1234.html
00761 
00762                 // Select an anonymous UDP port
00763                 ipxClientSocket = SDLNet_UDP_Open(0);
00764                 if(ipxClientSocket) {
00765                         // Bind UDP port to address to channel
00766                         UDPChannel = SDLNet_UDP_Bind(ipxClientSocket,-1,&ipxServConnIp);
00767                         //ipxClientSocket = SDLNet_TCP_Open(&ipxServConnIp);
00768                         SDLNet_Write16(0xffff, regHeader.checkSum);
00769                         SDLNet_Write16(sizeof(regHeader), regHeader.length);
00770 
00771                         // Echo packet with zeroed dest and src is a server registration packet
00772                         SDLNet_Write32(0, regHeader.dest.network);
00773                         regHeader.dest.addr.byIP.host = 0x0;
00774                         regHeader.dest.addr.byIP.port = 0x0;
00775                         SDLNet_Write16(0x2, regHeader.dest.socket);
00776 
00777                         SDLNet_Write32(0, regHeader.src.network);
00778                         regHeader.src.addr.byIP.host = 0x0;
00779                         regHeader.src.addr.byIP.port = 0x0;
00780                         SDLNet_Write16(0x2, regHeader.src.socket);
00781                         regHeader.transControl = 0;
00782 
00783                         regPacket.data = (Uint8 *)&regHeader;
00784                         regPacket.len = sizeof(regHeader);
00785                         regPacket.maxlen = sizeof(regHeader);
00786                         regPacket.channel = UDPChannel;
00787                         // Send registration string to server.  If server doesn't get
00788                         // this, client will not be registered
00789                         numsent = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
00790                         
00791                         if(!numsent) {
00792                                 LOG_MSG("IPX: Unable to connect to server: %s", SDLNet_GetError());
00793                                 SDLNet_UDP_Close(ipxClientSocket);
00794                                 return false;
00795                         } else {
00796                                 // Wait for return packet from server.
00797                                 // This will contain our IPX address and port num
00798                                 Bits result;
00799                                 Bit32u ticks, elapsed;
00800                                 ticks = GetTicks();
00801 
00802                                 while(true) {
00803                                         elapsed = GetTicks() - ticks;
00804                                         if(elapsed > 5000) {
00805                                                 LOG_MSG("Timeout connecting to server at %s", strAddr);
00806                                                 SDLNet_UDP_Close(ipxClientSocket);
00807 
00808                                                 return false;
00809                                         }
00810                                         CALLBACK_Idle();
00811                                         result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
00812                                         if (result != 0) {
00813                                                 memcpy(localIpxAddr.netnode, regHeader.dest.addr.byNode.node, sizeof(localIpxAddr.netnode));
00814                                                 memcpy(localIpxAddr.netnum, regHeader.dest.network, sizeof(localIpxAddr.netnum));
00815                                                 break;
00816                                         }
00817                                         
00818                                 }
00819 
00820                                 LOG_MSG("IPX: Connected to server.  IPX address is %d:%d:%d:%d:%d:%d", CONVIPX(localIpxAddr.netnode));
00821 
00822                                 incomingPacket.connected = true;
00823                                 TIMER_AddTickHandler(&IPX_ClientLoop);
00824                                 return true;
00825                         }
00826                 } else {
00827                         LOG_MSG("IPX: Unable to open socket");
00828                 }
00829         } else {
00830                 LOG_MSG("IPX: Unable resolve connection to server");
00831         }
00832         return false;
00833 }
00834 
00835 void IPX_NetworkInit() {
00836 
00837         localIpxAddr.netnum[0] = 0x0;
00838         localIpxAddr.netnum[1] = 0x0;
00839         localIpxAddr.netnum[2] = 0x0;
00840         localIpxAddr.netnum[3] = 0x1;
00841         localIpxAddr.netnode[0] = 0x00;
00842         localIpxAddr.netnode[1] = 0x00;
00843         localIpxAddr.netnode[2] = 0x00;
00844         localIpxAddr.netnode[3] = 0x00;
00845         localIpxAddr.netnode[4] = 0x00;
00846         localIpxAddr.netnode[5] = 0x00;
00847 
00848         socketCount = 0;
00849         return;
00850 }
00851 
00852 class IPXNET : public Program {
00853 public:
00854         void HelpCommand(const char *helpStr) {
00855                 // Help on connect command
00856                 if(strcasecmp("connect", helpStr) == 0) {
00857                         WriteOut("IPXNET CONNECT opens a connection to an IPX tunneling server running on another\n");
00858                         WriteOut("DOSBox session.  The \"address\" parameter specifies the IP address or host name\n");
00859                         WriteOut("of the server computer.  One can also specify the UDP port to use.  By default\n");
00860                         WriteOut("IPXNET uses port 213, the assigned IANA port for IPX tunneling, for its\nconnection.\n\n");
00861                         WriteOut("The syntax for IPXNET CONNECT is:\n\n");
00862                         WriteOut("IPXNET CONNECT address <port>\n\n");
00863                         return;
00864                 }
00865                 // Help on the disconnect command
00866                 if(strcasecmp("disconnect", helpStr) == 0) {
00867                         WriteOut("IPXNET DISCONNECT closes the connection to the IPX tunneling server.\n\n");
00868                         WriteOut("The syntax for IPXNET DISCONNECT is:\n\n");
00869                         WriteOut("IPXNET DISCONNECT\n\n");
00870                         return;
00871                 }
00872                 // Help on the startserver command
00873                 if(strcasecmp("startserver", helpStr) == 0) {
00874                         WriteOut("IPXNET STARTSERVER starts and IPX tunneling server on this DOSBox session.  By\n");
00875                         WriteOut("default, the server will accept connections on UDP port 213, though this can be\n");
00876                         WriteOut("changed.  Once the server is started, DOSBox will automatically start a client\n");
00877                         WriteOut("connection to the IPX tunneling server.\n\n");
00878                         WriteOut("The syntax for IPXNET STARTSERVER is:\n\n");
00879                         WriteOut("IPXNET STARTSERVER <port>\n\n");
00880                         return;
00881                 }
00882                 // Help on the stop server command
00883                 if(strcasecmp("stopserver", helpStr) == 0) {
00884                         WriteOut("IPXNET STOPSERVER stops the IPX tunneling server running on this DosBox\nsession.");
00885                         WriteOut("  Care should be taken to ensure that all other connections have\nterminated ");
00886                         WriteOut("as well since stopping the server may cause lockups on other\nmachines still using ");
00887                         WriteOut("the IPX tunneling server.\n\n");
00888                         WriteOut("The syntax for IPXNET STOPSERVER is:\n\n");
00889                         WriteOut("IPXNET STOPSERVER\n\n");
00890                         return;
00891                 }
00892                 // Help on the ping command
00893                 if(strcasecmp("ping", helpStr) == 0) {
00894                         WriteOut("IPXNET PING broadcasts a ping request through the IPX tunneled network.  In    \n");
00895                         WriteOut("response, all other connected computers will respond to the ping and report\n");
00896                         WriteOut("the time it took to receive and send the ping message.\n\n");
00897                         WriteOut("The syntax for IPXNET PING is:\n\n");
00898                         WriteOut("IPXNET PING\n\n");
00899                         return;
00900                 }
00901                 // Help on the status command
00902                 if(strcasecmp("status", helpStr) == 0) {
00903                         WriteOut("IPXNET STATUS reports the current state of this DOSBox's sessions IPX tunneling\n");
00904                         WriteOut("network.  For a list of the computers connected to the network use the IPXNET \n");
00905                         WriteOut("PING command.\n\n");
00906                         WriteOut("The syntax for IPXNET STATUS is:\n\n");
00907                         WriteOut("IPXNET STATUS\n\n");
00908                         return;
00909                 }
00910         }
00911 
00912         void Run(void)
00913         {
00914                 WriteOut("IPX Tunneling utility for DOSBox\n\n");
00915                 if(!cmd->GetCount()) {
00916                         WriteOut("The syntax of this command is:\n\n");
00917                         WriteOut("IPXNET [ CONNECT | DISCONNECT | STARTSERVER | STOPSERVER | PING | HELP |\n         STATUS ]\n\n");
00918                         return;
00919                 }
00920                 
00921                 if(cmd->FindCommand(1, temp_line)) {
00922                         if(strcasecmp("help", temp_line.c_str()) == 0) {
00923                                 if(!cmd->FindCommand(2, temp_line)) {
00924                                         WriteOut("The following are valid IPXNET commands:\n\n");
00925                                         WriteOut("IPXNET CONNECT        IPXNET DISCONNECT       IPXNET STARTSERVER\n");
00926                                         WriteOut("IPXNET STOPSERVER     IPXNET PING             IPXNET STATUS\n\n");
00927                                         WriteOut("To get help on a specific command, type:\n\n");
00928                                         WriteOut("IPXNET HELP command\n\n");
00929 
00930                                 } else {
00931                                         HelpCommand(temp_line.c_str());
00932                                         return;
00933                                 }
00934                                 return;
00935                         } 
00936                         if(strcasecmp("startserver", temp_line.c_str()) == 0) {
00937                                 if(!isIpxServer) {
00938                                         if(incomingPacket.connected) {
00939                                                 WriteOut("IPX Tunneling Client already connected to another server.  Disconnect first.\n");
00940                                                 return;
00941                                         }
00942                                         bool startsuccess;
00943                                         if(!cmd->FindCommand(2, temp_line)) {
00944                                                 udpPort = 213;
00945                                         } else {
00946                                                 udpPort = (unsigned int)strtol(temp_line.c_str(), NULL, 10);
00947                                         }
00948                                         startsuccess = IPX_StartServer((Bit16u)udpPort);
00949                                         if(startsuccess) {
00950                                                 WriteOut("IPX Tunneling Server started\n");
00951                                                 isIpxServer = true;
00952                                                 ConnectToServer("localhost");
00953                                         } else {
00954                                                 WriteOut("IPX Tunneling Server failed to start.\n");
00955                                                 if(udpPort < 1024) WriteOut("Try a port number above 1024. See IPXNET HELP CONNECT on how to specify a port.\n");
00956                                         }
00957                                 } else {
00958                                         WriteOut("IPX Tunneling Server already started\n");
00959                                 }
00960                                 return;
00961                         }
00962                         if(strcasecmp("stopserver", temp_line.c_str()) == 0) {
00963                                 if(!isIpxServer) {
00964                                         WriteOut("IPX Tunneling Server not running in this DOSBox session.\n");
00965                                 } else {
00966                                         isIpxServer = false;
00967                                         DisconnectFromServer(false);
00968                                         IPX_StopServer();
00969                                         WriteOut("IPX Tunneling Server stopped.");
00970                                 }
00971                                 return;
00972                         }
00973                         if(strcasecmp("connect", temp_line.c_str()) == 0) {
00974                                 char strHost[1024];
00975                                 if(incomingPacket.connected) {
00976                                         WriteOut("IPX Tunneling Client already connected.\n");
00977                                         return;
00978                                 }
00979                                 if(!cmd->FindCommand(2, temp_line)) {
00980                                         WriteOut("IPX Server address not specified.\n");
00981                                         return;
00982                                 }
00983                                 strcpy(strHost, temp_line.c_str());
00984 
00985                                 if(!cmd->FindCommand(3, temp_line)) {
00986                                         udpPort = 213;
00987                                 } else {
00988                                         udpPort = (unsigned int)strtol(temp_line.c_str(), NULL, 10);
00989                                 }
00990 
00991                                 if(ConnectToServer(strHost)) {
00992                         WriteOut("IPX Tunneling Client connected to server at %s.\n", strHost);
00993                                 } else {
00994                                         WriteOut("IPX Tunneling Client failed to connect to server at %s.\n", strHost);
00995                                 }
00996                                 return;
00997                         }
00998                         
00999                         if(strcasecmp("disconnect", temp_line.c_str()) == 0) {
01000                                 if(!incomingPacket.connected) {
01001                                         WriteOut("IPX Tunneling Client not connected.\n");
01002                                         return;
01003                                 }
01004                                 // TODO: Send a packet to the server notifying of disconnect
01005                                 WriteOut("IPX Tunneling Client disconnected from server.\n");
01006                                 DisconnectFromServer(false);
01007                                 return;
01008                         }
01009 
01010                         if(strcasecmp("status", temp_line.c_str()) == 0) {
01011                                 WriteOut("IPX Tunneling Status:\n\n");
01012                                 WriteOut("Server status: ");
01013                                 if(isIpxServer) WriteOut("ACTIVE\n"); else WriteOut("INACTIVE\n");
01014                                 WriteOut("Client status: ");
01015                                 if(incomingPacket.connected) {
01016                                         WriteOut("CONNECTED -- Server at %d.%d.%d.%d port %d\n", CONVIP(ipxServConnIp.host), udpPort);
01017                                 } else {
01018                                         WriteOut("DISCONNECTED\n");
01019                                 }
01020                                 if(isIpxServer) {
01021                                         WriteOut("List of active connections:\n\n");
01022                                         int i;
01023                                         IPaddress *ptrAddr;
01024                                         for(i=0;i<SOCKETTABLESIZE;i++) {
01025                                                 if(IPX_isConnectedToServer(i,&ptrAddr)) {
01026                                                         WriteOut("     %d.%d.%d.%d from port %d\n", CONVIP(ptrAddr->host), SDLNet_Read16(&ptrAddr->port));
01027                                                 }
01028                                         }
01029                                         WriteOut("\n");
01030                                 }
01031                                 return;
01032                         }
01033 
01034                         if(strcasecmp("ping", temp_line.c_str()) == 0) {
01035                                 Bit32u ticks;
01036                                 IPXHeader pingHead;
01037 
01038                                 if(!incomingPacket.connected) {
01039                                         WriteOut("IPX Tunneling Client not connected.\n");
01040                                         return;
01041                                 }
01042                                 TIMER_DelTickHandler(&IPX_ClientLoop);
01043                                 WriteOut("Sending broadcast ping:\n\n");
01044                                 pingSend();
01045                                 ticks = GetTicks();
01046                                 while((GetTicks() - ticks) < 1500) {
01047                                         CALLBACK_Idle();
01048                                         if(pingCheck(&pingHead)) {
01049                                                 WriteOut("Response from %d.%d.%d.%d, port %d time=%dms\n", CONVIP(pingHead.src.addr.byIP.host), SDLNet_Read16(&pingHead.src.addr.byIP.port), GetTicks() - ticks);
01050                                         }
01051                                 }
01052                                 TIMER_AddTickHandler(&IPX_ClientLoop);
01053                                 return;
01054                         }
01055                 }
01056         }
01057 };
01058 
01059 static void IPXNET_ProgramStart(Program * * make) {
01060         *make=new IPXNET;
01061 }
01062 
01063 Bitu IPX_ESRHandler(void) {
01064         LOG_IPX("ESR: >>>>>>>>>>>>>>>" );
01065         while(ESRList!=NULL) {
01066                 // LOG_IPX("ECB: SN%7d notified.", ESRList->SerialNumber);
01067                 if(ESRList->databuffer) ESRList->writeData();
01068                 if(ESRList->getESRAddr()) {
01069                         // setup registers                      
01070                         SegSet16(es, RealSeg(ESRList->ECBAddr));
01071                         reg_si = RealOff(ESRList->ECBAddr);
01072                         reg_al = 0xff;
01073                         CALLBACK_RunRealFar(RealSeg(ESRList->getESRAddr()),
01074                                                                 RealOff(ESRList->getESRAddr()));
01075                 }
01076                 delete ESRList;
01077         }       // while
01078 
01079         IO_WriteB(0xa0,0x63);   //EOI11
01080         IO_WriteB(0x20,0x62);   //EOI2
01081         LOG_IPX("ESR: <<<<<<<<<<<<<<<");
01082         return CBRET_NONE;
01083 }
01084 
01085 void VFILE_Remove(const char *name);
01086 
01087 // FIXME: VS2015 doesn't seem to like class IPX::dospage
01088 static Bit16u dospage;
01089 
01090 class IPX: public Module_base {
01091 private:
01092         CALLBACK_HandlerObject callback_ipx;
01093         CALLBACK_HandlerObject callback_esr;
01094         CALLBACK_HandlerObject callback_ipxint;
01095         RealPt old_73_vector = 0;
01096         bool ipx_init;
01097 public:
01098         IPX(Section* configuration):Module_base(configuration) {
01099                 Section_prop * section = static_cast<Section_prop *>(configuration);
01100                 ipx_init = false;
01101                 if(!section->Get_bool("ipx")) return;
01102                 if(!SDLNetInited) {
01103                         if(SDLNet_Init() == -1){
01104                                 LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
01105                                 return;
01106                         }
01107                         SDLNetInited = true;
01108                 }
01109 
01110                 ECBList = NULL;
01111                 ESRList = NULL;
01112                 isIpxServer = false;
01113                 isIpxConnected = false;
01114                 IPX_NetworkInit();
01115 
01116                 DOS_AddMultiplexHandler(IPX_Multiplex);
01117 
01118                 callback_ipx.Install(&IPX_Handler,CB_RETF,"IPX Handler");
01119                 ipx_callback = callback_ipx.Get_RealPointer();
01120 
01121                 callback_ipxint.Install(&IPX_IntHandler,CB_IRET,"IPX (int 7a)");
01122                 callback_ipxint.Set_RealVec(0x7a);
01123 
01124                 callback_esr.Allocate(&IPX_ESRHandler,"IPX_ESR");
01125                 Bit16u call_ipxesr1 = callback_esr.Get_callback();
01126 
01127                 if(!dospage) dospage = DOS_GetMemory(2,"IPX dospage"); // can not be freed yet
01128 
01129                 PhysPt phyDospage = PhysMake(dospage,0);
01130 
01131                 LOG_IPX("ESR callback address: %x, HandlerID %d", phyDospage,call_ipxesr1);
01132 
01133                 //save registers
01134                 phys_writeb(phyDospage+0,(Bit8u)0xFA);    // CLI
01135                 phys_writeb(phyDospage+1,(Bit8u)0x60);    // PUSHA
01136                 phys_writeb(phyDospage+2,(Bit8u)0x1E);    // PUSH DS
01137                 phys_writeb(phyDospage+3,(Bit8u)0x06);    // PUSH ES
01138                 phys_writew(phyDospage+4,(Bit16u)0xA00F); // PUSH FS
01139                 phys_writew(phyDospage+6,(Bit16u)0xA80F); // PUSH GS
01140  
01141                 // callback
01142                 phys_writeb(phyDospage+8,(Bit8u)0xFE);  // GRP 4
01143                 phys_writeb(phyDospage+9,(Bit8u)0x38);  // Extra Callback instruction
01144                 phys_writew(phyDospage+10,call_ipxesr1);        // Callback identifier
01145  
01146                 // register recreation
01147                 phys_writew(phyDospage+12,(Bit16u)0xA90F); // POP GS
01148                 phys_writew(phyDospage+14,(Bit16u)0xA10F); // POP FS
01149                 phys_writeb(phyDospage+16,(Bit8u)0x07);    // POP ES
01150                 phys_writeb(phyDospage+17,(Bit8u)0x1F);    // POP DS
01151                 phys_writeb(phyDospage+18,(Bit8u)0x61);    // POPA
01152                 phys_writeb(phyDospage+19,(Bit8u)0xCF);    // IRET: restores flags, CS, IP
01153  
01154                 // IPX version 2.12
01155                 //phys_writeb(phyDospage+27,(Bit8u)0x2);
01156                 //phys_writeb(phyDospage+28,(Bit8u)0x12);
01157                 //IPXVERpointer = RealMake(dospage,27);
01158 
01159                 RealPt ESRRoutineBase = RealMake(dospage, 0);
01160 
01161                 // Interrupt enabling
01162                 RealSetVec(0x73,ESRRoutineBase,old_73_vector);  // IRQ11
01163                 IO_WriteB(0xa1,IO_ReadB(0xa1)&(~8));                    // enable IRQ11
01164 
01165                 PROGRAMS_MakeFile("IPXNET.COM",IPXNET_ProgramStart);
01166 
01167                 ipx_init = true;
01168         }
01169 
01170         ~IPX() {
01171                 // FIXME: This now gets called at DOSBox exit.
01172                 //        We should do this elsewhere, such as booting a guest OS or "power off"
01173                 PIC_RemoveEvents(IPX_AES_EventHandler);
01174                 if(!ipx_init) return;
01175 
01176                 if(isIpxServer) {
01177                         isIpxServer = false;
01178                         IPX_StopServer();
01179                 }
01180                 DisconnectFromServer(false);
01181 
01182                 DOS_DelMultiplexHandler(IPX_Multiplex);
01183                 RealSetVec(0x73,old_73_vector);
01184                 IO_WriteB(0xa1,IO_ReadB(0xa1)|8);       // disable IRQ11
01185    
01186                 PhysPt phyDospage = PhysMake(dospage,0);
01187                 for(Bit8u i = 0;i < 32;i++)
01188                         phys_writeb(phyDospage+i,(Bit8u)0x00);
01189 
01190                 VFILE_Remove("IPXNET.COM");
01191         }
01192 };
01193 
01194 static IPX* test;
01195 
01196 void IPX_ShutDown(Section*) {
01197         delete test;    
01198 }
01199 
01200 void IPX_OnReset(Section*) {
01201         if (test == NULL) {
01202                 LOG(LOG_MISC,LOG_DEBUG)("Allocating IPX emulation");
01203                 test = new IPX(control->GetSection("ipx"));
01204         }
01205 }
01206 
01207 void IPX_Init() {
01208         LOG(LOG_MISC,LOG_DEBUG)("Initializing IPX emulation");
01209 
01210         AddExitFunction(AddExitFunctionFuncPair(IPX_ShutDown),true);
01211         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(IPX_OnReset));
01212 }
01213 
01214 #endif