OXYGENHUB
◆ main openmsx / src / Printer.cc
43.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "Printer.hh"
2
3
#include "Paper.hh"
4
#include "MSXCharacterSets.hh"
5
6
#include "IntegerSetting.hh"
7
#include "MSXCliComm.hh"
8
#include "MSXException.hh"
9
#include "MSXMotherBoard.hh"
10
#include "serialize.hh"
11
12
#include "Math.hh"
13
#include "xrange.hh"
14
15
#include <algorithm>
16
#include <array>
17
#include <cassert>
18
#include <cmath>
19
#include <memory>
20
#include <ranges>
21
22
namespace openmsx {
23
24
bool PrinterCore::getStatus(EmuTime /*time*/)
25
{
26
	return false; // false = low = ready
27
}
28
29
void PrinterCore::setStrobe(bool strobe, EmuTime /*time*/)
30
{
31
	if (!strobe && prevStrobe) {
32
		// falling edge
33
		write(toPrint);
34
	}
35
	prevStrobe = strobe;
36
}
37
38
void PrinterCore::writeData(uint8_t data, EmuTime /*time*/)
39
{
40
	toPrint = data;
41
}
42
43
void PrinterCore::plugHelper(Connector& /*connector*/, EmuTime /*time*/)
44
{
45
	// nothing
46
}
47
48
void PrinterCore::unplugHelper(EmuTime /*time*/)
49
{
50
	forceFormFeed();
51
}
52
53
54
// class ImagePrinter
55
56
ImagePrinter::ImagePrinter(MSXMotherBoard& motherBoard_, bool graphicsHiLo_)
57
	: motherBoard(motherBoard_)
58
	, dpiSetting(motherBoard.getSharedStuff<IntegerSetting>(
59
		"print-resolution",
60
		motherBoard.getCommandController(), "print-resolution",
61
		"resolution of the output image of emulated dot matrix printer in DPI",
62
		300, 72, 1200))
63
	, graphicsHiLo(graphicsHiLo_)
64
{
65
}
66
67
ImagePrinter::~ImagePrinter()
68
{
69
	flushEmulatedPrinter();
70
}
71
72
void ImagePrinter::write(uint8_t data)
73
{
74
	if (ramLoadOffset < ramLoadEnd) {
75
		fontInfo.ram[ramLoadOffset++] = data;
76
	} else if (sizeRemainingDataBytes) {
77
		if (eightBit == 0) {
78
			data &= 0x80;
79
		} else if (eightBit == 1) {
80
			data |= 0x80;
81
		}
82
		printGraphicByte(data);
83
		sizeRemainingDataBytes--;
84
	} else if (escSequence) {
85
		escSequence = false;
86
87
		std::ranges::fill(abEscSeq, 0);
88
		abEscSeq[0] = data;
89
		sizeEscPos = 1;
90
91
		remainingCommandBytes = calcEscSequenceLength(data);
92
93
		if (!remainingCommandBytes) {
94
			processEscSequence();
95
		}
96
	} else if (remainingCommandBytes) {
97
		abEscSeq[sizeEscPos++] = data;
98
99
		if (!--remainingCommandBytes) {
100
			processEscSequence();
101
		}
102
	} else {
103
		processCharacter(data);
104
	}
105
}
106
107
void ImagePrinter::forceFormFeed()
108
{
109
	flushEmulatedPrinter();
110
}
111
112
void ImagePrinter::resetEmulatedPrinter()
113
{
114
	resetSettings();
115
116
	pageHeight = pageTop + lines * lineFeed;
117
	hpos       = leftBorder;
118
	vpos       = pageTop;
119
}
120
121
void ImagePrinter::plot9Dots(double x, double y, unsigned pattern)
122
{
123
	for (auto i : xrange(9)) {
124
		if (pattern & (1 << i)) {
125
			paper->plot(x, y + (8 - i) * pixelSizeY);
126
		}
127
	}
128
}
129
130
void ImagePrinter::printGraphicByte(uint8_t data)
131
{
132
	ensurePrintPage();
133
134
	double destY      = vpos * pixelSizeY;
135
	double destHeight = pixelSizeY * 9.0;
136
137
	// Print Data to high 8 bits
138
	unsigned charBits = (graphicsHiLo ? Math::reverseByte(data) : data) << 1;
139
140
	printAreaTop    = std::min(printAreaTop, destY);
141
	printAreaBottom = std::max(printAreaBottom, destY + destHeight);
142
143
	// Print bit-mask
144
	plot9Dots(hpos * pixelSizeX, destY, charBits);
145
146
	// Move print-position...
147
	seekPrinterHeadRelative(1.0 / graphDensity);
148
}
149
150
void ImagePrinter::seekPrinterHeadRelative(double offset)
151
{
152
	hpos += offset;
153
	if (unsigned(hpos) > rightBorder) {
154
		hpos = leftBorder;
155
		vpos += lineFeed;
156
		if (vpos >= pageHeight) {
157
			doubleWidth ^= normalAfterLine;
158
			flushEmulatedPrinter();
159
		}
160
	}
161
}
162
163
void ImagePrinter::ensurePrintPage()
164
{
165
	if (!paper) {
166
		// A4 paper format (210mm x 297mm) at 300dpi
167
		// TODO make this configurable
168
		int dpi = dpiSetting->getInt();
169
		auto paperSizeX = unsigned((210 / 25.4) * dpi);
170
		auto paperSizeY = unsigned((297 / 25.4) * dpi);
171
172
		auto [dotsX, dotsY] = getNumberOfDots();
173
		pixelSizeX = double(paperSizeX) / dotsX;
174
		pixelSizeY = double(paperSizeY) / dotsY;
175
176
		paper = std::make_unique<Paper>(paperSizeX, paperSizeY,
177
		                                pixelSizeX, pixelSizeY);
178
	}
179
}
180
181
void ImagePrinter::flushEmulatedPrinter()
182
{
183
	if (paper) {
184
		if (printAreaBottom > printAreaTop) {
185
			try {
186
				auto filename = paper->save();
187
				motherBoard.getMSXCliComm().printInfo(
188
					"Printed to ", filename);
189
			} catch (MSXException& e) {
190
				motherBoard.getMSXCliComm().printWarning(
191
					"Failed to print: ", e.getMessage());
192
			}
193
			printAreaTop = -1.0;
194
			printAreaBottom = 0.0;
195
		}
196
		paper.reset();
197
	}
198
	hpos = leftBorder;
199
	vpos = pageTop;
200
}
201
202
static constexpr unsigned compress9(unsigned a)
203
{
204
	unsigned result = 0;
205
	for (auto i : xrange(9)) {
206
		if (a & (1 << i)) {
207
			result |= 1 << (i / 2);
208
		}
209
	}
210
	return result;
211
}
212
void ImagePrinter::printVisibleCharacter(uint8_t data)
213
{
214
	ensurePrintPage();
215
216
	double iYPos = 0;
217
	std::span mem = fontInfo.useRam ? fontInfo.ram : fontInfo.rom;
218
	std::span charBitmap = subspan(mem, fontInfo.charWidth * data, fontInfo.charWidth);
219
	uint8_t attribute = charBitmap[0];
220
	unsigned start = (attribute >> 4) & 0x07;
221
	unsigned end = attribute & 0x0f;
222
	unsigned topBits = attribute >> 7;
223
	bool script = superscript || subscript;
224
225
	if (!proportional) {
226
		start = 0;  // Fixed width font
227
		end   = fontInfo.charWidth - 1;
228
	}
229
	if (subscript) {
230
		iYPos /= 2.0;
231
		iYPos += pixelSizeY * 4.5;
232
	}
233
	if (script) {
234
		iYPos -= pixelSizeY * 4.5;
235
	}
236
237
	double hPos = hpos;
238
	double headRelative = (doubleWidth ? 2 : 1) * fontInfo.pixelDelta / fontDensity;
239
240
	double destY = vpos * pixelSizeY + iYPos;
241
	double destHeight = pixelSizeY * 9.0;
242
	double dblStrikeOffset = doubleStrike ? (pixelSizeY * (1.0 / 2.5)) : 0.0;
243
	printAreaTop    = std::min(printAreaTop, destY);
244
	printAreaBottom = std::max(printAreaBottom, destY + destHeight + dblStrikeOffset);
245
246
	for (auto i : xrange(start, end)) {
247
		unsigned charBits = unsigned(charBitmap[i + 1]) << topBits;
248
249
		if (underline) {
250
			charBits |= 2;
251
		}
252
		if (script) {
253
			charBits = compress9(charBits);
254
		}
255
256
		for (auto d : xrange(doubleWidth ? 2 : 1)) {
257
			for (auto b : xrange(bold ? 2 : 1)) {
258
				for (auto y : xrange(doubleStrike ? 2: 1)) {
259
					double destX = (hPos + (d + b * 0.5) / fontDensity) * pixelSizeX;
260
					plot9Dots(destX, destY + y * dblStrikeOffset, charBits);
261
				}
262
			}
263
		}
264
		hPos += headRelative;
265
	}
266
	seekPrinterHeadRelative((1 + end - start) * headRelative);
267
}
268
269
270
// class ImagePrinterMSX
271
272
ImagePrinterMSX::ImagePrinterMSX(MSXMotherBoard& motherBoard_)
273
	: ImagePrinter(motherBoard_, true)
274
{
275
	resetEmulatedPrinter();
276
}
277
278
zstring_view ImagePrinterMSX::getName() const
279
{
280
	return "msx-printer";
281
}
282
283
zstring_view ImagePrinterMSX::getDescription() const
284
{
285
	// TODO which printer type
286
	return "Emulate MSX printer, prints to image.";
287
}
288
289
std::pair<unsigned, unsigned> ImagePrinterMSX::getNumberOfDots()
290
{
291
	return {825, 825};
292
}
293
294
void ImagePrinterMSX::resetSettings()
295
{
296
	lineFeed     = 12.0;
297
	leftBorder   = 48;
298
	rightBorder  = 800;
299
	graphDensity = 1.0;
300
	fontDensity  = 1.0;
301
	pageTop      = 48;
302
	lines        = 72;
303
	fontWidth    = 8;
304
	eightBit     = -1;
305
306
	// note: this only overwrites 9/12 of the fontInfo.rom array.
307
	copy_to_range(getMSXFontRotated(), fontInfo.rom);
308
	fontInfo.charWidth = 9;
309
	fontInfo.pixelDelta = 1.0;
310
	fontInfo.useRam = false;
311
}
312
313
314
unsigned ImagePrinterMSX::calcEscSequenceLength(uint8_t character)
315
{
316
	switch (character) {
317
		case 'C':
318
			return 1;
319
		case 'T': case 'Z':
320
			return 2;
321
		case 'O': case '\\': case 'L': case '/':
322
			return 3;
323
		case 'S':
324
			return 4;
325
		case 'G':
326
			return 7;
327
		default:
328
			return 0;
329
	}
330
}
331
332
unsigned ImagePrinterMSX::parseNumber(unsigned sizeStart, unsigned sizeChars)
333
{
334
	unsigned Value = 0;
335
	while (sizeChars--) {
336
		Value = Value * 10;
337
		uint8_t data = abEscSeq[sizeStart++];
338
		if (data >= '0' && data <= '9') {
339
			Value += unsigned(data - '0');
340
		}
341
	}
342
	return Value;
343
}
344
345
void ImagePrinterMSX::processEscSequence()
346
{
347
	switch (abEscSeq[0]) {
348
		case 'N':
349
			proportional = false;
350
			fontDensity = 1.0;
351
			break;
352
		case 'E':
353
			proportional = false;
354
			fontDensity = 1.40;
355
			break;
356
		case 'Q':
357
			proportional = false;
358
			fontDensity = 1.72;
359
			break;
360
		case 'P':
361
			proportional = true;
362
			fontDensity = 0.90;
363
			break;
364
		case '!':
365
			letterQuality = true;
366
			break;
367
		case '\"':
368
			letterQuality = false;
369
			break;
370
		case 'C':
371
			switch (abEscSeq[1]) {
372
				case 'D':
373
					doubleStrike = true;
374
					break;
375
				case 'd':
376
					doubleStrike = false;
377
					break;
378
				case 'I':
379
					italic = true;
380
					break;
381
				case 'i':
382
					italic = false;
383
					break;
384
				case 'B':
385
					bold = true;
386
					break;
387
				case 'b':
388
					bold = false;
389
					break;
390
				case 'S':
391
					superscript = true;
392
					break;
393
				case 's':
394
					superscript = false;
395
					break;
396
				case 'U':
397
					subscript = true;
398
					break;
399
				case 'u':
400
					subscript = false;
401
					break;
402
			}
403
			break;
404
		case '(': // ???: Set a horizontal tab position
405
			break;
406
		case ')': // ???: Partially delete a horizontal tab position
407
			break;
408
		case '2': // ???: Clear horizontal tabs
409
			break;
410
		case 'O':
411
			switch (abEscSeq[1]) {
412
				case 'S':
413
					perforationSkip = parseNumber(2, 2);
414
					break;
415
				case 'I': // ???: Set page-height(inches)
416
					break;
417
				default: // ???: Set page-height (lines)
418
					break;
419
			}
420
			break;
421
		case '/': // Right margin
422
			break;
423
		case 'L':
424
			leftBorder = parseNumber(1, 3);
425
			break;
426
		case 'A': // ???: Line-feed 1/6"
427
			lineFeed = 12.0;
428
			break;
429
		case 'B': // ???: Line-feed 1/9"
430
			lineFeed = 8.0;
431
			break;
432
		case 'T': // ???: Line-feed nn/144"
433
			lineFeed = parseNumber(1, 2) * (1.0 / 2.0);
434
			break;
435
		case 'Z': // ???: Line-feed nn/216"
436
			lineFeed = parseNumber(1, 2) * (1.0 / 3.0);
437
			break;
438
		case '[': // ???: Uni-directional printing
439
			break;
440
		case ']': // ???: Bi-directional printing
441
			break;
442
		case 'p':
443
			detectPaperOut = true;
444
			break;
445
		case 'q':
446
			detectPaperOut = false;
447
			break;
448
		case 13: // (ESC, CR) Move printer-head to end-position
449
			break;
450
		case '@':
451
			resetEmulatedPrinter();
452
			break;
453
		case '\\':
454
			rightBorder = parseNumber(1, 3);
455
			break;
456
		case 'G':
457
			graphDensity = std::max(0.1, parseNumber(1, 3) * (1.0 / 100.0));
458
			sizeRemainingDataBytes = parseNumber(4, 4);
459
			break;
460
		case 'S': // Print graphics, density depending on font
461
			sizeRemainingDataBytes = parseNumber(1, 4);
462
			break;
463
		case 'X':
464
			underline = true;
465
			break;
466
		case 'Y':
467
			underline = false;
468
			break;
469
		case '&':
470
		case '$':
471
			japanese = !japanese;
472
			break;
473
		case 'f': // ???: Scroll paper forward
474
			break;
475
		case 'r': // ???: Scroll paper back
476
			break;
477
	}
478
}
479
480
void ImagePrinterMSX::processCharacter(uint8_t data)
481
{
482
	if (alternateChar) {
483
		// Print SOH-preceded character
484
		printVisibleCharacter(data & 0x1F);
485
		alternateChar = false;
486
	} else {
487
		switch (data) {
488
			case 1: // SOH: A symbol code preceding code
489
				alternateChar = true;
490
				break;
491
			case 7: // BEL: Audible beep (buzzer, 0.3s)
492
				break;
493
			case 8: // BS: Backstep (1 Character)
494
				// TODO: fix for other font-sizes
495
				hpos = std::max(hpos - 8, double(leftBorder));
496
				break;
497
			case 9: // HAT: Horizontal tabulator
498
				// TODO: fix for other font-sizes
499
				hpos = ((unsigned(hpos) + 64 - leftBorder) & ~63)
500
				     + leftBorder;
501
				if (hpos < rightBorder) {
502
					break;
503
				}
504
				hpos = leftBorder;
505
				[[fallthrough]];
506
			case 10: // LF: Carriage return + Line feed
507
			case 11: // VT: Vertical tabulator (like LF)
508
				//hpos = leftBorder;
509
				vpos += lineFeed;
510
				if (vpos >= pageHeight) {
511
					flushEmulatedPrinter();
512
				}
513
				break;
514
			case 12: // FF: Form feed
515
				ensurePrintPage();
516
				flushEmulatedPrinter();
517
				break;
518
			case 13: // CR: Carriage return
519
				hpos = leftBorder;
520
				break;
521
			case 14: // SO: Double character-width on
522
				doubleWidth = true;
523
				break;
524
			case 15: // SI: Double character-width off
525
				doubleWidth = false;
526
				break;
527
			case 27:
528
				escSequence = true;
529
				break;
530
			default:
531
				if (data >= 32) {
532
					// Yes, we can print it!
533
					printVisibleCharacter(data);
534
				}
535
				break;
536
		}
537
	}
538
}
539
540
template<typename Archive>
541
void ImagePrinterMSX::serialize(Archive& /*ar*/, unsigned /*version*/)
542
{
543
	// TODO is this worth it?
544
}
545
INSTANTIATE_SERIALIZE_METHODS(ImagePrinterMSX);
546
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, ImagePrinterMSX, "ImagePrinterMSX");
547
548
549
// class ImagePrinterEpson
550
551
static constexpr std::array<uint8_t, 12 * 256> EpsonFontRom = {
552
	0x8b, 0x04, 0x0a, 0x20, 0x8a, 0x60, 0x0a, 0x20, 0x1c, 0x02, 0x00, 0x00, //   0
553
	0x8b, 0x1c, 0x22, 0x08, 0xa2, 0x48, 0x22, 0x08, 0x22, 0x18, 0x00, 0x00, //   1
554
	0x9b, 0x00, 0x3c, 0x00, 0x82, 0x40, 0x02, 0x00, 0x3c, 0x02, 0x00, 0x00, //   2
555
	0x9a, 0x00, 0x1c, 0x22, 0x80, 0x62, 0x00, 0x22, 0x1c, 0x00, 0x00, 0x00, //   3
556
	0x96, 0x00, 0x12, 0x80, 0x5e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, //   4
557
	0xa7, 0x00, 0x00, 0x40, 0xa0, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x00, 0x00, //   5
558
	0x8b, 0x12, 0x00, 0x7e, 0x80, 0x12, 0x80, 0x02, 0x80, 0x42, 0x00, 0x00, //   6
559
	0xc8, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //   7
560
	0x8b, 0x06, 0x00, 0x09, 0x00, 0x51, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, //   8
561
	0x8b, 0x5e, 0x80, 0x10, 0x80, 0x08, 0x40, 0x04, 0x40, 0x9e, 0x00, 0x00, //   9
562
	0x8a, 0x40, 0x9e, 0x00, 0x90, 0x40, 0x10, 0x4e, 0x80, 0x00, 0x00, 0x00, //  10
563
	0x8b, 0x92, 0x28, 0x44, 0x00, 0x44, 0x00, 0x44, 0x28, 0x92, 0x00, 0x00, //  11
564
	0x8b, 0xfe, 0x00, 0xa0, 0x00, 0x48, 0x00, 0x1e, 0x00, 0x0a, 0x00, 0x00, //  12
565
	0x8b, 0x06, 0x08, 0x54, 0xa0, 0x04, 0xa0, 0x54, 0x08, 0x06, 0x00, 0x00, //  13
566
	0x8b, 0x04, 0x0a, 0x20, 0x0a, 0xa0, 0x0a, 0x20, 0x1c, 0x02, 0x00, 0x00, //  14
567
	0x0a, 0x38, 0x44, 0x01, 0x44, 0x01, 0x46, 0x00, 0x44, 0x00, 0x00, 0x00, //  15
568
	0x9a, 0x00, 0x50, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x14, 0x00, 0x00, 0x00, //  16
569
	0x8a, 0x7e, 0x80, 0x00, 0x80, 0x12, 0x80, 0x12, 0x6c, 0x00, 0x00, 0x00, //  17
570
	0x8b, 0x3e, 0x40, 0x90, 0x00, 0xfe, 0x00, 0x92, 0x00, 0x92, 0x00, 0x00, //  18
571
	0x8b, 0x2c, 0x02, 0x28, 0x02, 0x1c, 0x20, 0x0a, 0x20, 0x1a, 0x00, 0x00, //  19
572
	0x8b, 0x3a, 0x44, 0x00, 0x8a, 0x10, 0xa2, 0x00, 0x44, 0xb8, 0x00, 0x00, //  20
573
	0x8b, 0x02, 0x08, 0x14, 0x22, 0x08, 0x22, 0x14, 0x08, 0x20, 0x00, 0x00, //  21
574
	0xa9, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, //  22
575
	0x8b, 0x06, 0x88, 0x14, 0x20, 0x44, 0x20, 0x14, 0x88, 0x06, 0x00, 0x00, //  23
576
	0x8b, 0x1c, 0xa2, 0x00, 0x22, 0x00, 0x22, 0x00, 0xa2, 0x1c, 0x00, 0x00, //  24
577
	0x8b, 0x3c, 0x82, 0x00, 0x02, 0x00, 0x02, 0x00, 0x82, 0x3c, 0x00, 0x00, //  25
578
	0x8b, 0x04, 0x0a, 0xa0, 0x0a, 0x20, 0x0a, 0xa0, 0x1c, 0x02, 0x00, 0x00, //  26
579
	0x9a, 0x00, 0x1c, 0xa2, 0x00, 0x22, 0x00, 0xa2, 0x1c, 0x00, 0x00, 0x00, //  27
580
	0x8a, 0x3c, 0x80, 0x02, 0x00, 0x02, 0x80, 0x3c, 0x02, 0x00, 0x00, 0x00, //  28
581
	0x8b, 0x3e, 0x00, 0x2a, 0x00, 0x6a, 0x80, 0x2a, 0x00, 0x22, 0x00, 0x00, //  29
582
	0x8b, 0x1c, 0x22, 0x08, 0x22, 0x48, 0xa2, 0x08, 0x22, 0x18, 0x00, 0x00, //  30
583
	0x8b, 0xa8, 0x00, 0x68, 0x00, 0x3e, 0x00, 0x68, 0x00, 0xa8, 0x00, 0x00, //  31
584
	0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  32
585
	0xc8, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  33
586
	0xa9, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, //  34
587
	0x8b, 0x28, 0x00, 0xfe, 0x00, 0x28, 0x00, 0xfe, 0x00, 0x28, 0x00, 0x00, //  35
588
	0x8b, 0x24, 0x00, 0x54, 0x00, 0xfe, 0x00, 0x54, 0x00, 0x48, 0x00, 0x00, //  36
589
	0x8b, 0xc0, 0x02, 0xc4, 0x08, 0x10, 0x20, 0x46, 0x80, 0x06, 0x00, 0x00, //  37
590
	0x8b, 0x4c, 0xa0, 0x12, 0xa0, 0x4a, 0x00, 0x04, 0x08, 0x12, 0x00, 0x00, //  38
591
	0xc8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, //  39
592
	0xc9, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x82, 0x00, 0x00, 0x00, 0x00, //  40
593
	0xa7, 0x00, 0x00, 0x82, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  41
594
	0x8b, 0x10, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x10, 0x00, 0x00, //  42
595
	0x8b, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, //  43
596
	0x39, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, //  44
597
	0x8b, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, //  45
598
	0xa8, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  46
599
	0x9a, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, //  47
600
	0x8b, 0x38, 0x44, 0x00, 0x82, 0x00, 0x82, 0x00, 0x44, 0x38, 0x00, 0x00, //  48
601
	0xa9, 0x00, 0x00, 0x42, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, //  49
602
	0x8b, 0x42, 0x80, 0x06, 0x80, 0x0a, 0x80, 0x12, 0x80, 0x62, 0x00, 0x00, //  50
603
	0x8b, 0x84, 0x00, 0x82, 0x00, 0xa2, 0x00, 0xd2, 0x00, 0x8c, 0x00, 0x00, //  51
604
	0x8b, 0x08, 0x10, 0x28, 0x40, 0x88, 0x00, 0xfe, 0x00, 0x08, 0x00, 0x00, //  52
605
	0x8b, 0xe4, 0x02, 0xa0, 0x02, 0xa0, 0x02, 0xa0, 0x02, 0x9c, 0x00, 0x00, //  53
606
	0x8b, 0x0c, 0x12, 0x20, 0x52, 0x80, 0x12, 0x00, 0x12, 0x0c, 0x00, 0x00, //  54
607
	0x8b, 0x80, 0x00, 0x82, 0x04, 0x88, 0x10, 0xa0, 0x40, 0x80, 0x00, 0x00, //  55
608
	0x8b, 0x6c, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x6c, 0x00, 0x00, //  56
609
	0x8b, 0x60, 0x90, 0x00, 0x90, 0x02, 0x94, 0x08, 0x90, 0x60, 0x00, 0x00, //  57
610
	0xa7, 0x00, 0x00, 0x36, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  58
611
	0x27, 0x00, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  59
612
	0x89, 0x10, 0x00, 0x28, 0x00, 0x44, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, //  60
613
	0x8b, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x00, //  61
614
	0xab, 0x00, 0x00, 0x82, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00, //  62
615
	0x8b, 0x40, 0x80, 0x00, 0x80, 0x0a, 0x80, 0x10, 0x80, 0x60, 0x00, 0x00, //  63
616
	0x8b, 0x38, 0x44, 0x82, 0x10, 0xaa, 0x00, 0xaa, 0x00, 0x7a, 0x00, 0x00, //  64
617
	0x8b, 0x1e, 0x20, 0x48, 0x80, 0x08, 0x80, 0x48, 0x20, 0x1e, 0x00, 0x00, //  65
618
	0x8b, 0x82, 0x7c, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x6c, 0x00, 0x00, //  66
619
	0x8b, 0x7c, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x44, 0x00, 0x00, //  67
620
	0x8b, 0x82, 0x7c, 0x82, 0x00, 0x82, 0x00, 0x82, 0x44, 0x38, 0x00, 0x00, //  68
621
	0x8b, 0xfe, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x82, 0x00, 0x00, //  69
622
	0x8b, 0xfe, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x80, 0x00, 0x00, //  70
623
	0x8b, 0x7c, 0x82, 0x00, 0x82, 0x10, 0x82, 0x10, 0x82, 0x5c, 0x00, 0x00, //  71
624
	0x8b, 0xfe, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x00, //  72
625
	0xa9, 0x00, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, //  73
626
	0x8a, 0x0c, 0x02, 0x00, 0x82, 0x00, 0x82, 0x7c, 0x80, 0x00, 0x00, 0x00, //  74
627
	0x8b, 0xfe, 0x00, 0x10, 0x00, 0x28, 0x00, 0x44, 0x00, 0x82, 0x00, 0x00, //  75
628
	0x8b, 0xfe, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, //  76
629
	0x8b, 0xfe, 0x00, 0x40, 0x20, 0x10, 0x20, 0x40, 0x00, 0xfe, 0x00, 0x00, //  77
630
	0x8b, 0xfe, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0xfe, 0x00, 0x00, //  78
631
	0x8b, 0x7c, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x7c, 0x00, 0x00, //  79
632
	0x8b, 0xfe, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x60, 0x00, 0x00, //  80
633
	0x8b, 0x7c, 0x82, 0x00, 0x82, 0x08, 0x82, 0x04, 0x80, 0x7a, 0x00, 0x00, //  81
634
	0x8b, 0xfe, 0x00, 0x90, 0x00, 0x90, 0x00, 0x98, 0x04, 0x62, 0x00, 0x00, //  82
635
	0x8b, 0x64, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x4c, 0x00, 0x00, //  83
636
	0x8b, 0x80, 0x00, 0x80, 0x00, 0xfe, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, //  84
637
	0x8b, 0xfc, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xfc, 0x00, 0x00, //  85
638
	0x8b, 0xe0, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0xe0, 0x00, 0x00, //  86
639
	0x8b, 0xfc, 0x02, 0x04, 0x08, 0x30, 0x08, 0x04, 0x02, 0xfc, 0x00, 0x00, //  87
640
	0x9a, 0x00, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x00, 0x00, 0x00, //  88
641
	0x8b, 0x80, 0x40, 0x20, 0x10, 0x0e, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, //  89
642
	0x9a, 0x00, 0x82, 0x04, 0x8a, 0x10, 0xa2, 0x40, 0x82, 0x00, 0x00, 0x00, //  90
643
	0xa9, 0x00, 0x00, 0xfe, 0x00, 0x82, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, //  91
644
	0x9a, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, 0x00, //  92
645
	0xa9, 0x00, 0x00, 0x82, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, //  93
646
	0x8b, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00, //  94
647
	0x0b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, //  95
648
	0xb7, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //  96
649
	0x8b, 0x04, 0x0a, 0x20, 0x0a, 0x20, 0x0a, 0x20, 0x1c, 0x02, 0x00, 0x00, //  97
650
	0x8a, 0xfe, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x1c, 0x00, 0x00, 0x00, //  98
651
	0x8a, 0x1c, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x00, 0x00, //  99
652
	0x8a, 0x1c, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0xfe, 0x00, 0x00, 0x00, // 100
653
	0x8b, 0x1c, 0x22, 0x08, 0x22, 0x08, 0x22, 0x08, 0x22, 0x18, 0x00, 0x00, // 101
654
	0x89, 0x10, 0x00, 0x10, 0x7e, 0x90, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, // 102
655
	0x0a, 0x38, 0x44, 0x01, 0x44, 0x01, 0x44, 0x01, 0x7e, 0x00, 0x00, 0x00, // 103
656
	0x8a, 0xfe, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x1e, 0x00, 0x00, 0x00, // 104
657
	0x98, 0x00, 0x22, 0x00, 0xbe, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 105
658
	0x99, 0x00, 0x01, 0x00, 0x01, 0x20, 0x01, 0xbe, 0x00, 0x00, 0x00, 0x00, // 106
659
	0x9a, 0x00, 0xfe, 0x00, 0x08, 0x00, 0x14, 0x00, 0x22, 0x00, 0x00, 0x00, // 107
660
	0x98, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 108
661
	0x8b, 0x1e, 0x20, 0x00, 0x20, 0x1e, 0x20, 0x00, 0x20, 0x1e, 0x00, 0x00, // 109
662
	0x8a, 0x3e, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x1e, 0x00, 0x00, 0x00, // 110
663
	0x8b, 0x1c, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x1c, 0x00, 0x00, // 111
664
	0x0a, 0x7f, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x38, 0x00, 0x00, 0x00, // 112
665
	0x1b, 0x00, 0x38, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7e, 0x00, 0x00, // 113
666
	0x8a, 0x3e, 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, // 114
667
	0x8b, 0x10, 0x2a, 0x00, 0x2a, 0x00, 0x2a, 0x00, 0x2a, 0x04, 0x00, 0x00, // 115
668
	0x8a, 0x20, 0x00, 0x7c, 0x02, 0x20, 0x02, 0x20, 0x02, 0x00, 0x00, 0x00, // 116
669
	0x8b, 0x3c, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x3c, 0x02, 0x00, 0x00, // 117
670
	0x8b, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 118
671
	0x8b, 0x3c, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3c, 0x00, 0x00, // 119
672
	0x89, 0x22, 0x14, 0x00, 0x08, 0x00, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, // 120
673
	0x0b, 0x40, 0x20, 0x11, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, // 121
674
	0x89, 0x22, 0x04, 0x22, 0x08, 0x22, 0x10, 0x22, 0x00, 0x00, 0x00, 0x00, // 122
675
	0xaa, 0x00, 0x00, 0x10, 0x00, 0x6c, 0x82, 0x00, 0x82, 0x00, 0x00, 0x00, // 123
676
	0xc7, 0x00, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 124
677
	0xaa, 0x00, 0x82, 0x00, 0x82, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // 125
678
	0x8b, 0x40, 0x80, 0x00, 0x80, 0x40, 0x20, 0x00, 0x20, 0x40, 0x00, 0x00, // 126
679
	0x8b, 0x7c, 0x82, 0x04, 0x8a, 0x10, 0xa2, 0x40, 0x82, 0x7c, 0x00, 0x00, // 127
680
	0x8a, 0x04, 0x0a, 0x80, 0x2a, 0x60, 0x0a, 0x24, 0x1a, 0x00, 0x00, 0x00, // 128
681
	0x8a, 0x0c, 0x12, 0x28, 0x82, 0x68, 0x02, 0x28, 0x10, 0x00, 0x00, 0x00, // 129
682
	0x8a, 0x0c, 0x32, 0x00, 0x82, 0x40, 0x02, 0x0c, 0x32, 0x00, 0x00, 0x00, // 130
683
	0x8a, 0x0c, 0x12, 0x00, 0xa0, 0x42, 0x00, 0x24, 0x18, 0x00, 0x00, 0x00, // 131
684
	0x98, 0x00, 0x02, 0x00, 0x16, 0x88, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, // 132
685
	0xa9, 0x00, 0x00, 0x40, 0xa0, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x00, 0x00, // 133
686
	0x8b, 0x12, 0x00, 0x1e, 0x60, 0x12, 0x80, 0x12, 0x80, 0x40, 0x00, 0x00, // 134
687
	0x9a, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, 0x80, 0x00, 0x00, 0x00, // 135
688
	0x8a, 0x06, 0x01, 0x08, 0x01, 0x10, 0x21, 0x80, 0x02, 0x00, 0x00, 0x00, // 136
689
	0x8b, 0x06, 0x58, 0x80, 0x08, 0x84, 0x40, 0x06, 0x58, 0x80, 0x00, 0x00, // 137
690
	0x8b, 0x12, 0x4c, 0x80, 0x10, 0x80, 0x50, 0x02, 0x4c, 0x80, 0x00, 0x00, // 138
691
	0x8b, 0x02, 0x18, 0x24, 0x80, 0x44, 0x02, 0x48, 0x30, 0x80, 0x00, 0x00, // 139
692
	0x8b, 0x06, 0x38, 0xc0, 0x20, 0x88, 0x26, 0xd8, 0x02, 0x08, 0x00, 0x00, // 140
693
	0x8b, 0x02, 0x04, 0x08, 0x14, 0x40, 0xa4, 0x00, 0xbe, 0x40, 0x00, 0x00, // 141
694
	0x8a, 0x04, 0x0a, 0x20, 0x0a, 0x20, 0x8a, 0x24, 0x1a, 0x00, 0x00, 0x00, // 142
695
	0x1b, 0x00, 0x18, 0x21, 0x04, 0x41, 0x06, 0x40, 0x04, 0x40, 0x00, 0x00, // 143
696
	0x8b, 0x02, 0x10, 0x6a, 0x00, 0xaa, 0x00, 0xac, 0x10, 0x80, 0x00, 0x00, // 144
697
	0x8a, 0x06, 0x18, 0x60, 0x00, 0x82, 0x10, 0x82, 0x6c, 0x00, 0x00, 0x00, // 145
698
	0x8b, 0x0e, 0x30, 0x40, 0x90, 0x0e, 0x70, 0x82, 0x10, 0x82, 0x00, 0x00, // 146
699
	0x8b, 0x04, 0x22, 0x08, 0x22, 0x1c, 0x22, 0x08, 0x22, 0x10, 0x00, 0x00, // 147
700
	0x8b, 0x1a, 0x24, 0x42, 0x08, 0x92, 0x20, 0x84, 0x48, 0xb0, 0x00, 0x00, // 148
701
	0x8a, 0x0c, 0x11, 0x02, 0x2c, 0x12, 0x20, 0x44, 0x18, 0x00, 0x00, 0x00, // 149
702
	0xa9, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // 150
703
	0x8b, 0x02, 0x04, 0x08, 0x14, 0x80, 0x24, 0x00, 0x3e, 0x80, 0x00, 0x00, // 151
704
	0x8b, 0x0c, 0x12, 0x00, 0xa2, 0x00, 0x22, 0x00, 0xa4, 0x18, 0x00, 0x00, // 152
705
	0x8b, 0x0c, 0x32, 0x00, 0x82, 0x00, 0x02, 0x00, 0x8c, 0x30, 0x00, 0x00, // 153
706
	0x8a, 0x04, 0x0a, 0x20, 0x8a, 0x20, 0x0a, 0x24, 0x9a, 0x00, 0x00, 0x00, // 154
707
	0x8a, 0x0c, 0x12, 0x00, 0xa0, 0x02, 0x00, 0x24, 0x98, 0x00, 0x00, 0x00, // 155
708
	0x8b, 0x0c, 0x32, 0x80, 0x02, 0x00, 0x02, 0x0c, 0xb2, 0x00, 0x00, 0x00, // 156
709
	0x8b, 0x06, 0x18, 0x22, 0x08, 0x22, 0x48, 0x22, 0x80, 0x20, 0x00, 0x00, // 157
710
	0x8a, 0x0c, 0x12, 0x28, 0x02, 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, 0x00, // 158
711
	0x8b, 0x08, 0x20, 0x88, 0x66, 0x18, 0x20, 0x48, 0x20, 0x80, 0x00, 0x00, // 159
712
	0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 160
713
	0x9a, 0x00, 0x02, 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 161
714
	0x9a, 0x00, 0x20, 0x40, 0x80, 0x00, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 162
715
	0x8b, 0x28, 0x06, 0x38, 0xc0, 0x28, 0x06, 0x38, 0xc0, 0x28, 0x00, 0x00, // 163
716
	0x8a, 0x00, 0x24, 0x10, 0x46, 0x38, 0xc4, 0x10, 0x48, 0x00, 0x00, 0x00, // 164
717
	0x8b, 0x40, 0x82, 0x44, 0x88, 0x10, 0x22, 0x44, 0x82, 0x04, 0x00, 0x00, // 165
718
	0x8b, 0x0c, 0x10, 0x42, 0xa0, 0x12, 0xa8, 0x44, 0x0a, 0x10, 0x00, 0x00, // 166
719
	0xc8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // 167
720
	0xba, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, // 168
721
	0x98, 0x00, 0x02, 0x00, 0x04, 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, // 169
722
	0x8b, 0x10, 0x04, 0x50, 0x28, 0x10, 0x28, 0x14, 0x40, 0x10, 0x00, 0x00, // 170
723
	0x8b, 0x10, 0x00, 0x14, 0x08, 0x10, 0x20, 0x50, 0x00, 0x10, 0x00, 0x00, // 171
724
	0x29, 0x00, 0x00, 0x01, 0x04, 0x0a, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, // 172
725
	0x8b, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, // 173
726
	0xa8, 0x00, 0x00, 0x02, 0x04, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // 174
727
	0x9a, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 175
728
	0x8b, 0x1c, 0x20, 0x42, 0x00, 0x82, 0x00, 0x84, 0x08, 0x70, 0x00, 0x00, // 176
729
	0x99, 0x00, 0x02, 0x00, 0x46, 0x18, 0x62, 0x80, 0x00, 0x00, 0x00, 0x00, // 177
730
	0x8b, 0x02, 0x40, 0x06, 0x80, 0x0a, 0x80, 0x12, 0x80, 0x60, 0x00, 0x00, // 178
731
	0x8b, 0x04, 0x00, 0x82, 0x00, 0x92, 0x00, 0xb2, 0x4c, 0x80, 0x00, 0x00, // 179
732
	0x8b, 0x08, 0x10, 0x08, 0x20, 0x08, 0x46, 0x38, 0xc0, 0x08, 0x00, 0x00, // 180
733
	0x8b, 0x04, 0x60, 0x82, 0x20, 0x82, 0x20, 0x84, 0x18, 0x80, 0x00, 0x00, // 181
734
	0x8a, 0x0c, 0x10, 0x22, 0x10, 0x42, 0x10, 0x82, 0x0c, 0x00, 0x00, 0x00, // 182
735
	0x8b, 0x80, 0x02, 0x84, 0x08, 0x90, 0x20, 0x80, 0x40, 0x80, 0x00, 0x00, // 183
736
	0x8b, 0x0c, 0x62, 0x10, 0x82, 0x10, 0x82, 0x10, 0x8c, 0x60, 0x00, 0x00, // 184
737
	0x8a, 0x60, 0x02, 0x90, 0x04, 0x90, 0x08, 0x90, 0x60, 0x00, 0x00, 0x00, // 185
738
	0xa9, 0x00, 0x00, 0x02, 0x14, 0x22, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, // 186
739
	0x2a, 0x00, 0x00, 0x01, 0x04, 0x2a, 0x44, 0x28, 0x40, 0x00, 0x00, 0x00, // 187
740
	0x9a, 0x00, 0x10, 0x08, 0x24, 0x02, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, // 188
741
	0x8a, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x00, 0x00, 0x00, // 189
742
	0x9a, 0x00, 0x02, 0x00, 0x04, 0x80, 0x48, 0x20, 0x10, 0x00, 0x00, 0x00, // 190
743
	0x8a, 0x48, 0x02, 0x80, 0x08, 0x80, 0x10, 0x80, 0x60, 0x00, 0x00, 0x00, // 191
744
	0x8b, 0x1c, 0x20, 0x42, 0x80, 0x12, 0x88, 0x22, 0x88, 0x70, 0x00, 0x00, // 192
745
	0x8b, 0x02, 0x04, 0x08, 0x10, 0x28, 0x40, 0x88, 0x00, 0xfe, 0x00, 0x00, // 193
746
	0x8b, 0x06, 0x98, 0x62, 0x80, 0x12, 0x80, 0x12, 0x8c, 0x60, 0x00, 0x00, // 194
747
	0x8b, 0x1c, 0x22, 0x40, 0x82, 0x00, 0x82, 0x00, 0x84, 0x40, 0x00, 0x00, // 195
748
	0x8b, 0x06, 0x98, 0x62, 0x80, 0x02, 0x80, 0x04, 0x88, 0x70, 0x00, 0x00, // 196
749
	0x8b, 0x06, 0x38, 0xc2, 0x10, 0x82, 0x10, 0x82, 0x00, 0x80, 0x00, 0x00, // 197
750
	0x8b, 0x06, 0x38, 0xc0, 0x10, 0x80, 0x10, 0x80, 0x00, 0x80, 0x00, 0x00, // 198
751
	0x8b, 0x1c, 0x22, 0x40, 0x82, 0x00, 0x92, 0x04, 0x98, 0x40, 0x00, 0x00, // 199
752
	0x8b, 0x06, 0x38, 0xc0, 0x10, 0x00, 0x10, 0x06, 0x38, 0xc0, 0x00, 0x00, // 200
753
	0x92, 0x00, 0x02, 0x00, 0x86, 0x18, 0xe2, 0x00, 0x80, 0x00, 0x00, 0x00, // 201
754
	0x8b, 0x0c, 0x02, 0x00, 0x02, 0x80, 0x04, 0x98, 0x60, 0x80, 0x00, 0x00, // 202
755
	0x8b, 0x06, 0x38, 0xc0, 0x10, 0x20, 0x08, 0x44, 0x02, 0x80, 0x00, 0x00, // 203
756
	0x9a, 0x00, 0x06, 0x18, 0x62, 0x80, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, // 204
757
	0x8b, 0x06, 0x38, 0xc0, 0x00, 0x30, 0x00, 0x46, 0x38, 0xc0, 0x00, 0x00, // 205
758
	0x8b, 0x06, 0x38, 0xc0, 0x20, 0x10, 0x08, 0x06, 0x38, 0xc0, 0x00, 0x00, // 206
759
	0x8b, 0x0c, 0x32, 0x40, 0x82, 0x00, 0x82, 0x04, 0x98, 0x60, 0x00, 0x00, // 207
760
	0x8b, 0x06, 0x18, 0x60, 0x90, 0x00, 0x90, 0x00, 0x90, 0x60, 0x00, 0x00, // 208
761
	0x8b, 0x1c, 0x20, 0x42, 0x00, 0x8a, 0x00, 0x84, 0x0a, 0x70, 0x00, 0x00, // 209
762
	0x8b, 0x06, 0x18, 0x60, 0x80, 0x10, 0x88, 0x14, 0x82, 0x60, 0x00, 0x00, // 210
763
	0x8b, 0x04, 0x62, 0x00, 0x92, 0x00, 0x92, 0x00, 0x8c, 0x40, 0x00, 0x00, // 211
764
	0x8b, 0x80, 0x00, 0x86, 0x18, 0xe0, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, // 212
765
	0x8b, 0x0c, 0x32, 0xc0, 0x02, 0x00, 0x02, 0x0c, 0x30, 0xc0, 0x00, 0x00, // 213
766
	0x9b, 0x00, 0xfe, 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, // 214
767
	0x8b, 0x06, 0x38, 0xc4, 0x08, 0x10, 0x08, 0x06, 0x38, 0xc0, 0x00, 0x00, // 215
768
	0x8b, 0x02, 0x84, 0x48, 0x20, 0x18, 0x24, 0x02, 0x40, 0x80, 0x00, 0x00, // 216
769
	0x8b, 0x80, 0x40, 0x26, 0x18, 0x00, 0x20, 0x00, 0x40, 0x80, 0x00, 0x00, // 217
770
	0x8b, 0x02, 0x04, 0x8a, 0x00, 0x92, 0x00, 0xa2, 0x40, 0x80, 0x00, 0x00, // 218
771
	0x9b, 0x00, 0x06, 0x18, 0x62, 0x80, 0x02, 0x80, 0x00, 0x80, 0x00, 0x00, // 219
772
	0xa8, 0x00, 0x00, 0xc0, 0x30, 0x0c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 220
773
	0x8a, 0x02, 0x00, 0x02, 0x80, 0x06, 0x98, 0x60, 0x80, 0x00, 0x00, 0x00, // 221
774
	0x9a, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x40, 0x20, 0x00, 0x00, 0x00, // 222
775
	0x0b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, // 223
776
	0xb7, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 224
777
	0x8a, 0x04, 0x0a, 0x20, 0x0a, 0x20, 0x0a, 0x24, 0x1a, 0x00, 0x00, 0x00, // 225
778
	0x8a, 0x06, 0x18, 0xe2, 0x00, 0x22, 0x00, 0x24, 0x18, 0x00, 0x00, 0x00, // 226
779
	0x8a, 0x0c, 0x10, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x00, 0x00, 0x00, // 227
780
	0x8b, 0x0c, 0x10, 0x02, 0x20, 0x02, 0x20, 0x06, 0x38, 0xc0, 0x00, 0x00, // 228
781
	0x8a, 0x0c, 0x12, 0x28, 0x02, 0x28, 0x02, 0x28, 0x10, 0x00, 0x00, 0x00, // 229
782
	0x8b, 0x20, 0x00, 0x26, 0x18, 0x60, 0x00, 0xa0, 0x00, 0x80, 0x00, 0x00, // 230
783
	0x1b, 0x00, 0x18, 0x25, 0x00, 0x45, 0x00, 0x46, 0x18, 0x60, 0x00, 0x00, // 231
784
	0x8a, 0x06, 0x18, 0xe0, 0x00, 0x20, 0x00, 0x26, 0x18, 0x00, 0x00, 0x00, // 232
785
	0x99, 0x00, 0x02, 0x00, 0x26, 0x18, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, // 233
786
	0x89, 0x01, 0x00, 0x01, 0x00, 0x26, 0x18, 0xa0, 0x00, 0x00, 0x00, 0x00, // 234
787
	0x8a, 0x06, 0x18, 0x60, 0x88, 0x04, 0x12, 0x00, 0x20, 0x00, 0x00, 0x00, // 235
788
	0x99, 0x00, 0x02, 0x00, 0x06, 0x98, 0x62, 0x80, 0x00, 0x00, 0x00, 0x00, // 236
789
	0x8a, 0x26, 0x18, 0x20, 0x06, 0x38, 0x00, 0x26, 0x18, 0x00, 0x00, 0x00, // 237
790
	0x89, 0x26, 0x18, 0x20, 0x00, 0x20, 0x06, 0x18, 0x00, 0x00, 0x00, 0x00, // 238
791
	0x8a, 0x0c, 0x12, 0x00, 0x20, 0x02, 0x00, 0x24, 0x18, 0x00, 0x00, 0x00, // 239
792
	0x0a, 0x03, 0x1c, 0x60, 0x04, 0x40, 0x04, 0x48, 0x30, 0x00, 0x00, 0x00, // 240
793
	0x1b, 0x00, 0x18, 0x24, 0x00, 0x44, 0x00, 0x47, 0x18, 0x60, 0x00, 0x00, // 241
794
	0x89, 0x06, 0x38, 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, // 242
795
	0x8a, 0x02, 0x10, 0x02, 0x28, 0x02, 0x28, 0x04, 0x20, 0x00, 0x00, 0x00, // 243
796
	0x9a, 0x00, 0x20, 0x0c, 0x32, 0xc0, 0x22, 0x00, 0x20, 0x00, 0x00, 0x00, // 244
797
	0x8a, 0x0c, 0x32, 0x00, 0x02, 0x00, 0x02, 0x0c, 0x32, 0x00, 0x00, 0x00, // 245
798
	0x9a, 0x00, 0x3e, 0x00, 0x04, 0x00, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, // 246
799
	0x8b, 0x0e, 0x30, 0x04, 0x00, 0x18, 0x04, 0x00, 0x06, 0x38, 0x00, 0x00, // 247
800
	0x8b, 0x02, 0x00, 0x24, 0x10, 0x08, 0x04, 0x12, 0x00, 0x20, 0x00, 0x00, // 248
801
	0x1b, 0x00, 0x40, 0x21, 0x12, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, // 249
802
	0x8b, 0x02, 0x00, 0x26, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x20, 0x00, 0x00, // 250
803
	0x9a, 0x00, 0x10, 0x04, 0x1a, 0x60, 0x82, 0x00, 0x80, 0x00, 0x00, 0x00, // 251
804
	0x99, 0x00, 0x02, 0x04, 0x08, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, // 252
805
	0x9a, 0x00, 0x02, 0x00, 0x82, 0x0c, 0xb0, 0x40, 0x10, 0x00, 0x00, 0x00, // 253
806
	0x8b, 0x40, 0x80, 0x00, 0x80, 0x40, 0x20, 0x00, 0x20, 0x40, 0x00, 0x00, // 254
807
	0x8b, 0x1a, 0x24, 0x42, 0x08, 0x92, 0x20, 0x84, 0x48, 0xb0, 0x00, 0x00  // 255
808
};
809
810
ImagePrinterEpson::ImagePrinterEpson(MSXMotherBoard& motherBoard_)
811
	: ImagePrinter(motherBoard_, false)
