MODULE PrinterLib
! ========================================================================
! True BASIC, Inc
! 12 Commerce Avenue
! West Lebanon, New Hampshire 03784-9758
! (800) 872-2742,  (603) 298-8517
! ========================================================================
! Author: Paul Castonguay
! ========================================================================
!
! PURPOSE: To allow control of any device connected directly to the
!          SERIAL or PARALLEL port.
!
!          Actually, this MODULE is designed to interact with a printer
!          because a printer is such a common device.  However, the
!          example is instructional and is intended to be a model for
!          how to control any device connected to either the serial or
!          the parallel port.
!
!    To see a demonstration of the use of this MODULE file, refer to
!    the Port_Control.TRU program in the Examples drawer.
!
!          Files which allow direct access to the Amiga's operating
!          system are in the AmigaTools drawer.  In the last release
!          (version 1.0) these were part of the Developer's Toolkit.
!          This time they have been placed within the basic product.
!          Note that the Developer's Toolkit does contain other tools
!          which are not in the AmigaTools drawer, tools that you may 
!          find useful for designing programs that interact directly 
!          with the operating system.  Contact True BASIC product
!          support for details.
!
!          This file is placed in UserLib because it is not really a
!          part of True BASIC.  It is a support LIBRARY that should be 
!          maintained by you, the user.  Note that this file provides
!          direct access to an NEC PinWriter printer.  You can convert
!          it to support any other printer by assigning the proper codes
!          to the SHARED variables in the initialization section below.
!
!          Look in the Examples drawer for a file called
!          Port_Control.TRU, which exercises this MODULE.  Remember, this
!          is only an instructional example to help you design programs
!          to access any device that may be connected to your computer.
!          It uses the example of a printer only because we cannot
!          predict in advance exactly what kind of device you will want
!          to control.  If you have one of the standard model printers
!          that Commodore supports with a driver program, you should be
!          controlling it using a more general method.  There is a
!          LIBRARY in the AmigaTools drawer called PrinterLIB* that you
!          can use for that purpose.  Note that the PAGE.TRU program in
!          the TBDo drawer is an excellent example of the use of that
!          library.  Refer to the file PrinterLIB.TRU for further
!          information on how to use the PrinterLIB* library.
!
!
!
! DESCRIPTION:
!
!    To send text to the printer from within a True BASIC program you
!    normally open a communications channel to the printer:
!
!        OPEN #1: PRINTER
!
!    ... and send text using the PRINT statement:
!
!        PRINT #1: "This text will appear on the printer."
!
!    To control features of your printer you should PRINT AmigaDOS
!    printer escape sequences, which are then automatically translated
!    for you into the corresponding codes that your printer needs.
!
!    However, this MODULE contains three routines that directly control
!    a printer connected to either the SERIAL or PARALLEL port by sending
!    its own particular codes directly to the port.
!
!    Since all printers require different sets of codes to access their
!    various features, you must inform this MODULE of your printer's
!    particular code requirements.  You do that by assigning codes to a
!    series of SHARED variables, each of which provides access to a
!    different feature.  You can even add to the list of variables in order
!    access more advanced features that your printer may have.  Refer to
!    to the documentation of your printer to find out what codes are used
!    for what features.
!
!    The techniques presented in this MODULE reflect the requirements
!    of any device, not just a printer.
!
!    To account for whether your printer is attached to the SERIAL or 
!    PARALLEL port, you must assign an appropiate value to the Port$
!    variable within the SHARED variable initialization section of this
!    MODULE.
!
!    If your program has opened a communications channel to the printer
!    you must first close that channel before calling routines in this
!    module, otherwise a conflict will occur.  After calling a routine
!    in this module you can re-open the communications channel to the
!    printer if you want to.  When controlling devices other than printers,
!    this conflict will not occur if the printer and the device are
!    connected to different ports.
!
!
!
!
!                    LIBRARY ROUTINES (all PUBLIC)
!                    -----------------------------
!                           SetPrintStyle
!                           SetPrintMargin
!                           OutPort
!
!
!   
! SYSTEM REQUIREMENTS:
!
!    Amiga* and Dos* support files must be present in either your 
!    current directory, or your AmigaTools directory.
!                     
!
! EXCEPTIONS:
!
!    NUMBER    ROUTINE               DESCRIPTION
!    ------    -------               -----------
!
!     900                       Could not open port.
!     901                       Trouble writting to port.
!     902                       Bad code string.
!     903                       Bad left margin
!     904                       Bad right margin
!
! ========================================================================
!
!
!                         HOW TO USE THE ROUTINES
!
!
! ------------------------------------------------------------------------
! SUBROUTINE: OutPort(Text_String$)
! ------------------------------------------------------------------------
!    To OUTPUT strings directly to the printer, you make the following
!    CALL:  
!
!       CALL OutPort("Hello printer.")
!
!    This SUBROUTINE does not require you to open and close the printer
!    port.  It does so itself, automatically.  Note however that if you
!    already have a channel open to the printer from within your program
!    there will be a conflict.  For that reason, if you use True BASIC's
!    normal method of sending text to the printer, as in the following 
!    code:
!
!       OPEN #n: PRINTER
!       PRINT #n: "This text goes to the printer."
!
!    ... you must formally CLOSE #n before calling this SUBROUTINE, then
!    OPEN it again if you want to send more text in the conventional
!    manner.  If your program must make many adjustments to the various
!    features of your printer you might prefer to send your text to the
!    printer by using this SUBROUTINE and not have to OPEN and CLOSE the
!    printer channel so often.  Note that you can use True BASIC's USING$
!    function to format your data into stings before sending them to this
!    SUBROUTINE.  Thus, you can enjoy all the formatting characteristics
!    of PRINT USING that you know and love.
!
!    An error is produced if for any reason this SUBROUTINE cannot open
!    the required SERIAL or PARALLEL port.
!
!
! ------------------------------------------------------------------------
! SUBROUTINE: SetPrintStyle(Control_String$)
! ------------------------------------------------------------------------
!    Allows access to a variety of printer features related to the
!    appearance of printed text; things like draft, letter quality,
!    italics, bold, double width text, ... etc.  Each feature is
!    accessed by passing an argument string which specifies that 
!    particular feature.  Here is an example that selects elite print
!    style, which is 12 characters per inch:
!
!       CALL SetPrintStyle("Elite")
!
!    It's that simple.
!
!    An error is generated if you pass a string which doesn't mean
!    anything.  Below is a list of strings which have been designed
!    into this routine:
!
!       Pica
!       Elite
!       Draft
!       LetterQuality
!       Italic_ON
!       Italic_OFF
!       Underline_ON
!       Underline_OFF
!       DoubleWidth_ON
!       DoubleWidth_OFF
!       Condensed_ON
!       Condensed_OFF
!       Bold_ON
!       Bold_OFF
!       
!    To specify a combination of features, like Elite, LetterQuality, and
!    Italic,  you must make a separate call to the SetPrintStyle() routine
!    for each different feature.  These features are set up independently
!    and do not affect each other.
!    
!    Some features inherently cancel certain others, like Draft which
!    cancels LetterQuality.  You cannot print in both draft and letter
!    quality modes at the same time.
!
!    Other features must be specifically turned on or off as desired,
!    as with Italic_ON and Italic_OFF.
!
!    If your printer has other features that you would like to use, you
!    can add new control strings to the above list, declare new SHARED
!    variables to store their corresponding codes, and add new branches
!    to the SELECT CASE construct in the SetPrintStyle SUBROUTINE in 
!    order to process them.
!
!
! ------------------------------------------------------------------------
! SUBROUTINE: SetPrintMargin(Left_or_Right$, columns)
! ------------------------------------------------------------------------
!    Allows control of left or right margins.
!
!    Left margin is set by specifying "Left" and the column number
!    desired:
!
!       CAll SetPrintMargin("Left", 10)
!
!    An error is produced if you specify a column number that is either
!    less than or equal to zero, or equal to or greater than the current
!    right margin setting.
!
!    Right margin is set by specifying "Right" and the column number
!    desired:
!
!       CAll SetPrintMargin("Right", 70)
!
!    An error is produced if you specify a column number that is either
!    less than or equal to the current left margin setting, or greater
!    than 160, which is the upper limit on my NEC printer.  Naturally,
!    you should change this yourself for your own printer.
!
!
!
!
! ========================================================================



