Apple IIgs #35
Printer Driver Specifications

Revised by Matt Deatherage (September 1990)
Written by Dan Hitchens, Matt Deatherage & Suki Lee (May 1988)

This Technical Note describes the routines and internal structures needed to design a printer driver for the Apple IIgs system, and you should use this Note with the Apple IIgs Toolbox Reference manuals. An overview and associated parameters for each of the printer driver routines are in the Print Manager chapter, and you should refer to these for a complete picture.

Changed since March 1990: Added corrections and further descriptions.


Printing Modes

There are two printing modes: immediate and deferred.

File Structure

The user can install new printer drivers into the system by copying a printer driver file into a subdirectory called DRIVERS within the SYSTEM subdirectory. The printer driver file must be of type $BB and have an auxiliary type of $0001.

Print Driver Calls

A printer driver must support the following calls:

Printer drivers may support the following calls if they use the new driver structure outlined below:

Print Driver Entry

Example


StartOfNewDriver    START

                    dc i2'0'                           ; new style driver
                    dc i2'(ListEnd-PrDriverList)/4'    ; count

                    jmp (PrDriverList,x)


PrDriverList        dc a4'PrDefault'
                    dc a4'PrValidate'
                    dc a4'PrStlDialog'
                    dc a4'PrJobDialog'
                    dc a4'PrDriverVer'
                    dc a4'PrOpenDoc'
                    dc a4'PrCloseDoc'
                    dc a4'PrOpenPage'
                    dc a4'PrClosePage'
                    dc a4'PrPicFile'
                    dc a4'InvalidRoutine'
                    dc a4'PrError'
                    dc a4'PrSetError'
                    dc a4'GetDeviceName'
                    dc a4'PrPixelMap'
                    dc a4'PrGetPrinterSpecs'
                    dc a4'PrGetPgOrientation'
ListEnd             anop

In previous versions of this Note, the PrPixelMap and PrDriverVer entries were reversed.

Note that when using the above technique, you're using a 16-bit jump into a table of 24-bit addresses. If all your entry points are in the same segment, this is not a problem.

If your routines' entry points are not all in the same segment, you need a dispatching routine like the following:


StartOfNewDriver    START

                    dc i2 '0'                           ; new style driver
                    dc i2 '(ListEnd-PrDriverList)/4'    ; count

                    lda PrDriverList+2,x
                    sep #$20
                    pha                                 ; push high byte of 
;                                                         address
                    rep #$20
                    lda PrDriverList,x
                    dec a                               ; decrement low 2 
;                                                         bytes only
                    pha                                 ; push modified low 
;                                                         word of address
                    rtl                                 ; transfer to the 
;                                                         routine

See Apple IIgs Technical Note #90, 65816 Tips and Pitfalls, for a discussion of dispatching with RTL.

Print Driver Exit

When one of your routines is ready to exit, it needs to remove the input parameters from the stack, leaving the result space (if any) and the two RTL addresses. Set the accumulator and the carry flag to reflect any error you are returning, then perform an RTL.

Example

If there are N bytes of input parameters to remove, use something like the following. This code assumes that the error code is in the accumulator.


                    tay                                 ; keep error code in Y 
;                                                         temporarily
                    lda 5,s
                    sta N+5,s
                    lda 3,s
                    sta N+3,s
                    lda 1,s
                    sta N+1,s
                    tsc
                    clc
                    adc #N
                    tcs
                    tya                                 ; get error code
                    cmp #1                              ; set carry if error 
;                                                         is not zero
                    rtl

Figure 1 diagrams the stack just before exiting the print driver:


    |
    | Previous Contents
    |___________________
    |
    | Results (if any)
    |___________________
    |
    | RTL2 (3 bytes)
    |___________________
    |
    | RTL1 (3 bytes)
    |___________________

                        <-- Stack Pointer

    Figure 1 - Stack Prior to Exiting the Print Driver

You should do an RTL with the contents of the flags and registers set appropriately. (See the Return from Call section of the "Using The Apple Tools" chapter of the Apple IIgs Toolbox Reference.)

Print Record Structure

Since application programs often need to fiddle with parts of the print record (i.e., the values in the style subrecord), we have defined ways for applications to interpret the print record, and specifically the style subrecord.

iDev, the first word of the printer information subrecord, has two defined values for third-party printer drivers. A value of $8001 indicates a dot-matrix printer while a value of $8003 indicates a laser printer.

A value of $8001 indicates that fields of the style subrecord should be interpreted as they are by the ImageWriter driver, as documented in the Apple IIgs Toolbox Reference. The first seven bits (0-6) of wDev are defined as for the ImageWriter driver. Bits 7-11 are reserved for Apple's use and must be set to zero. Bits 12-15 may be used by third-party printer drivers as necessary; these bits are set to zero in Apple's drivers.

A value of $8003 indicates that fields of the style subrecord should be interpreted as they are by the LaserWriter driver. The first four bits (0-3) of wDev are defined as for the LaserWriter driver. Bits 4-11 are reserved for Apple's use and must be set to zero. Bits 12-15 may be used by third-party printer drivers as necessary; these bits are set to zero in Apple's drivers.

