1 /*
2  *  Copyright (C) 2002-2010  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 /* $Id: ipx.cpp,v 1.17 2009-05-27 09:15:41 qbix79 Exp $ */
20 
21 #include "dosbox.h"
22 
23 #if C_IPX
24 
25 #include <string.h>
26 #include <time.h>
27 #include <stdio.h>
28 #include "cross.h"
29 #include "support.h"
30 #include "cpu.h"
31 #include "regs.h"
32 #include "inout.h"
33 #include "setup.h"
34 #include "debug.h"
35 #include "callback.h"
36 #include "dos_system.h"
37 #include "mem.h"
38 #include "ipx.h"
39 #include "ipxserver.h"
40 #include "timer.h"
41 #include "SDL_net.h"
42 #include "programs.h"
43 #include "pic.h"
44 
45 #define SOCKTABLESIZE	150 // DOS IPX driver was limited to 150 open sockets
46 
47 struct ipxnetaddr {
48 	Uint8 netnum[4];   // Both are big endian
49 	Uint8 netnode[6];
50 } localIpxAddr;
51 
52 Bit32u udpPort;
53 bool isIpxServer;
54 bool isIpxConnected;
55 IPaddress ipxServConnIp;			// IPAddress for client connection to server
56 UDPsocket ipxClientSocket;
57 int UDPChannel;						// Channel used by UDP connection
58 Bit8u recvBuffer[IPXBUFFERSIZE];	// Incoming packet buffer
59 
60 static RealPt ipx_callback;
61 
62 SDLNet_SocketSet clientSocketSet;
63 
64 packetBuffer incomingPacket;
65 
66 static Bit16u socketCount;
67 static Bit16u opensockets[SOCKTABLESIZE];
68 
swapByte(Bit16u sockNum)69 static Bit16u swapByte(Bit16u sockNum) {
70 	return (((sockNum>> 8)) | (sockNum << 8));
71 }
72 
UnpackIP(PackedIP ipPack,IPaddress * ipAddr)73 void UnpackIP(PackedIP ipPack, IPaddress * ipAddr) {
74 	ipAddr->host = ipPack.host;
75 	ipAddr->port = ipPack.port;
76 }
77 
PackIP(IPaddress ipAddr,PackedIP * ipPack)78 void PackIP(IPaddress ipAddr, PackedIP *ipPack) {
79 	ipPack->host = ipAddr.host;
80 	ipPack->port = ipAddr.port;
81 }
82 
83 ECBClass *ECBList;  // Linked list of ECB's
84 ECBClass* ESRList;	// ECBs waiting to be ESR notified
85 
86 #ifdef IPX_DEBUGMSG
87 Bitu ECBSerialNumber = 0;
88 Bitu ECBAmount = 0;
89 #endif
90 
91 
ECBClass(Bit16u segment,Bit16u offset)92 ECBClass::ECBClass(Bit16u segment, Bit16u offset) {
93 	ECBAddr = RealMake(segment, offset);
94 	databuffer = 0;
95 
96 #ifdef IPX_DEBUGMSG
97 	SerialNumber = ECBSerialNumber;
98 	ECBSerialNumber++;
99 	ECBAmount++;
100 
101 	LOG_IPX("ECB: SN%7d created.   Number of ECBs: %3d, ESR %4x:%4x, ECB %4x:%4x",
102 		SerialNumber,ECBAmount,
103 		real_readw(RealSeg(ECBAddr),
104 		RealOff(ECBAddr)+6),
105 		real_readw(RealSeg(ECBAddr),
106 		RealOff(ECBAddr)+4),segment,offset);
107 #endif
108 	isInESRList = false;
109 	prevECB = NULL;
110 	nextECB = NULL;
111 
112 	if (ECBList == NULL)
113 		ECBList = this;
114 	else {
115 		// Transverse the list until we hit the end
116 		ECBClass *useECB = ECBList;
117 
118 		while(useECB->nextECB != NULL)
119 			useECB = useECB->nextECB;
120 
121 		useECB->nextECB = this;
122 		this->prevECB = useECB;
123 	}
124 
125 	iuflag = getInUseFlag();
126 	mysocket = getSocket();
127 }
writeDataBuffer(Bit8u * buffer,Bit16u length)128 void ECBClass::writeDataBuffer(Bit8u* buffer, Bit16u length) {
129 	if(databuffer!=0) delete [] databuffer;
130 	databuffer = new Bit8u[length];
131 	memcpy(databuffer,buffer,length);
132 	buflen=length;
133 
134 }
writeData()135 bool ECBClass::writeData() {
136 	Bitu length=buflen;
137 	Bit8u* buffer = databuffer;
138 	fragmentDescriptor tmpFrag;
139 	setInUseFlag(USEFLAG_AVAILABLE);
140 	Bitu fragCount = getFragCount();
141 	Bitu bufoffset = 0;
142 	for(Bitu i = 0;i < fragCount;i++) {
143 		getFragDesc(i,&tmpFrag);
144 		for(Bitu t = 0;t < tmpFrag.size;t++) {
145 			real_writeb(tmpFrag.segment, tmpFrag.offset + t, buffer[bufoffset]);
146 			bufoffset++;
147 			if(bufoffset >= length) {
148 				setCompletionFlag(COMP_SUCCESS);
149 				setImmAddress(&buffer[22]);  // Write in source node
150 				return true;
151 			}
152 		}
153 	}
154 	if(bufoffset < length) {
155 		setCompletionFlag(COMP_MALFORMED);
156 		return false;
157 	}
158 	return false;
159 }
160 
getSocket(void)161 Bit16u ECBClass::getSocket(void) {
162 	return swapByte(real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 0xa));
163 }
164 
getInUseFlag(void)165 Bit8u ECBClass::getInUseFlag(void) {
166 	return real_readb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8);
167 }
168 
setInUseFlag(Bit8u flagval)169 void ECBClass::setInUseFlag(Bit8u flagval) {
170 	iuflag = flagval;
171 	real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x8, flagval);
172 }
173 
setCompletionFlag(Bit8u flagval)174 void ECBClass::setCompletionFlag(Bit8u flagval) {
175 	real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr) + 0x9, flagval);
176 }
177 
getFragCount(void)178 Bit16u ECBClass::getFragCount(void) {
179 	return real_readw(RealSeg(ECBAddr), RealOff(ECBAddr) + 34);
180 }
181 
getFragDesc(Bit16u descNum,fragmentDescriptor * fragDesc)182 void ECBClass::getFragDesc(Bit16u descNum, fragmentDescriptor *fragDesc) {
183 	Bit16u memoff = RealOff(ECBAddr) + 30 + ((descNum+1) * 6);
184 	fragDesc->offset = real_readw(RealSeg(ECBAddr), memoff);
185 	memoff += 2;
186 	fragDesc->segment = real_readw(RealSeg(ECBAddr), memoff);
187 	memoff += 2;
188 	fragDesc->size = real_readw(RealSeg(ECBAddr), memoff);
189 }
190 
getESRAddr(void)191 RealPt ECBClass::getESRAddr(void) {
192 	return RealMake(real_readw(RealSeg(ECBAddr),
193 		RealOff(ECBAddr)+6),
194 		real_readw(RealSeg(ECBAddr),
195 		RealOff(ECBAddr)+4));
196 }
197 
NotifyESR(void)198 void ECBClass::NotifyESR(void) {
199 	Bit32u ESRval = real_readd(RealSeg(ECBAddr), RealOff(ECBAddr)+4);
200 	if(ESRval || databuffer) { // databuffer: write data at realmode/v86 time
201 		// LOG_IPX("ECB: SN%7d to be notified.", SerialNumber);
202 		// take the ECB out of the current list
203 		if(prevECB == NULL) {	// was the first in the list
204 			ECBList = nextECB;
205 			if(ECBList != NULL) ECBList->prevECB = NULL;
206 		} else {		// not the first
207 			prevECB->nextECB = nextECB;
208 			if(nextECB != NULL) nextECB->prevECB = prevECB;
209 		}
210 
211 		nextECB = NULL;
212 		// put it to the notification queue
213 		if(ESRList==NULL) {
214 			ESRList = this;
215 			prevECB = NULL;
216 		} else  {// put to end of ESR list
217 			ECBClass* useECB = ESRList;
218 
219 			while(useECB->nextECB != NULL)
220 				useECB = useECB->nextECB;
221 
222 			useECB->nextECB = this;
223 			prevECB = useECB;
224 		}
225 		isInESRList = true;
226 		PIC_ActivateIRQ(11);
227 	}
228 	// this one does not want to be notified, delete it right away
229 	else delete this;
230 }
231 
setImmAddress(Bit8u * immAddr)232 void ECBClass::setImmAddress(Bit8u *immAddr) {
233 	for(Bitu i=0;i<6;i++)
234 		real_writeb(RealSeg(ECBAddr), RealOff(ECBAddr)+28+i, immAddr[i]);
235 }
236 
getImmAddress(Bit8u * immAddr)237 void ECBClass::getImmAddress(Bit8u* immAddr) {
238 	for(Bitu i=0;i<6;i++)
239 		immAddr[i] = real_readb(RealSeg(ECBAddr), RealOff(ECBAddr)+28+i);
240 }
241 
~ECBClass()242 ECBClass::~ECBClass() {
243 #ifdef IPX_DEBUGMSG
244 	ECBAmount--;
245 	LOG_IPX("ECB: SN%7d destroyed. Remaining ECBs: %3d", SerialNumber,ECBAmount);
246 #endif
247 
248 	if(isInESRList) {
249 		// in ESR list, always the first element is deleted.
250 		ESRList=nextECB;
251 	} else {
252 		if(prevECB == NULL) {	// was the first in the list
253 			ECBList = nextECB;
254 			if(ECBList != NULL) ECBList->prevECB = NULL;
255 		} else {	// not the first
256 			prevECB->nextECB = nextECB;
257 			if(nextECB != NULL) nextECB->prevECB = prevECB;
258 		}
259 	}
260 	if(databuffer!=0) delete [] databuffer;
261 }
262 
263 
264 
sockInUse(Bit16u sockNum)265 static bool sockInUse(Bit16u sockNum) {
266 	for(Bitu i=0;i<socketCount;i++) {
267 		if (opensockets[i] == sockNum) return true;
268 	}
269 	return false;
270 }
271 
OpenSocket(void)272 static void OpenSocket(void) {
273 	Bit16u sockNum, sockAlloc;
274 	sockNum = swapByte(reg_dx);
275 
276 	if(socketCount >= SOCKTABLESIZE) {
277 		reg_al = 0xfe; // Socket table full
278 		return;
279 	}
280 
281 	if(sockNum == 0x0000) {
282 		// Dynamic socket allocation
283 		sockAlloc = 0x4002;
284 		while(sockInUse(sockAlloc) && (sockAlloc < 0x7fff)) sockAlloc++;
285 		if(sockAlloc > 0x7fff) {
286 			// I have no idea how this could happen if the IPX driver
287 			// is limited to 150 open sockets at a time
288 			LOG_MSG("IPX: Out of dynamic sockets");
289 		}
290 		sockNum = sockAlloc;
291 	} else {
292 		if(sockInUse(sockNum)) {
293 			reg_al = 0xff; // Socket already open
294 			return;
295 		}
296 	}
297 
298 	opensockets[socketCount] = sockNum;
299 	socketCount++;
300 
301 	reg_al = 0x00; // Success
302 	reg_dx = swapByte(sockNum);  // Convert back to big-endian
303 }
304 
CloseSocket(void)305 static void CloseSocket(void) {
306 	Bit16u sockNum, i;
307 	ECBClass* tmpECB = ECBList;
308 	ECBClass* tmp2ECB = ECBList;
309 
310 	sockNum = swapByte(reg_dx);
311 	if(!sockInUse(sockNum)) return;
312 
313 	for(i=0;i<socketCount-1;i++) {
314 		if (opensockets[i] == sockNum) {
315 			// Realign list of open sockets
316 			memcpy(&opensockets[i], &opensockets[i+1], SOCKTABLESIZE - (i + 1));
317 			break;
318 		}
319 	}
320 	--socketCount;
321 
322 	// delete all ECBs of that socket
323 	while(tmpECB!=0) {
324 		tmp2ECB = tmpECB->nextECB;
325 		if(tmpECB->getSocket()==sockNum) {
326 			tmpECB->setCompletionFlag(COMP_CANCELLED);
327 			tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
328 			delete tmpECB;
329 		}
330 		tmpECB = tmp2ECB;
331 	}
332 }
333 
334 //static RealPt IPXVERpointer;
335 
IPX_Multiplex(void)336 static bool IPX_Multiplex(void) {
337 	if(reg_ax != 0x7a00) return false;
338 	reg_al = 0xff;
339 	SegSet16(es,RealSeg(ipx_callback));
340 	reg_di = RealOff(ipx_callback);
341 
342 	//reg_bx = RealOff(IPXVERpointer);
343 	//reg_cx = RealSeg(ipx_callback);
344 	return true;
345 }
346 
IPX_AES_EventHandler(Bitu param)347 static void IPX_AES_EventHandler(Bitu param)
348 {
349 	ECBClass* tmpECB = ECBList;
350 	ECBClass* tmp2ECB;
351 	while(tmpECB!=0) {
352 		tmp2ECB = tmpECB->nextECB;
353 		if(tmpECB->iuflag==USEFLAG_AESCOUNT && param==(Bitu)tmpECB->ECBAddr) {
354 			tmpECB->setCompletionFlag(COMP_SUCCESS);
355 			tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
356 			tmpECB->NotifyESR();
357 			// LOG_IPX("AES Notification: ECB S/N %d",tmpECB->SerialNumber);
358 			return;
359 		}
360 		tmpECB = tmp2ECB;
361 	}
362 	LOG_MSG("!!!! Rouge AES !!!!" );
363 }
364 
365 static void sendPacket(ECBClass* sendecb);
366 
handleIpxRequest(void)367 static void handleIpxRequest(void) {
368 	ECBClass *tmpECB;
369 
370 	switch (reg_bx) {
371 		case 0x0000:	// Open socket
372 			OpenSocket();
373 			LOG_IPX("IPX: Open socket %4x", swapByte(reg_dx));
374 			break;
375 		case 0x0001:	// Close socket
376 			LOG_IPX("IPX: Close socket %4x", swapByte(reg_dx));
377 			CloseSocket();
378 			break;
379 		case 0x0002:	// get local target
380 						// es:si
381 						// Currently no support for multiple networks
382 
383 			for(Bitu i = 0; i < 6; i++)
384 				real_writeb(SegValue(es),reg_di+i,real_readb(SegValue(es),reg_si+i+4));
385 
386 			reg_cx=1;		// time ticks expected
387 			reg_al=0x00;	//success
388 			break;
389 
390 		case 0x0003:		// Send packet
391 			tmpECB = new ECBClass(SegValue(es),reg_si);
392 			if(!incomingPacket.connected) {
393 				tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
394 				tmpECB->setCompletionFlag(COMP_UNDELIVERABLE);
395 				delete tmpECB;	// not notify?
396 				reg_al = 0xff; // Failure
397 			} else {
398 				tmpECB->setInUseFlag(USEFLAG_SENDING);
399 				//LOG_IPX("IPX: Sending packet on %4x", tmpECB->getSocket());
400 				reg_al = 0x00; // Success
401 				sendPacket(tmpECB);
402 			}
403 
404 			break;
405 		case 0x0004:  // Listen for packet
406 			tmpECB = new ECBClass(SegValue(es),reg_si);
407 			// LOG_IPX("ECB: SN%7d RECEIVE.", tmpECB->SerialNumber);
408 			if(!sockInUse(tmpECB->getSocket())) {  // Socket is not open
409 				reg_al = 0xff;
410 				tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
411 				tmpECB->setCompletionFlag(COMP_HARDWAREERROR);
412 				delete tmpECB;
413 			} else {
414 				reg_al = 0x00;  // Success
415 				tmpECB->setInUseFlag(USEFLAG_LISTENING);
416 				/*LOG_IPX("IPX: Listen for packet on 0x%4x - ESR address %4x:%4x",
417 					tmpECB->getSocket(),
418 					RealSeg(tmpECB->getESRAddr()),
419 					RealOff(tmpECB->getESRAddr()));*/
420 			}
421 			break;
422 
423 		case 0x0005:	// SCHEDULE IPX EVENT
424 		case 0x0007:	// SCHEDULE SPECIAL IPX EVENT
425 		{
426 			tmpECB = new ECBClass(SegValue(es),reg_si);
427 			// LOG_IPX("ECB: SN%7d AES. T=%fms.", tmpECB->SerialNumber,
428 			//	(1000.0f/(1193182.0f/65536.0f))*(float)reg_ax);
429 			PIC_AddEvent(IPX_AES_EventHandler,
430 				(1000.0f/(1193182.0f/65536.0f))*(float)reg_ax,(Bitu)tmpECB->ECBAddr);
431 			tmpECB->setInUseFlag(USEFLAG_AESCOUNT);
432 			break;
433 		}
434 		case 0x0006:	// cancel operation
435 		{
436 			RealPt ecbaddress = RealMake(SegValue(es),reg_si);
437 			ECBClass* tmpECB= ECBList;
438 			ECBClass* tmp2ECB;
439 			while(tmpECB) {
440 				tmp2ECB=tmpECB->nextECB;
441 				if(tmpECB->ECBAddr == ecbaddress) {
442 					if(tmpECB->getInUseFlag()==USEFLAG_AESCOUNT)
443 						PIC_RemoveSpecificEvents(IPX_AES_EventHandler,(Bitu)ecbaddress);
444 					tmpECB->setInUseFlag(USEFLAG_AVAILABLE);
445 					tmpECB->setCompletionFlag(COMP_CANCELLED);
446 					delete tmpECB;
447 					reg_al=0;	// Success
448 					LOG_IPX("IPX: ECB canceled.");
449 					return;
450 				}
451 				tmpECB=tmp2ECB;
452 			}
453 			reg_al=0xff;	// Fail
454 			break;
455 		}
456 		case 0x0008:		// Get interval marker
457 			reg_ax = mem_readw(0x46c); // BIOS_TIMER
458 			break;
459 		case 0x0009:		// Get internetwork address
460 		{
461 			LOG_IPX("IPX: Get internetwork address %2x:%2x:%2x:%2x:%2x:%2x",
462 				localIpxAddr.netnode[5], localIpxAddr.netnode[4],
463 				localIpxAddr.netnode[3], localIpxAddr.netnode[2],
464 				localIpxAddr.netnode[1], localIpxAddr.netnode[0]);
465 
466 			Bit8u * addrptr = (Bit8u *)&localIpxAddr;
467 			for(Bit16u i=0;i<10;i++)
468 				real_writeb(SegValue(es),reg_si+i,addrptr[i]);
469 			break;
470 		}
471 		case 0x000a:		// Relinquish control
472 			break;			// Idle thingy
473 
474 		case 0x000b:		// Disconnect from Target
475 			break;			// We don't even connect
476 
477 		case 0x000d:		// get packet size
478 			reg_cx=0;		// retry count
479 			reg_ax=1024;	// real implementation returns 1024
480 			break;
481 
482 		case 0x0010:		// SPX install check
483 			reg_al=0;		// SPX not installed
484 			break;
485 
486 		case 0x001a:		// get driver maximum packet size
487 			reg_cx=0;		// retry count
488 			reg_ax=IPXBUFFERSIZE;	// max packet size: something near the
489 									// ethernet packet size
490 			break;
491 
492 		default:
493 			LOG_MSG("Unhandled IPX function: %4x", reg_bx);
494 			break;
495 	}
496 }
497 
498 // Entrypoint handler
IPX_Handler(void)499 Bitu IPX_Handler(void) {
500 	handleIpxRequest();
501 	return CBRET_NONE;
502 }
503 
504 // INT 7A handler
IPX_IntHandler(void)505 Bitu IPX_IntHandler(void) {
506 	handleIpxRequest();
507 	return CBRET_NONE;
508 }
509 
pingAck(IPaddress retAddr)510 static void pingAck(IPaddress retAddr) {
511 	IPXHeader regHeader;
512 	UDPpacket regPacket;
513 	Bits result;
514 
515 	SDLNet_Write16(0xffff, regHeader.checkSum);
516 	SDLNet_Write16(sizeof(regHeader), regHeader.length);
517 
518 	SDLNet_Write32(0, regHeader.dest.network);
519 	PackIP(retAddr, &regHeader.dest.addr.byIP);
520 	SDLNet_Write16(0x2, regHeader.dest.socket);
521 
522 	SDLNet_Write32(0, regHeader.src.network);
523 	memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(regHeader.src.addr.byNode.node));
524 	SDLNet_Write16(0x2, regHeader.src.socket);
525 	regHeader.transControl = 0;
526 	regHeader.pType = 0x0;
527 
528 	regPacket.data = (Uint8 *)&regHeader;
529 	regPacket.len = sizeof(regHeader);
530 	regPacket.maxlen = sizeof(regHeader);
531 	regPacket.channel = UDPChannel;
532 
533 	result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
534 }
535 
pingSend(void)536 static void pingSend(void) {
537 	IPXHeader regHeader;
538 	UDPpacket regPacket;
539 	Bits result;
540 
541 	SDLNet_Write16(0xffff, regHeader.checkSum);
542 	SDLNet_Write16(sizeof(regHeader), regHeader.length);
543 
544 	SDLNet_Write32(0, regHeader.dest.network);
545 	regHeader.dest.addr.byIP.host = 0xffffffff;
546 	regHeader.dest.addr.byIP.port = 0xffff;
547 	SDLNet_Write16(0x2, regHeader.dest.socket);
548 
549 	SDLNet_Write32(0, regHeader.src.network);
550 	memcpy(regHeader.src.addr.byNode.node, localIpxAddr.netnode, sizeof(regHeader.src.addr.byNode.node));
551 	SDLNet_Write16(0x2, regHeader.src.socket);
552 	regHeader.transControl = 0;
553 	regHeader.pType = 0x0;
554 
555 	regPacket.data = (Uint8 *)&regHeader;
556 	regPacket.len = sizeof(regHeader);
557 	regPacket.maxlen = sizeof(regHeader);
558 	regPacket.channel = UDPChannel;
559 
560 	result = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
561 	if(!result) {
562 		LOG_MSG("IPX: SDLNet_UDP_Send: %s\n", SDLNet_GetError());
563 	}
564 }
565 
receivePacket(Bit8u * buffer,Bit16s bufSize)566 static void receivePacket(Bit8u *buffer, Bit16s bufSize) {
567 	ECBClass *useECB;
568 	ECBClass *nextECB;
569 	Bit16u *bufword = (Bit16u *)buffer;
570 	Bit16u useSocket = swapByte(bufword[8]);
571 	IPXHeader * tmpHeader;
572 	tmpHeader = (IPXHeader *)buffer;
573 
574 	// Check to see if ping packet
575 	if(useSocket == 0x2) {
576 		// Is this a broadcast?
577 		if((tmpHeader->dest.addr.byIP.host == 0xffffffff) &&
578 			(tmpHeader->dest.addr.byIP.port == 0xffff)) {
579 			// Yes.  We should return the ping back to the sender
580 			IPaddress tmpAddr;
581 			UnpackIP(tmpHeader->src.addr.byIP, &tmpAddr);
582 			pingAck(tmpAddr);
583 			return;
584 		}
585 	}
586 
587 	useECB = ECBList;
588 	while(useECB != NULL)
589 	{
590 		nextECB = useECB->nextECB;
591 		if(useECB->iuflag == USEFLAG_LISTENING && useECB->mysocket == useSocket) {
592 			useECB->writeDataBuffer(buffer, bufSize);
593 			useECB->NotifyESR();
594 			return;
595 		}
596 		useECB = nextECB;
597 	}
598 	LOG_IPX("IPX: RX Packet loss!");
599 }
600 
IPX_ClientLoop(void)601 static void IPX_ClientLoop(void) {
602 	int numrecv;
603 	UDPpacket inPacket;
604 	inPacket.data = (Uint8 *)recvBuffer;
605 	inPacket.maxlen = IPXBUFFERSIZE;
606 	inPacket.channel = UDPChannel;
607 
608 	// Its amazing how much simpler UDP is than TCP
609 	numrecv = SDLNet_UDP_Recv(ipxClientSocket, &inPacket);
610 	if(numrecv) receivePacket(inPacket.data, inPacket.len);
611 }
612 
613 
DisconnectFromServer(bool unexpected)614 void DisconnectFromServer(bool unexpected) {
615 	if(unexpected) LOG_MSG("IPX: Server disconnected unexpectedly");
616 	if(incomingPacket.connected) {
617 		incomingPacket.connected = false;
618 		TIMER_DelTickHandler(&IPX_ClientLoop);
619 		SDLNet_UDP_Close(ipxClientSocket);
620 	}
621 }
622 
sendPacket(ECBClass * sendecb)623 static void sendPacket(ECBClass* sendecb) {
624 	Bit8u outbuffer[IPXBUFFERSIZE];
625 	fragmentDescriptor tmpFrag;
626 	Bit16u i, fragCount,t;
627 	Bit16s packetsize;
628 	Bit16u *wordptr;
629 	Bits result;
630 	UDPpacket outPacket;
631 
632 	sendecb->setInUseFlag(USEFLAG_AVAILABLE);
633 	packetsize = 0;
634 	fragCount = sendecb->getFragCount();
635 	for(i=0;i<fragCount;i++) {
636 		sendecb->getFragDesc(i,&tmpFrag);
637 		if(i==0) {
638 			// Fragment containing IPX header
639 			// Must put source address into header
640 			Bit8u * addrptr;
641 
642 			// source netnum
643 			addrptr = (Bit8u *)&localIpxAddr.netnum;
644 			for(Bit16u m=0;m<4;m++) {
645 				real_writeb(tmpFrag.segment,tmpFrag.offset+m+18,addrptr[m]);
646 			}
647 			// source node number
648 			addrptr = (Bit8u *)&localIpxAddr.netnode;
649 			for(Bit16u m=0;m<6;m++) {
650 				real_writeb(tmpFrag.segment,tmpFrag.offset+m+22,addrptr[m]);
651 			}
652 			// Source socket
653 			real_writew(tmpFrag.segment,tmpFrag.offset+28, swapByte(sendecb->getSocket()));
654 
655 			// blank checksum
656 			real_writew(tmpFrag.segment,tmpFrag.offset, 0xffff);
657 		}
658 
659 		for(t=0;t<tmpFrag.size;t++) {
660 			outbuffer[packetsize] = real_readb(tmpFrag.segment, tmpFrag.offset + t);
661 			packetsize++;
662 			if(packetsize>=IPXBUFFERSIZE) {
663 				LOG_MSG("IPX: Packet size to be sent greater than %d bytes.", IPXBUFFERSIZE);
664 				sendecb->setCompletionFlag(COMP_UNDELIVERABLE);
665 				sendecb->NotifyESR();
666 				return;
667 			}
668 		}
669 	}
670 
671 	// Add length and source socket to IPX header
672 	wordptr = (Bit16u *)&outbuffer[0];
673 	// Blank CRC
674 	//wordptr[0] = 0xffff;
675 	// Length
676 	wordptr[1] = swapByte(packetsize);
677 	// Source socket
678 	//wordptr[14] = swapByte(sendecb->getSocket());
679 
680 	sendecb->getFragDesc(0,&tmpFrag);
681 	real_writew(tmpFrag.segment,tmpFrag.offset+2, swapByte(packetsize));
682 
683 
684 	Bit8u immedAddr[6];
685 	sendecb->getImmAddress(immedAddr);
686 	// filter out broadcasts and local loopbacks
687 	// Real implementation uses the ImmedAddr to check wether this is a broadcast
688 
689 	bool islocalbroadcast=true;
690 	bool isloopback=true;
691 
692 	Bit8u * addrptr;
693 
694 	addrptr = (Bit8u *)&localIpxAddr.netnum;
695 	for(Bitu m=0;m<4;m++) {
696 		if(addrptr[m]!=outbuffer[m+0x6])isloopback=false;
697 	}
698 	addrptr = (Bit8u *)&localIpxAddr.netnode;
699 	for(Bitu m=0;m<6;m++) {
700 		if(addrptr[m]!=outbuffer[m+0xa])isloopback=false;
701 		if(immedAddr[m]!=0xff) islocalbroadcast=false;
702 	}
703 	LOG_IPX("SEND crc:%2x",packetCRC(&outbuffer[0], packetsize));
704 	if(!isloopback) {
705 		outPacket.channel = UDPChannel;
706 		outPacket.data = (Uint8 *)&outbuffer[0];
707 		outPacket.len = packetsize;
708 		outPacket.maxlen = packetsize;
709 		// Since we're using a channel, we won't send the IP address again
710 		result = SDLNet_UDP_Send(ipxClientSocket, UDPChannel, &outPacket);
711 
712 		if(result == 0) {
713 			LOG_MSG("IPX: Could not send packet: %s", SDLNet_GetError());
714 			sendecb->setCompletionFlag(COMP_HARDWAREERROR);
715 			sendecb->NotifyESR();
716 			DisconnectFromServer(true);
717 			return;
718 		} else {
719 			sendecb->setCompletionFlag(COMP_SUCCESS);
720 			LOG_IPX("Packet sent: size: %d",packetsize);
721 		}
722 	}
723 	else sendecb->setCompletionFlag(COMP_SUCCESS);
724 
725 	if(isloopback||islocalbroadcast) {
726 		// Send packet back to ourselves.
727 		receivePacket(&outbuffer[0],packetsize);
728 		LOG_IPX("Packet back: loopback:%d, broadcast:%d",isloopback,islocalbroadcast);
729 	}
730 	sendecb->NotifyESR();
731 }
732 
pingCheck(IPXHeader * outHeader)733 static bool pingCheck(IPXHeader * outHeader) {
734 	char buffer[1024];
735 	Bits result;
736 	UDPpacket regPacket;
737 	IPXHeader *regHeader;
738 	regPacket.data = (Uint8 *)buffer;
739 	regPacket.maxlen = sizeof(buffer);
740 	regPacket.channel = UDPChannel;
741 	regHeader = (IPXHeader *)buffer;
742 
743 	result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
744 	if (result != 0) {
745 		memcpy(outHeader, regHeader, sizeof(IPXHeader));
746 		return true;
747 	}
748 	return false;
749 }
750 
ConnectToServer(char const * strAddr)751 bool ConnectToServer(char const *strAddr) {
752 	int numsent;
753 	UDPpacket regPacket;
754 	IPXHeader regHeader;
755 	if(!SDLNet_ResolveHost(&ipxServConnIp, strAddr, (Bit16u)udpPort)) {
756 
757 		// Generate the MAC address.  This is made by zeroing out the first two
758 		// octets and then using the actual IP address for the last 4 octets.
759 		// This idea is from the IPX over IP implementation as specified in RFC 1234:
760 		// http://www.faqs.org/rfcs/rfc1234.html
761 
762 		// Select an anonymous UDP port
763 		ipxClientSocket = SDLNet_UDP_Open(0);
764 		if(ipxClientSocket) {
765 			// Bind UDP port to address to channel
766 			UDPChannel = SDLNet_UDP_Bind(ipxClientSocket,-1,&ipxServConnIp);
767 			//ipxClientSocket = SDLNet_TCP_Open(&ipxServConnIp);
768 			SDLNet_Write16(0xffff, regHeader.checkSum);
769 			SDLNet_Write16(sizeof(regHeader), regHeader.length);
770 
771 			// Echo packet with zeroed dest and src is a server registration packet
772 			SDLNet_Write32(0, regHeader.dest.network);
773 			regHeader.dest.addr.byIP.host = 0x0;
774 			regHeader.dest.addr.byIP.port = 0x0;
775 			SDLNet_Write16(0x2, regHeader.dest.socket);
776 
777 			SDLNet_Write32(0, regHeader.src.network);
778 			regHeader.src.addr.byIP.host = 0x0;
779 			regHeader.src.addr.byIP.port = 0x0;
780 			SDLNet_Write16(0x2, regHeader.src.socket);
781 			regHeader.transControl = 0;
782 
783 			regPacket.data = (Uint8 *)&regHeader;
784 			regPacket.len = sizeof(regHeader);
785 			regPacket.maxlen = sizeof(regHeader);
786 			regPacket.channel = UDPChannel;
787 			// Send registration string to server.  If server doesn't get
788 			// this, client will not be registered
789 			numsent = SDLNet_UDP_Send(ipxClientSocket, regPacket.channel, &regPacket);
790 
791 			if(!numsent) {
792 				LOG_MSG("IPX: Unable to connect to server: %s", SDLNet_GetError());
793 				SDLNet_UDP_Close(ipxClientSocket);
794 				return false;
795 			} else {
796 				// Wait for return packet from server.
797 				// This will contain our IPX address and port num
798 				Bits result;
799 				Bit32u ticks, elapsed;
800 				ticks = GetTicks();
801 
802 				while(true) {
803 					elapsed = GetTicks() - ticks;
804 					if(elapsed > 5000) {
805 						LOG_MSG("Timeout connecting to server at %s", strAddr);
806 						SDLNet_UDP_Close(ipxClientSocket);
807 
808 						return false;
809 					}
810 					CALLBACK_Idle();
811 					result = SDLNet_UDP_Recv(ipxClientSocket, &regPacket);
812 					if (result != 0) {
813 						memcpy(localIpxAddr.netnode, regHeader.dest.addr.byNode.node, sizeof(localIpxAddr.netnode));
814 						memcpy(localIpxAddr.netnum, regHeader.dest.network, sizeof(localIpxAddr.netnum));
815 						break;
816 					}
817 
818 				}
819 
820 				LOG_MSG("IPX: Connected to server.  IPX address is %d:%d:%d:%d:%d:%d", CONVIPX(localIpxAddr.netnode));
821 
822 				incomingPacket.connected = true;
823 				TIMER_AddTickHandler(&IPX_ClientLoop);
824 				return true;
825 			}
826 		} else {
827 			LOG_MSG("IPX: Unable to open socket");
828 		}
829 	} else {
830 		LOG_MSG("IPX: Unable resolve connection to server");
831 	}
832 	return false;
833 }
834 
IPX_NetworkInit()835 void IPX_NetworkInit() {
836 
837 	localIpxAddr.netnum[0] = 0x0;
838 	localIpxAddr.netnum[1] = 0x0;
839 	localIpxAddr.netnum[2] = 0x0;
840 	localIpxAddr.netnum[3] = 0x1;
841 	localIpxAddr.netnode[0] = 0x00;
842 	localIpxAddr.netnode[1] = 0x00;
843 	localIpxAddr.netnode[2] = 0x00;
844 	localIpxAddr.netnode[3] = 0x00;
845 	localIpxAddr.netnode[4] = 0x00;
846 	localIpxAddr.netnode[5] = 0x00;
847 
848 	socketCount = 0;
849 	return;
850 }
851 
852 class IPXNET : public Program {
853 public:
HelpCommand(const char * helpStr)854 	void HelpCommand(const char *helpStr) {
855 		// Help on connect command
856 		if(strcasecmp("connect", helpStr) == 0) {
857 			WriteOut("IPXNET CONNECT opens a connection to an IPX tunneling server running on another\n");
858 			WriteOut("DosBox session.  The \"address\" parameter specifies the IP address or host name\n");
859 			WriteOut("of the server computer.  One can also specify the UDP port to use.  By default\n");
860 			WriteOut("IPXNET uses port 213, the assigned IANA port for IPX tunneling, for its\nconnection.\n\n");
861 			WriteOut("The syntax for IPXNET CONNECT is:\n\n");
862 			WriteOut("IPXNET CONNECT address <port>\n\n");
863 			return;
864 		}
865 		// Help on the disconnect command
866 		if(strcasecmp("disconnect", helpStr) == 0) {
867 			WriteOut("IPXNET DISCONNECT closes the connection to the IPX tunneling server.\n\n");
868 			WriteOut("The syntax for IPXNET DISCONNECT is:\n\n");
869 			WriteOut("IPXNET DISCONNECT\n\n");
870 			return;
871 		}
872 		// Help on the startserver command
873 		if(strcasecmp("startserver", helpStr) == 0) {
874 			WriteOut("IPXNET STARTSERVER starts and IPX tunneling server on this DosBox session.  By\n");
875 			WriteOut("default, the server will accept connections on UDP port 213, though this can be\n");
876 			WriteOut("changed.  Once the server is started, DosBox will automatically start a client\n");
877 			WriteOut("connection to the IPX tunneling server.\n\n");
878 			WriteOut("The syntax for IPXNET STARTSERVER is:\n\n");
879 			WriteOut("IPXNET STARTSERVER <port>\n\n");
880 			return;
881 		}
882 		// Help on the stop server command
883 		if(strcasecmp("stopserver", helpStr) == 0) {
884 			WriteOut("IPXNET STOPSERVER stops the IPX tunneling server running on this DosBox\nsession.");
885 			WriteOut("  Care should be taken to ensure that all other connections have\nterminated ");
886 			WriteOut("as well sinnce stoping the server may cause lockups on other\nmachines still using ");
887 			WriteOut("the IPX tunneling server.\n\n");
888 			WriteOut("The syntax for IPXNET STOPSERVER is:\n\n");
889 			WriteOut("IPXNET STOPSERVER\n\n");
890 			return;
891 		}
892 		// Help on the ping command
893 		if(strcasecmp("ping", helpStr) == 0) {
894 			WriteOut("IPXNET PING broadcasts a ping request through the IPX tunneled network.  In    \n");
895 			WriteOut("response, all other connected computers will respond to the ping and report\n");
896 			WriteOut("the time it took to receive and send the ping message.\n\n");
897 			WriteOut("The syntax for IPXNET PING is:\n\n");
898 			WriteOut("IPXNET PING\n\n");
899 			return;
900 		}
901 		// Help on the status command
902 		if(strcasecmp("status", helpStr) == 0) {
903 			WriteOut("IPXNET STATUS reports the current state of this DosBox's sessions IPX tunneling\n");
904 			WriteOut("network.  For a list of the computers connected to the network use the IPXNET \n");
905 			WriteOut("PING command.\n\n");
906 			WriteOut("The syntax for IPXNET STATUS is:\n\n");
907 			WriteOut("IPXNET STATUS\n\n");
908 			return;
909 		}
910 	}
911 
Run(void)912 	void Run(void)
913 	{
914 		WriteOut("IPX Tunneling utility for DosBox\n\n");
915 		if(!cmd->GetCount()) {
916 			WriteOut("The syntax of this command is:\n\n");
917 			WriteOut("IPXNET [ CONNECT | DISCONNECT | STARTSERVER | STOPSERVER | PING | HELP |\n         STATUS ]\n\n");
918 			return;
919 		}
920 
921 		if(cmd->FindCommand(1, temp_line)) {
922 			if(strcasecmp("help", temp_line.c_str()) == 0) {
923 				if(!cmd->FindCommand(2, temp_line)) {
924 					WriteOut("The following are valid IPXNET commands:\n\n");
925 					WriteOut("IPXNET CONNECT        IPXNET DISCONNECT       IPXNET STARTSERVER\n");
926 					WriteOut("IPXNET STOPSERVER     IPXNET PING             IPXNET STATUS\n\n");
927 					WriteOut("To get help on a specific command, type:\n\n");
928 					WriteOut("IPXNET HELP command\n\n");
929 
930 				} else {
931 					HelpCommand(temp_line.c_str());
932 					return;
933 				}
934 				return;
935 			}
936 			if(strcasecmp("startserver", temp_line.c_str()) == 0) {
937 				if(!isIpxServer) {
938 					if(incomingPacket.connected) {
939 						WriteOut("IPX Tunneling Client alreadu connected to another server.  Disconnect first.\n");
940 						return;
941 					}
942 					bool startsuccess;
943 					if(!cmd->FindCommand(2, temp_line)) {
944 						udpPort = 213;
945 					} else {
946 						udpPort = strtol(temp_line.c_str(), NULL, 10);
947 					}
948 					startsuccess = IPX_StartServer((Bit16u)udpPort);
949 					if(startsuccess) {
950 						WriteOut("IPX Tunneling Server started\n");
951 						isIpxServer = true;
952 						ConnectToServer("localhost");
953 					} else {
954 						WriteOut("IPX Tunneling Server failed to start.\n");
955 						if(udpPort < 1024) WriteOut("Try a port number above 1024. See IPXNET HELP CONNECT on how to specify a port.\n");
956 					}
957 				} else {
958 					WriteOut("IPX Tunneling Server already started\n");
959 				}
960 				return;
961 			}
962 			if(strcasecmp("stopserver", temp_line.c_str()) == 0) {
963 				if(!isIpxServer) {
964 					WriteOut("IPX Tunneling Server not running in this DosBox session.\n");
965 				} else {
966 					isIpxServer = false;
967 					DisconnectFromServer(false);
968 					IPX_StopServer();
969 					WriteOut("IPX Tunneling Server stopped.");
970 				}
971 				return;
972 			}
973 			if(strcasecmp("connect", temp_line.c_str()) == 0) {
974 				char strHost[1024];
975 				if(incomingPacket.connected) {
976 					WriteOut("IPX Tunneling Client already connected.\n");
977 					return;
978 				}
979 				if(!cmd->FindCommand(2, temp_line)) {
980 					WriteOut("IPX Server address not specified.\n");
981 					return;
982 				}
983 				strcpy(strHost, temp_line.c_str());
984 
985 				if(!cmd->FindCommand(3, temp_line)) {
986 					udpPort = 213;
987 				} else {
988 					udpPort = strtol(temp_line.c_str(), NULL, 10);
989 				}
990 
991 				if(ConnectToServer(strHost)) {
992                 	WriteOut("IPX Tunneling Client connected to server at %s.\n", strHost);
993 				} else {
994 					WriteOut("IPX Tunneling Client failed to connect to server at %s.\n", strHost);
995 				}
996 				return;
997 			}
998 
999 			if(strcasecmp("disconnect", temp_line.c_str()) == 0) {
1000 				if(!incomingPacket.connected) {
1001 					WriteOut("IPX Tunneling Client not connected.\n");
1002 					return;
1003 				}
1004 				// TODO: Send a packet to the server notifying of disconnect
1005 				WriteOut("IPX Tunneling Client disconnected from server.\n");
1006 				DisconnectFromServer(false);
1007 				return;
1008 			}
1009 
1010 			if(strcasecmp("status", temp_line.c_str()) == 0) {
1011 				WriteOut("IPX Tunneling Status:\n\n");
1012 				WriteOut("Server status: ");
1013 				if(isIpxServer) WriteOut("ACTIVE\n"); else WriteOut("INACTIVE\n");
1014 				WriteOut("Client status: ");
1015 				if(incomingPacket.connected) {
1016 					WriteOut("CONNECTED -- Server at %d.%d.%d.%d port %d\n", CONVIP(ipxServConnIp.host), udpPort);
1017 				} else {
1018 					WriteOut("DISCONNECTED\n");
1019 				}
1020 				if(isIpxServer) {
1021 					WriteOut("List of active connections:\n\n");
1022 					int i;
1023 					IPaddress *ptrAddr;
1024 					for(i=0;i<SOCKETTABLESIZE;i++) {
1025 						if(IPX_isConnectedToServer(i,&ptrAddr)) {
1026 							WriteOut("     %d.%d.%d.%d from port %d\n", CONVIP(ptrAddr->host), SDLNet_Read16(&ptrAddr->port));
1027 						}
1028 					}
1029 					WriteOut("\n");
1030 				}
1031 				return;
1032 			}
1033 
1034 			if(strcasecmp("ping", temp_line.c_str()) == 0) {
1035 				Bit32u ticks;
1036 				IPXHeader pingHead;
1037 
1038 				if(!incomingPacket.connected) {
1039 					WriteOut("IPX Tunneling Client not connected.\n");
1040 					return;
1041 				}
1042 				TIMER_DelTickHandler(&IPX_ClientLoop);
1043 				WriteOut("Sending broadcast ping:\n\n");
1044 				pingSend();
1045 				ticks = GetTicks();
1046 				while((GetTicks() - ticks) < 1500) {
1047 					CALLBACK_Idle();
1048 					if(pingCheck(&pingHead)) {
1049 						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);
1050 					}
1051 				}
1052 				TIMER_AddTickHandler(&IPX_ClientLoop);
1053 				return;
1054 			}
1055 		}
1056 	}
1057 };
1058 
IPXNET_ProgramStart(Program ** make)1059 static void IPXNET_ProgramStart(Program * * make) {
1060 	*make=new IPXNET;
1061 }
1062 
IPX_ESRHandler(void)1063 Bitu IPX_ESRHandler(void) {
1064 	LOG_IPX("ESR: >>>>>>>>>>>>>>>" );
1065 	while(ESRList!=NULL) {
1066 		// LOG_IPX("ECB: SN%7d notified.", ESRList->SerialNumber);
1067 		if(ESRList->databuffer) ESRList->writeData();
1068 		if(ESRList->getESRAddr()) {
1069 			// setup registers
1070 			SegSet16(es, RealSeg(ESRList->ECBAddr));
1071 			reg_si = RealOff(ESRList->ECBAddr);
1072 			reg_al = 0xff;
1073 			CALLBACK_RunRealFar(RealSeg(ESRList->getESRAddr()),
1074 								RealOff(ESRList->getESRAddr()));
1075 		}
1076 		delete ESRList;
1077 	}	// while
1078 
1079 	IO_WriteB(0xa0,0x63);	//EOI11
1080 	IO_WriteB(0x20,0x62);	//EOI2
1081 	LOG_IPX("ESR: <<<<<<<<<<<<<<<");
1082 	return CBRET_NONE;
1083 }
1084 
1085 void VFILE_Remove(const char *name);
1086 
1087 class IPX: public Module_base {
1088 private:
1089 	CALLBACK_HandlerObject callback_ipx;
1090 	CALLBACK_HandlerObject callback_esr;
1091 	CALLBACK_HandlerObject callback_ipxint;
1092 	RealPt old_73_vector;
1093 	static Bit16u dospage;
1094 public:
IPX(Section * configuration)1095 	IPX(Section* configuration):Module_base(configuration) {
1096 		Section_prop * section = static_cast<Section_prop *>(configuration);
1097 		if(!section->Get_bool("ipx")) return;
1098 		if(!SDLNetInited) {
1099 			if(SDLNet_Init() == -1){
1100 				LOG_MSG("SDLNet_Init failed: %s\n", SDLNet_GetError());
1101 				return;
1102 			}
1103 			SDLNetInited = true;
1104 		}
1105 
1106 		ECBList = NULL;
1107 		ESRList = NULL;
1108 		isIpxServer = false;
1109 		isIpxConnected = false;
1110 		IPX_NetworkInit();
1111 
1112 		DOS_AddMultiplexHandler(IPX_Multiplex);
1113 
1114 		callback_ipx.Install(&IPX_Handler,CB_RETF,"IPX Handler");
1115 		ipx_callback = callback_ipx.Get_RealPointer();
1116 
1117 		callback_ipxint.Install(&IPX_IntHandler,CB_IRET,"IPX (int 7a)");
1118 		callback_ipxint.Set_RealVec(0x7a);
1119 
1120 		callback_esr.Allocate(&IPX_ESRHandler,"IPX_ESR");
1121 		Bit16u call_ipxesr1 = callback_esr.Get_callback();
1122 
1123 		if(!dospage) dospage = DOS_GetMemory(2); // can not be freed yet
1124 
1125 		PhysPt phyDospage = PhysMake(dospage,0);
1126 
1127 		LOG_IPX("ESR callback address: %x, HandlerID %d", phyDospage,call_ipxesr1);
1128 
1129 		//save registers
1130 		phys_writeb(phyDospage+0,(Bit8u)0xFA);    // CLI
1131 		phys_writeb(phyDospage+1,(Bit8u)0x60);    // PUSHA
1132 		phys_writeb(phyDospage+2,(Bit8u)0x1E);    // PUSH DS
1133 		phys_writeb(phyDospage+3,(Bit8u)0x06);    // PUSH ES
1134 		phys_writew(phyDospage+4,(Bit16u)0xA00F); // PUSH FS
1135 		phys_writew(phyDospage+6,(Bit16u)0xA80F); // PUSH GS
1136 
1137 		// callback
1138 		phys_writeb(phyDospage+8,(Bit8u)0xFE);  // GRP 4
1139 		phys_writeb(phyDospage+9,(Bit8u)0x38);  // Extra Callback instruction
1140 		phys_writew(phyDospage+10,call_ipxesr1);        // Callback identifier
1141 
1142 		// register recreation
1143 		phys_writew(phyDospage+12,(Bit16u)0xA90F); // POP GS
1144 		phys_writew(phyDospage+14,(Bit16u)0xA10F); // POP FS
1145 		phys_writeb(phyDospage+16,(Bit8u)0x07);    // POP ES
1146 		phys_writeb(phyDospage+17,(Bit8u)0x1F);    // POP DS
1147 		phys_writeb(phyDospage+18,(Bit8u)0x61);    // POPA
1148 		phys_writeb(phyDospage+19,(Bit8u)0xCF);    // IRET: restores flags, CS, IP
1149 
1150 		// IPX version 2.12
1151 		//phys_writeb(phyDospage+27,(Bit8u)0x2);
1152 		//phys_writeb(phyDospage+28,(Bit8u)0x12);
1153 		//IPXVERpointer = RealMake(dospage,27);
1154 
1155 		RealPt ESRRoutineBase = RealMake(dospage, 0);
1156 
1157 		// Interrupt enabling
1158 		RealSetVec(0x73,ESRRoutineBase,old_73_vector);	// IRQ11
1159 		IO_WriteB(0xa1,IO_ReadB(0xa1)&(~8));			// enable IRQ11
1160 
1161 		PROGRAMS_MakeFile("IPXNET.COM",IPXNET_ProgramStart);
1162 	}
1163 
~IPX()1164 	~IPX() {
1165 		Section_prop * section = static_cast<Section_prop *>(m_configuration);
1166 		PIC_RemoveEvents(IPX_AES_EventHandler);
1167 		if(!section->Get_bool("ipx")) return;
1168 
1169 		if(isIpxServer) {
1170 			isIpxServer = false;
1171 			IPX_StopServer();
1172 		}
1173 		DisconnectFromServer(false);
1174 
1175 		DOS_DelMultiplexHandler(IPX_Multiplex);
1176 		RealSetVec(0x73,old_73_vector);
1177 		IO_WriteB(0xa1,IO_ReadB(0xa1)|8);	// disable IRQ11
1178 
1179 		PhysPt phyDospage = PhysMake(dospage,0);
1180 		for(Bitu i = 0;i < 32;i++)
1181 			phys_writeb(phyDospage+i,(Bit8u)0x00);
1182 
1183 		VFILE_Remove("IPXNET.COM");
1184 	}
1185 };
1186 
1187 static IPX* test;
1188 
IPX_ShutDown(Section * sec)1189 void IPX_ShutDown(Section* sec) {
1190 	delete test;
1191 }
1192 
IPX_Init(Section * sec)1193 void IPX_Init(Section* sec) {
1194 	test = new IPX(sec);
1195 	sec->AddDestroyFunction(&IPX_ShutDown,true);
1196 }
1197 
1198 //Initialize static members;
1199 Bit16u IPX::dospage = 0;
1200 
1201 #endif
1202