gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-lib/examples/cross-platform/gbprinter/src/gbprinter.c
1 #include <gbdk/platform.h>
2 #include <stdint.h>
3 #include <string.h>
4 #include <stdbool.h>
5
6 #include "gbprinter.h"
7
8 #define REINIT_SEIKO
9
10 #define START_TRANSFER 0x81
11 #define PRN_BUSY_TIMEOUT PRN_SECONDS(2)
12 #define PRN_COMPLETION_TIMEOUT PRN_SECONDS(20)
13 #define PRN_SEIKO_RESET_TIMEOUT 10
14
15 #define PRN_FINAL_MARGIN 0x03
16
17 static const uint8_t PRN_PKT_INIT[] = { PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_INIT), PRN_LE(0), PRN_LE(0x01), PRN_LE(0) };
18 static const uint8_t PRN_PKT_STATUS[] = { PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_STATUS), PRN_LE(0), PRN_LE(0x0F), PRN_LE(0) };
19 static const uint8_t PRN_PKT_EOF[] = { PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_DATA), PRN_LE(0), PRN_LE(0x04), PRN_LE(0) };
20 static const uint8_t PRN_PKT_CANCEL[] = { PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_BREAK), PRN_LE(0), PRN_LE(0x01), PRN_LE(0) };
21
22 start_print_pkt_t PRN_PKT_START = {
23 .magic = PRN_MAGIC, .command = PRN_CMD_PRINT, .length = 4,
24 .print = TRUE, .margins = 0, .palette = PRN_PALETTE_NORMAL, .exposure = PRN_EXPOSURE_DARK,
25 .crc = 0, .trail = 0
26 };
27
28 static uint16_t printer_status;
29 static uint8_t printer_tile_num;
30
31 #if defined(NINTENDO)
32 uint8_t printer_send_receive(uint8_t b) {
33 SB_REG = b;
34 SC_REG = 0x81;
35 while (SC_REG & 0x80);
36 return SB_REG;
37 }
38 #elif defined(SEGA)
39 // DPC0 = MISO
40 // DPC1 = MOSI
41 // DPC6 = CLK
42 // GND = GND
43 uint8_t printer_send_receive(uint8_t data) NAKED PRESERVES_REGS(h, l, iyh, iyl) {
44 data;
45 __asm
46 ld e, #0b00000001
47 ld c, #_GG_EXT_CTL
48 out (c), e
49 ld c, #_GG_EXT_7BIT
50 out (c), e
51 ld b, #8
52 1$:
53 rlca
54 rl e
55 rl e
56
57 res 7, e
58 res 6, e
59 out (c), e
60
61 ld d, b
62 ld b, #15
63 2$: djnz 2$
64
65 set 6, e
66 out (c), e
67
68 ld b, #8
69 3$: djnz 3$
70 ld b, d
71
72 in e, (c)
73 rrca
74 srl e
75 rla
76 djnz 1$
77 ret
78 __endasm;
79 }
80 #endif
81
82 uint8_t printer_send_byte(uint8_t b) {
83 return (uint8_t)(printer_status = ((printer_status << 8) | printer_send_receive(b)));
84 }
85
86 uint8_t printer_send_command(const uint8_t *command, uint8_t length) {
87 uint8_t index = 0;
88 while (index++ < length) printer_send_byte(*command++);
89 return ((uint8_t)(printer_status >> 8) == PRN_MAGIC_DETECT) ? (uint8_t)printer_status : PRN_STATUS_MASK_ERRORS;
90 }
91 #define PRINTER_SEND_COMMAND(CMD) printer_send_command((const uint8_t *)&(CMD), sizeof(CMD))
92
93 uint8_t printer_print_tile(const uint8_t *tiledata) {
94 static const uint8_t PRINT_TILE[] = { 0x88,0x33,0x04,0x00,0x80,0x02 };
95 static uint16_t printer_CRC;
96 if (printer_tile_num == 0) {
97 const uint8_t * data = PRINT_TILE;
98 for (uint8_t i = sizeof(PRINT_TILE); i != 0; i--) printer_send_receive(*data++);
99 printer_CRC = 0x04 + 0x80 + 0x02;
100 }
101 for(uint8_t i = 0x10; i != 0; i--, tiledata++) {
102 printer_CRC += *tiledata;
103 printer_send_receive(*tiledata);
104 }
105 if (++printer_tile_num == 40) {
106 printer_send_receive((uint8_t)printer_CRC);
107 printer_send_receive((uint8_t)(printer_CRC >> 8));
108 printer_send_receive(0x00);
109 printer_send_receive(0x00);
110 printer_CRC = printer_tile_num = 0;
111 return TRUE;
112 }
113 return FALSE;
114 }
115
116 inline void printer_init(void) {
117 printer_tile_num = 0;
118 PRINTER_SEND_COMMAND(PRN_PKT_INIT);
119 }
120
121 extern bool printer_check_cancel(void);
122
123 uint8_t printer_wait(uint16_t timeout, uint8_t mask, uint8_t value) {
124 uint8_t error;
125 while (((error = PRINTER_SEND_COMMAND(PRN_PKT_STATUS)) & mask) != value) {
126 if (printer_check_cancel()) {
127 PRINTER_SEND_COMMAND(PRN_PKT_CANCEL);
128 return PRN_STATUS_CANCELLED;
129 }
130 if (timeout-- == 0) return PRN_STATUS_MASK_ERRORS;
131 if (error & PRN_STATUS_MASK_ERRORS) break;
132 vsync();
133 }
134 return error;
135 }
136
137 uint8_t gbprinter_detect(uint8_t delay) {
138 printer_init();
139 return printer_wait(delay, PRN_STATUS_MASK_ANY, PRN_STATUS_OK);
140 }
141
142 uint8_t gbprinter_print_image(const uint8_t * image_map, const uint8_t * image, int8_t pos_x, uint8_t width, uint8_t height) {
143 static const uint8_t * img;
144 static uint8_t error;
145
146 uint8_t tile_data[16], rows = (((height + 1) >> 1) << 1), pkt_count = 0;
147
148 if ((rows >> 1) == 0) return PRN_STATUS_OK;
149
150 img = image;
151
152 printer_tile_num = 0;
153
154 for (uint8_t y = 0; y != rows; y++) {
155 for (int16_t x = 0; x != PRN_TILE_WIDTH; x++) {
156 // overlay the picture tile if in range
157 if ((y < height) && (x >= pos_x) && (x < (pos_x + width))) {
158 uint8_t tile = image_map[(y * width) + (x - pos_x)];
159 memcpy(tile_data, img + ((uint16_t)tile << 4), sizeof(tile_data));
160 } else {
161 memset(tile_data, 0, sizeof(tile_data));
162 }
163 // print the resulting tile
164 if (printer_print_tile(tile_data)) {
165 pkt_count++;
166 if (printer_check_cancel()) {
167 PRINTER_SEND_COMMAND(PRN_PKT_CANCEL);
168 return PRN_STATUS_CANCELLED;
169 }
170 }
171 if (pkt_count == 9) {
172 pkt_count = 0;
173 PRINTER_SEND_COMMAND(PRN_PKT_EOF);
174 // setup margin if last packet
175 gbprinter_set_print_params((y == (rows - 1)) ? PRN_FINAL_MARGIN : PRN_NO_MARGINS, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK);
176 PRINTER_SEND_COMMAND(PRN_PKT_START);
177 // query printer status
178 if ((error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY)) & PRN_STATUS_MASK_ERRORS) return error;
179 if ((error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0)) & PRN_STATUS_MASK_ERRORS) return error;
180 #ifdef REINIT_SEIKO
181 // reinit printer (required by Seiko?)
182 if (y != (rows - 1)) {
183 PRINTER_SEND_COMMAND(PRN_PKT_INIT);
184 if (error = printer_wait(PRN_SEIKO_RESET_TIMEOUT, PRN_STATUS_MASK_ANY, PRN_STATUS_OK)) return error;
185 }
186 #endif
187 }
188 }
189 }
190 if (pkt_count) {
191 PRINTER_SEND_COMMAND(PRN_PKT_EOF);
192 // setup printing if required
193 gbprinter_set_print_params(PRN_FINAL_MARGIN, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK);
194 PRINTER_SEND_COMMAND(PRN_PKT_START);
195 // query printer status
196 if ((error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY)) & PRN_STATUS_MASK_ERRORS) return error;
197 if ((error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0)) & PRN_STATUS_MASK_ERRORS) return error;
198 }
199 return PRINTER_SEND_COMMAND(PRN_PKT_STATUS);
200 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.