! ****************************************************
! Shared variables to store codes for various features
! of whatever printer you are using.  You must assign
! codes yourself as per your printer's documentation
! in the SHARED variable initialization section below.
! ****************************************************
SHARE ESC$
SHARE Pica$
SHARE Elite$
SHARE Draft$
SHARE LetterQuality$
SHARE Italic_ON$
SHARE Italic_OFF$
SHARE Underline_ON$
SHARE Underline_OFF$
SHARE Elongated_ON$
SHARE Elongated_OFF$
SHARE Condensed_ON$
SHARE Condensed_OFF$
SHARE Enhanced_ON$
SHARE Enhanced_OFF$
SHARE DoubleStrike_ON$
SHARE DoubleStrike_OFF$
SHARE LeftMargin$
SHARE LeftMargin
SHARE RightMargin$
SHARE RightMargin
SHARE Port$


!           ================================================
!           ==== SHARED variable initialization section ====
!           ================================================


! *************************************
! Choose either parallel or serial port
! "PAR:" ==> Parallel port
! "SER:" ==> Serial port
! *************************************
LET Port$ = "PAR:"




! ------------------------------------------------------------------------
!
!                 Printer codes for NEC P5200 PinWriter
!
! I have provided codes for the very minimum number of printer features
! available on most printers.  Simply look up the codes required to 
! obtain the same features on your printer and fill them in below.  The
! idea was not for me to design the most complete routine for one
! particular printer, but to demonstrate how one can be designed in
! general.  To add more features you must declare new control strings and
! SHARED variables, as well as add new branches to the SELECT CASE
! construct in the SetPrintStyle SUBROUTINE below.
!
! Note that to provide Bold I combined the features of enhanced and
! double-strike printing.  Your printer may have a single Bold feature.
! ------------------------------------------------------------------------
LET ESC$              = CHR$(27)

