]> icculus.org git repositories - btb/d2x.git/blob - arch/dos/ipx.c
don't use hardcoded descriptions of joystick buttons/axes
[btb/d2x.git] / arch / dos / ipx.c
1 /* $Id: ipx.c,v 1.7 2004-08-28 23:17:45 schaffner Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Routines for IPX communications, copied from d1x
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #ifdef __GNUC__
26 #define _BORLAND_DOS_REGS 1
27 #define far
28 #endif
29
30 #include <i86.h>
31 #include <dos.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <malloc.h>
35 #include <stdlib.h>
36 #include <conio.h>
37 #include <assert.h>
38
39 #include "pstypes.h"
40 #include "timer.h"
41 #include "ipx.h"
42 #include "error.h"
43 #include "u_dpmi.h"
44 #include "key.h"
45 #include "ipx_drv.h"
46
47 typedef unsigned char BYTE;
48 typedef unsigned short WORD;
49 typedef unsigned long DWORD;
50
51 typedef struct local_address {
52         ubyte address[6];
53 } __pack__ local_address;
54
55 typedef struct net_address {
56         BYTE                            network_id[4];                  
57         local_address   node_id;
58         WORD                            socket_id;
59 } __pack__ net_address;
60
61 typedef struct ipx_header {
62         WORD                    checksum;
63         WORD                    length;
64         BYTE                    transport_control;
65         BYTE                    packet_type;
66         net_address     destination;
67         net_address     source;
68 } __pack__ ipx_header;
69
70 typedef struct ecb_header {
71         WORD                    link[2];
72         WORD                    esr_address[2];
73         BYTE                    in_use;
74         BYTE                    completion_code;
75         WORD                    socket_id;
76         BYTE                    ipx_reserved[14];        
77         WORD                    connection_id;
78         local_address immediate_address;
79         WORD                    fragment_count;
80         WORD                    fragment_pointer[2];
81         WORD                    fragment_size;
82 } __pack__ ecb_header;
83
84 typedef struct packet_data {
85         int                     packetnum;
86         byte                    data[IPX_MAX_DATA_SIZE];
87 } __pack__ packet_data;
88
89 typedef struct ipx_packet {
90         ecb_header      ecb;
91         ipx_header      ipx;
92         packet_data     pd;
93 } __pack__ ipx_packet;
94
95 static int ipx_packetnum = 0;
96
97 #define MAX_PACKETS 64
98
99 static packet_data packet_buffers[MAX_PACKETS];
100 static short packet_free_list[MAX_PACKETS];
101 static int num_packets = 0;
102 static int largest_packet_index = 0;
103 static short packet_size[MAX_PACKETS];
104
105 WORD ipx_socket=0;
106 static ubyte ipx_installed=0;
107 WORD ipx_vector_segment;
108 WORD ipx_vector_offset;
109 ubyte ipx_socket_life = 0;      // 0=closed at prog termination, 0xff=closed when requested.
110 //DWORD ipx_network = 0;
111 //local_address ipx_my_node;
112 #define ipx_my_node (ipx_MyAddress+4)
113 WORD ipx_num_packets=32;                // 32 Ipx packets
114 ipx_packet * packets;
115 int neterrors = 0;
116 ushort ipx_packets_selector;
117
118 ecb_header * last_ecb=NULL;
119 int lastlen=0;
120
121 static void got_new_packet( ecb_header * ecb );
122 static void ipx_listen_for_packet(ecb_header * ecb );
123
124 static void free_packet( int id )
125 {
126         packet_buffers[id].packetnum = -1;
127         packet_free_list[ --num_packets ] = id;
128         if (largest_packet_index==id)   
129                 while ((--largest_packet_index>0) && (packet_buffers[largest_packet_index].packetnum == -1 ));
130 }
131
132 static int ipx_dos_get_packet_data( ubyte * data )
133 {
134         int i, n, best, best_id, size;
135
136         for (i=1; i<ipx_num_packets; i++ )      {
137                 if ( !packets[i].ecb.in_use )   {
138                         got_new_packet( &packets[i].ecb );
139                         packets[i].ecb.in_use = 0;
140                         ipx_listen_for_packet(&packets[i].ecb);
141                 }                       
142         }
143
144         best = -1;
145         n = 0;
146         best_id = -1;
147
148         for (i=0; i<=largest_packet_index; i++ )        {
149                 if ( packet_buffers[i].packetnum > -1 ) {
150                         n++;
151                         if ( best == -1 || (packet_buffers[i].packetnum<best) ) {
152                                 best = packet_buffers[i].packetnum;
153                                 best_id = i;
154                         }
155                 }                       
156         }
157
158         //mprintf( (0, "Best id = %d, pn = %d, last_ecb = %x, len=%x, ne = %d\n", best_id, best, last_ecb, lastlen, neterrors ));
159         //mprintf( (1, "<%d> ", neterrors ));
160
161         if ( best_id < 0 ) return 0;
162
163         size = packet_size[best_id];
164         memcpy( data, packet_buffers[best_id].data, size );
165         free_packet(best_id);
166
167         return size;
168 }
169
170 #ifndef __GNUC__
171 unsigned int swap_short( unsigned int short )
172 #pragma aux swap_short parm [eax] = "xchg al,ah";
173 #else
174 static inline unsigned int swap_short( unsigned int sshort ) {
175         int __retval;
176         asm("xchg %%ah,%%al" : "=a" (__retval) : "a" (sshort));
177         return __retval;
178 }
179 #endif
180
181 static void got_new_packet( ecb_header * ecb )
182 {
183         ipx_packet * p;
184         int id;
185         unsigned short datasize;
186
187         datasize = 0;
188         last_ecb = ecb;
189         p = (ipx_packet *)ecb;
190
191         if ( p->ecb.in_use ) { neterrors++; return; }
192         if      ( p->ecb.completion_code )      { neterrors++; return; }
193
194         //      Error( "Recieve error %d for completion code", p->ecb.completion_code );
195         
196         if ( memcmp( &p->ipx.source.node_id, ipx_my_node, 6 ) ) {
197                 datasize=swap_short(p->ipx.length);
198                 lastlen=datasize;
199                 datasize -= sizeof(ipx_header);
200                 // Find slot to put packet in...
201                 if ( datasize > 0 && datasize <= sizeof(packet_data) )  {
202                         if ( num_packets >= MAX_PACKETS ) {
203                                 //printf( 1, "IPX: Packet buffer overrun!!!\n" );
204                                 neterrors++;
205                                 return;
206                         }               
207                         id = packet_free_list[ num_packets++ ];
208                         if (id > largest_packet_index ) largest_packet_index = id;
209                         packet_size[id] = datasize-sizeof(int);
210                         packet_buffers[id].packetnum =  p->pd.packetnum;
211                         if ( packet_buffers[id].packetnum < 0 ) { neterrors++; return; }
212                         memcpy( packet_buffers[id].data, p->pd.data, packet_size[id] );
213                 } else {
214                         neterrors++; return;
215                 }
216         } 
217         // Repost the ecb
218         p->ecb.in_use = 0;
219         //ipx_listen_for_packet(&p->ecb);
220 }
221
222 /*ubyte * ipx_get_my_local_address()
223 {
224         return ipx_my_node.address;
225 }
226
227 ubyte * ipx_get_my_server_address()
228 {
229         return (ubyte *)&ipx_network;
230 }*/
231
232 static void ipx_listen_for_packet(ecb_header * ecb )    
233 {
234         dpmi_real_regs rregs;
235         ecb->in_use = 0x1d;
236         memset(&rregs,0,sizeof(dpmi_real_regs));
237         rregs.ebx = 4;  // Listen For Packet function
238         rregs.esi = DPMI_real_offset(ecb);
239         rregs.es = DPMI_real_segment(ecb);
240         dpmi_real_int386x( 0x7A, &rregs );
241 }
242
243 /*static void ipx_cancel_listen_for_packet(ecb_header * ecb )   
244 {
245         dpmi_real_regs rregs;
246         memset(&rregs,0,sizeof(dpmi_real_regs));
247         rregs.ebx = 6;  // IPX Cancel event
248         rregs.esi = DPMI_real_offset(ecb);
249         rregs.es = DPMI_real_segment(ecb);
250         dpmi_real_int386x( 0x7A, &rregs );
251 }
252 */
253
254 static void ipx_send_packet(ecb_header * ecb )  
255 {
256         dpmi_real_regs rregs;
257         memset(&rregs,0,sizeof(dpmi_real_regs));
258         rregs.ebx = 3;  // Send Packet function
259         rregs.esi = DPMI_real_offset(ecb);
260         rregs.es = DPMI_real_segment(ecb);
261         dpmi_real_int386x( 0x7A, &rregs );
262 }
263
264 typedef struct {
265         ubyte   network[4];
266         ubyte           node[6];
267         ubyte           local_target[6];
268 } __pack__ net_xlat_info;
269
270 static void ipx_dos_get_local_target( ubyte * server, ubyte * node, ubyte * local_target )
271 {
272         net_xlat_info * info;
273         dpmi_real_regs rregs;
274                 
275         // Get dos memory for call...
276         info = (net_xlat_info *)dpmi_get_temp_low_buffer( sizeof(net_xlat_info) );      
277         assert( info != NULL );
278         memcpy( info->network, server, 4 );
279         memcpy( info->node, node, 6 );
280         
281         memset(&rregs,0,sizeof(dpmi_real_regs));
282
283         rregs.ebx = 2;          // Get Local Target     
284         rregs.es = DPMI_real_segment(info);
285         rregs.esi = DPMI_real_offset(info->network);
286         rregs.edi = DPMI_real_offset(info->local_target);
287
288         dpmi_real_int386x( 0x7A, &rregs );
289
290         // Save the local target...
291         memcpy( local_target, info->local_target, 6 );
292 }
293
294 static void ipx_close()
295 {
296         dpmi_real_regs rregs;
297         if ( ipx_installed )    {
298                 // When using VLM's instead of NETX, the sockets don't
299                 // seem to automatically get closed, so we must explicitly
300                 // close them at program termination.
301                 ipx_installed = 0;
302                 memset(&rregs,0,sizeof(dpmi_real_regs));
303                 rregs.edx = ipx_socket;
304                 rregs.ebx = 1;  // Close socket
305                 dpmi_real_int386x( 0x7A, &rregs );
306         }
307 }
308
309 static int ipx_init(int socket_number)
310 {
311         int show_address=0;
312         dpmi_real_regs rregs;
313         ubyte *ipx_real_buffer;
314         int i;
315
316 //      atexit(ipx_close);
317
318         ipx_packetnum = 0;
319
320         // init packet buffers.
321         for (i=0; i<MAX_PACKETS; i++ )  {
322                 packet_buffers[i].packetnum = -1;
323                 packet_free_list[i] = i;
324         }
325         num_packets = 0;
326         largest_packet_index = 0;
327
328         // Get the IPX vector
329         memset(&rregs,0,sizeof(dpmi_real_regs));
330         rregs.eax=0x00007a00;
331         dpmi_real_int386x( 0x2f, &rregs );
332
333         if ( (rregs.eax & 0xFF) != 0xFF )       {
334                 return IPX_NOT_INSTALLED;   
335         }
336         ipx_vector_offset = rregs.edi & 0xFFFF;
337         ipx_vector_segment = rregs.es;
338         //printf( "IPX entry point at %.4x:%.4x\n", ipx_vector_segment, ipx_vector_offset );
339
340         // Open a socket for IPX
341
342         memset(&rregs,0,sizeof(dpmi_real_regs));
343         swab( (char *)&socket_number,(char *)&ipx_socket, 2 );
344         rregs.edx = ipx_socket;
345         rregs.eax = ipx_socket_life;
346         rregs.ebx = 0;  // Open socket
347         dpmi_real_int386x( 0x7A, &rregs );
348         
349         ipx_socket = rregs.edx & 0xFFFF;
350         
351         if ( rregs.eax & 0xFF ) {
352                 //mprintf( (1, "IPX error opening channel %d\n", socket_number-IPX_DEFAULT_SOCKET ));
353                 return IPX_SOCKET_TABLE_FULL;
354         }
355         
356         ipx_installed = 1;
357
358         // Find our internetwork address
359         ipx_real_buffer = dpmi_get_temp_low_buffer( 1024 );     // 1k block
360         if ( ipx_real_buffer == NULL )  {
361                 //printf( "Error allocation realmode memory\n" );
362                 return IPX_NO_LOW_DOS_MEM;
363         }
364
365         memset(&rregs,0,sizeof(dpmi_real_regs));
366         rregs.ebx = 9;          // Get internetwork address
367         rregs.esi = DPMI_real_offset(ipx_real_buffer);
368         rregs.es = DPMI_real_segment(ipx_real_buffer);
369         dpmi_real_int386x( 0x7A, &rregs );
370
371         if ( rregs.eax & 0xFF ) {
372                 //printf( "Error getting internetwork address!\n" );
373                 return IPX_SOCKET_TABLE_FULL;
374         }
375
376 /*      memcpy( &ipx_network, ipx_real_buffer, 4 );
377         memcpy( ipx_my_node, &ipx_real_buffer[4], 6 );*/
378         memcpy(ipx_MyAddress,ipx_real_buffer,10);
379
380         if ( show_address )     {
381                 printf( "My IPX addresss is " );
382                 printf( "%02X%02X%02X%02X/", ipx_real_buffer[0],ipx_real_buffer[1],ipx_real_buffer[2],ipx_real_buffer[3] );
383                 printf( "%02X%02X%02X%02X%02X%02X\n", ipx_real_buffer[4],ipx_real_buffer[5],ipx_real_buffer[6],ipx_real_buffer[7],ipx_real_buffer[8],ipx_real_buffer[9] );
384                 printf( "\n" );
385         }
386
387         packets = dpmi_real_malloc( sizeof(ipx_packet)*ipx_num_packets, &ipx_packets_selector );
388         if ( packets == NULL )  {
389                 //printf( "Couldn't allocate real memory for %d packets\n", ipx_num_packets );
390                 return IPX_NO_LOW_DOS_MEM;
391         }
392 #if 0 /* adb: not needed, fails with cwsdpmi */
393         if (!dpmi_lock_region( packets, sizeof(ipx_packet)*ipx_num_packets ))   {
394                 //printf( "Couldn't lock real memory for %d packets\n", ipx_num_packets );
395                 return IPX_NO_LOW_DOS_MEM;
396         }
397 #endif
398         memset( packets, 0, sizeof(ipx_packet)*ipx_num_packets );
399
400         for (i=1; i<ipx_num_packets; i++ )      {
401                 packets[i].ecb.in_use = 0x1d;
402                 //packets[i].ecb.in_use = 0;
403                 packets[i].ecb.socket_id = ipx_socket;
404                 packets[i].ecb.fragment_count = 1;
405                 packets[i].ecb.fragment_pointer[0] = DPMI_real_offset(&packets[i].ipx);
406                 packets[i].ecb.fragment_pointer[1] = DPMI_real_segment(&packets[i].ipx);
407                 packets[i].ecb.fragment_size = sizeof(ipx_packet)-sizeof(ecb_header);                   //-sizeof(ecb_header);
408
409                 ipx_listen_for_packet(&packets[i].ecb);
410         }
411
412         packets[0].ecb.socket_id = ipx_socket;
413         packets[0].ecb.fragment_count = 1;
414         packets[0].ecb.fragment_pointer[0] = DPMI_real_offset(&packets[0].ipx);
415         packets[0].ecb.fragment_pointer[1] = DPMI_real_segment(&packets[0].ipx);
416         packets[0].ipx.packet_type = 4;         // IPX packet
417         packets[0].ipx.destination.socket_id = ipx_socket;
418 //      memcpy( packets[0].ipx.destination.network_id, &ipx_network, 4 );
419         memset( packets[0].ipx.destination.network_id, 0, 4 );
420
421         return IPX_INIT_OK;
422 }
423
424 static void ipx_dos_send_packet_data( ubyte * data, int datasize, ubyte *network, ubyte *address, ubyte *immediate_address )
425 {
426         assert(ipx_installed);
427
428         if ( datasize >= IPX_MAX_DATA_SIZE )    {
429                 printf( "Data too big\n" );
430 //added/replaced on 11/8/98 by Victor Rachels to stop crappage
431                 return;
432 //                exit(1);
433 //end this section replacement - VR
434         }
435
436         // Make sure no one is already sending something
437         while( packets[0].ecb.in_use )
438         {
439         }
440         
441         if (packets[0].ecb.completion_code)     {
442 //                printf( "Send error %d for completion code\n", packets[0].ecb.completion_code );
443 //added/replaced on 11/8/98 by Victor Rachels to stop crappage
444                 return;
445         //        exit(1);
446 //end this section replacement - VR
447
448         }
449
450         // Fill in destination address
451         if ( memcmp( network, &ipx_network, 4 ) )
452                 memcpy( packets[0].ipx.destination.network_id, network, 4 );
453         else
454                 memset( packets[0].ipx.destination.network_id, 0, 4 );
455         memcpy( packets[0].ipx.destination.node_id.address, address, 6 );
456         memcpy( packets[0].ecb.immediate_address.address, immediate_address, 6 );
457         packets[0].pd.packetnum = ipx_packetnum++;
458
459         // Fill in data to send
460         packets[0].ecb.fragment_size = sizeof(ipx_header) + sizeof(int) + datasize;
461
462         assert( datasize > 1 );
463         assert( packets[0].ecb.fragment_size <= 576 );
464
465         memcpy( packets[0].pd.data, data, datasize );
466
467         // Send it
468         ipx_send_packet( &packets[0].ecb );
469
470 }
471
472 /*static int ipx_change_default_socket( ushort socket_number )
473 {
474         int i;
475         WORD new_ipx_socket;
476         dpmi_real_regs rregs;
477
478         if ( !ipx_installed ) return -3;
479
480         // Open a new socket    
481         memset(&rregs,0,sizeof(dpmi_real_regs));
482         swab( (char *)&socket_number,(char *)&new_ipx_socket, 2 );
483         rregs.edx = new_ipx_socket;
484         rregs.eax = ipx_socket_life;
485         rregs.ebx = 0;  // Open socket
486         dpmi_real_int386x( 0x7A, &rregs );
487         
488         new_ipx_socket = rregs.edx & 0xFFFF;
489         
490         if ( rregs.eax & 0xFF ) {
491                 //printf( (1, "IPX error opening channel %d\n", socket_number-IPX_DEFAULT_SOCKET ));
492                 return -2;
493         }
494
495         for (i=1; i<ipx_num_packets; i++ )      {
496                 ipx_cancel_listen_for_packet(&packets[i].ecb);
497         }
498
499         // Close existing socket...
500         memset(&rregs,0,sizeof(dpmi_real_regs));
501         rregs.edx = ipx_socket;
502         rregs.ebx = 1;  // Close socket
503         dpmi_real_int386x( 0x7A, &rregs );
504
505         ipx_socket = new_ipx_socket;
506
507         // Repost all listen requests on the new socket...      
508         for (i=1; i<ipx_num_packets; i++ )      {
509                 packets[i].ecb.in_use = 0;
510                 packets[i].ecb.socket_id = ipx_socket;
511                 ipx_listen_for_packet(&packets[i].ecb);
512         }
513
514         packets[0].ecb.socket_id = ipx_socket;
515         packets[0].ipx.destination.socket_id = ipx_socket;
516
517         ipx_packetnum = 0;
518         // init packet buffers.
519         for (i=0; i<MAX_PACKETS; i++ )  {
520                 packet_buffers[i].packetnum = -1;
521                 packet_free_list[i] = i;
522         }
523         num_packets = 0;
524         largest_packet_index = 0;
525
526         return 0;
527 }
528 */
529
530 struct ipx_driver ipx_dos = {
531 //      NULL,
532         ipx_init,
533         ipx_close,
534         NULL,
535         NULL,
536         NULL,
537         NULL,
538         1,
539         ipx_dos_get_local_target,
540         ipx_dos_get_packet_data,
541         ipx_dos_send_packet_data
542 };
543
544 void arch_ipx_set_driver(int ipx_driver)
545 {
546         driver = &ipx_dos;
547         if (ipx_driver != IPX_DRIVER_IPX)
548                 Warning("Unknown network driver! Defaulting to real IPX");
549 }
550
551
552 //---typedef struct rip_entry {
553 //---   uint            network;
554 //---   ushort  nhops;
555 //---   ushort  nticks;
556 //---} rip_entry;
557 //---
558 //---typedef struct rip_packet {
559 //---   ushort          operation;              //1=request, 2=response
560 //---   rip_entry       rip[50];
561 //---} rip_packet;
562 //---
563 //---
564 //---void  ipx_find_all_servers()
565 //---{
566 //---   int i;
567 //---   rip_packet * rp;
568 //---   assert(ipx_installed);
569 //---
570 //---   ipx_change_default_socket( 0x0453 );
571 //---   //      ipx_change_default_socket( 0x5304 );
572 //---
573 //---   // Make sure no one is already sending something
574 //---   while( packets[0].ecb.in_use )
575 //---   {
576 //---   }
577 //---   
578 //---   if (packets[0].ecb.completion_code)     {
579 //---           printf( "AAAA:Send error %d for completion code\n", packets[0].ecb.completion_code );
580 //---           //exit(1);
581 //---   }
582 //---
583 //---   rp = (rip_packet *)&packets[0].pd;
584 //---
585 //---   // Fill in destination address
586 //---   {
587 //---           char mzero1[] = {0,0,0,1};
588 //---           char mzero[] = {0,0,0,0,0,1};
589 //---           char immediate[6];
590 //---           //memcpy( packets[0].ipx.destination.network_id, &ipx_network, 4 );
591 //---           //memcpy( packets[0].ipx.destination.node_id.address, ipx_my_node.address, 6 );
592 //---
593 //---           memcpy( packets[0].ipx.destination.network_id, mzero1, 4 );
594 //---           memcpy( packets[0].ipx.destination.node_id.address, mzero, 6 );
595 //---
596 //---           memcpy( packets[0].ipx.destination.socket_id, &ipx_socket, 2 );
597 //---           memcpy( packets[0].ipx.source.network_id, &ipx_network, 4 );
598 //---           memcpy( packets[0].ipx.source.node_id.address, ipx_my_node.address, 6 );
599 //---           memcpy( packets[0].ipx.source.socket_id, &ipx_socket, 2 );
600 //---           //memcpy( packets[0].ecb.immediate_address.address, ipx_my_node.address, 6 );
601 //---           //mzero1[3] = 1;
602 //---           //memcpy( packets[0].ipx.destination.network_id, mzero1, 4 );
603 //---           //mzero[5] = 1;
604 //---           //memcpy( packets[0].ipx.destination.node_id.address, mzero, 6 );
605 //---           //ipx_get_local_target( mzero1, mzero, immediate );
606 //---           //memcpy( packets[0].ecb.immediate_address.address, mzero, 6 );
607 //---           //memcpy( packets[0].ecb.immediate_address.address, immediate, 6 );
608 //---           //mzero[5] = 0;
609 //---   }
610 //---
611 //---   packets[0].ipx.packet_type = 1;         // RIP packet
612 //---
613 //---   // Fill in data to send
614 //---   packets[0].ecb.fragment_size = sizeof(ipx_header) + sizeof(rip_packet);
615 //---   assert( packets[0].ecb.fragment_size <= 576 );
616 //---
617 //---   rp->operation = 0;              // Request
618 //---   for (i=0;i<50; i++)     {
619 //---           rp->rip[i].network = 0xFFFFFFFF;
620 //---           rp->rip[i].nhops = 0;
621 //---           rp->rip[i].nticks = 0;
622 //---   }
623 //---
624 //---   // Send it
625 //---   ipx_send_packet( &packets[0].ecb );
626 //---
627 //---   for (i=0;i<50; i++)     {
628 //---           if ( rp->rip[i].network != 0xFFFFFFFF )
629 //---                   printf( "Network = %8x, Hops=%d, Ticks=%d\n", rp->rip[i].network, rp->rip[i].nhops, rp->rip[i].nticks );
630 //---   }
631 //---}
632 //---
633 //---