sbbsexec.c 20.8 KB
Newer Older
1
2
3
4
/* sbbsexec.c */

/* Synchronet Windows NT/2000 VDD for FOSSIL and DOS I/O Interrupts */

5
/* $Id: sbbsexec.c,v 1.41 2018/07/24 01:11:08 rswindell Exp $ */
6
7
8
9
10

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
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
 *																			*
 * 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 <windows.h> 
#include <stdio.h>
#include <vddsvc.h>
41
#include "uartdefs.h"
42
43
#include "vdd_func.h"
#include "ringbuf.h"
44
#include "genwrap.h"
45
#include "dirwrap.h"
46
#include "threadwrap.h"
47
#include "ini_file.h"
48

49
#define INI_FILENAME			"sbbsexec.ini"
50
51
#define RINGBUF_SIZE_IN			10000
#define DEFAULT_MAX_MSG_SIZE	4000
52
#define LINEAR_RX_BUFLEN		10000
53

54
/* UART Parameters and virtual registers */
55
WORD uart_io_base				= UART_COM1_IO_BASE;	/* COM1 */
56
BYTE uart_irq					= UART_COM1_IRQ;
57
BYTE uart_ier_reg				= 0;
58
BYTE uart_lcr_reg				= UART_LCR_8_DATA_BITS;
59
BYTE uart_mcr_reg				= UART_MCR_RTS | UART_MCR_DTR;
60
61
62
63
64
65
66
67
68
69
70
71
BYTE uart_lsr_reg				= UART_LSR_EMPTY_DATA | UART_LSR_EMPTY_XMIT;
BYTE uart_msr_reg				= UART_MSR_CTS | UART_MSR_DSR;
BYTE uart_scratch_reg			= 0;
BYTE uart_divisor_latch_lsb		= 0x03;	/* 38400 */
BYTE uart_divisor_latch_msb		= 0x00;

#if defined (_DEBUG)
	int log_level = LOG_DEBUG;
#else
	int log_level = LOG_WARNING;
#endif

72
BOOL		virtualize_uart=TRUE;
73
double		yield_interval=1.0;
74
75
BOOL		hangup_supported=TRUE;
HANDLE		hangup_event=NULL;
76
77
78
79
80
HANDLE		hungup_event=NULL;
HANDLE		interrupt_event=NULL;
HANDLE		rdslot=INVALID_HANDLE_VALUE;
HANDLE		wrslot=INVALID_HANDLE_VALUE;
RingBuf		rdbuf;
81
82
str_list_t	ini;
char		ini_fname[MAX_PATH+1];
83
char		revision[16];
84

85
86
87
88
89
90
91
92
void lputs(int level, char* msg)
{	
	char buf[1024];
	if(level > log_level)
		return;

	SAFEPRINTF(buf,"SBBS: %s\r\n", msg);
	OutputDebugString(buf);
93
94
95
96
97
98
99
}

static void lprintf(int level, const char *fmt, ...)
{
	char sbuf[1024];
	va_list argptr;

100
101
102
	if(level > log_level)
		return;

103
104
105
106
107
108
109
    va_start(argptr,fmt);
    _vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
    lputs(level,sbuf);
}

110
111
112
113
114
115
116
117
void hangup()
{
	if(hangup_supported && hangup_event!=NULL) {
		lprintf(LOG_DEBUG,"Hanging-up at application request");
		SetEvent(hangup_event);
	}
}

