From: fdeck@smiley.helios.nd.edu (francis deck)
Newsgroups: sci.electronics
Subject: Printer Port
Date: 17 Apr 92 21:12:08 GMT
Article-I.D.: news.1992Apr17.211208.29470
Organization: University of Notre Dame, Notre Dame

I got so many requests about my printer port FAQs that I thought
I'd just post them.  They are in the form of two Turbo Pascal
programs:

{
SYSTEM RAM DATA

   Location        Size         Description

   $40:08          4 words      I/O address of up to 4 printer adapters.

   $40:10          1 word       Bits 15-14 = Number of printer adapters.

   $40:78          4 bytes      One byte per adapter.  Time-out values
                                for printers 1 through 4.

PARALLEL PRINTER I/O PORTS

   The i/o address found by checking System RAM location $40:08
   is first of a block of three i/o ports, here numbered A+0
   through A+2.

   Port                    Description

   A+0                     Data output port, bits D0-D7.

   A+1                     Status input port, where:

                           Bit         Mnemonic
                           0-1         Reserved
                           2           IRQ has occurred
                           3           Error
                           4           Select Input
                           5           Paper Out
                           6           Acknowledge
                           7           Busy

   A+2                     Control output port, where:

                           Bit         Mnemonic
                           0           Strobe
                           1           Auto Feed
                           2           Init Printer
                           3           Select
                           4           Enable IRQ
                           5-7         Reserved

PARALLEL PORT PINOUT

   The parallel port is a female DB25 connector, with the
   following pinout:

   Pin                     Mnemonic
   1                       Strobe
   2-9                     D0-D7
   10                      Acknowledge
   11                      Busy
   12                      Paper Out
   13                      Select (input)
   14                      Auto Feed
   15                      Error
   16                      Init Printer
   17                      Select (output)
   18-25                   Ground

COMMENTS

   Printer port outputs and inputs are simple TTL lines.  For
   high-speed applications, output lines may have to be
   terminated with pull-up resistors at the receiving end.

   I understand that some PCs implement the parallel port with
   an 8255 or some such device, allowing reconfiguration of the
   data direction of the port i/o lines.  I don't do this,
   since some PCs, my Zenith included, use unidirectional port
   chips for all the LPT i/o lines.  For greatest
   compatibility, stick to the data directions listed above!

REFERENCES

   System BIOS for IBM PC/XT/AT computers and compatibles; the
   complete guide to ROM-based system software, Phoenix
   Technologies, Ltd, (Reading Mass.: Addison-Wesley Pub. Co.,
   1989)

   Owner's manual for Zenith Z-148 PC.

{======== Turbo Pascal printer port demp LPTDEMO.PAS =======}

program lptdemo;

var
   ina, outa, ctrla: word; {input, output, control port addresses}

const
     nlpt = 1; {printer port number}
     base: word = $40; {base address in BIOS system RAM}
     offs: array[1..3] of word = ($08,$0A,$0C); {offset in system RAM}

begin
     {This initialization is mandatory}

     outa := memw[base:offs[nlpt]];
     ina := outa + 1;
     ctrla := outa + 2;

     {now, let's play with the ports}

     port[outa] := 1; {set D2 line}
     writeln ('Pin 2 should be TTL HIGH');
     readln;

     port[outa] := 0; {reset D2 line}
     writeln ('Pin 2 should be TTL LOW');
     readln;

     port[ctrla] := 1; {set strobe line}
     writeln ('pin 1 should be TTL HIGH');
     readln;

     port[ctrla] := 0;
     writeln ('pin 1 should be TTL LOW');
     readln;

     {endless loop reading Acknowledge line}

     while true do if port[ina] and 64 <> 0
           then writeln ('Pin 10 = HIGH')
           else writeln ('Pin 10 = LOW');

     end.

{-------- Demonstrate IRQ7 handler ---------}

{This program demonstrates the parallel printer interrupt IRQ7, by
 counting transitions on the Acknowledge pin of the printer port.
 I am indebted to Kerry Yackoboski at Univ. of Manitoba for writing
 the interrupt handler--all I did was add the part which turns the
 port's interrupt generator on.

 Francis J. Deck
 fdeck@grumpy.helios.nd.edu
 "Elegance is for tailors!" -- Einstein}

program isrtest;

uses
    crt,
    dos;

const
     pc_imr = $21; {interrupt mask register--which interrupts are
                    active?}

     pc_isr = $20; {interrupt status register--will be used to send
                    an EOI (end of interrupt) command to the 8255
                    interrupt controller}

     irq7 = $0f; {the interrupt number of IRQ7.}
     eoi7 = $60 + 7; {EOI instruction for IRQ7};

var
   imr: byte; {saved value of interrupt mask register}
   count: integer;

{-------- Service routine for IRQ7 --------}

procedure int_7_handler;

interrupt;

begin
     port[pc_imr] := imr; {restore old value of interrupt mask register
                           so subsequent IRQ7's won't happen while this
                           routine is running.}

     count := count + 1;
     writeln ('Interrupt ',count);

     port[pc_isr] := eoi7; {send EOI to 8255 controller.  You gotta send
                           this EOI before re-enabling the interrupt, or
                           a spurious IRQ7 will be generated.}

     port[pc_imr] := port[pc_imr] and $7f; {set mask register again so
                                            IRQ7 can happen.}
     end;

{-------- Mainline --------}

const
     nlpt = 1; {printer port number--1, 2, or 3}
     base: word = $40; {base in memory for printer port info}
     offs: array[1..3] of word = ($08,$0A,$0C); {offsets in RAM for info
                                                 on ports 1, 2, and 3}

var
   ina, outa, ctrla: word; {input, output, control port addresses}

var
   old_int_vec: pointer; {saved value of vector for IRQ7}

begin
     clrscr;
     writeln ('This program demonstrates the use of the parallel printer');
     writeln ('port interrupt IRQ7.  It generates an interrupt every time');
     writeln ('the Acknowledge input of the printer port (pin 10) goes');
     writeln ('from LOW to HIGH.  You can test it at this point by');
     writeln ('repeatedly connecting and disconnecting a jumper wire from');
     writeln ('pin 10 to pin 18 of the port.  The program will terminate');
     writeln ('when you press any key...');
     writeln;
     count := 0;

     port[pc_isr] := eoi7; {send an EOI to clear all pending interrupts}

     getintvec (irq7,old_int_vec); {store away existing value of interrupt
                                  vector}

     setintvec (irq7,@int_7_handler); {now, point IRQ7 to our own little
                                       routine}

     {find out, from BIOS system RAM, where the printer port is...}

     outa := memw[base:offs[nlpt]]; {data output port}
     ina := outa + 1; {handshake input port}
     ctrla := outa + 2; {handshake output port}

     port[ctrla] := 16; {enable the port to generate IRQ7 upon positive-
                         going transition on Acknowledge pin.}

     imr := port[pc_imr]; {save existing value of interrupt mask}

     port[pc_imr] := port[pc_imr] and $7f; {set 7th. bit of IMR to 1}

     repeat until keypressed; {just loop forever, while the user plays
                               with pin 10}

     port[ctrla] := 0; {disable port's IRQ7 generation}

     port[pc_imr] := imr; {restore IMR}

     port[pc_isr] := eoi7; {send EOI, clearing any further interrupts}

     setintvec ($f,old_int_vec); {restore value of interrupt vector}

     end.