summaryrefslogblamecommitdiffstats
path: root/private/mvdm/softpc.new/base/comms/printer.c
blob: 0ef68ce1d93ce0301d612137eb712a1ef1b72a9e (plain) (tree)
1
2
3
4
5
6
7
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
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
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
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
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836



































































































































































































































































































































































































































































































































































































































































































































































































































































                                                                                                                                            
#include "insignia.h"
#include "host_def.h"
#ifdef PRINTER

/*
 * VPC-XT Revision 1.0
 *
 * Title:	Parallel Printer Port Emulation
 *
 * Description:	Emulates the IBM || Printer card as used in the original
 *		IBM XT, which is itself a H/W emulation of an Intel 8255.
 *
 * Author:	Henry Nash
 *
 * Notes:	None
 *
 * Mods:
 *		<chrisP 11Sep91>
 *		Allow transition to NOTBUSY in the OUTA state as well as the READY
 *		state.  i.e. at the leading edge of the ACK pulse after just one
 *		INB (STATUS) rather than two.  Our printer port emulation relies on
 *		these INB's to toggle the ACK line and set NOTBUSY true again.  So
 *		the port could be left in the BUSY condition at the end of an app's
 *		print job (which can confuse the next print request).  NOTE we could
 *		still have a problem if the PC app bypasses the BIOS and is too lazy
 *		to do even one INB(STATUS) after the last print byte.
 */



/* for NTVDM port -- williamh
 * There are such things called Dongles which many software companies have
 * used for copy protection. Each software comes with its dedicated Dongle
 * that records necessary indentification inforamtion. It is required
 * to plug the device onto the parallel port in order to run the software
 * correctly. The device has an outlet which can be connectted to parallel
 * port printer so the the user doesn't sacrifice his parallel port when
 * the device in plugged on the original connector.
 * There are several Dongle vendors and each one of them provides their
 * propietary library or driver for the applications to link to. These
 * drivers know how to read/WRITE the Dongle to verify a legitmate copy.
 * Since it has to maintain compatiblility with standard PC parallel port,
 * the devices are designed in a way that it can be programmed without
 * disturbing ordinary parallel port operation. To do this, it usually does
 * this:
 * (1) Turn off strobe.
 * (2) output data pattern to data port
 * (3) delay a little bit(looping in instructions) and then go to (2)
 *     until the chunk of data has been sent. NOTE THAT THE STROBE LINE
 *     IS NEVER "strobe"
 * (4). Read status port and by interpreting the line differently,
 *     the driver decodes any id information it is looking for.
 *
 * In order to support these devices, we have to do these:
 * (1). We can not fake printer status. We have to get the real
 *	status line states.
 * (2). we have to output data to the printer without waiting the data
 *	to be qualified(strobing).
 * (3). We must be smart enough to detect the application is done with
 *	its Dongle things and wants everything goes back to normal.
 *	We must adjust ourselves under this circumestances.
 * (4). Down level printer driver must provide function that we can call
 *	to control the port directly.
 * (5). Printer h/w interrupt is not allowed to be enabled under this
 *	circumstance --and how can we make sure of that?????
 *
 */

#ifdef SCCSID
static char SccsID[] = "@(#)printer.c	1.19 11/14/94 Copyright Insignia Solutions Ltd.";
#endif

#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_PRINTER.seg"
#endif


/*
 *    O/S include files.
 */
#include <stdio.h>
#include TypesH
#include TimeH
#ifdef SYSTEMV
#ifdef STINGER
#include <sys/termio.h>
#endif
#endif

/*
 * SoftPC include files
 */
#include "xt.h"
#include CpuH
#include "sas.h"
#include "ios.h"
#include "bios.h"
#include "printer.h"
#include "error.h"
#include "config.h"
#include "host_lpt.h"
#include "ica.h"
#include "quick_ev.h"

#include "debug.h"
#ifndef PROD
#include "trace.h"
#endif


/*
 * ============================================================================
 * Global data
 * ============================================================================
 */


