1 /*
2 * drv-mps803.c - MPS803 printer driver.
3 *
4 * Written by
5 * Thomas Bretz <tbretz@gsi.de>
6 * Andreas Boose <viceteam@t-online.de>
7 *
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 /* #define DEBUG_MPS803 */
29
30 #include "vice.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "archdep.h"
37 #include "driver-select.h"
38 #include "drv-mps803.h"
39 #include "log.h"
40 #include "output-select.h"
41 #include "output.h"
42 #include "palette.h"
43 #include "sysfile.h"
44 #include "types.h"
45
46 #ifdef DEBUG_MPS803
47 #define DBG(x) printf x
48 #else
49 #define DBG(x)
50 #endif
51
52 #define MAX_COL 480
53 #define MAX_ROW 66 * 10
54
55 #define MPS803_ROM_SIZE (7 * 512)
56
57 #define MPS_REVERSE 0x01
58 #define MPS_CRSRUP 0x02 /* set in gfxmode (default) unset in businessmode */
59 #define MPS_BITMODE 0x04
60 #define MPS_DBLWDTH 0x08
61 #define MPS_REPEAT 0x10
62 #define MPS_ESC 0x20
63 #define MPS_QUOTED 0x40 /* odd number of quotes in line (textmode) */
64 #define MPS_BUSINESS 0x80 /* opened with SA = 7 in businessmode */
65
66 struct mps_s {
67 uint8_t line[MAX_COL][7];
68 int repeatn;
69 int pos;
70 int tab;
71 uint8_t tabc[3];
72 int mode;
73 };
74 typedef struct mps_s mps_t;
75
76 #ifdef USE_EMBEDDED
77 #include "printermps803.h"
78 #else
79 static uint8_t charset[512][7];
80 #endif
81
82 static mps_t drv_mps803[NUM_OUTPUT_SELECT];
83 static palette_t *palette = NULL;
84
85 /* Logging goes here. */
86 static log_t drv803_log = LOG_ERR;
87
88 /* ------------------------------------------------------------------------- */
89 /* MPS803 printer engine. */
90
set_mode(mps_t * mps,unsigned int m)91 static void set_mode(mps_t *mps, unsigned int m)
92 {
93 mps->mode |= m;
94 }
95
del_mode(mps_t * mps,unsigned int m)96 static void del_mode(mps_t *mps, unsigned int m)
97 {
98 mps->mode &= ~m;
99 }
100
is_mode(mps_t * mps,unsigned int m)101 static int is_mode(mps_t *mps, unsigned int m)
102 {
103 return mps->mode & m;
104 }
105
get_charset_bit(mps_t * mps,int nr,unsigned int col,unsigned int row)106 static int get_charset_bit(mps_t *mps, int nr, unsigned int col,
107 unsigned int row)
108 {
109 int reverse, result;
110
111 reverse = is_mode(mps, MPS_REVERSE);
112
113 result = charset[nr][row] & (1 << (7 - col)) ? !reverse : reverse;
114
115 return result;
116 }
117
print_cbm_char(mps_t * mps,const uint8_t rawchar)118 static void print_cbm_char(mps_t *mps, const uint8_t rawchar)
119 {
120 unsigned int y, x;
121 int c, err = 0;
122
123 c = (int)rawchar;
124
125 /* in the ROM, graphics charset comes first, then business */
126 if (!is_mode(mps, MPS_CRSRUP)) {
127 c += 256;
128 }
129
130 for (y = 0; y < 7; y++) {
131 if (is_mode(mps, MPS_DBLWDTH)) {
132 for (x = 0; x < 6; x++) {
133 if ((mps->pos + x * 2) >= MAX_COL) {
134 err = 1;
135 break;
136 }
137 mps->line[mps->pos + x * 2][y] = get_charset_bit(mps, c, x, y);
138 if ((mps->pos + x * 2 + 1) >= MAX_COL) {
139 err = 1;
140 break;
141 }
142 mps->line[mps->pos + x * 2 + 1][y] = get_charset_bit(mps, c, x, y);
143 }
144 } else {
145 for (x = 0; x < 6; x++) {
146 if ((mps->pos + x) >= MAX_COL) {
147 err = 1;
148 break;
149 }
150 mps->line[mps->pos + x][y] = get_charset_bit(mps, c, x, y);
151 }
152 }
153 }
154
155 if (err) {
156 log_error(drv803_log, "Printing beyond limit of %d dots.", MAX_COL);
157 }
158
159 mps->pos += is_mode(mps, MPS_DBLWDTH) ? 12 : 6;
160 }
161
write_line(mps_t * mps,unsigned int prnr)162 static void write_line(mps_t *mps, unsigned int prnr)
163 {
164 int x, y;
165
166 for (y = 0; y < 7; y++) {
167 for (x = 0; x < 480; x++) {
168 output_select_putc(prnr, (uint8_t)(mps->line[x][y]
169 ? OUTPUT_PIXEL_BLACK : OUTPUT_PIXEL_WHITE));
170 }
171 output_select_putc(prnr, (uint8_t)(OUTPUT_NEWLINE));
172 }
173
174 if (!is_mode(mps, MPS_BITMODE)) {
175 /* bitmode: 9 rows/inch (7lines/row * 9rows/inch=63 lines/inch) */
176 /* charmode: 6 rows/inch (7lines/row * 6rows/inch=42 lines/inch) */
177 /* --> 63lines/inch - 42lines/inch = 21lines/inch missing */
178 /* --> 21lines/inch / 9row/inch = 3lines/row missing */
179 output_select_putc(prnr, OUTPUT_NEWLINE);
180 output_select_putc(prnr, OUTPUT_NEWLINE);
181 output_select_putc(prnr, OUTPUT_NEWLINE);
182 }
183
184 mps->pos = 0;
185 }
186
clear_buffer(mps_t * mps)187 static void clear_buffer(mps_t *mps)
188 {
189 unsigned int x, y;
190
191 for (x = 0; x < MAX_COL; x++) {
192 for (y = 0; y < 7; y++) {
193 mps->line[x][y] = 0;
194 }
195 }
196 }
197
bitmode_off(mps_t * mps)198 static void bitmode_off(mps_t *mps)
199 {
200 del_mode(mps, MPS_BITMODE);
201 }
202
print_bitmask(mps_t * mps,unsigned int prnr,const char c)203 static void print_bitmask(mps_t *mps, unsigned int prnr, const char c)
204 {
205 unsigned int y;
206 unsigned int i;
207
208 if (!mps->repeatn) {
209 mps->repeatn=1;
210 }
211
212 for (i = 0; i < (unsigned int)(mps->repeatn); i++) {
213 if (mps->pos >= MAX_COL) { /* flush buffer*/
214 write_line(mps, prnr);
215 clear_buffer(mps);
216 }
217 for (y = 0; y < 7; y++) {
218 mps->line[mps->pos][y] = c & (1 << (y)) ? 1 : 0;
219 }
220
221 mps->pos++;
222 }
223 mps->repeatn=0;
224 }
225
print_char(mps_t * mps,unsigned int prnr,const uint8_t c)226 static void print_char(mps_t *mps, unsigned int prnr, const uint8_t c)
227 {
228 if (mps->tab) { /* decode tab-number*/
229 mps->tabc[2 - mps->tab] = c;
230
231 if (mps->tab == 1) {
232 mps->pos =
233 is_mode(mps, MPS_ESC) ?
234 mps->tabc[0] << 8 | mps->tabc[1] :
235 atoi((char *)mps->tabc) * 6;
236
237 del_mode(mps, MPS_ESC);
238 }
239
240 mps->tab--;
241 return;
242 }
243
244 if (is_mode(mps, MPS_ESC) && (c != 16)) {
245 del_mode(mps, MPS_ESC);
246 }
247
248 if (is_mode(mps, MPS_REPEAT)) {
249 mps->repeatn = c;
250 del_mode(mps, MPS_REPEAT);
251 return;
252 }
253
254 if (is_mode(mps, MPS_BITMODE) && (c & 128)) {
255 print_bitmask(mps, prnr, c);
256 return;
257 }
258
259 /* it seems that CR works even in quote mode */
260 switch (c) {
261 case 13: /* CR*/
262 mps->pos = 0;
263 if (is_mode(mps, MPS_BUSINESS)) {
264 del_mode(mps, MPS_CRSRUP);
265 } else {
266 set_mode(mps, MPS_CRSRUP);
267 }
268 /* CR resets Quote mode, revers mode, ... */
269 del_mode(mps, MPS_QUOTED);
270 del_mode(mps, MPS_REVERSE);
271 write_line(mps, prnr);
272 clear_buffer(mps);
273 return;
274 }
275
276 /* in text mode ignore most (?) other control chars when quote mode is active */
277 if (!is_mode(mps, MPS_QUOTED) || is_mode(mps, MPS_BITMODE)) {
278
279 switch (c) {
280 case 8:
281 set_mode(mps, MPS_BITMODE);
282 return;
283
284 case 10: /* LF*/
285 write_line(mps, prnr);
286 clear_buffer(mps);
287 return;
288
289 #ifdef notyet
290 /* Not really sure if the MPS803 recognizes this one... */
291 case 13 + 128: /* shift CR: CR without LF (from 4023 printer) */
292 mps->pos = 0;
293 if (is_mode(mps, MPS_BUSINESS)) {
294 del_mode(mps, MPS_CRSRUP);
295 } else {
296 set_mode(mps, MPS_CRSRUP);
297 }
298 /* CR resets Quote mode, revers mode, ... */
299 del_mode(mps, MPS_QUOTED);
300 del_mode(mps, MPS_REVERSE);
301 return;
302 #endif
303
304 case 14: /* EN on*/
305 set_mode(mps, MPS_DBLWDTH);
306 if (is_mode(mps, MPS_BITMODE)) {
307 bitmode_off(mps);
308 }
309 return;
310
311 case 15: /* EN off*/
312 del_mode(mps, MPS_DBLWDTH);
313 if (is_mode(mps, MPS_BITMODE)) {
314 bitmode_off(mps);
315 }
316 return;
317
318 case 16: /* POS*/
319 mps->tab = 2; /* 2 chars (digits) following, number of first char*/
320 return;
321
322 /*
323 * By sending the cursor up code [CHR$(145)] to your printer, following
324 * characters will be printed in cursor up (graphic) mode until either
325 * a carriage return or cursor down code [CHR$(17)] is detected.
326 *
327 * By sending the cursor down code [CHR$(17)] to your printer,
328 * following characters will be printed in business mode until either
329 * a carriage return or cursor up code [CHR$(145)] is detected.
330 */
331 case 17: /* crsr dn, enter businessmode local */
332 del_mode(mps, MPS_CRSRUP);
333 return;
334
335 case 145: /* CRSR up, enter gfxmode local */
336 set_mode(mps, MPS_CRSRUP);
337 return;
338
339 case 18:
340 set_mode(mps, MPS_REVERSE);
341 return;
342
343 case 146: /* 18+128*/
344 del_mode(mps, MPS_REVERSE);
345 return;
346
347 case 26: /* repeat last chr$(8) c times.*/
348 set_mode(mps, MPS_REPEAT);
349 mps->repeatn = 1;
350 return;
351
352 case 27:
353 set_mode(mps, MPS_ESC); /* followed by 16, and number MSB, LSB*/
354 return;
355 }
356
357 }
358
359 if (is_mode(mps, MPS_BITMODE)) {
360 return;
361 }
362
363 /*
364 * When an odd number of CHR$(34) is detected in a line, the control
365 * codes $00-$1F and $80-$9F will be made visible by printing a
366 * reverse character for each of these controls. This will continue
367 * until an even number of quotes [CHR$(34)] has been received or until
368 * end of this line.
369 */
370 if (c == 34) {
371 mps->mode ^= MPS_QUOTED;
372 }
373
374 if (mps->pos >= MAX_COL) { /* flush buffer*/
375 write_line(mps, prnr);
376 clear_buffer(mps);
377 }
378
379 if (is_mode(mps, MPS_QUOTED)) {
380 if (c <= 0x1f) {
381 set_mode(mps, MPS_REVERSE);
382 print_cbm_char(mps, (uint8_t)(c + 0x40));
383 del_mode(mps, MPS_REVERSE);
384 return;
385 }
386 if ((c >= 0x80) && (c <= 0x9f)) {
387 set_mode(mps, MPS_REVERSE);
388 print_cbm_char(mps, (uint8_t)(c - 0x20));
389 del_mode(mps, MPS_REVERSE);
390 return;
391 }
392 }
393
394 print_cbm_char(mps, c);
395 }
396
init_charset(uint8_t chrset[512][7],const char * name)397 static int init_charset(uint8_t chrset[512][7], const char *name)
398 {
399 uint8_t romimage[MPS803_ROM_SIZE];
400
401 if (sysfile_load(name, romimage, MPS803_ROM_SIZE, MPS803_ROM_SIZE) < 0) {
402 log_error(drv803_log, "Could not load MPS-803 charset '%s'.", name);
403 return -1;
404 }
405
406 memcpy(chrset, romimage, MPS803_ROM_SIZE);
407
408 return 0;
409 }
410
411 /* ------------------------------------------------------------------------- */
412 /* Interface to the upper layer. */
413
drv_mps803_open(unsigned int prnr,unsigned int secondary)414 static int drv_mps803_open(unsigned int prnr, unsigned int secondary)
415 {
416 /*
417 * sa = 0: graphic mode.. . (default)
418 * sa = 7: business mode
419 * This is *probably* incorrect: I suspect it happens anew for every
420 * OPEN CHANNEL SA (each PRINT# statement). Or maybe the state is
421 * even remembered for each SA separately.
422 */
423 if (secondary == 0) {
424 set_mode(&drv_mps803[prnr], MPS_CRSRUP);
425 } else if (secondary == 7) {
426 set_mode(&drv_mps803[prnr], MPS_BUSINESS);
427 } else if (secondary == DRIVER_FIRST_OPEN) {
428 /* Is this the first open? */
429 output_parameter_t output_parameter;
430
431 output_parameter.maxcol = MAX_COL;
432 output_parameter.maxrow = MAX_ROW;
433 output_parameter.dpi_x = 60; /* mps803 has different horizontal & vertical dpi - see pg 49 of the manual part H. */
434 output_parameter.dpi_y = 72; /* NOTE - mixed dpi might not be liked by some image viewers */
435 output_parameter.palette = palette;
436
437 return output_select_open(prnr, &output_parameter);
438 }
439
440 return 0;
441 }
442
drv_mps803_close(unsigned int prnr,unsigned int secondary)443 static void drv_mps803_close(unsigned int prnr, unsigned int secondary)
444 {
445 output_select_close(prnr);
446 }
447
448 /*
449 * We would like to have calls for LISTEN and UNLISTEN as well...
450 * this may be important for emulating the proper cursor up/down
451 * mode associated with SA=0 or 7.
452 */
453
drv_mps803_putc(unsigned int prnr,unsigned int secondary,uint8_t b)454 static int drv_mps803_putc(unsigned int prnr, unsigned int secondary, uint8_t b)
455 {
456 DBG(("drv_mps803_putc(%d,%d:$%02x)\n", prnr, secondary, b));
457 print_char(&drv_mps803[prnr], prnr, b);
458 return 0;
459 }
460
drv_mps803_getc(unsigned int prnr,unsigned int secondary,uint8_t * b)461 static int drv_mps803_getc(unsigned int prnr, unsigned int secondary, uint8_t *b)
462 {
463 DBG(("drv_mps803_getc(%d,%d)\n", prnr, secondary));
464 return output_select_getc(prnr, b);
465 }
466
drv_mps803_flush(unsigned int prnr,unsigned int secondary)467 static int drv_mps803_flush(unsigned int prnr, unsigned int secondary)
468 {
469 DBG(("drv_mps803_flush(%d,%d)\n", prnr, secondary));
470 return output_select_flush(prnr);
471 }
472
drv_mps803_formfeed(unsigned int prnr)473 static int drv_mps803_formfeed(unsigned int prnr)
474 {
475 DBG(("drv_mps803_formfeed(%d)\n", prnr));
476 return 0;
477 }
478
drv_mps803_init_resources(void)479 int drv_mps803_init_resources(void)
480 {
481 driver_select_t driver_select;
482
483 driver_select.drv_name = "mps803";
484 driver_select.drv_open = drv_mps803_open;
485 driver_select.drv_close = drv_mps803_close;
486 driver_select.drv_putc = drv_mps803_putc;
487 driver_select.drv_getc = drv_mps803_getc;
488 driver_select.drv_flush = drv_mps803_flush;
489 driver_select.drv_formfeed = drv_mps803_formfeed;
490
491 driver_select_register(&driver_select);
492
493 return 0;
494 }
495
drv_mps803_init(void)496 int drv_mps803_init(void)
497 {
498 const char *color_names[2] = {"Black", "White"};
499
500 drv803_log = log_open("MPS-803");
501
502 init_charset(charset, "mps803");
503
504 palette = palette_create(2, color_names);
505
506 if (palette == NULL) {
507 return -1;
508 }
509
510 if (palette_load("mps803" FSDEV_EXT_SEP_STR "vpl", palette) < 0) {
511 #ifndef __LIBRETRO__
512 log_error(drv803_log, "Cannot load palette file `%s'.",
513 "mps803" FSDEV_EXT_SEP_STR "vpl");
514 #endif
515 return -1;
516 }
517
518 return 0;
519 }
520
drv_mps803_shutdown(void)521 void drv_mps803_shutdown(void)
522 {
523 DBG(("drv_mps803_shutdown\n"));
524 palette_free(palette);
525 }
526