22 Character Oriented Special Files

Character oriented peripheral devices are relatively slow (\({\lt}\) 1000 charaeters per second) and involve character by character transmission of variable length, usually short, records.

A device handler (as its name suggests) is the software part of the interface between a device and the general system. In general, the device handler is the only part of the software which recognises the idiosyncrasies of a particular device.

As far as possible or reasonable, a single device driver is written to serve many separate devices of similar types, and, where appropriate, several such devices simultaneously. The group of “interactive terminals” (with keyboard input and a serial printer or visual display output) can just be coerced with difficulty into a single device driver, as the reader may judge during his perusal of the file “tty.c”.

The standard UNIX device handlers for character devices make use of the procedures “putc” and “getc” which store and retrieve characters into and from a standard buffer pool. This will be described in more detail in Chapter Twenty-Three.

The “PDP11 Peripherals Handbook” should be consulted for more complete information on the device controller hardware and the devices themselves.

22.1 LP11 Line Printer Driver

This driver is to be found in the file “lp.c” (Sheets 88, 89). Much of the complexity of this driver is contained in the proeedure “lpcanon” (8879). This procedure is involved in the proper handling of special characters and this is a separate issue from the one we wish to study first.

Initially one may ignore “lpcanon” by assuming that all calls upon it (lines 8859, 8865, 8875) are simply replaced by similar calls upon “lpoutput” (8986). “lpcanon” acts as a “final filter” for characters going to the line printer: handling code conversions, special format characters, etc.

22.2 lpopen (8850)

When a line printer file is opened, the normal calling sequence is followed:

“open” (5774) calls “open1”, which (5832) calls “openi”, which (6716) calls, in the case of a character special file, “cdevsw[..].d_open”. In the case of the line printer, this latter translates (4675) to “lpopen”.

8853:

Take the error exit if either another line printer file is already open, or if the line printer is not ready (e.g the power is off, or there is no paper, or the printer drum gate is open, or the temperature is too high, or the operator has switched the printer off-line.)

8857:

Set the “lp11.flag” to indicate that the file is open, the printer has a “form feed” capability and lines are to be indented by eight characters.

22.3 Notes

(a)

“lp11” is a seven word structure defined beginning at line 8829. The first three words of the structure in fact constitute a structure of type “clist” (7908). Only the first element is explicitly manipulated in “lp.c”. The next two are used implicitly by “putc” and “getc”.

(b)

“flag” is the fourth element of the structure. The remaining three elements are

“mcc”

maximum character count

“ccc”

current character count

“mlc”

maximum line count

(c)

The line printer controller has two registers on the UNIBUS.

Line Printer Status Register (“lpsr”)

bit 15

Set when an error condition exists (see above);

bit 7 “DONE”

Set when the printer controller is ready to receive the next character;

bit 6 “IENABLE”

Set to allow “DONE” or “Error” to cause an interrupt;

Line Printer Data Buffer Register (“lpbuf”)

Bits 6 through 0 hold the seven bit ASCII code for the character to be printed. This register is “write only”.

8858:

Set the “enable interrupts” bit in the line printer status register;

8859:

Send a “form feed” (or “new page”) character to the printer, to ensure that characters which follow will start on a new page. (As already noted above, at this stage we are ignoring “lpcanon” and assuming line 8859 to be simply “lpoutput (FORM)”. “lpcanon” does things like suppressing all but the first “form feed” in a string of “form feed”s and “new line”s, to avoid wasting paper.);

22.4 lpoutput (8986)

This procedure is called with a character to be printed, as a parameter.

8988:

“lp11.cc” is a count of the number of characters waiting to be sent to the line printer. If this is already large enough (“LPHWAT”, 8819), “sleep” for a while (so as not to flood the character buffer pool);

8990:

Call “putc” (0967) to store the character in a safe place. (The function of “putc” and its companion “getc” is a major topic to be discussed in Chapter Twenty Three.) It should be noted that no check is made that “putc” was successful in storing the character. (There may have been no space in the character buffers.) In practice there seems to be no real problem here, but one can wonder.

8991:

Raise the processor priority sufficiently to inhibit the interrupts from the line printer, call “lpstart” and then drop the priority again.

22.5 lpstart (8967)

While the line printer is ready, and while there are still characters stored away in the “safe place”, keep sending characters to the printer controller.

The presumption is that while the controller is building up a set of characters for a complete line, the “DONE” bit will reset faster than the CP can feed characters to the controller.

However once a print cycle has been initiated, the “DONE” bit will not be reset again for a period of the order of 100 milliseconds (depending on the speed of the printer).

Note that during this series of data transfers, interrupts will be inhiblted and so “lpint” will not be getting into the act whenever the “DONE” bit is set, except possibly once at the very end when the processor priority is reduced again.

22.6 lpinit(8976)

This procedure is called to handle interrupts from the line printer. As mentioned above, most potential interrupts are ignored by the processor. Those interrupts which are accepted by the CP will be associated with either

(a)

completion of a print cycle; or

(b)

the printer going ready after a period during which the “Error” bit was set; or

(c)

the last transfer in a series of character transfers;

8980:

Start transferring characters into the printer buffer again;

8981:

Wakeup the process waiting to feed characters to the printer if the number of characters waiting to be sent is either zero or exactly “LPLWAT” (8818).

This latter condition is somewhat puzzling in that it will only occasionally be satisfied. The intention surely is “if the number of characters in the list is getting low, start refilling”. However if “lpstart” carries out a series of transfers without interruption (at least by “lpint”) the number of characters could go from a value greater than “LPLWAT” to one less than this without this test ever being made. Accordingly the waiting process will not be awakened until the list is completely empty. The result could be frequently to delay the initiation of the next print cycle, and hence to allow the printer to run below its rated capacity.

