1 /*
2  * drv-nl10.c - NL10 printer driver.
3  *
4  * Written by
5  *  David Hansel <david@hansels.net>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "archdep.h"
34 #include "driver-select.h"
35 #include "drv-nl10.h"
36 #include "log.h"
37 #include "output-select.h"
38 #include "output.h"
39 #include "palette.h"
40 #include "sysfile.h"
41 #include "types.h"
42 #include "lib.h"
43 
44 /* MAX_COL must be a multiple of 32 */
45 /* 2432 x 3172 */
46 #define BORDERX 16
47 #define BORDERY 2
48 #define MAX_COL (80 * 30 + 2 * BORDERX)
49 #define MAX_ROW (66 * 48 + 2 * BORDERY)
50 #define BUF_ROW (4 * 4 * 9 + 1)
51 #define CHARSET_SIZE       200
52 #define NL10_ROM_SIZE      0x8000
53 
54 #define NL10_NLQ           0x00001
55 #define NL10_ELITE         0x00002
56 #define NL10_CONDENSED     0x00004
57 #define NL10_EXPANDED      0x00008
58 #define NL10_EXPANDED_LINE 0x00010
59 #define NL10_UNDERLINE     0x00020
60 #define NL10_SUPERSCRIPT   0x00040
61 #define NL10_SUBSCRIPT     0x00080
62 #define NL10_ITALIC        0x00100
63 #define NL10_BOLD          0x00200
64 #define NL10_EMPHASIZE     0x00400
65 #define NL10_PROP          0x00800
66 #define NL10_USERAM        0x01000
67 #define NL10_ASCII         0x02000
68 #define NL10_CBMTEXT       0x04000
69 #define NL10_REVERSE       0x08000
70 #define NL10_QUOTED        0x10000
71 #define NL10_ZERO_CROSSED  0x20000
72 
73 #define NL10_GFX_OFF       0x00
74 #define NL10_GFX_SINGLE    0x01
75 #define NL10_GFX_DOUBLE    0x02
76 #define NL10_GFX_QUAD      0x03
77 #define NL10_GFX_CRT       0x04
78 #define NL10_GFX_PLOT      0x05
79 #define NL10_GFX_CRT2      0x06
80 #define NL10_GFX_REVERSE   0x40
81 #define NL10_GFX_7PIN      0x80
82 
83 #define NL10_ESCBUF_SIZE   60
84 
85 typedef struct nl10_s {
86     uint8_t esc[NL10_ESCBUF_SIZE], esc_ctr;
87     uint8_t line[BUF_ROW][MAX_COL];
88     uint8_t htabs[41], vtabs[41], macro[16];
89     uint8_t mapping[256];
90     uint8_t *char_ram, *char_ram_nlq;
91     uint8_t expand, expand_half;
92 
93     int marg_l, marg_r, marg_t, marg_b;
94     int mapping_intl_id;
95     int pos_x, pos_y, pos_y_pix;
96     int col_nr, line_nr;
97     int isopen, mode, gfx_mode, gfx_count;
98     int linespace; /* in 1/216 inch */
99 } nl10_t;
100 
101 
102 static palette_t *palette = NULL;
103 
104 /* Logging goes here.  */
105 static log_t drvnl10_log = LOG_ERR;
106 
107 #ifdef USE_EMBEDDED
108 #include "printernl10cbm.h"
109 #else
110 static uint8_t drv_nl10_rom[NL10_ROM_SIZE];
111 #endif
112 
113 static uint8_t *drv_nl10_charset = drv_nl10_rom;
114 static uint8_t drv_nl10_charset_nlq[CHARSET_SIZE * 47];
115 static uint8_t drv_nl10_charset_nlq_italic[CHARSET_SIZE * 47];
116 
117 STATIC_PROTOTYPE const uint8_t drv_nl10_charset_mapping_intl[3][8][14];
118 STATIC_PROTOTYPE const uint8_t drv_nl10_charset_mapping[3][256];
119 
120 static int drv_nl10_init_charset(void);
121 static int handle_control_sequence(nl10_t *nl10, unsigned int prnr, const uint8_t c);
122 static int handle_esc_control_sequence(nl10_t *nl10, unsigned int prnr, const uint8_t c);
123 
124 static nl10_t drv_nl10[NUM_OUTPUT_SELECT];
125 
126 
127 /* ------------------------------------------------------------------------- */
128 /* NL-10 printer engine. */
129 
set_mode(nl10_t * nl10,unsigned int m)130 static inline void set_mode(nl10_t *nl10, unsigned int m)
131 {
132     nl10->mode |= m;
133 }
134 
del_mode(nl10_t * nl10,unsigned int m)135 static inline void del_mode(nl10_t *nl10, unsigned int m)
136 {
137     nl10->mode &= ~m;
138 }
139 
is_mode(nl10_t * nl10,unsigned int m)140 static inline int is_mode(nl10_t *nl10, unsigned int m)
141 {
142     return nl10->mode & m;
143 }
144 
145 
get_char_data(nl10_t * nl10,uint8_t c)146 static uint8_t *get_char_data(nl10_t *nl10, uint8_t c)
147 {
148     uint8_t *data;
149 
150     if (nl10->mapping[c] == 0xff) {
151         data = NULL;
152     } else if (is_mode(nl10, NL10_NLQ)) {
153         if (is_mode(nl10, NL10_USERAM) && c >= 32 && c <= 127) {
154             data = nl10->char_ram_nlq + (c - 32) * 47;
155         } else if (is_mode(nl10, NL10_ITALIC)) {
156             data = drv_nl10_charset_nlq_italic + nl10->mapping[c] * 47;
157         } else {
158             data = drv_nl10_charset_nlq + nl10->mapping[c] * 47;
159         }
160     } else {
161         if (is_mode(nl10, NL10_USERAM) && c >= 32 && c <= 127) {
162             data = nl10->char_ram + (c - 32) * 12;
163         } else {
164             data = drv_nl10_charset + nl10->mapping[c] * 12;
165         }
166     }
167 
168     return data;
169 }
170 
171 
get_char_width(nl10_t * nl10,uint8_t c,int no_prop)172 static double get_char_width(nl10_t *nl10, uint8_t c, int no_prop)
173 {
174     uint8_t *data = get_char_data(nl10, c);
175     double w;
176 
177     if (data == NULL) {
178         return 0;
179     } else if (is_mode(nl10, NL10_NLQ)) {
180         w = 30;
181     } else if (is_mode(nl10, NL10_ELITE)) {
182         w = is_mode(nl10, NL10_CONDENSED) ? 15 : 25;
183     } else {
184         w = is_mode(nl10, NL10_CONDENSED) ? 17.5 : 30;
185     }
186 
187     if (!no_prop && is_mode(nl10, NL10_PROP) && !is_mode(nl10, NL10_NLQ)) {
188         w = (w / 11.0) * ((data[0] & 15) - ((data[0] >> 4) & 7)) + 1;
189     }
190 
191     return w * (is_mode(nl10, NL10_EXPANDED | NL10_EXPANDED_LINE) ? 2 : 1) * nl10->expand;
192 }
193 
194 
init_mapping(nl10_t * nl10,int intl)195 static void init_mapping(nl10_t *nl10, int intl)
196 {
197     int mapping;
198     if (is_mode(nl10, NL10_ASCII)) {
199         mapping = 0;
200     } else if (is_mode(nl10, NL10_CBMTEXT)) {
201         mapping = 2;
202     } else {
203         mapping = 1;
204     }
205 
206     nl10->mapping_intl_id = intl;
207 
208     memcpy(nl10->mapping, drv_nl10_charset_mapping[mapping], 256);
209     nl10->mapping[0x23] = drv_nl10_charset_mapping_intl[mapping][intl][0];
210     nl10->mapping[0x24] = drv_nl10_charset_mapping_intl[mapping][intl][1];
211     nl10->mapping[0x40] = drv_nl10_charset_mapping_intl[mapping][intl][2];
212     nl10->mapping[0x5b] = drv_nl10_charset_mapping_intl[mapping][intl][3];
213     nl10->mapping[0x5c] = drv_nl10_charset_mapping_intl[mapping][intl][4];
214     nl10->mapping[0x5d] = drv_nl10_charset_mapping_intl[mapping][intl][5];
215     nl10->mapping[0x7b] = drv_nl10_charset_mapping_intl[mapping][intl][6];
216     nl10->mapping[0x7c] = drv_nl10_charset_mapping_intl[mapping][intl][7];
217     nl10->mapping[0x7d] = drv_nl10_charset_mapping_intl[mapping][intl][8];
218     nl10->mapping[0x7e] = drv_nl10_charset_mapping_intl[mapping][intl][9];
219     nl10->mapping[0xdb] = drv_nl10_charset_mapping_intl[mapping][intl][10];
220     nl10->mapping[0xdc] = drv_nl10_charset_mapping_intl[mapping][intl][11];
221     nl10->mapping[0xdd] = drv_nl10_charset_mapping_intl[mapping][intl][12];
222     nl10->mapping[0xde] = drv_nl10_charset_mapping_intl[mapping][intl][13];
223 
224     if (is_mode(nl10, NL10_ZERO_CROSSED)) {
225         nl10->mapping[0x30] = 0x1f;
226     }
227 }
228 
229 
reset(nl10_t * nl10)230 static void reset(nl10_t *nl10)
231 {
232     int i;
233     memset(nl10->line, 0, MAX_COL * BUF_ROW);
234 
235     nl10->line_nr = 1;
236     nl10->linespace = 12 * 3;
237     nl10->mode = 0;
238     nl10->gfx_mode = 0;
239     nl10->col_nr = 0;
240     nl10->expand = 1;
241     nl10->marg_l = BORDERX;
242     nl10->marg_r = MAX_COL - BORDERX;
243     nl10->marg_t = 0;
244     nl10->marg_b = 0;
245     nl10->pos_x = nl10->marg_l;
246     /* init_mapping(nl10, 0); */
247 
248     for (i = 0; i < 40; i++)
249     {
250         nl10->htabs[i] = 8 * (i + 1);
251         nl10->vtabs[i] = 0;
252     }
253 
254     nl10->htabs[40] = 0;
255     nl10->vtabs[40] = 0;
256 }
257 
258 
reset_hard(nl10_t * nl10)259 static void reset_hard(nl10_t *nl10)
260 {
261     reset(nl10);
262     memset(nl10->char_ram, 0, 12 * 96);
263     memset(nl10->char_ram_nlq, 0, 47 * 96);
264 }
265 
266 
store_char(uint8_t * dest,const uint8_t * src)267 static int store_char(uint8_t *dest, const uint8_t *src)
268 {
269     uint8_t c, r;
270     int ret = 0, s = (src[0] >> 4) & 7, e = src[0] & 15;
271 
272     if (s < 0 || s > 7) {
273         log_warning(drvnl10_log, "Illegal prop-start value: %u\n", s);
274     } else if (e < 4 || e > 11) {
275         log_warning(drvnl10_log, "Illegal prop-end value: %u\n", e);
276     } else if ((e - s) < 4) {
277         log_warning(drvnl10_log, "Illegal character width: (s=%u, e=%u)\n", s, e);
278     } else {
279         ret = 1;
280     }
281 
282     dest[0] = ret ? src[0] : ((src[0] & 0x80) | 10);
283     for (c = 0; c < 11; c++)
284     {
285         dest[c + 1] = src[c + 1];
286         if (c != 0) {
287             for (r = 0; r < 8; r++) {
288                 if ((dest[c] & (1 << r)) && (dest[c + 1] & (1 << r))) {
289                     log_warning(drvnl10_log, "Illegal dot col=%u, row=%u\n", c + 1, r + 1);
290                     dest[c + 1] = dest[c + 1] & ~(1 << r);
291                     ret = 0;
292                 }
293             }
294         }
295     }
296 
297     return ret;
298 }
299 
300 
store_char_nlq(uint8_t * dest,const uint8_t * src)301 static int store_char_nlq(uint8_t *dest, const uint8_t *src)
302 {
303     uint8_t c, r;
304     int ret = 1;
305 
306     dest[0] = src[0];
307     for (c = 0; c < 46; c++)
308     {
309         dest[c + 1] = src[c + 1];
310         if (c != 0 && c != 23) {
311             for (r = 0; r < 8; r++) {
312                 if ((dest[c] & (1 << r)) && (dest[c + 1] & (1 << r))) {
313                     log_warning(drvnl10_log, "Illegal dot col=%u, row=%u\n", c + 1, r + 1);
314                     dest[c + 1] = dest[c + 1] & ~(1 << r);
315                     ret = 0;
316                 }
317             }
318         }
319     }
320 
321     return ret;
322 }
323 
324 
inc_y(nl10_t * nl10)325 static inline int inc_y(nl10_t *nl10)
326 {
327     switch ((nl10->pos_y++) % 3) {
328         case 0: return 1;
329         case 1: return 2;
330         case 2: return 1;
331     }
332 
333     return 0;
334 }
335 
336 
linefeed(nl10_t * nl10,unsigned int prnr)337 static void linefeed(nl10_t *nl10, unsigned int prnr)
338 {
339     int c, i, j;
340 
341     for (i = 0; i < nl10->linespace; i++) {
342         for (j = inc_y(nl10); j > 0; j--) {
343             while (nl10->pos_y_pix < BORDERY) {
344                 output_select_putc(prnr, (uint8_t)(OUTPUT_NEWLINE));
345                 nl10->pos_y_pix++;
346             }
347 
348             /* output topmost row */
349             for (c = 0; c < MAX_COL; c++) {
350                 output_select_putc(prnr, (uint8_t)(nl10->line[0][c] ? OUTPUT_PIXEL_BLACK : OUTPUT_PIXEL_WHITE));
351             }
352             output_select_putc(prnr, (uint8_t)(OUTPUT_NEWLINE));
353 
354             /* move everything else one row up */
355             memmove(nl10->line[0], nl10->line[1], (BUF_ROW - 1) * MAX_COL * sizeof(uint8_t));
356 
357             /* clear bottom row */
358             memset(nl10->line[BUF_ROW - 1], 0, MAX_COL * sizeof(uint8_t));
359 
360             /* increase pixel row count */
361             nl10->pos_y_pix++;
362 
363             /* check end-of-page */
364             if (nl10->pos_y_pix >= MAX_ROW - BORDERY) {
365                 while (nl10->pos_y_pix++ < MAX_ROW) {
366                     output_select_putc(prnr, (uint8_t)(OUTPUT_NEWLINE));
367                 }
368                 nl10->line_nr = 0;
369                 nl10->pos_y = 0;
370                 nl10->pos_y_pix = 0;
371             }
372         }
373     }
374 
375     nl10->line_nr++;
376 }
377 
378 
output_buf(nl10_t * nl10,unsigned int prnr)379 static void output_buf(nl10_t *nl10, unsigned int prnr)
380 {
381     int r, c;
382 
383     /* output buffer */
384     for (r = 0; r < BUF_ROW; r++) {
385         for (c = 0; c < MAX_COL; c++) {
386             output_select_putc(prnr, (uint8_t)(drv_nl10[prnr].line[r][c] ? OUTPUT_PIXEL_BLACK : OUTPUT_PIXEL_WHITE));
387         }
388         output_select_putc(prnr, (uint8_t)(OUTPUT_NEWLINE));
389     }
390 
391     /* clear buffer */
392     memset(nl10->line, 0, BUF_ROW * MAX_COL * sizeof(uint8_t));
393 
394     nl10->pos_y += (BUF_ROW / 4 * 3);
395     nl10->pos_y_pix += BUF_ROW;
396 }
397 
398 
formfeed(nl10_t * nl10,unsigned int prnr)399 static void formfeed(nl10_t *nl10, unsigned int prnr)
400 {
401     int r;
402     output_buf(nl10, prnr);
403     for (r = nl10->pos_y_pix; r < MAX_ROW; r++) {
404         output_select_putc(prnr, (uint8_t)(OUTPUT_NEWLINE));
405     }
406     nl10->line_nr = 1;
407     nl10->pos_y = 0;
408     nl10->pos_y_pix = 0;
409 }
410 
411 
draw_point2(nl10_t * nl10,int x,int y)412 inline static void draw_point2(nl10_t *nl10, int x, int y)
413 {
414 /*
415    **
416    #*
417    **
418 */
419 
420     nl10->line[y][x] = 1;
421     nl10->line[y][x + 1] = 1;
422     nl10->line[y + 1][x] = 1;
423     nl10->line[y - 1][x] = 1;
424     nl10->line[y + 1][x + 1] = 1;
425     nl10->line[y - 1][x + 1] = 1;
426 }
427 
draw_point3(nl10_t * nl10,int x,int y)428 inline static void draw_point3(nl10_t *nl10, int x, int y)
429 {
430 /*
431     *
432    *#*
433     *
434 */
435 
436     nl10->line[y][x] = 1;
437     nl10->line[y][x - 1] = 1;
438     nl10->line[y][x + 1] = 1;
439     nl10->line[y - 1][x] = 1;
440     nl10->line[y + 1][x] = 1;
441 }
442 
draw_char_nlq(nl10_t * nl10,const uint8_t c)443 static void draw_char_nlq(nl10_t *nl10, const uint8_t c)
444 {
445 /*
446      NLQ (80 char per line, 30 pixels per char):
447       0         1         2
448       012345678901234567890123456789
449       ** * ** * ** * ** * ** * ** *
450       #**#*#**#*#**#*#**#*#**#*#**#*
451       ** * ** * ** * ** * ** * ** *
452 
453        ** * ** * ** * ** * ** * **
454        #**#*#**#*#**#*#**#*#**#*#*
455        ** * ** * ** * ** * ** * **
456 */
457 
458     int i, j, k, n, rs, re, underline, expanded;
459     uint8_t *cdata = get_char_data(nl10, c);
460 
461     if (cdata) {
462         int xs = nl10->pos_x;
463         uint8_t desc = (cdata[0] & 128) ? 0 : 1;
464 
465         underline = is_mode(nl10, NL10_UNDERLINE) ? 1 : 0;
466         expanded = (is_mode(nl10, NL10_EXPANDED | NL10_EXPANDED_LINE) ? 2 : 1) * nl10->expand;
467 
468         if (nl10->expand_half == 0) {
469             rs = 0; re = 16;
470         } else if (nl10->expand_half == 1) {
471             rs = 0; re = 8;
472         } else if (nl10->expand_half == 2) {
473             rs = 8; re = 16;
474         } else {
475             rs = 0; re = 16;
476         }
477 
478         for (i = 0; i < 23; i++) {
479             for (k = 0; k < expanded; k++)
480             {
481                 uint16_t data;
482                 data = ((cdata[i + 1] & 0x01 ? 0x0002 : 0) + (cdata[i + 24] & 0x01 ? 0x0001 : 0) +
483                         (cdata[i + 1] & 0x02 ? 0x0008 : 0) + (cdata[i + 24] & 0x02 ? 0x0004 : 0) +
484                         (cdata[i + 1] & 0x04 ? 0x0020 : 0) + (cdata[i + 24] & 0x04 ? 0x0010 : 0) +
485                         (cdata[i + 1] & 0x08 ? 0x0080 : 0) + (cdata[i + 24] & 0x08 ? 0x0040 : 0) +
486                         (cdata[i + 1] & 0x10 ? 0x0200 : 0) + (cdata[i + 24] & 0x10 ? 0x0100 : 0) +
487                         (cdata[i + 1] & 0x20 ? 0x0800 : 0) + (cdata[i + 24] & 0x20 ? 0x0400 : 0) +
488                         (cdata[i + 1] & 0x40 ? 0x2000 : 0) + (cdata[i + 24] & 0x40 ? 0x1000 : 0) +
489                         (cdata[i + 1] & 0x80 ? 0x8000 : 0) + (cdata[i + 24] & 0x80 ? 0x4000 : 0));
490 
491                 for (j = rs; j < re; j++) {
492                     for (n = 0; n < nl10->expand; n++)
493                     {
494                         if (underline && (j + desc) == 16 && n == 0) {
495                         } else if (data & (1 << (15 - j))) {
496                             if (i & 2 || expanded > 1) {
497                                 draw_point3(nl10, nl10->pos_x - expanded / 2 + k, ((j + desc) * nl10->expand + n) * 2 + 1);
498                             } else {
499                                 draw_point2(nl10, nl10->pos_x - expanded / 2 + k, ((j + desc) * nl10->expand + n) * 2 + 1);
500                             }
501                         }
502                     }
503                 }
504 
505                 nl10->pos_x += ((i * expanded + k) % 4) == 1 ? 2 : 1;
506             }
507         }
508 
509         nl10->pos_x += expanded;
510 
511         if (underline) {
512             for (i = xs; i < nl10->pos_x; i++) {
513                 if ((i & 3) == 1) {
514                     draw_point2(nl10, i, (8 * nl10->expand) * 4 + 1);
515                 }
516             }
517         }
518     }
519 }
520 
draw_char_draft(nl10_t * nl10,const uint8_t c)521 static void draw_char_draft(nl10_t *nl10, const uint8_t c)
522 {
523 /*
524      Pica (80 char per line, 30 pixels per char):
525       0         1         2
526       012345678901234567890123456789
527        *    *    *    *    *    *
528       *#*  *#*  *#*  *#*  *#*  *#*
529        *    *    *    *    *    *
530 
531          **   **   **   **   **
532          #*   #*   #*   #*   #*
533          **   **   **   **   **
534 
535      Elite (96 char per line, 25 pixels per char):
536       0         1         2
537       0123456789012345678901234
538        *   *   *   *   *   *
539       *#* *#* *#* *#* *#* *#*
540        *   *   *   *   *   *
541 
542          *   *   *   *   *
543         *#* *#* *#* *#* *#*
544          *   *   *   *   *
545 
546      Condensed (136 char per line, 17 3/5 pixels per char):
547       0         1
548       01234567890123456
549       ** ** ** ** ** **
550       #* #* #* #* #* #*
551       ** ** ** ** ** **
552 
553         *  *  *  *  *
554        *#**#**#**#**#*
555         *  *  *  *  *
556 */
557 
558     int i, j, k, l, m, n, cs, ce, rs, re, expanded, condensed, pinspace, pinoffset;
559     int bold, emphasize, underline, elite;
560     uint8_t desc, *cdata = get_char_data(nl10, c);
561 
562     if (cdata) {
563         int xs = nl10->pos_x;
564 
565         elite = is_mode(nl10, NL10_ELITE) ? 1 : 0;
566         expanded = (is_mode(nl10, NL10_EXPANDED | NL10_EXPANDED_LINE) ? 2 : 1) * nl10->expand;
567         underline = is_mode(nl10, NL10_UNDERLINE) ? 1 : 0;
568         bold = is_mode(nl10, NL10_BOLD) ? 1 : 0;
569         emphasize = is_mode(nl10, NL10_EMPHASIZE) ? 1 : 0;
570         condensed = is_mode(nl10, NL10_CONDENSED) && (!emphasize) && (!bold) ? 1 : 0;
571         pinoffset = is_mode(nl10, NL10_SUBSCRIPT) ? 4 * 4 : 0;
572         pinspace = (is_mode(nl10, NL10_SUPERSCRIPT) || is_mode(nl10, NL10_SUBSCRIPT)) ? 2 : 4;
573 
574         if (is_mode(nl10, NL10_PROP)) {
575             cs = (cdata[0] >> 4) & 7; ce = (cdata[0] & 15) - 1;
576         } else {
577             cs = 0; ce = 10;
578         }
579 
580         if (nl10->expand_half == 0) {
581             rs = 0; re = 8;
582         } else if (nl10->expand_half == 1) {
583             rs = 0; re = 4;
584         } else if (nl10->expand_half == 2) {
585             rs = 4; re = 8;
586         } else {
587             rs = 0; re = 8;
588         }
589 
590         desc = (cdata[0] & 128) ? 0 : 1;
591         for (i = cs; i <= ce; i++)
592         {
593             uint8_t data = cdata[i + 1];
594             for (j = rs; j < re; j++) {
595                 if (data & (1 << (7 - j))) {
596                     for (l = 0; l <= emphasize; l++) {
597                         for (m = 0; m <= bold; m++) {
598                             for (n = 0; n < nl10->expand; n++) {
599                                 for (k = 0; k < expanded; k++)
600                                 {
601                                     if (underline && (j + desc) == 8 && n == 0) {
602                                     } else if (condensed) {
603                                         if ((expanded == 1) && ((i + l) & 1)) {
604                                             draw_point2(nl10, nl10->pos_x + 1 * l - 1, ((j + desc) * nl10->expand + n) * pinspace + pinoffset + 1 + 2 * m);
605                                         } else {
606                                             draw_point3(nl10, nl10->pos_x + 2 * l + 3 * k, ((j + desc) * nl10->expand + n) * pinspace + pinoffset + 1 + 2 * m);
607                                         }
608                                     } else if (elite) {
609                                         draw_point3(nl10, nl10->pos_x + l + 2 * l + 4 * k, ((j + desc) * nl10->expand + n) * pinspace + pinoffset + 1 + 2 * m);
610                                     } else {
611                                         if ((expanded == 1) && ((i + l) & 1)) {
612                                             draw_point2(nl10, nl10->pos_x + 2 * l, ((j + desc) * nl10->expand + n) * pinspace + pinoffset + 1 + 2 * m);
613                                         } else {
614                                             draw_point3(nl10, nl10->pos_x + 3 * l + 5 * k, ((j + desc) * nl10->expand + n) * pinspace + pinoffset + 1 + 2 * m);
615                                         }
616                                     }
617                                 }
618                             }
619                         }
620                     }
621                 }
622             }
623 
624             if (condensed) {
625                 nl10->pos_x += (expanded > 1) ? (3 * (expanded / 2)) : ((i & 1) ? 1 : 2);
626             } else if (elite) {
627                 nl10->pos_x += 2 * expanded;
628             } else {
629                 nl10->pos_x += (expanded > 1) ? (5 * (expanded / 2)) : ((i & 1) ? 3 : 2);
630             }
631         }
632 
633         if (condensed) {
634             nl10->pos_x += (((nl10->col_nr % 5) & 1) ? 0 : expanded) + expanded / 2;
635         } else if (elite) {
636             nl10->pos_x += expanded * 3;
637         } else {
638             nl10->pos_x += expanded * 3 - expanded / 2;
639         }
640 
641         if (underline) {
642             for (i = xs; i < nl10->pos_x; i++) {
643                 if ((i & 3) == 1) {
644                     draw_point2(nl10, i, (8 * nl10->expand) * pinspace + pinoffset + 1);
645                 }
646             }
647         }
648     }
649 }
650 
651 
draw_char_draft_reverse(nl10_t * nl10,const uint8_t c)652 static void draw_char_draft_reverse(nl10_t *nl10, const uint8_t c)
653 {
654     int i, j, k, expanded;
655     uint8_t *cdata = get_char_data(nl10, c);
656 
657     if (cdata) {
658         expanded = (is_mode(nl10, NL10_EXPANDED | NL10_EXPANDED_LINE) ? 2 : 1) * nl10->expand;
659 
660         for (i = 0; i <= 11; i++) {
661             for (k = 0; k < expanded; k++) {
662                 for (j = 0; j < 7; j++) {
663                     uint8_t bit = 1 << (7 - j);
664 
665                     if ((i < 11 && (cdata[i + 1] & bit)) || (i > 0 && (cdata[i] & bit))) {
666                     } else {
667                         if (i & 1) {
668                             draw_point2(nl10, nl10->pos_x, (j * 4) + 1);
669                         } else {
670                             draw_point3(nl10, nl10->pos_x, (j * 4) + 1);
671                         }
672 
673                         if (i == 7) {
674                             draw_point2(nl10, nl10->pos_x, (j * 4) + 1);
675                         }
676                     }
677                 }
678 
679                 nl10->pos_x += ((i * expanded + k) & 1) ? 3 : 2;
680             }
681         }
682     }
683 }
684 
685 
draw_char(nl10_t * nl10,const uint8_t c)686 static void draw_char(nl10_t *nl10, const uint8_t c)
687 {
688     /*printf("draw_char %i %i\n", c, cc);*/
689 
690     if (is_mode(nl10, NL10_NLQ)) {
691         if (is_mode(nl10, NL10_SUBSCRIPT | NL10_SUPERSCRIPT)) {
692             int tmp = nl10->mode;
693             nl10->mode = tmp & (NL10_SUBSCRIPT | NL10_SUPERSCRIPT | NL10_UNDERLINE | NL10_EXPANDED | NL10_EXPANDED_LINE);
694             draw_char_draft(nl10, c);
695             nl10->mode = tmp;
696         } else {
697             draw_char_nlq(nl10, c);
698         }
699     } else if (is_mode(nl10, NL10_REVERSE)) {
700         draw_char_draft_reverse(nl10, c);
701     } else {
702         draw_char_draft(nl10, c);
703     }
704 }
705 
706 
draw_graphics(nl10_t * nl10,uint8_t c)707 static void draw_graphics(nl10_t *nl10, uint8_t c)
708 {
709     int j;
710 
711     if (nl10->gfx_mode & NL10_GFX_7PIN) {
712         switch (nl10->gfx_mode & ~(NL10_GFX_7PIN | NL10_GFX_REVERSE)) {
713             case NL10_GFX_SINGLE:
714                 {
715                     /* 480 dots per line */
716 
717                     for (j = 0; j < 7; j++) {
718                         if (((c & (1 << j)) != 0) ^ ((nl10->gfx_mode & NL10_GFX_REVERSE) != 0)) {
719                             draw_point3(nl10, nl10->pos_x, j * 4 + 1);
720                         }
721                     }
722 
723                     nl10->pos_x += 5;
724                     break;
725                 }
726 
727             case NL10_GFX_DOUBLE:
728                 {
729                     /* 960 dots per line */
730 
731                     /* 01234 */
732                     /* #**#* */
733 
734                     for (j = 0; j < 7; j++) {
735                         if (c & (1 << j)) {
736                             if (nl10->gfx_count & 1) {
737                                 draw_point3(nl10, nl10->pos_x, j * 4 + 1);
738                             } else {
739                                 draw_point2(nl10, nl10->pos_x, j * 4 + 1);
740                             }
741                         }
742                     }
743 
744                     nl10->pos_x += (nl10->gfx_count & 1) ? 2 : 3;
745                     nl10->gfx_count++;
746                     break;
747                 }
748         }
749     } else {
750         switch (nl10->gfx_mode) {
751             case NL10_GFX_SINGLE:
752                 {
753                     /* 480 dots per line */
754 
755                     for (j = 0; j < 8; j++) {
756                         if (c & (1 << (7 - j))) {
757                             draw_point3(nl10, nl10->pos_x, j * 4 + 1);
758                         }
759                     }
760 
761                     nl10->pos_x += 5;
762                     break;
763                 }
764 
765             case NL10_GFX_PLOT:
766                 {
767                     /* 576 dots per line */
768 
769                     /* 0123456789012345678901234 */
770                     /* *#** *#* *#* *#* *#* *#*  */
771 
772                     for (j = 0; j < 8; j++) {
773                         if (c & (1 << (7 - j))) {
774                             draw_point3(nl10, nl10->pos_x, j * 4 + 1);
775                             if (!(nl10->gfx_count % 6)) {
776                                 draw_point3(nl10, nl10->pos_x + 1, j * 4 + 1);
777                             }
778                         }
779                     }
780 
781                     nl10->pos_x += (nl10->gfx_count % 6) ? 4 : 5;
782                     break;
783                 }
784 
785             case NL10_GFX_CRT:
786                 {
787                     /* 640 dots per line */
788 
789                     /* 012345678901234 */
790                     /* #* *#* *#* *#*  */
791 
792                     for (j = 0; j < 8; j++) {
793                         if (c & (1 << (7 - j))) {
794                             if ((nl10->gfx_count % 4) == 3) {
795                                 draw_point2(nl10, nl10->pos_x, j * 4 + 1);
796                             } else {
797                                 draw_point3(nl10, nl10->pos_x, j * 4 + 1);
798                             }
799                         }
800                     }
801 
802                     nl10->pos_x += (nl10->gfx_count % 4) ? 4 : 3;
803                     break;
804                 }
805 
806             case NL10_GFX_CRT2:
807                 {
808                     /* 720 dots per line */
809 
810                     /* 0123456789 */
811                     /* #* *#* #*  */
812 
813                     for (j = 0; j < 8; j++) {
814                         if (c & (1 << (7 - j))) {
815                             if ((nl10->gfx_count % 3) == 2) {
816                                 draw_point3(nl10, nl10->pos_x, j * 4 + 1);
817                             } else {
818                                 draw_point2(nl10, nl10->pos_x, j * 4 + 1);
819                             }
820                         }
821                     }
822 
823                     nl10->pos_x += (nl10->gfx_count % 3) ? 3 : 4;
824                     break;
825                 }
826 
827             case NL10_GFX_DOUBLE:
828                 {
829                     /* 960 dots per line */
830 
831                     /* 01234 */
832                     /* #**#* */
833 
834                     for (j = 0; j < 8; j++) {
835                         if (c & (1 << (7 - j))) {
836                             if (nl10->gfx_count & 1) {
837                                 draw_point3(nl10, nl10->pos_x, j * 4 + 1);
838                             } else {
839                                 draw_point2(nl10, nl10->pos_x, j * 4 + 1);
840                             }
841                         }
842                     }
843 
844                     nl10->pos_x += (nl10->gfx_count & 1) ? 2 : 3;
845                     break;
846                 }
847 
848             case NL10_GFX_QUAD:
849                 {
850                     /* 1920 dots per line */
851 
852                     /* 01234  */
853                     /* #*     */
854                     /*  *#*   */
855                     /*   *#*  */
856                     /*    *#* */
857 
858                     for (j = 0; j < 8; j++) {
859                         if (c & (1 << (7 - j))) {
860                             if ((nl10->gfx_count % 4) == 0) {
861                                 draw_point2(nl10, nl10->pos_x, j * 4 + 1);
862                             } else {
863                                 draw_point3(nl10, nl10->pos_x, j * 4 + 1);
864                             }
865                         }
866                     }
867 
868                     nl10->pos_x += (nl10->gfx_count % 4) ? 1 : 2;
869                     break;
870                 }
871         }
872     }
873 }
874 
875 
print_char(nl10_t * nl10,unsigned int prnr,const uint8_t c)876 static void print_char(nl10_t *nl10, unsigned int prnr, const uint8_t c)
877 {
878     /* handle dot-graphics pringing */
879     if (nl10->gfx_mode != NL10_GFX_OFF) {
880         if (nl10->gfx_mode & NL10_GFX_7PIN) {
881             /* 7-pin (CBM) mode */
882             if ((nl10->esc_ctr == 0) && (c & 0x80)) {
883                 draw_graphics(nl10, c);
884                 return;
885             }
886         } else {
887             /* 8-pin (epson) mode */
888             draw_graphics(nl10, c);
889             nl10->gfx_count--;
890             if (nl10->gfx_count == 0) {
891                 nl10->gfx_mode = NL10_GFX_OFF;
892             }
893             return;
894         }
895     }
896 
897     /* handle CBM quoted-mode (print description strings for control characters,
898        e.g. print "(rvs)" for character 0x12) */
899     if (is_mode(nl10, NL10_QUOTED)) {
900         uint16_t i = 0;
901 
902         /* find pointer to description string in ROM */
903         if ((c >= 0x01) && (c < 0x20) && (c != 0x0d)) {
904             i = ((drv_nl10_rom[0x428e + (c - 0x01) * 2] & 0x7f) << 8) + drv_nl10_rom[0x428f + (c - 0x01) * 2];
905         } else if ((c >= 0x80) && (c < 0xa0)) {
906             i = ((drv_nl10_rom[0x42cc + (c - 0x80) * 2] & 0x7f) << 8) + drv_nl10_rom[0x42cd + (c - 0x80) * 2];
907         }
908 
909         if (i) {
910             /* if found, read and print description string from ROM
911                (terminated by 0xff) */
912             while ((i < NL10_ROM_SIZE) && (drv_nl10_rom[i] != 0xff)) {
913                 print_char(nl10, prnr, drv_nl10_rom[i++]);
914             }
915             return;
916         }
917     }
918 
919     /* ensure that top margin is honored */
920     while (nl10->line_nr <= nl10->marg_t) {
921         linefeed(nl10, prnr);
922     }
923 
924     /* ensure that left margin is honored */
925     if (nl10->pos_x < nl10->marg_l) {
926         nl10->pos_x = nl10->marg_l;
927     }
928 
929     /* ensure that right margin is honored */
930     if ((nl10->pos_x + get_char_width(nl10, c, 0)) > nl10->marg_r) {
931         linefeed(nl10, prnr);
932         nl10->pos_x = nl10->marg_l;
933         nl10->col_nr = 0;
934     }
935 
936     /* ensure that bottom margin is honored */
937     if (nl10->marg_b > 0 && nl10->line_nr > ((MAX_ROW - 2 * BORDERY) / (nl10->linespace * 4 / 3) - nl10->marg_b)) {
938         formfeed(nl10, prnr);
939     }
940 
941     /* check if character is part of a control sequence and, if so, process it there.
942        Otherwise draw the character. */
943     if (!handle_control_sequence(nl10, prnr, c)) {
944         if (c == '"') {
945             if (is_mode(nl10, NL10_QUOTED)) {
946                 del_mode(nl10, NL10_QUOTED);
947             } else {
948                 set_mode(nl10, NL10_QUOTED);
949             }
950         }
951 
952         draw_char(nl10, c);
953         nl10->col_nr++;
954     }
955 
956     /*printf("modes: esc=%i mode=%i gfx_mode=%i gfx_ctr=%i ls=%i px=%i\n", nl10->esc_ctr, nl10->mode, nl10->gfx_mode, nl10->gfx_count, nl10->linespace, nl10->pos_x);*/
957 }
958 
959 
handle_control_sequence(nl10_t * nl10,unsigned int prnr,const uint8_t c)960 static int handle_control_sequence(nl10_t *nl10, unsigned int prnr, const uint8_t c)
961 {
962     if (nl10->esc_ctr >= NL10_ESCBUF_SIZE) {
963         /* We should never get here.  If we do then there is a bug
964            in the ESC handling routine */
965         log_warning(drvnl10_log, "ESC counter overflow");
966         nl10->esc_ctr = 0;
967     }
968 
969     nl10->esc[nl10->esc_ctr] = c;
970     switch (nl10->esc[0]) {
971         case 0:
972             break;
973 
974         case 7:
975             /* beep (NOT IMPLEMENTED) */
976             break;
977 
978         case 8:
979             {
980                 if (is_mode(nl10, NL10_ASCII)) {
981                     /* ASCII: step back */
982                     nl10->pos_x -= (int) get_char_width(nl10, ' ', 1);
983                 } else {
984                     /* CBM: set single density graphics and line spacing 7/72" */
985                     nl10->gfx_mode = NL10_GFX_SINGLE | NL10_GFX_7PIN;
986                     nl10->linespace = 3 * 7;
987                 }
988                 break;
989             }
990 
991         case 9:
992             {
993                 if (is_mode(nl10, NL10_ASCII)) {
994                     /* ASCII: horizontal tab */
995                     int i;
996                     double w = get_char_width(nl10, ' ', 1);
997                     for (i = 0; nl10->htabs[i] > 0; i++)
998                     {
999                         int p = nl10->marg_l + (int) (w * nl10->htabs[i]);
1000                         if ((nl10->pos_x < p) && (p < nl10->marg_r)) {
1001                             nl10->pos_x = p;
1002                             break;
1003                         }
1004                     }
1005                 } else {
1006                     /* CBM: set double density graphics and line spacing 7/72" */
1007                     nl10->gfx_mode = NL10_GFX_DOUBLE | NL10_GFX_7PIN;
1008                     nl10->linespace = 3 * 7;
1009                 }
1010 
1011                 break;
1012             }
1013 
1014         case 10:
1015             /* linefeed */
1016             linefeed(nl10, prnr);
1017             break;
1018 
1019         case 11:
1020             {
1021                 /* advance to next vertical tab position */
1022                 int i = 0;
1023                 while ((nl10->line_nr >= nl10->vtabs[i]) && (i == 0 || (nl10->vtabs[i] > nl10->vtabs[i - 1]))) {
1024                     i++;
1025                 }
1026 
1027                 if ((nl10->vtabs[i] <= nl10->vtabs[i - 1])) {
1028                     /* we're past the last tab. go to top of next page */
1029                     formfeed(nl10, prnr);
1030 
1031                     /* find the first tab greater than the top margin */
1032                     i = 0;
1033                     while ((nl10->marg_t >= nl10->vtabs[i]) && (i == 0 || (nl10->vtabs[i] > nl10->vtabs[i - 1]))) {
1034                         i++;
1035                     }
1036 
1037                     if (nl10->vtabs[i] <= nl10->vtabs[i - 1]) {
1038                         /* past the last tab again => there is no valid tab */
1039                         i = -1;
1040                     }
1041                 }
1042 
1043                 if (i >= 0) {
1044                     while (nl10->line_nr < nl10->vtabs[i]) {
1045                         linefeed(nl10, prnr);
1046                     }
1047                 }
1048                 break;
1049             }
1050 
1051         case 12:
1052             /* formfeed */
1053             formfeed(nl10, prnr);
1054             break;
1055 
1056         case 13:
1057             /* carriage return */
1058             linefeed(nl10, prnr);
1059             del_mode(nl10, NL10_QUOTED | NL10_EXPANDED_LINE);
1060             nl10->pos_x = nl10->marg_l;
1061             nl10->col_nr = 0;
1062             break;
1063 
1064         case 14:
1065             {
1066                 if (is_mode(nl10, NL10_ASCII)) {
1067                     /* ASCII: turn on expanded print for current line */
1068                     set_mode(nl10, NL10_EXPANDED_LINE);
1069                 } else {
1070                     /* CBM: turn on expanded print (and turn off graphics printing) */
1071                     set_mode(nl10, NL10_EXPANDED);
1072 
1073                     if (nl10->gfx_mode & NL10_GFX_7PIN) {
1074                         nl10->gfx_mode = NL10_GFX_OFF;
1075                         nl10->linespace = 12 * 3;
1076                     }
1077                 }
1078 
1079                 break;
1080             }
1081 
1082         case 15:
1083             {
1084                 if (is_mode(nl10, NL10_ASCII)) {
1085                     /* ASCII: turn on condensed print */
1086                     set_mode(nl10, NL10_CONDENSED);
1087                 } else {
1088                     /* CBM: turn off expanded print (and turn off graphics printing) */
1089                     del_mode(nl10, NL10_EXPANDED);
1090 
1091                     if (nl10->gfx_mode & NL10_GFX_7PIN) {
1092                         nl10->gfx_mode = NL10_GFX_OFF;
1093                         nl10->linespace = 12 * 3;
1094                     }
1095                 }
1096 
1097                 break;
1098             }
1099 
1100         case 16:
1101             {
1102                 /* skip to horizontal print position */
1103                 if (nl10->esc_ctr < 2) {
1104                     nl10->esc_ctr++;
1105                 } else {
1106                     int i = 0;
1107                     if ((nl10->esc[1] >= '0') && (nl10->esc[1] <= '9')) {
1108                         i += 10 * (nl10->esc[1] - '0');
1109                     }
1110                     if ((nl10->esc[2] >= '0') && (nl10->esc[2] <= '9')) {
1111                         i += 1 * (nl10->esc[2] - '0');
1112                     }
1113                     if (i > 79) {
1114                         i = 79;
1115                     }
1116                     nl10->pos_x = BORDERX + 30 * i;
1117                     nl10->esc_ctr = 0;
1118                 }
1119                 break;
1120             }
1121 
1122         case 17:
1123             set_mode(nl10, NL10_CBMTEXT);
1124             init_mapping(nl10, nl10->mapping_intl_id);
1125             break;
1126 
1127         case 18:
1128             {
1129                 if (is_mode(nl10, NL10_ASCII)) {
1130                     /* ASCII: Set 'elite' print mode */
1131                     del_mode(nl10, NL10_ELITE);
1132                 } else {
1133                     /* CBM: Enable reverse print */
1134                     set_mode(nl10, NL10_REVERSE);
1135                 }
1136                 break;
1137             }
1138 
1139         case 19:
1140             {
1141                 if (!is_mode(nl10, NL10_ASCII)) {
1142                     /* clear top/bottom margins (only CMD mode) */
1143                     nl10->marg_t = 0;
1144                     nl10->marg_b = 0;
1145                 }
1146                 break;
1147             }
1148 
1149         case 20:
1150             {
1151                 if (is_mode(nl10, NL10_ASCII)) {
1152                     set_mode(nl10, NL10_EXPANDED | NL10_EXPANDED_LINE);
1153                 }
1154                 break;
1155             }
1156 
1157         case 26:
1158             {
1159                 if (nl10->esc_ctr < 2) {
1160                     nl10->esc_ctr++;
1161                 } else {
1162                     int i;
1163                     if ((nl10->gfx_mode & NL10_GFX_7PIN) && (nl10->esc[2] & 0x80)) {
1164                         for (i = 0; i < nl10->esc[1]; i++) {
1165                             draw_graphics(nl10, nl10->esc[2]);
1166                         }
1167                     }
1168                     nl10->esc_ctr = 0;
1169                 }
1170                 break;
1171             }
1172 
1173         case 27:
1174             {
1175                 /* ESC sequence */
1176                 if (nl10->esc_ctr < 1) {
1177                     nl10->esc_ctr++;
1178                 } else {
1179                     return handle_esc_control_sequence(nl10, prnr, c);
1180                 }
1181                 break;
1182             }
1183 
1184         case 145:
1185             /* enable CBM text character mode */
1186             del_mode(nl10, NL10_CBMTEXT);
1187             init_mapping(nl10, nl10->mapping_intl_id);
1188             break;
1189 
1190         case 146:
1191             /* disable reverse printing */
1192             del_mode(nl10, NL10_REVERSE);
1193             break;
1194 
1195         case 147:
1196             {
1197                 if (!is_mode(nl10, NL10_ASCII)) {
1198                     nl10->marg_b = 6;
1199                 }
1200                 break;
1201             }
1202 
1203         default:
1204             return 0;
1205     }
1206 
1207     return 1;
1208 }
1209 
1210 
handle_esc_control_sequence(nl10_t * nl10,unsigned int prnr,const uint8_t c)1211 static int handle_esc_control_sequence(nl10_t *nl10, unsigned int prnr, const uint8_t c)
1212 {
1213     switch (nl10->esc[1]) {
1214         case 10:
1215             {
1216                 /* reverse paper one line (NOT IMPLEMENTED) */
1217                 log_warning(drvnl10_log, "Command 'reverse paper one line' (%i %i) not implemented.",
1218                             nl10->esc[0], nl10->esc[1]);
1219                 nl10->esc_ctr = 0;
1220                 break;
1221             }
1222 
1223         case 12:
1224             {
1225                 /* reverse paper to top of page (NOT IMPLEMENTED) */
1226                 log_warning(drvnl10_log, "Command 'reverse paper to top of page' (%i %i) not implemented.",
1227                             nl10->esc[0], nl10->esc[1]);
1228                 nl10->esc_ctr = 0;
1229                 break;
1230             }
1231 
1232         case 15:
1233             /* enable expanded print */
1234             set_mode(nl10, NL10_EXPANDED);
1235             break;
1236 
1237         case 16:
1238             {
1239                 /* skip to horizontal point position */
1240                 if (nl10->esc_ctr < 3) {
1241                     nl10->esc_ctr++;
1242                 } else {
1243                     int i = 256 * nl10->esc[2] + nl10->esc[3];
1244                     if (i > 479) {
1245                         i = 479;
1246                     }
1247                     nl10->pos_x = BORDERX + 5 * i;
1248                     nl10->esc_ctr = 0;
1249                 }
1250                 break;
1251             }
1252 
1253         case 18:
1254             {
1255                 /* CBM: enable single-density reverse graphics printing */
1256                 if (!is_mode(nl10, NL10_ASCII)) {
1257                     nl10->gfx_mode = NL10_GFX_SINGLE | NL10_GFX_REVERSE | NL10_GFX_7PIN;
1258                 }
1259                 nl10->esc_ctr = 0;
1260                 break;
1261             }
1262 
1263         case 25:
1264             {
1265                 /* auto-feed mode control (NOT IMPLEMENTED) */
1266                 if (nl10->esc_ctr < 2) {
1267                     nl10->esc_ctr++;
1268                 } else {
1269                     log_warning(drvnl10_log, "Command 'auto-feed mode control' (%i %i %i) not implemented.",
1270                                 nl10->esc[0], nl10->esc[1], nl10->esc[2]);
1271                     nl10->esc_ctr = 0;
1272                 }
1273                 break;
1274             }
1275 
1276         case 33:
1277             {
1278                 /* master command for print style changes */
1279                 if (nl10->esc_ctr < 2) {
1280                     nl10->esc_ctr++;
1281                 } else {
1282                     del_mode(nl10, NL10_ELITE | NL10_CONDENSED | NL10_EXPANDED | NL10_UNDERLINE);
1283                     if (nl10->esc[2] & 1) {
1284                         set_mode(nl10, NL10_ELITE);
1285                     }
1286                     if (nl10->esc[2] & 2) {
1287                         set_mode(nl10, NL10_PROP);
1288                     }
1289                     if (nl10->esc[2] & 4) {
1290                         set_mode(nl10, NL10_CONDENSED);
1291                     }
1292                     if (nl10->esc[2] & 8) {
1293                         set_mode(nl10, NL10_EMPHASIZE);
1294                     }
1295                     if (nl10->esc[2] & 16) {
1296                         set_mode(nl10, NL10_BOLD);
1297                     }
1298                     if (nl10->esc[2] & 32) {
1299                         set_mode(nl10, NL10_EXPANDED);
1300                     }
1301                     if (nl10->esc[2] & 128) {
1302                         set_mode(nl10, NL10_UNDERLINE);
1303                     }
1304                     nl10->esc_ctr = 0;
1305                 }
1306                 break;
1307             }
1308 
1309         case 37:
1310             {
1311                 /* enable/disable use of character RAM */
1312                 if (nl10->esc_ctr < 3) {
1313                     nl10->esc_ctr++;
1314                 } else {
1315                     if ((nl10->esc[2] == '1' || nl10->esc[2] == 1) && nl10->esc[3] == 0) {
1316                         set_mode(nl10, NL10_USERAM);
1317                     } else if ((nl10->esc[2] == '0' || nl10->esc[2] == 0) && nl10->esc[3] == 0) {
1318                         del_mode(nl10, NL10_USERAM);
1319                     }
1320 
1321                     nl10->esc_ctr = 0;
1322                 }
1323                 break;
1324             }
1325 
1326         case 38:
1327             {
1328                 /* download new character data to RAM */
1329                 if (nl10->esc_ctr < 4 ||
1330                     nl10->esc_ctr < 4 + (is_mode(nl10, NL10_NLQ) ? 47 : 12)) {
1331                     nl10->esc_ctr++;
1332                 } else {
1333                     int i;
1334 
1335                     i = nl10->esc[3];
1336                     if ((i >= 32) && (i <= 127)) {
1337                         if (is_mode(nl10, NL10_NLQ)) {
1338                             store_char_nlq(nl10->char_ram_nlq + (i - 32) * 47, nl10->esc + 5);
1339                         } else {
1340                             store_char(nl10->char_ram + (i - 32) * 12, nl10->esc + 5);
1341                         }
1342                     }
1343 
1344                     nl10->esc[3]++;
1345                     nl10->esc_ctr = (nl10->esc[3] <= nl10->esc[4]) ? 5 : 0;
1346                 }
1347 
1348                 break;
1349             }
1350 
1351         case 42:
1352             {
1353                 /* enter graphics mode */
1354                 if (nl10->esc_ctr < 4) {
1355                     nl10->esc_ctr++;
1356                 } else {
1357                     nl10->gfx_mode = NL10_GFX_OFF;
1358                     switch (nl10->esc[2]) {
1359                         case 0:
1360                             nl10->gfx_mode = NL10_GFX_SINGLE;
1361                             break;
1362                         case 1:
1363                         case 2:
1364                             nl10->gfx_mode = NL10_GFX_DOUBLE;
1365                             break;
1366                         case 3:
1367                             nl10->gfx_mode = NL10_GFX_QUAD;
1368                             break;
1369                         case 4:
1370                             nl10->gfx_mode = NL10_GFX_CRT; break;
1371                         case 5:
1372                             nl10->gfx_mode = NL10_GFX_PLOT;
1373                             break;
1374                         case 6:
1375                             nl10->gfx_mode = NL10_GFX_CRT2;
1376                             break;
1377                     }
1378 
1379                     nl10->gfx_count = nl10->esc[3] + 256 * nl10->esc[4];
1380                     nl10->esc_ctr = 0;
1381                 }
1382                 break;
1383             }
1384 
1385         case 43:
1386             {
1387                 /* macro commands */
1388                 if (nl10->esc_ctr < 3) {
1389                     nl10->esc_ctr++;
1390                 } else if (nl10->esc[2] == 1) {
1391                     /* execute macro */
1392                     uint8_t i;
1393                     for (i = 0; i < 16; i++) {
1394                         if (nl10->macro[i] == 30) {
1395                             break;
1396                         } else {
1397                             print_char(nl10, prnr, nl10->macro[i]);
1398                         }
1399                     }
1400                     nl10->esc_ctr = 0;
1401                 } else if (nl10->esc_ctr < 2 + 16 && nl10->esc[nl10->esc_ctr] != 30) {
1402                     nl10->esc_ctr++;
1403                 } else {
1404                     /* define macro */
1405                     uint8_t i;
1406                     for (i = 0; i < 16; i++) {
1407                         nl10->macro[i] = nl10->esc[2 + i];
1408                     }
1409                     nl10->esc_ctr = 0;
1410                 }
1411 
1412                 break;
1413             }
1414 
1415         case 45:
1416             {
1417                 /* turn underline on/off */
1418                 if (nl10->esc_ctr < 2) {
1419                     nl10->esc_ctr++;
1420                 } else {
1421                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1422                         del_mode(nl10, NL10_UNDERLINE);
1423                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1424                         set_mode(nl10, NL10_UNDERLINE);
1425                     }
1426 
1427                     nl10->esc_ctr = 0;
1428                 }
1429                 break;
1430             }
1431 
1432         case 48:
1433             /* line spacing 1/8" */
1434             nl10->linespace = 3 * 9;
1435             nl10->esc_ctr = 0;
1436             break;
1437 
1438         case 49:
1439             /* line spacing 7/72" */
1440             nl10->linespace = 3 * 7;
1441             nl10->esc_ctr = 0;
1442             break;
1443 
1444         case 50:
1445             /* line spacing 1/6" */
1446             nl10->linespace = 3 * 12;
1447             nl10->esc_ctr = 0;
1448             break;
1449 
1450         case 51:
1451             {
1452                 /* set line spacing to n/216" */
1453                 if (nl10->esc_ctr < 2) {
1454                     nl10->esc_ctr++;
1455                 } else {
1456                     nl10->linespace = nl10->esc[2];
1457                     nl10->esc_ctr = 0;
1458                 }
1459                 break;
1460             }
1461 
1462         case 52:
1463             /* Selects italic characters */
1464             set_mode(nl10, NL10_ITALIC);
1465             nl10->esc_ctr = 0;
1466             break;
1467 
1468         case 53:
1469             /* Cancels italic characters */
1470             del_mode(nl10, NL10_ITALIC);
1471             nl10->esc_ctr = 0;
1472             break;
1473 
1474         case 58:
1475             {
1476                 /* copy character set to RAM */
1477                 if (nl10->esc_ctr < 4) {
1478                     nl10->esc_ctr++;
1479                 } else {
1480                     if (nl10->esc[2] == 0 && nl10->esc[3] == 0 && nl10->esc[4] == 0) {
1481                         int b;
1482                         for (b = 0; b < 96; b++) {
1483                             memcpy(nl10->char_ram + b * 12, drv_nl10_charset + nl10->mapping[b + 32] * 12, 12);
1484                             memcpy(nl10->char_ram_nlq + b * 47, drv_nl10_charset_nlq + nl10->mapping[b + 32] * 47, 47);
1485                         }
1486                     }
1487                     nl10->esc_ctr = 0;
1488                 }
1489 
1490                 break;
1491             }
1492 
1493         case 64:
1494             /* (Soft) Reset printer */
1495             reset(nl10);
1496             nl10->esc_ctr = 0;
1497             break;
1498 
1499         case 65:
1500             {
1501                 /* line spacing n/72" */
1502                 if (nl10->esc_ctr < 2) {
1503                     nl10->esc_ctr++;
1504                 } else {
1505                     nl10->linespace = 3 * nl10->esc[2];
1506                     nl10->esc_ctr = 0;
1507                 }
1508                 break;
1509             }
1510 
1511         case 66:
1512             {
1513                 /* set vertical tabs */
1514                 if (nl10->esc_ctr < 3 || (nl10->esc_ctr < 42 && (nl10->esc[nl10->esc_ctr] > nl10->esc[nl10->esc_ctr - 1]))) {
1515                     nl10->esc_ctr++;
1516                 } else {
1517                     int i;
1518                     for (i = 2; i < nl10->esc_ctr; i++) {
1519                         nl10->vtabs[i - 2] = nl10->esc[i];
1520                     }
1521                     nl10->vtabs[i - 2] = 0;
1522                     nl10->esc_ctr = 0;
1523                 }
1524                 break;
1525             }
1526 
1527         case 67:
1528             {
1529                 /* set page length */
1530                 if (nl10->esc_ctr < 2 || ((nl10->esc[2] == 0) && nl10->esc_ctr < 3)) {
1531                     nl10->esc_ctr++;
1532                 } else {
1533                     if (nl10->esc[2] == 0) {
1534                         /* set page length to n inches (NOT IMPLEMENTED) */
1535                         log_warning(drvnl10_log, "Command 'set page length to n inches' (%i %i %i %i) not implemented.",
1536                                     nl10->esc[0], nl10->esc[1], nl10->esc[2], nl10->esc[3]);
1537                         nl10->esc_ctr = 0;
1538                     } else {
1539                         /* set page length to n lines (NOT IMPLEMENTED) */
1540                         log_warning(drvnl10_log, "Command 'set page length to n lines' (%i %i %i) not implemented.",
1541                                     nl10->esc[0], nl10->esc[1], nl10->esc[2]);
1542                         nl10->esc_ctr = 0;
1543                     }
1544                 }
1545                 break;
1546             }
1547 
1548         case 68:
1549             {
1550                 /* set horizontal tabs */
1551                 if (nl10->esc_ctr < 3 || (nl10->esc_ctr < 42 && (nl10->esc[nl10->esc_ctr] > nl10->esc[nl10->esc_ctr - 1]))) {
1552                     nl10->esc_ctr++;
1553                 } else {
1554                     int i;
1555                     for (i = 2; i < nl10->esc_ctr; i++) {
1556                         nl10->htabs[i - 2] = nl10->esc[i];
1557                     }
1558                     nl10->htabs[i - 2] = 0;
1559                     nl10->esc_ctr = 0;
1560                 }
1561                 break;
1562             }
1563 
1564         case 69:
1565             /* Selects emphasized printing */
1566             set_mode(nl10, NL10_EMPHASIZE);
1567             nl10->esc_ctr = 0;
1568             break;
1569 
1570         case 70:
1571             /* Cancels emphasized printing */
1572             del_mode(nl10, NL10_EMPHASIZE);
1573             nl10->esc_ctr = 0;
1574             break;
1575 
1576         case 71:
1577             /* Selects boldface printing */
1578             set_mode(nl10, NL10_BOLD);
1579             nl10->esc_ctr = 0;
1580             break;
1581 
1582         case 72:
1583             /* Cancels boldface printing */
1584             del_mode(nl10, NL10_BOLD);
1585             nl10->esc_ctr = 0;
1586             break;
1587 
1588         case 74:
1589             {
1590                 /* one-time linefeed of n/216" */
1591                 if (nl10->esc_ctr < 2) {
1592                     nl10->esc_ctr++;
1593                 } else {
1594                     int tmp = nl10->linespace;
1595                     nl10->linespace = nl10->esc[2];
1596                     linefeed(nl10, prnr);
1597                     nl10->linespace = tmp;
1598                     nl10->esc_ctr = 0;
1599                 }
1600                 break;
1601             }
1602 
1603         case 75: /* Prints normal density graphics */
1604         case 76: /* Prints double density graphics */
1605         case 89: /* Prints double density graphics (double speed) */
1606         case 90: /* Prints quadruple density graphics */
1607             {
1608                 if (nl10->esc_ctr < 3) {
1609                     nl10->esc_ctr++;
1610                 } else {
1611                     nl10->gfx_mode = NL10_GFX_OFF;
1612                     switch (nl10->esc[1]) {
1613                         case 'K':
1614                             nl10->gfx_mode = NL10_GFX_SINGLE;
1615                             break;
1616                         case 'L':
1617                         case 'Y':
1618                             nl10->gfx_mode = NL10_GFX_DOUBLE;
1619                             break;
1620                         case 'Z':
1621                             nl10->gfx_mode = NL10_GFX_QUAD;
1622                             break;
1623                     }
1624 
1625                     nl10->gfx_count = nl10->esc[2] + 256 * nl10->esc[3];
1626                     nl10->esc_ctr = 0;
1627                 }
1628                 break;
1629             }
1630 
1631         case 77:
1632             /* Sets print pitch to elite */
1633             set_mode(nl10, NL10_ELITE);
1634             nl10->esc_ctr = 0;
1635             break;
1636 
1637         case 78:
1638             {
1639                 /* set bottom margin to n lines */
1640                 if (nl10->esc_ctr < 2) {
1641                     nl10->esc_ctr++;
1642                 } else {
1643                     nl10->marg_b = nl10->esc[2];
1644                     nl10->esc_ctr = 0;
1645                 }
1646                 break;
1647             }
1648 
1649         case 79:
1650             /* clear top/bottom margins */
1651             nl10->marg_t = 0;
1652             nl10->marg_b = 0;
1653             nl10->esc_ctr = 0;
1654             break;
1655 
1656         case 80:
1657             /* Set print pitch to pica */
1658             del_mode(nl10, NL10_ELITE);
1659             nl10->esc_ctr = 0;
1660             break;
1661 
1662         case 81:
1663             {
1664                 /* Set right margin */
1665                 if (nl10->esc_ctr < 2) {
1666                     nl10->esc_ctr++;
1667                 } else {
1668                     nl10->marg_r = BORDERX + (int) (get_char_width(nl10, ' ', 1) * nl10->esc[2]);
1669                     if (nl10->marg_r > MAX_COL - BORDERX) {
1670                         nl10->marg_r = MAX_COL - BORDERX;
1671                     }
1672                     nl10->esc_ctr = 0;
1673                 }
1674                 break;
1675             }
1676 
1677         case 82:
1678             {
1679                 /* Select an international character set */
1680                 if (nl10->esc_ctr < 2) {
1681                     nl10->esc_ctr++;
1682                 } else {
1683                     init_mapping(nl10, nl10->esc[2]);
1684                     nl10->esc_ctr = 0;
1685                 }
1686                 break;
1687             }
1688 
1689         case 83:
1690             {
1691                 /* Select superscript or subscript */
1692                 if (nl10->esc_ctr < 2) {
1693                     nl10->esc_ctr++;
1694                 } else {
1695                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1696                         del_mode(nl10, NL10_SUBSCRIPT);
1697                         set_mode(nl10, NL10_SUPERSCRIPT);
1698                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1699                         set_mode(nl10, NL10_SUBSCRIPT);
1700                         del_mode(nl10, NL10_SUPERSCRIPT);
1701                     }
1702 
1703                     nl10->esc_ctr = 0;
1704                 }
1705                 break;
1706             }
1707 
1708         case 84:
1709             {
1710                 /* Cancel superscript and subscript */
1711                 del_mode(nl10, NL10_SUPERSCRIPT | NL10_SUBSCRIPT);
1712                 nl10->esc_ctr = 0;
1713                 break;
1714             }
1715 
1716         case 87:
1717             {
1718                 /* Select/cancel expanded print */
1719                 if (nl10->esc_ctr < 2) {
1720                     nl10->esc_ctr++;
1721                 } else {
1722                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1723                         del_mode(nl10, NL10_EXPANDED);
1724                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1725                         set_mode(nl10, NL10_EXPANDED);
1726                     }
1727 
1728                     nl10->esc_ctr = 0;
1729                 }
1730                 break;
1731             }
1732 
1733         case 93:
1734             {
1735                 /* 0=CBM mode, 1=ASCII mode */
1736                 if (nl10->esc_ctr < 2) {
1737                     nl10->esc_ctr++;
1738                 } else {
1739                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1740                         del_mode(nl10, NL10_ASCII);
1741                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1742                         set_mode(nl10, NL10_ASCII);
1743                     }
1744 
1745                     init_mapping(nl10, nl10->mapping_intl_id);
1746                     nl10->esc_ctr = 0;
1747                 }
1748                 break;
1749             }
1750 
1751         case 97:
1752             {
1753                 /* set horizontal alignment (NOT IMPLEMENTED) */
1754                 if (nl10->esc_ctr < 2) {
1755                     nl10->esc_ctr++;
1756                 } else {
1757                     log_warning(drvnl10_log, "Command 'set horizontal alignment' (%i %i %i) not implemented.",
1758                                 nl10->esc[0], nl10->esc[1], nl10->esc[2]);
1759                     nl10->esc_ctr = 0;
1760                 }
1761                 break;
1762             }
1763 
1764         case 104:
1765             {
1766                 /* Select double/quadruple sized printing */
1767                 if (nl10->esc_ctr < 2) {
1768                     nl10->esc_ctr++;
1769                 } else {
1770                     switch (nl10->esc[2]) {
1771                         case 0:
1772                             nl10->expand = 1;
1773                             nl10->expand_half = 0;
1774                             break;
1775                         case 1:
1776                             nl10->expand = 2;
1777                             nl10->expand_half = 0;
1778                             break;
1779                         case 2:
1780                             nl10->expand = 4;
1781                             nl10->expand_half = 0;
1782                             break;
1783                         case 3:
1784                             nl10->expand = 2;
1785                             nl10->expand_half = 1;
1786                             break;
1787                         case 4:
1788                             nl10->expand = 4;
1789                             nl10->expand_half = 1;
1790                             break;
1791                         case 5:
1792                             nl10->expand = 2;
1793                             nl10->expand_half = 2;
1794                             break;
1795                         case 6:
1796                             nl10->expand = 4;
1797                             nl10->expand_half = 2;
1798                             break;
1799                     }
1800                     nl10->esc_ctr = 0;
1801                 }
1802                 break;
1803             }
1804 
1805         case 108:
1806             {
1807                 /* Set left margin */
1808                 if (nl10->esc_ctr < 2) {
1809                     nl10->esc_ctr++;
1810                 } else {
1811                     nl10->marg_l = BORDERX + (int) (get_char_width(nl10, ' ', 1) * nl10->esc[2]);
1812                     nl10->esc_ctr = 0;
1813                 }
1814                 break;
1815             }
1816 
1817         case 112:
1818             {
1819                 /* Select/cancel proportional print */
1820                 if (nl10->esc_ctr < 2) {
1821                     nl10->esc_ctr++;
1822                 } else {
1823                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1824                         del_mode(nl10, NL10_PROP);
1825                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1826                         set_mode(nl10, NL10_PROP);
1827                     }
1828 
1829                     nl10->esc_ctr = 0;
1830                 }
1831                 break;
1832             }
1833 
1834         case 114:
1835             {
1836                 /* set top margin to n lines */
1837                 if (nl10->esc_ctr < 2) {
1838                     nl10->esc_ctr++;
1839                 } else {
1840                     nl10->marg_t = nl10->esc[2];
1841                     nl10->esc_ctr = 0;
1842                 }
1843                 break;
1844             }
1845 
1846         case 120:
1847             {
1848                 /* Select/cancel NLQ mode */
1849                 if (nl10->esc_ctr < 2) {
1850                     nl10->esc_ctr++;
1851                 } else {
1852                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1853                         del_mode(nl10, NL10_NLQ);
1854                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1855                         set_mode(nl10, NL10_NLQ);
1856                     }
1857 
1858                     nl10->esc_ctr = 0;
1859                 }
1860 
1861                 break;
1862             }
1863 
1864         case 126:
1865             {
1866                 /* print 0 with/without slash */
1867                 if (nl10->esc_ctr < 2) {
1868                     nl10->esc_ctr++;
1869                 } else {
1870                     if ((nl10->esc[2] == '0') || (nl10->esc[2] == 0)) {
1871                         del_mode(nl10, NL10_ZERO_CROSSED);
1872                     } else if ((nl10->esc[2] == '1') || (nl10->esc[2] == 1)) {
1873                         set_mode(nl10, NL10_ZERO_CROSSED);
1874                     }
1875 
1876                     init_mapping(nl10, nl10->mapping_intl_id);
1877                     nl10->esc_ctr = 0;
1878                 }
1879 
1880                 break;
1881             }
1882 
1883         default:
1884             {
1885                 log_warning(drvnl10_log, "Unsupported escape-sequence: %i %i",
1886                             nl10->esc[0], nl10->esc[1]);
1887                 nl10->esc_ctr = 0;
1888                 return 1;
1889             }
1890     }
1891 
1892     return 1;
1893 }
1894 
1895 
1896 /* ------------------------------------------------------------------------- */
1897 /* Interface to the upper layer.  */
1898 
drv_nl10_open(unsigned int prnr,unsigned int secondary)1899 static int drv_nl10_open(unsigned int prnr, unsigned int secondary)
1900 {
1901     nl10_t *nl10 = &(drv_nl10[prnr]);
1902 
1903     if (secondary == DRIVER_FIRST_OPEN) {
1904         output_parameter_t output_parameter;
1905 
1906         output_parameter.maxcol = MAX_COL;
1907         output_parameter.maxrow = MAX_ROW;
1908         output_parameter.dpi_x = 300;
1909         output_parameter.dpi_y = 300;
1910         output_parameter.palette = palette;
1911 
1912         drv_nl10[prnr].pos_y = 0;
1913         drv_nl10[prnr].pos_y_pix = 0;
1914         drv_nl10[prnr].isopen = 1;
1915 
1916         return output_select_open(prnr, &output_parameter);
1917     }
1918 
1919     if (secondary == 7) {
1920         set_mode(nl10, NL10_CBMTEXT);
1921     } else {
1922         del_mode(nl10, NL10_CBMTEXT);
1923     }
1924 
1925     init_mapping(nl10, drv_nl10[prnr].mapping_intl_id);
1926 
1927     return 0;
1928 }
1929 
drv_nl10_close(unsigned int prnr,unsigned int secondary)1930 static void drv_nl10_close(unsigned int prnr, unsigned int secondary)
1931 {
1932     /* cannot call output_select_close() here since it would eject the
1933        current page, which is not what "close"ing a channel to a real
1934        printer does */
1935     /*
1936     if (secondary == DRIVER_LAST_CLOSE) {
1937         output_select_close(prnr);
1938     }
1939     */
1940 }
1941 
drv_nl10_putc(unsigned int prnr,unsigned int secondary,uint8_t b)1942 static int drv_nl10_putc(unsigned int prnr, unsigned int secondary, uint8_t b)
1943 {
1944     print_char(&drv_nl10[prnr], prnr, b);
1945     return 0;
1946 }
1947 
drv_nl10_getc(unsigned int prnr,unsigned int secondary,uint8_t * b)1948 static int drv_nl10_getc(unsigned int prnr, unsigned int secondary, uint8_t *b)
1949 {
1950     return 0x80;
1951 }
1952 
drv_nl10_flush(unsigned int prnr,unsigned int secondary)1953 static int drv_nl10_flush(unsigned int prnr, unsigned int secondary)
1954 {
1955     return 0;
1956 }
1957 
drv_nl10_formfeed(unsigned int prnr)1958 static int drv_nl10_formfeed(unsigned int prnr)
1959 {
1960     nl10_t *nl10 = &(drv_nl10[prnr]);
1961     if (nl10->isopen) {
1962         formfeed(nl10, prnr);
1963     }
1964     return 0;
1965 }
1966 
drv_nl10_init_resources(void)1967 int drv_nl10_init_resources(void)
1968 {
1969     driver_select_t driver_select;
1970 
1971     driver_select.drv_name = "nl10";
1972     driver_select.drv_open = drv_nl10_open;
1973     driver_select.drv_close = drv_nl10_close;
1974     driver_select.drv_putc = drv_nl10_putc;
1975     driver_select.drv_getc = drv_nl10_getc;
1976     driver_select.drv_flush = drv_nl10_flush;
1977     driver_select.drv_formfeed = drv_nl10_formfeed;
1978 
1979     driver_select_register(&driver_select);
1980 
1981     return 0;
1982 }
1983 
drv_nl10_init(void)1984 int drv_nl10_init(void)
1985 {
1986     int i;
1987     static const char *color_names[2] =
1988     {
1989         "Black", "White"
1990     };
1991 
1992     drvnl10_log = log_open("NL10");
1993 
1994     for (i = 0; i < NUM_OUTPUT_SELECT; i++) {
1995         drv_nl10[i].char_ram = lib_malloc(96 * 12);
1996         drv_nl10[i].char_ram_nlq = lib_malloc(96 * 47);
1997         reset_hard(&(drv_nl10[i]));
1998         drv_nl10[i].isopen = 0;
1999     }
2000 
2001     if (drv_nl10_init_charset() < 0) {
2002         return -1;
2003     }
2004 
2005     palette = palette_create(2, color_names);
2006 
2007     if (palette == NULL) {
2008         return -1;
2009     }
2010 
2011     if (palette_load("nl10" FSDEV_EXT_SEP_STR "vpl", palette) < 0) {
2012 #ifndef __LIBRETRO__
2013         log_error(drvnl10_log, "Cannot load palette file `%s'.",
2014                   "nl10" FSDEV_EXT_SEP_STR "vpl");
2015 #endif
2016         return -1;
2017     }
2018 
2019     log_message(drvnl10_log, "Printer driver initialized.");
2020 
2021     return 0;
2022 }
2023 
drv_nl10_shutdown(void)2024 void drv_nl10_shutdown(void)
2025 {
2026     int i;
2027     palette_free(palette);
2028 
2029     for (i = 0; i < NUM_OUTPUT_SELECT; i++) {
2030         if (drv_nl10[i].isopen) {
2031             output_select_close(i);
2032         }
2033 
2034         lib_free(drv_nl10[i].char_ram);
2035         lib_free(drv_nl10[i].char_ram_nlq);
2036     }
2037 }
2038 
2039 
drv_nl10_reset(void)2040 void drv_nl10_reset(void)
2041 {
2042     int i;
2043     for (i = 0; i < NUM_OUTPUT_SELECT; i++) {
2044         reset_hard(&(drv_nl10[i]));
2045     }
2046 }
2047 
2048 
2049 /* ------------------------------------------------------------------------- */
2050 /* character data and mappings  */
2051 
2052 
2053 static const uint8_t drv_nl10_charset_mapping_intl[3][8][14] =
2054 {   /* ASCII */
2055     {
2056         /*    #     $     @     [     \     ]     {     |     }     ~    */
2057         /*   35,   36,   64,   91,   92,   93,  123,  124,  125,  126,  219,  220,  221,  222 */
2058         /* 0x23, 0x24, 0x40, 0x5b, 0x5c, 0x5d, 0x7b, 0x7c, 0x7d, 0x7e, 0xdb, 0xdc, 0xdd, 0xde */
2059 
2060         {  0x23, 0x24, 0x40, 0x5b, 0x5c, 0x5d, 0x7b, 0x7c, 0x7d, 0x7e, 0x81, 0x82, 0x83, 0x84}, /* CBM standard */
2061         {  0x23, 0x24, 0x40, 0x5b, 0x5c, 0x5d, 0x7b, 0x7c, 0x7d, 0x7e, 0x7b, 0x7c, 0x7d, 0x7e}, /* USA */
2062         {  0x23, 0x24, 0x10, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x11, 0x1a, 0x1b, 0x1c, 0x11}, /* Germany */
2063         {  0x23, 0x24, 0x40, 0x12, 0x14, 0x0d, 0x13, 0x15, 0x0e, 0x7e, 0x13, 0x15, 0x0e, 0x7e}, /* Denmark */
2064         {  0x23, 0x24, 0x00, 0x05, 0x0f, 0x10, 0x1e, 0x02, 0x01, 0x16, 0x1e, 0x02, 0x01, 0x16}, /* France */
2065         {  0x23, 0x0b, 0x1d, 0x17, 0x18, 0x0d, 0x1a, 0x1b, 0x0e, 0x1c, 0x1a, 0x1b, 0x0e, 0x1c}, /* Sweden */
2066         {  0x23, 0x24, 0x40, 0x05, 0x5c, 0x1e, 0x00, 0x03, 0x01, 0x04, 0x00, 0x03, 0x01, 0x04}, /* Italy */
2067         {  0x0c, 0x24, 0x40, 0x07, 0x09, 0x08, 0x16, 0x0a, 0x7d, 0x7e, 0x16, 0x0a, 0x7d, 0x7e}, /* Spain */
2068     },
2069 
2070     /* CBM graphics */
2071     {
2072         {  0x23, 0x24, 0x40, 0x5b, 0x06, 0x5d, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* CBM standard */
2073         {  0x23, 0x24, 0x40, 0x5b, 0x5c, 0x5d, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* USA */
2074         {  0x23, 0x24, 0x10, 0x17, 0x18, 0x19, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* Germany */
2075         {  0x23, 0x24, 0x40, 0x12, 0x14, 0x0d, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* Denmark */
2076         {  0x23, 0x24, 0x00, 0x05, 0x0f, 0x10, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* France */
2077         {  0x23, 0x0b, 0x1d, 0x17, 0x18, 0x0d, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* Sweden */
2078         {  0x23, 0x24, 0x40, 0x05, 0x5c, 0x1e, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* Italy */
2079         {  0x0c, 0x24, 0x40, 0x07, 0x09, 0x08, 0xa1, 0xa2, 0xa3, 0xa4, 0xa1, 0xa2, 0xa3, 0xa4}, /* Spain */
2080     },
2081 
2082     /* CBM text */
2083     {
2084         {  0x23, 0x24, 0x40, 0x5b, 0x06, 0x5d, 0x81, 0x82, 0x83, 0x84, 0x81, 0x82, 0x83, 0x84}, /* CBM standard */
2085         {  0x23, 0x24, 0x40, 0x5b, 0x5c, 0x5d, 0x7b, 0x7c, 0x7d, 0x7e, 0x7b, 0x7c, 0x7d, 0x7e}, /* USA */
2086         {  0x23, 0x24, 0x10, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x11, 0x1a, 0x1b, 0x1c, 0x11}, /* Germany */
2087         {  0x23, 0x24, 0x40, 0x12, 0x14, 0x0d, 0x13, 0x15, 0x0e, 0x7e, 0x13, 0x15, 0x0e, 0x7e}, /* Denmark */
2088         {  0x23, 0x24, 0x00, 0x05, 0x0f, 0x10, 0x1e, 0x02, 0x01, 0x16, 0x1e, 0x02, 0x01, 0x16}, /* France */
2089         {  0x23, 0x0b, 0x1d, 0x17, 0x18, 0x0d, 0x1a, 0x1b, 0x0e, 0x1c, 0x1a, 0x1b, 0x0e, 0x1c}, /* Sweden */
2090         {  0x23, 0x24, 0x40, 0x05, 0x5c, 0x1e, 0x00, 0x03, 0x01, 0x04, 0x00, 0x03, 0x01, 0x04}, /* Italy */
2091         {  0x0c, 0x24, 0x40, 0x07, 0x09, 0x08, 0x16, 0x0a, 0x7d, 0x7e, 0x16, 0x0a, 0x7d, 0x7e}, /* Spain */
2092     }
2093 };
2094 
2095 
2096 static const uint8_t drv_nl10_charset_mapping[3][256] =
2097 {   /* ASCII */
2098     {
2099         /* unprintable */
2100         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2101         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2102 
2103         /* digits and punctuation */
2104         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
2105         0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
2106 
2107         /* uppercase characters */
2108         0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
2109         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
2110 
2111         /* lowercase characters */
2112         0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
2113         0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x20,
2114 
2115         /* unprintable */
2116         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2117         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2118 
2119         /* cbm graphic symbols */
2120         0x20, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xc7, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
2121         0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2122 
2123         /* uppercase characters */
2124         0x86, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
2125         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x81, 0x82, 0x83, 0x84, 0x85,
2126 
2127         /* cbm graphic symbols */
2128         0x20, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xc7, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
2129         0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0x84
2130     },
2131     /* CBM graphic */
2132     {
2133         /* unprintable */
2134         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2135         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2136 
2137         /* digits and punctuation */
2138         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
2139         0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
2140 
2141         /* uppercase characters */
2142         0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
2143         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x06, 0x5d, 0x7f, 0x80,
2144 
2145         /* shift-graphic symbols */
2146         0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
2147         0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
2148 
2149         /* unprintable */
2150         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2151         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2152 
2153         /* cbm-graphic symbols */
2154         0x20, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
2155         0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2156 
2157         /* shift-graphic symbols */
2158         0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
2159         0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
2160 
2161         /* cbm-graphic symbols */
2162         0x20, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
2163         0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xa4
2164     },
2165     /* CBM text */
2166     {
2167         /* unprintable */
2168         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2169         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2170 
2171         /* digits and punctuation */
2172         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
2173         0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
2174 
2175         /* lowercase characters */
2176         0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
2177         0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x06, 0x5d, 0x7f, 0x80,
2178 
2179         /* uppercase characters */
2180         0x86, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
2181         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x81, 0x82, 0x83, 0x84, 0x85,
2182 
2183         /* unprintable */
2184         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2185         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2186 
2187         /* cbm-graphic symbols */
2188         0x20, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xc7, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
2189         0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
2190 
2191         /* uppercase characters */
2192         0x86, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
2193         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x81, 0x82, 0x83, 0x84, 0x85,
2194 
2195         /* cbm-graphic symbols */
2196         0x20, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xc7, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5,
2197         0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0x84
2198     }
2199 };
2200 
2201 /* -------------------------------------------------------------------------- */
2202 
drv_nl10_init_charset(void)2203 static int drv_nl10_init_charset(void)
2204 {
2205     char *name = "nl10-cbm";
2206     int i, j;
2207 
2208     memset(drv_nl10_charset_nlq, 0, CHARSET_SIZE * 47);
2209     memset(drv_nl10_charset_nlq_italic, 0, CHARSET_SIZE * 47);
2210 
2211     /* load nl-10 rom file */
2212     if (sysfile_load(name, drv_nl10_rom, NL10_ROM_SIZE, NL10_ROM_SIZE) < 0) {
2213         memset(drv_nl10_rom, 0, NL10_ROM_SIZE);
2214         log_error(drvnl10_log, "Could not load NL-10 ROM file '%s'.", name);
2215         return -1;
2216     }
2217 
2218     /* check version string */
2219     if (memcmp(drv_nl10_rom + 0x3c7c, "STAR NL-10C VER 1.1\xff", 20) != 0) {
2220         log_warning(drvnl10_log, "Invalid NL-10 ROM file.");
2221     }
2222 
2223     /* init NLQ characters */
2224     for (i = 0; i < 129; i++) {
2225         /* roman */
2226         memcpy(drv_nl10_charset_nlq + (i * 47) + 0, drv_nl10_rom + 0x0960 + i * 24, 24);
2227         memcpy(drv_nl10_charset_nlq + (i * 47) + 24, drv_nl10_rom + 0x2191 + i * 24, 23);
2228 
2229         /* italic */
2230         memcpy(drv_nl10_charset_nlq_italic + (i * 47) + 0, drv_nl10_rom + 0x1578 + i * 24, 24);
2231         memcpy(drv_nl10_charset_nlq_italic + (i * 47) + 24, drv_nl10_rom + 0x2da9 + i * 24, 23);
2232     }
2233 
2234     /* construct nlq cbm-graphic characters from draft cbm-graphic characters */
2235     for (i = 129; i < CHARSET_SIZE; i++) {
2236         drv_nl10_charset_nlq[i * 47] = drv_nl10_charset[i * 12] & 128 ? 255 : 0;
2237         drv_nl10_charset_nlq_italic[i * 47] = drv_nl10_charset[i * 12] & 128 ? 255 : 0;
2238 
2239         for (j = 0; j < 6; j++) {
2240             uint8_t b = drv_nl10_charset[i * 12 + j * 2 + 1];
2241             drv_nl10_charset_nlq[i * 47 + j * 4 + 1] = b;
2242             drv_nl10_charset_nlq[i * 47 + j * 4 + 3] = b;
2243             drv_nl10_charset_nlq[i * 47 + j * 4 + 24] = b;
2244             drv_nl10_charset_nlq[i * 47 + j * 4 + 26] = b;
2245 
2246             drv_nl10_charset_nlq_italic[i * 47 + j * 4 + 1] = b;
2247             drv_nl10_charset_nlq_italic[i * 47 + j * 4 + 3] = b;
2248             drv_nl10_charset_nlq_italic[i * 47 + j * 4 + 24] = b;
2249             drv_nl10_charset_nlq_italic[i * 47 + j * 4 + 26] = b;
2250         }
2251     }
2252 
2253     return 0;
2254 }
2255