LET Pica$             = ESC$ & CHR$(80)
LET Elite$            = ESC$ & CHR$(77)
LET Draft$            = ESC$ & CHR$(120) & CHR$(0)
LET LetterQuality$    = ESC$ & CHR$(120) & CHR$(1)
LET Italic_ON$        = ESC$ & CHR$(52)
LET Italic_OFF$       = ESC$ & CHR$(53)
LET Underline_ON$     = ESC$ & CHR$(45) & CHR$(1)
LET Underline_OFF$    = ESC$ & CHR$(45) & CHR$(0)
LET Elongated_ON$     = ESC$ & CHR$(87) & CHR$(1)
LET Elongated_OFF$    = ESC$ & CHR$(87) & CHR$(0)
LET Condensed_ON$     = ESC$ & CHR$(15)
LET Condensed_OFF$    = CHR$(18)
LET Enhanced_ON$      = ESC$ & CHR$(69)
LET Enhanced_OFF$     = ESC$ & CHR$(70)
LET DoubleStrike_ON$  = ESC$ & CHR$(71)
LET DoubleStrike_OFF$ = ESC$ & CHR$(72)
LET LeftMargin$       = ESC$ & CHR$(108)
LET LeftMargin        = 1
LET RightMargin$      = ESC$ & CHR$(81)
LET RightMargin       = 80




