dosxtrn.c 16.5 KB
Newer Older
1 2 3 4 5 6
/* Synchronet External DOS Program Launcher (16-bit MSVC 1.52c project) */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <dos.h>			/* _dos_set/getvect() */
#include <windows.h>		/* BOOL, etc. */
#include "vdd_func.h"
#include "isvbop.h"			/* ddk\inc */
42
#include "fossdefs.h"
43 44
#include "../git_branch.h"
#include "../git_hash.h"
45

46
#define DOSXTRN_REVISION	26
rswindell's avatar
rswindell committed
47 48
#define VDD_FILENAME	"sbbsexec.dll"

49 50 51 52 53 54
#if 0
#define DEBUG_INT_CALLS
#define DEBUG_DOS_CALLS
#define DEBUG_FOSSIL_CALLS
#endif

55 56 57 58 59 60 61
/****************************************************************************/
/* Truncates white-space chars off end of 'str' and terminates at first tab */
/****************************************************************************/
static void truncsp(char *str)
{
	size_t c;

62 63 64 65
	str[strcspn(str,"\t")]=0;
	c=strlen(str);
	while(c && (unsigned char)str[c-1]<=' ') c--;
	str[c]=0;
66 67 68
}
short	vdd=0;
BYTE	node_num=0;
69
char	id_string[128];
70 71 72 73 74 75
#ifdef DEBUG_INT_CALLS
ulong	int14calls=0;
ulong	int16calls=0;
ulong	int21calls=0;
ulong	int29calls=0;
#endif
76 77 78

void (interrupt *oldint14)();
void (interrupt *oldint16)();
79
void (interrupt *oldint21)();
80 81
void (interrupt *oldint29)();

82

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
static int vdd_buf(BYTE op, int count, WORD buf_seg, WORD buf_off)
{
	int retval;

	_asm {
		push	bx
		push	cx
		push	es
		push	di
		mov		ax,	vdd
		mov		bh,	node_num
		mov		bl,	op
		mov		cx,	count
		mov		es, buf_seg
		mov		di, buf_off
	}
	DispatchCall();
	_asm {
		mov		retval, ax
		pop		di
		pop		es
		pop		cx
		pop		bx
	}
	return(retval);
}

110 111 112 113 114 115 116 117
static int vdd_str(BYTE op, char* str)
{
	WORD			buf_seg;

	_asm mov buf_seg, ss;
	return vdd_buf(op, strlen(str), buf_seg, (WORD)str);
}

118
static int vdd_op(BYTE op)
119 120 121
{
	int retval;

122 123 124 125
#if FALSE	/* disable yield? */
	if(op==VDD_YIELD)
		return(0);
#endif
126 127 128 129 130 131 132 133 134 135 136 137 138 139
	_asm {
		push	bx
		mov		ax,	vdd
		mov		bh,	node_num
		mov		bl,	op
	}
	DispatchCall();
	_asm {
		mov		retval, ax
		pop		bx
	}
	return(retval);
}

140

141 142 143 144 145 146 147 148 149 150 151
void vdd_getstatus(vdd_status_t* status)
{
	WORD			buf_seg;

	_asm mov buf_seg, ss;
	if(vdd_buf(VDD_STATUS, sizeof(vdd_status_t), buf_seg, (WORD)status)!=0)
		memset(status,0,sizeof(vdd_status_t));
}

WORD PortStatus()
{
Rob Swindell's avatar
Rob Swindell committed
152
	WORD			status=FOSSIL_MDM_STATUS_DCD_CHNG; /* AL bit 3 (change in DCD) always set */
153 154 155 156
	vdd_status_t	vdd_status;

	vdd_getstatus(&vdd_status);

157
	if(vdd_status.online)			/* carrier detect */
Rob Swindell's avatar
Rob Swindell committed
158
		status|=FOSSIL_MDM_STATUS_DCD;
159

160
	if(vdd_status.inbuf_full)		/* receive data ready  */
Rob Swindell's avatar
Rob Swindell committed
161
		status|=FOSSIL_LINE_STATUS_RDA;
162

163 164
/*	if(vm->overrun)					/* overrun error detected */
/*		status|=0x0200;				/* OVRN */
165 166

	if(vdd_status.outbuf_full
167
		<vdd_status.outbuf_size/2)	/* room available in output buffer */
Rob Swindell's avatar
Rob Swindell committed
168
		status|=FOSSIL_LINE_STATUS_THRE;
169

170
	if(!vdd_status.outbuf_full)		/* output buffer is empty */
Rob Swindell's avatar
Rob Swindell committed
171
		status|=FOSSIL_LINE_STATUS_TSRE;
172 173 174 175

	return(status);
}