/*
 * ============================================================================
 * Static data and defines
 * ============================================================================
 */

#define PRINTER_BIT_MASK	0x3	/* bits decoded from address bus */
#define CONTROL_REG_MASK	0xE0;	/* unused bits drift to HIGH */
#define STATUS_REG_MASK		0x07;	/* unused bits drift to HIGH */

#define DATA_OFFSET	0		/* ouput register */
#define STATUS_OFFSET	1		/* status register */
#define CONTROL_OFFSET	2		/* control register */

#ifdef	ERROR
#undef	ERROR
#endif

static half_word output_reg[NUM_PARALLEL_PORTS];
static half_word control_reg[NUM_PARALLEL_PORTS];
#define NOTBUSY		0x80
#define ACK		0x40
#define PEND		0x20
#define SELECT		0x10
#define ERROR		0x08

static half_word status_reg[NUM_PARALLEL_PORTS];
#define IRQ		0x10
#define SELECT_IN	0x08
#define INIT_P		0x04
#define AUTO_FEED	0x02
#define STROBE		0x01

LOCAL IU8 retryErrorCount = 0;   /* num status inb before clearing ERROR */

static int state[NUM_PARALLEL_PORTS]; /* state control variable */
/*
 * set up arrays of all port addresses 
 */
static io_addr port_start[] = {LPT1_PORT_START,LPT2_PORT_START,LPT3_PORT_START};
static io_addr port_end[] = {LPT1_PORT_END, LPT2_PORT_END, LPT3_PORT_END};
static int port_no[] = {LPT1_PORT_START & LPT_MASK, LPT2_PORT_START & LPT_MASK,
			LPT3_PORT_START & LPT_MASK };
static half_word lpt_adapter[] = {LPT1_ADAPTER, LPT2_ADAPTER, LPT3_ADAPTER};
static sys_addr port_address[] = {LPT1_PORT_ADDRESS, LPT2_PORT_ADDRESS, LPT3_PORT_ADDRESS};
static sys_addr timeout_address[] = {LPT1_TIMEOUT_ADDRESS, LPT2_TIMEOUT_ADDRESS, LPT3_TIMEOUT_ADDRESS};
static q_ev_handle handle_for_out_event[NUM_PARALLEL_PORTS];
static q_ev_handle handle_for_outa_event[NUM_PARALLEL_PORTS];

#if defined(NTVDM) && defined(MONITOR)
/* sudeepb 24-Jan-1993 for printing performance for x86 */
sys_addr lp16BitPrtBuf;
sys_addr lp16BitPrtId;
sys_addr lp16BitPrtCount;
sys_addr lp16BitPrtBusy;
#endif

#define STATE_READY     0
#define STATE_OUT       1
#define STATE_OUTA      2
#if defined(NTVDM)
#define STATE_DATA	3
#define STATE_DONGLE	4
#endif

/*
 * State transitions:
 *
 *	    +->	 STATE_READY
 *	    |	  |
 *          |     | ........ write char to output_reg, print on low-high strobe
 *	    |	  V          set NOTBUSY to false
 *	    |	 STATE_OUT
 *	    |	  |
 *	    |	  | ........ (read status) set ACK low
 *	    |	  V
 *	    |	 STATE_OUTA
 *	    |	  |
 *	    |	  | ........ (read status) set ACK high
 *	    +-----+
 *
 *	Caveat: if the control register interrupt request bit is set,
 *	we assume that the application isn't interested in getting the
 *	ACKs and just wants to know when the printer state changes back
 *	to NOTBUSY. I'm not sure to want extent you can get away with
 *	this: however, applications using the BIOS printer services
 *	should be OK.
 */

#ifdef PS_FLUSHING
LOCAL IBOOL psFlushEnabled[NUM_PARALLEL_PORTS];	/* TRUE if PostScript flushing
						is enabled */
#endif	/* PS_FLUSHING */


/*
 * ============================================================================
 * Internal functions & macros 
 * ============================================================================
 */

