gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-lib/examples/megaduck/laptop_printer/src/megaduck_printer.c
1 #include <gbdk/platform.h>
2 #include <stdint.h>
3 #include <stdbool.h>
4
5 #include <duck/laptop_io.h>
6
7 #include "megaduck_printer.h"
8
9 uint8_t tile_row_buffer[DEVICE_SCREEN_WIDTH * BYTES_PER_PRINTER_TILE];
10
11 // For sending a bulk printer command to megaduck printer
12 //
13 // Expects duck_io_tx_buf to be pre-loaded with payload
14 //
15 // The System ROM uses an infinite retry which would block
16 // program execution forever if the printer failed. Instead
17 // 10x has been determined via trial and error as a reasonable
18 // number of retries.
19 static bool print_send_command_and_buffer_delay_1msec_10x_retry(uint8_t command) {
20
21 uint8_t retry = 10u;
22 while (retry--) {
23 bool result = duck_io_send_cmd_and_buffer(command);
24 delay(1);
25 if (result == true) return true;
26 }
27 return false;
28 }
29
30
31 // Transforming tile data for Printer use
32 //
33 // SCREEN ---> PRINTER
34 //
35 // This (1bpp) input tile Should be transformed to the following PRINTER formatted output:
36 //
37 // *BITS* (X) Tile bytes (X)
38 // 7 ___ 0 Bytes 0 ___ 7
39 // |
40 // 0 X....... = [0] = 0x80 0 X.......
41 // (Y)| X....... = [1] = 0x80 (Y)| X.......
42 // | X....... = [2] = 0x80 * | X.......
43 // b | X....... = [3] = 0x80 B | X.......
44 // y | ........ = [4] = 0x00 I | ........
45 // t | ........ = [5] = 0x00 T | ........
46 // e | ........ = [6] = 0x00 S | ........
47 // s 7 .XXXXXXX = [7] = 0x7F * 7 .XXXXXXX
48 // [0 ... 7] <- Tile Bytes <- {0xF0, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}
49 //
50 // The first ROW in above The first COLUMN in above
51 // represents the byte 0x80 represents the byte 0xF0
52
53
54 // Converts one plane (i.e. monochrome, not 4 shades of grey) of
55 // an 8x8 Game Boy format tile for printing on the Mega Duck Printer.
56 void duck_printer_convert_tile(uint8_t * p_out_buf, uint8_t * p_tile_buf) {
57
58 // Clear printer tile
59 for (uint8_t c = 0u; c < BYTES_PER_PRINTER_TILE; c++) {
60 p_out_buf[c] = 0u;
61 }
62
63 // Transform tile bytes into printable row buffer bytes
64 // Tile must get flipped horizontally and rotated -90 degrees
65 //
66 // Note the +2 increment for tile row, skipping the interleaved higher bit plane
67 //
68 // For each tile row byte take the X axis bits representing pixels
69 // and transform them into column oriented bits spread across 8 bytes
70 //
71 uint8_t out_bit = 0x80u; // X axis bit to set in the output for corresponding input
72 for (uint8_t tile_row = 0u; tile_row < BYTES_PER_VRAM_TILE; tile_row += 2u) {
73 uint8_t tile_byte = p_tile_buf[tile_row];
74
75 // Scan X axis of tile Left to right
76 uint8_t tile_bit = 0x80u;
77 for (uint8_t out_col = 0; out_col < TILE_HEIGHT; out_col++) {
78
79 if (tile_byte & tile_bit) p_out_buf[out_col] |= out_bit;
80 tile_bit >>= 1;
81 }
82 out_bit >>= 1;
83 }
84 }
85
86
87 // Converts one both planes (i.e. 4 shades of grey) of an 8x8 Game Boy format
88 // tile into partially dithered monochrome for printing on the Mega Duck Printer.
89 //
90 // Color 0: always white
91 // Color 1: white or black based on checkerboard dither pattern
92 // Color 2 or 3: always black
93 void duck_printer_convert_tile_dithered(uint8_t * p_out_buf, uint8_t * p_tile_buf) {
94
95 // Clear printer tile
96 for (uint8_t c = 0u; c < BYTES_PER_PRINTER_TILE; c++) {
97 p_out_buf[c] = 0u;
98 }
99
100 // Transform tile bytes into printable row buffer bytes
101 // Tile must get flipped horizontally and rotated -90 degrees
102 //
103 // For each tile row byte take the X axis bits representing pixels
104 // and transform them into column oriented bits spread across 8 bytes
105 //
106 uint8_t out_bit = 0x80u; // X axis bit to set in the output for corresponding input
107 uint8_t dither = 0xAAu; // Dither pattern
108 for (uint8_t tile_row = 0u; tile_row < BYTES_PER_VRAM_TILE; tile_row += 2u) {
109 uint8_t tile_byte0 = p_tile_buf[tile_row];
110 uint8_t tile_byte1 = p_tile_buf[tile_row+1];
111
112 // LSByte first, Scan X axis Left to right
113 uint8_t tile_bit = 0x80u;
114 for (uint8_t out_col = 0; out_col < TILE_HEIGHT; out_col++) {
115
116 if (tile_byte1 & tile_bit) {
117 p_out_buf[out_col] |= out_bit;; // Color 2 or 3 = always on
118 } else if ((tile_byte0 & dither) & tile_bit) {
119 p_out_buf[out_col] |= out_bit; // Color 1 enabled based on checkerboard dither pattern
120 }
121
122 tile_bit >>= 1;
123 }
124 // Flip dither pattern for next source tile row
125 dither = ~dither;
126 out_bit >>= 1;
127 }
128 }
129
130
131 // Send a prepared 20 x 8x8 row of tile data in duck_io_tx_buf[]
132 // to the Mega Duck printer in 1bpp format.
133 //
134 // The tile data should already be transformed using one of the
135 // duck_printer_convert_tile_...() functions.
136 bool duck_printer_send_tile_row_1pass(void) {
137
138 uint8_t * p_row_buffer = tile_row_buffer;
139
140 // Send 13 x 12 byte packets with row data
141 duck_io_tx_buf_len = PRINTER_LEN_12_ROW_DATA;
142 for (uint8_t packet = 0u; packet < PRINTER_1_PASS_ROW_NUM_PACKETS; packet++) {
143
144 for (uint8_t c = 0u; c < (duck_io_tx_buf_len); c++)
145 duck_io_tx_buf[c] = *p_row_buffer++;
146
147 if (!print_send_command_and_buffer_delay_1msec_10x_retry(DUCK_IO_CMD_PRINT_SEND_BYTES)) {
148 // This delay seems to fix periodic skipped tile glitching
149 delay(PRINT_DELAY_BETWEEN_ROWS_1000MSEC);
150
151 return false; // Fail out if there was a problem
152 }
153 }
154
155 uint8_t txbyte;
156 // Now send remaining bulk non-packetized data (unclear why transmit methods are split)
157 for (txbyte = 0u; txbyte < PRINTER_1_PASS_ROW_NUM_BULK_DATA_BYTES; txbyte++) {
158 duck_io_read_byte_with_msecs_timeout(PRINTER_1_PASS_BULK_ACK_TIMEOUT_100MSEC);
159 duck_io_send_byte(*p_row_buffer++);
160 }
161
162 // Send last four bulk bytes after end of tile data, unclear what they are for
163 for (txbyte = 0u; txbyte < PRINTER_1_PASS_ROW_NUM_BULK_UNKNOWN_BYTES; txbyte++) {
164 duck_io_read_byte_with_msecs_timeout(PRINTER_1_PASS_BULK_ACK_TIMEOUT_100MSEC);
165 duck_io_send_byte(0x00);
166 }
167
168 // Wait for last bulk data ACK (with 1msec delay for unknown reason)
169 duck_io_read_byte_with_msecs_timeout(PRINT_ROW_END_ACK_WAIT_TIMEOUT_200MSEC);
170
171 // End of row: wait for Carriage Return confirmation ACK from the printer
172 // System ROM doesn't seem to care about the return value, so we won't either for now
173 duck_io_read_byte_with_msecs_timeout(PRINT_ROW_END_ACK_WAIT_TIMEOUT_200MSEC);
174
175 // This delay seems to fix periodic skipped tile glitching
176 // as well as peripheral controller asic lockup and cpu reset
177 // if the keyboard is polled too soon after the end of a
178 // print row is sent.
179 delay(PRINT_DELAY_BETWEEN_ROWS_1000MSEC);
180
181 return true; // Success
182 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.