Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Admitting Objects to Pascal


SEP89: STRUCTURED PROGRAMMING

I graduated from Lane Technical High School in Chicago in 1970. Three years later they first admitted girls. I had an opportunity to go back seven or eight years after graduation to speak to one of the student groups, and the change stopped me in my tracks.

Girls! What a thought ...

The sense of balance, of progress, or rightness was astounding. The place was no longer permeated by that old testosterone fog. Right there in front of me, gawky boy-nerds were walking down the hall in animated conversation with gawky girl-nerds. It made me wonder why this hadn't been done fifty years earlier, and for a moment I ached to be fourteen again.

Over the past couple of months, both Borland and Microsoft have admitted objects to the Pascal language, and the effect is much the same. It feels right, right in a way that makes you want to turn the clock back to 1972 and start all over again.

Nonetheless, having objects now is sufficient. And I promise you, programming in Pascal will never be the same again.

QuickPascal

Microsoft's QuickPascal has finally shipped, and at $99 list (street price considerably less, if you follow the mail order houses) it now ties with Smalltalk/V as the least expensive DOS OOP product.

QuickPascal's environment is quite slick, with mouse support and a multifile editor its main features. There are a source-level integrated debugger and a very good on-line help system. Users of MS Pascal 5.0 should note that QuickPascal is only very broadly compatible with MS Pascal, but instead is a syntactic clone of Turbo Pascal 5.0. All Turbo Pascal 5.0 code I've tried to compile under QuickPascal has compiled and run correctly, but ... QuickPascal's .EXE file is considerably larger than Turbo Pascal's, and the size of the largest compilable program under Quick is correspondingly smaller.

The two compilers' object-oriented features are different enough to be a nuisance but similar enough to treat together.

Parallel Evolution

Considering that the two products were developed in isolation, Microsoft's QuickPascal and Borland's Turbo Pascal 5.5 have a remarkably similar slant on object orientation. Their differences can be attributed to ancestry: QuickPascal adheres pretty closely to the Apple Object Pascal specification, whereas Turbo Pascal 5.5 draws about equally on Object Pascal and C++. In an interlocking directorate like this, similarities are likely to outnumber differences.

For both products, objects are closely related to records and are defined in just about the same way. The important thing to notice about object definitions is that code and data are defined together, within a named structure. This is how object-oriented Pascal implements encapsulation:

TYPE
  PrintByte  =
    OBJECT
      BV: Byte; { Byte Value }
      FUNCTION
        DontPrint(ShouldI : Byte): Boolean;
      PROCEDURE PrintDecimal;
      PROCEDURE PrintHex;
      PROCEDURE PrintBinary;
      PROCEDURE PrintSymbol;
    END;

The new reserved word OBJECT makes this an object definition. Referencing code and data is done through another record-like convention, dotting. Assuming an object instance named MyByte, you would reference field BV as MyByte.BV, and call the PrintHex method as MyByte. PrintHex.

Object Types with No Class

Adam gave all the animals unique names because when he said, "Hey you with the four legs and a tail!" half of creation ambled over. The naming of names is a critical issue in object-oriented programming, because so many of the concepts are new. Sadly, there are major disparities in how QuickPascal and Turbo Pascal refer to certain concepts.

The worst and most confusing is what to call objects themselves. Just as variable types and variables are not the same things, objects and their types are not the same either. An object type in Turbo Pascal is simply an "object type," and an object variable (what we also call an instance of an object type) is just an "object." QuickPascal uses the term "class" instead of "object type," but the meaning is exactly the same. The term "class" comes from Smalltalk, as I described last month. In this column I'll be using the term "object type" rather than "class" in this column (regardless of which Pascal is under discussion) because it's more descriptive and sounds less mystical/magical.

Creating Objects

Turbo and Quick diverge most sharply in their means of creating objects. Turbo Pascal objects are true to their record-like heritage. You can create an object in the same ways you create a record, either as a static variable in the data segment, or as a dynamic variable on the heap:

     VAR
         { Declaring a static object: }
         MyByte: PrintByte;
         { A pointer to a dynamic object: }
         MyBytePtr: ^PrintByte;

         { Allocating a dynamic object: }
          New(MyBytePtr);

These items could as well be records as objects. The syntax is the same.

QuickPascal implements objects as something truly different. An object instance variable may exist only on the heap; there is no way to create an object in the data segment. Syntactically, object variables sit on the fence half-way between records and pointers. An object is treated like a record when defined, treated like a pointer when created, and then treated like a record when used, with a couple of twists:

     VAR
         { Allocates no space!! }
         QuickByte: PrintByte;

         { Allocates object on heap: }
         New(QuickByte);

         { Reference without caret: }
         QuickByte.BV := 42;

