Date: Thu, 30 Jul 87 10:31:02 edt From: David Krowitz Subject: Apollo print server for NEC P9XL Pinwriter Here is a recently completed print server for a NEC P9XL Pinwriter connected to an Apollo workstation via the serial I/O port. The packed source files begin below the dashed line. Run them through /bin/sh to unpack the files. -- David Krowitz mit-erl!mit-kermit!krowitz@eddie.mit.edu mit-erl!mit-kermit!krowitz@mit-eddie.arpa krowitz@mit-mc.arpa (in order of decreasing preference) ------------------------------------------------------------------------ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # nec.config # user1.ins.pas # user1.nec.bld # user1.nec.doc # user1.nec.doc.install # user1.nec.pas # This archive created: Thu Jul 30 10:26:59 1987 export PATH; PATH=/bin:$PATH if test -f 'nec.config' then echo shar: will not over-write existing file "'nec.config'" else cat << \SHAR_EOF > 'nec.config' PAGE_WIDTH 132 PAGE_LENGTH 66 PAGENO_COLUMN 120 FILE_BANNERS OFF DEVICE user1 SIO_LINE 1 SPEED 9600 TOP_MARGIN 2 BOTTOM_MARGIN 2 FORM_FEEDS 0 PLOT_MODE ON RESOLUTION 360 SHAR_EOF chmod +x 'nec.config' fi # end of overwriting check if test -f 'user1.ins.pas' then echo shar: will not over-write existing file "'user1.ins.pas'" else cat << \SHAR_EOF > 'user1.ins.pas' { PRSVR.INS.PAS, Print server routines and associated data types which are exported for user supplied device drivers. Changes: 11/04/86 jjm Added comments 10/13/86 jjm added prf_$orient and prf_$paper_t 08/18/86 jjm added server_db and driver_db sr9.5 info 03/30/85 jjm updated for sr9 release 04/09/84 jjm update to sr8 05/09/83 gtr original coding } CONST pr_$bufsize = 2048 ; TYPE pr_$buf_t = ARRAY [1..pr_$bufsize] OF char ; pr_$t = (pr_$user1, pr_$user2, pr_$user3, pr_$user4) ; pr_$set_op_t = ( pr_$font_weight, pr_$font_size, pr_$text_precision, pr_$data_format, pr_$pitch, pr_$y_dimension, pr_$x_dimension, pr_$rep_factor, pr_$config, pr_$copies, pr_$server_db ); pr_$inq_op_t = ( pr_$bpi, pr_$rep_ability, pr_$driver_db ); pr_$font_weight_t = ( pr_$light, pr_$medium, pr_$bold ); pr_$text_precision_t = ( pr_$draft, pr_$letter_quality ); pr_$data_format_t = ( pr_$text, pr_$plot, pr_$transparent ); prsvr_color_format_t = (none, {not a color printer} pixel, {color map format} scan_line_rgb,{3 plane formats} scan_line_ymc, plane_rgb, plane_ymc ); {this tells the server how to send bitmap data} pr_$interface_t = ( pr_$serial, pr_$parallel, pr_$external, pr_$versatec, pr_$multibus); prf_$paper_t = ( prf_$cut_sheet, prf_$fan_fold, prf_$roll, prf_$transparency, prf_$null ); prf_$orient_t = ( prf_$landscape, prf_$portrait ); (* This data structure is set up by PRSVR based on PRF options specified by the user and information the driver passes to PRSVR via the return_info call. When the setmode call operation type = pr_$server_db , dereference the server_db_ptr_t in pr_$data_rec_t to obtain the current parameters for this job. Use these values to format and control how the data is output to the print device. Ignore any other operation types (except interface) passed by the set mode call. They exist only for backwards compatability. *) server_db_t = RECORD copies : binteger; print_mode : pr_$data_format_t; cpi : real; {characters per inch} lpi : real; {lines per inch} weight : pr_$font_weight_t; lq : boolean; resolution : pinteger; {set the printer to print at this resolution} magnification : integer; {set the printer to magnify a bitmap by this amount} bitmap_size : RECORD x : integer; {the number of bits PRSVR will send} y : integer; planes : integer; {bits per pixel} END; color : boolean; {this file should be printed in color} bw_rev : boolean; {set if user requests it and the printer can do it } color_map : UNIV_PTR; {pointer to the gpr bitmap color map} page_layout : prf_$orient_t; page_reversal : boolean; collate_copies : boolean; paper : RECORD page_size : RECORD w : REAL; l : REAL; END; style : prf_$paper_t; END; END; server_db_ptr_t = ^server_db_t; {this points you to the PRSVR server_db} (* Use this data structure to pass PRSVR information about the driver and printers capabilities. PRSVR will issue a return_info call at startup, requesting this information. When the operation type of this call = pr_$driver_db , deference driver_db_ptr_t in pr_$data_rec_t to pass PRSVR this information. Ignore any other operations passed by the return info call. They exist only for backwards compatability. *) driver_db_t = RECORD {information about the printer that the driver supplies} valid : boolean; { set this to true } copies : boolean; {does the printer do multiple copies} cpi : ARRAY [1..10] OF real; {an array of character spacings} lpi : ARRAY [1..10] OF real; resolution : ARRAY [1..4] OF pinteger; { the printer plots at these resolutions} res_min : pinteger; {a range of resolutions from minimum ...} res_max : pinteger; {... to maximum} magnification : ARRAY [1..16] OF binteger; color_format : prsvr_color_format_t; {tell prsvr how to send color images} bw_rev : boolean; {set if the printer can bw reverse image} paper : RECORD page_size : RECORD w : REAL; l : REAL; END; style : prf_$paper_t; margins : RECORD {unprintable regions of paper} l,r,t,b : real; END; END; image_rotation : boolean; {the controller can rotate a bitmap} END; driver_db_ptr_t = ^driver_db_t; {a pointer to the PRSVR driver_db_t} pr_$data_rec_t = packed RECORD font_weight : pr_$font_weight_t ; font_size : real ; text_precision : pr_$text_precision_t ; bpi : RECORD x : integer ; y : integer ; END; data_format: pr_$data_format_t; pitch : real ; x_dimension : pinteger ; y_dimension : pinteger ; rep_factor : pinteger; rep_ability : boolean; copies : pinteger; interface: pr_$interface_t; server_db_ptr : server_db_ptr_t; driver_db_ptr : driver_db_ptr_t; END ; {Called once when the print server starts. Open I/O channels in this call} PROCEDURE user2_init ( IN sio_line: integer; IN sio_speed: integer ) ; EXTERN ; PROCEDURE user3_init ( IN sio_line: integer; IN sio_speed: integer ) ; EXTERN ; PROCEDURE user4_init ( IN sio_line: integer; IN sio_speed: integer ) ; EXTERN ; {Called at program startup. Use only the driver_db operation} PROCEDURE user2_return_info ( IN operation : pr_$inq_op_t; OUT datum : pr_$data_rec_t ); EXTERN ; PROCEDURE user3_return_info ( IN operation : pr_$inq_op_t; OUT datum : pr_$data_rec_t ); EXTERN ; PROCEDURE user4_return_info ( IN operation : pr_$inq_op_t; OUT datum : pr_$data_rec_t ); EXTERN ; {Called once at startup to set the interface} {Called at the beggining of each job. Use only the server_db operation to set job parameters} PROCEDURE user2_set_mode ( IN operation : pr_$set_op_t; IN datum : pr_$data_rec_t ); EXTERN ; PROCEDURE user3_set_mode ( IN operation : pr_$set_op_t; IN datum : pr_$data_rec_t ); EXTERN ; PROCEDURE user4_set_mode ( IN operation : pr_$set_op_t; IN datum : pr_$data_rec_t ); EXTERN ; {called multiple times for each job. This is the data to format and print} PROCEDURE user2_write ( IN str: UNIV pr_$buf_t ; IN strlen: pinteger ) ; EXTERN ; PROCEDURE user3_write ( IN str: UNIV pr_$buf_t ; IN strlen: pinteger ) ; EXTERN ; PROCEDURE user4_write ( IN str: UNIV pr_$buf_t ; IN strlen: pinteger ) ; EXTERN ; {Called at the end of each job. Reset job specific paramters to default values} PROCEDURE user2_flush ; EXTERN ; PROCEDURE user3_flush ; EXTERN ; PROCEDURE user4_flush ; EXTERN ; {Called when the print server terminates. Terminate any current jobs and close I/O channels} PROCEDURE user2_close ; EXTERN ; PROCEDURE user3_close ; EXTERN ; PROCEDURE user4_close ; EXTERN ; %eject ; SHAR_EOF chmod +x 'user1.ins.pas' fi # end of overwriting check if test -f 'user1.nec.bld' then echo shar: will not over-write existing file "'user1.nec.bld'" else cat << \SHAR_EOF > 'user1.nec.bld' ########################################################################################## ### ### ### Command file for Compiling and Binding the NEC Pinwriter P9XL Print Server ### ### ### ########################################################################################## von pas user1.nec.pas -cpu any -opt 3 bind -b prsvr.user1.nec /com/prsvr user1.nec.bin voff SHAR_EOF chmod +x 'user1.nec.bld' fi # end of overwriting check if test -f 'user1.nec.doc' then echo shar: will not over-write existing file "'user1.nec.doc'" else cat << \SHAR_EOF > 'user1.nec.doc' ******************************************************************************* ***** ***** ***** USER1.NEC.DOC ***** ***** Version 2 ***** ***** ***** ***** Programming Notes for the NEC P9XL Print Server ***** ***** ***** ***** Copyright (c) 1987 ***** ***** David M. Krowitz ***** ***** Massachusetts Institute of Technology ***** ***** Department of Earth, Atmosheric, and Planetary Sciences ***** ******************************************************************************* Interfacing the Printer with the Apollo --------------------------------------- We have interfaced our NEC P9XL printer to an Apollo DN3000 using a 3-wire RS232-C null modem cable. The wiring of the cable is: pin 2 - pin 3, pin 3 - pin 2, and pin 7 - pin 7 (ie. cross the data receive and data transmit wires, and connect the signal ground wire). We are using the X-ON/X-OFF protocal to control the flow of data between the Apollo and the printer rather than using the DTR and CTS signals in the RS232-C cable. The SIO line characteristics necessary to run the printer using X-ON/X-OFF are set up in the routine USER1_INIT. It is also necessary to set the DIP switches on the back of the printer to the correct settings in order to use the 3-wire cable. The following switch settings were used (be certain that you don't confuse the 2 DIP switches associated with the printer's RS232-C port with the 3 switches that are for the parallel port): on SW1: switch 1 off Baud rate set to 9600 baud switch 2 on switch 3 on switch 4 off 8 bits per character switch 5 off Parity checking disabled switch 6 off Odd parity (ignored) switch 7 off Printer operation (default value) switch 8 off Loopback test (default value) on SW2: switch 1 off DCD,CTS,DSR signals enabled switch 2 on RS232-C mode switch 3 off (must be off) switch 4 off Reverse channel set (default value) switch 5 off X-ON/X-OFF hand flow control switch 6 off (must be off) switch 7 on Receive buffer capacity set to 1936 bytes switch 8 on Description of the Print Server Routines ---------------------------------------- The NEC print server program consists of six main subroutines (procedures) which are compiled and then bound to the standard print server program (/COM/PRSVR) supplied by Apollo. The subroutines are: USER1_INIT, USER1_WRITE, USER1_FLUSH, USER1_CLOSE, USER1_SET_MODE, and USER1_RETURN_INFO. Section 6.5 of The DOMAIN System Administrator's Guide gives a general description of the standard print server supplied by Apollo and the steps needed to generate a print server for a printer other than those supported by Apollo. The arguments to the user-supplied subroutines and the format of the data passed to the subroutines are defined in the Apollo supplied insert files /SYS/INS/PRSVR.INS.PAS or /SYS/INS/PRSVR.INS.C which, while defining the options and data types doesn't really tell you what these various options are supposed to do. To get that information, you either have to call Apollo or you just have to play with the print server for awhile and see how it responds to your poking and prying. Unfortunately for those of you who program in Fortran, there is no insert file for that language - you just have to learn Pascal. The USER1_INIT subroutine is called once by the print server when it starts up. The subroutine is given the line number of the SIO line to which the printer is connected and the baud rate of that SIO line. Both of these numbers are read from the printer configuration file (NEC.CONFIG) by the print server at startup time. The USER1_INIT subroutine opens the specified SIO line for stream output and then sets the necessary SIO line characteristics. These include the use of X-ON/X-OFF by both the Apollo node and the printer (input_sync and host_sync), turning off the echoing of characters which the printer may send to the host (something which is never really done except for X-ON and X-OFF), disabling the quit character on the SIO line (so if the printer accidentally glitches it can't kill the print server process), and the parity, number of bits per character, and number of stop bits of the characters being transmitted. After defining the SIO line for the printer, USER1_INIT also initializes several strings of control characters for the printer, and resets the graphics buffer counter. Finally, USER1_INIT sends the printer-init command to make sure the printer is correctly initialized. Note that the printer-init command also sets the top-of-page to the current position of the paper, so the paper should be correctly position in the printer before starting the print server. The USER1_WRITE subroutine does most of the work of the print server. This subroutine is called from the print server to perform the actual output of the data to the NEC printer. The subroutine is given a buffer of up to 2048 characters and a buffer length. In practice, I have never seen the buffer length exceed 256 bytes, except when printing bitmap graphics. The USER1_WRITE subroutine checks the current printer output mode and then outputs the contents of the buffer according to the mode. The mode is set by the print server calling the USER1_SET_MODE subroutine prior to the first call to USER1_WRITE for the current output file. There are three possible output modes: 'text', 'transparent', and 'plot'. If the current output mode is 'text' (ie. a PRF command with no -PLOT or -TRANSPARENT switches), the print server keeps track of the current page and line counts and prints a page header and and page number at the top of each page. The print server calls USER1_WRITE one time for each buffer of text to be printed and simply inserts an extra call to USER1_WRITE at the top of each page to print the page header and page number. All that USER1_WRITE has to do in text mode is to dump the buffer to the printer. If the current output mode is 'transparent' (ie. a PRF -TRANSPARENT command), the print server sends the contents of the output file to the printer with no modifications. USER1_WRITE is called once for each buffer of output (same as with 'text' mode), but no page headers are inserted into the output stream. In this output mode USER1_WRITE does nothing except to dump the buffer to the NEC's output stream. If the current output mode is 'plot' (ie. a PRF -PLOT command), the print server checks that the output file is a GMF bitmap file, calls USER1_SET_MODE to set the size of the bitmap, and then calls USER1_WRITE once for each horizontal scan line in the bitmap. The print server will not attempt to print bitmap files unless the PLOT_MODE switch in the printer's configuration file (ie. NEC.CONFIG -- which is specified on the command line which started the print server) is set to ON. The print server will try to automatically scale the bitmap so that it is roughly the same size on the printed page as it is on the display screen of the Apollo. (I am assuming that the bitmap was created from a dump from the screen. Actually, the print server checks the bits-per-inch at which the bitmap was created and compares that against the bit-per-inch of the printer and then tries to scale the bitmap to the closest integer factor. The bits-per-inch of the printer is defined in the USER1_RETURN_INFO subroutine. You can define the bit-per-inch at which the bitmap is created through one of the GPR or GMR calls.) Since the NEC printer's resolution (360 bpi) is roughly three times that of the Apollo's display screen, the print server will replace each bit in the bitmap with a three dot by three dot block on the printer. This results in the print server calling USER1_WRITE three times the Y_BITMAP_SIZE with buffers containg three times as many bits as the X_BITMAP_SIZE. The -MAGNIFICATION option of the PRF command can be used to control the scaling of the bitmap by the print server. The default value (0) will cause the print server to try to scale the bitmap to the same size that it was on the screen (this results in a three to one scaling on the printer). In this case, the print server will simply tell the USER1_SET_MODE routine that the bitmap is three times the size of the bitmap on the screen and it will scale the bitmap before passing the data to the USER1_WRITE routine for printing. Since the NEC printer prints 24 horizontal scan line on each pass, the USER1_WRITE routine buffers up 24 complete scan lines, padding short lines with zeros, before dumping the buffer to the printer. Lines which are longer than the maximum printable by the NEC (2448 dots across the page) are truncated to the maximum length which can be printed. If the user has requested the bitmap to be inverted (ie. black and white reversed), then each byte of the scan line is inverted as it is buffered. The bits in each of the horizontal scan lines sent to USER1_WRITE by the print server are packed 8 bits to each byte, the high order bit of the byte being the leftmost bit on the scan line. Since the NEC printer (along with most other dot matrix printers) wants to print one complete vertical column of bits at a time, it is necessary to repack the bits in the scan lines which have been buffered before they can be sent to the printer. USER1_WRITE calls the WRITE_PLOT_BUFFER routine to repack the bits and output the repacked buffer to the printer. The NEC takes three 8-bit bytes for each column of 24 bits which it prints. The WRITE_PLOT_BUFFER subroutine also attempts to speed up the output process by stripping zero bytes off of the ending the buffer to avoid the expense of having to repack and transmit these bytes. (Note that a buffer of 24 horizontal lines of 1024 bits requires 3072 bytes to be sent to the printer - which takes almost 3 seconds to do at 9600 baud.) The USER1_SET_MODE subroutine is called by the printer server prior to the output of each file being printed to set the proper printer output mode and to such things as the bitmap size (for plot mode files). The print server may also call USER1_SET_MODE after the end of an output file to reset the printer mode or to change the printer mode to 'text' so it can output a top-of-page command (ie. a form feed) after finishing the printing of a bitmap. Note that we can not use the NEC's printer-init command to reset the printer's mode to the default text mode. This is because the printer-init command resets the printer's top-of-page to the current paper position, and the print server may call USER1_SET_MODE at any point on the page. The USER1_FLUSH subroutine is called once at the end of each file output by the print server and also in between changing printer output modes. This routine checks if the printer is 'plot' and also checks if there is a partially full buffer of bitmap data. If this is the case, USER1_FLUSH will call USER1_WRITE with a dummy output buffer to force the output of the final buffer of the bitmap data. USER1_FLUSH then resets all of the page, line, and buffer counters used during the output of the file. The USER1_CLOSE subroutine is only called when the print server is stopping permanently (as opposed to waiting between files). All that it does is to close the printer's output stream. Version 1 --------- Original version of the NEC P9XL print server derived from the Toshiba P1351 print server. Version 2 --------- Cleaned up code a bit. All minor changes not visible to the user. Files Needed to Build the Print Server -------------------------------------- The files which are provided for the NEC P9XL print server are: USER1.NEC.DOC - This file. USER1.NEC.DOC.INSTALL - Notes on how to install the print server. USER1.NEC.PAS - The Pascal sources for the print server. USER1.INS.PAS - An edited version of the standard Apollo insert file, /SYS/INS/PRSVR.INS.PAS, which is used by USER1.NEC.PAS to define the data types and structures used by the print server. USER1.NEC.BLD - A shell script file for compiling the USER1 routines (and their subroutines) and binding them with the Apollo supplied print server. NEC.CONFIG - The configuration file for the NEC printer to be given as an argument to the print server when it is started. PRSVR.USER1.NEC - A ready to run NEC print server. Just in case you don't have a Pascal compiler. This is the file which is produced by USER1.BLD. You will also need the following standard Apollo-supplied files: /SYS/INS/BASE.INS.PAS - These are all standard insert files which /SYS/INS/SIO.INS.PAS are used by USER1.NEC.PAS. /SYS/INS/STREAMS.INS.PAS /SYS/INS/PGM.INS.PAS /COM/PRSVR - The standard Apollo print server which must be bound with the NEC routines to produce a working print server. Used in USER1.BLD to produce PRSVR.USER1.NEC. Bugs, Questions, and Improvements --------------------------------- If you find a bugs in the print server, have questions on how to install or use it, or have a good idea for improving the program please feel free to contact me at the address below. David M. Krowitz MIT dept. of Earth, Atmospheric, and Planetary Sciences Room 54-527 Cambridge, MA 02139 (617) 253-6180 network mailing address: mit-erl!mit-kermit!krowitz@eddie.mit.edu mit-erl!mit-kermit!krowitz@mit-eddie.arpa krowitz@mit-mc.arpa (in order of decreasing preference) SHAR_EOF chmod +x 'user1.nec.doc' fi # end of overwriting check if test -f 'user1.nec.doc.install' then echo shar: will not over-write existing file "'user1.nec.doc.install'" else cat << \SHAR_EOF > 'user1.nec.doc.install' ******************************************************************************* ***** ***** ***** USER1.NEC.DOC.INSTALL ***** ***** Version 1 ***** ***** Installing the NEC Pinwriter P9XL Print Server ***** ***** ***** ***** Copyright (c) 1987 ***** ***** David M. Krowitz ***** ***** Massachusetts Institute of Technology ***** ***** Department of Earth, Atmosheric, and Planetary Sciences ***** ******************************************************************************* Read the general installation and documentation notes in USER1.NEC.DOC if you have not already done so. Then use the shell script file USER1.NEC.BLD to compile the NEC device-driver routines and to bind them to the standard Apollo printer server located in /COM/PRSVR. This will create the NEC Pinwriter version of the print server (PRSVR.USER1.NEC) in you working directory. Then edit the print server configuration file (NEC.CONFIG) and make sure it contains the proper SIO line number and baud rate for your installation. Next copy the new print server (PRSVR.USER1.NEC) and the print server configuration file (NEC.CONFIG) to the system directory from which you normally run the print server on your system. /COM or /SYS/NODE_DATA are two common directories which we use. You can then run the print server using the command: /COM/PRSVR.USER1.NEC /COM/NEC.CONFIG (assuming the files are stored in /COM). This will run the server in the current window. The server will be stopped when you log out. If you want to run the print server automatically when the system is brought up, add the following command line to your installation's startup file in /SYS/NODE_DATA: ### ### To startup user-defined print server for the NEC Pinwriter printer ### CPS /COM/PRSVR.USER1.NEC -N PRINT_SERVER /COM/NEC.CONFIG (again, assuming the files are kept in /COM). This will run the print server automatically when the node is brought up and will give it the process a server status so that you can logout and leave the print server running for other users on the network. Apollo's standard print server (and the NEC version) looks for files to be printed in the /SYS/PRINT directory of the node on which the print server is running. If you want files from other nodes on the network to be automatically printed on the NEC you should delete the /SYS/PRINT directories on those nodes and replace them with links to the /SYS/PRINT directory on the node running the print server. Otherwise, users can use the command: PRF -S //NODE_NAME to have the PRF command send the file to the node which has the printer attached to it. More information on the installation and use of print servers can be found in 'Administering Your DOMAIN System', section 5.7. SHAR_EOF chmod +x 'user1.nec.doc.install' fi # end of overwriting check if test -f 'user1.nec.pas' then echo shar: will not over-write existing file "'user1.nec.pas'" else cat << \SHAR_EOF > 'user1.nec.pas' {********************************************************************************** ***** ***** ***** USER1.NEC.PAS ***** ***** ***** ***** Serial Printer Driver for the NEC Pinwriter P9XL dot-matrix printer ***** ***** Version 3 ***** ***** David M. Krowitz July 30, 1987. ***** ***** ***** ***** Copyright (c) 1987 ***** ***** David M. Krowitz ***** ***** Massachusetts Institute of Technology ***** ***** Department of Earth, Atmosheric, and Planetary Sciences ***** ********************************************************************************** } MODULE USER1_NEC; %NOLIST; %INSERT '/sys/ins/base.ins.pas'; %INSERT '/sys/ins/sio.ins.pas'; %INSERT '/sys/ins/streams.ins.pas'; %INSERT '/sys/ins/pgm.ins.pas'; %INSERT 'user1.ins.pas'; %LIST; CONST {Program version number - should be same as in file header above} version_number = 3; {Definitions of standard ascii control characters} nul = CHR(0); {null character} bs = CHR(8); {backspace (control-H)} tab = CHR(9); {tab (control-I)} lf = CHR(10); {line feed (control-J)} vt = CHR(11); {vertical tab (control-K)} ff = CHR(12); {form feed (control-L)} cr = CHR(13); {carriage return (control-M)} sub = CHR(26); {sub (control-Z)} esc = CHR(27); {escape} fs = CHR(28); {form seperator} rs = CHR(30); {rs} {Define maximum bitmap size which can be printed} max_bitmap_size = 2448; {maximum number of pixels across the page (at 180 dpi)} max_buffer_size = 306; {maximum buffer size to hold bitmap (2448/8)} TYPE str1_t = packed array[1..1] of char; str2_t = packed array[1..2] of char; str3_t = packed array[1..3] of char; str4_t = packed array[1..4] of char; str5_t = packed array[1..5] of char; str6_t = packed array[1..6] of char; str7_t = packed array[1..7] of char; str8_t = packed array[1..8] of char; str9_t = packed array[1..9] of char; str10_t = packed array[1..10] of char; str11_t = packed array[1..11] of char; str12_t = packed array[1..12] of char; str13_t = packed array[1..13] of char; str14_t = packed array[1..14] of char; str15_t = packed array[1..15] of char; str16_t = packed array[1..16] of char; str128_t = packed array[1..128] of char; bitmap_buffer_t = packed array[1..24,1..max_buffer_size] of char; print_buffer_t = packed array[1..max_bitmap_size,1..3] of char; VAR {Definitions of control sequences for the NEC Pinwriter P9XL printer} init_printer: str2_t; {initialize printer characteristics} pitch5: str3_t; {use 5 characters per inch when printing} pitch6: str3_t; {use 6 characters per inch when printing} pitch10: str3_t; {use 10 characters per inch when printing} pitch12: str3_t; {use 12 characters per inch when printing} pitch17: str3_t; {use 17 characters per inch when printing} pitch20: str3_t; {use 20 characters per inch when printing} weight_bold: str2_t; {use boldface printing} weight_medium: str2_t; {use regular printing} font_draft: str3_t; {use draft quality printing} font_lq: str3_t; {use letter quality printing} image_data: str5_t; {start 24 pin by nnnn column bit map image transfer} lpi_6: str3_t; {set vertical spacing to 1/6" for default text size} lpi_8: str3_t; {set vertical spacing to 1/8" for text output} plot_vert_spacing: str3_t; {set vertical spacing to 24/180" for bit map graphics} crlf: str2_t; {carriage-return, line-feed sequence} {Defintions of global variables} x_bitmap_size: pinteger; {x dimension of GMR bit map being printed} y_bitmap_size: pinteger; {y dimension of GMR bit map being printed} scale_factor: pinteger; {factor by which bitmap has been scaled up by top-level routines} bitmap_buffer: bitmap_buffer_t; {buffer of 24 lines of 2448 bits to hold data for one pass of the NEC printer's print-head} bcount: pinteger; {count of number of buffers of GMR data} invert_image: boolean; {TRUE if user requested black/white reversed for bitmap} plot_flag: boolean; {TRUE if we have been printing a bitmap} trans_flag: boolean; {TRUE if we have been printing in transparent mode} printer_mode: pr_$data_format_t; {printer mode: text, transparent, or plot} server_ptr: server_db_ptr_t; {pointer to database set up by server} driver_ptr: driver_db_ptr_t; {pointer to database of NEC's abilities} stream_id: stream_$id_t; {stream id returned by STREAM_$OPEN} status: status_$t; {status returned by SIO and STREAM calls} seek_key: stream_$SK_t; {seek_key returned by STREAM calls} testmask: array[1..8] of integer; {Constants for testing bits in a byte} setmask: array[1..8] of integer; {Constants for setting bits in a byte} PROCEDURE USER1_INIT ( IN sio_line: integer; IN sio_speed: UNIV sio_$value_t ); VAR sioname: array[1..3] of str9_t; {names of SIO lines for STREAM_$OPEN call} i,j: pinteger; {counters} BEGIN {Identify ourselves.} WRITELN ('This is the NEC Pinwriter P9XL Printer Server - Version ',version_number:-1,'.'); WRITELN; {Open I/O stream and set SIO line characteristics} sioname[1] := '/dev/sio1'; sioname[2] := '/dev/sio2'; sioname[3] := '/dev/sio3'; stream_$open (sioname[sio_line],9,stream_$append,stream_$no_conc_write, stream_id,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not open output stream: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$speed,sio_speed,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$SPEED: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$no_nl,true,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$NO_NL on: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$nlc_delay,0,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$NLC_DELAY to 0: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$input_sync,true,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$INPUT_SYNC on: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$host_sync,true,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$HOST_SYNC on: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$no_echo,true,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$NO_ECHO on: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$cts_enable,false,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$CTS_ENABLE off: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$quit_enable,false,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$QUIT_ENABLE off: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$parity,sio_$no_parity,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$PARITY none: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$bits_per_char,sio_$8bpc,status); IF (status.all <> STATUS_$OK) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$BPC to 8 bits/char: ', sioname[sio_line],' ****'); PGM_$EXIT; END; sio_$control (stream_id,sio_$stop_bits,sio_$stop_1,status); IF (status.fail = TRUE) THEN BEGIN WRITELN ('**** USER1_INIT: Error - could not set SIO_$STOP to 1 stop bit: ', sioname[sio_line],' ****'); PGM_$EXIT; END; {Initialize strings of control characters for printer} init_printer[1] := fs; {Reset the printer} init_printer[2] := '@'; pitch5[1] := esc; {Set horizontal spacing to 5 char/inch} pitch5[2] := '!'; pitch5[3] := CHR(32); pitch6[1] := esc; {Set horizontal spacing to 6 char/inch} pitch6[2] := '!'; pitch6[3] := CHR(33); pitch10[1] := esc; {Set horizontal spacing to 10 char/inch} pitch10[2] := '!'; pitch10[3] := CHR(0); pitch12[1] := esc; {Set horizontal spacing to 12 char/inch} pitch12[2] := '!'; pitch12[3] := CHR(1); pitch17[1] := esc; {Set horizontal spacing to 17 char/inch} pitch17[2] := '!'; pitch17[3] := CHR(4); pitch20[1] := esc; {Set horizontal spacing to 20 char/inch} pitch20[2] := '!'; pitch20[3] := CHR(5); weight_bold[1] := esc; {Set printing to boldface} weight_bold[2] := 'G'; weight_medium[1] := esc; {Set printing to regular (non boldface)} weight_medium[2] := 'H'; font_draft[1] := esc; {Set printing to draft quality} font_draft[2] := 'x'; font_draft[3] := CHR(0); font_lq[1] := esc; {Set printing to letter quality} font_lq[2] := 'x'; font_lq[3] := CHR(1); image_data[1] := esc; {Send 24 pin, 180 dpi, bitmap graphics data} image_data[2] := '*'; image_data[3] := CHR(39); image_data[4] := '0'; {Number of columns to print MOD 256} image_data[5] := '0'; {Number of columns to print DIV 256} plot_vert_spacing[1] := esc; {Set vertical spacing to 24/180 inch} plot_vert_spacing[2] := '3'; plot_vert_spacing[3] := CHR(24); lpi_6[1] := esc; {Set vertical spacing to 6 lines inch (text)} lpi_6[2] := '2'; lpi_8[1] := esc; {Set vertical spacing to 8 lines inch (text)} lpi_8[2] := '0'; crlf[1] := cr; crlf[2] := lf; {Initialize the graphics output variables} bcount := 0; {No scan lines of graphics output waiting to be printed} scale_factor := 1; {Doesn't seem to be used in SR9.0 so must init ourselves for SR8 compatibility} invert_image := FALSE; {Do normal bitmap printing (not b/w reversed)} plot_flag := FALSE; {Not printing a bitmap right now} trans_flag := FALSE; {Not printing in transparent mode, either} {Initialize bit testing and bit setting masks} testmask[1] := 16#80; testmask[2] := 16#40; testmask[3] := 16#20; testmask[4] := 16#10; testmask[5] := 16#08; testmask[6] := 16#04; testmask[7] := 16#02; testmask[8] := 16#01; setmask[1] := 16#80; setmask[2] := 16#40; setmask[3] := 16#20; setmask[4] := 16#10; setmask[5] := 16#08; setmask[6] := 16#04; setmask[7] := 16#02; setmask[8] := 16#01; {Initialize the printer settings} STREAM_$PUT_CHR (stream_id,ADDR(init_printer),2,seek_key,status); printer_mode := pr_$text; END; {End of USER1_INIT} PROCEDURE USER1_WRITE ( IN buffer: UNIV pr_$buf_t; IN buffer_length: pinteger ); PROCEDURE USER1_WRITE_TEXT ( IN buffer: UNIV pr_$buf_t; IN buffer_length: pinteger ); VAR output_buffer: pr_$buf_t; output_length: pinteger; i,j,k: pinteger; BEGIN STREAM_$PUT_CHR (stream_id,ADDR(buffer),buffer_length,seek_key,status); END; {End of USER1_WRITE_TEXT} PROCEDURE USER1_WRITE_TRANSPARENT ( IN buffer: UNIV pr_$buf_t; IN buffer_length: pinteger ); BEGIN STREAM_$PUT_CHR (stream_id,ADDR(buffer),buffer_length,seek_key,status); END; {End of USER1_WRITE_TRANSPARENT} PROCEDURE WRITE_PLOT_BUFFER ( IN buffer: bitmap_buffer_t; IN bitmap_length: pinteger ); VAR print_buffer: print_buffer_t; {NEC output buffer} buffer_length: pinteger; {length of non-zero data in input buffer} data_length: pinteger; {length of non-zero data in print_buffer} i,j,k: pinteger; {index counters} ii,jj,kk: pinteger; {buffer subscript variables} iii,jjj,kkk: pinteger; {more buffer subscript variables} bit_value: pinteger; {value of bit being repacked} temp1: pinteger; {holds bits being repacked} temp2: pinteger; {tests bytes to see if repacking needed} BEGIN {Find end of the non-zero data in the input buffer. Don't want to waste time by having to repack a lot of zero bytes at the end of a line.} buffer_length := (((bitmap_length-1) div 8) +1); k := 0; REPEAT FOR j := 1 TO 24 DO BEGIN IF buffer[j,buffer_length] <> CHR(0) THEN k := 1; END; IF k = 0 THEN buffer_length := buffer_length-1; UNTIL (k <> 0) OR (buffer_length = 0); {If the print buffer is all zeroes, then just output a carriage-return line-feed sequence. Otherwise, repack and print the non-zero portion of the printer buffer.} IF buffer_length = 0 THEN BEGIN STREAM_$PUT_CHR (stream_id,ADDR(crlf),2,seek_key,status); END ELSE BEGIN {Repack the bitmap from 24 horizontal rows of BUFFER_LENGTH*8 bits (8 bits per byte) into BUFFER_LENGTH*8 columns of 3 bytes each (8 bits per byte).} FOR i := 1 TO buffer_length DO BEGIN ii := (i-1)*8; FOR j := 0 TO 2 DO BEGIN jj := j*8; jjj := j+1; {Check if 8 byte high by 8 bit wide section of bitmap is all zeros. If so, then we don't need to repack the bits in this section, we just set the output buffer to the correct constants. If the area is all ones, we also do not have to repack the bitmap, we just set the output buffer to a different constant.} temp2 := 0; FOR k := 1 TO 8 DO BEGIN temp2 := temp2+ORD(buffer[(jj+k),i]); END; IF (temp2 = 0) THEN BEGIN FOR iii := 1 TO 8 DO BEGIN {Section is all zeros - don't need to repack} print_buffer[ii+iii,jjj] := CHR(0); END; END ELSE IF (temp2 = 8*16#FF) THEN BEGIN FOR iii := 1 TO 8 DO BEGIN {Section is all ones - don't need to repack} print_buffer[ii+iii,jjj] := CHR(16#FF); END; END ELSE BEGIN FOR iii := 1 TO 8 DO BEGIN {Section is non-zero - repack individual bits} temp1 := 0; {Init the bits} FOR k := 1 TO 8 DO BEGIN {Repack 8 bits into byte} bit_value := (ORD(buffer[(jj+k),i])&testmask[iii]); IF bit_value <> 0 THEN BEGIN temp1 := temp1!setmask[k]; END; END; print_buffer[ii+iii,jjj] := CHR(temp1); {Put byte in output buffer} END; END; END; END; {Find end of the non-zero data in the printer output buffer. Don't want to waste time by having to transmit a lot of zeroes at the end of a line.} IF buffer_length*8 < bitmap_length THEN BEGIN data_length := buffer_length*8; END ELSE BEGIN data_length := bitmap_length; END; k := 0; REPEAT FOR j := 1 TO 3 DO BEGIN IF print_buffer[data_length,j] <> CHR(0) THEN k := 1; END; IF k = 0 THEN data_length := data_length-1; UNTIL (k <> 0) OR (data_length = 0); {Set up the bitmap image data transfer header and output buffer.} image_data[4] := CHR(data_length MOD 256); image_data[5] := CHR(data_length DIV 256); STREAM_$PUT_CHR (stream_id,ADDR(image_data),5,seek_key,status); STREAM_$PUT_CHR (stream_id,ADDR(print_buffer),data_length*3,seek_key,status); STREAM_$PUT_CHR (stream_id,ADDR(crlf),2,seek_key,status); END; END; {End of WRITE_PLOT_BUFFER} PROCEDURE USER1_WRITE_PLOT ( IN buffer: UNIV pr_$buf_t; IN buffer_length: pinteger ); VAR i: integer; {counter} j: integer; {size of bitmap buffer in bytes} k: integer; {size of bitmap in pixels across the page} BEGIN {Check that BUFFER_LENGTH does not exceed the maximum buffer size and that X_BITMAP_SIZE*SCALE_FACTOR does not exceed the maximum printable bitmap size.} IF buffer_length <= max_buffer_size THEN j := buffer_length ELSE j := max_buffer_size; IF x_bitmap_size*scale_factor <= max_bitmap_size THEN k := x_bitmap_size*scale_factor ELSE k := max_bitmap_size; {Buffer up 24 lines by X_BITMAP_SIZE*SCALE_FACTOR columns of the bitmap and then dump them out to the printer. Pad short lines with zeros. Invert the buffer if the user requested black/white reversal for printing bitmap.} bcount := bcount+1; i:= 0; IF (invert_image = FALSE) THEN BEGIN WHILE i < j DO BEGIN i := i+1; bitmap_buffer[bcount,i] := buffer[i]; END; WHILE i < (((k-1) div 8)+1) DO BEGIN i := i+1; bitmap_buffer[bcount,i] := CHR(0); END; END ELSE BEGIN WHILE i < j DO BEGIN i := i+1; bitmap_buffer[bcount,i] := CHR(~ORD(buffer[i])); END; WHILE i < (((k-1) div 8)+1) DO BEGIN i := i+1; bitmap_buffer[bcount,i] := CHR(16#FF); END; END; IF bcount = 24 THEN BEGIN write_plot_buffer (bitmap_buffer,k); bcount := 0; END; END; {End of USER1_WRITE_PLOT} BEGIN {Beginning of actual USER1_WRITE code} {Determine printer mode and dispatch for output of buffer} CASE printer_mode OF pr_$text: user1_write_text (buffer,buffer_length); pr_$transparent: user1_write_transparent (buffer,buffer_length); pr_$plot: user1_write_plot (buffer,buffer_length) END; END; {End of USER1_WRITE} PROCEDURE USER1_SET_MODE ( IN mode: pr_$set_op_t; IN data: pr_$data_rec_t ); BEGIN CASE mode OF pr_$font_weight: IF (data.font_weight = pr_$bold) THEN STREAM_$PUT_CHR (stream_id,ADDR(weight_bold),2,seek_key,status) ELSE STREAM_$PUT_CHR (stream_id,ADDR(weight_medium),2,seek_key,status); pr_$font_size: ; pr_$text_precision: CASE data.text_precision OF pr_$draft: STREAM_$PUT_CHR (stream_id,ADDR(font_draft),3,seek_key,status); pr_$letter_quality: STREAM_$PUT_CHR (stream_id,ADDR(font_lq),3,seek_key,status); END; pr_$data_format: BEGIN printer_mode := data.data_format; CASE printer_mode OF pr_$text: BEGIN STREAM_$PUT_CHR (stream_id,ADDR(lpi_6),3,seek_key,status); END; pr_$transparent: BEGIN STREAM_$PUT_CHR (stream_id,ADDR(lpi_6),3,seek_key,status); trans_flag := TRUE; END; pr_$plot: BEGIN STREAM_$PUT_CHR (stream_id,ADDR(plot_vert_spacing),3,seek_key,status); plot_flag := TRUE; END; END; END; pr_$pitch: CASE ROUND(data.pitch*10.0) OF 50: STREAM_$PUT_CHR (stream_id,ADDR(pitch5),3,seek_key,status); 60: STREAM_$PUT_CHR (stream_id,ADDR(pitch6),3,seek_key,status); 100: STREAM_$PUT_CHR (stream_id,ADDR(pitch10),3,seek_key,status); 120: STREAM_$PUT_CHR (stream_id,ADDR(pitch12),3,seek_key,status); 170: STREAM_$PUT_CHR (stream_id,ADDR(pitch17),3,seek_key,status); 200: STREAM_$PUT_CHR (stream_id,ADDR(pitch20),3,seek_key,status); END; pr_$x_dimension: x_bitmap_size := data.x_dimension; pr_$y_dimension: y_bitmap_size := data.y_dimension; pr_$rep_factor: scale_factor := data.rep_factor; pr_$config: ; pr_$copies: ; pr_$server_db: BEGIN server_ptr := data.server_db_ptr; printer_mode := server_ptr^.print_mode; IF server_ptr^.lq = FALSE THEN STREAM_$PUT_CHR (stream_id,ADDR(font_draft),3,seek_key,status) ELSE STREAM_$PUT_CHR (stream_id,ADDR(font_lq),3,seek_key,status); IF server_ptr^.bw_rev = TRUE THEN invert_image := TRUE ELSE invert_image := FALSE; CASE printer_mode OF pr_$text: BEGIN CASE ROUND (server_ptr^.lpi*10) OF 60: STREAM_$PUT_CHR (stream_id,ADDR(lpi_6),3,seek_key,status); 80: STREAM_$PUT_CHR (stream_id,ADDR(lpi_8),3,seek_key,status); END; CASE ROUND(server_ptr^.cpi*10.0) OF 50: STREAM_$PUT_CHR (stream_id,ADDR(pitch5),3,seek_key,status); 60: STREAM_$PUT_CHR (stream_id,ADDR(pitch6),3,seek_key,status); 100: STREAM_$PUT_CHR (stream_id,ADDR(pitch10),3,seek_key,status); 120: STREAM_$PUT_CHR (stream_id,ADDR(pitch12),3,seek_key,status); 170: STREAM_$PUT_CHR (stream_id,ADDR(pitch17),3,seek_key,status); 200: STREAM_$PUT_CHR (stream_id,ADDR(pitch20),3,seek_key,status); END; IF (server_ptr^.weight = pr_$bold) THEN BEGIN STREAM_$PUT_CHR (stream_id,ADDR(weight_bold),2,seek_key,status); END ELSE BEGIN STREAM_$PUT_CHR (stream_id,ADDR(weight_medium),2,seek_key,status); END; END; pr_$transparent: BEGIN STREAM_$PUT_CHR (stream_id,ADDR(lpi_6),3,seek_key,status); trans_flag := TRUE; END; pr_$plot: BEGIN x_bitmap_size := server_ptr^.bitmap_size.x; y_bitmap_size := server_ptr^.bitmap_size.y; STREAM_$PUT_CHR (stream_id,ADDR(plot_vert_spacing),3,seek_key,status); plot_flag := TRUE; END; END; END; END; END; {End of USER1_SET_MODE} PROCEDURE USER1_RETURN_INFO ( IN query: pr_$inq_op_t; OUT data: pr_$data_rec_t ); BEGIN CASE query OF pr_$bpi: BEGIN data.bpi.x := 180; data.bpi.y := 180; END; pr_$rep_ability: data.rep_ability := false; pr_$driver_db: BEGIN driver_ptr := data.driver_db_ptr; WITH driver_ptr^ DO BEGIN valid := TRUE; copies := FALSE; cpi[1] := 10.0; {Put the built-in font first so it will be default font} cpi[2] := 12.0; cpi[3] := 5.0; {Put the expanded fonts next} cpi[4] := 6.0; cpi[5] := 17.0; {Put the compressed fonts next} cpi[6] := 20.0; lpi[1] := 6.0; lpi[2] := 8.0; resolution[1] := 180; res_min := 180; res_max := 180; magnification[1] := 1; color_format := none; bw_rev := TRUE; image_rotation := FALSE; END; END; END; END; {End of USER1_RETURN_INFO} PROCEDURE USER1_FLUSH; VAR temp: pr_$data_format_t; {printer mode: text, transparent, or plot} dummy: pr_$buf_t; {dummy buffer} BEGIN {If printer is in plot mode then make sure that the last buffer of bitmap data is filled out to 24 lines and printed. If the printer mode is TRANSPARENT or PLOT, then we need to eject the page from the printer. Under SR8, the PRSVR would change the printer mode to TEXT and issue a form feed after the end of a PLOT or TRANSPARENT mode output. Under SR9, we have to do the page ejection ourselves.} IF (bcount <> 0) THEN BEGIN temp := printer_mode; printer_mode := pr_$plot; REPEAT user1_write(dummy,0); UNTIL bcount = 0; printer_mode := temp; END; IF (plot_flag = TRUE) OR (trans_flag = TRUE) THEN BEGIN STREAM_$PUT_CHR (stream_id,ADDR(ff),1,seek_key,status); END; plot_flag := FALSE; trans_flag := FALSE; END; {End of USER1_FLUSH} PROCEDURE USER1_CLOSE; BEGIN STREAM_$CLOSE (stream_id,status); END; {End of USER1_CLOSE} {***** End of module USER1_NEC *****} SHAR_EOF chmod +x 'user1.nec.pas' fi # end of overwriting check # End of shell archive exit 0