home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / canada-remote-systems / geos / drivers / shelldc2.sda / PRINTSHELLDOCS (.txt) < prev    next >
GEOS ConVerT  |  2019-04-13  |  24KB  |  78 lines

  1. /    Print Shell Docs/
  2. PRG formatted GEOS file V1.0
  3. EpsonFX-80 72dpi
  4. OP V2.0 or higherhell
  5. ShellHeader
  6. BLASTER'S CONVERTER V2.5
  7. PetASCII
  8. PChanGC
  9. Write Image V2.0
  10. geoWrite    V2.1
  11. .For geoProgrammers only.  Printout quickly in DRAFT.
  12. Read ShellDocs1 first.
  13. @GEOS Printer Shell
  14. The method GEOS uses for printing is ideally suited for 8-pin printers operating at 80 dpi (dots per inch).  For other types of printers, or for high density smoothing drivers, or for color printing, the usual method is cumbersome to write drivers for.  This Shell is actually the first half of a printer driver that makes writing the remaining half a much easier task.  In fact, people who understand the original GEOS printer driver specification may have more trouble working with this Shell than someone new to the subject because it is so radically different, at least as far as graphics printing is concerned.
  15. To begin with, let us assume that we will be writing the graphics portion of a driver for a monochrome (non-color) printer.  The Shell provides us with several subroutines which we can call at will and a generous supply of zero-page RAM as well as a full page of additional RAM to use as we please.  Since the GEOS environment has been switched out, GEOS subroutines and variables are inaccessible.
  16. @OpenWindow
  17. The first subroutine we will use is called OpenWindow.  The Window that we are going to open is three columns wide (the full page is eighty columns wide) and up to twenty four lines of pixels high.  It occupies 72 bytes at a fixed location on page zero so that we can use a variety of efficient addressing modes to access it.  It is organized as three "panes" of 24 sequential bytes each.  The first byte represents the eight pixels at the top of the first pane in the upper left hand corner of the Window.  The 24th byte corresponds to the bottom of the left pane and the next byte is the top of the center pane, etc.  We need to pass to the subroutine the desired column number in the Y-register (which it will store in a variable called ActiveColumn) and it returns with the pixel image in the Window on page zero.  The column number can range from 1 to 80 and corresponds to the center pane of the Window.
  18. When we open the Window with the center at column 1, the Shell automatically fills the left pane with blanks (zeroes) and likewise with the right pane when the center pane is at column 80.  In addition, at the start of each page, the top eight lines of the Window are pre-loaded with a blank border and the actual page image begins on the ninth line.  When we eventually move down to the bottom of the page (or below), blanks will fill the Window from the bottom.  The purpose of all these blanks is to make it easy to write high density smoothing algorithms which need to examine a neighborhood of pixels to calculate how to adjust the printed dots in the center of the neighborhood.
  19. @Shift Pixels
  20. The second subroutine we will examine is called ShiftPixels.  It shifts all the pixels in the Window to the left by one position and clears the Z-flag except after the eighth shift which corresponds to one column's worth of shifts.  The pixels that are shifted in at the right hand edge of the Window are not the "real" pixels from the page image but are merely blanks (zero bits) so obviously after the eighth shift the entire right hand pane of the Window is unusable.  The intention is for a smoothing algorithm to be centered around the left hand pixels of the center pane so that there can be up to seven pixels surrounding the center pixel where the smoothing dots are being calculated.  In actual practice, most smoothing algorithms will rarely examine more than one or two surrounding pixels because of the extreme complexity of such algorithms, but the Shell does provide for more in case some enterprising programmer wants to use them.
  21. @MoveDown
  22. The third subroutine is called MoveDown and is used to move the Window down the page any number of pixels (lines) according to the number passed in the accumulator when the subroutine is called.  The Shell only allows (vertical) motion in the downward direction (unlike the horizontal positioning done by OpenWindow which can instantly go to any column).
  23. We know when we have run off the bottom of the page by examining a variable called "PrintMode".  As long as the bottom of the Window contains real pixels, PrintMode will have a value of $40.  When the Shell runs out of pixels, it will fill the bottom of the Window with a blank border and PrintMode will be decremented.  The Window will be totally blank when PrintMode equals $3d (or less) but some drivers may want to test for $3e or $3f to avoid "printing" blanks beyond the physical end of the page.
  24. @SendByte
  25.  and 
  26. @SendMessage
  27. There are two more subroutines which do the actual work of communicating with the printer: SendByte, which transmits the byte in the accumulator to the printer, and SendMessage, which transmits one of several fixed strings of bytes which have been assembled, patched, or edited into the driver.  These messages are numbered and selected by passing the desired message number in the X-register.  The organization of the messages is optimized so that users who are not programmers can easily edit them using a special editor to make minor adjustments in the operation of a driver or to fix a driver to work with different but similar printers.  By convention, the first eight messages have standard functions to make this editing process easier for the novice.
  28. @First Example -- Epson 8-pin 80 dpi
  29. Now that we have studied these five subroutines we are ready to look at some examples of real drivers.  The first is for a single pass 8-pin 80 dpi printer.  Examine the file called Epson8pin.lnk and notice that it links four files at $7900 and creates the output printer driver "Epson8pin".  The first of these files is the Shell itself which should never be modified.  This is so that special user-friendly editors will continue to work.  The second file handles all of the graphics routines and is where most of the work is done in applying the Shell to specific printers.  The third file handles the text routines and usually will be PrintASCII.rel as shown or PetASCII.rel for most Commodore compatible printers or PetASCIInoFF.rel (not written yet) for those that do not support the form feed code.  The last file handles the interface and typically will be PChanSerial5.rel for most serial bus interfaces, PChanSerial7.rel for most Commodore compatible, PChanCable.rel for the geoPrint Cable, or PChanRS232C.rel (not written yet) for RS232C printers.
  30. The last two linked files must be position-independent so that if they are relocated by an editor they will continue to work.  There are specific jump instructions within the Shell that the editor will modify if a user wants to change the interface type.
  31. @Graphics Printing
  32. Now let us focus our attention on the graphics portion of the driver, Epson8pinG.  We start with two constants which define the size of the printed page in cards of 8 by 8 pixels.  The maximum width allowed is 80, but the length can be as large as 255 (although most applications will not supply more than 94).  Next we ".include" the ShellZero file which defines zero page RAM and allows us to set up a variable which will be used to assemble the graphic byte that will eventually be sent to the printer.  ShellZero allows us to allocate up to 47 bytes.  In addition, the Shell provides a pointer at $10 (r7) which can be used with the indirect indexed addressing mode [(r7),y] 
  33. to access another 256 bytes of general purpose memory.
  34. @InitForPri
  35. @InitForPrint
  36. The first executable code must be preceded by the ".psect" directive and in this example begins at the label InitForPrint.  This routine is called once at the beginning of the printing session.  It is the only routine that operates in the GEOS environment and is where dialog boxes can be presented to the user to request such things as print quality or the desired font for NLQ printing.  This driver does not do anything with InitForPrint except return.
  37. @PrintPage
  38. The next routine, PrintPage, is called once per page and is responsible for printing the entire page.  We can treat this just like a mainline routine since it will call other subroutines in the Shell and in this file (although it ends with an "rts" when the page is finished printing).  As an example, the first thing it does is to send a page initialization code sequence to the printer (also called a "message") by setting the X-register to mBeginPage and calling SendMessage.  The actual message sent is assembled on the next page of the source file.  We should always include these instructions even if we do not plan to use any initialization code so that other users can edit our driver and attach their own code sequence.
  39. Next we begin the general process of scanning the print head (with DoScan), moving the Window down the number of pixels corresponding to the height of the print head, and repeating until PrintMode indicates that the bottom border has been reached.  Finally we send the mEndPage message.  The main work of printing is done by DoScan which we will examine in detail next.
  40. @DoScan
  41. Although DoScan is only called from one place in this driver, we still prefer to make it a separate subroutine to make the transition to other drivers easier.  The responsibility of this subroutine is to send all of the bytes required to scan the print head across the page one time.  We start with a message (code sequence) telling the printer we want to print a given number of graphic bytes (in this case 640).  Then we set the Y-register to 1 because we want to start a loop with the Window opened on column one.
  42. Now we start another loop in which we will calculate and send to the printer a graphic byte corresponding to the eight vertical pins on the print head which are taken from the left hand pixels of the center column of the Window.  Then we shift the pixels in the Window to the left one position and repeat the process a total of eight times (until the Shell subroutine ShiftPixels returns with the zero-flag set).  This completes the innermost loop.
  43. To complete the outermost loop we examine the variable ActiveColumn and repeat the loop if we have not yet finished column 80.  When we repeat, the incremented value of the Y-register will open the Window on the next column to the right.
  44. After all 80 columns have been printed we send one last message to end the scan: usually a carriage return and a line feed.
  45. @DoGraphicByte
  46. The innermost subroutine, DoGraphicByte, picks up eight bits corresponding to the leftmost pixels in the center colu
  47. @DoGraphicByte
  48. The innermost subroutine, DoGraphicByte, picks up eight bits corresponding to the leftmost pixels in the center column of the Window.  The X-register is used as a pixel or line index starting at the top of the Window and incrementing down through a total of eight lines.  Note that the center column of the Window is referenced by adding 24 to the Window label.  We are only interested in picking up the MSB (Most Significant Bit) of each Window byte into the carry bit.  We could have rotated the Window byte itself, but this would have corrupted the Window, so we instead load the Window byte into the accumulator and rotate the accumulator.  This becomes very important when we get to smoothing algorithms that need to examine a neighborhood of uncorrupted pixels.
  49. After rotating eight bits into GraphicByte we send it to the printer and return.  Some printers require the top pin to be controlled by the MSB of this byte which is achieved by using a rotate-left instruction.  Other printers which require the top pin to be specified by the LSB would use a rotate-right instruction.
  50. @Wrapping Up
  51. The second page of this file consists of a single Color Filter and the information about the messages.  It is important that these items always appear in this order in all drivers so that the editors will be able to locate them.
  52. A Color Filter contains two sixteen bit words, one for the active foreground colors and one for the active background colors.  Each of the sixteen allowable colors has a corresponding bit in both of these words.  For black-only printing, the LSB (bit 0) of the first word is the only active foreground color allowed.  No colors are active as background colors.  These Filters will be discussed in greater detail later.
  53. The location of each message is defined by an offset byte which the SendMessage subroutine uses to find the message as offset from the beginning of the messages.  Since only one byte is used, all of the messages must add up to no more than 256 bytes.  Similarly, the lengths of the messages are defined and are used by the SendMessage routine.  Each message is given a descriptive label which is  equated to its message number.  More messages can be easily added by following the same pattern, but the eight included in this driver must be present in the same order in all drivers.  There should always be one more numbered message label at the end of the list so that the proper length calculation can be made.
  54. @Moving On
  55. One of the most obvious improvements we can make to our driver is to have it examine the graphic bytes before sending any of them to the printer and send a code sequence for a shorter scan whenever there is white space on the left or right hand edges.  We would need to modify the DoGraphicByte subroutine so that it stores the byte either on page zero or in the buffer pointed to by r7.  In both cases we would use the Y-register to index eight bytes generated by a new subroutine corresponding to the innermost loop of DoScan.  DoScan itself would be broken into three parts, one would start the Window on column 80, working backward until a non-blank graphic byte was found.  The next part would start on column 1 working forward again until a non-blank graphic byte was found.  For each column, an ASCII space would be sent to the printer.  Then the third part would calculate the proper code sequence to begin the scan based on the number of columns between the two non-blank graphic bytes as determined in the first two parts.  If the entire scan is blank, then nothing would be sent.  A good driver design will allow the lowest level subroutine to be used by all three parts.
  56. @Color Printing
  57. In order to enable color printing, the InitForPrint subroutine must set the variable ColorFlag to $ff.  Then if the applicati
  58. @Color Printing
  59. In order to enable color printing, the InitForPrint subroutine must set the variable ColorFlag to $ff.  Then if the application also allows color printing, a filter determines which colors will be printed.  For example, suppose we want to make a series of drivers that work with various colored ribbons in a non-color printer.  All we have to do is create a different filter in each driver for each ribbon.  For the red ribbon, we use the word %0000010000000100 twice, once for the foreground and once for the background.  This allows both shades of red (color numbers 2 and 10) to be active.  (See page 61 of the C64 User's Guide for a convenient listing of the color numbers.)  For the green ribbon, we would use the word %0010000000100000 (color numbers 5 and 13).
  60. Rather than have several different drivers which the user must select from before printing, we could put all the filters in the same driver, one after the other and have a dialog box (in the InitForPrint subroutine) request which one the user wants.  The desired filter would be made active by storing an offset to it in the variable SelectFilter.  The first filter is selected with the value 0, the second with 4, the third with 8, etc, because each filter takes four bytes.
  61. If we had a real color printer we would want the driver to change the ribbon color and select the different filters.  This would typically be done just before the DoScan subroutine was called in the PrintPage routine rather than in InitForPrint.  The idea here is to have the driver set the color to yellow by sending whatever message the printer understands, then selecting the appropriate filter and calling DoScan.  Since we don't want the linefeed to be performed, we would change the mEndScan message so that it just printed the line without feeding.  Next we would repeat the sequence for a different color.  To get different shades we could do two passes on the same ribbon color with two different filters in which the lighter shade was active in only one of them.
  62. @ColorWindow
  63. Another approach to getting shades is to make the printer operate at a higher density and control the number of extra dots printed by the different shades.  This approach is very important, not just for the time saved in printing, but for the amount of ribbon used by a printer like the Okimate 20.  In this case, we want to assemble several "passes" done with different filters into memory and then OR them together before actually sending them to the printer.  In order to be a little more efficient at doing this, the Shell provides another subroutine called ColorWindow which is used after OpenWindow when we want to leave the Window where it is but with a different selected filter.
  64. Although the ribbon color is selected before calling DoScan, the filter is selected within DoScan just before calling a new subroutine, DoColumn.  This subroutine would clear a section of memory (either on page zero or in the buffer pointed to by r7), select the first filter, call OpenWindow, and then repeatedly call DoGraphicBytes which would OR one column's worth of bytes into memory.  Then it would select another filter and call ColorWindow and OR another column's worth of bytes into the same memory locations.  If more shades are desired, this last part would be repeated.  Finally, after the column is completed for all shades of a given color, the bytes would be sent to the printer, or if we wanted to take advantage of sending shorter lines ending or beginning with white space, we would simply test for a non-blank byte.  It is desirable to write our routines to allow for this kind of capability.
  65. @Smoothing
  66. Although there are many ways to implement smoothing drivers, one of the simplest is with table lookups.  Unfortunately, this can be very wasteful of memory because we only need one bit per table entry.  Also, if we wish to use all eight neighbor cells surrounding a center cell plus the center cell itself for indexing into the table, we need a table with 512 entries which requires an index of nine bits, one too many for the 6502's index registers.  And to add insult to injury, we always need multiple tables, one for each of the added smoothed bits.  For example, a driver running at double the normal densities in both the horizontal and vertical directions will need to expand each pixel into four printable dot positions.
  67. To solve these problems, each table of 512 bits is packed into 64 bytes, and special subroutines have been written to take advantage of the rotational symmetry of the neighborhood of nine pixels so that the same table can be used for all four smoothed dots.  For the case of a driver that operates at three times the normal densities in both axes, each pixel expands into nine dots, and three tables of 64 bytes are needed, one for the four corner dots, one for the four side dots, and one for the center dot.  If it turns out that none of the smoothed dots are printed when the center pixel is turned off (which is likely for 9-pin printers because their pins tend to be large), then the three tables can be half as large, 32 bytes.
  68. It is important to remember that these smoothing algorithms are not a part of the Shell and any others can be used just as effectively.  However, special editors will also be made available that allow the tables to be created or modified by users.  It is simply a matter of fact that no one algorithm can suit every purpose.
  69. @More Examples
  70. The additional examples of drivers are all smoothing drivers operating at high density.  In the case of the Epson 8-pin, three passes are made at three times normal densities in both the horizontal and veritcal directions, but unlike previous drivers which have two short line feeds and one long one, this driver advances the line feed by the same amount after each pass.  Three different smoothing algorithms are cycled through on each pass.  Actually, this is achieved simply by using a different table to look up the smoothed dots.
  71. Similar smoothing algorithms are used for 24-pin printers, but they operate at two times the normal density in both axes.  For Epson printers this results in a 90 dpi driver but the Okimate 20 produces the correct proportions.
  72. @Helpful Hints
  73. Make sure you study the previous document "Shell easy docs" before trying to master this one.  There is also a geoPaint document illustrating the Window called "ShellWindow".  Both of these are available along with a minimum set of files to assemble and link the Epson8pin driver.
  74. To put together any driver, seven files are required: 1) a link file, 2) a ShellHeader.rel file, 3) the ShellZero file (unassembled), 4) the PrinterShell.rel file, 5) the printer-specific graphics file, 6) a relocatable text file, and 7) a relocatable interface file.
  75. There should rarely be the need to assemble any more than the printer-specific graphics file.  To avoid complications, it is best to use the relocatable versions of the PrinterShell, the text file (Print ASCII  
  76. or PetASCII), and the interface file (PChanSerial5, PChanSerial7, or PChanGC).  The PrinterShell should never be re-assembled and the others should only be re-assembled if a change is required.
  77. nce for a different color.  To get dif
  78.