One solution to this problem is to change entirely the buffering strategy for line printers. A less drastic change would involve inventing a new flag, “lp11.wflag” say, replacing lines 8981, 8982 by something like

  if (lp11.cc <= LPLWAT && lp11.wflag)
  {       wakeup (&lp11);
          lp11.wflag = 0
  }

and replacing line 8989 by

  {       lp11.wflag++;
          sleep(&lp11, LPRI);
  }

22.7 lpwrite (8870)

This is the procedure which is invoked as a result of the write system call:

“write” (5722) calls “rdwr”, which (5755) calls “writei”, which (6287) calls “cdevsw[..].d write”, which translates (4675) to “lpwrite”.

“lpwrite” takes the non-null characters of a null terminated string recorded in the user area, and passes them to “lpoutput” (via “lpcanon”) one at a time.

The list of procedure calls which leads to the invocation of this procedure is similar to that for “lpopen”. A “form feed” character is output to clear the current page, and the “open” flag is reset.

22.8 Discussion

“lpwrite” is called one or more times to send a string of characters to the printer. In turn it calls “lpcanon” which calls “lpoutput”. If at any point too many characters are stored away, the procss will “sleep” in “lpoutput”. Sooner or later “lpoutput” will continue, will store the character in a buffer area, and will then call “lpstart” to send, if possible, a string of characters to the printer controller.

“lpstart” is called both when more characters are available to be sent, and when an interrupt from the printer is taken.

The majority of calls on “lpstart” will in fact achieve nothing. Occasionally (usually when the printer has just completed a print cycle) “lpstart” will be able to send a whole string of characters to the printer controller.

22.9 lpcanon (8879)

This procedure interprets characters being sent to the line printer and make various modifications, insertions and deletions. It thus functions as a filter.

8884:

The section of code from here to line 8913 is concerned with character translation when the full 96 character set is not available, and a 64 character set is in use.

Since the capabilities of a printer do not usually change with time, the defined variable “CAP” (8840) must be set once and for all (at a particular installation).

The run-time test on (lp11.flag & CAP) could be replaced by a compile-time test on (CAP) and if the compiler has its “druthers”, if CAP turns out to be zero, the whole section of code to line 8913 could be compiled down to nothing.

The present code could be said to plan ahead for a situation where an installation may have two or more printers of different types. Even so there is a basic inconsistency here in the use of “CAP”, “IND” and “EJECT” on the one hand, and “EJLINE” and “MAXCOL” on the other. In fact since forms of different sizes are not uncommonly used on a single printer, the last two should not be constants at all, but should be dynamically settable.

8885:

Lower case alphabetics are translated by the addition of a constant, which is conveniently defined as “’A’ – ’a”’;

8887:

Certain of the remaining characters are special characters which are printed as a similar character with an overprinted minus sign, e.g. “{” (8889) is printed as “{-”;

8909:

The “similiar” character is output via a recursive call on “lpcanon”, which will increment “lp11.ccc” by one as a side effect;

8910:

Decrement the current character count (for the same effect as a “back space” character) and ...

8911:

Prepare to output a minus sign;

8915:

The “switch” statement beginning here extends to line 8963. Certain characters involved in vertical and horizontal spacing are given special interpretations with delayed actions;

8917:

For a horizontal tab character, round the current character count up to the next multiple of eight. Do not output any blank characters immediately;

8921:

For a “form feed” or “new line” character, if:

(a) the printer does not have a “page restore” capability; or

(b) the current line is not empty; or

(c) some lines have been completed since the last “form feed” character, then ...

8925:

reset “lp11.mcc” to zero;

8926:

Increment the completed line count;

8927:

Convert a “new line” character to a “form feed” if sufficient lines have been completed on the current page, and the printer has a “form feed” capability;

8929:

Output the character, and if was a “form feed”, reset number of completed lines zero;

Examination of this code will show that:

(a)

Any string of “form feed”s or “new line”s which begins with a “form feed”, will, if sent to a printer with “form feed” capability, be reduced to a single “form feed”;

(b)

A “form feed” character sent to a printer without the “form feed” capability, will cause a new line to be started but will be passed on otherwise without comment.

8934:

For “carriage return”s, and, note, “form feed”s and “new line”s, reset the current character count to zero or eight, depending on “IND”, and return;

8949:

For all other characters ...

8950:

If a string of “backspace”s (real or contrived) and/or “carriage returns” has been received, output a single “carriage return” and reset the maximum character count to zero;

8954:

Provided the count does not exceed the maximum line length, output blank characters to bring the maximum character count to the current character count. (Perhaps these two variables would be more accurately called the “actual character count” and the “logical character count”.);

8959:

Output the actual character.

22.10 For idle readers: A suggestion

It will be observed that backspaces for overprinting or underscoring characters introduce separate print cycles, and where these features are in heavy use, the effective output rate of the printer may be drastically reduced. If this is considered a serious problem, “lpcanon” could be rewritten to ensure that no more than two print cycles are used per line in such cases.

22.11 PC-11 Paper Tape Reader/Punch Driver

This driver is to be found in the file “pc.c” on Sheets 86, 87. It is simpler than the line printer driver in that there is ro routine analogous to “lpcanon”. However it is more complicated in that there is both an input and an output device which can be simultaneously and independently active.

A description of the operation of this device is inciuded in the document “The UNIX I/O System” by D. Ritchie. Certain special features may be noted:

(1)

Only one process may open the file for reading at a time, but there is no limit on the nmber of writers;

(2)

This routine pays a little more attention to error conditions than the line printer driver, but the treatment is still not exhaustive;

(3)

“passc” (8695) knows how many characters are required and returns a negative valie when “enough” is reached;

(4)

“pcclose” is careful to flush out any remaining characters in the input queue if and only if it believes the device was opened for input.