This is supposed to be a sort of short-hand, allowing programmers to dispense with the multitude of caret symbols that would be necessary if objects on the heap really were treated as pointer referents. And while shorthand is convenient, it's hard to read, and I'm afraid this somewhat inconsistent syntax is something (like irregular verbs in Spanish) that you just have to bite the mnemonic bullet and remember.

Once a Quick object is created, it can be referenced just as a Turbo Pascal object can be. The inconsistency returns when you want to reclaim an object's heap space. You pass the object's identifier to Dispose, again as though the object were a pointer:

     Dispose(QuickByte);

Overriding Methods

Creating a child object type that inherits from a parent object type is done identically in both implementations. The name of the parent type is enclosed in parentheses after the child type's OBJECT reserved word:

      TYPE
        PrintByteHP =
          OBJECT(PrintByte)
            PROCEDURE Print Symbol {; OVERRIDE;}
          END;

The differences between the two products emerge again when a method defined in the parent object type is redefined, or overridden, by a new method with the same name. Turbo Pascal simply allows the method to be overridden by redefining it. QuickPascal requires that the overriding method be flagged to the compiler with the OVERRIDE reserved word. I kind of like that: It makes the code easier to comprehend by flagging unmistakably whether a method is brand new in the object hierarchy, or whether it redefines a method farther up the hierarchy tree.

In Search of Self

The one thing that drives me truly berserk about QuickPascal is its Selfishness. Within the body of a method, the object's data fields need to be qualified by the use of a new predefined identifier, Self. Here's a simple QuickPascal method definition belonging to the PrintByte object type:

     PROCEDURE PrintByte.PrintDecimal;

     BEGIN
       Write(LST,Self.BV:3);
     END;

This is peculiar -- traditional Pascal scoping should allow the compiler to recognize a reference to an object's fields as local variables. After all, the method definition begins with the PrintByte. qualifier, and field BV is as much a part of PrintByte as the PrintDecimal method. Most remarkably, using the WITH Self DO structure does not work with methods. (It does work with object data fields, however.) Methods must be qualified by Self itself, directly. This may be a bug or it may be a feature, depending on how you look at it, but every time I look, I see six legs.

Yet another interesting wrinkle here is that Turbo Pascal allows, but does not require, the use of the Self identifier in the same context.

A Simple Object Pascal Example

This column's listings present a simple but useful example of Object Pascal in action. I've given it for both Turbo Pascal 5.5 and QuickPascal, and comparing the two will give you a reasonable feel for the way the two implementations go about things. Listings One and Two are for Turbo Pascal, and Listings Three and Four are for QuickPascal.

The program itself prints an ASCII table, including all 256 character patterns from the smiley faces on up through the box drawing and math characters. The catch in creating such a program is that while many PC-compatible printers can print a smiley face or its brothers, the nature of the command to print such special characters varies all over the map.

The PRINBYTE.PAS (and PRINTBYTQ.PAS for QuickPascal) implements a simple object that is nothing more than a byte value with some methods to display it to the printer in various formats, including decimal, hex, binary, and "literal," that is, in the form of a single character. The value is stored in the PrintByte object's sole data field, BV. The value-added in the PrintByte object lies in the object's methods.

Most ASCII characters can be printed as literal symbols simply by sending them to the printer port. Send a 65 value and you'll get an A on the printer. Send the value 236 and you'll get an infinity symbol. Some characters, however, are control characters, and exist not to be displayed so much as to dictate commands to the printer. These control characters include carriage return (13), line feed (10), and BEL (7). Send a control character to the printer and you won't see any symbol, but instead the printer will index or beep or do something else that involves its mechanism rather than its output.

As you might imagine, some characters do double duty as both control characters and symbols. The control character guise of such characters (typically, values 1-31) takes priority. Send a 7 to the printer, and the printer's beeper will beep. To get a hardcopy of the small circular bullet that is character 7's symbolic guise, you must send a control sequence to the printer. Such a control sequence is often an ESC character (27) followed by the control character, but each printer has its own way of responding to control sequences, and no standard prevails.

Go Print Yourself!

Printing the ASCII chart on any arbitrary printer would mean a program that knew about every printer on the market, which is obviously impossible. The way out is for the PrintByte object to take the low road and refuse to print any control character at all -- and let the user of the PrintByte object extend PrintByte to match the user's specific printer. The example shown uses the HP LaserJet II printer as the specific printer. You can modify (and should, for practice) ASCII.PAS or ASCIIQ.PAS to match the needs of your own printer, whatever it might be.

Extendability of code is one of the major benefits of object-oriented programming. The way is to define a child object of the PrintByte object, and override only the method that prints the symbol to the printer. This is the PrintSymbol method.