#define set_low(val, bit)		val &= ~bit
#define set_high(val, bit)		val |=  bit
#define low_high(val1, val2, bit)	(!(val1 & bit) && (val2 & bit))
#define high_low(val1, val2, bit)	((val1 & bit) && !(val2 & bit))
#define toggled(val1, val2, bit)	((val1 & bit) != (val2 & bit))
#define negate(val, bit)		val ^= bit

/*
 * Defines and variables to handle tables stored in 16-bit code for NT
 * monitors.
 */
#if defined(NTVDM) && defined(MONITOR)

static BOOL intel_setup = FALSE;

static sys_addr status_addr;
static sys_addr control_addr;
static sys_addr state_addr;

#define get_status(adap)	(sas_hw_at_no_check(status_addr+(adap)))
#define set_status(adap,val)	(sas_store_no_check(status_addr+(adap),(val)))

#define get_control(adap)	(sas_hw_at_no_check(control_addr+(adap)))
#define set_control(adap,val)	(sas_store_no_check(control_addr+(adap),(val)))

#define get_state(adap)		(sas_hw_at_no_check(state_addr+(adap)))
#define set_state(adap,val)	(sas_store_no_check(state_addr+(adap),(val)))

#else /* NTVDM && MONITOR */

#define get_status(adap)	(status_reg[adapter])
#define set_status(adap,val)	(status_reg[adapter] = (val))

#define get_control(adap)	(control_reg[adapter])
#define set_control(adap,val)	(control_reg[adapter] = (val))

#define get_state(adap)		(state[adapter])
#define set_state(adap,val)	(state[adapter] = (val))

#endif /* NTVDM && MONITOR */

static void printer_inb IPT2(io_addr, port, half_word *, value);
static void printer_outb IPT2(io_addr, port, half_word, value);
static void notbusy_check IPT1(int,adapter);


/*
 * ============================================================================
 * External functions 
 * ============================================================================
 */

void printer_post IFN1(int,adapter)
{
	/*
	 * Set up BIOS data area.
	 */
	sas_storew(port_address[adapter],(word)port_start[adapter]);
	sas_store(timeout_address[adapter], (half_word)0x14 );		/* timeout */
}

#if defined(NTVDM) && defined(MONITOR)
static void lpr_state_outa_event IFN1(long,adapter) 
{ 
	set_status(adapter, get_status(adapter) | ACK);
	set_state(adapter, STATE_READY);
}

static void lpr_state_out_event IFN1(long,adapter)
{
	set_status(adapter, get_status(adapter) & ~ACK);
	set_state(adapter, STATE_OUTA); 
	handle_for_outa_event[adapter]=add_q_event_t(lpr_state_outa_event,HOST_PRINTER_DELAY,adapter); 
} 

#else	/* NTVDM && MONITOR */

static void lpr_state_outa_event IFN1(long,adapter) 
{ 
	set_high(status_reg[adapter],ACK);
	state[adapter]=STATE_READY;
}

static void lpr_state_out_event IFN1(long,adapter)
{
	set_low(status_reg[adapter], ACK);
	state[adapter]=STATE_OUTA; 
	handle_for_outa_event[adapter]=add_q_event_t(lpr_state_outa_event,HOST_PRINTER_DELAY,adapter); 
} 
#endif	/* NTVDM && MONITOR */