If an application wishes to take advantages of specific features of a third-party printer driver, it has to know that it is dealing with that driver. Since all drivers look pretty much alike, the Print Manager allows you to ask for the name of the currently selected printer driver. An application may make the Print Manager call PMGetPrinterName, which is documented in Volume 3 of the Toolbox Reference. The Print Manager returns the name of the currently selected printer in a Pascal (length byte) string. The name returned is the name of the file from which the driver was loaded. If you intend to use this method to identify a driver, you must inform users not to rename the Printer Driver file on the boot disk.

For alternate driver identification, Developer Technical Support assigns new iDev values if you feel it is absolutely necessary for your driver. Please keep in mind, however, that no application knows how to interpret style records for non-standard iDev values, and that Apple does not publish such interpretations.

Print Driver Calls

Your printer driver handles the following calls:

PrDefault ($0913)

Description:
Fills the fields of the specified print record with default values for the printer.
Passed:
Returned:
None
Performs the following:

PrValidate ($0A13)

Description:
Checks the print record to see that it is valid for the currently installed printer driver.
Passed:
Returned:
Performs the following:

PrStlDialog ($0B13)

Description:
Performs a style dialog with the user.
Passed:
Returned:
Performs the following:
Note: The following are items typically found in printer style dialogs:

Every printer style dialog should have an OK button (default) and a Cancel button.

Note: When calling other routines in your printer driver (like PrValidate), be sure to do so through the Tool Dispatcher ($E10000 or $E10004) so any necessary patches have an opportunity to execute.

PrJobDialog ($0C13)

Description:
Performs a job dialog with the user.
Passed:
Returned:
Performs the following:
Note: The following are items typically found in printer job dialogs:

Every printer job dialog should have an OK button (default) and a Cancel button.

Note: When calling other routines in your printer driver (like PrValidate), be sure to do so through the Tool Dispatcher ($E10000 or $E10004) so any necessary patches have an opportunity to execute.

PrPixelMap ($0D13)

Description:
Prints all or part of the specified pixel map.
Passed:
Returned:
None
Performs the following:

You can choose to print the pixel map in any convenient fashion; one convenient way is to allocate a new print record and call your normal printing routines. This method is outlined below.

PrOpenDoc ($0E13)

Description:
This routine initializes the things needed to open a document. In deferred mode, it establishes a grafPort and makes it the current port for printing.
Passed:
Returned:
Performs the following:
Possible errors:

PrCloseDoc ($0F13)

Description:
Closes the grafPort being used for printing. For immediate mode, this routine ends the printing job. For deferred mode, this routine ends the recording of the document to be printed.
Passed:
Returned:
None
Performs the following:
Possible errors:

PrOpenPage ($1013)

Description:
Begins a new page only if the page falls within the page range specified in the job subrecord.
Passed:
Returned:
None
Performs the following:
Possible errors:

PrClosePage ($1113)

Description:
This signals the end of a page.
Passed:
Returned:
None
Performs the following:
Possible errors:

PrPicFile ($1213)

Description:
Prints a picture file generated in deferred mode.
Passed:
Returned:
None
Performs the following:
Possible errors:

PrError ($1413)

Description:
Gets the error code from the last Print Manager call.
Passed:
None
Returned:
Performs the following:
Possible Errors:

PrSetError ($1513)

Description:
Sets the error value.
Passed:
Returned:
None
Performs the following:

GetDeviceName ($1713)

(also known as PrChanged)

Description:
Used as a communications tool between the printer driver and port driver.
Passed:
None
Returned:
None
Performs the following:

This routine will be called by the Print Manager when your driver is first loaded so a network port driver can find devices of your type. Applications should not make this call. When this routine will be called is not guaranteed; you can't use this as a substitute for a startup call.

PrDriverVer ($2313)

Description:
Returns the version number of the currently installed printer driver.
Passed:
Returned:
Performs the following:
Note: The internal version number is stored major byte, minor byte i.e., $0103 represents version 1.3)

PrGetPrinterSpecs ($1813)

Description:
Returns the type of printer and the printer's characteristics.
Passed:
Returned:
Performs the following:

The value returned for PrinterType should be the driver's iDev value.

PrGetPgOrientation ($3813)

Description:
Returns the page orientation from a print record.
Passed:
Returned:
Performs the following:

Immediate Mode Procedures

To print in the immediate mode, you need to install procedures which cause printing when you make QuickDraw II calls (as noted in PrOpenPage). This section describes the structure and parameters for these routines.

The basic idea is that your driver replaces low-level QuickDraw II routines with pointers to your own routines. For example, when someone wants QuickDraw II to draw some text (say with DrawString), QuickDraw II calls your low-level routine to draw the text. You can then print the text instead.

To install the immediate mode procedures, first create a procedure table for sixteen entries (16*4 bytes) and fill it with the standard procedures by calling SetStdProcs. Once you have the standard procedures, install the addresses of your replacement procedures into it and call SetGrafProcs. Installing your procedure addresses causes the appropriate QuickDraw II calls to call your procedures, which, in turn, perform the actual printing.

