ACCESSING PRINTER_DAT
=====================
by Dilwyn Jones
This article sets out to explain the file structure of the PRINTER_DAT
printer driver used by Quill, Archive and Abacus. A program is
presented which prints to the screen all of the codes used in the
driver and in so doing, shows how you can access the driver for use in
your own programs, for example, or to write an alternative printer
driver editor. The program listed works with the Xchange versions of
these printer drivers too, since the file format appears to be the
same.
The program listed uses no toolkit commands, it should work on all
versions of the QL. If you have Toolkit 2 or other basic extensions
toolkit, it should be possible to rewrite the program to use some
extensions such as string and byte fetching extensions to greatly
simplify the program.
First, I shall explain the file format used. It was quite tricky to
work out the file format and I hope I got it right since I do not have
formal documentation on Quill.
4 bytes "prt1" characters used to identify printer driver
1 byte total of lengths of code strings below, but if length
bytes are 255, this value is not added to this byte
10 bytes driver name, which is always 10 characters long,
filled with extra spaces to make it 10 characters
1 byte port type
if port type = 1 or 2: ser1 or ser2 respectively
1 byte parity code (0=none,1=space,2=mark,3=odd,4=even
1 word baudrate, stored as 2 byte integer, MSB first
15 bytes spaces to pad to file position 34
if port type = 0 : parallel or other non-serial port
1 byte length of name of printer port (e.g. 3 for PAR)
x bytes spaces to pad out to file position 34, where x is
17-(length of name of printer port)
1 byte number of lines per page
1 byte number of characters per line
1 byte forms type, 1 for continuous, 0 for cut sheets
4 bytes appear to be spare, filled with spaces or nulls.
The following data all consist of strings containing codes for various
printer functions. The format is unusual in that the string consists of
one byte for the length of the string rather than 2 bytes normally used
on the QL. If the length byte is 0 or 255 (hex FF), there are no other
codes. 0 appears to represent NO CODES, or NONE as it appears in
INSTALL_BAS. 255 appears to represent an inbuilt default value.
string end of line codes
string preamble codes
string postamble codes
string bold on codes
string bold off codes
string underline on codes
string underline off codes
string subscript on codes
string subscript off codes
string superscript on codes
string superscript off codes
Next comes the translate character strings. The format of the strings
is the same (byte for length, followed by that number of codes). The
first character after the length byte is the character code translated
from, and the remainder of the characters indicate what that first
character is translated to.
10 strings translate 1 to 10
1 byte usually a linefeed code.
Note that the strings are in an unusual format. One byte indicates how
many characters to follow in that string. If that length byte is 0 it
means 'no codes for this function'. If the length byte is 255 it means
use an inbuilt default value, used for only a few cases. This does not
get added to the 'total of lengths of code string' byte.
That lot was a bit of a mouthful. Now for the easy bit, the program to
decode it all. I cannot guarantee this will work on every printer_dat
and xchange_dat file, but it seems to work for the ones I have tried
with it. It asks for the filename of the printer driver file (if you
just press ENTER it uses the default of 'printer_dat' on FLP1_). It
reads it and then prints it to the screen. Since there is a long list
to print, it might scroll off the screen. To pause it to view the firt
half, press CTRL F5 to freeze the screen momentarily.
You can use similar techniques to read printer_dat files for your own
programs. The data ends up in strings in this program and by reading
the codes into strings in your programs, you can simply send the codes
to the printer with a PRINT statement when required. For example,
"PRINT #printer_channel,bold_on$;" to turn bold printing on.
HINT: The Psion programs allow you to specify a parallel printer port
in place of the usual SER1 or SER2. This program will print that out,
of course. But some versions of the Psion programs seem to have
difficulty with the name PAR - they require this to be entered as 2PAR
for some reason, I don't know why the number TWO is required before the
name. Of course, if you use serial ports, you will not care (and not
know) about this.
Within Quill itself, if the driver is configured for, say, SER1 or
SER2, you can still print to another device such as PAR or network by
preceding the device name with an underscore '_' character, e.g. F3
Print, Current, Whole, To _PAR.
100 REMark PRINTER_DAT reader by Dilwyn Jones 1994
110 CLS : CLS #0
120 INPUT#0,'Enter name of printer driver (default PRINTER_DAT):';driver$
130 IF driver$ = '' : driver$ = 'FLP1_PRINTER_DAT'
140 OPEN_IN #3,driver$
150 ident$ = ' ' : REMark 4 spaces
160 FOR a = 1 TO 4 : ident$(a) = INKEY$(#3)
170 IF ident$ <> 'prt1' : PRINT #0,'Unsuitable file' : STOP
180 code_strings_length = CODE(INKEY$(#3))
190 driver_name$ = FILL$(' ',10)
200 FOR a = 1 TO 10 : driver_name$(a) = INKEY$(#3)
210 PRINT'DRIVER NAME : ';driver_name$
220 :
230 REMark get port details
240 port_type = CODE(INKEY$(#3))
250 IF port_type = 1 OR port_type = 2 THEN
260 REMark serial port
270 PRINT 'PRINTER PORT : SER';port_type
280 parity = CODE(INKEY$(#3)) : PRINT 'PARITY : ';
290 SELect ON parity
300 =0:PRINT'NONE'
310 =1:PRINT'SPACE'
320 =2:PRINT'MARK'
330 =3:PRINT'ODD'
340 =4:PRINT'EVEN'
350 END SELect
360 baudrate = 256*CODE(INKEY$(#3))+CODE(INKEY$(#3))
370 PRINT 'BAUDRATE : ';baudrate
380 FOR a = 1 TO 15 : temp$ = INKEY$(#3) : REMark skip the spaces
390 ELSE
400 REMark parallel or other type of port
410 PRINT'PRINTER PORT : ';
420 portlen=CODE(INKEY$(#3)) : REMark name of port
430 FOR a = 1 TO portlen : PRINT INKEY$(#3);
440 PRINT
450 FOR a = 1 TO 17-portlen : temp$ = INKEY$(#3) : REMark skip spaces
460 END IF
470 lines_per_page = CODE(INKEY$(#3))
480 PRINT'LINES PER PAGE : ';lines_per_page
490 chars_per_line = CODE(INKEY$(#3))
500 PRINT'CHARACTERS PER LINE : ';chars_per_line
510 forms_type = CODE(INKEY$(#3))
520 PRINT'FORMS TYPE : ';
530 IF forms_type = 1 THEN
540 PRINT'Continuous'
550 ELSE
560 PRINT'Cut'
570 END IF
580 FOR a = 1 TO 4 : temp$ = INKEY$(#3) : REMark skip spaces
590 :
600 REMark read code strings for end of line, pre/postamble, bold etc
610 PRINT'END OF LINE CODE : ';
620 eol$ = FETCH_CODE_STRING$ : CODE_STRING eol$
630 PRINT'PREAMBLE CODES : ';
640 preamble$ = FETCH_CODE_STRING$ : CODE_STRING preamble$
650 PRINT'POSTAMBLE CODES : ';
660 postamble$ = FETCH_CODE_STRING$ : CODE_STRING postamble$
670 PRINT'BOLD ON CODES : ';
680 bold_on$ = FETCH_CODE_STRING$ : CODE_STRING bold_on$
690 PRINT'BOLD OFF CODES : ';
700 bold_off$ = FETCH_CODE_STRING$ : CODE_STRING bold_off$
710 PRINT'UNDERLINE ON CODES : ';
720 under_on$ = FETCH_CODE_STRING$ : CODE_STRING under_on$
730 PRINT'UNDERLINE OFF CODES : ';
740 under_off$ = FETCH_CODE_STRING$ : CODE_STRING under_off$
750 PRINT'SUBSCRIPT ON CODES : ';
760 sub_on$ = FETCH_CODE_STRING$ : CODE_STRING sub_on$
770 PRINT'SUBSCRIPT OFF CODES : ';
780 sub_off$ = FETCH_CODE_STRING$ : CODE_STRING sub_off$
790 PRINT'SUPERSCRIPT ON CODES : ';
800 sup_on$ = FETCH_CODE_STRING$ : CODE_STRING sup_on$
810 PRINT'SUPERSCRIPT OFF CODES : ';
820 sup_off$ = FETCH_CODE_STRING$ : CODE_STRING sup_off$
830 :
840 REMark translates 1 - 10
850 FOR tr = 1 TO 10
860 PRINT'TRANSLATE ';tr;': ';
870 t$ = FETCH_CODE_STRING$
880 IF LEN(t$)=1 THEN
890 CODE_STRING t$
900 ELSE
910 PRINT'FROM ';CODE(t$);' (';t$(1);') TO ';
920 CODE_STRING t$(2 TO LEN(t$))
930 END IF 940 END FOR tr
950 CLOSE #3
960 :
970 DEFine PROCedure CODE_STRING (str)
980 LOCal a
990 IF str = CHR$(255) THEN PRINT'DEFAULT' : RETurn
1000 IF str = CHR$(0) THEN PRINT'NONE' : RETurn
1010 FOR a = 1 TO LEN(str)
1020 PRINT CODE(str(a));
1030 IF a < LEN(str) THEN PRINT','; : ELSE PRINT : END IF
1040 END FOR a
1050 END DEFine CODE_STRING
1060 :
1070 DEFine FuNction FETCH_CODE_STRING$
1080 LOCal a,cd_length,temp$
1090 cd_length = CODE(INKEY$(#3))
1100 IF cd_length = 0 THEN RETurn CHR$(0)
1110 IF cd_length = 255 THEN RETurn CHR$(255)
1120 temp$ = FILL$(' ',cd_length)
1130 FOR a = 1 TO cd_length : temp$(a) = INKEY$(#3)
1140 RETurn temp$
1150 END DEFine FETCH_CODE_STRING$
               (
geocities.com/siliconvalley/pines/Pines)                   (
geocities.com/siliconvalley/pines)                   (
geocities.com/siliconvalley)