176 177
#ifdef DEBUG_FOSSIL_CALLS
	DWORD fossil_calls[0x100];
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

const char* fossil_func(int func)
{
	switch(func) {
		case FOSSIL_FUNC_SET_RATE:		return "set rate";
		case FOSSIL_FUNC_PUT_CHAR:		return "put char";
		case FOSSIL_FUNC_GET_CHAR:		return "get char";
		case FOSSIL_FUNC_GET_STATUS:	return "get status";
		case FOSSIL_FUNC_INIT:			return "init";
		case FOSSIL_FUNC_UNINIT:		return "uninit";
		case FOSSIL_FUNC_DTR:			return "dtr";
		case FOSSIL_FUNC_GET_TIMER:		return "get timer";
		case FOSSIL_FUNC_FLUSH_OUT:		return "flush out";
		case FOSSIL_FUNC_PURGE_OUT:		return "purge out";
		case FOSSIL_FUNC_PURGE_IN:		return "purge in";
		case FOSSIL_FUNC_WRITE_CHAR:	return "write char";
		case FOSSIL_FUNC_PEEK:			return "peek";
		case FOSSIL_FUNC_GET_KB:		return "get kb";
		case FOSSIL_FUNC_GET_KB_WAIT:	return "get kb wait";
		case FOSSIL_FUNC_FLOW_CTRL:		return "flow_ctrl";
		case FOSSIL_FUNC_CTRL_C:		return "ctrl_c";
		case FOSSIL_FUNC_BREAK:			return "break";
		case FOSSIL_FUNC_GET_INFO:		return "get info";
		default: return "unknown";
	}
}
204 205
#endif