static void printer_inb IFN2(io_addr,port, half_word *,value)
{
	int	adapter, i;

	note_trace1(PRINTER_VERBOSE,"inb from printer port %#x ",port);
	/*
	** Scan the ports to find out which one is used. NB the
	** port must be valid one because we only used io_define_inb()
	** for the valid ports
	*/
	for(i=0; i < NUM_PARALLEL_PORTS; i++)
		if((port & LPT_MASK) == port_no[i])
			break;
        adapter = i % NUM_PARALLEL_PORTS;
		
	port = port & PRINTER_BIT_MASK;		/* clear unused bits */

	switch(port)
	{
	case DATA_OFFSET:
                *value = output_reg[adapter];
		break;

	case STATUS_OFFSET:
		switch(get_state(adapter))
		{
#if defined(NTVDM)
		case STATE_DONGLE:
			/* read directly from the port for Dongle */
			*value = host_read_printer_status_port(adapter);
			set_status(adapter, *value);
			break;
		case STATE_DATA:

#endif

		case STATE_READY:
			notbusy_check(adapter);
                        *value = get_status(adapter) | STATUS_REG_MASK;


                        /* Clear ERROR as it will be set if we fail on the print. */
                        /* Clear after two inbs as DOS seems to require this. */
                        if (retryErrorCount > 0)
                            retryErrorCount--;
                        else
                            set_status(adapter, get_status(adapter) | ERROR);
			break;
    	case STATE_OUT:
			*value = get_status(adapter) | STATUS_REG_MASK;
#ifndef DELAYED_INTS
			delete_q_event(handle_for_out_event[adapter]);
                        lpr_state_out_event(adapter);
#else
			set_low(status_reg[adapter], ACK);
                        state[adapter] = STATE_OUTA;
#endif /* DELAYED INTS */
			break;
    	case STATE_OUTA:
			notbusy_check(adapter);		/* <chrisP 11Sep91> */
			*value = get_status(adapter) | STATUS_REG_MASK;
#ifndef DELAYED_INTS
			delete_q_event(handle_for_outa_event[adapter]);
			lpr_state_outa_event(adapter);
#else
			set_high(status_reg[adapter], ACK);
			state[adapter] = STATE_READY;
#endif
			break;
    	default:	
			note_trace1(PRINTER_VERBOSE, 
			            "<pinb() - unknown state %x>", 
			            get_state(adapter));
			break;
		}
		break;
	case CONTROL_OFFSET:
		*value = get_control(adapter) | CONTROL_REG_MASK;
		negate(*value, STROBE);
		negate(*value, AUTO_FEED);
		negate(*value, SELECT_IN);
		break;
	}
	note_trace3(PRINTER_VERBOSE, "<pinb() %x, ret %x, state %x>",
		    port, *value, get_state(adapter));


}

