all repos — mgba @ dc976eaf51a9bea691736a03f1113e34ad819462

mGBA Game Boy Advance Emulator

src/gb/sio/printer.c (view raw)

  1/* Copyright (c) 2013-2017 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/gb/sio/printer.h>
  7
  8#include <mgba/internal/gb/gb.h>
  9#include <mgba/internal/gb/io.h>
 10
 11
 12static bool GBPrinterInit(struct GBSIODriver* driver);
 13static void GBPrinterDeinit(struct GBSIODriver* driver);
 14static void GBPrinterWriteSB(struct GBSIODriver* driver, uint8_t value);
 15static uint8_t GBPrinterWriteSC(struct GBSIODriver* driver, uint8_t value);
 16
 17void GBPrinterCreate(struct GBPrinter* printer) {
 18	printer->d.init = GBPrinterInit;
 19	printer->d.deinit = GBPrinterDeinit;
 20	printer->d.writeSB = GBPrinterWriteSB;
 21	printer->d.writeSC = GBPrinterWriteSC;
 22	printer->print = NULL;
 23}
 24
 25bool GBPrinterInit(struct GBSIODriver* driver) {
 26	struct GBPrinter* printer = (struct GBPrinter*) driver;
 27
 28	printer->checksum = 0;
 29	printer->command = 0;
 30	printer->remainingBytes = 0;
 31	printer->currentIndex = 0;
 32	printer->compression = false;
 33	printer->byte = 0;
 34	printer->next = GB_PRINTER_BYTE_MAGIC_0;
 35	printer->status = 0;
 36	printer->printWait = -1;
 37
 38	printer->buffer = malloc(GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS / 2);
 39
 40	return true;
 41}
 42
 43void GBPrinterDeinit(struct GBSIODriver* driver) {
 44	struct GBPrinter* printer = (struct GBPrinter*) driver;
 45	free(printer->buffer);
 46}
 47
 48static void GBPrinterWriteSB(struct GBSIODriver* driver, uint8_t value) {
 49	struct GBPrinter* printer = (struct GBPrinter*) driver;
 50	printer->byte = value;
 51}
 52
 53static void _processByte(struct GBPrinter* printer) {
 54	switch (printer->command) {
 55	case GB_PRINTER_COMMAND_DATA:
 56		if (printer->currentIndex < GB_VIDEO_VERTICAL_PIXELS * GB_VIDEO_HORIZONTAL_PIXELS / 2) {
 57			printer->buffer[printer->currentIndex] = printer->byte;
 58			++printer->currentIndex;
 59		}
 60		break;
 61	case GB_PRINTER_COMMAND_PRINT:
 62		// TODO
 63		break;
 64	default:
 65		break;
 66	}
 67}
 68
 69static uint8_t GBPrinterWriteSC(struct GBSIODriver* driver, uint8_t value) {
 70	struct GBPrinter* printer = (struct GBPrinter*) driver;
 71	if ((value & 0x81) == 0x81) {
 72		switch (printer->next) {
 73		driver->p->pendingSB = 0;
 74		case GB_PRINTER_BYTE_MAGIC_0:
 75			if (printer->byte == 0x88) {
 76				printer->next = GB_PRINTER_BYTE_MAGIC_1;
 77			} else {
 78				printer->next = GB_PRINTER_BYTE_MAGIC_0;
 79			}
 80			break;
 81		case GB_PRINTER_BYTE_MAGIC_1:
 82			if (printer->byte == 0x33) {
 83				printer->next = GB_PRINTER_BYTE_COMMAND;
 84			} else {
 85				printer->next = GB_PRINTER_BYTE_MAGIC_0;
 86			}
 87			break;
 88		case GB_PRINTER_BYTE_COMMAND:
 89			printer->checksum = printer->byte;
 90			printer->command = printer->byte;
 91			printer->next = GB_PRINTER_BYTE_COMPRESSION;
 92			break;
 93		case GB_PRINTER_BYTE_COMPRESSION:
 94			printer->checksum += printer->byte;
 95			printer->compression = printer->byte;
 96			printer->next = GB_PRINTER_BYTE_LENGTH_0;
 97			break;
 98		case GB_PRINTER_BYTE_LENGTH_0:
 99			printer->checksum += printer->byte;
100			printer->remainingBytes = printer->byte;
101			printer->next = GB_PRINTER_BYTE_LENGTH_1;
102			break;
103		case GB_PRINTER_BYTE_LENGTH_1:
104			printer->checksum += printer->byte;
105			printer->remainingBytes |= printer->byte << 8;
106			if (printer->remainingBytes) {
107				printer->next = GB_PRINTER_BYTE_DATA;
108			} else {
109				printer->next = GB_PRINTER_BYTE_CHECKSUM_0;
110			}
111			switch (printer->command) {
112			case GB_PRINTER_COMMAND_INIT:
113				printer->currentIndex = 0;
114				printer->status &= ~(GB_PRINTER_STATUS_PRINT_REQ | GB_PRINTER_STATUS_READY);
115				break;
116			default:
117				break;
118			}
119			break;
120		case GB_PRINTER_BYTE_DATA:
121			printer->checksum += printer->byte;
122			if (!printer->compression) {
123				_processByte(printer);
124			} else {
125				printer->next = printer->byte & 0x80 ? GB_PRINTER_BYTE_COMPRESSED_DATUM : GB_PRINTER_BYTE_UNCOMPRESSED_DATA;
126				printer->remainingCmpBytes = (printer->byte & 0x7F) + 1;
127				if (printer->byte & 0x80) {
128					++printer->remainingCmpBytes;
129				}
130			}
131			--printer->remainingBytes;
132			if (!printer->remainingBytes) {
133				printer->next = GB_PRINTER_BYTE_CHECKSUM_0;
134			}
135			break;
136		case GB_PRINTER_BYTE_UNCOMPRESSED_DATA:
137			printer->checksum += printer->byte;
138			_processByte(printer);
139			--printer->remainingCmpBytes;
140			if (!printer->remainingCmpBytes) {
141				printer->next = GB_PRINTER_BYTE_DATA;
142			}
143			--printer->remainingBytes;
144			if (!printer->remainingBytes) {
145				printer->next = GB_PRINTER_BYTE_CHECKSUM_0;
146			}
147			break;
148		case GB_PRINTER_BYTE_COMPRESSED_DATUM:
149			printer->checksum += printer->byte;
150			while (printer->remainingCmpBytes) {
151				_processByte(printer);
152				--printer->remainingCmpBytes;
153			}
154			--printer->remainingBytes;
155			if (!printer->remainingBytes) {
156				printer->next = GB_PRINTER_BYTE_CHECKSUM_0;
157			} else {
158				printer->next = GB_PRINTER_BYTE_DATA;
159			}
160			break;
161		case GB_PRINTER_BYTE_CHECKSUM_0:
162			printer->checksum ^= printer->byte;
163			printer->next = GB_PRINTER_BYTE_CHECKSUM_1;
164			break;
165		case GB_PRINTER_BYTE_CHECKSUM_1:
166			printer->checksum ^= printer->byte << 8;
167			printer->next = GB_PRINTER_BYTE_KEEPALIVE;
168			break;
169		case GB_PRINTER_BYTE_KEEPALIVE:
170			driver->p->pendingSB = 0x81;
171			printer->next = GB_PRINTER_BYTE_STATUS;
172			break;
173		case GB_PRINTER_BYTE_STATUS:
174			switch (printer->command) {
175			case GB_PRINTER_COMMAND_DATA:
176				if (printer->currentIndex >= 0x280 && !(printer->status & GB_PRINTER_STATUS_CHECKSUM_ERROR)) {
177					printer->status |= GB_PRINTER_STATUS_READY;
178				}
179				break;
180			case GB_PRINTER_COMMAND_PRINT:
181				if (printer->currentIndex >= GB_VIDEO_HORIZONTAL_PIXELS * 2) {
182					printer->printWait = 0;
183				}
184				break;
185			case GB_PRINTER_COMMAND_STATUS:
186				if (!printer->printWait) {
187					printer->status &= ~GB_PRINTER_STATUS_READY;
188					printer->status |= GB_PRINTER_STATUS_PRINTING | GB_PRINTER_STATUS_PRINT_REQ;
189					if (printer->print) {
190						size_t y;
191						for (y = 0; y < printer->currentIndex / (2 * GB_VIDEO_HORIZONTAL_PIXELS); ++y) {
192							uint8_t lineBuffer[GB_VIDEO_HORIZONTAL_PIXELS * 2];
193							uint8_t* buffer = &printer->buffer[sizeof(lineBuffer) * y];
194							size_t i;
195							for (i = 0; i < sizeof(lineBuffer); i += 2) {
196								uint8_t ilo = buffer[i + 0x0];
197								uint8_t ihi = buffer[i + 0x1];
198								uint8_t olo = 0;
199								uint8_t ohi = 0;
200								olo |= ((ihi & 0x80) >> 0) | ((ilo & 0x80) >> 1);
201								olo |= ((ihi & 0x40) >> 1) | ((ilo & 0x40) >> 2);
202								olo |= ((ihi & 0x20) >> 2) | ((ilo & 0x20) >> 3);
203								olo |= ((ihi & 0x10) >> 3) | ((ilo & 0x10) >> 4);
204								ohi |= ((ihi & 0x08) << 4) | ((ilo & 0x08) << 3);
205								ohi |= ((ihi & 0x04) << 3) | ((ilo & 0x04) << 2);
206								ohi |= ((ihi & 0x02) << 2) | ((ilo & 0x02) << 1);
207								ohi |= ((ihi & 0x01) << 1) | ((ilo & 0x01) << 0);
208								lineBuffer[(((i >> 1) & 0x7) * GB_VIDEO_HORIZONTAL_PIXELS / 4) + ((i >> 3) & ~1)] = olo;
209								lineBuffer[(((i >> 1) & 0x7) * GB_VIDEO_HORIZONTAL_PIXELS / 4) + ((i >> 3) |  1)] = ohi;
210							}
211							memcpy(buffer, lineBuffer, sizeof(lineBuffer));
212						}
213						printer->print(printer, printer->currentIndex * 4 / GB_VIDEO_HORIZONTAL_PIXELS, printer->buffer);
214					}
215				}
216				if (printer->printWait >= 0) {
217					--printer->printWait;
218				}
219			default:
220				break;
221			}
222			driver->p->pendingSB = printer->status;
223			printer->next = GB_PRINTER_BYTE_MAGIC_0;
224			break;
225		}
226		printer->byte = 0;
227	}
228	return value;
229}
230
231void GBPrinterDonePrinting(struct GBPrinter* printer) {
232	printer->status &= ~GB_PRINTER_STATUS_PRINTING;
233}