The routines that need to be written are known as QuickDraw II "bottleneck procedures." For most dot-matrix printer drivers, the one of most concern when writing immediate mode procedures is StdText. If your target device has an alternate page imaging language, you may wish to print entirely in immediate mode. In this case, you want to intercept most of the bottleneck procedures. Apple IIgs Technical Note #34, Low-Level QuickDraw II Routines, contains information on how to install these procedures. The sample code which follows shows how to replace StdPixels and StdText.

Example:


;*****************************************************************
;** Example of Immediate Mode Printer Procedures.               **
;*****************************************************************
Immedprocs     Start

SrcRect        equ $DC
SrcLocInfo     equ $CC
DrawVerb       equ $38
TextPtr        equ $da
TextLength     equ $d8
CharToDraw     equ $d6

;------------------------------------------------------------------
;
; StdPixels Procedure (Prints Pixel maps)
;
;------------------------------------------------------------------
Pixel          Entry

               phb                          ;save data bank reg on stack
               phk                          ;get program bank reg.
               plb                          ;use as data bank reg.

               lda iPrErr                   ;get errors
               beq Continue                 ;branch if none
               brl ExitPixel                ;branch if errors

Continue       anop

;This gets the source rectangle and stores it at PixelRect
               ldx #6
MoveSrc        lda SrcRect,x
               sta PixelRect,x
               dex
               dex
               bpl MoveSrc

;This gets the source LocInfo and stores it at PixelLoc 
               ldx #16-2
MoveLI         lda SrcLocInfo,x
               sta PixelLoc,x
               dex
               dex
               bpl MoveLI

               pushlong #PixelLoc           ;push pointer to LocInfo
               pushlong #PixelRect          ;push pointer to rectangle


;++++++++++++++++++++++
; Insert code here to print a pixel map
;    INPUT:    PixelLoc   LONG, Pointer to pixel LocInfo
;              PixelRect  LONG, Pointer to pixels BoundsRect
;    SP->
;++++++++++++++++++++++

Exitpixel      lda #0                       ;return with no errors
               clc
               plb                          ;restore data bank
               rtl                          ;return with long

PixelLoc       ds 16                        ;pixel LocInfo
PixelRect      ds 8                         ;pixel rectangle

;------------------------------------------------------------------
;
; StdText Procedure (Prints Standard Text)
;
;------------------------------------------------------------------
StdText        Entry

               phb                          ;save data bank reg on stack
               phk                          ;get program bank reg. 
               plb                          ;use as data bank reg.

               pushlong #PenPos
               _GetPen                      ;current pen pos. -> PenPos

;++++++++++++++++++++++
; Insert Code Here to move the printers head to the corresponding
; PenPos position (if needed).
;++++++++++++++++++++++

               pushword #0                  ;space for textwidth
                                            ;(for call to _TextWidth)

               lda DrawVerb                 ;get DrawVerb
               beq DoCar                    ;if DrawVerb=0 then DoCar

               cmp #1
               beq Dotext2                  ;if DrawVerb=1 then Dotext2
;
;We get here if it's a "C" string (DrawVerb=2)
;
DoCstring      anop
               sep #$20
               longa off
;Search down through string looking for terminator to calc. length
               ldy #0
KeepLooking    lda [TextPtr],y
               beq TheEnd
               iny
               bra KeepLooking
TheEnd         rep #$20
               longa on
               lda TextPtr+2
               pha                          ;push the pointer to string
               lda Textptr
               pha
               phy                          ;push the length of sting
               bra Common

;
;We get here if it's just one character (DrawVerb=0)
;
DoCar          anop
               pushword #0
               tdc
               clc
               adc #CharToDraw              ;calculate addr. of char.
               pha                          ;push addr. of character
               pushword #1                  ;push length of one char.
               bra Common

;
;We get here if it's a string of text (DrawVerb=1)
;
DoText2        anop
               lda TextPtr+2
               pha                          ;push pointer to the string
               lda Textptr
               pha
               lda TextLength
               pha                          ;push the strings length
Common         lda 5,s                      ;Dup the last 3 words of
               pha                          ;the stack (for _TextWidth)
               lda 5,s
               pha
               lda 5,s
               pha
;++++++++++++++++++++++
; Insert code here to print the text
;
;    INPUT:    TextPointer    LONG, Pointer to text to print
;              TextLength     WORD, No. of bytes to print
;    SP->
;++++++++++++++++++++++
               _TextWidth                   ;get the texts width (DH)
               pushword #0                  ;set (DV)=0
               _Move                        ;move current pen location

ExitText       lda #0                       ;return with no errors
               clc
               plb                          ;restore data bank
               rtl                          ;returnith long

PenPos         ds 4                         ;pen position
               end

Further Reference


This and all of the other Apple II Technical Notes have been converted to HTML by Aaron Heiss as a public service to the Apple II community, with permission by Apple Computer, Inc. Any and all trademarks, registered and otherwise, are properties of their owners.

PostScript is a registered trademark of Adobe Systems Incorporated.