STEALING THE ST PRINTER DRIVERST SCREEN DUMPS FOR ANY PRINTER
by Tom Hudson
This Desk Accessory lets you install your own customized printer driver for either vertical or horizontal screen dumps at the press of the [Alternate] [Help] key combination. Printer drivers are included on the START disk and several can also be found on-line on CompuServe. TOS must be in ROM for this program to operate properly. Programs related to this article may be found in your START disk within the folder labeled PRNTDRVR
If you don't own an Epson FX model printer, or one of its compatibles, you may find your ST screen dumps leave a lot to be desired. That masterful work of art may look like it was filtered through venetian blinds- or worse, it may be just a jumble of nonsense letters. Or, how about an infinite form-feed?
One of the features most often mentioned in "wish lists" for the Atari ST is the ability to load screen printer drivers for various printers other than those built into the ST. There are several reasons one might want to print the computer screen-aside from impressing friends with printer artwork. Printers such as the Okimate 20, for example, can be a perfect source of low-cost overhead transparencies by transferring their see-through, plastic-like pigment to acetate sheets. Or, if a bug appears in a commercial program, you may document the problem by dumping the screen to the printer and forwarding it to the company.
Fortunately, when Atari created the ROM version of TOS, they created a vector which is called to perform the graphic screen dump process.
This article will describe the screen-dump process and how to alter the screen-dump vector to point to a user-installed printer driver routine. Since there are a good number of printer drivers already written for the DEGAS paint program, the screen-dump routine installer presented here will follow the DEGAS printer driver standard. (DEGAS printer drivers can be found in DL4 of the Atari16 section of CompuServe's SIG*Atari, as well as on the DEGAS disk. We have also include some on the START disk with the extender .PRT.)
If you wish to use the installer immediately, skip to the section USING THE INSTALLER, and have fun!
THE BUILT-IN ROUTINES
Each ST computer comes equipped with a simple screen-dump routine in TOS designed primarily for Epson-compatible printers. It may be started by pressing [Alternate] and [Help] simultaneously, or by calling the Atari BIOS trap #14 exception processor with a parameter of 20 decimal (Scrdmp). The GEM Desktop also has a convenient drop-down menu selection which starts the screen-dump routine.
For the [Alternate] [Help] version of the screen dump, the ACIA Receiver Buffer Full Interrupt routine performs a series of tests to determine the type of interrupt that occurred. If it is an intelligent keyboard (IKBD) interrupt, the key combination pressed is checked to see if it is [Alternate] [Help]. If so, the interrupt sets a flag telling the vertical-blank handler to start the screen dump. The flag that gets everything started is PRTCNT, which is located at $4EE. Normally, the value stored in PRTCNT is a -1 (word). When the Alt-Help keystroke is detected, the interrupt increments PRTCNT, making it zero.
The system vertical-blank (VBLANK) handler is what actually starts the screen dump. Each time the code is executed (50 or 70 times per second, depending on the type of monitor you are using), it tests the PRTCNT location to see if it is zero. If not, the VBLANK routine proceeds normally However, if PRCNT contains zero, the VBLANK executes the SCRDMP routine to dump the screen to the printer. In the RAM-based TOS, this is a direct BSR (Branch to SubRoutine) instruction. In the ROM TOS, the SCRDMP routine is executed via an indirect JSR (Jump to SubRoutine) to the address contained at $502. This routine does all the set-up work and calls a routine called PRTBLK, which does the job of printing the specified portion of the screen (in the case of the Alt-Help or Scrdmp functions, the entire screen is dumped to the printer).
During the screen dump, the dump routine watches the PRTCNT location to see if it changes from its zero status. If so, the user has pressed Alt-Help again indicating he or she wants to abort the screen dump operation, and the dumper will exit. After the dump is complete, the PRTCNT location is reset to -1 and system operation returns to normal.
Interestingly, Atari has a vector to the screen dump routine, but none to the PRTBLK routine. Logically, PRTBLK should have had a vector pointing to it, since it does the actual printing. A pointer to a table containing all the printing parameters is passed to PRTBLK, but without a vector that we can alter, this information is useless.
The key to harnessing the screen-dump routine and having the system execute our own screen-dump code is the screen-dump vector at $502. We install our routine in a safe portion of memory and repoint the screen-dump vector to our code.
When a system trap instruction is used to start the screen dump, the trap handler simply calls a routine which sets PRTCNT to zero, simulating the action of the Alt-Help keystroke. It then calls the SCRDMP, either by a direct BSR (RAM TOS) or via the screen-dump vector at $502 (ROM TOS). After the screen-dump code returns, the PRTCNT flag is reset to -1, and the system processing returns to normal.
Since there are two ways of initiating a screen dump in the ST (Alt-Help and TRAP #14), the programmer must be sure that each calling method will properly call the installed dumper. In the ROM TOS, this is no problem-one vector change takes care of all the screen dump possibilities. In the RAM TOS, the programmer must install a pre-VBLANK processor and a pre-TRAP 14 handler which will intercept the Scrdmp call and execute the desired user-installed code.
For the sake of simplicity, this article assumes that the ROM TOS is installed. Atari says the ROM TOS is installed in a majority of machines at this point, making the restriction of ROM TOS a minor one
WHAT ABOUT THE CONFIGURATION?
The built-in screen-dump routine uses a special printer configuration word, PCONFIG, to determine what type of printer is being used. This word has bits that refer to the port (serial or parallel), the type of printer (color or black & white), the mode (draft or final), and so on. The PCONFIG word is set up by the "Install printer" desk accessory provided with the ST.
The location of PCONFIG has not been documented by Atari as of April 1986, but in the ROM 105 it is located at $E4A. I don't recommend using this location in any programs until Atari guarantees that it will not change. DEGAS drivers are typically set up for one configuration anyway, so they do not need to look at the PCONFIG word.
THE PRINTER DRIVERS
When I was writing DEGAS, in June of 1985, the support of various printers, including color printers, was a major concern. The existing screen-dump routine in the ST was a simple, black & white driver for Epson printers that only printed images with four levels of gray-scale. I was not satisfied with the output, and decided that since no mechanism was then available to load screendump drivers into the system, I would create a standard driver format for DEGAS. This driver format, while not infinitely flexible, will allow screen dumps in color or black & white, to the parallel or serial port, to impact or laser printers, or even to plotters.
The drivers are not limited to simple screen dumpers, either. Since the driver is passed a set of parameters giving the color palette and screen address, a numher of useful utility routines could be installed with DEGAS thinking they were printer drivers. Two that come to mind are a color rotation handler for color palette animation and a screen "clipping" routine which would allow the user to define an area of the screen to be held in a buffer for later use. The possibilities are endless.
The DEGAS printer drivers are designed to be a block of executable, position-independent 68000 object code 2000 bytes in length, which begin execution at the first byte in the block. The 2000-byte length was arbitrarily assigned, and is adequate for black & white and color drivers alike, with careful programming techniques.
The drivers are called with the C statement:
If the word command is a zero, the driver should initialize itself and the printer and return with a 1 in D0.W if the operation was successful, or a zero if there was an error. Typically, the initialization function sends the printer a command to set the linefeed and graphics mode accordingly, to check to see if the printer is connected and powered on. The printer driver is ALWAYS initialized before each screen dump.
If command is a 1, the driver is to perform the screen-dump function. During this process, the driver continually tests the keyboard to see if [Undo] has been pressed, and abort if it has. A successful screen dump returns a 1 in D0.W An aborted screen dump returns with a 2 in D0.W If any error occurs during the screen dump, the driver returns with a zero in D0.W
The word resolution indicates the graphics mode that the driver is to use in dumping the screen to the printer. A zero in this value indicates the 16-color, 320-by-200 pixel low-resolution mode, a 1 indicates the 4-color, 640-by-200 pixel medium-resolution mode, and a 2 indicates the 640-by-400 pixel monochrome mode.
The longword (four-byte) screen is a pointer to the base address of the screen data that will he used for the screen-dump operation.
created the ROM
version of TOS, they
created a vector...to
perform the graphic
The longword palette is a pointer to an array of 16 word values that contain the colors used on the screen. These are in the standard form, in which the low three nibbles contain the red, green and blue settings of that color. These nibble values range from 0-7. For example, if a particular color palette entry contains $0456, the red level is set at 4, the green level is 5, and the blue level is 6. These entries can range from $0000 (black) to $0777 (white).
Longword workarea is a pointer to a 1280-byte (once again, an arbitrary length) portion of memory reserved for the driver's use. The driver can put whatever information it likes in this area, including print buffers and working variables. Since the driver code must be position-independent, this is the best place to put variables.
Commented source code for two DEGAS drivers (the Epson-compatible black & white driver and the Epson JX-80 color printer driver) are included on the START disk as examples so other programmers can see how one is written. Many printers are very similar and will only require the modification of various control codes to produce an operating driver. Others may require a bit more work, and I explain possible solutions below.
Note that the Epson black & white driver can provide screen dumps in two different formats, and uses the [Alternate] key to determine which format the user wants. If [Alternate] is not pressed, the driver prints a large image of the screen sideways on the page. If the key is pressed, the screen is printed in a vertical format, slightly smaller. If you write a driver for another black & white printer, it is a good idea to to maintain this "standard." Color printer drivers rarely have enough room to fit both horizontal and vertical routines in 2000 bytes, so they are written to be vertical-format only.
THE EPSON BLACK & WHITE DRIVER
Take a look at the EPSON.S file on the START disk. The first thing you notice in this driver is that it saves all the 68000 processor registers in the work area, using A0 to point to the work areas start. The A0 register is maintained throughout the driver code, and always points to the work area. All working variables (PHASE, KBSHIFT, etc.) are set up as offsets from this address. PHASE is always referenced as PHASE(A0). This is necessary because the driver code must be position-independent and absolute addresses cannot be used. If the 1280-byte work area is insufficient, the Malloc function can allocate needed memory Because of a bug in the current GEMDOS Malloc call, this procedure is not recommended. So far, all printer drivers have been written using just the 1280-byte work area provided, with room to spare.
The driver uses a bit mask byte, PHASE, to mask off data to be added to the print data buffer. As each line of pixels on the computer screen is processed, PHASE is shifted right one or two bits, depending on the resolution. The bits that are on in PHASE determine which pins in the print head are used for that line of pixels. When PHASE is initialized to 192 decimal (SC0 or %11000000), the mask is set for two print head pins. When it is set to 128 decimal ($80 or %10000000), it is set for one pin. As the mask is shifted to the right, it finally becomes zero. At that point, a print line 8 dots high is ready to be printed, and the PHASE mask is reset.
An important part of the printer driver is the code labeled printit:. This is a useful subroutine which takes care of several concerns. First, it provides a single subroutine which prints any number of bytes to the Centronics parallel port. Second, it takes care of printer time-out handling just in case the user tries printing out a picture without a printer connected. If the driver makes 270,000 unsuccessful attempts to send a byte to the printer, it returns an error code. More than a quarter of a million tries may sound like a lot, but this takes about 30 seconds, a good figure for most printers-especially those which shut down when the print head reaches a certain temperature. The 30-second time-out value also allows certain printers to operate at their slowed-down speed without causing the screen dump to abort. If the operation was successful, printit: returns with a zero in D0.W If it failed, printit: returns a -1.
Finally, printit: takes care of saving the 68000 registers D1-D2 and A0-A2, some of which may be altered by the TRAP instructions used to send data to the printer.
Both printer drivers included on the disk contain their own routines for getting pixel values from the screen. While these routines take up precious room in the 2000-byte driver file, they are faster than going through the Line A "get pixel" routine. If you find yourself needing room in the driver, you may want to use the Line A calls to save space, at the expense of some speed.
The horizontal-format screen dump prints an image 800 printer-dots wide, an even multiple of the 200 or 400-pixel high screens. This is an easy conversion for the driver.
The vertical-format dump is a little more complicated. This dump is 960 dots wide, which is a multiple of three for the low-resolution 320-pixel-wide mode, but only 1.5 for the medium and high-resolution modes. To take care of this, the driver outputs two printer dots for each pixel with an even X coordinate, and only one each for pixels with an odd X coordinate. Thus, vertical-format screen dumps may show strange effects on vertical lines.
Another important routine in the black & white driver is the ppix: subroutine, which converts the luminance level of a given color register value to a number ranging from 0-7. This gives standard DEGAS screen dumps 8 levels of gray-scale, a good range for most pictures. The number of gray-scale levels could be set to any value, depending on the programming of the driver. This gray-scale value is then used as an index into the gray-scale pixel table, which is built during the driver initialization at 1200(A0).
This driver assumes that the print head is configured with bit 0 at the head's bottom. If you want to modify this driver for a printer with bit 0 at the top, you'll have to flip the bits in the PHASE byte and reverse the directions of their shift operations.
If you are using a printer such as the Okidata Microline 193 which uses the character $03 to control graphics functions, you'll have to set up a special intercept routine in printit: which takes care of graphic data bytes with an $03 value.
THE JX-80 COLOR DRIVER
One of the ST's big selling points is the ability to generate stunning color graphics, and as a result, many users will want to produce color printouts of their graphics masterpieces. Fitting a color printer driver into a 2000-byte block of code was a real challenge, but I think the solution used in the JX-80 color driver is effective. It produces fairly accurate representations of colors very efficiently.
The JX-80 color printer driver is on the START disk as JX8OC.S Most of the basic routines used in the JX-80 driver will look familiar; they were lifted verbatim from the DEGAS Epson driver, and perform the same functions. There are several significant exceptions.
You will notice that there are now two tables built during the initialization phase. The first, BLACK (located at 200(A0)), is a gray-scale table in which each entry is a 16-bit table. Each of these table entries is a 4-bit by 4-bit mask (16 bits total) which is overlayed on the printer dot pattern. The table ranges from black (all bits on) to white (all bits off).
The second table, WHITE (located at 300(A0)), is a white-scale table similar to BLACK. Instead of adding black as the brightness of a pixel diminishes, the WHITE table adds white as the brightness of a pixel increases. We will see how this works in a moment.
The process of printing a color picture on most personal color printers is the same, regardless of the printer. There is usually a multicolor ribbon containing yellow, magenta and cyan portions. Most color impact printers also have a fourth color, black, for normal printing operations, such as program listings. Our color printer driver is simplified because it does not use the black portion of the ribbon. We get black by mixing the other three colors.
To make the various colors needed for each screen-dump line, the print head must make three separate passes over each print line, one in each color. It is best to make the three color passes starting with a yellow pass, then a red pass, then a cyan pass, to reduce color contamination problems on the ribbon. If, for example, the cyan pass was made first, followed by the yellow pass, the yellow ribbon would pick up cyan ink from the paper, making the yellow ribbon more of a green color. The same applies to the magenta ribbon, which is less contaminated by yellow than by cyan.
Since there are only three basic colors present on the ribbon, some sort of algorithm is needed to mix them on the paper to produce colors that approximate those on the computer screen. This is accomplished by the combination of a large table, cvalues:, which provides color information, and a simple algorithm for adjusting brightness.
cvalues: is at the end of the listing. It contains 512 one-byte entries (one byte per color possible on the ST) made up of two groups of three bits each (the high-order two bits are not used). Each three-bit group represents a printer color to be used for the corresponding screen color, and the two colors specified in the byte are mixed in a 50-50 combination on the paper. The color specified by the low-order three bits is used on even printer dots, and the color specified by the next three bits is used on odd printer dots. For a solid color, the two colors are set to the same value.
The three bits give a total of eight color combinations, of which white and black are not used, leaving six colors to be mixed. The total number of combinations of the various colors is 20. Twenty colors out of 512 would not be a very good proportion, so additional processing is done using the BLACK and WHITE tables.
...is the screen dump
vector at $502.
Each pixel's color is analyzed according to the following steps:
- The total brightness of the pixel's red, green and blue components is calculated, ranging from 0-21.
- If the color's table entry is zero, the program goes to the grayit: routine, which calculates a level of gray-scale for the pixel, similar to the process used in the black & white driver. This is a special case for all colors which have the same red, green and blue levels, and produces a good gray.
- If the pixel's brightness is less than 7, black dots are added from the BLACK table (see the blakck: routine).
- If the pixel's brightness is equal to 7, the color is left as is (no adjustment of the black & white content).
- If the brightness is greater than 7, white pixels are added from the WHITE table (see the whitck: routine).
A special procedure is required to transform driver source code to .PRT files. Included on the START disk is SAVER.C, the C source code for a program which will save assembled printer drivers to disk in the standard DEGAS.PRT format. To use SAVER.C, compile it to an object file using your C compiler. Next, assemble the assembly source for the printer driver you want to save into an object file. Now, link these files together using LINK68 or a comparable 68000 linker program to produce an executable .PRG program file. (See SAVER.BAT on your START disk for an example of using the EPSON.S driver.)
Remember that to successfully link the SAVER.C and the printer driver, the printer driver's executable code MUST start with the label s_dumper, so that SAVER.C knows where the driver code starts.
Once you have linked SAVER.C and the printer driver into an executable program (such as SAVER. PRG), you must execute the SAVER.PRG program to create the final printer-driver file. The program displays the file selector box. Enter the name of the printer driver file (be sure to use an extension of . PRT on the filename). The program will report the success or failure of the write operation and return to the desktop. If the file write was successful, the printer-driver file is ready to use with DEGAS or the printer driver installer program.
Also on the START disk are the files INSTALL.ACC, INSTALL. C and INSTAL.S. INSTALL.ACC is a GEM Desk Accessory which performs the installation of a DEGAS printer driver. INSTALL.C is the C source code for the accessory, and INSTAL.S is the AL source code for the assembly language portion of the accessory. These two source files must be compiled and linked with ACSTART.O in order to produce an executable desk accessory (See INSTALL.BAT on your START disk.)
The C source code is fairly straight forward, except for a couple of interesting points.
When started by the GEM desktop code, the installer displays a two-button alert box which gives the user the choice of a VERTICAL or HORIZONTAL printout. This option is very important. You'll recall from the above discussion on printer drivers that the DEGAS drivers examine [Alternate] when initialized to see if it is being pressed. If it is, the driver shifts to a vertical print format; if not, the screen dump is printed sideways. Unfortunately, when the screen dump is initiated by the Alt-Help keystroke, [Alternate] will ALWAYS be pressed as the driver is initializing, and therefore all Alt-Help dumps would be printed vertically!
This was an undesirable result, but fortunately the ST BIOS has a way to get around the problem. There is a TRAP #13 call included in the OSBIND.H file called Getshift which allows you to get or set the status of the keyboard's Control, Alternate and Shift keys so that the next call to Getshift will think certain keys are being pressed. Some early versions of the OSBIND. H file contained an error in the way the Getshift call was #defined. Take a look at your OSBIND.H file and be sure the definition reads:
#define Getshift(a) bios(11,a)
The earlier definitions left out the parameter specifier (a).
The first alert box allows the user to tell the installer which type of output is desired before loading the printer driver. Furthermore, if the user wants to change the output format at a later time, he or she simply selects the "Printer Driver" accessory and clicks on the appropriate button to change the output format. The result of the alert is determined and the variable prtmode is set to either 0 (no shift keys pressed) or 8 (Alternate key pressed). Use this variable to set the shift key state when the screen dump is called.
printer driver is
installed, you can
print the screen to
whatever printer the
driver is compatible
The program then calls the GEM file selector dialog and accepts a printer-driver filename. The file is opened and read into the address of the scrdmp() function. This function is defined in the INSTALL.S file, and is nothing more than 2000 bytes of reserved memory. If the file read into memory was less than 2000 bytes in length, an alert box appears informing the user, and the installer aborts without installing a driver.
If the file is the correct length (no check is made to see if the file is over 2000 bytes, because some drivers downloaded with terminal software may have extra bytes tacked onto the end of the file), the driver is installed by calling the install() function. This simply replaces the screen dump vector at $502 with the address of our screen dump control routine, dumpctrl().
Whenever the system is asked to perform a screen dump from this point on, the VBLANK code will call dumpctrl() to do the dump. dumpctrl() requests the system screen resolution, the physical base address of the screen and the color palette, then sets the keyboard shift bits according to the contents of prtmode and initializes the printer driver (command = 0).
The printer driver returns a success code of 0 or 1. If the value is 1, the initialization was a success, and the printer driver is called again with a command word of 1, telling it to dump the screen. When complete, control returns to the system.
USING THE INSTALLER
First, you must have TOS in ROM in order to use this desk accessory. To get the printer driver installer up and running on your ST, just copy the INSTALL.ACC file from the START disk to the disk you use to boot your system. You should also place an appropriate printer driver file on this disk. If you can't find the right one on your START disk, several have been placed on CompuServe. Now, boot your ST with the disk containing INSTALL.ACC and the Desk drop-down menu will have a new entry called "Printer Driver."
To install a particular printer driver on your computer, select the "Printer Driver" menu item. A dialog box will appear giving the credits for the installer accessory At this point, you will have two options: Horizontal or Vertical. A horizontal screen dump is printed sideways on the printer paper in a fairly large image size. A vertical screen dump is printed right-side-up on the paper, slightly smaller. Choose your format. You can always change it later.
The computer will display the file selector dialog, showing all the printer driver files (.PRT) on the current disk. Pick your printer driver and click on 'OK" (if you already have a driver installed and just wanted to change the format from horizontal to vertical, you can click on "Cancel" to avoid reloading the file).
You will be notified of problems in installing the printer driver. Be sure you always have a successful load before trying to perform a screen dump. Failure to do so could lock up your computer.
Once a printer driver is installed, you can print the screen to whatever printer the driver is compatible with, either in color or black & white. Just press Alt-Help like you normally would or use the GEM desktop's "Print screen" option.
To abort a screen dump using the DEGAS drivers, press [Undo]. This is the only difference between the installable DEGAS drivers and the built-in driver, which aborts when Alt-Help is pressed a second time.
It is not necessary to set the printer configuration by using the "Install Printer" desk accessory when using the DEGAS printer drivers. The DEGAS drivers are specifically written for one configuration and do not use the information in the "Install Printer" dialog.
As more and more printer drivers become available for the ST series, the machine will become more useful to a larger group of people. I hope this information and the printer driver installer will inspire more programmers to write DEGAS-format printer drivers-They're not just for DEGAS any more!
- The Motorola MC68000 Microprocessor Family: Assembly Language, Interface Design, and System Design, by Thomas L. Harman and Barbara Lawson. Prentice-Hall, Inc., Englewood Cliffs, NJ
- A Hitchhiker's Guide to the BIOS, Atari Corp.