206 207 208 209 210 211 212 213 214 215 216
void interrupt winNTint14(
	unsigned _es, unsigned _ds,
	unsigned _di, unsigned _si,
	unsigned _bp, unsigned _sp,
	unsigned _bx, unsigned _dx,
	unsigned _cx, unsigned _ax,
	)
{
	BYTE			ch;
	BYTE far*		p;
	WORD			buf_seg;
Rob Swindell's avatar
Rob Swindell committed
217
	int				rd;
218 219
	int				wr;
	vdd_status_t	vdd_status;
220 221 222
    fossil_info_t info = { 
		 sizeof(info)
		,FOSSIL_REVISION
223
		,DOSXTRN_REVISION	/* driver revision */
224 225 226
		,0			/* ID string pointer */	
		,0,0		/* receive buffer size/free (overwritten later) */
		,0,0		/* transmit buffer size/free (overwritten later) */
227
        ,80,25		/* screen dimensions (cols, rows) */
228
					/* port settings (i.e. 38400 N-8-1): */
229 230 231 232
        ,FOSSIL_BAUD_RATE_38400
		|FOSSIL_PARITY_NONE
		|FOSSIL_DATA_BITS_8
		|FOSSIL_STOP_BITS_1
233
	};
234

235 236 237 238
#ifdef DEBUG_INT_CALLS
	int14calls++;
#endif

239 240 241 242
#ifdef DEBUG_FOSSIL_CALLS
	fossil_calls[_ax>>8]++;
#endif

243
	switch(_ax>>8) {
244
		case FOSSIL_FUNC_SET_RATE:	/* Initialize/Set baud rate */
245 246
			_ax = PortStatus();
			break;
247
		case FOSSIL_FUNC_PUT_CHAR: /* write char to com port, with wait */
248 249
			ch=_ax&0xff;
			_asm mov buf_seg, ss;
Rob Swindell's avatar
Rob Swindell committed
250
			wr = vdd_buf(VDD_WRITE, 1, buf_seg, (WORD)&ch);
251
			_ax = PortStatus();
Rob Swindell's avatar
Rob Swindell committed
252 253
			if(wr != 1)
				_ax |= FOSSIL_LINE_STATUS_TIMEOUT;
254
			break;
255
		case FOSSIL_FUNC_GET_CHAR: /* read char from com port, with wait */
256
			_asm mov buf_seg, ss;
Rob Swindell's avatar
Rob Swindell committed
257 258 259
			rd = vdd_buf(VDD_READ, 1, buf_seg, (WORD)&ch);
			_ax = ch;
			if(rd != 1) {
260
				vdd_op(VDD_YIELD);
Rob Swindell's avatar
Rob Swindell committed
261
				_ax = FOSSIL_LINE_STATUS_TIMEOUT;
262
			}
263
			break;
264
		case FOSSIL_FUNC_GET_STATUS:	/* request status */
265
			_ax=PortStatus();
Rob Swindell's avatar
Rob Swindell committed
266
			if(_ax == FOSSIL_MDM_STATUS_DCD_CHNG | FOSSIL_MDM_STATUS_DCD | FOSSIL_LINE_STATUS_THRE | FOSSIL_LINE_STATUS_TSRE)
267
				vdd_op(VDD_MAYBE_YIELD);
268
			break;
269
		case FOSSIL_FUNC_INIT:	/* initialize */
270 271
			_ax=FOSSIL_SIGNATURE;	/* magic number = success */
			_bx=FOSSIL_REVISION<<8 | FOSSIL_FUNC_HIGHEST;	/* FOSSIL rev/maximum FOSSIL func supported */
272
			break;
273 274 275
		case FOSSIL_FUNC_DTR:
			if((_ax&0xff)==0)	/* Lower DTR */
				vdd_op(VDD_HANGUP);
276
			break;
277 278 279
        case FOSSIL_FUNC_FLUSH_OUT:	/* flush output buffer	*/
			break;
        case FOSSIL_FUNC_PURGE_OUT:	/* purge output buffer	*/
280
			vdd_op(VDD_OUTBUF_PURGE);
281
			break;
282
        case FOSSIL_FUNC_PURGE_IN:	/* purge input buffer	*/
283
			vdd_op(VDD_INBUF_PURGE);
284
			break;
285
		case FOSSIL_FUNC_WRITE_CHAR:	/* write char to com port, no wait */
286 287 288
			ch=_ax&0xff;
			_asm mov buf_seg, ss;
			_ax = vdd_buf(VDD_WRITE, 1, buf_seg, (WORD)&ch);
Rob Swindell's avatar
Rob Swindell committed
289 290
			if(_ax != 1)
				vdd_op(VDD_YIELD);
291
			break;
292
        case FOSSIL_FUNC_PEEK:	/* non-destructive read-ahead */
293
			_asm mov buf_seg, ss;
Rob Swindell's avatar
Rob Swindell committed
294 295 296
			rd = vdd_buf(VDD_PEEK, 1, buf_seg, (WORD)&ch);
			_ax = ch;
			if(rd == 0) {
297
				vdd_op(VDD_YIELD);
Rob Swindell's avatar
Rob Swindell committed
298 299
				_ax = FOSSIL_CHAR_NOT_AVAILABLE;
			}
300
			break;
301
        case FOSSIL_FUNC_READ_BLOCK:	/* read block, no wait */
Rob Swindell's avatar
Rob Swindell committed
302
			_ax = vdd_buf(VDD_READ, _cx, _es, _di);
303
			if(_ax == 0)
304
				vdd_op(VDD_YIELD);
305
			break;
306
        case FOSSIL_FUNC_WRITE_BLOCK:	/* write block, no wait */
307 308
			_ax = vdd_buf(VDD_WRITE, _cx, _es, _di);
			break;
309
        case FOSSIL_FUNC_GET_INFO:	/* driver info */
310 311 312 313 314
			vdd_getstatus(&vdd_status);
			info.inbuf_size=vdd_status.inbuf_size;
			info.inbuf_free=info.inbuf_size-vdd_status.inbuf_full;
			info.outbuf_size=vdd_status.outbuf_size;
			info.outbuf_free=info.outbuf_size-vdd_status.outbuf_full;
315
			info.id_string = id_string;
316

317 318
			if(vdd_status.inbuf_full==vdd_status.outbuf_full==0)
				vdd_op(VDD_MAYBE_YIELD);
319

320 321 322 323 324 325 326
			p = _MK_FP(_es,_di);
            wr=sizeof(info);
            if(wr>_cx)
            	wr=_cx;
            _fmemcpy(p, &info, wr);
        	_ax=wr;
            break;
327
		case FOSSIL_FUNC_GET_KB:
Rob Swindell's avatar
Rob Swindell committed
328
			_ax = FOSSIL_CHAR_NOT_AVAILABLE;
329
			break;
330 331 332
	}
}

333 334 335 336 337
void int14stub(void)
{
	/* This function will be overwritten later (during runtime) with FOSSIL signature */
}