118
119
120
121
void parse_ini(char* program)
{
	char section[MAX_PATH+1];

122
123
124
	if(ini==NULL)	/* no initialization file */
		return;

125
126
127
128
129
	/* Read the root section of the sbbsexec.ini file */
	log_level=iniGetLogLevel(ini,program,"LogLevel",log_level);
	if(iniGetBool(ini,program,"Debug",FALSE))
		log_level=LOG_DEBUG;
	yield_interval=iniGetFloat(ini,program,"YieldInterval",yield_interval);
130
	hangup_supported=iniGetBool(ini,program,"CanDisconnect",hangup_supported);
131

132
133
134
135
	lprintf(LOG_INFO,"Parsed %s section of %s"
		,program==ROOT_SECTION ? "root" : program
		,ini_fname);

136
137
138
139
140
	/* [UART] section */
	if(program==ROOT_SECTION)
		SAFECOPY(section,"UART");
	else
		SAFEPRINTF(section,"%s.UART",program);
141
142
143

	virtualize_uart=iniGetBool(ini,section,"Virtualize",virtualize_uart);
	switch(iniGetInteger(ini,section,"ComPort",0)) {
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
		case 1:	/* COM1 */
			uart_irq		=UART_COM1_IRQ;
			uart_io_base	=UART_COM1_IO_BASE;
			break;
		case 2:	/* COM2 */
			uart_irq		=UART_COM2_IRQ;
			uart_io_base	=UART_COM2_IO_BASE;
			break;
		case 3:	/* COM3 */
			uart_irq		=UART_COM3_IRQ;
			uart_io_base	=UART_COM3_IO_BASE;
			break;
		case 4:	/* COM4 */
			uart_irq		=UART_COM4_IRQ;
			uart_io_base	=UART_COM4_IO_BASE;
			break;
	}
	uart_irq=(BYTE)iniGetShortInt(ini,section,"IRQ",uart_irq);
	uart_io_base=iniGetShortInt(ini,section,"Address",uart_io_base);
163
164
165
166

	lprintf(LOG_INFO,"Parsed %s section of %s"
		,section
		,ini_fname);
167
168
169
}


170
/* Mutex-protected pending interrupt "queue" */
171
int pending_interrupts = 0;
172
CRITICAL_SECTION interrupt_mutex;
173

174
void set_interrupt_pending(BYTE intr, BOOL assert)
175
{
176
	EnterCriticalSection(&interrupt_mutex);
177
178
179
180
	lprintf(LOG_DEBUG,"%sasserting interrupt %02X (pending: %02X, IER: %02X)"
		,assert ? "" : "de-", intr
		,pending_interrupts
		,uart_ier_reg);
181
	if(assert) {
182
183
184
185
		if(uart_ier_reg&intr) {					/* is interrupt enabled? */
			pending_interrupts |= intr;			/* flag as pending */
			SetEvent(interrupt_event);
		}
186
187
	} else /* de-assert */
		pending_interrupts &= ~intr;		/* remove as pending */
188

189
	LeaveCriticalSection(&interrupt_mutex);
190
191
}

192
193
194
#define assert_interrupt(i)		set_interrupt_pending(i, TRUE)
#define deassert_interrupt(i)	set_interrupt_pending(i, FALSE)

195
196
197
void _cdecl interrupt_thread(void *arg)
{
	while(1) {
198
199
200
		if(WaitForSingleObject(interrupt_event,INFINITE)!=WAIT_OBJECT_0)
			break;
		if((uart_ier_reg&pending_interrupts) != 0) {
201
202
			lprintf(LOG_DEBUG,"VDDSimulateInterrupt (pending: %02X) - IER: %02X"
				,pending_interrupts, uart_ier_reg);
203
			VDDSimulateInterrupt(ICA_MASTER, uart_irq, /* count: */1);
204
		}
205
206
207
208
209
210
211
212
#if 0
		/* "Real 16550s should always reassert
		 *  this interrupt whenever the transmitter is idle and
		 *  the interrupt is enabled."
		 */
		if(pending_interrupts==0 && uart_ier_reg&UART_IER_TX_EMPTY)
			pending_interrupts|=UART_IER_TX_EMPTY;
#endif
213
214
215
	}
}

216
void _cdecl input_thread(void* arg)
217
{
218
219
	char	buf[LINEAR_RX_BUFLEN];
	int		count;
220

221
	lprintf(LOG_DEBUG,"input_thread: started");
222
	while(1) {
223
224
225
226
227
228
229
230
231
232
233
		count=RingBufFree(&rdbuf);
		if(count<1) {
			lprintf(LOG_WARNING,"input_thread: input buffer full!");
			YIELD();
			continue;
		}
		if(count>sizeof(buf))
			count=sizeof(buf);
		if(!ReadFile(rdslot,buf,count,&count,NULL)) {
			if(GetLastError()==ERROR_HANDLE_EOF) {	/* closed by VDD_CLOSE */
				lprintf(LOG_INFO,"input_thread: ReadFile returned EOF");
234
				break;
235
			}
rswindell's avatar
rswindell committed
236
			lprintf(LOG_ERR,"!input_thread: ReadFile Error %d (size=%d)"
237
238
239
240
				,GetLastError(),count);
			continue;
		}
		if(count==0) {
rswindell's avatar
rswindell committed
241
			lprintf(LOG_ERR,"!input_thread: ReadFile read 0");
242
243
244
			continue;
		}
		RingBufWrite(&rdbuf,buf,count);
245

246
247
248
249
250
251
		if(virtualize_uart) {
			/* Set the "Data ready" bit in the LSR */
			uart_lsr_reg |= UART_LSR_DATA_READY;

			assert_interrupt(UART_IER_RX_DATA); /* assert rx data interrupt */
		}
252
	}
253
	lprintf(LOG_DEBUG,"input_thread: terminated");
254
}
255