ASCII.PAS and ASCIIQ.PAS (for QuickPascal) demonstrates this. A child object type, PrintByteHP, is defined so that it inherits all of PrintByte's data and methods. PrintByteHP changes only what must be changed -- no unnecessary duplication of code is needed. The only change is to the PrintSymbol method, which is redefined in ASCII.PAS and ASCIIQ.PAS. Note the presence of the OVERRIDE reserved word in ASCIIQ.PAS.

The sense of the PrintByteHP object type is that it extends the PrintByte object type by "knowing" how to print control symbols to the LaserJet II printer. (The PrintByte type simply punts by printing a space for any control character.) So you as a programmer don't have to fuss with control sequences. You simply tell a particular PrintByteHP object to go print itself, and shazam! You've got smiley faces all over the place.

Look Ma, No Source!

One of the interesting benefits of this process is that an object can be extended by a programmer without having the source code. I provide you with PRINBYTE.PAS, but all you really need is the object type's definition (basically, the procedure headers and data definitions) and the linkable unit file implementing the object. This can be done to some extent with Pascal units right now, in that you can write a different version of a routine in a unit, and as long as your rewritten routine is linked after the routine it replaces, the replacement routine will be used by all code linked afterwards.

The real, real slick wrinkle (not shown in this example) is the way that the original object can make use of its child objects that did not exist when the parent object was compiled. (Think about that for a moment.) It's part of the mind-warping notion of polymorphism, and I'll return with some concrete examples in a future column.

In the meantime, I powerfully recommend that you pick up either Turbo Pascal 5.5 or QuickPascal and start practicing your objects now.

Nothing more important has happened to programming languages since procedures parted the in-line code chaos in the seventies.

Blows Against the Empire

As my sixties Day-Glo Desiderata poster used to say, "No doubt the universe is unfolding as it should." Those ubiquitous 370-class mainframes (as distinct from what are now called -- gakkh! -- supercomputers) are evolving into their appropriate ecological niche as relatively dumb but cavernous file servers under the control of personal computers out where the real work gets done.

Not surprisingly, your average MIS/DP department doesn't see things this way. (Just as T. Rex didn't say, "Hi, Boss!" to the shrew with egg yolk on his whiskers.) Ask DP for a mainframe application, and the schedule will be something like six months plus a fudge factor of three months to two years, depending on how recently the MIS manager got a promotion. I can smile now (having been out of Xerox MIS for six years) but the DP backlog is still a serious problem out in pinstripe land.

And I don't normally have much truck with teenage mutant Ninja languages, but one has recently come to light that has landed some serious blows against the DP empire. It's called REXX, and if you're in the position of living in the walls of T. Rex's nest, it's a great way of making omelets out of the big guy.

REXX is a structured language developed originally for 370-class mainframes by a delightful IBM curmudgeon named Mike Cowlishaw. It was designed as a replacement for the mainframe batch processor cancer they call JCL, and has therefore been called a "super batch language." REXX can be used as a batch language, but it's a real programming language in its own right with all the body parts a proper language ought to have. (Including GOTO -- now that's authentic!) It's most similar to Basic in that it's usually interpreted and has that inescapable line-oriented mainframe flavor about it.

REXX is important to this column: It has a crisp definition published by Cowlishaw that has enabled identical implementations on both mainframes and on PCs under DOS. This means that you can build a REXX application in the safety and comfort of your cubicle, and upload it to your mainframe users when T. Rex isn't looking. Shazam! You've cut the development time for a simple readafile/writearecord DP application from ten months to ten hours.

There are numerous mainframe implementations of REXX, and I'm told one can be had for almost any of the innumerable different 370 operating environments. The only commercial implementation for DOS is the one I tested and will describe later, Personal REXX from The Mansfield Software Group.

With Feet in Two Worlds

I've not had the honor of sweating blood over the VM/CMS mainframe operating system, so I can't tell you how REXX looks from its perspective. From DOS, however, REXX is very much a language with feet in two worlds. On one hand, as I've said, REXX is a perfectly ordinary and reasonable interpreted language. All the familiar program structures are there and implemented quite nicely. On the other hand, when REXX encounters a clause it does not recognize as part of a REXX statement, it automatically passes that clause to DOS to execute. This ability is what makes REXX a super-batch language.

Mansfield allows REXX to be loaded from disk as a transient program that remains in memory only while the current REXX program is being interpreted, or REXX can be loaded as a TSR that remains in memory until unloaded or until reboot. The TSR REXX takes 150K of RAM, which prevents its use with many of the larger business applications. My hunch is that memory management is the real kicker in making REXX pay off under DOS. You'll have to experiment to get a feel for what's possible and what's impossible.

