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}