256
257
unsigned vdd_read(BYTE* p, unsigned count)
{
258
	sem_wait(&rdbuf.sem);
259
260
261
262
263
	count=RingBufRead(&rdbuf,p,count);
	if(count==0)
		lprintf(LOG_ERR,"!VDD_READ: RingBufRead read 0");

	return(count);
264
265
}

266
267
unsigned yields=0;

268
void yield()
269
270
{
	yields++;
271
	lprintf(LOG_DEBUG,"Yielding (yields=%u)", yields);
272
273
274
	Sleep(1);
}

275
276
long double last_yield=0;

277
278
void maybe_yield()
{
279
280
281
282
	long double t;

	t=xp_timer();

283
	if(yield_interval && (t-last_yield)*1000.0 >= yield_interval) {
284
		yield();
285
286
287
288
289
290
291
		last_yield=t;
	}
}

void reset_yield()
{
	last_yield=xp_timer();
292
293
}

rswindell's avatar
rswindell committed
294
295
296
297
/***********************/
/* UART Virtualization */
/***********************/

298
299
300
301
302
303
304
305
306
307
308
309
310
static char *chr(uchar ch)
{
	static char str[25];

	if(ch>=' ' && ch<='~')
		sprintf(str,"'%c' (%02Xh)",ch,ch);
	else if(ch<' ')
		sprintf(str,"^%c  (%02Xh)",'@'+ch,ch);
	else
		sprintf(str,"%u (%02Xh)",ch,ch);
	return(str); 
}

311
312
313
314
315
316
317
318
319
320
321
322
323
VOID uart_wrport(WORD port, BYTE data)
{
	int reg = port - uart_io_base;
	int retval;

	lprintf(LOG_DEBUG,"write of port: %x (%s) <- %02X", port, uart_reg_desc[reg], data);

	switch(reg) {
		case UART_BASE:
			if(uart_lcr_reg&UART_LCR_DLAB) {
				uart_divisor_latch_lsb = data;
				lprintf(LOG_DEBUG,"set divisor latch low byte: %02X", data);
			} else {
324
				lprintf(LOG_DEBUG,"WRITE DATA: %s", chr(data));
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
				if(!WriteFile(wrslot,&data,sizeof(BYTE),&retval,NULL)) {
					lprintf(LOG_ERR,"!VDD_WRITE: WriteFile Error %d (size=%d)"
						,GetLastError(),retval);
				} else {
					assert_interrupt(UART_IER_TX_EMPTY);
					reset_yield();
				}
			}
			break;
		case UART_IER:
			if(uart_lcr_reg&UART_LCR_DLAB) {
				uart_divisor_latch_msb = data;
				lprintf(LOG_DEBUG,"set divisor latch high byte: %02X", data);
			} else
				uart_ier_reg = data;
			assert_interrupt(UART_IER_TX_EMPTY);	/* should this be re-asserted for all writes? */
			break;
		case UART_IIR: /* FCR not supported */
			break;
		case UART_LCR:
			uart_lcr_reg = data;
			break;
		case UART_MCR:
			uart_mcr_reg = data;
349
			if((uart_mcr_reg&UART_MCR_DTR) == 0)	/* Dropping DTR (i.e. "hangup") */
350
				hangup();
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
			break;
		case UART_SCRATCH:
			uart_scratch_reg = data;
			break;
		default:
			lprintf(LOG_ERR,"UNSUPPORTED register: %u", reg);
			break;
			
	}
}

