[net.periphs] DMA LP-11 or Centronics interface

dan@rna.UUCP (12/18/84)

Hi,
	As a DMA version of the LP-11, you could try the parallel
output of a DMF-32. If you don't want an entire DMF-32, I believe
ABLE makes just the parallel output function.
	I also recall other third party controller manufacturers that
make DMA line printer interfaces. Try MDB or DATASYSTEMS (Wespercorp).

					Dan

kahrs@alice.UUCP (Mark Kahrs) (12/28/84)

OK all you informed people out there; does anyone know of a DMA unibus
LP-11/Centronics board. The DEC M7258 card
is PIO and we'd like to pump them bits out a bit (sic) faster.

Thanks for any replies,

Mark.

rpw3@redwood.UUCP (Rob Warnock) (12/29/84)

+---------------
| OK all you informed people out there; does anyone know of a DMA unibus
| LP-11/Centronics board. The DEC M7258 card
| is PIO and we'd like to pump them bits out a bit (sic) faster.
| 
| Thanks for any replies,
| Mark.
+---------------

I am posting this instead of mailing, 'cause I think it's of general interest...

Hey, folks! If you're talking about a line printer (and not some other
strange thingy that you might be using a Centronics interface for), going
to DMA will not buy you much (if anything!) compared to a properly coded
PIO-style driver, since you still have to examine/touch/maybe-modify each
character anyway (to handle tabs, control characters, underlining, auto-FF,
etc., -- anything the printer doesn't handle for you).

If your PIO-style driver is written properly (many aren't), it will be doing
all of that stuff while the printer is taking the preceeding character, and
the driver will LOOP at interrupt level as long as the printer continues to
take chars at full speed.  But the real key is not when you do the processing
(sys-call level vs. interrupt level), but how many interrupts you take per
line printed. With EITHER a DMA device or a properly written PIO driver, that
number should be "one" (1) per line, NOT one per character.

Most printers will gobble a whole line's worth of chars as fast as you can
pump them, putting them into an internal buffer until it hits some kind of
action character (line-feed, carriage-return, form feed, etc.). Then it will
go away and not bother you for a whole line's worth of print time.  Because
of this, you DON'T want to dismiss the interrupt on each character, but on
each LINE.

The way you do this (without knowing or caring what the printer's actions
chars are), is to BUSY WAIT (yes, you heard me) or POLL on the "ready" line
from the printer just long enough to see if the printer is going to take
another character from you during "this line". If the busy-wait loop expires
(and for most printers we are talking a SMALL wait, say 5-20 microseconds --
MUCH less than dismissing the interrupt and coming back in), then and only
then do you dismiss the interrupt.

In fact, the per-character overhead of using the PIO interface is not much
(if any) more than the overhead of filling the DMA buffer!

May I suggest as an experiment (before spending money on extra hardware),
that you re-write your driver AS IF you had a DMA interface (buffer and all),
and code the interrupt handler to stuff the printer buffer in a tight loop
as indicated above. I have gotten burst transfer rates on a VAX-11/780 of
over 50Kbytes/sec for such drivers. (Hint: DON'T use the c-list stuff.)

Sample code fragment for one way to do the busy-wait poll for a "soft-DMA"
driver:


lprint(){
	/* ... here with buffer addr/count set up... */

	while (count && (   (lpr->status & LPR_DONE)
			 || (lpr->status & LPR_DONE)	/* as many of these */
			 || (lpr->status & LPR_DONE)    /* as it takes for  */
			 || (lpr->status & LPR_DONE)	/* your printer to  */
			 || (lpr->status & LPR_DONE)	/* eat one character*/
			 || (lpr->status & LPR_DONE)	/* in burst mode,   */
			 || (lpr->status & LPR_DONE)	/* typically 10us.   */
			  )){
		lpr->data = *buf++;
		--count;
		}
	if (count == 0){
		lpr->status &= ~LPR_ENABLE;	/* no more data --
						 * turn off interrupt enable
						 */
		wakeup(&lpr);			/* maybe the process has more */
		}
	return;			/* dismiss interrupt */
}

That's not meant to be any great coding example... it's just off the top
of my head. (It assumes you are NOT doing the formatting on the fly, which
you should, albeit at process level and not at interrupt level.)

You can also use a

	while (count && waitfordone())

where "waitfordone" is something like:

waitfordone(){
	for(i = LPR_MAGIC; i > 0; --i)		/* LPR_MAGIC is typically 10 */
		if(lpr->status & LPR_DONE)
			return 1;
	return 0;
}

But in most cases, the in-line code is faster, since the very first test
often succeeds. If you're really skeptical about this approach, you can put
some metering code in "waitfordone" to measure how many loops you take for
each time you enter, but be warned that the measurement code will eat the
majority of the overhead! The typical number of loops is zero, that is, the
"done" flag is already true on entry (plus each "line" will cost one set of
LPR_MAGIC loops when the printer goes "not ready").

Finally, if your current driver already employs all of these techniques,
and you still aren't satisfied, I dare say a DMA controller isn't going
to buy you anything. Good luck.


Rob Warnock
Systems Architecture Consultant

UUCP:	{ihnp4,ucbvax!dual}!fortune!redwood!rpw3
DDD:	(415)572-2607
USPS:	510 Trinidad Lane, Foster City, CA  94404