! ========================================================================
! SetPrintStyle(Control_String$)
! ========================================================================
!
! PURPOSE: To access various features of your printer having to do with
!          the appearance of printed text by directly controling the
!          PARALLEL or SERIAL port to which it is connected.
!
!
! SCOPE: PUBLIC
!
!
! PARAMETERS:
!
!    INPUT:
!
!       Control_String$ ..... Text string describing desired feature.
!
!
! SHARED VARIABLES
!
!    INPUT:
!
!       Pica$ 
!       Elite$
!       Draft$
!       LetterQuality$
!       Italic_ON$
!       Italic_OFF$
!       Underline_ON$
!       Underline_OFF$
!       Elongated_ON$
!       Elongated_OFF$
!       Condensed_ON$
!       Condensed_OFF$
!       Enhanced_ON$
!       Enhanced_OFF$
!       DoubleStrike_ON$
!       DoubleStrike_OFF$
!
!
! ROUTINES CALLED:
!
!    OutPort using SHARED variable corresponding to a desired feature
!    as an argument.
!
!
! OVERVIEW:
!
!    Control_String$ is tested against known features.  If a match is
!    found the proper codes are sent to the OutPort subroutine of this
!    MODULE by passing to it the SHARED variable corresponding to the
!    desired feature.  If no match, an error is produced.
!
!
! SYSTEM REQUIREMENTS:
!
!    Amiga* and Dos* support files must be present in either your 
!    current directory, or your AmigaTools directory.
!                     
!
! EXCEPTIONS:
!
!       902 ................ Bad code string.
!
! ========================================================================
SUB SetPrintStyle(Control_String$)

   ! *************************
   ! Required system LIBRARIES
   ! *************************
   LIBRARY "{AmigaTools}dos*", "{AmigaTools}amiga*"


   ! **********************************************
   ! Test the control string and branch accordingly
   ! **********************************************
   SELECT CASE UCASE$(Control_String$)

      CASE "PICA"

         CALL OutPort(Pica$)

      CASE "ELITE"

         CALL OutPort(Elite$)

      CASE "DRAFT"                      ! Fastest print, lowest quality

         CALL OutPort(Draft$)

      CASE "LETTERQUALITY"              ! Highest print quality, but slower

         CALL OutPort(LetterQuality$)

      CASE "ITALIC_ON"

         CALL OutPort(Italic_ON$)

      CASE "ITALIC_OFF"

         CALL OutPort(Italic_OFF$)

      CASE "UNDERLINE_ON"

         CALL OutPort(Underline_ON$)

      CASE "UNDERLINE_OFF"

         CALL OutPort(Underline_OFF$)

      CASE "DOUBLEWIDTH_ON"

         CALL OutPort(Elongated_ON$)

      CASE "DOUBLEWIDTH_OFF"

         CALL OutPort(Elongated_OFF$)

      CASE "CONDENSED_ON"

         CALL OutPort(Condensed_ON$)

      CASE "CONDENSED_OFF"

         CALL OutPort(Condensed_OFF$)

      CASE "BOLD_ON"                         ! To form bold I used both
                                             ! enhanced and double strike
         CALL OutPort(Enhanced_ON$)          ! features of my printer
         CALL OutPort(DoubleStrike_ON$)

      CASE "BOLD_OFF"

         CALL OutPort(Enhanced_OFF$)
         CALL OutPort(DoubleStrike_OFF$)

      CASE ELSE

         CAUSE ERROR 902, "Bad code string."

   END SELECT


END SUB  ! End of SetPrintStyle




! ========================================================================
! SetPrintMargin(Left_or_Right$, Column)
! ========================================================================
!
! PURPOSE: To control left and right margins of printer by direct control
!          through the PARALLEL or SERIAL port.
!
!
! SCOPE: PUBLIC
!
!
! PARAMETERS:
!
!    INPUT:
!
!       Left_or_Right$ ..... Text string describing desired margin to set.
!       Column ............. Numeric argument specifying column number
!                            of desired margin setting.
!
! SHARED VARIABLES
!
!    INPUT:
!
!       LeftMargin$
!       LeftMargin
!       RightMargin$
!       RightMargin
!
!
! ROUTINES CALLED:
!
!    OutPort using SHARED variable corresponding to the desired margin
!    and column number.
!
!
! OVERVIEW:
!
!    Left_or_Right$ is tested for which margin is desired.  If a match is
!    found the proper codes are sent to the OutPort subroutine of this
!    MODULE by passing to it a string consisting of the SHARED variable
!    corresponding to the desired margin with the column number appended
!    to it.  If no match, an error is produced.
!
!    Note that some testing is performed so that you cannot setup 
!    illegal margins.
!
!
! SYSTEM REQUIREMENTS:
!
!    Amiga* and Dos* support files must be present in either your 
!    current directory, or your AmigaTools directory.
!                     
!
! EXCEPTIONS:
!
!        902 ................ Bad code sent to printer.
!        903 ................ Bad left margin position.
!        904 ................ Bad right margin position.
!
! ========================================================================
SUB SetPrintMargin(Left_or_Right$, Column)

   LIBRARY "{AmigaTools}dos*", "{AmigaTools}amiga*"

   SELECT CASE UCASE$(Left_or_Right$)

      CASE "LEFT"

         IF Column > 0 AND Column < RightMargin THEN

            ! ***************************************
            ! Create string consisting of left margin
            ! codes and desired column position.
            ! ***************************************
            LET SelectLeftMargin$ = LeftMargin$ & CHR$(Column)

            ! ******************************
            ! Send codes to the printer port
            ! ******************************
            CALL OutPort(SelectLeftMargin$)

            ! ******************************************************
            ! Update current value of LeftMargin for later reference
            ! ******************************************************
            LET LeftMargin = Column

         ELSE

            CAUSE ERROR 903,"Bad left margin position."

         END IF

      CASE "RIGHT"

         IF Column > LeftMargin AND Column <= 160 THEN

            ! ****************************************
            ! Create string consisting of right margin
            ! codes and desired column position.
            ! ****************************************
            LET SelectRightMargin$ = RightMargin$ & CHR$(Column)

            ! ******************************
            ! Send codes to the printer port
            ! ******************************
            CALL OutPort(SelectRightMargin$)

            ! *******************************************************
            ! Update current value of RightMargin for later reference
            ! *******************************************************
            LET RightMargin = Column

         ELSE

            CAUSE ERROR 904,"Bad right margin position."

         END IF

      CASE ELSE

         CAUSE ERROR 902, "Bad code string."

   END SELECT