VOID uart_rdport(WORD port, PBYTE data)
{
	int reg = port - uart_io_base;
	DWORD avail;

	lprintf(LOG_DEBUG,"read of port: %x (%s)", port, uart_reg_desc[reg]);

	switch(reg) {
		case UART_BASE:
			if(uart_lcr_reg&UART_LCR_DLAB) {
				lprintf(LOG_DEBUG,"reading divisor latch LSB");
				*data = uart_divisor_latch_lsb;
				break;
			}
			if((avail=RingBufFull(&rdbuf))!=0) {
				vdd_read(data,sizeof(BYTE));
378
				lprintf(LOG_DEBUG,"READ DATA: %s", chr(*data));
379
380
				avail--;
				reset_yield();
381
382
			} else
				*data=0;
383
			if(avail==0) {
384
				lprintf(LOG_DEBUG,"No more data");
385
386
387
388
389
				/* Clear the data ready bit in the LSR */
				uart_lsr_reg &= ~UART_LSR_DATA_READY;

				/* Clear data ready interrupt identification in IIR */
				deassert_interrupt(UART_IER_RX_DATA);
390
391
			} else	/* re-assert RX data (increment the semaphore) */
				assert_interrupt(UART_IER_RX_DATA);
392
			break;
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
		case UART_IER:
			if(uart_lcr_reg&UART_LCR_DLAB) {
				lprintf(LOG_DEBUG,"reading divisor latch MSB");
				*data = uart_divisor_latch_msb;
			} else
				*data = uart_ier_reg;
			break;
		case UART_IIR:
			/* Report IIR based on *priority* of pending interrupts */
			if(pending_interrupts & UART_IER_LINE_STATUS)
				*data = UART_IIR_LINE_STATUS;
			else if(pending_interrupts & UART_IER_RX_DATA)
				*data = UART_IIR_RX_DATA;
			else if(pending_interrupts & UART_IER_TX_EMPTY) {
				*data = UART_IIR_TX_EMPTY;
				/* "Transmit Holding Register Empty" interrupt */
				/*  is reset on read of IIR */
				deassert_interrupt(UART_IER_TX_EMPTY);
			}
			else if(pending_interrupts & UART_IER_MODEM_STATUS)
				*data = UART_IIR_MODEM_STATUS;
			else
				*data = UART_IIR_NONE;
			break;
		case UART_LCR:
			*data = uart_lcr_reg;
			break;
		case UART_MCR:
			*data = uart_mcr_reg;
			break;
		case UART_LSR:
			*data = uart_lsr_reg;
			maybe_yield();
			/* Clear line status interrupt pending */
			deassert_interrupt(UART_IER_LINE_STATUS);
			break;
		case UART_MSR:
			if(WaitForSingleObject(hungup_event,0)==WAIT_OBJECT_0)
				uart_msr_reg &=~ UART_MSR_DCD;
			else
				uart_msr_reg |= UART_MSR_DCD;
			*data = uart_msr_reg;
			maybe_yield();
			/* Clear modem status interrupt pending */
			deassert_interrupt(UART_IER_MODEM_STATUS);
			break;
		case UART_SCRATCH:
			*data = uart_scratch_reg;
			break;
		default:
			lprintf(LOG_ERR,"UNSUPPORTED register: %u", reg);
			break;
	}

447
448
	if(reg!=UART_BASE)
		lprintf(LOG_DEBUG, "returning 0x%02X", *data);
449
450
}

rswindell's avatar
rswindell committed
451
452
/* VDD DOS Interface (mainly for FOSSIL driver in dosxtrn.exe) */