static void printer_outb IFN2(io_addr,port, half_word,value)
{
	int	adapter, i;
	half_word old_control;
#ifdef PC_CONFIG
	char	variable_text[MAXPATHLEN];
	int softpcerr;
	int severity;

	softpcerr = 0;
	severity = 0;
#endif


	note_trace2(PRINTER_VERBOSE,"outb to printer port %#x with value %#x",
	            port, value);

	/*
	** Scan the ports to find out which one is used. NB the
	** port must be valid one because we only used io_define_inb()
	** for the valid ports
	*/
	for(i=0; i < NUM_PARALLEL_PORTS; i++)
		if((port & LPT_MASK) == port_no[i])
			break;
	adapter = i % NUM_PARALLEL_PORTS; 			

	note_trace3(PRINTER_VERBOSE, "<poutb() %x, val %x, state %x>",
		    port, value, get_state(adapter));

	port = port & PRINTER_BIT_MASK;		/* clear unused bits */

	switch(get_state(adapter))
	{
#if defined(NTVDM)
	case STATE_DONGLE:
	    if (port == DATA_OFFSET) {
		output_reg[adapter] = value;
		host_print_byte(adapter, value);
		break;
	    }
	    // fall through
	case STATE_DATA:
		if (port == DATA_OFFSET) {
		    if (host_set_lpt_direct_access(adapter, TRUE)) {
			host_print_byte(adapter, output_reg[adapter]);
			host_print_byte(adapter, value);
			set_state(adapter, STATE_DONGLE);
			/* Write char to internal buffer */
			output_reg[adapter] = value;
		    }
		    else {
			    /* unable to open the lpt for direct access,
			       mark the device busy */

#if !defined(MONITOR)
			set_low(status_reg[adapter], NOTBUSY);
#else /* NTVDM && !MONITOR */
			set_status(adapter, 0x7F);
#endif


		    }
		    break;
		}

	// fall through
#endif
	case STATE_OUT:
	case STATE_OUTA:
	case STATE_READY:
		switch(port)
		{
		case DATA_OFFSET:
#if defined(NTVDM)
			set_state(adapter, STATE_DATA);
#endif
			/* Write char to internal buffer */
			output_reg[adapter] = value;
			break;
		case STATUS_OFFSET:
			/* Not possible */
			break;

		case CONTROL_OFFSET:
			/* Write control bits */
			old_control = get_control(adapter);	/* Save old value to see what's changed */
			set_control(adapter, value);
			if (low_high(old_control, value, INIT_P))
#ifdef PC_CONFIG
				/* this was a call to host_print_doc - <chrisP 28Aug91> */
				host_reset_print(&softpcerr, &severity);
			if (softpcerr != 0)
				host_error(softpcerr, severity, variable_text);
#else
				/* this was a call to host_print_doc - <chrisP 28Aug91> */
				host_reset_print(adapter);
#endif

			if (toggled(old_control, value, AUTO_FEED))
				host_print_auto_feed(adapter,
					((value & AUTO_FEED) != 0));

			if (low_high(old_control, value, STROBE))
			{
#if defined(NTVDM)
			    if (get_state(adapter) == STATE_DONGLE) {
				host_set_lpt_direct_access(adapter, FALSE);
				/* pass through to print out the last byte
				 * which we have sent it out the data port
				 * while we are in DONGLE state.
				 */

				set_state(adapter, STATE_READY);
			    }
#endif

#ifdef PS_FLUSHING
				/*
				 * If PostScript flushing is enabled for this
				 * port then we flush on a Ctrl-D
				 */
				if ( psFlushEnabled[adapter] &&
				     output_reg[adapter] == 0x04 /* ^D */ ) {
					host_print_doc(adapter);
				} else {
#endif	/* PS_FLUSHING */
				       /*
				 	* Send the stored internal buffer to
				 	* the printer
				 	*/
                                	if(host_print_byte(adapter,output_reg[adapter]) == FALSE)
					{
				    		set_status(adapter, get_status(adapter) & ~ERROR); /* active Low */
				    		/* NTVDM had here(?): set_status(adapter, ACK|PEND|SELECT|ERROR); */
				    		/* two status inbs before we clear ERROR */
				    		retryErrorCount = 2;  
					}
					else
					{
                                    		/* clear ERROR condition */
                                    		set_status(adapter, get_status(adapter) | ERROR);
#if defined(NTVDM) && !defined(MONITOR)
				    		set_low(status_reg[adapter], NOTBUSY);
#else /* NTVDM && !MONITOR */
				    		set_status(adapter,
					       	get_status(adapter) & ~NOTBUSY);
#endif /* NTVDM && !MONITOR */
				    		set_state(adapter, STATE_OUT);
#ifndef DELAYED_INTS
				    		handle_for_out_event[adapter]=add_q_event_t(lpr_state_out_event,HOST_PRINTER_DELAY,adapter);
#endif /* DELAYED_INTS */
					}
#ifdef PS_FLUSHING
				}
#endif	/* PS_FLUSHING */
			}
			else if (high_low(old_control, value, STROBE)
				 	&& get_state(adapter) == STATE_OUT)
			{
				if (value & IRQ)
				{
					/*
					 * Application is using
					 * interrupts, so we can't
					 * rely on INBs being 
					 * used to check the
					 * printer status.
					 */
					set_state(adapter, STATE_READY);
					notbusy_check(adapter);
				}
			}

#if defined(NTVDM)
			else if (low_high(old_control, value, IRQ) &&
				 get_state(adapter) == STATE_DONGLE) {

				host_set_lpt_direct_access(adapter, FALSE);
				set_state(adapter, STATE_READY);
			}

#endif

#ifndef	PROD
			if (old_control & IRQ)
				note_trace1(PRINTER_VERBOSE, "Warning: LPT%d is being interrupt driven\n",
					number_for_adapter(adapter));
#endif
			break;
		}
		break;
	default:	
		note_trace1(PRINTER_VERBOSE, "<poutb() - unknown state %x>",
		            get_state(adapter));
		break;
	}
}