812
{
813
	resetEmulatedPrinter();
814
}
815
816
zstring_view ImagePrinterEpson::getName() const
817
{
818
	return "epson-printer";
819
}
820
821
zstring_view ImagePrinterEpson::getDescription() const
822
{
823
	return "Emulate Epson FX80 printer, prints to image.";
824
}
825
826
std::pair<unsigned, unsigned> ImagePrinterEpson::getNumberOfDots()
827
{
828
	return {610, 825};
829
}
830
831
void ImagePrinterEpson::resetSettings()
832
{
833
	lineFeed     = 12.0;
834
	leftBorder   = 48;
835
	rightBorder  = leftBorder + 480;
836
	graphDensity = 1.0;
837
	fontDensity  = 1.0;
838
	pageTop      = 48;
839
	lines        = 72;
840
	fontWidth    = 6;
841
	eightBit     = -1;
842
843
	copy_to_range(EpsonFontRom, fontInfo.rom);
844
	fontInfo.charWidth = 12;
845
	fontInfo.pixelDelta = 0.5;
846
	fontInfo.useRam = false;
847
}
848
849
unsigned ImagePrinterEpson::calcEscSequenceLength(uint8_t character)
850
{
851
	switch (character & 127) {
852
		case '!': case '-': case '/': case '3': case 'A': case 'J':
853
		case 'N': case 'Q': case 'R': case 'S': case 'U': case 'W':
854
		case 'b': case 'i': case 'j': case 'l': case 'p': case 's':
855
			return 1;
856
		case '%': case '?': case 'K': case 'L': case 'Z': case '^':
857
			return 2;
858
		case '*': case ':': case '&':
859
			return 3;
860
		case 'B': // Set tabs, variable length (up to 16 tabs)
861
			return 0;
862
		case 'C': // Set form length, variable length (2 or 3)
863
			return 0;
864
		case 'D': // Set tabs, variable length (up to 32 tabs)
865
			return 0;
866
		default:
867
			return 0;
868
	}
869
}
870
871
unsigned ImagePrinterEpson::parseNumber(unsigned sizeStart, unsigned sizeChars)
872
{
873
	unsigned Value = 0;
874
	sizeStart += sizeChars;
875
	while (sizeChars--) {
876
		Value = Value * 256 + abEscSeq[--sizeStart];
877
	}
878
	return Value;
879
}
880
881
void ImagePrinterEpson::processEscSequence()
882
{
883
	uint8_t character = abEscSeq[0] & 127;
884
885
	switch (character) {
886
		case '!': { // Master Print Mode Select
887
			unsigned masterSelect  = parseNumber(1, 1);
888
			elite        = (masterSelect & 1) != 0;
889
			compressed   = (masterSelect & 4) != 0;
890
			bold         = (masterSelect & 8) != 0;
891
			doubleStrike = (masterSelect & 16) != 0;
892
			doubleWidth  = (masterSelect & 32) != 0;
893
894
			if (elite) {
895
				fontDensity = 1.20;
896
			} else if (compressed) {
897
				fontDensity = 1.72;
898
			} else {
899
				fontDensity = 1.00;
900
			}
901
			break;
902
		}
903
		case '#': // Accept Eight Bit as-is
904
			break;
905
		case '%': // Activates Character Set
906
			fontInfo.useRam = parseNumber(1, 1) & 1;
907
			break;
908
		case '&': // Custom character set, variable length
909
			ramLoadOffset =       12 * parseNumber(2, 1);
910
			ramLoadEnd = std::max(12 * parseNumber(3, 1) + 12, ramLoadOffset);
911
			break;
912
		case '*': // Turn Graphics Mode ON
913
			ninePinGraphics = false;
914
			switch (parseNumber(1, 1)) {
915
				default:
916
				case 0:
917
					graphDensity = 1.0;
918
					break;
919
				case 1: case 2:
920
					graphDensity = 2.0;
921
					break;
922
				case 3:
923
					graphDensity = 4.0;
924
					break;
925
				case 4:
926
					graphDensity = 1.33;
927
					break;
928
				case 5:
929
					graphDensity = 1.2;
930
					break;
931
				case 6:
932
					graphDensity = 1.5;
933
					break;
934
			}
935
			sizeRemainingDataBytes = parseNumber(2, 2);
936
			break;
937
		case '-': // Turn Underline Mode ON/OFF
938
			underline = parseNumber(1, 1) != 0;
939
			break;
940
		case '/': // Selects Vertical Tab Channel
941
			break;
942
		case '0': // Sets Line Spacing to 1/8 inch
943
			lineFeed = 9.0;
944
			break;
945
		case '1': // Sets Line Spacing to 7/72 inch
946
			lineFeed = 7.0;
947
			break;
948
		case '2': // Sets Line Spacing to 1/6 inch
949
			lineFeed = 12.0;
950
			break;
951
		case '3': // Sets Line Spacing to n/216 inch
952
			lineFeed = (parseNumber(1, 1) & 127) * (1.0 / 3.0);
953
			break;
954
		case '4': // Turn Italic Mode ON
955
			italic = true;
956
			break;
957
		case '5': // Turn Italic Mode OFF
958
			italic = false;
959
			break;
960
		case '6': // Turn Printing of International Italic characters ON
961
			noHighEscapeCodes = true;
962
			break;
963
		case '7': // Turn Printing of International Italic characters OFF
964
			noHighEscapeCodes = false;
965
			break;
966
		case '8': // Turn Paper Out Sensor ON
967
			detectPaperOut = true;
968
			break;
969
		case '9': // Turn Paper Out Sensor OFF
970
			detectPaperOut = false;
971
			break;
972
		case ':': // Copies Rom Character set to RAM
973
			copy_to_range(fontInfo.rom, fontInfo.ram);
974
			break;
975
		case '<': // Turn Uni-directional printing ON (left to right)
976
			leftToRight = true;
977
			break;
978
		case '=': // Sets eight bit to 0
979
			eightBit = 0;
980
			break;
981
		case '>': // Sets eight bit to 1
982
			eightBit = 1;
983
			break;
984
		case '?': // Redefines Graphics Codes
985
			break;
986
		case '@': // Reset
987
			eightBit = -1;
988
			ninePinGraphics = false;
989
			graphDensity = 1.0;
990
			fontDensity = 1.0;
991
			underline = false;
992
			lineFeed = 12.0;
993
			italic = false;
994
			detectPaperOut = false;
995
			leftToRight = false;
996
			doubleStrike = false;
997
			elite = false;
998
			compressed = false;
999
			rightBorder = 6 * 78;
1000
			subscript = false;
1001
			superscript = false;
1002
			doubleWidth = false;
1003
			bold = false;
1004
			proportional = false;
1005
			fontInfo.useRam = false;
1006
			noHighEscapeCodes = false;
1007
			alternateChar = false;
1008
			countryCode = CountryCode::USA;
1009
			break;
1010
		case 'A': // Sets Line Spacing to n/72 inch
1011
			lineFeed = parseNumber(1, 1) & 127;
1012
			break;
1013
		case 'B': // Set tabs, variable length (up to 16 tabs)
1014
			break;
1015
		case 'C': // Set form length, variable length (2 or 3)
1016
			break;
1017
		case 'D': // Set tabs, variable length (up to 32 tabs)
1018
			break;
1019
		case 'E': // Turn Emphasized Mode ON
1020
			bold = true;
1021
			break;
1022
		case 'F': // Turn Emphasized Mode OFF
1023
			bold = false;
1024
			break;
1025
		case 'G': // Turn Double Strike Mode ON
1026
			doubleStrike = true;
1027
			break;
1028
		case 'H': // Turn Double Strike Mode OFF
1029
			doubleStrike = false;
1030
			break;
1031
		case 'I': // Enables printing of chars 1-31
1032
			alternateChar = parseNumber(1, 1) & 1;
1033
			break;
1034
		case 'J': // Forces Line Feed with n/216 inch
1035
			vpos += (parseNumber(1, 1) & 127) * (1.0 / 3.0);
1036
			if (vpos >= pageHeight) {
1037
				flushEmulatedPrinter();
1038
			}
1039
			break;
1040
		case 'K': // Turn Single Density Graphics on (480 dot mode)
1041
			graphDensity = 1.0;
1042
			ninePinGraphics = false;
1043
			sizeRemainingDataBytes = parseNumber(1, 2);
1044
			break;
1045
		case 'L': // Turn Double Density Graphics on (960 dot mode)
1046
			graphDensity = 2.0;
1047
			ninePinGraphics = false;
1048
			sizeRemainingDataBytes = parseNumber(1, 2);
1049
			break;
1050
		case 'M': // Turn Elite mode ON
1051
			elite = true;
1052
			fontDensity = 1.20;
1053
			break;
1054
		case 'N': // Turn Skip Over Perforation ON
1055
			break;
1056
		case 'O': // Turn Skip Over Perforation OFF
1057
			break;
1058
		case 'P': // Turn Elite mode OFF
1059
			elite = false;
1060
			fontDensity = compressed ? 1.72 : 1.00;
1061
			break;
1062
		case 'Q': { // Set Right Margin
1063
			auto width = std::min(parseNumber(1, 2), 78u); // TODO Font dependent !!
1064
			rightBorder = 6 * width;
1065
			break;
1066
		}
1067
		case 'R': // Select International Character Set
1068
			countryCode = static_cast<CountryCode>(parseNumber(1, 1));
1069
			if (countryCode > CountryCode::JAPAN) {
1070
				countryCode = CountryCode::USA;
1071
			}
1072
			break;
1073
		case 'S': { // Turn Script Mode ON
1074
			auto script = parseNumber(1, 1) & 1;
1075
			superscript = script == 0;
1076
			subscript   = script == 1;
1077
			break;
1078
		}
1079
		case 'T': // Turn Script Mode OFF
1080
			subscript = false;
1081
			superscript = false;
1082
			break;
1083
		case 'U': // Turn Uni-directional mode ON/OFF
1084
			leftToRight = parseNumber(1, 1) != 0;
1085
			break;
1086
		case 'W': // Turn Expanded Mode ON/OFF
1087
			normalAfterLine = false;
1088
			doubleWidth = parseNumber(1, 1) != 0;
1089
			break;
1090
		case 'Y': // Turn High Speed Double Density Graphics ON
1091
			break;
1092
		case 'Z': // Turns Quadruple Density Graphics ON
1093
			graphDensity = 4.0;
1094
			ninePinGraphics = false;
1095
			sizeRemainingDataBytes = parseNumber(1, 2);
1096
			break;
1097
		case '^': // Turn Nine Pin Graphics Mode ON
1098
			graphDensity = parseNumber(1, 1) ? 2.0 : 1.0;
1099
			ninePinGraphics = true;
1100
			sizeRemainingDataBytes = 2 * parseNumber(2, 2);
1101
			break;
1102
		case 'b': // Set Vertical Tab
1103
			break;
1104
		case 'i': // Turn Immediate Mode ON/OFF
1105
			break;
1106
		case 'j': // Immediate Reverse Line Feed
1107
			vpos -= (parseNumber(1, 1) & 127) * (1.0 / 3.0);
1108
			vpos = std::max(vpos, pageTop);
1109
			break;
1110
		case 'l': // Set Left Margin
1111
			break;
1112
		case 'p': // Turn proportional mode ON/OFF
1113
			proportional = parseNumber(1, 1) != 0;
1114
			break;
1115
		case 's': // Set Print Speed
1116
			break;
1117
		case 127: // Deletes Last Character in Buffer
1118
			break;
1119
	}
1120
}
1121
1122
// International character code translation for the Epson FX-80 printer
1123
//                                                   US   FR   DE   GB   DK   SE   IT   SP   JP
1124
static constexpr std::array<uint8_t, 9> intlChar35  = { 35,  35,  35,   6,  35,  35,  35,  12,  35};
1125
static constexpr std::array<uint8_t, 9> intlChar36  = { 36,  36,  36,  36,  36,  11,  36,  36,  36};
1126
static constexpr std::array<uint8_t, 9> intlChar64  = { 64,   0,  16,  64,  64,  29,  64,  64,  64};
1127
static constexpr std::array<uint8_t, 9> intlChar91  = { 91,   5,  23,  91,  18,  23,   5,   7,  91};
1128
static constexpr std::array<uint8_t, 9> intlChar92  = { 92,  15,  24,  92,  20,  24,  92,   9,  31};
1129
static constexpr std::array<uint8_t, 9> intlChar93  = { 93,  16,  25,  93,  13,  13,  30,   8,  93};
1130
static constexpr std::array<uint8_t, 9> intlChar94  = { 94,  94,  94,  94,  94,  25,  94,  94,  94};
1131
static constexpr std::array<uint8_t, 9> intlChar96  = { 96,  96,  96,  96,  96,  30,   2,  96,  96};
1132
static constexpr std::array<uint8_t, 9> intlChar123 = {123,  30,  26, 123,  19,  26,   0,  22, 123};
1133
static constexpr std::array<uint8_t, 9> intlChar124 = {124,   2,  27, 124,  21,  27,   3,  10, 124};
1134
static constexpr std::array<uint8_t, 9> intlChar125 = {125,   1,  28, 125,  14,  14,   1, 125, 125};
1135
static constexpr std::array<uint8_t, 9> intlChar126 = {126,  22,  17, 126, 126,  28,   4, 126, 126};
1136
1137
void ImagePrinterEpson::processCharacter(uint8_t data)
1138
{
1139
	if (data >= 32) {
1140
		if (italic) {
1141
			data |= 128;
1142
		} else {
1143
			data &= 127;
1144
		}
1145
	}
1146
1147
	if (!noHighEscapeCodes && data >= 128 && data < 160) {
1148
		data &= 31;
1149
	}
1150
1151
	// Convert international characters
1152
	auto cc = std::to_underlying(countryCode);
1153
	switch (data & 0x7f) {
1154
		case 35:  data = (data & 0x80) | intlChar35 [cc]; break;
1155
		case 36:  data = (data & 0x80) | intlChar36 [cc]; break;
1156
		case 64:  data = (data & 0x80) | intlChar64 [cc]; break;
1157
		case 91:  data = (data & 0x80) | intlChar91 [cc]; break;
1158
		case 92:  data = (data & 0x80) | intlChar92 [cc]; break;
1159
		case 93:  data = (data & 0x80) | intlChar93 [cc]; break;
1160
		case 94:  data = (data & 0x80) | intlChar94 [cc]; break;
1161
		case 96:  data = (data & 0x80) | intlChar96 [cc]; break;
1162
		case 123: data = (data & 0x80) | intlChar123[cc]; break;
1163
		case 124: data = (data & 0x80) | intlChar124[cc]; break;
1164
		case 125: data = (data & 0x80) | intlChar125[cc]; break;
1165
		case 126: data = (data & 0x80) | intlChar126[cc]; break;
1166
	}
1167
1168
	if (data >= 32) {
1169
		printVisibleCharacter(data);
1170
		return;
1171
	}
1172
1173
	switch (data) {
1174
		case 0: // Terminates horizontal and vertical TAB setting
1175
			break;
1176
		case 7: // Sound beeper
1177
			break;
1178
		case 8: // Backspace
1179
			// TODO: fix for other font-sizes
1180
			hpos = std::max(hpos - 8, double(leftBorder));
1181
			break;
1182
		case 9: // Horizontal TAB
1183
			// TODO: fix for other font-sizes
1184
			hpos = ((unsigned(hpos) + 64 - leftBorder) & ~63)
1185
			     + leftBorder;
1186
			if (hpos < rightBorder) {
1187
				break;
1188
			}
1189
			hpos = leftBorder;
1190
			[[fallthrough]];
1191
		case 10: // Line Feed
1192
		case 11: // Vertical TAB
1193
			vpos += lineFeed;
1194
			if (vpos >= pageHeight) {
1195
				flushEmulatedPrinter();
1196
			}
1197
			break;
1198
		case 12: // Form Feed
1199
			ensurePrintPage();
1200
			flushEmulatedPrinter();
1201
			break;
1202
		case 13: // Carrige return
1203
			hpos = leftBorder;
1204
			break;
1205
		case 14: // Turns expanded mode ON
1206
			doubleWidth = true;
1207
			normalAfterLine = true;
1208
			break;
1209
		case 15: // Shift in. Empties buffer, turns compressed mode ON (17.16 cpi)
1210
			compressed = true;
1211
			if (!elite) {
1212
				fontDensity = 1.72;
1213
			}
1214
			break;
1215
		case 17: // Device Control 1:
1216
			break;
1217
		case 18: // Device Control 2: turns compressed mode OFF
1218
			compressed = false;
1219
			fontDensity = 1.00;
1220
			break;
1221
		case 19: // Device Control 3:
1222
			break;
1223
		case 20: // Device Control 4: Turns expanded mode OFF
1224
			doubleWidth = false;
1225
			break;
1226
		case 24: // Cancels all text in the print buffer
1227
			break;
1228
		case 27: // Escape
1229
			escSequence = true;
1230
			break;
1231
		default:
1232
			if (alternateChar) {
1233
				printVisibleCharacter(data);
1234
			}
1235
			break;
1236
	}
1237
}
1238
1239
template<typename Archive>
1240
void ImagePrinterEpson::serialize(Archive& /*ar*/, unsigned /*version*/)
1241
{
1242
	// TODO is this worth it?
1243
}
1244
INSTANTIATE_SERIALIZE_METHODS(ImagePrinterEpson);
1245
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, ImagePrinterEpson, "ImagePrinterEpson");
1246
1247
1248
} // namespace openmsx