END SUB  ! End of SetPrintMargin




! ========================================================================
! OutPort(String$)
! ========================================================================
!
! PURPOSE: To open the parallel port, send it a String$ which can
!          represent either printer codes or text, then close the port.
!
!
! SCOPE: PUBLIC
!
!
! PARAMETERS:
!
!    INPUT:
!
!       String$ ..... Text or string of either printer codes or text.
!
!
! SHARED VARIABLES
!
!    INPUT:
!
!       Port$ .... PAR: or SER: port, to which you printer is connected
!
!
! ROUTINES CALLED:
!
!    Addr                      Amiga* LIBRARY
!    Open()                    dos* LIBRARY
!    Write()                   dos* LIBRARY
!    Close()                   dos* LIBRARY
!
!
! OVERVIEW:
!
!    A NULL character is appended to Port$, as required by AmigaDOS
!    run-time system library.  Port is opened.  If successful the
!    String$ parameter is sent to the port.  A test is made to see
!    if String$ was successfully sent.  Finally the port is closed.
!
!
! SYSTEM REQUIREMENTS:
!
!    Amiga* and Dos* support files must be present in either your 
!    current directory, or your AmigaTools directory.
!                     
!
! EXCEPTIONS:
!
!        902 ................ Bad code sent to printer.
!        903 ................ Bad left margin position.
!        904 ................ Bad right margin position.
!
! ========================================================================
SUB OutPort(String$)

   LIBRARY "{AmigaTools}dos*", "{AmigaTools}amiga*"

   DECLARE FUNCTION Open, Close, Addr, Write

   ! *************************************************
   !          Build name of file to Open.
   ! AmigaDOS requires NULL character at end of string
   ! *************************************************
   LET Name$ = Port$ & CHR$(0)

   ! *********************************************************
   !                AmigaDOS Open() function
   !
   ! Addr(Name$) ====> Address of name of file to open.
   !                   In this case it's the paralles port.
   ! 1005 ===========> Sepecifies access mode to opened file.
   !                   Read/Write existing file, position
   !                   file pointer at beginning of file
   ! *********************************************************
   LET Port = Open(Addr(Name$), 1005)

   ! ****************
   ! Did Open() fail?
   ! ****************
   IF Port = 0 THEN

      CAUSE ERROR 900, "Could not open parallel port."

   ELSE

      ! **************************
      ! Send string to opened port
      ! **************************
      LET String_Length = LEN(String$)
      LET Success = Write(Port, Addr(String$), String_Length)

      ! *************************
      ! Close port before leaving
      ! *************************
      LET Dummy = Close(Port)

      ! *******************************************
      ! Check if there was an error writing to port
      ! *******************************************
      IF Success <> String_Length THEN

         CAUSE ERROR 901, "Trouble writting to parallel port."

      END IF

   END IF

END SUB  ! End of OutPort



END MODULE  ! End of NECPrinterLib