void printer_status_changed IFN1(int,adapter)
{
	note_trace1(PRINTER_VERBOSE, "<printer_status_changed() adapter %d>",
	            adapter);

	/* check whether the printer has just changed state to NOTBUSY */
	notbusy_check(adapter);
}

/*
 * ============================================================================
 * Internal functions 
 * ============================================================================
 */

static void notbusy_check IFN1(int,adapter)
{
	/*
	 *	This function is used to detect when the printer
	 *	state transition to NOTBUSY occurs.
	 *
	 *	If the parallel port is being polled, the port
	 *	emulation will stop this transition occurring
	 *	until the application has detected the ACK
	 *	pulse. notbusy_check() is then called each time the
	 *	port status is read using the INB; when the host
	 *	says the printer is HOST_LPT_BUSY, the port status
	 *	returns to the NOTBUSY state.
	 *
	 *	If the parallel port is interrupt driven, we cannot
	 *	rely on the application using the INB: so we first
	 *	check the host printer status immediately after
	 *	outputting the character. If the host printer isn't
	 *	HOST_LPT_BUSY, then we interrupt immediately;
	 *	otherwise, we rely on the printer_status_changed()
	 *	call to notify us of when HOST_LPT_BUSY is cleared.
	 */

	/* <chrisP 11Sep91> allow not busy at leading edge of ack pulse too */
	if (	 (get_state(adapter) == STATE_READY ||
#if defined(NTVDM)
		  get_state(adapter) == STATE_DATA ||
#endif
		  get_state(adapter) == STATE_OUTA)
	     &&	!(get_status(adapter) & NOTBUSY)
	     &&	!(host_lpt_status(adapter) & HOST_LPT_BUSY))
	{
#if defined(NTVDM) && !defined(MONITOR)
		set_high(status_reg[adapter], NOTBUSY);
#else /* NTVDM && !MONITOR */
		set_status(adapter, get_status(adapter) | NOTBUSY);
#endif /* NTVDM && !MONITOR */

#ifndef	PROD
		if (io_verbose & PRINTER_VERBOSE)
		    fprintf(trace_file, "<printer notbusy_check() - adapter %d changed to NOTBUSY>\n", adapter);
#endif

		if (get_control(adapter) & IRQ)
                {
			ica_hw_interrupt(0, CPU_PRINTER_INT, 1);
		}
	}
}
#ifdef SEGMENTATION
/*
 * The following #include specifies the code segment into which this
 * module will by placed by the MPW C compiler on the Mac II running
 * MultiFinder.
 */
#include "SOFTPC_INIT.seg"
#endif

/*
** Initialise the printer port required.
*/
void printer_init IFN1(int,adapter)
{
	io_addr i;

	io_define_inb( lpt_adapter[adapter], printer_inb );
	io_define_outb( lpt_adapter[adapter], printer_outb );
	for(i = port_start[adapter]; i < port_end[adapter]; i++)
		io_connect_port(i,lpt_adapter[adapter],IO_READ_WRITE);

#if defined(NTVDM) && defined(MONITOR)
	/*
	 * If we know the addresses of the 16-bit variables write directly
	 * to them, otherwise save the value until we do.
	 */
	if (intel_setup)
	{
	    set_status(adapter, 0xDF);
	    set_control(adapter, 0xEC);
	}
	else
#endif /* NTVDM && MONITOR */
	{
	    control_reg[adapter] = 0xEC;
	    status_reg[adapter] = 0xDF;
	}
        output_reg[adapter] = 0xAA;

	/*
	 * The call to host_print_doc has been removed since it is
	 * sensible to distinguish between a hard flush (on ctl-alt-del)
	 * or menu reset and a soft flush under user control or at end
	 * of PC application. The calls to host_lpt_close() followed
	 * by host_lpt_open() should already cause a flush to occur,
	 * so no functionality is lost. The first time printer_init is
	 * called host_lpt_close() is not called, but this cannot
	 * matter since host_print_doc() can only be a no-op.
	 */
	/* host_print_doc(adapter); */
	host_print_auto_feed(adapter, FALSE);

#if defined(NTVDM) && defined(MONITOR)
	if (intel_setup)
	    set_state(adapter, STATE_READY);
	else
#endif /* NTVDM && MONITOR */
	    state[adapter] = STATE_READY;

} /* end of printer_init() */