338 339 340 341 342 343 344 345 346 347 348 349 350
void interrupt winNTint16(
	unsigned _es, unsigned _ds,
	unsigned _di, unsigned _si,
	unsigned _bp, unsigned _sp,
	unsigned _bx, unsigned _dx,
	unsigned _cx, unsigned _ax,
	unsigned _ip, unsigned _cs,
    unsigned flags )
{
	BYTE			ch;
	WORD			buf_seg;
	vdd_status_t	status;

351 352 353 354
#ifdef DEBUG_INT_CALLS
	int16calls++;
#endif

355 356
	vdd_getstatus(&status);
 	switch(_ax>>8) {
357 358
    	case 0x00:	/* Read char from keyboard */
        case 0x10:	/* Read char from enhanced keyboard */
359 360 361 362 363
			if(status.inbuf_full) {
				_asm mov buf_seg, ss;
				vdd_buf(VDD_READ, 1, buf_seg, (WORD)&ch);
				_ax=ch;
				return;
364
			} 
365
			vdd_op(VDD_MAYBE_YIELD);
366
			break;
367 368
    	case 0x01:	/* Get keyboard status */
        case 0x11:	/* Get enhanced keyboard status */
369 370 371
			if(status.inbuf_full) {
				_asm mov buf_seg, ss;
				vdd_buf(VDD_PEEK, 1, buf_seg, (WORD)&ch);
372
                flags&=~(1<<6);	/* clear zero flag */
373 374
                _ax=ch;
				return;
375
			}
376
			vdd_op(VDD_MAYBE_YIELD);
377 378 379 380 381 382
	        break;
	}

	_chain_intr(oldint16);		
}

383 384
#ifdef DEBUG_DOS_CALLS
	DWORD dos_calls[0x100];
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
#endif

void interrupt winNTint21(
	unsigned _es, unsigned _ds,
	unsigned _di, unsigned _si,
	unsigned _bp, unsigned _sp,
	unsigned _bx, unsigned _dx,
	unsigned _cx, unsigned _ax,
	)
{
#ifdef DEBUG_INT_CALLS
	int21calls++;
#endif
	if(_ax>>8 == 0x2c)	/* GET_SYSTEM_TIME */
		vdd_op(VDD_MAYBE_YIELD);
400 401
#ifdef DEBUG_DOS_CALLS
	dos_calls[_ax>>8]++;
402 403 404 405
#endif
	_chain_intr(oldint21);
}

406 407 408 409 410 411 412 413 414 415
void interrupt winNTint29(
	unsigned _es, unsigned _ds,
	unsigned _di, unsigned _si,
	unsigned _bp, unsigned _sp,
	unsigned _bx, unsigned _dx,
	unsigned _cx, unsigned _ax,
	)
{
	char	ch;
	WORD	buf_seg;
416 417 418
#ifdef DEBUG_INT_CALLS
	int29calls++;
#endif
419 420 421 422 423 424 425 426

	ch=_ax&0xff;
	_asm mov buf_seg, ss
	vdd_buf(VDD_WRITE, 1, buf_seg, (WORD)&ch);

	_chain_intr(oldint29);
}

427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
/****************************************************************************/
/* Return the filename portion of a full pathname							*/
/****************************************************************************/
char* getfname(const char* path)
{
	const char* fname;
	const char* bslash;

	fname=strrchr(path,'/');
	bslash=strrchr(path,'\\');
	if(bslash>fname)
		fname=bslash;
	if(fname!=NULL) 
		fname++;
	else 
		fname=(char*)path;
	return((char*)fname);
}

rswindell's avatar
rswindell committed
446
char *	DllName		=VDD_FILENAME;
447 448
char *	InitFunc	="VDDInitialize";
char *	DispFunc	="VDDDispatch";
449 450
#define MAX_ENVVARS 32
#define MAX_ARGS	32
451 452 453 454 455

