DOSBox-X
|
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, ®Header.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 *)®Header; 00529 regPacket.len = sizeof(regHeader); 00530 regPacket.maxlen = sizeof(regHeader); 00531 regPacket.channel = UDPChannel; 00532 00533 SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, ®Packet); 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 *)®Header; 00556 regPacket.len = sizeof(regHeader); 00557 regPacket.maxlen = sizeof(regHeader); 00558 regPacket.channel = UDPChannel; 00559 00560 result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, ®Packet); 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, ®Packet); 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 *)®Header; 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, ®Packet); 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, ®Packet); 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