#if defined(NTVDM) && defined(MONITOR)
/*
** Store 16-bit address of status table and fill it with current values.
*/
#ifdef ANSI
void printer_setup_table(sys_addr table_addr)
#else /* ANSI */
void printer_setup_table(table_addr)
sys_addr table_addr;
#endif /* ANSI */
{
    int i;
    sys_addr lp16BufSize;
    unsigned int cbBuf;
    word    lptBasePortAddr[NUM_PARALLEL_PORTS];

    if (!intel_setup)
    {

	/*
	 * Store the addresses of the tables resident in 16-bit code. These
	 * are:
	 *	status register		(NUM_PARALLEL_PORTS bytes)
	 *	state register		(NUM_PARALLEL_PORTS bytes)
	 *	control register	(NUM_PARALLEL_PORTS bytes)
	 *	host_lpt_status		(NUM_PARALLEL_PORTS bytes)
	 *
	 * Then transfer any values which have already been set up into the
	 * variables. This is in case printer_init has been called prior to
	 * this function.
	 */
	status_addr = table_addr;
	state_addr = table_addr + NUM_PARALLEL_PORTS;
        control_addr = table_addr + 2 * NUM_PARALLEL_PORTS;
	for (i = 0; i < NUM_PARALLEL_PORTS; i++)
	{
	    set_status(i, status_reg[i]);
	    set_state(i, state[i]);
	    set_control(i, control_reg[i]);
	    lptBasePortAddr[i] = port_start[i];
	}

	/* Let host know where host_lpt_status is stored in 16-bit code. */
	host_printer_setup_table(table_addr, NUM_PARALLEL_PORTS, lptBasePortAddr);
/* sudeepb 24-Jan-1993 for printing performance for x86 */
        lp16BufSize = table_addr + 4 * NUM_PARALLEL_PORTS;
        cbBuf = (sas_w_at_no_check(lp16BufSize));
        lp16BitPrtBuf = table_addr + (4 * NUM_PARALLEL_PORTS) + 2;
        lp16BitPrtId  = lp16BitPrtBuf + cbBuf;
        lp16BitPrtCount = lp16BitPrtId + 1;
        lp16BitPrtBusy =  lp16BitPrtCount + 2;
	intel_setup = TRUE;
    }
}
#endif /* NTVDM && MONITOR */

#endif 

#ifdef NTVDM
void printer_is_being_closed(int adapter)
{

#if defined(MONITOR)
	set_state(adapter, STATE_READY);
#else
	state[adapter] = STATE_READY;

#endif
}

#endif


#ifdef PS_FLUSHING
/*(
=========================== printer_psflush_change ============================
PURPOSE:
	Handle change of PostScript flush configuration option for a printer
	port.
INPUT:
	hostID - Configuration item I.D.
	apply - TRUE if change to be applied
OUTPUT:
	None
ALGORITHM:
	If PostScript flushing is being enabled then;
		set the PostScript flush enable flag for the port;
		disable autoflush for the port;
	else;
		reset the PostScript flush enable flag for the port;
		enable autoflush for the port;
===============================================================================
)*/

GLOBAL void printer_psflush_change IFN2(
    IU8, hostID,
    IBOOL, apply
) {
    IS32 adapter = hostID - C_LPT1_PSFLUSH;

    assert1(adapter < NUM_PARALLEL_PORTS,"Bad hostID %d",hostID);

    if ( apply )
        if ( psFlushEnabled[adapter] = (IBOOL)config_inquire(hostID,NULL) )
            host_lpt_disable_autoflush(adapter);
        else
            host_lpt_enable_autoflush(adapter);
}
#endif	/* PS_FLUSHING */