int main(int argc, char **argv)
{
	char	str[128];
	char	cmdline[128],*p;
456 457
	char	dll[256];
	char	exec_dir[128];
458 459
	char*	envvar[MAX_ENVVARS];
	char*	arg[MAX_ARGS];
460
	int		i,c,d,envnum=0;
461
	int		mode = SBBSEXEC_MODE_UNSPECIFIED;
462
	FILE*	fp;
Rob Swindell's avatar
Rob Swindell committed
463
	BOOL	x64=FALSE;
464
	BOOL	success=FALSE;
465
	WORD	buf_seg;
466
	WORD	w;
467

468
	sprintf(id_string,"Synchronet FOSSIL Driver (DOSXTRN) revision %u %s/%s", DOSXTRN_REVISION, GIT_BRANCH, GIT_HASH);
469 470
	if(argc<2) {
		fprintf(stderr
471 472
			,"%s - Copyright %s Rob Swindell\n"
			,id_string, __DATE__+7);
473
		fprintf(stderr
474
			,"usage: dosxtrn <path/dosxtrn.env> [NT|x64] [node_num] [mode]\n");
475 476
		return(1);
	}
477

478 479 480
	sprintf(exec_dir,"%.*s",sizeof(exec_dir)-1,argv[0]);
	p=getfname(exec_dir);
	*p=0;
rswindell's avatar
rswindell committed
481
	sprintf(dll,"%s%s",exec_dir,VDD_FILENAME);
482 483
	DllName=dll;

Rob Swindell's avatar
Rob Swindell committed
484
	if(argc>2) {
485 486
		if(strcmp(argv[2],"x64") == 0)
			x64=TRUE;
Rob Swindell's avatar
Rob Swindell committed
487
	}
488 489 490 491
	if(argc>3)
		node_num=atoi(argv[3]);
	if(argc>4)
		mode=atoi(argv[4]);
492

493 494 495
	if(mode == SBBSEXEC_MODE_UNSPECIFIED)
		mode = SBBSEXEC_MODE_DEFAULT;

496 497 498 499 500 501 502 503 504
	if((fp=fopen(argv[1],"r"))==NULL) {
		fprintf(stderr,"!Error opening %s\n",argv[1]);
		return(2);
	}

	fgets(cmdline, sizeof(cmdline), fp);
	truncsp(cmdline);

	arg[0]=cmdline;	/* point to the beginning of the string */
505
	for(c=0,d=1;cmdline[c] && d < (MAX_ARGS - 1);c++)	/* Break up command line */
506 507
		if(cmdline[c]==' ') {
			cmdline[c]=0;			/* insert nulls */
508 509
			arg[d++]=cmdline+c+1;	/* point to the beginning of the next arg */
		}
510 511
	arg[d]=0;

512
	while(!feof(fp) && envnum < MAX_ENVVARS) {
513 514 515
		if(!fgets(str, sizeof(str), fp))
			break;
		truncsp(str);
516
		if((envvar[envnum]=strdup(str))==NULL) {
517 518 519 520 521 522 523 524 525 526
			fprintf(stderr,"!MALLOC ERROR\n");
			return(4);
		}
		_putenv(envvar[envnum++]);
	}
	fclose(fp);

	/* Save int14 handler */
	oldint14=_dos_getvect(0x14);

527 528 529 530 531
	/* Overwrite stub function */
	((BYTE*)int14stub)[0] = 0xe9;	/* jump (relative) */
	((BYTE*)int14stub)[3] = 0x90;	/* NOP */
	((BYTE*)int14stub)[4] = 0x90;	/* NOP */
	((BYTE*)int14stub)[5] = 0x90;	/* NOP */
532 533 534
	((BYTE*)int14stub)[6] = FOSSIL_SIGNATURE&0xff;	/* FOSSIL sig (LSB) */
	((BYTE*)int14stub)[7] = FOSSIL_SIGNATURE>>8;	/* FOSSIL sig (MSB) */
	((BYTE*)int14stub)[8] = FOSSIL_FUNC_HIGHEST;	/* FOSSIL highest func supported */
535

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
	for(i=0;i<2;i++) {

		/* Register VDD */
       	_asm {
			push	es
			push	ds
			pop		es
			mov     si, DllName		; ds:si = dll name
			mov     di, InitFunc    ; es:di = init routine
			mov     bx, DispFunc    ; ds:bx = dispatch routine
		};
		if(!x64) {	// NTVDMx64 (based on an older Windows NTVDM) requires an init routine
			_asm {	/* Vista work-around, apparently doesn't support an InitFunc (RegisterModule fails with AX=1) */
				xor		di,di
				mov		es,di
rswindell's avatar
rswindell committed
551
			};
552
		}
553 554 555 556 557 558 559
		RegisterModule();
		_asm {
			mov		vdd, ax
			jc		err
			mov		success, TRUE
			err:
			pop		es
560
		}
561 562 563 564 565 566 567 568
		if(success)
			break;
		DllName=VDD_FILENAME;	/* try again with no path (for Windows Vista) */
	}
	if(!success) {
		fprintf(stderr,"Error %d loading %s\n",vdd,DllName);
		return(-1);
	}
569

570
#if 0
571 572
	fprintf(stderr,"vdd handle=%d\n",vdd);
	fprintf(stderr,"mode=%d\n",mode);
573
#endif
574
	vdd_str(VDD_LOAD_INI_FILE, exec_dir);
575

576
	vdd_str(VDD_LOAD_INI_SECTION, getfname(arg[0]));
577

578 579
	sprintf(str,"%s, rev %u %s/%s, %s %s mode=%u", __FILE__, DOSXTRN_REVISION, GIT_BRANCH, GIT_HASH, __DATE__, __TIME__, mode);
	vdd_str(VDD_DEBUG_OUTPUT, str);
580

581 582 583 584 585 586 587
	if(mode & SBBSEXEC_MODE_UART)
		vdd_op(VDD_VIRTUALIZE_UART);
	i=vdd_op(VDD_OPEN);
	if(i) {
		fprintf(stderr,"!VDD_OPEN ERROR: %d\n",i);
		UnRegisterModule();
		return(-1);
588
	}
589 590 591 592 593
	oldint16=_dos_getvect(0x16);
	oldint21=_dos_getvect(0x21);
	oldint29=_dos_getvect(0x29);
	if(mode & SBBSEXEC_MODE_FOSSIL) {
		*(WORD*)((BYTE*)int14stub+1) = (WORD)winNTint14 - (WORD)&int14stub - 3;	/* jmp offset */
594
		_dos_setvect(0x14,(void(interrupt *)())int14stub); 
595
	}
596 597 598 599 600
	_dos_setvect(0x21,winNTint21); 
	if(mode&SBBSEXEC_MODE_DOS_IN)
		_dos_setvect(0x16,winNTint16); 
	if(mode&SBBSEXEC_MODE_DOS_OUT) 
		_dos_setvect(0x29,winNTint29); 
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615

	_heapmin();
	i=_spawnvp(_P_WAIT, arg[0], arg);

	p=argv[1]+(strlen(argv[1])-3);
	strcpy(p,"RET");
	if((fp=fopen(argv[1],"w+"))==NULL) {
		fprintf(stderr,"!Error opening %s\n",argv[1]);
		return(3);
	}
	fprintf(fp,"%d",i);

	/* Restore original ISRs */
	_dos_setvect(0x14,oldint14);

616
	vdd_op(VDD_CLOSE);
617

618 619 620
	_dos_setvect(0x16,oldint16);
	_dos_setvect(0x21,oldint21);
	_dos_setvect(0x29,oldint29);
621

622 623
	sprintf(str,"%s returned %d", arg[0], i);
	vdd_str(VDD_DEBUG_OUTPUT, str);
624

625
#ifdef DEBUG_INT_CALLS
626 627 628 629
	sprintf(str,"int14h calls: %lu", int14calls);	vdd_str(VDD_DEBUG_OUTPUT, str);
	sprintf(str,"int16h calls: %lu", int16calls);	vdd_str(VDD_DEBUG_OUTPUT, str);
	sprintf(str,"int21h calls: %lu", int21calls);	vdd_str(VDD_DEBUG_OUTPUT, str);
	sprintf(str,"int29h calls: %lu", int29calls);	vdd_str(VDD_DEBUG_OUTPUT, str);
630
#endif
631
#ifdef DEBUG_DOS_CALLS
632 633 634 635 636
	for(i=0;i<0x100;i++) {
		if(dos_calls[i]>100) {
			sprintf(str,"int21h function %02X calls: %u"
				,i, dos_calls[i]);
			vdd_str(VDD_DEBUG_OUTPUT, str);
637
		}
638
	}
639
#endif
640
#ifdef DEBUG_FOSSIL_CALLS
641 642 643 644 645
	for(i=0;i<0x100;i++) {
		if(fossil_calls[i]>0) {
			sprintf(str,"int14h function %02X (%-10s) calls: %lu"
				,i, fossil_func(i), fossil_calls[i]);
			vdd_str(VDD_DEBUG_OUTPUT, str);
646
		}
647
	}
648 649
#endif

650 651 652
	/* Unregister VDD */
	_asm mov ax, vdd;
	UnRegisterModule();
rswindell's avatar
rswindell committed
653

654 655
	return(i);
}