REXX as a batch processor doesn't interest me nearly as much as REXX as a mainframe bridge, and as a mainframe bridge it won't be managing DOS applications.

Language Highlights

Space is short, so I won't rehash how REXX implements ordinary structured elements like DO loops, IF statements, and so on. They're all there, and resemble Pascal's strongly. REXX does contain some remarkable features that I would like to call attention to. For the full story on REXX, get Cowlishaw's "white book" on the language, which defines it thoroughly and is one of the most readable such white books as well: The REXX Language, M.F. Cowlishaw, Prentice-Hall, 1985. (Note that The Mansfield Software Group bundles Cowlishaw's book with their Personal REXX product, so if you buy the book and then buy Personal REXX, you'll be buying the book twice.)

The PARSE instruction provides a generalized string parser that dismantles a string value into several separate variables according to a template. A good example is entry of date values:

   SAY "Enter the date as mm/dd/yy"
   PARSE PULL mo'/'da'/'yr

Here, the user is told what string data to enter with the SAY instruction. The PULL instruction is roughly analogous to Readln, in that it pulls data in from an input queue. The template is mo '/' da '/' yr, indicating that the string data accepted through PULL is to be separated out into three string variables, with slash symbols acting as separators.

This is about as simple as a useful template gets, and REXX's parser is capable of a lot more. Patterns to be matched may be stored in variables and may be looked for in specific positions in the string if desired. Parsers are elemental code machines that should be a part of every language, but in fact are part of almost none.

Perhaps the most intriguing REXX construct is INTERPRET, which allows a REXX program to build a mini-REXX program on the fly and then interpret it. Interpreted Prolog implementations can do something like this, but none of the traditional languages (Pascal, C, Modula-2, Basic, Fortran, ADA) have anything that comes even close. In his book, Cowlishaw does little with INTERPRET but uses it to write a simple formula calculator emulator, but in fact this way of treating code as data is used in a lot of AI research -- if any readers have used REXX in this fashion, I'd like to hear about it.

That Old Mainframe Goblin

While REXX the language is easy to program in and relatively easy to read, REXX the language processor is haunted by that old mainframe goblin, complexity. There are any number of ways to feed statements to REXX, and lots of niggling little gotchas that must be digested and understood to make the program operate to its best advantage. Many of these seem unnecessary, like the separate TSR that does nothing but grab interrupt vector 60. Grabbing vectors is not time consuming and should be done in an initialization section/exit procedure manner inside the language processor, as with Turbo Pascal. A much simpler Personal REXX could be created if The Mansfield Software Group decided to go the distance.

On the other hand, if you can (or must) handle mainframes, you can handle REXX. Give it a shot.

Sometimes a Great Notion...

... never quite seems to set the world on fire as it should. I am personally fond of technical anthologies; that is, books to which several experts on a subject have each contributed a chapter. Such books are rare, and publishers for some reason don't like them.

So you'll have to forgive me for recommending one such book here, even though I was a (relatively minor) contributor. It's from a small publisher and has had little distribution, but get it if you can: Turbo Pascal Innovations, edited by Judie Overbeek and Rick Gessner. If you can't find it at a bookstore (likely) the book can be ordered directly from the publisher.

The book contains eight chapters in all, each on a different subject by a different author. The topics include (among others) DOS time and date topics, directory management, high-precision code timing, graphics, and procedural types. I don't have time to describe them all, but there are two absolutely essential chapters: One by Rick Gessner on user-interface design, and another by Lane Ferris describing a unit allowing you to write TSR programs that multitask with ordinary DOS programs.

Products Mentioned

Turbo Pascal 5.5 Borland International 1800 Green Hills Road Scotts Valley, CA 95066 408-438-8400 Turbo Pascal $149 Turbo Pascal Professional (Includes Turbo Debugger & Turbo Assembler) $250

Turbo Pascal Innovations Ed. by Judie Overbeek & Rick Gessner Rockland Publishing, Inc., 1989 1706 Bison Drive Kalispell, MT 59901 406-756-9079 ISBN 0-939621-01-0 $32.95 (includes listings diskette)

QuickPascal 1.0 Microsoft 16011 NE 36th Way Redmond, WA 98073 206-882-8088 $99.00

The REXX Language M.F. Cowlishaw Prentice-Hall, 1985 ISBN 0-13-780685-X $19.95

Personal REXX The Mansfield Software Group PO Box 532 Storrs, CT 06268 203-429-8402 DOS version $150.00 OS/2 version (which includes DOS version) $175.00