453
454
455
456
__declspec(dllexport) void __cdecl VDDDispatch(void) 
{
	char			str[512];
	DWORD			count;
457
	DWORD			msgs;
458
459
460
461
	int				retval;
	int				node_num;
	BYTE*			p;
	vdd_status_t*	status;
462
463
464
465
	static  DWORD	writes;
	static  DWORD	bytes_written;
	static	DWORD	reads;
	static  DWORD	bytes_read;
466
467
468
	static  DWORD	inbuf_poll;
	static	DWORD	online_poll;
	static	DWORD	status_poll;
469
470
	static	DWORD	vdd_yields;
	static	DWORD	vdd_calls;
471
472
	VDD_IO_HANDLERS  IOHandlers = { NULL };
	static VDD_IO_PORTRANGE PortRange;
473

474
	retval=0;
475
476
	node_num=getBH();

rswindell's avatar
rswindell committed
477
	lprintf(LOG_DEBUG,"VDD_OP: (handle=%d) %d (arg=%X)", getAX(),getBL(),getCX());
478
	vdd_calls++;
479

480
481
482
	switch(getBL()) {

		case VDD_OPEN:
483

484
			sscanf("$Revision: 1.41 $", "%*s %s", revision);
rswindell's avatar
rswindell committed
485

486
487
			lprintf(LOG_INFO,"Synchronet Virtual Device Driver, rev %s %s %s"
				,revision, __DATE__, __TIME__);
488
#if 0
489
490
			sprintf(str,"sbbsexec%d.log",node_num);
			fp=fopen(str,"wb");
rswindell's avatar
rswindell committed
491
#endif
492
493
494

			sprintf(str,"\\\\.\\mailslot\\sbbsexec\\wr%d",node_num);
			rdslot=CreateMailslot(str
495
				,0 //LINEAR_RX_BUFLEN		/* Max message size (0=any) */
496
				,MAILSLOT_WAIT_FOREVER 	/* Read timeout */
497
498
				,NULL);
			if(rdslot==INVALID_HANDLE_VALUE) {
499
500
				lprintf(LOG_ERR,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
501
502
503
504
505
506
507
508
509
510
511
512
513
				retval=1;
				break;
			}

			sprintf(str,"\\\\.\\mailslot\\sbbsexec\\rd%d",node_num);
			wrslot=CreateFile(str
				,GENERIC_WRITE
				,FILE_SHARE_READ
				,NULL
				,OPEN_EXISTING
				,FILE_ATTRIBUTE_NORMAL
				,(HANDLE) NULL);
			if(wrslot==INVALID_HANDLE_VALUE) {
514
515
				lprintf(LOG_ERR,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
516
517
518
519
520
521
522
523
524
525
526
				retval=2;
				break;
			}

			if(RingBufInit(&rdbuf, RINGBUF_SIZE_IN)!=0) {
				retval=3;
				break;
			}

			sprintf(str,"sbbsexec_hungup%d",node_num);
			hungup_event=OpenEvent(
527
528
529
				EVENT_ALL_ACCESS,	/* access flag  */
				FALSE,				/* inherit flag  */
				str);				/* pointer to event-object name  */
530
			if(hungup_event==NULL) {
531
532
				lprintf(LOG_ERR,"!VDD_OPEN: Error %d opening %s"
					,GetLastError(),str);
533
534
535
536
				retval=4;
				break;
			}

537
538
539
540
541
542
			sprintf(str,"sbbsexec_hangup%d",node_num);
			hangup_event=OpenEvent(
				EVENT_ALL_ACCESS,	/* access flag  */
				FALSE,				/* inherit flag  */
				str);				/* pointer to event-object name  */
			if(hangup_event==NULL) {
543
				lprintf(LOG_WARNING,"!VDD_OPEN: Error %d opening %s"
544
545
546
					,GetLastError(),str);
			}

547
548
549
			status_poll=0;
			inbuf_poll=0;
			online_poll=0;
550
			yields=0;
551

552
553
554
555
556
557
558
559
560
561
562
			lprintf(LOG_INFO,"Yield interval: %f milliseconds", yield_interval);

			if(virtualize_uart) {
				lprintf(LOG_INFO,"Virtualizing UART (0x%x, IRQ %u)"
					,uart_io_base, uart_irq);

				IOHandlers.inb_handler = uart_rdport;
				IOHandlers.outb_handler = uart_wrport;
				PortRange.First=uart_io_base;
				PortRange.Last=uart_io_base + UART_IO_RANGE;

rswindell's avatar
rswindell committed
563
				VDDInstallIOHook((HANDLE)getAX(), 1, &PortRange, &IOHandlers);
564
565
566
567
568
569
570

				interrupt_event=CreateEvent(NULL,FALSE,FALSE,NULL);
				InitializeCriticalSection(&interrupt_mutex);

				_beginthread(interrupt_thread, 0, NULL);
			}

571
			lprintf(LOG_DEBUG,"VDD_OPEN: Opened successfully (wrslot=%p)", wrslot);
572

573
574
			_beginthread(input_thread, 0, NULL);

575
576
577
578
			retval=0;
			break;

		case VDD_CLOSE:
579
580
581
582
			lprintf(LOG_INFO,"VDD_CLOSE: rdbuf=%u "
				"status_poll=%u inbuf_poll=%u online_poll=%u yields=%u vdd_yields=%u vdd_calls=%u"
				,RingBufFull(&rdbuf),status_poll,inbuf_poll,online_poll
				,yields,vdd_yields,vdd_calls);
583
584
585
586
587
			lprintf(LOG_INFO,"           read=%u bytes (in %u calls)",bytes_read,reads);
			lprintf(LOG_INFO,"           wrote=%u bytes (in %u calls)",bytes_written,writes);

			if(virtualize_uart) {
				lprintf(LOG_INFO,"Uninstalling Virtualizaed UART IO Hook");
rswindell's avatar
rswindell committed
588
				VDDDeInstallIOHook((HANDLE)getAX(), 1, &PortRange);
589
			}
590

591
592
			CloseHandle(rdslot);
			CloseHandle(wrslot);
593
594
			if(hungup_event!=NULL)
				CloseHandle(hungup_event);
595
596
597
			if(hangup_event!=NULL)
				CloseHandle(hangup_event);

598
599
#if 0	/* This isn't strictly necessary... 
		   and possibly the cause of a NULL dereference in the input_thread */
600
			RingBufDispose(&rdbuf);
601
#endif
602
603
604
605
606
607
608
			status_poll=0;
			retval=0;

			break;

		case VDD_READ:
			count = getCX();
609
610
			if(count != 1)
				lprintf(LOG_DEBUG,"VDD_READ of %d",count);
611
612
			p = (BYTE*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
613
			retval=vdd_read(p, count);
614
615
			reads++;
			bytes_read+=retval;
616
			reset_yield();
617
618
619
620
			break;

		case VDD_PEEK:
			count = getCX();
621
622
			if(count != 1)
				lprintf(LOG_DEBUG,"VDD_PEEK of %d",count);
623
624
625
626

			p = (BYTE*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			retval=RingBufPeek(&rdbuf,p,count);
627
			reset_yield();
628
629
630
631
			break;

		case VDD_WRITE:
			count = getCX();
632
633
			if(count != 1)
				lprintf(LOG_DEBUG,"VDD_WRITE of %d",count);
634
635
636
			p = (BYTE*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			if(!WriteFile(wrslot,p,count,&retval,NULL)) {
637
638
				lprintf(LOG_ERR,"!VDD_WRITE: WriteFile Error %d (size=%d)"
					,GetLastError(),retval);
639
				retval=0;
640
641
642
			} else {
				writes++;
				bytes_written+=retval;
643
				reset_yield();
644
645
646
647
648
649
650
651
			}
			break;

		case VDD_STATUS:

			status_poll++;
			count = getCX();
			if(count != sizeof(vdd_status_t)) {
652
				lprintf(LOG_DEBUG,"!VDD_STATUS: wrong size (%d!=%d)",count,sizeof(vdd_status_t));
653
654
655
656
657
658
				retval=sizeof(vdd_status_t);
				break;
			}
			status = (vdd_status_t*) GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 

659
			status->inbuf_size=RINGBUF_SIZE_IN;
660
			status->inbuf_full=RingBufFull(&rdbuf);
661
			msgs=0;
662

rswindell's avatar
rswindell committed
663
			/* OUTBUF FULL/SIZE */
664
			if(!GetMailslotInfo(
665
666
667
668
669
				wrslot,					/* mailslot handle  */
 				&status->outbuf_size,	/* address of maximum message size  */
				&status->outbuf_full,	/* address of size of next message  */
				&msgs,					/* address of number of messages  */
 				NULL					/* address of read time-out  */
670
				)) {
671
672
673
				lprintf(LOG_ERR,"!VDD_STATUS: GetMailSlotInfo(%p) failed, error %u (msgs=%u, inbuf_full=%u, inbuf_size=%u)"
					,wrslot
					,GetLastError(), msgs, status->inbuf_full, status->inbuf_size);
674
				status->outbuf_full=0;
675
				status->outbuf_size=DEFAULT_MAX_MSG_SIZE;
676
677
678
679
680
			} else
				lprintf(LOG_DEBUG,"VDD_STATUS: MailSlot maxmsgsize=%u, nextmsgsize=%u, msgs=%u"
					,status->outbuf_size
					,status->outbuf_full
					,msgs);
681
682
			if(status->outbuf_full==MAILSLOT_NO_MESSAGE)
				status->outbuf_full=0;
683
			status->outbuf_full*=msgs;
684
685
686
687
688
689
690
691
692
693
694
			
			/* ONLINE */
			if(WaitForSingleObject(hungup_event,0)==WAIT_OBJECT_0)
				status->online=0;
			else
				status->online=1;

			retval=0;	/* success */
			break;

		case VDD_INBUF_PURGE:
695
			RingBufReInit(&rdbuf);
696
697
698
699
			retval=0;
			break;

		case VDD_OUTBUF_PURGE:
700
			lprintf(LOG_WARNING,"!VDD_OUTBUF_PURGE: NOT IMPLEMENTED");
701
702
703
704
			retval=0;
			break;

		case VDD_INBUF_FULL:
705
			retval=RingBufFull(&rdbuf);
706
707
708
709
			inbuf_poll++;
			break;

		case VDD_INBUF_SIZE:
710
			retval=RINGBUF_SIZE_IN;
711
712
713
714
			break;

		case VDD_OUTBUF_FULL:
			if(!GetMailslotInfo(
715
716
717
718
719
				wrslot,		/* mailslot handle  */
 				NULL,		/* address of maximum message size  */
				&retval,	/* address of size of next message  */
				&msgs,		/* address of number of messages  */
 				NULL		/* address of read time-out  */
720
721
722
723
				))
				retval=0;
			if(retval==MAILSLOT_NO_MESSAGE)
				retval=0;
724
			retval*=msgs;
725
726
727
728
			break;

		case VDD_OUTBUF_SIZE:
			if(!GetMailslotInfo(
729
730
731
732
733
				wrslot,		/* mailslot handle  */
 				&retval,	/* address of maximum message size  */
				NULL,		/* address of size of next message  */
				NULL,		/* address of number of messages  */
 				NULL		/* address of read time-out  */
734
				)) 
735
				retval=DEFAULT_MAX_MSG_SIZE;
736
737
738
739
740
741
742
743
744
			break;

		case VDD_ONLINE:
			if(WaitForSingleObject(hungup_event,0)==WAIT_OBJECT_0)
				retval=0;
			else
				retval=1;
			online_poll++;
			break;
745

rswindell's avatar
rswindell committed
746
		case VDD_YIELD:			/* forced yield */
747
			vdd_yields++;
748
			yield();
749
750
			break;

rswindell's avatar
rswindell committed
751
		case VDD_MAYBE_YIELD:	/* yield if YieldInterval is enabled and expired */
752
			maybe_yield();
753
754
			break;

755
756
757
758
759
		case VDD_LOAD_INI_FILE:	/* Load and parse settings file */
			{
				FILE*	fp;
				char	cwd[MAX_PATH+1];

760
761
762
763
764
765
766
767
768
769
770
771
				/* Load exec/sbbsexec.ini first (setting default values) */
				count = getCX();
				p = (BYTE*)GetVDMPointer((ULONG)((getES() << 16)|getDI())
					,count,FALSE); 
				iniFileName(ini_fname, sizeof(ini_fname), p, INI_FILENAME);
				if((fp=fopen(ini_fname,"r"))!=NULL) {
					ini=iniReadFile(fp);
					fclose(fp);
					parse_ini(ROOT_SECTION);
				}

				/* Load cwd/sbbsexec.ini second (over-riding default values) */
772
773
774
775
776
777
778
779
780
781
782
				GetCurrentDirectory(sizeof(cwd),cwd);
				iniFileName(ini_fname, sizeof(ini_fname), cwd, INI_FILENAME);
				if((fp=fopen(ini_fname,"r"))!=NULL) {
					ini=iniReadFile(fp);
					fclose(fp);
					parse_ini(ROOT_SECTION);
				}
			}
			break;

		case VDD_LOAD_INI_SECTION:	/* Parse (program-specific) sub-section of settings file */
783
			count = getCX();
rswindell's avatar
rswindell committed
784
			p = (BYTE*)GetVDMPointer((ULONG)((getES() << 16)|getDI())
785
				,count,FALSE); 
786
			parse_ini(p);
787
			break;
788

789
790
791
792
793
794
795
		case VDD_DEBUG_OUTPUT:	/* Send string to debug output */
			count = getCX();
			p = (BYTE*)GetVDMPointer((ULONG)((getES() << 16)|getDI())
				,count,FALSE); 
			lputs(LOG_INFO, p);
			break;

796
797
798
799
		case VDD_HANGUP:
			hangup();
			break;

800
		default:
801
			lprintf(LOG_ERR,"!UNKNOWN VDD_OP: %d",getBL());
802
803
			break;
	}
804
	setAX((WORD)retval);
805
}