Copyright (c) Hyperion Entertainment and contributors.
Printer Device
This page is not yet fully updated to AmigaOS 4.x some of the information contained here may not be applicable in part or totally. |
Contents
- 1 Printer Device
- 2 Printer Device Commands and Functions
- 3 Printer Device Access
- 4 Device Interface
- 5 Sending Printer Commands to a Printer
- 6 Obtaining Printer Specific Data
- 7 Reading and Changing the Printer Preferences Settings
- 8 Querying the Printer Device
- 9 Error Codes from the Printer Device
- 10 Dumping a Rastport to a Printer
- 11 Creating a Printer Driver
- 12 Example Printer Driver Source Code
- 12.1 EpsonX
- 12.2 HP_Laserjet
- 12.2.1 HP_Laserjet: macros.i
- 12.2.2 HP_Laserjet: printertag.asm
- 12.2.3 HP_Laserjet: hp_rev.i
- 12.2.4 HP_Laserjet: init.asm
- 12.2.5 HP_Laserjet: data.c
- 12.2.6 HP_Laserjet: dospecial.c
- 12.2.7 HP_Laserjet: render.c
- 12.2.8 HP_Laserjet: density.c
- 12.2.9 HP_Laserjet transfer.c
- 12.2.10 HP_Laserjet transfer.asm
- 13 Additional Information on the Printer Device
Printer Device
The printer device offers a way of sending configuration-independent output to a printer attached to the Amiga. It can be thought of as a filter: it takes standard commands as input and translates them into commands understood by the printer. The commands sent to the printer are defined in a specific printer driver program. For each type of printer in use, a driver (or the driver of a compatible printer) should be present in the DEVS:Printers directory.
2cPrinter Driver Source Code In This Chapter
EpsonX & A YMCB, 8 pin, multi-density interleaved printer.
HP_LaserJet & A black and white, multi-density, page-oriented printer.
Printer Device Commands and Functions
<tbody> </tbody>Command | Command Operation |
CMD_FLUSH | 8.8cmRemove all queued requests for the printer device. Does not affect active requests. |
CMD_RESET | 8.8cmReset the printer device to its initialized state. All active and queued I/O requests will be aborted. |
CMD_START | 8.8cmRestart all paused I/O requests |
CMD_STOP | 8.8cmPause all active and queued I/O requests. |
CMD_WRITE | 8.8cmWrite out a stream of characters to the printer device. The number of characters can be specified or a NULL-terminated string can be sent. |
PRD_DUMPRPORT | 8.8cmDump the specified RastPort to a graphics printer. |
PRD_PRTCOMMAND | 8.8cmSend a command to the printer. |
PRD_QUERY | 8.8cmReturn the status of the printer port’s lines and registers. |
PRD_RAWWRITE | 8.8cmSend unprocessed output to the the printer. |
Exec and Intuition Functions as Used in This Chapter:
<tbody> </tbody>AbortIO() | 8.8cmAbort a command to the printer device. |
CloseDevice() | 8.8cmRelinquish use of the printer device. All requests must be complete before closing. |
DoIO() | 8.8cmStart a command and wait for completion (synchronous request). |
OpenDevice() | 8.8cmObtain use of the printer device. |
SendIO() | 8.8cmStart a command and return immediately (asynchronous request). |
WaitIO() | 8.8cmWait for the completion of an asynchronous request. When the request is complete, the message will be removed from the printer message port. |
Exec Support Functions as Used in This Chapter:
<tbody> </tbody>CreatePort() | 8.8cmCreate a signal message port for reply messages from the printer device. Exec will signal a task when a message arrives at the reply port. |
CreateExtIO() | 8.8cmCreate an I/O request structure of type printerIO. This structure will be used to send commands to the printer device. |
DeletePort() | 8.8cmDelete the message port created by CreatePort(). |
DeleteExtIO() | 8.8cmDelete an I/O request structure created by CreateExtIO(). |
Printer Device Access
The printer device is totally transparent to an application. It uses information set up by the Workbench Preferences Printer and PrinterGfx tools to identify the type of printer connection (serial or parallel), type of dithering, etc. It also offers the flexibility to send raw information to the printer for special non-standard or unsupported features. Raw data transfer is not recommended for conventional text and graphics since it will result in applications that will only work with certain printers. By using the standard printer device interface, an application can perform device independent output to a printer.
boxDon’t Hog The Device.The printer device is currently an exclusive access device. Do not tie it up needlessly.
There are two ways of doing output to the printer device:
PRT:
– the AmigaDOS printer device
PRT:
may be opened just like any other AmigaDOS file. You may send standard escape sequences to PRT: to specify the options you want as shown in the command table below. The escape sequences are interpreted by the printer driver, translated into printer-specific escape sequences and forwarded to the printer. When using PRT: the escape sequences and data must be sent as a character stream. Using PRT: is by far the easiest way of doing text output to a printer.
printer.device
– to directly access the printer device itself
By opening the printer device directly, you have full control over the printer. You can either send standard escape sequences as shown in the command table below or send raw characters directly to the printer with no processing at all. Doing this would be similar to sending raw characters to SER: or PAR: from AmigaDOS. (Since this interferes with device-independence it is strongly discouraged). Direct access to the printer device also allows you to transmit device I/O commands, such as reset and flush, and do a raster dump on a graphics-capable printer.
boxUse A Stream to Escape.All “raw escape sequences” transmitted to the printer through the printer device must take the form of a character stream.
Opening PRT:
When using the printer device as PRT:, you can open it just as though it were a normal AmigaDOS output file.
BPTR file; file = Open( "PRT:", MODE_NEWFILE ); /* Open PRT: */ if (file == 0) /* if the open was unsuccessful */ exit(PRINTER_WONT_OPEN);
Writing to PRT:
Once you’ve opened it, you can print by calling the AmigaDOS Write() standard I/O routine.
actual_length = Write(file, dataLocation, length);
where
is a file handle.
is a pointer to the first character in the output stream you wish to write. This stream can contain the standard escape sequences as shown in the command table below. The printer command aRAW (see the “Printer Device Command Functions” table below) can be used in the stream if character translation is not desired.
is the length of the output stream.
is the actual length of the write. For the printer device, if there are no errors, this will be the same as the length of write requested. The only exception is if you specify a value of -1 for length. In this case, -1 for length means that a null (0) terminated stream is being written to the printer device. The device returns the count of characters written prior to encountering the null. If it returns a value of -1 in actual_length, there has been an error.
box-1 = STOP!If a -1 is returned by Write(), do not do any additional printing.
Closing PRT:
When the printer I/O is complete, you should close PRT:. Don’t keep the device open when you are not using it. The user may have changed the printer settings by using the Workbench Preferences tool. There’s also the possibility the printer has been turned off and on again causing the printer to switch to its own default settings. Every time the printer device is opened, it reads the current Preferences settings. Hence, by always opening the printer device just before printing and always closing it afterwards, you ensure that your application is using the current Preferences settings.
Close(file);
boxIn DOS, You Must Be A Process.Printer I/O through the DOS must be done by a process, not by a task. DOS utilizes information in the process control block and would become confused if a simple task attempted to perform these activities. Printer I/O using the printer device directly, however, can be performed by a task.
The remainder of this chapter will deal with using the printer device directly.
Device Interface
The printer device operates like the other Amiga devices. To use it, you must first open the printer device, then send I/O requests to it, and then close it when finished. See the “Introduction to Amiga Devices” chapter for general information on device usage.
There are three distinct kinds of data structures required by the printer I/O routines. Some of the printer device I/O commands, such as CMD_START and CMD_WRITE require only an IOStdReq data structure. Others, such as PRD_DUMPRPORT and PRD_PRTCOMMAND, require an extended data structure called IODRPReq (for “Dump a RastPort Request”) or IOPrtCmdReq (for “Printer Command Request”).
For convenience, it is strongly recommended that you define a single data structure called printerIO, that can be used to represent any of the three pre-defined printer communications request blocks.
union printerIO { struct IOStdReq ios; struct IODRPReq iodrp; struct IOPrtCmdReq iopc; }; struct IODRPReq { struct Message io_Message; struct Device *io_Device; /* device node pointer */ struct Unit *io_Unit; /* unit (driver private)*/ UWORD io_Command; /* device command */ UBYTE io_Flags; BYTE io_Error; /* error or warning num */ struct RastPort *io_RastPort; /* raster port */ struct ColorMap *io_ColorMap; /* color map */ ULONG io_Modes; /* graphics viewport modes */ UWORD io_SrcX; /* source x origin */ UWORD io_SrcY; /* source y origin */ UWORD io_SrcWidth; /* source x width */ UWORD io_SrcHeight; /* source x height */ LONG io_DestCols; /* destination x width */ LONG io_DestRows; /* destination y height */ UWORD io_Special; /* option flags */ }; struct IOPrtCmdReq { struct Message io_Message; struct Device *io_Device; /* device node pointer */ struct Unit *io_Unit; /* unit (driver private)*/ UWORD io_Command; /* device command */ UBYTE io_Flags; BYTE io_Error; /* error or warning num */ UWORD io_PrtCommand; /* printer command */ UBYTE io_Parm0; /* first command parameter */ UBYTE io_Parm1; /* second command parameter */ UBYTE io_Parm2; /* third command parameter */ UBYTE io_Parm3; /* fourth command parameter */ };
See the include file exec/io.h for more information on IOStdReq and the include file devices/printer.h for more information on IODRPReq and IOPrtCmdReq.
Opening the Printer Device
Three primary steps are required to open the printer device:
- Create a message port using CreatePort(). Reply messages from the device must be directed to a message port.
- Create an extended I/O request structure of type printerIO with the CreateExtIO() function. This means that one memory area can be used to represent three distinct forms of memory layout for the three different types of data structures that must be used to pass commands to the printer device. By using CreateExtIO(), you automatically allocate enough memory to hold the largest structure in the union statement.
- Open the printer device. Call OpenDevice(), passing the I/O request.
union printerIO { struct IOStdReq ios; struct IODRPReq iodrp; struct IOPrtCmdReq iopc; }; struct MsgPort *PrintMP; /* Message port pointer */ union printerIO *PrintIO; /* I/O request pointer */ if (PrintMP=CreateMsgPort() ) if (PrintIO=(union printerIO *) CreateExtIO(PrintMP,sizeof(union printerIO)) ) if (OpenDevice("printer.device",0L,(struct IORequest *)PrintIO,0) ) printf("printer.device did not open\n");
The printer device automatically fills in default settings for all printer device parameters from Preferences. In addition, information about the printer itself is placed into the appropriate fields of printerIO. (See the “Obtaining Printer Specific Data” section below.)
boxPre-V36 Tasks and OpenDevice().Tasks in pre-V36 versions of the operating system are not able to safely OpenDevice() the printer device because it may be necessary to load it in from disk, something only a process could do under pre-V36. V36 and higher versions of the operating system do not have such a limitation.
Writing Text to the Printer Device
Text written to a printer can be either processed text or unprocessed text.
Processed text is written to the device using the CMD_WRITE command. The printer device accepts a character stream, translates any embedded escape sequences into the proper sequences for the printer being used and then sends it to the printer. The escape sequence translation is based on the printer driver selected either through Preferences or through your application. You may also send a NULL-terminated string as processed text.
Unprocessed text is written to the device using the PRD_RAWWRITE command. The printer device accepts a character stream and sends it unchanged to the printer. This implies that you know the exact escape sequences required by the printer you are using. You may not send a NULL-terminated string as unprocessed text.
One additional point to keep in mind when using PRD_RAWWRITE is that Preference settings for the printer are ignored. Unless the printer has already been initialized by another command, the printer’s own default settings will be used when printing raw, not the user’s Preferences settings.
You write processed text to the printer device by passing an IOStdReq to the device with CMD_WRITE set in io_Command, the number of bytes to be written set in io_Length and the address of the write buffer set in io_Data.
To write a NULL-terminated string, set the length to -1; the device will output from your buffer until it encounters a value of zero (0x00).
PrintIO->ios.io_Length = -1; PrintIO->ios.io_Data = (APTR)"I went to a fight and a hockey game broke out." PrintIO->ios.io_Command = CMD_WRITE; DoIO((struct IORequest *)PrintIO);
The length of the request is -1, meaning we are writing a NULL-terminated string. The number of characters sent will be found in io_Actual after the write request has completed.
You write unprocessed text to the printer device by passing an IOStdReq to the device with PRD_RAWWRITE set in io_Command, the number of bytes to be written set in io_Length and the address of the write buffer set in io_Data.
UBYTE *outbuffer; PrintIO->ios.io_Length = strlen(outbuffer); PrintIO->ios.io_Data = (APTR)outbuffer; PrintIO->ios.io_Command = PRD_RAWWRITE; DoIO((struct IORequest *)PrintIO);
boxIOStdReq Only.I/O requests with CMD_WRITE and PRD_RAWWRITE must use the IOStdReq structure of the union printerIO.
Important Points about Print Requests
Perform printer I/O from a separate task or process
It is quite reasonable for a user to expect that printing will be performed as a background operation. You should try to accommodate this expectation as much as possible.
Give the user a chance to stop
Your application should always allow the user to stop a print request before it is finished.
Don’t confuse aborting a print request with cancelling a page
Some applications seem to offer the user the ability to abort a multi-page print request when in fact the abort is only for the current page being printed. This results in the next page being printed instead of the request being stopped. Do not do this! It only confuses the user and takes away from your application. There is nothing wrong with allowing the user to cancel a page and continue to the next page, but it should be explicit that this is the case. If you abort a print request, the entire request should be aborted.
Closing the Printer Device
Each OpenDevice() must eventually be matched by a call to CloseDevice().
All I/O requests must be complete before CloseDevice(). If any requests are still pending, abort them with AbortIO().
AbortIO(PrintIO); /* Ask device to abort request, if pending */ WaitIO(PrintIO); /* Wait for abort, then clean up */ CloseDevice((struct IORequest *)PrintIO);
boxUse AbortIO()/WaitIO() Intelligently.Only call AbortIO()/WaitIO() for requests which have already been sent to the printer device. Using the AbortIO()/WaitIO() sequence on requests which have not been sent results in a hung condition.
Sending Printer Commands to a Printer
As mentioned before, it is possible to include printer commands (escape sequences) in the character stream and send them to the printer using the CMD_WRITE device I/O command. It is also possible to use the printer command names using the device I/O command PRD_PRTCOMMAND with the IOPrtCmdReq data structure. This gives you a mnemonic way of setting the printer to your program needs.
You send printer commands to the device by passing an IOPrtCmdReq to the device with PRD_PRTCOMMAND set in io_Command, the printer command set in io_PrtCommand and up to four parameters set in Parm0 through Parm3.
#include <devices/printer.h> PrintIO->iopc.io_PrtCommand = aSLRM; /* Set left & right margins */ PrintIO->iopc.io_Parm0 = 1; /* Set left margin = 1 */ PrintIO->iopc.io_Parm1 = 79; /* Set right margin = 79 */ PrintIO->iopc.io_Parm2 = 0; PrintIO->iopc.io_Parm3 = 0; PrintIO->iopc.io_Command = PRD_PRTCOMMAND; DoIO((struct IORequest *)PrintIO);
Consult the command function table listed below for other printer commands.
Printer Command Definitions
The following table describes the supported printer functions.
boxJust Because We Have It Doesn’t Mean You Do.Not all printers support every command. Unsupported commands will either be ignored or simulated using available functions.
To transmit a command to the printer device, you can either formulate a character stream containing the material shown in the “Escape Sequence” column of the table below or send an PRD_PRTCOMMAND device I/O command to the printer device with the “Name” of the function you wish to perform.
lllll
5cPrinter Device Command Functions (continued)
aFNT0 & 34 & ESC(B & US char set or Typeface 0 & DEC
aFNT1 & 35 & ESC(R & French char set or Typeface 1 & DEC
aFNT2 & 36 & ESC(K & German char set or Typeface 2 & DEC
aFNT3 & 37 & ESC(A & UK char set or Typeface 3 & DEC
aFNT4 & 38 & ESC(E & Danish I char set or Typeface 4 & DEC
aFNT5 & 39 & ESC(H & Swedish char set or Typeface 5 & DEC
aFNT6 & 40 & ESC(Y & Italian char set or Typeface 6 & DEC
aFNT7 & 41 & ESC(Z & Spanish char set or Typeface 7 & DEC
aFNT8 & 42 & ESC(J & Japanese char set or Typeface 8 & Amiga
aFNT9 & 43 & ESC(6 & Norwegian char set or Typeface 9 & DEC
aFNT10 & 44 & ESC(C & Danish II char set or Typeface 10 & Amiga
& & & (see “Suggested Typefaces” table)
aPROP2 & 45 & ESC[2p & Proportional on & Amiga
aPROP1 & 46 & ESC[1p & Proportional off & Amiga
aPROP0 & 47 & ESC[0p & Proportional clear & Amiga
aTSS & 48 & ESC[n E & Set proportional offset & ISO
aJFY5 & 49 & ESC[5 F & Auto left justify & ISO
aJFY7 & 50 & ESC[7 F & Auto right justify & ISO
aJFY6 & 51 & ESC[6 F & Auto full justify & ISO
aJFY0 & 52 & ESC[0 F & Auto justify off & ISO
aJFY3 & 53 & ESC[3 F & Letter space (justify) & ISO (special)
aJFY1 & 54 & ESC[1 F & Word fill(auto center) & ISO (special)
aVERP0 & 55 & ESC[0z & 1/8" line spacing & Amiga
aVERP1 & 56 & ESC[1z & 1/6" line spacing & Amiga
aSLPP & 57 & ESC[nt & Set form length n & DEC
aPERF & 58 & ESC[nq & Perf skip n (n>0) & Amiga
aPERF0 & 59 & ESC[0q & Perf skip off & Amiga
aLMS & 60 & ESC#9 & Left margin set & Amiga
aRMS & 61 & ESC#0 & Right margin set & Amiga
aTMS & 62 & ESC#8 & Top margin set & Amiga
aBMS & 63 & ESC#2 & Bottom margin set & Amiga
aSTBM & 64 & ESC[n;nr & Top and bottom margins & DEC
aSLRM & 65 & ESC[n;ns & Left and right margins & DEC
aCAM & 66 & ESC#3 & Clear margins & Amiga
aHTS & 67 & ESCH & Set horizontal tab & ISO
aVTS & 68 & ESCJ & Set vertical tabs & ISO
aTBC0 & 69 & ESC[0g & Clear horizontal tab & ISO
aTBC3 & 70 & ESC[3g & Clear all h. tabs & ISO
aTBC1 & 71 & ESC[1g & Clear vertical tab & ISO
aTBC4 & 72 & ESC[4g & Clear all v. tabs & ISO
aTBCALL & 73 & ESC#4 & Clear all h. & v. tabs & Amiga
aTBSALL & 74 & ESC#5 & Set default tabs & Amiga
aEXTEND & 75 & ESC[n"x & Extended commands & Amiga
aRAW & 76 & ESC[n"r & Next n chars are raw & Amiga
ll
Legend:
ISO & 10cmindicates that the sequence has been defined by the International Standards Organization. This is also very similar to ANSI x3.64.
DEC & 10cmindicates a control sequence defined by Digital Equipment Corporation.
Amiga & indicates a sequence unique to Amiga.
n & 10cmstands for a decimal number expressed as a set of ASCII digits. In the aRAW string ESC[5"rHELLO, n is substituted by 5, the number of RAW characters you send to the printer.
rlrl
2cISO Color Table & 2cSuggested Typefaces
0 & Black & 0 & Default typeface
1 & Red & 1 & Line Printer or equivalent
2 & Green & 2 & Pica or equivalent
3 & Yellow & 3 & Elite or equivalent
4 & Blue & 4 & Helvetica or equivalent
5 & Magenta & 5 & Times Roman or equivalent
6 & Cyan & 6 & Gothic or equivalent
7 & White & 7 & Script or equivalent
8 & NC & 8 & Prestige or equivalent
9 & Default & 9 & Caslon or equivalent
& & 10 & Orator or equivalent
Obtaining Printer Specific Data
Information about the printer in use can be obtained by reading the PrinterData and PrinterExtendedData structures. The values found in these structures are determined by the printer driver selected through Preferences. The data structures are defined in devices/prtbase.h.
Printer specific data is returned in printerIO when the printer device is opened. To read the structures, you must first set the PrinterData structure to point to iodrp.io_Device of the printerIO used to open the device and then set PrinterExtendedData to point to the extended data portion of PrinterData.
/* * Printer_Data.c * * Example getting driver specifics. * * Compiled with SAS C 5.10a. lc -cfist -v -L Printer_Data * * Run from CLI only */ #include <exec/types.h> #include <exec/ports.h> #include <devices/printer.h> #include <devices/prtbase.h> #include <clib/exec_protos.h> #include <clib/alib_protos.h> #include <clib/alib_stdio_protos.h> union printerIO { struct IOStdReq ios; struct IODRPReq iodrp; struct IOPrtCmdReq iopc; }; VOID main(VOID); VOID main(VOID) { struct MsgPort *PrinterMP; union printerIO *PIO; struct PrinterData *PD; struct PrinterExtendedData *PED; /* Create non-public messageport. Could use CreateMsgPort() for this, that's * V37 specific however. */ if (PrinterMP = (struct MsgPort *)CreatePort(0,0)) { /* Allocate printerIO union */ if (PIO = (union printerIO *)CreateExtIO(PrinterMP, sizeof(union printerIO))) { /* Open the printer.device */ if (!(OpenDevice("printer.device",0,(struct IORequest *)PIO,0))) { /* Now that we've got the device opened, let's see what we've got. * First, get a pointer to the printer data. */ PD = (struct PrinterData *)PIO->iodrp.io_Device; /* And a pointer to the extended data */ PED = (struct PrinterExtendedData *)&PD->pd_SegmentData->ps_PED; /* See the <devices/prtbase.h> include file for more details on * the PrinterData and PrinterExtendedData structures. */ printf("Printername: '%s', Version: %ld, Revision: %ld\n", PED->ped_PrinterName, PD->pd_SegmentData->ps_Version, PD->pd_SegmentData->ps_Revision); printf("PrinterClass: 0x%lx, ColorClass: 0x%lx\n", PED->ped_PrinterClass, PED->ped_ColorClass); printf("MaxColumns: %ld, NumCharSets: %ld, NumRows: %ld\n", PED->ped_MaxColumns, PED->ped_NumCharSets,PED->ped_NumRows); printf("MaxXDots: %ld, MaxYDots: %ld, XDotsInch: %ld, YDotsInch: %ld\n", PED->ped_MaxXDots, PED->ped_MaxYDots, PED->ped_XDotsInch, PED->ped_YDotsInch); CloseDevice((struct IORequest *)PIO); } else printf("Can't open printer.device\n"); DeleteExtIO((struct IORequest *)PIO); } else printf("Can't CreateExtIO\n"); DeletePort((struct MsgPort *)PrinterMP); } else printf("Can't CreatePort\n"); }
Reading and Changing the Printer Preferences Settings
The user preferences can be read and changed without running the Workbench Preferences tool. Reading printer preferences can be done by referring to PD->pd_Preferences. Listed on the next page are the printer Preferences fields and their valid ranges.
PICA
, ELITE
, FINE
DRAFT
, LETTER
SIX_LPI
, EIGHT_LPI
1 to PrintRightMargin
PrintLeftMargin
to 999
1 to 999
US_LETTER
, US_LEGAL, N_TRACTOR, W_TRACTOR, CUSTOM
FANFOLD
, SINGLE
IMAGE_POSITIVE
, IMAGE_NEGATIVE
ASPECT_HORIZ
, ASPECT_VERT
SHADE_BW
, SHADE_GREYSCALE, SHADE_COLOR
1 to 15
CORRECT_RED
, CORRECT_GREEN, CORRECT_BLUE, CENTER_IMAGE, IGNORE_DIMENSIONS, BOUNDED_DIMENSIONS, ABSOLUTE_DIMENSIONS, PIXEL_DIMENSIONS, MULTIPLY_DIMENSIONS, INTEGER_SCALING, ORDERED_DITHERING, HALFTONE_DITHERING, FLOYD_DITHERING, ANTI_ALIAS, GREY_SCALE2
0 to 65535
0 to 65535
1 to 7
0 to 255
This example program changes various settings in the printer device’s copy of preferences.
/* * Set_Prefs.c * * This example changes the printer device's COPY of preferences (as obtained * when the printer device was opened by a task via OpenDevice()). Note that * it only changes the printer device's copy of preferences, not the preferences * as set by the user via the preference editor(s). * * Compile with SAS C 5.10: LC -b1 -cfistq -v -y -L * * Run from CLI only */ #include <exec/types.h> #include <devices/printer.h> #include <devices/prtbase.h> #include <intuition/intuition.h> #include <intuition/screens.h> #include <intuition/preferences.h> #include <clib/exec_protos.h> #include <clib/alib_protos.h> #include <clib/alib_stdio_protos.h> #include <clib/graphics_protos.h> #include <clib/intuition_protos.h> struct Library *IntuitionBase; struct Library *GfxBase; union printerIO { struct IOStdReq ios; struct IODRPReq iodrp; struct IOPrtCmdReq iopc; }; struct MsgPort *PrintMP; union printerIO *pio; char message[] = "\ This is a test message to see how this looks when printed\n\ using various printer settings.\n\n"; VOID main(VOID); VOID DoPrinter(VOID); int DoTest(VOID); VOID main(VOID) { if (IntuitionBase = OpenLibrary("intuition.library",0L)) { if (GfxBase = OpenLibrary("graphics.library",0L)) { DoPrinter(); CloseLibrary(GfxBase); } CloseLibrary(IntuitionBase); } } VOID DoPrinter(VOID) { if (PrintMP = CreatePort(0L,0L)) { if (pio = (union printerIO *)CreateExtIO(PrintMP,sizeof(union printerIO))) { if (!(OpenDevice("printer.device",0L,(struct IORequest *)pio,0L))) { DoTest(); CloseDevice((struct IORequest *)pio); } DeleteExtIO((struct IORequest *)pio); } DeletePort(PrintMP); } } DoTest(VOID) { struct PrinterData *PD; struct Preferences *prefs; UWORD newpitch; UWORD newspacing; /* Send INIT sequence - make sure printer is inited - some */ /* printers may eject the current page if necessary when inited */ pio->ios.io_Command = CMD_WRITE; pio->ios.io_Data = "\033#1"; pio->ios.io_Length = -1L; if (DoIO((struct IORequest *)pio)) return(FALSE); /* Print some text using the default settings from Preferences */ pio->ios.io_Command = CMD_WRITE; pio->ios.io_Data = message; pio->ios.io_Length = -1L; if(DoIO((struct IORequest *)pio)) return(FALSE); /* Now try changing some settings * Note that we could just as well send the printer.device escape * sequences to change these settings, but this is an example program. */ /* Get pointer to printer data */ PD = (struct PrinterData *) pio->ios.io_Device; /* Get pointer to printer's copy of preferences * Note that the printer.device makes a copy of preferences when * the printer.device is successfully opened via OpenDevice(), * so we are only going to change the COPY of preferences */ prefs = &PD->pd_Preferences; /* Change a couple of settings */ if (prefs->PrintSpacing == SIX_LPI) newspacing = EIGHT_LPI; if (prefs->PrintSpacing == EIGHT_LPI) newspacing = SIX_LPI; if (prefs->PrintPitch == PICA) newpitch = ELITE; if (prefs->PrintPitch == ELITE) newpitch = FINE; if (prefs->PrintPitch == FINE) newpitch = PICA; /* And let's change the margins too for this example */ prefs->PrintLeftMargin = 20; prefs->PrintRightMargin = 40; prefs->PrintPitch = newpitch; prefs->PrintSpacing = newspacing; /* Send INIT sequence so that these settings are used */ pio->ios.io_Command = CMD_WRITE; pio->ios.io_Data = "\033#1"; pio->ios.io_Length = -1L; if(DoIO((struct IORequest *)pio)) return(FALSE); pio->ios.io_Command = CMD_WRITE; pio->ios.io_Data = message; pio->ios.io_Length = -1L; if(DoIO((struct IORequest *)pio)) return(FALSE); /* Send FormFeed so page is ejected */ pio->ios.io_Command = CMD_WRITE; pio->ios.io_Data = "\014"; pio->ios.io_Length = -1L; if(DoIO((struct IORequest *)pio)) return(FALSE); return(TRUE); }
boxDo Your Duty.The application program is responsible for range checking if the user is able to change the preferences from within the application.
Querying the Printer Device
The status of the printer port and registers can be determined by querying the printer device. The information returned will vary depending on the type of printer—parallel or serial—selected by the user. If parallel, the data returned will reflect the current state of the parallel port; if serial, the data returned will reflect the current state of the serial port.
You query the printer device by passing an IOStdReq to the device with PRD_QUERY set in io_Command and a pointer to a structure to hold the status set in io_Data.
struct PStat { UBYTE LSB; /* least significant byte of status */ UBYTE MSB; /* most significant byte of status */ }; union printerIO *PrintIO; struct PStat status; PrintIO->ios.io_Data = &status; /* point to status structure */ PrintIO->ios.io_Command = PRD_QUERY; DoIO((struct IORequest *)request);
The status is returned in the two UBYTES set in the io_Data field. The printer type, either serial or parallel, is returned in the io_Actual field.
io_Data | Bit | Active | Function (Serial Device) |
LSB | 0 | low | reserved |
1 | low | reserved | |
2 | low | reserved | |
3 | low | Data Set Ready | |
4 | low | Clear To Send | |
5 | low | Carrier Detect | |
6 | low | Ready To Send | |
7 | low | Data Terminal Ready | |
MSB | 8 | high | read buffer overflow |
9 | high | break sent (most recent output) | |
10 | high | break received (as latest input) | |
11 | high | transmit x-OFFed | |
12 | high | receive x-OFFed | |
13-15 | high | reserved | |
io_Data | Bit | Active | Function (Parallel Device) |
LSB | 0 | high | printer busy (offline) |
1 | high | paper out | |
2 | high | printer selected | |
3 | — | read=0; write=1 | |
4–7 | reserved | ||
MSB | 8–15 | reserved | |
io_Actual | 1-parallel, 2-serial |
Error Codes from the Printer Device
The printer device returns error codes whenever an operation is attempted. There are two types of error codes that can be returned. Printer device error codes have positive values; Exec I/O error codes have negative values. Therefore, an application should check for a non-zero return code as evidence of an error, not simply a value greater than zero.
PrintIO->ios.io_Length = strlen(outbuffer); PrintIO->ios.io_Data = (APTR)outbuffer; PrintIO->ios.io_Command = PRD_RAWWRITE; if (DoIO((struct IORequest *)PrintIO)) printf("RAW Write failed. Error: %d ",PrintIO->ios.io_Error);
The error is found in io_Error.
lcl
3cPrinter Device Error Codes
Error & Value & Explanation
PDERR_NOERR & 0 & Operation successful
PDERR_CANCEL & 1 & User canceled request
PDERR_NOTGRAPHICS & 2 & Printer cannot output graphics
PDERR_INVERTHAM & 3 & Obsolete
PDERR_BADDIMENSION & 4 & Print dimensions are illegal
PDERR_DIMENSIONOVERFLOW & 5 & Obsolete
PDERR_INTERNALMEMORY & 6 & No memory available for internal
& & variables
PDERR_BUFFERMEMORY & 7 & No memory available for print buffer
lcl
3cExec Error Codes
Error & Value & Explanation
IOERR_OPENFAIL & -1 & Device failed to open
IOERR_ABORTED & -2 & Request terminated early (after AbortIO())
IOERR_NOCMD & -3 & Command not supported by device
IOERR_BADLENGTH & -4 & Not a valid length
Dumping a Rastport to a Printer
You dump a RastPort (drawing area) to a graphics capable printer by passing an IODRPReq to the device with PRD_DUMPRPORT set in io_Command along with several parameters that define how the dump is to be rendered.
union printerIO *PrintIO struct RastPort *rastPort; struct ColorMap *colorMap; ULONG modeid; UWORD sx, sy, sw, sh; LONG dc, dr; UWORD s; PrintIO->iodrp.io_RastPort = rastPort; /* pointer to RastPort */ PrintIO->iodrp.io_ColorMap = colorMap; /* pointer to color map */ PrintIO->iodrp.io_Modes = modeid; /* ModeID of ViewPort */ PrintIO->iodrp.io_SrcX = sx; /* RastPort X offset */ PrintIO->iodrp.io_SrcY = sy; /* RastPort Y offset */ PrintIO->iodrp.io_SrcWidth = sw; /* print width from X offset */ PrintIO->iodrp.io_SrcHeight = sh; /* print height from Y offset */ PrintIO->iodrp.io_DestCols = dc; /* pixel width */ PrintIO->iodrp.io_DestRows = dr; /* pixel height */ PrintIO->iodrp.io_Special = s; /* flags */ PrintIO->iodrp.io_Command = PRD_DUMPRPORT; SendIO((struct IORequest *)request);
The asynchronous SendIO() routine is used in this example instead of the synchronous DoIO(). A call to DoIO() does not return until the I/O request is finished. A call to SendIO() returns immediately. This allows your task to do other processing such as checking if the user wants to abort the I/O request. It should also be used when writing a lot of text or raw data with CMD_WRITE and PRD_RAWWRITE.
Here is an overview of the possible arguments for the RastPort dump.
A pointer to a RastPort. The RastPort’s bitmap could be in Fast memory.
A pointer to a ColorMap. This could be a custom one.
The viewmode flags or the ModeID returned from GetVPModeID() (V36).
X offset in the RastPort to start printing from.
Y offset in the RastPort to start printing from.
Width of the RastPort to print from io_SrcX.
Height of the RastPort to print from io_SrcY.
Width of the dump in printer pixels.
Height of the dump in printer pixels.
Flag bits (described below).
Looking at these arguments you can see the enormous flexibility the printer device offers for dumping a RastPort. The RastPort pointed to could be totally custom defined. This flexibility means it is possible to build a BitMap with the resolution of the printer. This would result in having one pixel of the BitMap correspond to one pixel of the printer. In other words, only the resolution of the output device would limit the final result. With 12 bit planes and a custom ColorMap, you could dump 4,096 colors—without the HAM limitation—to a suitable printer. The offset, width and height parameters allow dumps of any desired part of the picture. Finally the ViewPort mode, io_DestCols, io_DestRows parameters, together with the io_Special flags define how the dump will appear on paper and aid in getting the correct aspect ratio.
Printer Special Flags
The printer special flags (io_Flags) of the IODRPReq provide a high degree of control over the printing of a RastPort.
Allows one of the dimensions to be reduced/expanded to preserve the correct aspect ratio of the printout.
Centers the image between the left and right edge of the paper.
Prevents the page from being ejected after a graphics dump. Usually used to mix graphics and text or multiple graphics dump on a page oriented printer (normally a laser printer).
The print size will be computed, and set in io_DestCols and io_DestRows, but won’t print. This way the application can see what the actual printsize in printerpixels would be.
Instructs the printer not to send a reset before and after the dump. This flag is obsolete for V1.3 (and higher) drivers.
This flag bit is set by the user in Preferences. Refer to “Reading and Changing the Printer Preferences Settings” if you want to change to density of the printout. (Or any other setting for that matter.)
The width is set to the maximum possible, as determined by the printer or the configuration limits.
The height is set to the maximum possible, as determined by the printer or the configuration limits.
Informs the printer device that the value in io_DestCols is to be taken as a longword binary fraction of the maximum for the dimension. For example, if io_DestCols is 0x8000, the width would be 1/2 (0x8000 / 0xFFFF) of the width of the paper.
Informs the printer device that the value in io_DestRows is to be taken as a longword binary fraction for the dimension.
Informs the printer device that the value in io_DestCols is specified in thousandths of an inch. For example, if io_DestCols is 8000, the width of the printout would be 8.000 inches.
Informs the printer device that the value in io_DestRows is specified in thousandths of an inch.
The flags are defined in the include file devices/printer.h.
Printing with Corrected Aspect Ratio
Using the special flags it is fairly easy to ensure a graphic dump will have the correct aspect ratio on paper. There are some considerations though when printing a non-displayed RastPort. One way to get a corrected aspect ratio dump is to calculate the printer’s ratio from XDotsInch and YDotsInch (taking into account that the printer may not have square pixels) and then adjust the width and height parameters accordingly. You then ask for a non-aspect-ratio-corrected dump since you already corrected it yourself.
Another possibility is having the printer device do it for you. To get a correct calculation you could build your RastPort dimensions in two ways:
- Using an integer multiple of one of the standard (NTSC) display resolutions and setting the io_Modes argument accordingly. For example if your RastPort dimensions were 1,280<math>\times</math>800 (an even multiple of 640<math>\times</math>400) you would set io_Modes to LACE | HIRES. Setting the SPECIAL_ASPECT flag would enable the printer device to properly calculate the aspect ratio of the image.
- When using an arbitrary sized RastPort, you can supply the ModeID of a display mode which has the aspect ratio you would like for your RastPort. The aspect ratio of the various display modes are defined as ticks-per-pixel in the Resolution field of the DisplayInfo structure. You can obtain this value from the graphics database. For example, the resolution of Productivity Mode is 22:22, in other words, 1:1, perfect for a RastPort sized to the limits of the output device. See the “Graphics Library” chapter of the Amiga ROM Kernel Reference Manual: Libraries for general information on the graphics system.
The following example will dump a RastPort to the printer and wait for either the printer to finish or the user to cancel the dump and act accordingly.
/* Demo_Dump.c * * Simple example of dumping a rastport to the printer, changing * printer preferences programmatically and handling error codes. * * Compile with SAS C 5.10a. lc -cfist -v -L Demo_Dump * * Requires Kickstart V37 * Run from CLI only */ #include <exec/types.h> #include <exec/memory.h> #include <exec/ports.h> #include <devices/printer.h> #include <devices/prtbase.h> #include <dos/dos.h> #include <intuition/intuition.h> #include <intuition/screens.h> #include <graphics/displayinfo.h> #include <clib/exec_protos.h> #include <clib/alib_protos.h> #include <clib/alib_stdio_protos.h> #include <clib/graphics_protos.h> #include <clib/intuition_protos.h> struct IntuitionBase *IntuitionBase; struct GfxBase *GfxBase; union printerIO { struct IOStdReq ios; struct IODRPReq iodrp; struct IOPrtCmdReq iopc; }; struct EasyStruct reqES = { sizeof(struct EasyStruct), 0, "DemoDump", "%s", NULL, }; /* Possible printer.device and I/O errors */ static UBYTE *ErrorText[] = { "PDERR_NOERR", "PDERR_CANCEL", "PDERR_NOTGRAPHICS", "INVERTHAM", /* OBSOLETE */ "BADDIMENSION", "DIMENSIONOVFLOW", /* OBSOLETE */ "INTERNALMEMORY", "BUFFERMEMORY", /* IO_ERRs */ "IOERR_OPENFAIL", "IOERR_ABORTED", "IOERR_NOCMD", "IOERR_BADLENGTH" }; /* Requester Action text */ static UBYTE *ActionText[] = { "OK|CANCEL", "Continue", "Abort", }; #define OKCANCELTEXT 0 #define CONTINUETEXT 1 #define ABORTTEXT 2 VOID main(VOID); VOID main(VOID) { struct MsgPort *PrinterMP; union printerIO *PIO; struct PrinterData *PD; struct PrinterExtendedData *PED; struct Screen *pubscreen; struct ViewPort *vp; STRPTR textbuffer; LONG modeID, i,j; ULONG dcol[5], drow[5]; ULONG signal; /* Fails silently if not V37 or greater. Nice thing to do would be to put up * a V33 requester of course. */ /* Set up once */ reqES.es_GadgetFormat = ActionText[CONTINUETEXT]; if (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37)) { /* Using graphics.library to get the displaymodeID of the public screen, * which we'll pass to the printer.device. */ if (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 37)) { if (textbuffer = (STRPTR)AllocMem(256, MEMF_CLEAR)) { /* Create non-public messageport. Since we depend on V37 already, we'll * use the new Exec function. */ if (PrinterMP = CreateMsgPort()) { /* Allocate printerIO union */ if (PIO = (union printerIO *)CreateExtIO(PrinterMP, sizeof(union printerIO))) { /* Open the printer.device */ if (!(OpenDevice("printer.device",0,(struct IORequest *)PIO,0))) { /* Yahoo, we've got it. * We'll use the PrinterData structure to get to the the printer * preferences later on. The PrinterExtendedData structure will * reflect the changes we'll make to the preferences. */ PD = (struct PrinterData *)PIO->iodrp.io_Device; PED = (struct PrinterExtendedData *)&PD->pd_SegmentData->ps_PED; /* We're all set. We'll grab the default public screen (normally * Workbench) and see what happens when we dump it with different * densities. * Next we'll put up a nice requester for the user and ask if * (s)he wants to actually do the dump. */ if (pubscreen = LockPubScreen(NULL)) { vp = &(pubscreen->ViewPort); /* Use graphics.library/GetVPModeID() to get the ModeID of the screen. */ if ((modeID = GetVPModeID(vp)) != INVALID_ID) { /* Seems we got a valid ModeID for the default public screen (surprise). * Do some fake screen dumps with densities 1, 3, 5 and 7. Depending on * the driver, one or more may be the same. */ /* Fill in those parts of the IODRPRequest which won't change */ PIO->iodrp.io_Command = PRD_DUMPRPORT; PIO->iodrp.io_RastPort = &(pubscreen->RastPort); PIO->iodrp.io_ColorMap = vp->ColorMap; PIO->iodrp.io_Modes = modeID; PIO->iodrp.io_SrcX = pubscreen->LeftEdge; PIO->iodrp.io_SrcY = pubscreen->TopEdge; PIO->iodrp.io_SrcWidth = pubscreen->Width; PIO->iodrp.io_SrcHeight = pubscreen->Height; for (i = 1,j=0; i < 8; i+=2,j++) { /* On return these will contain the actual dump dimension */ PIO->iodrp.io_DestCols = 0; PIO->iodrp.io_DestRows = 0; /* We'll simply change our local copy of the * Preferences structure. Likewise we could change * all printer-related preferences. */ PD->pd_Preferences.PrintDensity = i; PIO->iodrp.io_Special = SPECIAL_NOPRINT|SPECIAL_ASPECT; /* No need to do asynchronous I/O here */ DoIO((struct IORequest *)PIO); if (PIO->iodrp.io_Error == 0) { dcol[j] = PIO->iodrp.io_DestCols; drow[j] = PIO->iodrp.io_DestRows; } else { j = PIO->iodrp.io_Error; if (j < 0) j = j * -1 + 7; sprintf(textbuffer, "Error: %s\n", ErrorText[j]); reqES.es_GadgetFormat = ActionText[CONTINUETEXT]; EasyRequest(NULL, &reqES, NULL, textbuffer); break; } } /* Simple, lazy way to check if we encountered any problems */ if (i == 9) { /* Build an 'intelligent' requester */ sprintf(textbuffer, "%s: %5ld x %5ld\n%s: %5ld x %5ld\n%s: %5ld x%5ld\n%s: %5ld x %5ld\n\n%s", "Density 1", dcol[0], drow[0], "Density 3", dcol[1], drow[1], "Density 5", dcol[2], drow[2], "Density 7", dcol[3], drow[3], "Print screen at highest density?"); reqES.es_GadgetFormat = ActionText[OKCANCELTEXT]; /* Obviously the choice presented to the user here is a very * simple one. To print or not to print. In a real life * application, a requester could be presented, inviting * the user to select density, aspect, dithering etc. * The fun part is, of course, that the user can, to a certain * degree, be informed about the effects of her/his selections. */ if (EasyRequest(NULL, &reqES, NULL, textbuffer)) { /* We've still got the density preference set to the highest * density, so no need to change that. * All we do here is re-initialize io_DestCols/Rows and remove * the SPECIAL_NOPRINT flag from io_Special. */ PIO->iodrp.io_DestCols = 0; PIO->iodrp.io_DestRows = 0; PIO->iodrp.io_Special &= ~SPECIAL_NOPRINT; /* Always give the user a change to abort. * So we'll use SendIO(), instead of DoIO(), to be asynch and * catch a possible user request to abort printing. Normally, * the user would be presented with a nice, fat, ABORT requester. * However, since this example doesn't even open a window, and is * basically a 'GraphicDumpDefaultPubscreen' equivalent, we'll use * CTRL-C as the user-abort. Besides that, got to keep it short. */ SendIO((struct IORequest *)PIO); /* Now Wait() for either a user signal (CTRL-C) or a signal from * the printer.device */ signal = Wait(1 << PrinterMP->mp_SigBit | SIGBREAKF_CTRL_C); if (signal & SIGBREAKF_CTRL_C) { /* User wants to abort */ AbortIO((struct IORequest *)PIO); WaitIO((struct IORequest *)PIO); } if (signal & (1 << PrinterMP->mp_SigBit)) { /* printer is either ready or an error has occurred */ /* Remove any messages */ while(GetMsg(PrinterMP)); } /* Check for errors (in this case we count user-abort as an error) */ if (PIO->iodrp.io_Error != 0) { j = PIO->iodrp.io_Error; if (j < 0) j = j * -1 + 7; sprintf(textbuffer, "Error: %s\n", ErrorText[j]); reqES.es_GadgetFormat = ActionText[CONTINUETEXT]; EasyRequest(NULL, &reqES, NULL, textbuffer); } } /* else user doesn't want to print */ } } else /* Say what? */ EasyRequest(NULL, &reqES, NULL, "Invalid ModeID\n"); UnlockPubScreen(NULL, pubscreen); } else EasyRequest(NULL, &reqES, NULL, "Can't lock Public Screen\n"); CloseDevice((struct IORequest *)PIO); } else EasyRequest(NULL, &reqES, NULL, "Can't open printer.device\n"); DeleteExtIO((struct IORequest *)PIO); } else EasyRequest(NULL, &reqES, NULL, "Can't create Extented I/O Request\n"); DeleteMsgPort(PrinterMP); } else EasyRequest(NULL, &reqES, NULL, "Can't create Message port\n"); /* else Out of memory? 256 BYTES? */ FreeMem(textbuffer,256); } CloseLibrary(GfxBase); } /* else MAJOR confusion */ CloseLibrary(IntuitionBase); } }
Strip Printing
Strip printing is a method which allows you to print a picture that normally requires a large print buffer when there is not much memory available. This would allow, for example, a RastPort to be printed at a higher resolution than it was drawn in. Strip printing is done by creating a temporary RastPort as wide as the source RastPort, but not as high. The source RastPort is then rendered, a strip at a time, into the temporary RastPort which is dumped to the printer.
The height of the strip to dump must be an integer multiple of the printer’s NumRows if a non-aspect-ratio-corrected image is to be printed.
For an aspect-ratio-corrected image, the SPECIAL_NOPRINT flag will have to be used to find an io_DestRows that is an integer multiple of NumRows. This can be done by varying the source height and asking for a SPECIAL_NOPRINT dump until io_DestRows holds a number that is an integer multiple of the printer’s NumRows.
If smoothing is to work with strip printing, a raster line above and below the actual area should be added. The line above should be the last line from the previous strip, the line below should be the first line of the next strip. Of course, the first strip should not have a line added above and the last strip should not have a line added below.
The following is a strip printing procedure for a RastPort which is 200 lines high.
copy source line 0 through 50 (51 lines) to strip RastPort lines 0 through 50 (51 lines).
io_SrcY
= 0, io_Height = 50.
the printer device can see there is no line above the first line to dump (since SrcY = 0) and that there is a line below the last line to dump (since there is a 51 line RastPort and only 50 lines are dumped).
copy source line 49 through 100 (52 lines) to strip RastPort lines 0 through 51 (52 lines).
io_SrcY
= 1, io_Height = 50.
the printer device can see there is a line above the first line to dump (since io_SrcY = 1) and that there is a line below the last line to dump (since there is a 52 line RastPort and only 50 lines are dumped).
copy source line 99 through 150 (52 lines) to strip RastPort lines 0 through 51 (52 lines).
io_SrcY
= 1, io_Height = 50.
the printer device can see there is a line above the first line to dump (since io_SrcY = 1) and that there is a line below the last line to dump (since there is a 52 line RastPort and only 50 lines are dumped).
copy source line 149 through 199 (51 lines) to strip RastPort lines 0 through 50 (51 lines).
io_SrcY
= 1, io_Height = 50.
the printer device can see there is a line above the first line to dump (since io_SrcY = 1) and that there is no line below the last line to dump (since there is a 51 line RastPort and only 50 lines are dumped).
Additional Notes About Graphic Dumps
- When dumping a 1 bitplane image select the black and white mode in Preferences. This is much faster than a grey-scale or color dump.
- Horizontal dumps are much faster than vertical dumps.
- Smoothing doubles the print time. Use it for final copy only.
- F-S dithering doubles the print time. Ordered and half-tone dithering incur no extra overhead.
- The lower the density, the faster the printout.
- Friction-fed paper tends to be much more accurate than tractor-fed paper in terms of vertical dot placement (i.e., less horizontal strips or white lines).
- Densities which use more than one pass tend to produce muddy grey-scale or color printouts. It is recommended not to choose these densities when doing a grey-scale or color dump.
boxKeep This in Mind.It is possible that the printer has been instructed to receive a certain amount of data and is still in an “expecting” state if an I/O request has been aborted by the user. This means the printer would try to finish the job with the data the next I/O request might send. Currently the best way to overcome this problem is for the printer to be reset.
Creating a Printer Driver
Creating the printer-dependent modules for the printer device involves writing the data structures and code, compiling and assembling them, and linking to produce an Amiga binary object file. The modules a driver contains varies depending on whether the printer is non-graphics or graphics capable.
All drivers contain these modules:
Include file for init.asm, contains printer device macro definitions.
Printer specific capabilities such as density, character sets and color.
Opens the various libraries required by the printer driver. This will be the same for all printers.
Contains printer device RAW commands and the extended character set supported by the printer.
Printer specific special processing required for printer device commands like aSLRM and aSFC.
Graphic printer drivers require these additional modules:
Printer specific processing to do graphics output and fill the output buffer.
Printer specific processing called by render.c to output the buffer to the printer. Code it in assembly if speed is important.
Printer specific processing to construct the proper print density commands.
The first piece of the printer driver is the PrinterSegment structure described in devices/prtbase.h (this is pointed to by the BPTR returned by the LoadSeg() of the object file). The PrinterSegment contains the PrinterExtendedData (PED) structures (also described in devices/prtbase.h) at the beginning of the object. The PED structure contains data describing the capabilities of the printer, as well as pointers to code and other data. Here is the assembly code for a sample PrinterSegment, which would be linked to the beginning of the sequence of files as printertag.asm.
********************************************************************** * * printer device dependent code tag * * * ********************************************************************** SECTION printer *------ Included Files ----------------------------------------------- INCLUDE "exec/types.i" INCLUDE "exec/nodes.i" INCLUDE "exec/strings.i" INCLUDE "epsonX_rev.i" ; contains VERSION & REVISION INCLUDE "devices/prtbase.i" *------ Imported Names ----------------------------------------------- XREF _Init XREF _Expunge XREF _Open XREF _Close XREF _CommandTable XREF _PrinterSegmentData XREF _DoSpecial XREF _Render XREF _ExtendedCharTable *------ Exported Names ----------------------------------------------- XDEF _PEDData ********************************************************************** ; in case anyone tries to execute this MOVEQ #0,D0 RTS DC.W VERSION ; must be at least 35 (V1.3 and up) DC.W REVISION ; your own revision number _PEDData: DC.L printerName ; pointer to the printer name DC.L _Init ; pointer to the initialization code DC.L _Expunge ; pointer to the expunge code DC.L _Open ; pointer to the open code DC.L _Close ; pointer to the close code DC.B PPC_COLORGFX ; PrinterClass DC.B PCC_YMCB ; ColorClass DC.B 136 ; MaxColumns DC.B 10 ; NumCharSets DC.W 8 ; NumRows DC.L 1632 ; MaxXDots DC.L 0 ; MaxYDots DC.W 120 ; XDotsInch DC.W 72 ; YDotsInch DC.L _CommandTable ; pointer to Command strings DC.L _DoSpecial ; pointer to Command Code function DC.L _Render ; pointer to Graphics Render function DC.L 30 ; Timeout DC.L _ExtendedCharTable ; pointer to 8BitChar table DS.L 1 ; Flag for PrintMode (reserve space) DC.L 0 ; pointer to ConvFunc (char conversion function) printerName: DC.B 'EpsonX' DC.B 0 END
The printer name should be the brand name of the printer that is available for use by programs wishing to be specific about the printer name in any diagnostic or instruction messages. The four functions at the top of the structure are used to initialize this printer-dependent code:
This is called when the printer-dependent code is loaded and provides a pointer to the printer device for use by the printer-dependent code. It can also be used to open up any libraries or devices needed by the printer-dependent code.
This is called immediately before the printer-dependent code is unloaded, to allow it to close any resources obtained at initialization time.
This is called in the process of an OpenDevice() call, after the Preferences are read and the correct primitive I/O device (parallel or serial) is opened. It must return zero if the open is successful, or non-zero to terminate the open and return an error to the user.
This is called in the process of a CloseDevice() call to allow the printer-dependent code to close any resources obtained at open time.
The pd_ variable provided as a parameter to the initialization call is a pointer to the PrinterData structure described in devices/prtbase.h. This is also the same as the io_Device entry in printer I/O requests.
This points back to the PrinterSegment, which contains the PED.
This is available for use by the printer-dependent code—it is not otherwise used by the printer device.
This is the interface routine to the primitive I/O device. This routine uses two I/O requests to the primitive device, so writes are double-buffered. The data parameter points to the byte data to send, and the length is the number of bytes.
This waits for both primitive I/O requests to complete. This is useful if your code does not want to use double buffering. If you want to use the same data buffer for successive pd_PWrites, you must separate them with a call to this routine.
This is the copy of Preferences in use by the printer device, obtained when the printer was opened.
The timeout field is the number of seconds that an I/O request from the printer device to the primitive I/O device (parallel or serial) will remain posted and unsatisfied before the timeout requester is presented to the user. The timeout value should be long enough to avoid the requester during normal printing.
The PrintMode field is a flag which indicates whether text has been printed or not (1 means printed, 0 means not printed). This flag is used in drivers for page oriented printers to indicate that there is no alphanumeric data waiting for a formfeed.
Writing an Alphanumeric Printer Driver
The alphanumeric portion of the printer driver is designed to convert ANSI x3.64 style commands into the specific escape codes required by each individual printer. For example, the ANSI code for underline-on is ESC[4m. The Commodore MPS-1250 printer would like a ESC[-1 to set underline-on. The HP LaserJet accepts ESC[&dD as a start underline command. By using the printer driver, all printers may be handled in a similar manner.
There are two parts to the alphanumeric portion of the printer driver: the CommandTable data table and the DoSpecial() routine.
Command Table
The CommandTable is used to convert all escape codes that can be handled by simple substitution. It has one entry per ANSI command supported by the printer driver. When you are creating a custom CommandTable, you must maintain the order of the commands in the same sequence as that shown in devices/printer.h. By placing the specific codes for your printer in the proper positions, the conversion takes place automatically.
boxOctal knows NULL.If the code for your printer requires a decimal 0 (an ASCII NULL character), you enter this NULL into the CommandTable as octal 376 (decimal 254).
Placing an octal value of 377 (255 decimal) in a position in the command table indicates to the printer device that no simple conversion is available on this printer for this ANSI command. For example, if a daisy-wheel printer does not have a foreign character set, 377 octal (255 decimal) is placed in that position in the command table. However, 377 in a position can also mean that the ANSI command is to be handled by code located in the DoSpecial() function. For future compatibility all printer commands should be present in the command table, and those not supported by the printer filled with the dummy entry 377 octal.
DoSpecial()
The DoSpecial() function is meant to implement all the ANSI functions that cannot be done by simple substitution, but can be handled by a more complex sequence of control characters sent to the printer. These are functions that need parameter conversion, read values from Preferences, and so on. Complete routines can also be placed in dospecial.c. For instance, in a driver for a page oriented-printer such as the HP LaserJet, the dummy Close() routine from the init.asm file would be replaced by a real Close() routine in dospecial.c. This close routine would handle ejecting the paper after text has been sent to the printer and the printer has been closed.
The DoSpecial() function is set up as follows:
#include <exec/types.h> #include <devices/printer.h> #include <devices/prtbase.h> extern struct PrinterData *PD; int DoSpecial(UWORD *command,UBYTE outputBuffer[],BYTE *vline, BYTE *currentVMI,BYTE *crlfFlag,Parms[]) { /* code begins here... */
where
points to the command number. The devices/printer.h file contains the definitions for the routines to use (aRIN is initialize, and so on).
points to the value for the current line position.
points to the value for the current line spacing.
points to the setting of the “add line feed after carriage return” flag.
contain whatever parameters were given with the ANSI command.
points to the memory buffer into which the converted command is returned.
Almost every printer will require an aRIN (initialize) command in DoSpecial(). This command reads the printer settings from Preferences and creates the proper control sequence for the specific printer. It also returns the character set to normal (not italicized, not bold, and so on). Other functions depend on the printer.
Certain functions are implemented both in the CommandTable and in the DoSpecial() routine. These are functions such as superscript, subscript, PLU (partial line up), and PLD (partial line down), which can often be handled by a simple conversion. However, some of these functions must also adjust the printer device’s line-position variable.
boxSave the Data!Some printers lose data when sent their own reset command. For this reason, it is recommended that if the printer’s own reset command is going to be used, PD->pd_PWaitEnabled should be defined to be a character that the printer will not print. This character should be put in the reset string before and after the reset character(s) in the command table.
In the EpsonX[CBM_MPS-1250] DoSpecial() function you’ll see
if (*command == aRIS) { /* reset command */ PD->pd_PWaitEnabled = \375; /* preserve that data! */ }
while in the command table the string for reset is defined as "375033@375". This means that when the printer device outputs the reset string "033@", it will first see the "375", wait a second and output the reset string. While the printer is resetting, the printer device gets the second "375" and waits another second. This ensures that no data will be lost if a reset command is embedded in a string.
Printertag.asm
For an alphanumeric printer the printer-specific values that need to be filled in printertag.asm are as follows:
the maximum number of columns the printer can print across the page.
the number of character sets which can be selected.
a pointer to an extended character table. If the field is null, the default table will be used.
a pointer to a character conversion routine. If the field is null, no conversion routine will be used.
Extended Character Table
The 8BitChars field could contain a pointer to a table of characters for the ASCII codes $A0 to $FF. The symbols for these codes are shown in the “IFF Appendix” of this manual. If this field contains a NULL, it means no specific table is provided for the driver, and the default table is to be used instead.
Care should be taken when generating this table because of the way the table is parsed by the printer device. Valid expressions in the table include 011 where 011 is an octal number, 000 for null and n where n is a 1 to 3 digit decimal number. To enter an actual backslash in the table requires the somewhat awkward . As an example, here is a list of the first entries of the EpsonxX[CBM_MPS-1250] table:
char *ExtendedCharTable[] = { " ", /* NBSP */ "\033R\007[\033R\\0", /* i */ "c\010|", /* c| */ "\033R\003#\033R\\0", /* L- */ "\033R\005$\033R\\0", /* o */ "\033R\010\\\\\033R\\0", /* Y- */ "|", /* | */ "\033R\002@\033R\\0", /* SS */ "\033R\001~\033R\\0", /* " */ "c", /* copyright */ "\033S\\0a\010_\033T", /* a_ */ "<", /* << */ "~", /* - */ "-", /* SHY */ "r", /* registered trademark */ "-", /* - */ /* more entries go here */ };
Character Conversion Routine
The ConvFunc field contains a pointer to a character conversion function that allows you to selectively translate any character to a combination of other characters. If no translation conversion is necessary (for most printers it isn’t), the field should contain a null.
ConvFunc()
arguments are a pointer to a buffer, the character currently processed, and a CR/LF flag. The ConvFunc() function should return a -1 if no conversion has been done. If the character is not to be added to the buffer, a 0 can be returned. If any translation is done, the number of characters added to the buffer must be returned.
Besides simple character translation, the ConvFunc() function can be used to add features like underlining to a printer which doesn’t support them automatically. A global flag could be introduced that could be set or cleared by the DoSpecial() function. Depending on the status of the flag the ConvFunc() routine could, for example, put the character, a backspace and an underline character in the buffer and return 3, the number of characters added to the buffer.
The ConvFunc() function for this could look like the following example:
#define DO_UNDERLINE 0x01 #define DO_BOLD 0x02 /* etc */ external short myflags; int ConvFunc(char *buffer, char c, int crlf_flag) { int nr_of_chars_added = 0; /* for this example we only do this for chars in the 0x20-0x7e range */ /* Conversion of ESC (0x1b) and CSI (0x9b) is NOT recommended */ if (c > 0x1f && c < 0x7f) { /* within space - ~ range ? */ if (myflags & DO_UNDERLINE) { *buffer++ = c; /* the character itself */ *buffer++ = 0x08; /* a backspace */ *buffer++ = '_'; /* an underline char */ nr_of_chars_added = 3; /* added three chars to buffer */ } if (myflags & DO_BOLD) { if (nr_of_chars_added) { /* already have added something */ *buffer++ = 0x08; /* so we start with backspace */ ++nr_of_chars_added; /* and increment the counter */ } *buffer++ = c; *buffer++ = 0x08; *buffer++ = c; ++nr_of_chars_added; if (myflags & DO_UNDERLINE) { /* did we do underline too? */ *buffer++ = 0x08; /* then backspace again */ *buffer++ = '_'; /* (printer goes crazy by now) */ nr_of_chars_added += 2; /* two more chars */ } } } if (nr_of_chars_added) return(nr_of_chars_added); /* total nr of chars we added */ else return(-1); /* we didn't do anything */ }
In DoSpecial() the flagbits could be set or cleared, with code like the following:
if (*command == aRIS) /* reset command */ myflags = 0; /* clear all flags */ if (*command == aRIN) /* initialize command */ myflags = 0; if (*command == aSGR0) /* 'PLAIN' command */ myflags = 0; if (*command == aSGR4) /* underline on */ myflags |= DO_UNDERLINE; /* set underline bit */ if (*command == aSGR24) /* underline off */ myflags &= ~DO_UNDERLINE; /* clear underline bit */ if (*command == aSGR1) /* bold on */ myflags |= DO_BOLD; /* set bold bit */ if (*command == aSGR22) /* bold off */ myflags &= ~DO_BOLD; /* clear bold bit */
Try to keep the expansions to a minimum so that the throughput will not be slowed down too much, and to reduce the possibility of data overrunning the printer device buffer.
Writing a Graphics Printer Driver
Designing the graphics portion of a custom printer driver consists of two steps: writing the printer-specific Render(), Transfer() and SetDensity() functions, and replacing the printer-specific values in printertag.asm. Render(), Transfer() and SetDensity() comprise render.c, transfer.c, and density.c modules, respectively.
A printer that does not support graphics has a very simple form of Render(); it returns an error. Here is sample code for Render() for a non-graphics printer (such as an Alphacom or Diablo 630):
#include "exec/types.h" #include "devices/printer.h" int Render(void) { return(PDERR_NOTGRAPHICS); }
The following section describes the contents of a typical driver for a printer that does support graphics.
Render()
This function is the main printer-specific code module and consists of seven parts referred to here as cases:
- Pre-Master initialization (Case 5)
- Master initialization (Case 0)
- Putting the pixels in a buffer (Case 1)
- Dumping a pixel buffer to the printer (Case 2)
- Closing down (Case 4)
- Clearing and initializing the pixel buffer (Case 3)
- Switching to the next color(Case 6) (special case for multi-color printers)
boxState Your Case.The numbering of the cases reflects the value of each step as a case in a C-language switch statement. It does not denote the order that the functions are executed; the order in which they are listed above denotes that.
For each case, Render() receives four long variables as parameters: ct, x, y and status. These parameters are described below for each of the seven cases that Render() must handle.
Parameters:
0 or pointer to the IODRPReq structure passed to PCDumpRPort
io_Special
flag from the IODRPReq structure
0
When the printer device is first opened, Render() is called with c set to 0, to give the driver a chance to set up the density values before the actual graphic dump is called.
The parameter passed in x will be the io_Special flag which contains the density and other SPECIAL flags. The only flags used at this point are the DENSITY flags, all others should be ignored. Never call PWrite() during this case. When you are finished handling this case, return PDERR_NOERR.
Parameters:
pointer to a IODRPReq structure
width (in pixels) of printed picture
height (in pixels) of printed picture
boxEverything is A-OK.At this point the printer device has already checked that the values are within range for the printer. This is done by checking values listed in printertag.asm.
The x and y value should be used to allocate enough memory for a command and data buffer for the printer. If the allocation fails, PDERR_BUFFERMEMORY should be returned. In general, the buffer needs to be large enough for the commands and data required for one pass of the print head. These typically take the following form:
<start gfx cmd> <data> <end gfx cmd>
The <start gfx cmd> should contain any special, one-time initializations that the printer might require such as:
Carriage Return
Some printers start printing graphics without returning the printhead. Sending a CR assures that printing will start from the left edge.
Unidirectional
Some printers which have a bidirectional mode produce non-matching vertical lines during a graphics dump, giving a wavy result.
To prevent this, your driver should set the printer to unidirectional mode.
Clear margins
Some printers force graphic dumps to be done within the text margins, thus they should be cleared.
Other commands
Enter the graphics mode, set density, etc.
boxMulti-Pass? Don’t Forget the Memory.In addition to the memory for commands and data, a multi-pass color printer must allocate enough buffer space for each of the different color passes.
The printer should never be reset during the master initialization case This will cause problems during multiple dumps. Also, the pointer to the IODRPReq structure in ct should not be used except for those rare printers which require it to do the dump themselves. Return the PDERR_TOOKCONTROL error in that case so that the printer device can exit gracefully.
boxPDERR_TOOKCONTROL, An Error in Name Only.The printer device error code, PDERR_TOOKCONTROL, is not an error at all, but an internal indicator that the printer driver is doing the graphic dump entirely on its own. The printer device can assume the dump has been done. The calling application will not be informed of this, but will receive PDERR_NOERR instead.
The example render.c functions listed at the end of this chapter use double buffering to reduce the dump time which is why the AllocMem() calls are for , where BUFSIZE represents the amount of memory for one entire print cycle. However, contrary to the example source code, allocating the two buffers independently of each other is recommended. A request for one large block of contiguous memory might be refused. Two smaller requests are more likely to be granted.
Parameters:
pointer to a PrtInfo structure.
PCM color code (if the printer is PCC_MULTI_PASS).
printer row # (the range is 0 to pixel height - 1).
In this case, you are passed an entire row of YMCB intensity values (Yellow, Magenta, Cyan, Black). To handle this case, you call the Transfer() function in the transfer.c module. You should return PDERR_NOERR after handling this case. The PCM-defines for the x parameter from the file devices/prtgfx.h are PCMYELLOW, PCMMAGENTA, PCMCYAN and PCMBLACK.
Parameters:
0
0
- of rows sent (the range is 1 to NumRows).
At this point the data can be Run Length Encoded (RLE) if your printer supports it. If the printer doesn’t support RLE, the data should be white-space stripped. This involves scanning the buffer from end to beginning for the position of the first occurrence of a non-zero value. Only the data from the beginning of the buffer to this position should be sent to the printer. This will significantly reduce print times.
The value of y can be used to advance the paper the appropriate number of pixel lines if your printer supports that feature. This helps prevent white lines from appearing between graphic dumps.
You can also do post-processing on the buffer at this point. For example, if your printer uses the hexadecimal number $03 as a command and requires the sequence $03 $03 to send $03 as data, you would probably want to scan the buffer and expand any $03s to $03 $03 during this case. Of course, you’ll need to allocate space somewhere in order to expand the buffer.
The error from PWrite() should be returned after this call.
Parameters:
0
0
0
The printer driver does not send blank pixels so you must initialize the buffer to the value your printer uses for blank pixels (usually 0). Clearing the buffer should be the same for all printers. Initializing the buffer is printer specific, and it includes placing the printer-specific control codes in the buffer before and after the data.
This call is made before each Case 2 call. Clear your active print buffer—remember you are double buffering—and initialize it if necessary. After this call, PDERR_NOERR should be returned.
Parameters:
error code
io_Special
flag from the IODRPReq structure
0
This call is made at the end of the graphic dump or if the graphic dump was cancelled for some reason. At this point you should free the printer buffer memory. You can determine if memory was allocated by checking that the value of PD->pd_PrintBuf is not NULL. If memory was allocated, you must wait for the print buffers to clear (by calling PBothReady) and then deallocate the memory. If the printer—usually a page oriented printer—requires a page eject command, it can be given here. Before you do, though, you should check the SPECIAL_NOFORMFEED bit in x. Don’t issue the command if it is set.
If the error condition in ct is PDERR_CANCEL, you should not PWrite(). This error indicates that the user is trying to cancel the dump for whatever reason. Each additional PWrite() will generate another printer trouble requester. Obviously, this is not desirable.
During this render case PWrite() could be used to:
- reset the line spacing. If the printer doesn’t have an advance ’n’ dots command, then you’ll probably advance the paper by changing the line spacing. If you do, set it back to either 6 or 8 lpi (depending on Preferences) when you are finished printing.
- set bidirectional mode if you selected unidirectional mode in render Case 0.
- set black text; some printers print the text in the last color used, even if it was in graphics mode.
- restore the margins if you cancelled the margins in render Case 0.
- any other command needed to exit the graphics mode, eject the page, etc.
Either PDERR_NOERR or the error from PWrite() should be returned after this call.
This call provides support for printers which require that colors be sent in separate passes. When this call is made, you should instruct the printer to advance its color panel. This case is only needed for printers of the type PCC_MULTI_PASS, such as the CalComp ColorMaster.
Transfer()
Transfer()
dithers and renders an entire row of pixels passed to it by the Render() function. When Transfer() gets called, it is passed 5 parameters
Parameters:
a pointer to a PrtInfo structure
the row number
a pointer to the buffer
a pointer to the color buffers
the buffer offset for interleaved printing.
The dithering process of Transfer() might entail thresholding, grey-scale dithering, or color-dithering each destination pixel.
If PInfo->pi_threshold is non-zero, then the dither value is:
PInfo->pi_threshold ^ 15
If PInfo->pi_threshold is zero, then the dither value is computed by:
*(PInfo->pi_dmatrix + ((y & 3) * 2) + (x & 3))
where x is initialized to PInfo->pi_xpos and is incremented for each of the destination pixels. Since the printer device uses a 4<math>\times</math>4 dither matrix, you must calculate the dither value exactly as given above. Otherwise, your driver will be non-standard and the results will be unpredictable.
The Transfer() function renders by putting a pixel in the print buffer based on the dither value. If the intensity value for the pixel is greater than the dither value as computed above, then the pixel should be put in the print buffer. If it is less than, or equal to the dither value, it should be skipped to process the next pixel.
lll
Printer
& Type of
ColorClass & Dithering & Rendering logic
PCC_BW & Thresholding & 7.1cmTest the black value against the threshold value to see if you should render a black pixel.
& Grey Scale & 7.1cmTest the black value against the dither value to see if you should render a black pixel.
& Color & –
PCC_YMC & Thresholding & 7.1cmTest the black value against the threshold value to see if you should render a black pixel. Print yellow, magenta and cyan pixel to make black.
& Grey Scale & 7.1cmTest the black value against the dither value to see if you should render a black pixel. Print yellow, magenta and cyan pixel to make black.
& Color & 7.1cmTest the yellow value against the dither value to see if you should render a yellow pixel. Repeat this process for magenta and cyan.
PCC_YMCB & Thresholding & 7.1cmTest the black value against the threshold value to see if you should render a black pixel.
& Grey Scale & 7.1cmTest the black value against the dither value to see if you should render a black pixel.
& Color & 7.1cmTest the black value against the dither value to see if you should render a black pixel. If black is not rendered, then test the yellow value against the dither value to see if you should render a yellow pixel. Repeat this process for magenta and cyan. (See the EpsonX transfer.c file)
PCC_YMC_BW & Thresholding & 7.1cmTest the black value against the threshold value to see if you should render a black pixel.
& Grey Scale & 7.1cmTest the black value against the dither value to see if you should render a black pixel.
& Color & 7.1cmTest the yellow value against the dither value to see if you should render a yellow pixel. Repeat this process for magenta and cyan.
In general, if black is rendered for a specific printer dot, then the YMC values should be ignored, since the combination of YMC is black. It is recommended that the printer buffer be constructed so that the order of colors printed is yellow, magenta, cyan and black, to prevent smudging and minimize color contamination on ribbon color printers.
The example transfer.c files are provided in C for demonstration only. Writing this module in assembler can cut the time needed for a graphic dump in half. The EpsonX transfer.asm file is an example of this.
SetDensity()
SetDensity()
is a short function which implements multiple densities. It is called in the Pre-master initialization case of the Render() function. It is passed the density code in density_code. This is used to select the desired density or, if the user asked for a higher density than is supported, the maximum density available.
SetDensity()
should also handle narrow and wide tractor paper sizes.
Densities below 80 dpi should not be supported in SetDensity(), so that a minimum of 640 dots across for a standard 8.5<math>\times</math>11-inch paper is guaranteed. This results in a 1:1 correspondence of dots on the printer to dots on the screen in standard screen sizes. The HP LaserJet is an exception to this rule. Its minimum density is 75 dpi because the original HP LaserJet had too little memory to output a full page at a higher density.
Printertag.asm
For a graphic printer the printer-specific values that need to be filled in in printertag.asm are as follows:
The maximum number of dots the printer can print across the page.
The maximum number of dots the printer can print down the page. Generally, if the printer supports roll or form feed paper, this value should be 0 indicating that there is no limit. If the printer has a definite y dots maximum (as a laser printer does), this number should be entered here.
The dot density in x (supplied by SetDensity(), if it exists).
The dot density in y (supplied by SetDensity(), if it exists).
The printer class of the printer.
<tbody> </tbody>PPC_BWALPHA | black&white alphanumeric, no graphics. |
PPC_BWGFX | black&white (only) graphics. |
PPC_COLORALPHA | color alphanumeric, no graphics. |
PPC_COLORGFX | color (and maybe black&white) graphics. |
The color class the printer falls into.
<tbody> </tbody>PCC_BW | Black&White only |
PCC_YMC | Yellow Magenta Cyan only. |
PCC_YMC_BW | Yellow Magenta Cyan or Black&White, but not both |
PCC_YMCB | Yellow Magenta Cyan Black |
PCC_WB | White&Black only, 0 is BLACK |
PCC_BGR | Blue Green Red |
PCC_BGR_WB | Blue Green Red or Black&White |
PCC_BGRW | Blue Green Red White |
The number of pixel rows printed by one pass of the print head. This number is used by the non-printer-specific code to determine when to make a render Case 2 call to you. You have to keep this number in mind when determining how big a buffer you’ll need to store one print cycle’s worth of data.
Testing the Printer Driver
A printer driver should be thoroughly tested before it is released. Though labor intensive, the alphanumeric part of a driver can be easily tested. The graphics part is more difficult. Following are some recommendations on how to test this part.
Start with a black and white (threshold 8), grey scale and color dump of the same picture. The color dump should be in color, of course. The grey scale dump should be like the color dump, except it will consist of patterns of black dots. The black and white dump will have solid black and solid white areas.
Next, do a dump with the DestX and DestY dots set to an even multiple of the XDotsInch and YDotsInch for the printer. For example, if the printer has a resolution of 120<math>\times</math>144 dpi, a 480<math>\times</math>432 dump could be done. This should produce a printed picture which covers 4<math>\times</math>3 inches on paper. If the width of the picture is off, then the wrong value for XDotsInch has been put in printertag.asm. If the height of the picture is off, the wrong value for YDotsInch is in printertag.asm.
Do a color dump as wide as the printer can handle with the density set to 7.
Make sure that the printer doesn’t force graphic dumps to be done within the text margins. This can easily be tested by setting the text margins to 30 and 50, the pitch to 10 cpi and then doing a graphic dump wider than 2 inches. The dump should be left justified and as wide as you instructed. If the dump starts at character position 30 and is cut off at position 50, the driver will have to be changed. These changes involve clearing the margins before the dump (Case 0) and restoring the margins after the dump (Case 4). An example of this is present in render.c source example listed at the end of this chapter.
boxThe Invisible Setup.Before the graphic dump, some text must be sent to the printer to have the printer device initialize the printer. The “text” sent does not have to contain any printable characters (i.e., you can send a carriage return or other control characters).
As a final test, construct an image with a white background that has objects in it surrounded by white space. Dump this as black and white, grey scale and color. This will test the white-space stripping or RLE, and the ability of the driver to handle null lines. The white data areas should be separated by at least as many lines of white space as the NumRows value in the printertag.asm file.
Example Printer Driver Source Code
As an aid in writing printer drivers, source code for two different classes of printers is supplied. Both drivers have been successfully generated with Lattice C 5.10 and Lattice Assembler 5.10. The example drivers are:
EpsonX
A YMCB, 8 pin, multi-density interleaved printer.
HP_Laserjet
A black&white, multi-density, page-oriented printer.
All printer drivers use the following include file macros.i for init.asm.
********************************************************************** * * printer device macro definitions * ********************************************************************** *------ external definition macros ----------------------------------- XREF_EXE MACRO XREF _LVO\1 ENDM XREF_DOS MACRO XREF _LVO\1 ENDM XREF_GFX MACRO XREF _LVO\1 ENDM XREF_ITU MACRO XREF _LVO\1 ENDM *------ library dispatch macros -------------------------------------- CALLEXE MACRO CALLLIB _LVO\1 ENDM LINKEXE MACRO LINKLIB _LVO\1,_SysBase ENDM LINKDOS MACRO LINKLIB _LVO\1,_DOSBase ENDM LINKGFX MACRO LINKLIB _LVO\1,_GfxBase ENDM LINKITU MACRO LINKLIB _LVO\1,_IntuitionBase ENDM
EpsonX
For the EpsonX driver, both the assembly and C version of Transfer() are supplied. In the Makefile the (faster) assembly version is used to generate the driver.
The EpsonX driver can be generated with the following Makefile.
LC = lc:lc ASM = lc:asm CFLAGS = -iINCLUDE: -b0 -d0 -v ASMFLAGS = -iINCLUDE: LINK = lc:blink LIB = lib:lc.lib+lib:amiga.lib OBJ = printertag.o+init.o+data.o+dospecial.o+render.o+transfer.o+density.o TARGET = EpsonX @$(LC) $(CFLAGS) $* $(TARGET): printertag.o init.o data.o dospecial.o render.o density.o transfer.o @$(LINK) <WITH < FROM $(OBJ) TO $(TARGET) LIBRARY $(LIB) NODEBUG SC SD VERBOSE MAP $(TARGET).map H < init.o: init.asm @$(ASM) $(ASMFLAGS) init.asm printertag.o: printertag.asm epsonX_rev.i @$(ASM) $(ASMFLAGS) printertag.asm transfer.o: transfer.asm @$(ASM) $(ASMFLAGS) transfer.asm dospecial.o: dospecial.c data.o: data.c density.o: density.c render.o: render.c
EpsonX: macros.i
********************************************************************** * * printer device macro definitions * ********************************************************************** *------ external definition macros ----------------------------------- XREF_EXE MACRO XREF _LVO\1 ENDM XREF_DOS MACRO XREF _LVO\1 ENDM XREF_GFX MACRO XREF _LVO\1 ENDM XREF_ITU MACRO XREF _LVO\1 ENDM *------ library dispatch macros -------------------------------------- CALLEXE MACRO CALLLIB _LVO\1 ENDM LINKEXE MACRO LINKLIB _LVO\1,_SysBase ENDM LINKDOS MACRO LINKLIB _LVO\1,_DOSBase ENDM LINKGFX MACRO LINKLIB _LVO\1,_GfxBase ENDM LINKITU MACRO LINKLIB _LVO\1,_IntuitionBase ENDM
EpsonX: printertag.asm
********************************************************************** * * printer device dependent code tag * ********************************************************************** SECTION printer *------ Included Files ----------------------------------------------- INCLUDE "exec/types.i" INCLUDE "exec/nodes.i" INCLUDE "exec/strings.i" INCLUDE "epsonX_rev.i" INCLUDE "devices/prtbase.i" *------ Imported Names ----------------------------------------------- XREF _Init XREF _Expunge XREF _Open XREF _Close XREF _CommandTable XREF _PrinterSegmentData XREF _DoSpecial XREF _Render XREF _ExtendedCharTable *------ Exported Names ----------------------------------------------- XDEF _PEDData ********************************************************************** MOVEQ #0,D0 ; show error for OpenLibrary() RTS DC.W VERSION DC.W REVISION _PEDData: DC.L printerName DC.L _Init DC.L _Expunge DC.L _Open DC.L _Close DC.B PPC_COLORGFX ;PrinterClass DC.B PCC_YMCB ; ColorClass DC.B 136 ; MaxColumns DC.B 10 ; NumCharSets DC.W 8 ; NumRows DC.L 1632 ; MaxXDots DC.L 0 ; MaxYDots DC.W 120 ; XDotsInch DC.W 72 ; YDotsInch DC.L _CommandTable ; Commands DC.L _DoSpecial DC.L _Render DC.L 30 ; Timeout DC.L _ExtendedCharTable ; 8BitChars DS.L 1 ; PrintMode (reserve space) DC.L 0 ; ptr to char conversion function printerName: dc.b 'EpsonX',0 END
EpsonX: epsonx_rev.i
VERSION EQU 35 REVISION EQU 1
EpsonX: init.asm
********************************************************************** * * printer device functions * ********************************************************************** SECTION printer *------ Included Files ----------------------------------------------- INCLUDE "exec/types.i" INCLUDE "exec/nodes.i" INCLUDE "exec/lists.i" INCLUDE "exec/memory.i" INCLUDE "exec/ports.i" INCLUDE "exec/libraries.i" INCLUDE "macros.i" *------ Imported Functions ------------------------------------------- XREF_EXE CloseLibrary XREF_EXE OpenLibrary XREF _AbsExecBase XREF _PEDData *------ Exported Globals --------------------------------------------- XDEF _Init XDEF _Expunge XDEF _Open XDEF _Close XDEF _PD XDEF _PED XDEF _SysBase XDEF _DOSBase XDEF _GfxBase XDEF _IntuitionBase ********************************************************************** SECTION printer,DATA _PD DC.L 0 _PED DC.L 0 _SysBase DC.L 0 _DOSBase DC.L 0 _GfxBase DC.L 0 _IntuitionBase DC.L 0 ********************************************************************** SECTION printer,CODE _Init: MOVE.L 4(A7),_PD LEA _PEDData(PC),A0 MOVE.L A0,_PED MOVE.L A6,-(A7) MOVE.L _AbsExecBase,A6 MOVE.L A6,_SysBase * ;------ open the dos library LEA DLName(PC),A1 MOVEQ #0,D0 CALLEXE OpenLibrary MOVE.L D0,_DOSBase BEQ initDLErr * ;------ open the graphics library LEA GLName(PC),A1 MOVEQ #0,D0 CALLEXE OpenLibrary MOVE.L D0,_GfxBase BEQ initGLErr * ;------ open the intuition library LEA ILName(PC),A1 MOVEQ #0,D0 CALLEXE OpenLibrary MOVE.L D0,_IntuitionBase BEQ initILErr MOVEQ #0,D0 pdiRts: MOVE.L (A7)+,A6 RTS initPAErr: MOVE.L _IntuitionBase,A1 LINKEXE CloseLibrary initILErr: MOVE.L _GfxBase,A1 LINKEXE CloseLibrary initGLErr: MOVE.L _DOSBase,A1 LINKEXE CloseLibrary initDLErr: MOVEQ #-1,D0 BRA.S pdiRts ILName: DC.B 'intuition.library' DC.B 0 DLName: DC.B 'dos.library' DC.B 0 GLName: DC.B 'graphics.library' DC.B 0 DS.W 0 *--------------------------------------------------------------------- _Expunge: MOVE.L _IntuitionBase,A1 LINKEXE CloseLibrary MOVE.L _GfxBase,A1 LINKEXE CloseLibrary MOVE.L _DOSBase,A1 LINKEXE CloseLibrary *--------------------------------------------------------------------- _Open: MOVEQ #0,D0 RTS *--------------------------------------------------------------------- _Close: MOVEQ #0,D0 RTS END
EpsonX: data.c
/* Data.c table for EpsonX driver. */ char *CommandTable[] ={ "\375\033@\375",/* 00 aRIS reset */ "\377", /* 01 aRIN initialize */ "\012", /* 02 aIND linefeed */ "\015\012", /* 03 aNEL CRLF */ "\377", /* 04 aRI reverse LF */ /* 05 aSGR0 normal char set */ "\0335\033-\376\033F", "\0334", /* 06 aSGR3 italics on */ "\0335", /* 07 aSGR23 italics off */ "\033-\001", /* 08 aSGR4 underline on */ "\033-\376", /* 09 aSGR24 underline off */ "\033E", /* 10 aSGR1 boldface on */ "\033F", /* 11 aSGR22 boldface off */ "\377", /* 12 aSFC set foreground color */ "\377", /* 13 aSBC set background color */ /* 14 aSHORP0 normal pitch */ "\033P\022\033W\376", /* 15 aSHORP2 elite on */ "\033M\022\033W\376", "\033P", /* 16 aSHORP1 elite off */ /* 17 aSHORP4 condensed fine on */ "\017\033P\033W\376", "\022", /* 18 aSHORP3 condensed fine off */ "\033W\001", /* 19 aSHORP6 enlarge on */ "\033W\376", /* 20 aSHORP5 enlarge off */ "\377", /* 21 aDEN6 shadow print on */ "\377", /* 22 aDEN5 shadow print off */ "\033G", /* 23 aDEN4 double strike on */ "\033H", /* 24 aDEN3 double strike off */ "\033x\001", /* 25 aDEN2 NLQ on */ "\033x\376", /* 26 aDEN1 NLQ off */ "\033S\376", /* 27 aSUS2 superscript on */ "\033T", /* 28 aSUS1 superscript off */ "\033S\001", /* 29 aSUS4 subscript on */ "\033T", /* 30 aSUS3 subscript off */ "\033T", /* 31 aSUS0 normalize the line */ "\377", /* 32 aPLU partial line up */ "\377", /* 33 aPLD partial line down */ "\033R\376", /* 34 aFNT0 Typeface 0 */ "\033R\001", /* 35 aFNT1 Typeface 1 */ "\033R\002", /* 36 aFNT2 Typeface 2 */ "\033R\003", /* 37 aFNT3 Typeface 3 */ "\033R\004", /* 38 aFNT4 Typeface 4 */ "\033R\005", /* 39 aFNT5 Typeface 5 */ "\033R\006", /* 40 aFNT6 Typeface 6 */ "\033R\007", /* 41 aFNT7 Typeface 7 */ "\033R\010", /* 42 aFNT8 Typeface 8 */ "\033R\011", /* 43 aFNT9 Typeface 9 */ "\033R\012", /* 44 aFNT10 Typeface 10 */ "\033p1", /* 45 aPROP2 proportional on */ "\033p0", /* 46 aPROP1 proportional off */ "\377", /* 47 aPROP0 proportional clear */ "\377", /* 48 aTSS set proportional offset */ "\377", /* 49 aJFY5 auto left justify */ "\377", /* 50 aJFY7 auto right justify */ "\377", /* 51 aJFY6 auto full jusitfy */ "\377", /* 52 aJFY0 auto jusity off */ "\377", /* 53 aJFY3 letter space */ "\377", /* 54 aJFY1 word fill */ "\0330", /* 55 aVERP0 1/8" line spacing */ "\0332", /* 56 aVERP1 1/6" line spacing */ "\033C", /* 57 aSLPP set form length */ "\033N", /* 58 aPERF perf skip n (n > 0) */ "\033O", /* 59 aPERF0 perf skip off */ "\377", /* 60 aLMS set left margin */ "\377", /* 61 aRMS set right margin */ "\377", /* 62 aTMS set top margin */ "\377", /* 63 aBMS set bottom margin */ "\377", /* 64 aSTBM set T&B margins */ "\377", /* 65 aSLRM set L&R margins */ "\377", /* 66 aCAM clear margins */ "\377", /* 67 aHTS set horiz tab */ "\377", /* 68 aVTS set vert tab */ "\377", /* 69 aTBC0 clear horiz tab */ "\033D\376", /* 70 aTBC3 clear all horiz tabs */ "\377", /* 71 aTBC1 clear vert tab */ "\033B\376", /* 72 aTBC4 clear all vert tabs */ /* 73 aTBCALL clear all h & v tabs */ "\033D\376\033B\376", /* 74 aTBSALL set default tabs */ "\033D\010\020\030\040\050\060\070\100\110\120\130\376", "\377", /* 75 aEXTEND extended commands */ "\377" /* 76 aRAW next 'n' chars are raw */ }; /* For each character from character 160 to character 255, there is an entry in this table, which is used to print (or simulate printing of) the full Amiga character set. (see AmigaDos Developer's Manual, pp A-3) This table is used only if there is a valid pointer to this table in the PEDData table in the printertag.asm file, and the VERSION is 33 or greater. Otherwise, a default table is used instead. To place non-printable characters in this table, you can either enter them as in C strings (ie \011, where 011 is an octal number, or as \\000 where 000 is any decimal number, from 1 to 3 digits. This is usually used to enter a NUL into the array (C has problems with it otherwise.), or if you forgot your octal calculator. On retrospect, was a poor choice for this function, as you must say \\\\ to enter a backslash as a backslash. Live and learn... */ char *ExtendedCharTable[] = { " ", /* NBSP*/ "\033R\007[\033R\\0", /* i */ "c\010|", /* c| */ "\033R\003#\033R\\0", /* L- */ "\033R\005$\033R\\0", /* o */ "\033R\010\\\\\033R\\0", /* Y- */ "|", /* | */ "\033R\002@\033R\\0", /* SS */ "\033R\001~\033R\\0", /* " */ "c", /* copyright */ "\033S\\0a\010_\033T", /* a_ */ "<", /* << */ "~", /* - */ "-", /* SHY */ "r", /* registered trademark */ "-", /* - */ "\033R\001[\033R\\0", /* degrees */ "+\010_", /* +_ */ "\033S\\0002\033T", /* 2 */ "\033S\\0003\033T", /* 3 */ "'", /* ' */ "u", /* u */ "P", /* reverse P */ "\033S\\000.\033T", /* . */ ",", /* , */ "\033S\\0001\033T", /* 1 */ "\033R\001[\033R\\0\010-", /* o_ */ ">", /* >> */ "\033S\\0001\033T\010-\010\033S\0014\033T", /* 1/4 */ "\033S\\0001\033T\010-\010\033S\0012\033T", /* 1/2 */ "\033S\\0003\033T\010-\010\033S\0014\033T", /* 3/4 */ "\033R\007]\033R\\0", /* upside down ? */ "A\010`", /* ``A */ "A\010'", /* 'A */ "A\010^", /* ^A */ "A\010~", /* ~A */ "\033R\002[\033R\\0", /* "A */ "\033R\004]\033R\\0", /* oA */ "\033R\004[\033R\\0", /* AE */ "C\010,", /* C, */ "E\010`", /* ``E */ "\033R\011@\033R\\0", /* 'E */ "E\010^", /* ^E */ "E\010\033R\001~\033R\\0", /* "E */ "I\010`", /* ``I */ "I\010`", /* 'I */ "I\010^", /* ^I */ "I\010\033R\001~\033R\\0", /* "I */ "D\010-", /* -D */ "\033R\007\\\\\033R\\0", /* ~N */ "O\010`", /* ``O */ "O\010'", /* 'O */ "O\010^", /* ^O */ "O\010~", /* ~O */ "\033R\002\\\\\033R\\0", /* "O */ "x", /* x */ "\033R\004\\\\\033R\\0", /* 0 */ "U\010`", /* ``U */ "U\010'", /* 'U */ "U\010^", /* ^U */ "\033R\002]\033R\\0", /* "U */ "Y\010'", /* 'Y */ "T", /* Thorn */ "\033R\002~\033R\\0", /* B */ "\033R\001@\033R\\0", /* ``a */ "a\010'", /* 'a */ "a\010^", /* ^a */ "a\010~", /* ~a */ "\033R\002{\033R\\0", /* "a */ "\033R\004}\033R\\0", /* oa */ "\033R\004{\033R\\0", /* ae */ "\033R\001\\\\\033R\\0", /* c, */ "\033R\001}\033R\\0", /* ``e */ "\033R\001{\033R\\0", /* 'e */ "e\010^", /* ^e */ "e\010\033R\001~\033R\\0", /* "e */ "\033R\006~\033R\\0", /* ``i */ "i\010'", /* 'i */ "i\010^", /* ^i */ "i\010\033R\001~\033R\\0", /* "i */ "d", /* d */ "\033R\007|\033R\\0", /* ~n */ "\033R\006|\033R\\0", /* ``o */ "o\010'", /* 'o */ "o\010^", /* ^o */ "o\010~", /* ~o */ "\033R\002|\033R\\0", /* "o */ ":\010-" /* :- */ "\033R\004|\033R\\0", /* o/ */ "\033R\001|\033R\\0", /* ``u */ "u\010'", /* 'u */ "u\010^", /* ^u */ "\033R\002}\033R\\0", /* "u */ "y\010'", /* 'y */ "t", /* thorn */ "y\010\033R\001~\033R\\0" /* "y */ };
EpsonX: dospecial.c
/* DoSpecial for EpsonX driver. */ #include "exec/types.h" #include "devices/printer.h" #include "devices/prtbase.h" #define LMARG 3 #define RMARG 6 #define MARGLEN 8 #define CONDENSED 7 #define PITCH 9 #define QUALITY 17 #define LPI 24 #define INITLEN 26 DoSpecial(command, outputBuffer, vline, currentVMI, crlfFlag, Parms) char outputBuffer[]; UWORD *command; BYTE *vline; BYTE *currentVMI; BYTE *crlfFlag; UBYTE Parms[]; { extern struct PrinterData *PD; int x = 0, y = 0; /* 00-00 \375 wait 01-03 \033lL set left margin 04-06 \033Qq set right margin 07-07 \375 wait */ static char initMarg[MARGLEN+1] = "\375\033lL\033Qq\375"; /* 00-01 \0335 italics off 02-04 \033-\000 underline off 05-06 \033F boldface off 07-07 \022 cancel condensed mode 08-09 \033P select pica (10 cpi) 10-12 \033W\000 enlarge off 13-14 \033H doublestrike off 15-17 \033x\000 draft 18-19 \033T super/sub script off 20-22 \033p0 proportional off 23-24 \0332 6 lpi 25-25 \015 carriage return */ static char initThisPrinter[INITLEN+1] = "\0335\033-\000\033F\022\033P\033W\000\033H\033x\000\033T\033p0\0332\015"; static BYTE ISOcolorTable[10] = {0, 5, 6, 4, 3, 1, 2, 0}; if (*command == aRIN) { while (x < INITLEN) { outputBuffer[x] = initThisPrinter[x]; x++; } if (PD->pd_Preferences.PrintQuality == LETTER) { outputBuffer[QUALITY] = 1; } *currentVMI = 36; /* assume 1/6 line spacing (36/216 => 1/6) */ if (PD->pd_Preferences.PrintSpacing == EIGHT_LPI) { outputBuffer[LPI] = '0'; *currentVMI = 27; /* 27/216 => 1/8 */ } if (PD->pd_Preferences.PrintPitch == ELITE) { outputBuffer[PITCH] = 'M'; } else if (PD->pd_Preferences.PrintPitch == FINE) { outputBuffer[CONDENSED] = '\017'; /* condensed */ outputBuffer[PITCH] = 'P'; /* pica condensed */ } Parms[0] = PD->pd_Preferences.PrintLeftMargin; Parms[1] = PD->pd_Preferences.PrintRightMargin; *command = aSLRM; } if (*command == aCAM) { /* cancel margins */ y = PD->pd_Preferences.PaperSize == W_TRACTOR ? 136 : 80; if (PD->pd_Preferences.PrintPitch == PICA) { Parms[1] = (10 * y) / 10; } else if (PD->pd_Preferences.PrintPitch == ELITE) { Parms[1] = (12 * y) / 10; } else { /* fine */ Parms[1] = (17 * y) / 10; } Parms[0] = 1; y = 0; *command = aSLRM; } if (*command == aSLRM) { /* set left and right margins */ PD->pd_PWaitEnabled = 253; if (Parms[0] == 0) { initMarg[LMARG] = 0; } else { initMarg[LMARG] = Parms[0] - 1; } initMarg[RMARG] = Parms[1]; while (y < MARGLEN) { outputBuffer[x++] = initMarg[y++]; } return(x); } if (*command == aPLU) { if (*vline == 0) { *vline = 1; *command = aSUS2; return(0); } if (*vline < 0) { *vline = 0; *command = aSUS3; return(0); } return(-1); } if (*command == aPLD) { if (*vline == 0) { *vline = -1; *command = aSUS4; return(0); } if (*vline > 0) { *vline = 0; *command = aSUS1; return(0); } return(-1); } if (*command == aSUS0) { *vline = 0; } if (*command == aSUS1) { *vline = 0; } if (*command == aSUS2) { *vline = 1; } if (*command == aSUS3) { *vline = 0; } if (*command == aSUS4) { *vline = -1; } if (*command == aVERP0) { *currentVMI = 27; } if (*command == aVERP1) { *currentVMI = 36; } if (*command == aIND) { /* lf */ outputBuffer[x++] = '\033'; outputBuffer[x++] = 'J'; outputBuffer[x++] = *currentVMI; return(x); } if (*command == aRI) { /* reverse lf */ outputBuffer[x++] = '\033'; outputBuffer[x++] = 'j'; outputBuffer[x++] = *currentVMI; return(x); } if (*command == aSFC) { if (Parms[0] == 39) { Parms[0] = 30; /* set defaults */ } if (Parms[0] > 37) { return(0); /* ni or background color change */ } outputBuffer[x++] = '\033'; outputBuffer[x++] = 'r'; outputBuffer[x++] = ISOcolorTable[Parms[0] - 30]; /* Kludge to get this to work on a CBM_MPS-1250 which interprets 'ESCr' as go into reverse print mode. The 'ESCt' tells it to get out of reverse print mode. The 'NULL' is ignored by the CBM_MPS-1250 and required by all Epson printers as the terminator for the 'ESCtNULL' command which means select normal char set (which has no effect). */ outputBuffer[x++] = '\033'; outputBuffer[x++] = 't'; outputBuffer[x++] = 0; return(x); } if (*command == aRIS) { PD->pd_PWaitEnabled = 253; } return(0); }
EpsonX: render.c
/* EpsonX (EX/FX/JX/LX/MX/RX) driver. */ #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/memory.h> #include "devices/printer.h" #include "devices/prtbase.h" #define NUMSTARTCMD 7 /* # of cmd bytes before binary data */ #define NUMENDCMD 1 /* # of cmd bytes after binary data */ #define NUMTOTALCMD (NUMSTARTCMD + NUMENDCMD) /* total of above */ #define NUMLFCMD 4 /* # of cmd bytes for linefeed */ #define MAXCOLORBUFS 4 /* max # of color buffers */ #define STARTLEN 19 #define PITCH 1 #define CONDENSED 2 #define LMARG 8 #define RMARG 11 #define DIREC 15 static ULONG TwoBufSize; static UWORD RowSize, ColorSize, NumColorBufs, dpi_code, spacing; static UWORD colorcodes[MAXCOLORBUFS]; Render(ct, x, y, status) long ct, x, y, status; { extern void *AllocMem(), FreeMem(); extern struct PrinterData *PD; extern struct PrinterExtendedData *PED; UBYTE *CompactBuf(); static ULONG BufSize, TotalBufSize, dataoffset; static UWORD spacing, colors[MAXCOLORBUFS]; /* 00-01 \003P set pitch (10 or 12 cpi) 02-02 \022 set condensed fine (on or off) 03-05 \033W\000 enlarge off 06-08 \033ln set left margin to n 09-11 \033Qn set right margin to n 12-12 \015 carriage return 13-15 \033U1 set uni-directional mode 16-18 \033t\000 see kludge note below Kludge to get this to work on a CBM_MPS-1250 which interprets 'ESCr' as go into reverse print mode. The 'ESCt' tells it to get out of reverse print mode. The 'NULL' is ignored by the CBM_MPS-1250 and required by all Epson printers as the terminator for the 'ESCtNULL' command which means select normal char set (which has no effect). */ static UBYTE StartBuf[STARTLEN+1] = "\033P\022\033W\000\033ln\033Qn\015\033U1\033t\000"; UBYTE *ptr, *ptrstart; int err; switch(status) { case 0 : /* Master Initialization */ /* ct - pointer to IODRPReq structure. x - width of printed picture in pixels. y - height of printed picture in pixels. */ RowSize = x; ColorSize = RowSize + NUMTOTALCMD; if (PD->pd_Preferences.PrintShade == SHADE_COLOR) { NumColorBufs = MAXCOLORBUFS; colors[0] = ColorSize * 3; /* Black */ colors[1] = ColorSize * 0; /* Yellow */ colors[2] = ColorSize * 1; /* Magenta */ colors[3] = ColorSize * 2; /* Cyan */ colorcodes[0] = 4; /* Yellow */ colorcodes[1] = 1; /* Magenta */ colorcodes[2] = 2; /* Cyan */ colorcodes[3] = 0; /* Black */ } else { /* grey-scale or black&white */ NumColorBufs = 1; colors[0] = ColorSize * 0; /* Black */ colorcodes[0] = 0; /* Black */ } BufSize = ColorSize * NumColorBufs + NUMLFCMD; if (PED->ped_YDotsInch == 216) { TwoBufSize = BufSize * 3; TotalBufSize = BufSize * 6; } else if (PED->ped_YDotsInch == 144) { TwoBufSize = BufSize * 2; TotalBufSize = BufSize * 4; } else { TwoBufSize = BufSize * 1; TotalBufSize = BufSize * 2; } PD->pd_PrintBuf = AllocMem(TotalBufSize, MEMF_PUBLIC); if (PD->pd_PrintBuf == NULL) { err = PDERR_BUFFERMEMORY; } else { dataoffset = NUMSTARTCMD; /* This printer prints graphics within its text margins. This code makes sure the printer is in 10 cpi and then sets the left and right margins to their minimum and maximum values (respectively). A carriage return is sent so that the print head is at the leftmost position as this printer starts printing from the print head's position. The printer is put into unidirectional mode to reduce wavy vertical lines. */ StartBuf[PITCH] = 'P'; /* 10 cpi */ StartBuf[CONDENSED] = '\022'; /* off */ /* left margin of 1 */ StartBuf[LMARG] = 0; /* right margin of 80 or 136 */ StartBuf[RMARG] = PD->pd_Preferences. PaperSize == W_TRACTOR ? 136 : 80; /* uni-directional mode */ StartBuf[DIREC] = '1'; err = (*(PD->pd_PWrite))(StartBuf, STARTLEN); } break; case 1 : /* Scale, Dither and Render */ /* ct - pointer to PrtInfo structure. x - 0. y - row # (0 to Height - 1). */ Transfer(ct, y, &PD->pd_PrintBuf[dataoffset], colors, BufSize); err = PDERR_NOERR; /* all ok */ break; case 2 : /* Dump Buffer to Printer */ /* ct - 0. x - 0. y - # of rows sent (1 to NumRows). */ /* white-space strip */ ptrstart = &PD->pd_PrintBuf[dataoffset - NUMSTARTCMD]; if (PED->ped_YDotsInch == 72) { /* y range : 1 to 8 */ y = y * 3 - spacing; ptr = CompactBuf(ptrstart + NUMSTARTCMD, ptrstart, y, 1); } else if (PED->ped_YDotsInch == 144) { /* y range : 1 to 16 */ ptr = CompactBuf(ptrstart + NUMSTARTCMD, ptrstart, 2, 1); if (y > 1) { ptr = CompactBuf(&PD->pd_PrintBuf[ dataoffset + BufSize], ptr, y * 3 / 2 - 2, 0); } } else if (PED->ped_YDotsInch == 216) { /* y range : 1 to 24 */ ptr = CompactBuf(ptrstart + NUMSTARTCMD, ptrstart, 1, 1); if (y > 1) { ptr = CompactBuf(&PD->pd_PrintBuf[ dataoffset + BufSize], ptr, 1, 0); } if (y > 2) { ptr = CompactBuf(&PD->pd_PrintBuf[ dataoffset + BufSize * 2], ptr, y - 2, 0); } } err = (*(PD->pd_PWrite))(ptrstart, ptr - ptrstart); if (err == PDERR_NOERR) { dataoffset = (dataoffset == NUMSTARTCMD ? TwoBufSize : 0) + NUMSTARTCMD; } break; case 3 : /* Clear and Init Buffer */ /* ct - 0. x - 0. y - 0. */ ClearAndInit(&PD->pd_PrintBuf[dataoffset]); err = PDERR_NOERR; break; case 4 : /* Close Down */ /* ct - error code. x - io_Special flag from IODRPReq. y - 0. */ err = PDERR_NOERR; /* assume all ok */ /* if user did not cancel print */ if (ct != PDERR_CANCEL) { /* restore preferences pitch and margins */ if (PD->pd_Preferences.PrintPitch == ELITE) { StartBuf[PITCH] = 'M'; /* 12 cpi */ } else if (PD->pd_Preferences.PrintPitch == FINE) { StartBuf[CONDENSED] = '\017'; /* on */ } StartBuf[LMARG] = PD->pd_Preferences.PrintLeftMargin - 1; StartBuf[RMARG] = PD->pd_Preferences.PrintRightMargin; StartBuf[DIREC] = '0'; /* bi-directional */ err = (*(PD->pd_PWrite))(StartBuf, STARTLEN); } (*(PD->pd_PBothReady))(); if (PD->pd_PrintBuf != NULL) { FreeMem(PD->pd_PrintBuf, TotalBufSize); } break; case 5 : /* Pre-Master Initialization */ /* ct - 0 or pointer to IODRPReq structure. x - io_Special flag from IODRPReq. y - 0. */ /* kludge for sloppy tractor mechanism */ spacing = PD->pd_Preferences.PaperType == SINGLE ? 1 : 0; dpi_code = SetDensity(x & SPECIAL_DENSITYMASK); err = PDERR_NOERR; break; } return(err); } UBYTE *CompactBuf(ptrstart, ptr2start, y, flag) UBYTE *ptrstart, *ptr2start; long y; int flag; /* 0 - not first pass, !0 - first pass */ { static int x; UBYTE *ptr, *ptr2; long ct; int i; ptr2 = ptr2start; /* where to put the compacted data */ if (flag) { x = 0; /* flag no transfer required yet */ } for (ct=0; ct<NumColorBufs; ct++, ptrstart += ColorSize) { i = RowSize; ptr = ptrstart + i - 1; while (i > 0 && *ptr == 0) { i--; ptr--; } if (i != 0) { /* if data */ *(++ptr) = 13; /* <cr> */ ptr = ptrstart - NUMSTARTCMD; *ptr++ = 27; *ptr++ = 'r'; *ptr++ = colorcodes[ct]; /* color */ *ptr++ = 27; *ptr++ = dpi_code; /* density */ *ptr++ = i & 0xff; *ptr++ = i >> 8; /* size */ i += NUMTOTALCMD; if (x != 0) { /* if must transfer data */ /* get src start */ ptr = ptrstart - NUMSTARTCMD; do { /* transfer and update dest ptr */ *ptr2++ = *ptr++; } while (--i); } else { /* no transfer required */ ptr2 += i; /* update dest ptr */ } } if (i != RowSize + NUMTOTALCMD) { /* if compacted or 0 */ x = 1; /* flag that we need to transfer next time */ } } *ptr2++ = 13; /* cr */ *ptr2++ = 27; *ptr2++ = 'J'; *ptr2++ = y; /* y/216 lf */ return(ptr2); } ClearAndInit(ptr) UBYTE *ptr; { ULONG *lptr, i, j; /* Note : Since 'NUMTOTALCMD + NUMLFCMD' is > 3 bytes if is safe to do the following to speed things up. */ i = TwoBufSize - NUMTOTALCMD - NUMLFCMD; j = (ULONG)ptr; if (!(j & 1)) { /* if on a word boundary, clear by longs */ i = (i + 3) / 4; lptr = (ULONG *)ptr; do { *lptr++ = 0; } while (--i); } else { /* clear by bytes */ do { *ptr++ = 0; } while (--i); } return(0); }
EpsonX: transfer.asm
********************************************************************** * * Transfer routine for EpsonX * ********************************************************************** INCLUDE "exec/types.i" INCLUDE "intuition/intuition.i" INCLUDE "devices/printer.i" INCLUDE "devices/prtbase.i" INCLUDE "devices/prtgfx.i" XREF _PD XREF _PED XREF _LVODebug XREF _AbsExecBase XDEF _Transfer SECTION printer,CODE _Transfer: ; Transfer(PInfo, y, ptr, colors, BufOffset) ; struct PrtInfo *PInfo 4-7 ; UWORD y; 8-11 ; UBYTE *ptr; 12-15 ; UWORD *colors; 16-19 ; ULONG BufOffset 20-23 movem.l d2-d6/a2-a4,-(sp) ;save regs used movea.l 36(sp),a0 ;a0 = PInfo move.l 40(sp),d0 ;d0 = y movea.l 44(sp),a1 ;a1 = ptr movea.l 48(sp),a2 ;a2 = colors move.l 52(sp),d1 ;d1 = BufOffset move.l d0,d3 ;save y moveq.l #3,d2 and.w d0,d2 ;d2 = y & 3 lsl.w #2,d2 ;d2 = (y & 3) << 2 movea.l pi_dmatrix(a0),a3 ;a3 = dmatrix adda.l d2,a3 ;a3 = dmatrix + ((y & 3) << 2) movea.l _PED,a4 ;a4 = ptr to PED cmpi.w #216,ped_YDotsInch(a4) ;triple interleaving? bne.s 10$ ;no divu.w #3,d0 ;y /= 3 swap.w d0 ;d0 = y % 3 mulu.w d0,d1 ;BufOffset *= y % 3 swap.w d0 ;d0 = y / 3 bra.s 30$ 10$: cmpi.w #144,ped_YDotsInch(a4) ;double interleaving? bne.s 20$ ;no, clear BufOffset asr.w #1,d0 ;y /= 2 btst #0,d3 ;odd pass? bne.s 30$ ;no, dont clear BufOffset 20$: moveq.l #0,d1 ;BufOffset = 0 30$: move.w d0,d6 not.b d6 ;d6 = bit to set adda.l d1,a1 ;ptr += BufOffset movea.l _PD,a4 ;a4 = ptr to PD cmpi.w #SHADE_COLOR,pd_Preferences+pf_PrintShade(a4) ;color dump? bne not_color ;no color: ; a0 - PInfo ; a1 - ptr (ptr + BufOffset) ; a2 - colors ; a3 - dmatrix ptr ; d0 - y ; d1 - BufOffset ; d6 - bit to set movem.l d7/a5-a6,-(sp) ;save regs used movea.l a1,a4 movea.l a1,a5 movea.l a1,a6 adda.w (a2)+,a1 ;a1 = ptr + colors[0] (bptr) adda.w (a2)+,a4 ;a4 = ptr + colors[1] (yptr) adda.w (a2)+,a5 ;a5 = ptr + colors[2] (mptr) adda.w (a2)+,a6 ;a6 = ptr + colors[3] (cptr) ; move.l a6,-(sp) ; move.l _AbsExecBase,a6 ; jsr _LVODebug(a6) ; move.l (sp)+,a6 movea.l pi_ColorInt(a0),a2 ;a2 = ColorInt ptr move.w pi_width(a0),width ;# of pixels to do move.w pi_xpos(a0),d2 ;d2 = x movea.l pi_ScaleX(a0),a0 ;a0 = ScaleX (sxptr) move.b d6,d7 ;d7 = bit to set ; a0 - sxptr ; a1 - bptr ; a2 - ColorInt ptr ; a3 - dmatrix ptr ; a4 - yptr ; a5 - mptr ; a6 - cptr ; d1 - Black ; d2 - x ; d3 - dvalue (dmatrix[x & 3]) ; d4 - Yellow ; d5 - Magenta ; d6 - Cyan ; d7 - bit to set cwidth_loop: move.b PCMBLACK(a2),d1 ;d1 = Black move.b PCMYELLOW(a2),d4 ;d4 = Yellow move.b PCMMAGENTA(a2),d5 ;d5 = Magenta move.b PCMCYAN(a2),d6 ;d6 = Cyan addq.l #ce_SIZEOF,a2 ;advance to next entry move.w (a0)+,sx ;# of times to use this pixel csx_loop: moveq.l #3,d3 and.w d2,d3 ;d3 = x & 3 move.b 0(a3,d3.w),d3 ;d3 = dmatrix[x & 3] black: cmp.b d3,d1 ;render black? ble.s yellow ;no, try ymc bset.b d7,0(a1,d2.w) ;set black pixel bra.s csx_end yellow: cmp.b d3,d4 ;render yellow pixel? ble.s magenta ;no. bset.b d7,0(a4,d2.w) ;set yellow pixel magenta: cmp.b d3,d5 ;render magenta pixel? ble.s cyan ;no. bset.b d7,0(a5,d2.w) ;set magenta pixel cyan: cmp.b d3,d6 ;render cyan pixel? ble.s csx_end ;no, skip to next pixel. bset.b d7,0(a6,d2.w) ;clear cyan pixel csx_end: addq.w #1,d2 ;x++ subq.w #1,sx ;sx-- bne.s csx_loop subq.w #1,width ;width-- bne.s cwidth_loop movem.l (sp)+,d7/a5-a6 ;restore regs used bra exit not_color: ; a0 - PInfo ; a1 - ptr ; a2 - colors ; a3 - dmatrix ptr ; d0 - y ; d6 - bit to set adda.w (a2),a1 ;a1 = ptr + colors[0] move.w pi_width(a0),d1 ;d1 = width subq.w #1,d1 ;adjust for dbra move.w pi_threshold(a0),d3 ;d3 = threshold, thresholding? beq.s grey_scale ;no, grey-scaling threshold: ; a0 - PInfo ; a1 - ptr ; a3 - dmatrix ptr ; d1 - width-1 ; d3 - threshold ; d6 - bit to set eori.b #15,d3 ;d3 = dvalue movea.l pi_ColorInt(a0),a2 ;a2 = ColorInt ptr move.w pi_xpos(a0),d2 ;d2 = x movea.l pi_ScaleX(a0),a0 ;a0 = ScaleX (sxptr) adda.w d2,a1 ;ptr += x ; a0 - sxptr ; a1 - ptr ; a2 - ColorInt ptr ; a3 - dmatrix ptr (NOT USED) ; d1 - width ; d3 - dvalue ; d4 - Black ; d5 - sx ; d6 - bit to set twidth_loop: move.b PCMBLACK(a2),d4 ;d4 = Black addq.l #ce_SIZEOF,a2 ;advance to next entry move.w (a0)+,d5 ;d5 = # of times to use this pixel cmp.b d3,d4 ;render this pixel? ble.s tsx_end ;no, skip to next pixel. subq.w #1,d5 ;adjust for dbra tsx_render: ;yes, render this pixel sx times bset.b d6,(a1) ;*(ptr) |= bit; adda.w #1,a1 ;ptr++ dbra d5,tsx_render ;sx-- dbra d1,twidth_loop ;width-- bra.s exit ;all done tsx_end: adda.w d5,a1 ;ptr += sx dbra d1,twidth_loop ;width-- bra.s exit grey_scale: ; a0 - PInfo ; a1 - ptr ; a3 - dmatrix ptr ; d0 - y ; d1 - width-1 ; d6 - bit to set movea.l pi_ColorInt(a0),a2 ;a2 = ColorInt ptr move.w pi_xpos(a0),d2 ;d2 = x movea.l pi_ScaleX(a0),a0 ;a0 = ScaleX (sxptr) ; a0 - sxptr ; a1 - ptr ; a2 - ColorInt ptr ; a3 - dmatrix ptr ; d1 - width ; d2 - x ; d3 - dvalue (dmatrix[x & 3]) ; d4 - Black ; d5 - sx ; d6 - bit to set gwidth_loop: move.b PCMBLACK(a2),d4 ;d4 = Black addq.l #ce_SIZEOF,a2 ;advance to next entry move.w (a0)+,d5 ;d5 = # of times to use this pixel subq.w #1,d5 ;adjust for dbra gsx_loop: moveq.l #3,d3 and.w d2,d3 ;d3 = x & 3 move.b 0(a3,d3.w),d3 ;d3 = dmatrix[x & 3] cmp.b d3,d4 ;render this pixel? ble.s gsx_end ;no, skip to next pixel. bset.b d6,0(a1,d2.w) ;*(ptr + x) |= bit gsx_end addq.w #1,d2 ;x++ dbra d5,gsx_loop ;sx-- dbra d1,gwidth_loop ;width-- exit: movem.l (sp)+,d2-d6/a2-a4 ;restore regs used moveq.l #0,d0 ;flag all ok rts ;goodbye sx dc.w 0 width dc.w 0 END
EpsonX: transfer.c
/* C-language Transfer routine for EpsonX driver. */ #include <exec/types.h> #include <devices/printer.h> #include <devices/prtbase.h> #include <devices/prtgfx.h> Transfer(PInfo, y, ptr, colors, BufOffset) struct PrtInfo *PInfo; UWORD y; /* row # */ UBYTE *ptr; /* ptr to buffer */ UWORD *colors; /* indexes to color buffers */ ULONG BufOffset; /* used for interleaved printing */ { extern struct PrinterData *PD; extern struct PrinterExtendedData *PED; static UWORD bit_table[8] = {128, 64, 32, 16, 8, 4, 2, 1}; union colorEntry *ColorInt; UBYTE *bptr, *yptr, *mptr, *cptr, Black, Yellow, Magenta, Cyan; UBYTE *dmatrix, dvalue, threshold; UWORD x, width, sx, *sxptr, color, bit, x3; /* printer non-specific, MUST DO FOR EVERY PRINTER */ x = PInfo->pi_xpos; ColorInt = PInfo->pi_ColorInt; sxptr = PInfo->pi_ScaleX; width = PInfo->pi_width; /* printer specific */ if (PED->ped_YDotsInch == 216) { BufOffset *= y % 3; y /= 3; } else if (PED->ped_YDotsInch == 144) { BufOffset *= y & 1; y /= 2; } else { BufOffset = 0; } bit = bit_table[y & 7]; bptr = ptr + colors[0] + BufOffset; yptr = ptr + colors[1] + BufOffset; mptr = ptr + colors[2] + BufOffset; cptr = ptr + colors[3] + BufOffset; /* pre-compute threshold; are we thresholding? */ if (threshold = PInfo->pi_threshold) { /* thresholding */ dvalue = threshold ^ 15; bptr += x; do { /* for all source pixels */ /* pre-compute intensity values for Black component */ Black = ColorInt->colorByte[PCMBLACK]; ColorInt++; sx = *sxptr++; do { /* use this pixel 'sx' times */ if (Black > dvalue) { *bptr |= bit; } bptr++; /* done 1 more printer pixel */ } while (--sx); } while (--width); } else { /* not thresholding, pre-compute ptr to dither matrix */ dmatrix = PInfo->pi_dmatrix + ((y & 3) << 2); if (PD->pd_Preferences.PrintShade == SHADE_GREYSCALE) { do { /* for all source pixels */ /* pre-compute intensity values for Black */ Black = ColorInt->colorByte[PCMBLACK]; ColorInt++; sx = *sxptr++; do { /* use this pixel 'sx' times */ if (Black > dmatrix[x & 3]) { *(bptr + x) |= bit; } x++; /* done 1 more printer pixel */ } while (--sx); } while (--width); } else { /* color */ do { /* for all source pixels */ /* compute intensity values for each color */ Black = ColorInt->colorByte[PCMBLACK]; Yellow = ColorInt->colorByte[PCMYELLOW]; Magenta = ColorInt->colorByte[PCMMAGENTA]; Cyan = ColorInt->colorByte[PCMCYAN]; ColorInt++; sx = *sxptr++; do { /* use this pixel 'sx' times */ x3 = x >> 3; dvalue = dmatrix[x & 3]; if (Black > dvalue) { *(bptr + x) |= bit; } else { /* black not rendered */ if (Yellow > dvalue) { *(yptr + x) |= bit; } if (Magenta > dvalue) { *(mptr + x) |= bit; } if (Cyan > dvalue) { *(cptr + x) |= bit; } } ++x; /* done 1 more printer pixel */ } while (--sx); } while (--width); } } }
EpsonX: density.c
/* Density module for EpsonX driver. */ #include <exec/types.h> #include "devices/printer.h" #include "devices/prtbase.h" SetDensity(density_code) ULONG density_code; { extern struct PrinterData *PD; extern struct PrinterExtendedData *PED; /* SPECIAL_DENSITY 0 1 2 3 4 5 6 7 */ static int XDPI[8] = {120, 120, 120, 240, 120, 240, 240, 240}; static int YDPI[8] = {72, 72, 144, 72, 216, 144, 216, 216}; static char codes[8] = {'L', 'L', 'L', 'Z', 'L', 'Z', 'Z', 'Z'}; PED->ped_MaxColumns = PD->pd_Preferences.PaperSize == W_TRACTOR ? 136 : 80; density_code /= SPECIAL_DENSITY1; /* default is 80 chars (8.0 in.), W_TRACTOR is 136 chars (13.6 in.) */ PED->ped_MaxXDots = (XDPI[density_code] * PED->ped_MaxColumns) / 10; PED->ped_XDotsInch = XDPI[density_code]; PED->ped_YDotsInch = YDPI[density_code]; if ((PED->ped_YDotsInch = YDPI[density_code]) == 216) { PED->ped_NumRows = 24; } else if (PED->ped_YDotsInch == 144) { PED->ped_NumRows = 16; } else { PED->ped_NumRows = 8; } return((int)codes[density_code]); }
HP_Laserjet
The driver for the HP_LaserJet can be generated with the following Makefile.
LC = lc:lc ASM = lc:asm CFLAGS = -iINCLUDE: -b0 -d0 -v ASMFLAGS = -iINCLUDE: LINK = lc:blink LIB = lib:amiga.lib+lib:lc.lib OBJ = printertag.o+init.o+data.o+dospecial.o+render.o+transfer.o+density.o TARGET = hp_laserjet @$(LC) $(CFLAGS) $* $(TARGET): printertag.o init.o data.o dospecial.o render.o density.o transfer.o @$(LINK) <WITH < FROM $(OBJ) TO $(TARGET) LIBRARY $(LIB) NODEBUG SC SD VERBOSE MAP $(TARGET).map H < init.o: init.asm @$(ASM) $(ASMFLAGS) init.asm printertag.o: printertag.asm hp_rev.i @$(ASM) $(ASMFLAGS) printertag.asm transfer.o: transfer.asm @$(ASM) $(ASMFLAGS) transfer.asm dospecial.o: dospecial.c data.o: data.c density.o: density.c render.o: render.c
HP_Laserjet: macros.i
********************************************************************** * * printer device macro definitions * ********************************************************************** *------ external definition macros ----------------------------------- XREF_EXE MACRO XREF _LVO\1 ENDM XREF_DOS MACRO XREF _LVO\1 ENDM XREF_GFX MACRO XREF _LVO\1 ENDM XREF_ITU MACRO XREF _LVO\1 ENDM *------ library dispatch macros -------------------------------------- CALLEXE MACRO CALLLIB _LVO\1 ENDM LINKEXE MACRO LINKLIB _LVO\1,_SysBase ENDM LINKDOS MACRO LINKLIB _LVO\1,_DOSBase ENDM LINKGFX MACRO LINKLIB _LVO\1,_GfxBase ENDM LINKITU MACRO LINKLIB _LVO\1,_IntuitionBase ENDM
HP_Laserjet: printertag.asm
********************************************************************** * * printer device dependent code tag * ********************************************************************** SECTION printer *------ Included Files ----------------------------------------------- INCLUDE "exec/types.i" INCLUDE "exec/nodes.i" INCLUDE "exec/strings.i" INCLUDE "hp_rev.i" INCLUDE "devices/prtbase.i" *------ Imported Names ----------------------------------------------- XREF _Init XREF _Expunge XREF _Open XREF _Close XREF _CommandTable XREF _PrinterSegmentData XREF _DoSpecial XREF _Render XREF _ExtendedCharTable XREF _ConvFunc *------ Exported Names ----------------------------------------------- XDEF _PEDData ********************************************************************** MOVEQ #0,D0 ; show error for OpenLibrary() RTS DC.W VERSION DC.W REVISION _PEDData: DC.L printerName DC.L _Init DC.L _Expunge DC.L _Open DC.L _Close DC.B PPC_BWGFX ; PrinterClass DC.B PCC_BW ; ColorClass DC.B 0 ; MaxColumns DC.B 0 ; NumCharSets DC.W 1 ; NumRows DC.L 600 ; MaxXDots DC.L 795 ; MaxYDots DC.W 75 ; XDotsInch DC.W 75 ; YDotsInch DC.L _CommandTable ; Commands DC.L _DoSpecial DC.L _Render DC.L 30 ; Timeout DC.L _ExtendedCharTable ; 8BitChars DS.L 1 ; PrintMode (reserve space) DC.L _ConvFunc ; ptr to char conversion function printerName: dc.b 'HP_LaserJet',0 END
HP_Laserjet: hp_rev.i
VERSION EQU 35 REVISION EQU 1
HP_Laserjet: init.asm
********************************************************************** * * printer device functions * ********************************************************************** SECTION printer *------ Included Files ----------------------------------------------- INCLUDE "exec/types.i" INCLUDE "exec/nodes.i" INCLUDE "exec/lists.i" INCLUDE "exec/memory.i" INCLUDE "exec/ports.i" INCLUDE "exec/libraries.i" INCLUDE "macros.i" *------ Imported Functions ------------------------------------------- XREF_EXE CloseLibrary XREF_EXE OpenLibrary XREF _AbsExecBase XREF _PEDData *------ Exported Globals --------------------------------------------- XDEF _Init XDEF _Expunge XDEF _Open XDEF _PD XDEF _PED XDEF _SysBase XDEF _DOSBase XDEF _GfxBase XDEF _IntuitionBase ********************************************************************** SECTION printer,DATA _PD DC.L 0 _PED DC.L 0 _SysBase DC.L 0 _DOSBase DC.L 0 _GfxBase DC.L 0 _IntuitionBase DC.L 0 ********************************************************************** SECTION printer,CODE _Init: MOVE.L 4(A7),_PD LEA _PEDData(PC),A0 MOVE.L A0,_PED MOVE.L A6,-(A7) MOVE.L _AbsExecBase,A6 MOVE.L A6,_SysBase * ;------ open the dos library LEA DLName(PC),A1 MOVEQ #0,D0 CALLEXE OpenLibrary MOVE.L D0,_DOSBase BEQ initDLErr * ;------ open the graphics library LEA GLName(PC),A1 MOVEQ #0,D0 CALLEXE OpenLibrary MOVE.L D0,_GfxBase BEQ initGLErr * ;------ open the intuition library LEA ILName(PC),A1 MOVEQ #0,D0 CALLEXE OpenLibrary MOVE.L D0,_IntuitionBase BEQ initILErr MOVEQ #0,D0 pdiRts: MOVE.L (A7)+,A6 RTS initPAErr: MOVE.L _IntuitionBase,A1 LINKEXE CloseLibrary initILErr: MOVE.L _GfxBase,A1 LINKEXE CloseLibrary initGLErr: MOVE.L _DOSBase,A1 LINKEXE CloseLibrary initDLErr: MOVEQ #-1,D0 BRA.S pdiRts ILName: DC.B 'intuition.library' DC.B 0 DLName: DC.B 'dos.library' DC.B 0 GLName: DC.B 'graphics.library' DC.B 0 DS.W 0 *--------------------------------------------------------------------- _Expunge: MOVE.L _IntuitionBase,A1 LINKEXE CloseLibrary MOVE.L _GfxBase,A1 LINKEXE CloseLibrary MOVE.L _DOSBase,A1 LINKEXE CloseLibrary *--------------------------------------------------------------------- _Open: MOVEQ #0,D0 RTS END
HP_Laserjet: data.c
/* Data.c table for HP_LaserJet (Plus and II compatible) driver. */ char *CommandTable[] = { "\375\033E\375",/* 00 aRIS reset */ "\377", /* 01 aRIN initialize */ "\012", /* 02 aIND linefeed */ "\015\012", /* 03 aNEL CRLF */ "\033&a-1R", /* 04 aRI reverse LF */ /* 05 aSGR0 normal char set */ "\033&d@\033(sbS", "\033(s1S", /* 06 aSGR3 italics on */ "\033(sS", /* 07 aSGR23 italics off */ "\033&dD", /* 08 aSGR4 underline on */ "\033&d@", /* 09 aSGR24 underline off */ "\033(s5B", /* 10 aSGR1 boldface on */ "\033(sB", /* 11 aSGR22 boldface off */ "\377", /* 12 aSFC set foreground color */ "\377", /* 13 aSBC set background color */ "\033(s10h1T", /* 14 aSHORP0 normal pitch */ "\033(s12h2T", /* 15 aSHORP2 elite on */ "\033(s10h1T", /* 16 aSHORP1 elite off */ "\033(s15H", /* 17 aSHORP4 condensed fine on */ "\033(s10H", /* 18 aSHORP3 condensed fine off */ "\377", /* 19 aSHORP6 enlarge on */ "\377", /* 20 aSHORP5 enlarge off */ "\033(s7B", /* 21 aDEN6 shadow print on */ "\033(sB", /* 22 aDEN5 shadow print off */ "\033(s3B", /* 23 aDEN4 double strike on */ "\033(sB", /* 24 aDEN3 double strike off */ "\377", /* 25 aDEN2 NLQ on */ "\377", /* 26 aDEN1 NLQ off */ "\377", /* 27 aSUS2 superscript on */ "\377", /* 28 aSUS1 superscript off */ "\377", /* 29 aSUS4 subscript on */ "\377", /* 30 aSUS3 subscript off */ "\377", /* 31 aSUS0 normalize the line */ "\033&a-.5R", /* 32 aPLU partial line up */ "\033=", /* 33 aPLD partial line down */ "\033(s3T", /* 34 aFNT0 Typeface 0 */ "\033(s0T", /* 35 aFNT1 Typeface 1 */ "\033(s1T", /* 36 aFNT2 Typeface 2 */ "\033(s2T", /* 37 aFNT3 Typeface 3 */ "\033(s4T", /* 38 aFNT4 Typeface 4 */ "\033(s5T", /* 39 aFNT5 Typeface 5 */ "\033(s6T", /* 40 aFNT6 Typeface 6 */ "\033(s7T", /* 41 aFNT7 Typeface 7 */ "\033(s8T", /* 42 aFNT8 Typeface 8 */ "\033(s9T", /* 43 aFNT9 Typeface 9 */ "\033(s10T", /* 44 aFNT10 Typeface 10 */ "\033(s1P", /* 45 aPROP2 proportional on */ "\033(sP", /* 46 aPROP1 proportional off */ "\033(sP", /* 47 aPROP0 proportional clear */ "\377", /* 48 aTSS set proportional offset */ "\377", /* 49 aJFY5 auto left justify */ "\377", /* 50 aJFY7 auto right justify */ "\377", /* 51 aJFY6 auto full jusitfy */ "\377", /* 52 aJFY0 auto jusity off */ "\377", /* 53 aJFY3 letter space */ "\377", /* 54 aJFY1 word fill */ "\033&l8D", /* 55 aVERP0 1/8" line spacing */ "\033&l6D", /* 56 aVERP1 1/6" line spacing */ "\377", /* 57 aSLPP set form length */ "\033&l1L", /* 58 aPERF perf skip n (n > 0) */ "\033&lL", /* 59 aPERF0 perf skip off */ "\377", /* 60 aLMS set left margin */ "\377", /* 61 aRMS set right margin */ "\377", /* 62 aTMS set top margin */ "\377", /* 63 aBMS set bottom margin */ "\377", /* 64 aSTBM set T&B margins */ "\377", /* 65 aSLRM set L&R margins */ "\0339\015", /* 66 aCAM clear margins */ "\377", /* 67 aHTS set horiz tab */ "\377", /* 68 aVTS set vert tab */ "\377", /* 69 aTBC0 clear horiz tab */ "\377", /* 70 aTBC3 clear all horiz tabs */ "\377", /* 71 aTBC1 clear vert tab */ "\377", /* 72 aTBC4 clear all vert tabs */ "\377", /* 73 aTBCALL clear all h & v tabs */ "\377", /* 74 aTBSALL set default tabs */ "\377", /* 75 aEXTEND extended commands */ "\377" /* 76 aRAW next 'n' chars are raw */ }; char *ExtendedCharTable[] = { /* " ", "!", "c", "L", "o", "Y", "|", "S", "\"", "c", "a", "<", "~", "-", "r", "-", "*", "+", "2", "3", "'", "u", "P", ".", ",", "1", "o", ">", "/", "/", "/", "?", "A", "A", "A", "A", "A", "A", "A", "C", "E", "E", "E", "E", "I", "I", "I", "I", "D", "N", "O", "O", "O", "O", "O", "x", "O", "U", "U", "U", "U", "Y", "P", "B", "a", "a", "a", "a", "a", "a", "a", "c", "e", "e", "e", "e", "i", "i", "i", "i", "d", "n", "o", "o", "o", "o", "o", "/", "o", "u", "u", "u", "u", "y", "p", "y" */ " ", "\270", "\277", "\273", "\272", "\274", "|", "\275", "\253", "c", "\371", "\373", "~", "\366", "r", "\260", "\263", "\376", "2", "3", "\250", "\363", "\364", "\362", ",", "1", "\372", "\375", "\367", "\370", "\365", "\271", "\241", "\340", "\242", "\341", "\330", "\320", "\323", "\264", "\243", "\334", "\244", "\245", "\346", "\345", "\246", "\247", "\343", "\266", "\350", "\347", "\337", "\351", "\332", "x", "\322", "\255", "\355", "\256", "\333", "\261", "\360", "\336", "\310", "\304", "\300", "\342", "\314", "\324", "\327", "\265", "\311", "\305", "\301", "\315", "\331", "\325", "\321", "\335", "\344", "\267", "\312", "\306", "\302", "\352", "\316", "-\010:", "\326", "\313", "\307", "\303", "\317", "\262", "\361", "\357" };
HP_Laserjet: dospecial.c
/* DoSpecial for HP_LaserJet driver. */ #include "exec/types.h" #include "devices/printer.h" #include "devices/prtbase.h" #define LPI 7 #define CPI 15 #define QUALITY 17 #define INIT_LEN 30 #define LPP 7 #define FORM_LEN 11 #define LEFT_MARG 3 #define RIGHT_MARG 7 #define MARG_LEN 12 DoSpecial(command, outputBuffer, vline, currentVMI, crlfFlag, Parms) char outputBuffer[]; UWORD *command; BYTE *vline; BYTE *currentVMI; BYTE *crlfFlag; UBYTE Parms[]; { extern struct PrinterData *PD; extern struct PrinterExtendedData *PED; static UWORD textlength, topmargin; int x, y, j; static char initThisPrinter[INIT_LEN] = "\033&d@\033&l6D\033(s0b10h1q0p0s3t0u12V"; static char initForm[FORM_LEN] = "\033&l002e000F"; static char initMarg[MARG_LEN] = "\033&a000l000M\015"; static char initTMarg[] = "\033&l000e000F"; x = y = j = 0; if (*command == aRIN) { while(x < INIT_LEN) { outputBuffer[x] = initThisPrinter[x]; x++; } outputBuffer[x++] = '\015'; if (PD->pd_Preferences.PrintSpacing == EIGHT_LPI) { outputBuffer[LPI] = '8'; } if (PD->pd_Preferences.PrintPitch == ELITE) { outputBuffer[CPI] = '2'; } else if (PD->pd_Preferences.PrintPitch == FINE) { outputBuffer[CPI] = '5'; } if (PD->pd_Preferences.PrintQuality == LETTER) { outputBuffer[QUALITY] = '2'; } j = x; /* set the formlength = textlength, top margin = 2 */ textlength = PD->pd_Preferences.PaperLength; topmargin = 2; while (y < FORM_LEN) { outputBuffer[x++] = initForm[y++]; } numberString(textlength, j + LPP, outputBuffer); Parms[0] = PD->pd_Preferences.PrintLeftMargin; Parms[1] = PD->pd_Preferences.PrintRightMargin; *command = aSLRM; } if (*command == aSLRM) { j = x; y = 0; while(y < MARG_LEN) { outputBuffer[x++] = initMarg[y++]; } numberString(Parms[0] - 1, j + LEFT_MARG, outputBuffer); numberString(Parms[1] - 1, j + RIGHT_MARG, outputBuffer); return(x); } if ((*command == aSUS2) && (*vline == 0)) { *command = aPLU; *vline = 1; return(0); } if ((*command == aSUS2) && (*vline < 0)) { *command = aRI; *vline = 1; return(0); } if ((*command == aSUS1) && (*vline > 0)) { *command = aPLD; *vline = 0; return(0); } if ((*command == aSUS4) && (*vline == 0)) { *command = aPLD; *vline = -1; return(0); } if ((*command == aSUS4) && (*vline > 0)) { *command = aIND; *vline = -1; return(0); } if ((*command == aSUS3) && (*vline < 0)) { *command = aPLU; *vline = 0; return(0); } if(*command == aSUS0) { if (*vline > 0) { *command = aPLD; } if (*vline < 0) { *command = aPLU; } *vline = 0; return(0); } if (*command == aPLU) { (*vline)++; return(0); } if (*command == aPLD){ (*vline)--; return(0); } if (*command == aSTBM) { if (Parms[0] == 0) { Parms[0] = topmargin; } else { topmargin = --Parms[0]; } if (Parms[1] == 0) { Parms[1] = textlength; } else { textlength=Parms[1]; } while (x < 11) { outputBuffer[x] = initTMarg[x]; x++; } numberString(Parms[0], 3, outputBuffer); numberString(Parms[1] - Parms[0], 7, outputBuffer); return(x); } if (*command == aSLPP) { while(x < 11) { outputBuffer[x] = initForm[x]; x++; } /*restore textlength, margin*/ numberString(topmargin, 3, outputBuffer); numberString(textlength, 7, outputBuffer); return(x); } if (*command == aRIS) { PD->pd_PWaitEnabled = 253; } return(0); } numberString(Param, x, outputBuffer) UBYTE Param; int x; char outputBuffer[]; { if (Param > 199) { outputBuffer[x++] = '2'; Param -= 200; } else if (Param > 99) { outputBuffer[x++] = '1'; Param -= 100; } else { outputBuffer[x++] = '0'; /* always return 3 digits */ } if (Param > 9) { outputBuffer[x++] = Param / 10 + '0'; } else { outputBuffer[x++] = '0'; } outputBuffer[x++] = Param % 10 + '0'; } ConvFunc(buf, c, flag) char *buf, c; int flag; /* expand lf into lf/cr flag (0-yes, else no ) */ { if (c == '\014') { /* if formfeed (page eject) */ PED->ped_PrintMode = 0; /* no data to print */ } return(-1); /* pass all chars back to the printer device */ } Close(ior) struct printerIO *ior; { if (PED->ped_PrintMode) { /* if data has been printed */ (*(PD->pd_PWrite))("\014",1); /* eject page */ (*(PD->pd_PBothReady))(); /* wait for it to finish */ PED->ped_PrintMode = 0; /* no data to print */ } return(0); }
HP_Laserjet: render.c
/* HP_LaserJet driver. */ #include <exec/types.h> #include <exec/nodes.h> #include <exec/lists.h> #include <exec/memory.h> #include <devices/prtbase.h> #include <devices/printer.h> #define NUMSTARTCMD 7 /* # of cmd bytes before binary data */ #define NUMENDCMD 0 /* # of cmd bytes after binary data */ #define NUMTOTALCMD (NUMSTARTCMD + NUMENDCMD) /* total of above */ extern SetDensity(); /* 00-04 \033&l0L perf skip mode off 05-11 \033*t075R set raster graphics resolution (dpi) 12-16 \033*r0A start raster graphics */ char StartCmd[18] = "\033&l0L\033*t075R\033*r0A"; Render(ct, x, y, status) long ct, x, y, status; { extern void *AllocMem(), FreeMem(); extern struct PrinterData *PD; extern struct PrinterExtendedData *PED; static UWORD RowSize, BufSize, TotalBufSize, dataoffset; static UWORD huns, tens, ones; /* used to program buffer size */ UBYTE *ptr, *ptrstart; int i, err; err=PDERR_NOERR; switch(status) { case 0 : /* Master Initialization */ /* ct - pointer to IODRPReq structure. x - width of printed picture in pixels. y - height of printed picture in pixels. */ RowSize = (x + 7) / 8; BufSize = RowSize + NUMTOTALCMD; TotalBufSize = BufSize * 2; PD->pd_PrintBuf = AllocMem(TotalBufSize, MEMF_PUBLIC); if (PD->pd_PrintBuf == NULL) { err = PDERR_BUFFERMEMORY; /* no mem */ } else { ptr = PD->pd_PrintBuf; *ptr++ = 27; *ptr++ = '*'; *ptr++ = 'b'; /* transfer raster graphics */ *ptr++ = huns | '0'; *ptr++ = tens | '0'; *ptr++ = ones | '0'; /* printout width */ *ptr = 'W'; /* terminator */ ptr = &PD->pd_PrintBuf[BufSize]; *ptr++ = 27; *ptr++ = '*'; *ptr++ = 'b'; /* transfer raster graphics */ *ptr++ = huns | '0'; *ptr++ = tens | '0'; *ptr++ = ones | '0'; /* printout width */ *ptr = 'W'; /* terminator */ dataoffset = NUMSTARTCMD; /* perf skip mode off, set dpi, start raster gfx */ err = (*(PD->pd_PWrite))(StartCmd, 17); } break; case 1 : /* Scale, Dither and Render */ /* ct - pointer to PrtInfo structure. x - 0. y - row # (0 to Height - 1). */ Transfer(ct, y, &PD->pd_PrintBuf[dataoffset]); err = PDERR_NOERR; /* all ok */ break; case 2 : /* Dump Buffer to Printer */ /* ct - 0. x - 0. y - # of rows sent (1 to NumRows). White-space strip. */ i = RowSize; ptrstart = &PD->pd_PrintBuf[dataoffset - NUMSTARTCMD]; ptr = ptrstart + NUMSTARTCMD + i - 1; while (i > 0 && *ptr == 0) { i--; ptr--; } ptr = ptrstart + 3; /* get ptr to density info */ *ptr++ = (huns = i / 100) | '0'; *ptr++ = (i - huns * 100) / 10 | '0'; *ptr = i % 10 | '0'; /* set printout width */ err = (*(PD->pd_PWrite))(ptrstart, i + NUMTOTALCMD); if (err == PDERR_NOERR) { dataoffset = (dataoffset == NUMSTARTCMD ? BufSize : 0) + NUMSTARTCMD; } break; case 3 : /* Clear and Init Buffer */ /* ct - 0. x - 0. y - 0. */ ptr = &PD->pd_PrintBuf[dataoffset]; i = RowSize; do { *ptr++ = 0; } while (--i); break; case 4 : /* Close Down */ /* ct - error code. x - io_Special flag from IODRPReq struct y - 0. */ err = PDERR_NOERR; /* assume all ok */ /* if user did not cancel the print */ if (ct != PDERR_CANCEL) { /* end raster graphics, perf skip mode on */ if ((err = (*(PD->pd_PWrite)) ("\033*rB\033&l1L", 9)) == PDERR_NOERR) { /* if want to unload paper */ if (!(x & SPECIAL_NOFORMFEED)) { /* eject paper */ err = (*(PD->pd_PWrite)) ("\014", 1); } } } /* flag that there is no alpha data waiting that needs a formfeed (since we just did one) */ PED->ped_PrintMode = 0; /* wait for both buffers to empty */ (*(PD->pd_PBothReady))(); if (PD->pd_PrintBuf != NULL) { FreeMem(PD->pd_PrintBuf, TotalBufSize); } break; case 5 : /* Pre-Master Initialization */ /* ct - 0 or pointer to IODRPReq structure. x - io_Special flag from IODRPReq struct y - 0. */ /* select density */ SetDensity(x & SPECIAL_DENSITYMASK); break; } return(err); }
HP_Laserjet: density.c
/* Density module for HP_LaserJet */ #include <exec/types.h> #include <devices/printer.h> #include <devices/prtbase.h> SetDensity(density_code) ULONG density_code; { extern struct PrinterData *PD; extern struct PrinterExtendedData *PED; extern char StartCmd[]; /* SPECIAL_DENSITY 0 1 2 3 4 5 6 7 */ static int XDPI[8] = {75, 75, 100, 150, 300, 300, 300, 300}; static char codes[8][3] = { {'0','7','5'},{'0','7','5'},{'1','0','0'},{'1','5','0'}, {'3','0','0'},{'3','0','0'},{'3','0','0'},{'3','0','0'}, }; density_code /= SPECIAL_DENSITY1; PED->ped_MaxXDots = XDPI[density_code] * 8; /* 8 inches */ /* default is 10.0, US_LEGAL is 14.0 */ PED->ped_MaxYDots = PD->pd_Preferences.PaperSize == US_LEGAL ? 14 : 10; PED->ped_MaxYDots *= XDPI[density_code]; PED->ped_XDotsInch = PED->ped_YDotsInch = XDPI[density_code]; StartCmd[8] = codes[density_code][0]; StartCmd[9] = codes[density_code][1]; StartCmd[10] = codes[density_code][2]; }
HP_Laserjet transfer.c
/* Example transfer routine for HP_LaserJet driver. Transfer() should be written in assembly code for speed */ #include <exec/types.h> #include <devices/prtgfx.h> Transfer(PInfo, y, ptr) struct PrtInfo *PInfo; UWORD y; /* row # */ UBYTE *ptr; /* ptr to buffer */ { static UBYTE bit_table[] = {128, 64, 32, 16, 8, 4, 2, 1}; UBYTE *dmatrix, Black, dvalue, threshold; union colorEntry *ColorInt; UWORD x, width, sx, *sxptr, bit; /* pre-compute */ /* printer non-specific, MUST DO FOR EVERY PRINTER */ x = PInfo->pi_xpos; /* get starting x position */ ColorInt = PInfo->pi_ColorInt; /* get ptr to color intensities */ sxptr = PInfo->pi_ScaleX; width = PInfo->pi_width; /* get # of source pixels */ /* pre-compute threshold; are we thresholding? */ if (threshold = PInfo->pi_threshold) { /* thresholding */ dvalue = threshold ^ 15; /* yes, so pre-compute dither value */ do { /* for all source pixels */ /* pre-compute intensity value for Black */ Black = ColorInt->colorByte[PCMBLACK]; ColorInt++; /* bump ptr for next time */ sx = *sxptr++; /* dither and render pixel */ do { /* use this pixel 'sx' times */ /* if we should render Black */ if (Black > dvalue) { /* set bit */ *(ptr + (x >> 3)) |= bit_table[x & 7]; } ++x; /* done 1 more printer pixel */ } while (--sx); } while (--width); } else { /* not thresholding, pre-compute ptr to dither matrix */ dmatrix = PInfo->pi_dmatrix + ((y & 3) << 2); do { /* for all source pixels */ /* pre-compute intensity value for Black */ Black = ColorInt->colorByte[PCMBLACK]; ColorInt++; /* bump ptr for next time */ sx = *sxptr++; /* dither and render pixel */ do { /* use this pixel 'sx' times */ /* if we should render Black */ if (Black > dmatrix[x & 3]) { /* set bit */ *(ptr + (x >> 3)) |= bit_table[x & 7]; } ++x; /* done 1 more printer pixel */ } while (--sx); } while (--width); } }
HP_Laserjet transfer.asm
********************************************************************** * * Transfer routine for HP_LaserJet * ********************************************************************** INCLUDE "exec/types.i" INCLUDE "intuition/intuition.i" INCLUDE "devices/printer.i" INCLUDE "devices/prtbase.i" INCLUDE "devices/prtgfx.i" XREF _PD XDEF _Transfer SECTION printer,CODE _Transfer: ; Transfer(PInfo, y, ptr) ; struct PrtInfo *PInfo 4-7 ; UWORD y; 8-11 ; UBYTE *ptr; 12-15 ; movem.l d2-d6/a2-a3,-(sp) ;save regs used movea.l 32(sp),a0 ;a0 = PInfo move.l 36(sp),d0 ;d0 = y movea.l 40(sp),a1 ;a1 = ptr move.w pi_width(a0),d1 ;d1 = width subq.w #1,d1 ;adjust for dbra move.w pi_threshold(a0),d3 ;d3 = threshold, thresholding? beq.s grey_scale ;no, grey-scale threshold: ; a0 - PInfo ; a1 - ptr ; d0 - y ; d1 - width ; d3 - threshold eori.b #15,d3 ;d3 = dvalue movea.l pi_ColorInt(a0),a2 ;a2 = ColorInt ptr move.w pi_xpos(a0),d2 ;d2 = x movea.l pi_ScaleX(a0),a0 ;a0 = ScaleX (sxptr) ; a0 - sxptr ; a1 - ptr ; a2 - ColorInt ptr ; a3 - dmatrix ptr (NOT USED) ; d0 - byte to set (x >> 3) ; d1 - width ; d2 - x ; d3 - dvalue ; d4 - Black ; d5 - sx ; d6 - bit to set twidth_loop: move.b PCMBLACK(a2),d4 ;d4 = Black addq.l #ce_SIZEOF,a2 ;advance to next entry move.w (a0)+,d5 ;d5 = # of times to use this pixel (sx) cmp.b d3,d4 ;render this pixel? ble.s tsx_end ;no, skip to next pixel. subq.w #1,d5 ;adjust for dbra tsx_render: ;yes, render this pixel sx times move.w d2,d0 lsr.w #3,d0 ;compute byte to set move.w d2,d6 not.w d6 ;compute bit to set bset.b d6,0(a1,d0.w) ;*(ptr + x >> 3) |= 2 ^ x addq.w #1,d2 ;x++ dbra d5,tsx_render ;sx-- dbra d1,twidth_loop ;width-- bra.s exit ;all done tsx_end: add.w d5,d2 ;x += sx dbra d1,twidth_loop ;width-- bra.s exit grey_scale: ; a0 - PInfo ; a1 - ptr ; d0 - y ; d1 - width movea.l pi_ColorInt(a0),a2 ;a2 = ColorInt ptr moveq.l #3,d2 and.w d0,d2 ;d2 = y & 3 lsl.w #2,d2 ;d2 = (y & 3) << 2 movea.l pi_dmatrix(a0),a3 ;a3 = dmatrix adda.l d2,a3 ;a3 = dmatrix + ((y & 3) << 2) move.w pi_xpos(a0),d2 ;d2 = x movea.l pi_ScaleX(a0),a0 ;a0 = ScaleX (sxptr) ; a0 - sxptr ; a1 - ptr ; a2 - ColorInt ptr ; a3 - dmatrix ptr ; d0 - byte to set (x >> 3) ; d1 - width ; d2 - x ; d3 - dvalue (dmatrix[x & 3]) ; d4 - Black ; d5 - sx ; d6 - bit to set gwidth_loop: move.b PCMBLACK(a2),d4 ;d4 = Black addq.l #ce_SIZEOF,a2 ;advance to next entry move.w (a0)+,d5 ;d5 = # of times to use this pixel (sx) subq.w #1,d5 ;adjust for dbra gsx_loop: moveq.l #3,d3 and.w d2,d3 ;d3 = x & 3 move.b 0(a3,d3.w),d3 ;d3 = dmatrix[x & 3] cmp.b d3,d4 ;render this pixel? ble.s gsx_end ;no, skip to next pixel. move.w d2,d0 lsr.w #3,d0 ;compute byte to set move.w d2,d6 not.w d6 ;compute bit to set bset.b d6,0(a1,d0.w) ;*(ptr + x >> 3) |= 2 ^ x gsx_end addq.w #1,d2 ;x++ dbra d5,gsx_loop ;sx-- dbra d1,gwidth_loop ;width-- exit: movem.l (sp)+,d2-d6/a2-a3 ;restore regs used moveq.l #0,d0 ;flag all ok rts ;goodbye END
Additional Information on the Printer Device
Additional programming information on the printer device can be found in the include files and the Autodocs for the printer device. Both are contained in the Amiga ROM Kernel Reference Manual: Includes and Autodocs.
|ll|
2cPrinter Device Information
Includes & devices/printer.h
& devices/printer.i
& devices/prtbase.h
& devices/prtbase.i
& devices/prtgfx.h
& devices/prtgfx.i
AutoDocs & printer.doc
Additional printer drivers can be found on Fred Fish Disk #344 under RKMCompanion.