For all that user-interface design is touted as the key to improved user productivity, there's been little published on just how such interface code is designed. Gessner does an excellent job of providing some guidelines for user interface components, along with sample implementations of various line editors and menus. The code is solid and easy to read, and the approach demonstrates a sensitivity to human needs that rarely turns up in the programmer press.

The real blockbuster in this book, however, is Lane Ferris's multitasking TSR unit. Code like this doesn't happen by very often, and when it does, it is rarely opened up and dissected with such clarity. Lane's unit uses a round-robin time-slicing system to allow a TSR to pre-emptively execute multiple tasks while the DOS foreground program multitasks on equal terms. This means you can put something as simple as a real-time clock in the corner of your screen, or something as complex as a pop-up calculator that doesn't freeze your telecommunications session in the foreground. Very heavy stuff, and beautifully implemented.

Best of all, it's not written in C.

The reason books like this are valuable is that no one person is equally good at everything, and by presenting the best that several intelligent persons have to offer, the book becomes far more diverse and useful than a book any single person could create. In that respect it's like a very thick magazine with no ads. Nothing quite like it ... even though there should be.

When the Good Die Young

I was at the American Booksellers Association convention in Washington, D.C. when I got the news that Kent Porter had died in the saddle, doing what both he and I do every day: Writing and editing. I met Kent when he submitted a raft of articles to TURBO TECHNIX, and acted as his editor there for an outstanding series of projects, including his popular "Mouse Mysteries" series. Later, when TURBO TECHNIX became history, the partitions swapped a little and Kent became my editor here at DDJ. We understood one another in the way that only two editors -- who have edited one another's work -- can.

Kent's mission and mine were the same: To wipe the fuzz away from your windows onto the programming craft, and in doing so spread the magic around to the uninitiated. Magazines and books don't just happen -- behind each is a strong mind and a strong hand; writer and editor working together in a common cause. It takes some skill to see the work of the editor's hand sometimes -- unless you're another editor.

Good night, good friend. As to your mission: Stet.

It will stand.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063; or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue and format (MS-DOS, Macintosh, Kaypro).

_STRUCTURED PROGRAMMING COLUMN_ by Jeff Duntemann

[LISTING ONE]

<a name="01ec_0016">

{--------------------------------------------------------------}
{                          PrinByte                            }
{                                                              }
{    Byte-value print object for object extendability demo     }
{                                                              }
{                                    by Jeff Duntemann         }
{                                    Turbo Pascal V5.5         }
{                                    Last modified 5/11/89     }
{--------------------------------------------------------------}


UNIT PrinByte;

INTERFACE

USES Printer;


{--------------------------------------------------------------}
{ The PrintByte object "knows" how to print its byte value in  }
{ four different formats:  As decimal quantity, hex quantity,  }
{ binary quantity, and extended ASCII symbol.  It takes the    }
{ cautious path in printing symbols, as prints spaces for all  }
{ of the "control" characters 0-31, "rubout" ($7F), and char   }
{ $FF.  If your printer has some means of printing smiley      }
{ faces and all that other folderol in the low 32 characters,  }
{ override the PrintSymbol method with a new method that uses  }
{ whatever mechanism your printers offers to print those       }
{ characters "spaced out" by this implementation of the        }
{ PrintSymbol method.                                          }
{--------------------------------------------------------------}

TYPE
  PrintByte   = OBJECT
                  BV : Byte;     { Byte value }
                  FUNCTION  DontPrint(ShouldI : Byte) : Boolean;
                  PROCEDURE PrintDecimal;
                  PROCEDURE PrintHex;
                  PROCEDURE PrintBinary;
                  PROCEDURE PrintSymbol;
                END;


IMPLEMENTATION


{ Returns True for any code that CANNOT be printed verbatim }

FUNCTION PrintByte.DontPrint(ShouldI : Byte) : Boolean;

CONST
  NotThese = [0..31,127,255];  { Unprintable codes }

BEGIN
  IF ShouldI IN NotThese THEN DontPrint := True
    ELSE DontPrint := False;
END;



PROCEDURE PrintByte.PrintDecimal;

BEGIN
  Write(LST,BV:3);
END;



PROCEDURE PrintByte.PrintHex;

CONST
  HexDigits : ARRAY[0..15] OF Char = '0123456789ABCDEF';

BEGIN
  Write(LST,HexDigits[BV SHR 4],HexDigits[BV AND $0F]);
END;



PROCEDURE PrintByte.PrintBinary;

VAR
  Index : Integer;

BEGIN
  FOR Index := 7 DOWNTO 0 DO
    IF Odd(BV SHR Index) THEN Write(LST,'1')
      ELSE Write(LST,'0');
END;



PROCEDURE PrintByte.PrintSymbol;

BEGIN
  IF DontPrint(BV) THEN
    Write(LST,' ')
  ELSE
    Write(LST,Chr(BV));
END;


END.






<a name="01ec_0017"><a name="01ec_0017">
<a name="01ec_0018">
[LISTING TWO]
<a name="01ec_0018">

{--------------------------------------------------------------}
{                          ASCIIChart                          }
{                                                              }
{   Object Extendability demo program: Prints an ASCII chart   }
{                                                              }
{   Note: This program contains control codes specific to      }
{         the HP Laserjet Series II Printer.  Use with         }
{         other printers may be hazardous to your aesthetic    }
{         sensibilities.                                       }
{                                                              }
{                                    Jeff Duntemann            }
{                                    Turbo Pascal V5.5         }
{                                    Last modified 6/8/89      }
{--------------------------------------------------------------}



{--------------------------------------------------------------}
{ Run this on an HP Laserjet Series II, and you'll get an      }
{ ASCII chart including all the weird characters for which the }
{ Series II has characters in its built-in fonts.  If you have }
{ some other printer, you need only modify the PrintSymbol     }
{ method to use whatever mechanism your printer has to print   }
{ the weird characters.  By modifying PrintSymbol you are      }
{ extending the object PrintByte defined in unit PRINBYTE.PAS, }
{ WITHOUT needing full source code to PRINBYTE.PAS.            }
{--------------------------------------------------------------}



PROGRAM ASCIIChart;


USES Printer,    { Standard Borland unit }
     PrinByte;   { PRINBYTE.PAS from DDJ for September 1989 }


CONST
  Title    = 'THE EXTENDED ASCII CODE AND SYMBOL SET';
  Header   = '   Dec  Hex    Binary  Symbol     Dec  Hex    Binary  Symbol';
  BarChar  = Chr(205);
  ColChar  = Chr(177);
  FormFeed = Chr(12);


TYPE

{---------------------------------------------------------------}
{ This is a child object of PrintByte, defined in separate file }
{ PRINBYTE.PAS.  Remember:  PrintByteHP inherits EVERYTHING     }
{ defined in PrintByte.  EVERYTHING!  The only difference is    }
{ that the PrintSymbol method is overridden by an HP Series II  }
{ specific symbol-print method.  Everything else is EXACTLY as  }
{ it is defined in PrintByte.                                   }
{---------------------------------------------------------------}

  PrintByteHP = OBJECT(PrintByte)
                  PROCEDURE PrintSymbol
                END;


VAR
  I,J,K       : Integer;
  Char1,Char2 : PrintByteHP;  { Instances of PrintByteHP object type }


{--------------------------------------------------------------}
{ This method overrides the PrintSymbol method defined in the  }
{ PrintByte parent object defined in PRINCHAR.PAS:             }
{--------------------------------------------------------------}

PROCEDURE PrintByteHP.PrintSymbol;

BEGIN
  IF DontPrint(BV) THEN                { If DontPrint method says so, }
    Write(LST,Chr(27),'&p1X',Chr(BV))  { use "transparent data print" }
  ELSE                                 { feature to print weird chars }
    Write(LST,Chr(BV));                { Otherwise, just print 'em... }
END;



PROCEDURE Spaces(NumberOfSpaces : Integer);

VAR
  Index : Integer;

BEGIN
  FOR Index := 1 TO NumberOfSpaces DO
    Write(LST,' ')
END;



PROCEDURE NewPage;

VAR
  Index : Integer;

BEGIN
  Write(LST,FormFeed);
END;



PROCEDURE PrintHeader;

VAR
  Index : Integer;

BEGIN
  Spaces(10);
  Writeln(LST,Header);
  Spaces(10);
  FOR Index := 1 TO 60 DO
    Write(LST,BarChar);
  Writeln(LST);
END;



{--------------------------------------------------------------}
{ Nothing more elaborate here than printing the byte out as a  }
{ decimal number, a hex number, a binary number, and a symbol. }
{ The four "Target." calls are method calls to the object      }
{ passed to PrintChar in the Target parameter.                 }
{--------------------------------------------------------------}

PROCEDURE PrintChar(Target : PrintByteHP);

BEGIN
  Spaces(3);
  Target.PrintDecimal;
  Spaces(3);
  Target.PrintHex;
  Spaces(3);
  Target.PrintBinary;
  Spaces(3);
  Target.PrintSymbol;
  Spaces(4);
END;


{--------------------------------------------------------------}
{ This simply sets the HP Series II to its PC symbol set.      }
{--------------------------------------------------------------}

PROCEDURE InitHP;

BEGIN
  Write(LST,Chr(27),'(10U');        { Select symbol set 10U }
END;




BEGIN
  InitHP;                           { Select the PC symbol set }
  Spaces(10);                       { Output 10 spaces }
  Writeln(LST,Title);               { Print out the title string }
  Writeln(LST);                     { Space down one line }
  FOR I := 0 TO 3 DO                { 256 characters takes 4 pages }
    BEGIN
      FOR K := 1 TO 3 DO Writeln(LST);  { Space down 3 lines }
      PrintHeader;                      { Print the chart header }
      FOR J := 0 TO 31 DO               { Do 2 columns of 32 chars. }
        BEGIN
          Char1.BV := (I*64)+J; Char2.BV := (I*64)+J+32;
          Spaces(10);
          PrintChar(Char1);         { Print the left column character }
          Write(LST,ColChar);       { Print the column separator      }
          PrintChar(Char2);         { Print the right column character }
          Writeln(LST);
        END;
      NewPage;                      { Issue a form feed to printer }
    END;
END.






<a name="01ec_0019"><a name="01ec_0019">
<a name="01ec_001a">
[LISTING THREE]
<a name="01ec_001a">

{--------------------------------------------------------------}
{                          PrinByte                            }
{                                                              }
{    Byte-value print object for object extendibility demo     }
{                                                              }
{                                    by Jeff Duntemann         }
{                                    QuickPascal V1.0          }
{                                    Last modified 6/6/89      }
{--------------------------------------------------------------}


UNIT PrinByte;

INTERFACE

USES Printer;


{--------------------------------------------------------------}
{ The PrintByte object "knows" how to print its byte value in  }
{ four different formats:  As decimal quantity, hex quantity,  }
{ binary quantity, and extended ASCII symbol.  It takes the    }
{ cautious path in printing symbols, as prints spaces for all  }
{ of the "control" characters 0-31, "rubout" ($7F), and char   }
{ $FF.  If your printer has some means of printing smiley      }
{ faces and all that other folderol in the low 32 characters,  }
{ override the PrintSymbol method with a new method that uses  }
{ whatever mechanism your printers offers to print those       }
{ characters "spaced out" by this implementation of the        }
{ PrintSymbol method.                                          }
{--------------------------------------------------------------}

TYPE
  PrintByte   = OBJECT
        BV : Byte;     { Byte value }
        FUNCTION  DontPrint(ShouldI : Byte) : Boolean;
        PROCEDURE PrintDecimal;
        PROCEDURE PrintHex;
        PROCEDURE PrintBinary;
        PROCEDURE PrintSymbol;
      END;


IMPLEMENTATION


{ Returns True for any code that CANNOT be printed verbatim }

FUNCTION PrintByte.DontPrint(ShouldI : Byte) : Boolean;

CONST
  NotThese = [0..31,127,255];  { Unprintable codes }

BEGIN
  IF ShouldI IN NotThese THEN DontPrint := True
    ELSE DontPrint := False;
END;



PROCEDURE PrintByte.PrintDecimal;

BEGIN
  Write(LST,Self.BV:3);
END;



PROCEDURE PrintByte.PrintHex;

CONST
  HexDigits : ARRAY[0..15] OF Char = '0123456789ABCDEF';

BEGIN
  WITH Self DO
    Write(LST,HexDigits[BV SHR 4],HexDigits[BV AND $0F]);
END;



PROCEDURE PrintByte.PrintBinary;

VAR
  Index : Integer;

BEGIN
  WITH Self DO
    FOR Index := 7 DOWNTO 0 DO
      IF Odd(BV SHR Index) THEN Write(LST,'1')
   ELSE Write(LST,'0');
END;



PROCEDURE PrintByte.PrintSymbol;

BEGIN
  WITH Self DO
    IF DontPrint(BV) THEN
      Write(LST,' ')
    ELSE
      Write(LST,Chr(BV));
END;


END.






<a name="01ec_001b"><a name="01ec_001b">
<a name="01ec_001c">
[LISTING FOUR]
<a name="01ec_001c">

{--------------------------------------------------------------}
{                          ASCIIChart                          }
{                                                              }
{   Object Extendability demo program: Prints an ASCII chart   }
{                                                              }
{   Note: This program contains control codes specific to      }
{         the HP Laserjet Series II Printer.  Use with         }
{         other printers may be hazardous to your aesthetic    }
{         sensibilities.                                       }
{                                                              }
{                                    Jeff Duntemann            }
{                                    QuickPascal V1.0          }
{                                    Last modified 6/6/89      }
{--------------------------------------------------------------}



{--------------------------------------------------------------}
{ Run this on an HP Laserjet Series II, and you'll get an      }
{ ASCII chart including all the weird characters for which the }
{ Series II has characters in its built-in fonts.  If you have }
{ some other printer, you need only modify the PrintSymbol     }
{ method to use whatever mechanism your printer has to print   }
{ the weird characters.  By modifying PrintSymbol you are      }
{ extending the object PrintByte defined in unit PRINBYTE.PAS, }
{ WITHOUT needing full source code to PRINBYTE.PAS.            }
{--------------------------------------------------------------}



PROGRAM ASCIIChart;


USES Printer,    { Standard Borland unit }
     PrinByte;   { PRINBYTE.PAS from DDJ for September 1989 }


CONST
  Title    = 'THE EXTENDED ASCII CODE AND SYMBOL SET';
  Header   = '   Dec  Hex    Binary  Symbol     Dec  Hex    Binary  Symbol';
  BarChar  = Chr(205);
  ColChar  = Chr(177);
  FormFeed = Chr(12);


TYPE

{---------------------------------------------------------------}
{ This is a child object of PrintByte, defined in separate file }
{ PRINBYTE.PAS.  Remember:  PrintByteHP inherits EVERYTHING     }
{ defined in PrintByte.  EVERYTHING!  The only difference is    }
{ that the PrintSymbol method is overridden by an HP Series II  }
{ specific symbol-print method.  Everything else is EXACTLY as  }
{ it is defined in PrintByte.                                   }
{---------------------------------------------------------------}

  PrintByteHP = OBJECT(PrintByte)
        PROCEDURE PrintSymbol; OVERRIDE
      END;


VAR
  I,J,K       : Integer;
  Char1,Char2 : PrintByteHP;  { Instances of PrintByteHP object type }


{--------------------------------------------------------------}
{ This method overrides the PrintSymbol method defined in the  }
{ PrintByte parent object defined in PRINCHAR.PAS:             }
{--------------------------------------------------------------}

PROCEDURE PrintByteHP.PrintSymbol;

BEGIN
  WITH Self DO
    IF Self.DontPrint(BV) THEN           { If DontPrint method says so, }
      Write(LST,Chr(27),'&p1X',Chr(BV))  { use "transparent data print" }
    ELSE                                 { feature to print weird chars }
      Write(LST,Chr(BV));                { Otherwise, just print 'em... }
END;



PROCEDURE Spaces(NumberOfSpaces : Integer);

VAR
  Index : Integer;

BEGIN
  FOR Index := 1 TO NumberOfSpaces DO
    Write(LST,' ')
END;



PROCEDURE NewPage;

VAR
  Index : Integer;

BEGIN
  Write(LST,FormFeed);
END;



PROCEDURE PrintHeader;

VAR
  Index : Integer;

BEGIN
  Spaces(10);
  Writeln(LST,Header);
  Spaces(10);
  FOR Index := 1 TO 60 DO
    Write(LST,BarChar);
  Writeln(LST);
END;



{--------------------------------------------------------------}
{ Nothing more elaborate here than printing the byte out as a  }
{ decimal number, a hex number, a binary number, and a symbol. }
{ The four "Target." calls are method calls to the object      }
{ passed to PrintChar in the Target parameter.                 }
{--------------------------------------------------------------}

PROCEDURE PrintChar(Target : PrintByteHP);

BEGIN
  Spaces(3);
  Target.PrintDecimal;
  Spaces(3);
  Target.PrintHex;
  Spaces(3);
  Target.PrintBinary;
  Spaces(3);
  Target.PrintSymbol;
  Spaces(4);
END;


{--------------------------------------------------------------}
{ This simply sets the HP Series II to its PC symbol set.      }
{--------------------------------------------------------------}

PROCEDURE InitHP;

BEGIN
  Write(LST,Chr(27),'(10U');        { Select symbol set 10U }
END;




BEGIN
  InitHP;                           { Select the PC symbol set }
  New(Char1); New(Char2);           { Create objects on the heap }
  Spaces(10);                       { Output 10 spaces }
  Writeln(LST,Title);               { Print out the title string }
  Writeln(LST);                     { Space down one line }
  FOR I := 0 TO 3 DO                { 256 characters takes 4 pages }
    BEGIN
      FOR K := 1 TO 3 DO Writeln(LST);  { Space down 3 lines }
      PrintHeader;                      { Print the chart header }
      FOR J := 0 TO 31 DO               { Do 2 columns of 32 chars. }
   BEGIN
     Char1.BV := (I*64)+J; Char2.BV := (I*64)+J+32;
     Spaces(10);
     PrintChar(Char1);         { Print the left column character }
     Write(LST,ColChar);       { Print the column separator      }
     PrintChar(Char2);         { Print the right column character }
     Writeln(LST);
   END;
      NewPage;                      { Issue a form feed to printer }
    END;
END.












Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.