Browse Source

start adding gb printer support

Thomas B 1 month ago
parent
commit
494ba4bb8a
15 changed files with 601 additions and 18 deletions
  1. 2
    0
      .gitignore
  2. 11
    5
      Makefile
  3. 0
    1
      src/config.h
  4. 4
    0
      src/game.c
  5. 366
    0
      src/gbprinter.c
  6. 41
    0
      src/gbprinter.h
  7. 65
    0
      src/gbprinter_error.c
  8. 16
    0
      src/main.c
  9. 30
    9
      src/maps.c
  10. 2
    0
      src/maps.h
  11. 1
    0
      src/score.c
  12. 5
    0
      src/sgb_border.c
  13. 5
    0
      src/sgb_border.h
  14. 52
    3
      src/window.c
  15. 1
    0
      src/window.h

+ 2
- 0
.gitignore View File

@@ -5,3 +5,5 @@ compile_commands.json
5 5
 bear.cfg
6 6
 tmp
7 7
 *.sav
8
+gbe.ini
9
+*gb_print_*.bmp

+ 11
- 5
Makefile View File

@@ -49,6 +49,7 @@ ROMU := $(GBDK_HOME)/bin/romusage
49 49
 GB_EMU := gearboy
50 50
 SGB_EMU := sameboy
51 51
 BGB_EMU := wine ~/bin/bgb/bgb.exe
52
+GBE_EMU := wine ~/bin/gbe/gbe_plus_qt.exe
52 53
 FLASHER := flashgbx
53 54
 
54 55
 LCCFLAGS := -Wa-l -Wl-m -Wp-MMD -Wf--opt-code-speed
@@ -56,9 +57,10 @@ LCCFLAGS += -I$(SRC_DIR) -I$(BUILD_DIR)/$(DATA_DIR)
56 57
 LCCFLAGS += -Wm"-yn Duality" -Wm-yt0x1B -Wm-yoA -Wm-ya1 -Wm-yc -Wm-ys
57 58
 LCCFLAGS += -autobank -Wb-ext=.rel -Wb-v -Wf-bo255
58 59
 
59
-GB_EMUFLAGS := $(BIN)
60
-SGB_EMUFLAGS := $(BIN)
60
+GB_EMUFLAGS := $(BUILD_DIR)/$(BIN)
61
+SGB_EMUFLAGS := $(BUILD_DIR)/$(BIN)
61 62
 BGB_EMUFLAGS := $(BUILD_DIR)/$(BIN)
63
+GBE_EMUFLAGS := $(BUILD_DIR)/$(BIN)
62 64
 
63 65
 ifndef GBDK_RELEASE
64 66
 	LCCFLAGS += -debug -DDEBUG -Wa-j -Wa-y -Wa-s -Wl-j -Wl-y -Wl-u -Wm-yS
@@ -76,7 +78,7 @@ $(info BUILD_TYPE is $(BUILD_TYPE))
76 78
 DEPS=$(OBJS:%.o=%.d)
77 79
 -include $(DEPS)
78 80
 
79
-.PHONY: all run sgb_run bgb_run flash clean compile_commands.json usage $(GIT)
81
+.PHONY: all run sgb_run bgb_run gbe_run flash clean compile_commands.json usage $(GIT)
80 82
 .PRECIOUS: $(BUILD_DIR)/$(DATA_DIR)/%.c $(BUILD_DIR)/$(DATA_DIR)/%.h
81 83
 
82 84
 all: $(BIN)
@@ -98,11 +100,11 @@ usage: $(BUILD_DIR)/$(BIN)
98 100
 	@echo Analyzing $<
99 101
 	@$(ROMU) $(BUILD_DIR)/$(BIN:%.gb=%.map)
100 102
 
101
-run: $(BIN)
103
+run: $(BUILD_DIR)/$(BIN)
102 104
 	@echo Emulating $<
103 105
 	@$(GB_EMU) $(GB_EMUFLAGS)
104 106
 
105
-sgb_run: $(BIN)
107
+sgb_run: $(BUILD_DIR)/$(BIN)
106 108
 	@echo Emulating $<
107 109
 	@$(SGB_EMU) $(SGB_EMUFLAGS)
108 110
 
@@ -110,6 +112,10 @@ bgb_run: $(BUILD_DIR)/$(BIN)
110 112
 	@echo Emulating $<
111 113
 	@$(BGB_EMU) $(BGB_EMUFLAGS)
112 114
 
115
+gbe_run: $(BUILD_DIR)/$(BIN)
116
+	@echo Emulating $<
117
+	@$(GBE_EMU) $(GBE_EMUFLAGS)
118
+
113 119
 flash: $(BIN)
114 120
 	@echo Flashing $<
115 121
 	@$(FLASHER) $(FLASHFLAGS) $<

+ 0
- 1
src/config.h View File

@@ -21,7 +21,6 @@
21 21
 #define __CONFIG_H__
22 22
 
23 23
 #include <gbdk/platform.h>
24
-#include <gbdk/emu_debug.h>
25 24
 #include <stdint.h>
26 25
 
27 26
 #include "score.h"

+ 4
- 0
src/game.c View File

@@ -395,6 +395,10 @@ int32_t game(void) NONBANKED {
395 395
             snd_music(SND_GAME);
396 396
         }
397 397
 
398
+        if (key_pressed(J_SELECT) && conf_get()->debug_flags) {
399
+            map_dbg_reset();
400
+        }
401
+
398 402
         map_move(spd_x, spd_y);
399 403
 
400 404
         uint8_t hiwater = SPR_NUM_START;

+ 366
- 0
src/gbprinter.c View File

@@ -0,0 +1,366 @@
1
+/*
2
+ * gbprinter.c
3
+ * Duality
4
+ *
5
+ * Based on the gbprinter example from gbdk-2020:
6
+ * https://github.com/gbdk-2020/gbdk-2020/tree/develop/gbdk-lib/examples/gb/gbprinter
7
+ *
8
+ * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
9
+ *
10
+ * This program is free software: you can redistribute it and/or modify
11
+ * it under the terms of the GNU General Public License as published by
12
+ * the Free Software Foundation, either version 3 of the License, or
13
+ * (at your option) any later version.
14
+ *
15
+ * This program is distributed in the hope that it will be useful,
16
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+ * GNU General Public License for more details.
19
+ *
20
+ * See <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+#include <gbdk/platform.h>
24
+#include <gbdk/emu_debug.h>
25
+#include <stdio.h>
26
+#include <string.h>
27
+
28
+#include "input.h"
29
+#include "gbprinter.h"
30
+
31
+BANKREF(gbprinter)
32
+
33
+/** Width of the printed image in tiles
34
+*/
35
+#define PRN_TILE_WIDTH          20
36
+
37
+#define PRN_LOW(A) ((A) & 0xFF)
38
+#define PRN_HIGH(A) ((A) >> 8)
39
+
40
+/** 0x88,0x33 are mandatory first bytes to initialise a communication with printer
41
+ *   Any command sequence begins by these
42
+ */
43
+#define PRN_MAGIC               0x3388
44
+#define PRN_LE(A)               PRN_LOW(A),PRN_HIGH(A)
45
+
46
+/** magic number that is sent in the reply packet by the printer before the status byte
47
+ */
48
+#define PRN_MAGIC_DETECT        0x81
49
+
50
+/** INIT command is mandatory to initialize communication protocol with the printer
51
+ *   Two consecutive linked commands must never be more than 150 ms apart except the INIT command which is valid at least 10 seconds
52
+ */
53
+#define PRN_CMD_INIT            0x01
54
+
55
+/** PRINT command
56
+ *   Contains the palette, margins, number of prints and printing intensity
57
+ */
58
+#define PRN_CMD_PRINT           0x02
59
+
60
+/** DATA command
61
+ *   Can be any length between 0 and 640 bytes.
62
+ *   DATA command with lenght 0 triggers PRN_STATUS_FULL and is mandatory before print command
63
+ */
64
+#define PRN_CMD_DATA            0x04
65
+
66
+/** BREAK command
67
+ *   Not very usefull but exists (see Game Boy Programming Manual)
68
+ */
69
+#define PRN_CMD_BREAK           0x08
70
+
71
+/** STATUS command
72
+ *   Used to check status bits
73
+ *   Maybe be used alone before an INIT command to check physical connection with printer
74
+ *   Resets PRN_STATUS_UNTRAN
75
+ */
76
+#define PRN_CMD_STATUS          0x0F
77
+
78
+/** Palette format: the bits, grouped two by two, give the printing color of the encoded pixel value
79
+ *   for the default palette 0xE4 = 0b11100100 = [3 2 1 0]
80
+ *   Any value is valid, which means that 1 to 4 color images are possible
81
+ *   0x00 acts the same as 0xE4 for the printer
82
+ */
83
+#define PRN_PALETTE_NORMAL      0b11100100u
84
+#define PRN_PALETTE_INV         0b00011011u
85
+
86
+/** Don't use margins
87
+ */
88
+#define PRN_NO_MARGINS          0x00
89
+
90
+/** Exposure: 0x40 is default value, values from 0x80 to 0xFF act as 0x40
91
+ *   Determines the time used by the printer head to heat the thermal paper
92
+ */
93
+#define PRN_EXPOSURE_LIGHT      0x00
94
+#define PRN_EXPOSURE_DEFAULT    0x40
95
+#define PRN_EXPOSURE_DARK       0x7F
96
+
97
+/** Battery too low
98
+ */
99
+#define PRN_STATUS_LOWBAT       0x80
100
+
101
+/** Error not specified according to the Game Boy Programming manual
102
+ */
103
+#define PRN_STATUS_ER2          0x40
104
+
105
+/** Paper jam  (abnormal motor operation)
106
+ */
107
+#define PRN_STATUS_ER1          0x20
108
+
109
+/** Packet error (but not checksum error)
110
+ */
111
+#define PRN_STATUS_ER0          0x10
112
+
113
+/** Unprocessed data present in printer memory
114
+ *   Allows to verify that printer got some data in memory with correct checksum
115
+ *   is resetted by STATUS command
116
+ */
117
+#define PRN_STATUS_UNTRAN       0x08
118
+
119
+/** status data ready, mandatory to allow printing
120
+ *   is triggered by DATA command with lenght 0
121
+ */
122
+#define PRN_STATUS_FULL         0x04
123
+
124
+/** Message sent by the printer while physically printing
125
+ */
126
+#define PRN_STATUS_BUSY         0x02
127
+
128
+/** The received packet has a ckecksum error
129
+ */
130
+#define PRN_STATUS_SUM          0x01
131
+
132
+#define PRN_STATUS_MASK_ERRORS  0xF0
133
+#define PRN_STATUS_MASK_ANY     0xFF
134
+
135
+#define PRN_SECONDS(A)          ((A)*60)
136
+
137
+#define PRN_MAX_PROGRESS        8
138
+
139
+#define PRN_STATUS_CANCELLED    PRN_STATUS_ER2
140
+
141
+#define REINIT_SEIKO
142
+
143
+#define START_TRANSFER          0x81
144
+#define PRN_BUSY_TIMEOUT        PRN_SECONDS(2)
145
+#define PRN_COMPLETION_TIMEOUT  PRN_SECONDS(20)
146
+#define PRN_SEIKO_RESET_TIMEOUT 10
147
+
148
+#define PRN_FINAL_MARGIN        0x03
149
+
150
+typedef struct start_print_pkt_s {
151
+    uint16_t magic;
152
+    uint16_t command;
153
+    uint16_t length;
154
+    uint8_t print;
155
+    uint8_t margins;
156
+    uint8_t palette;
157
+    uint8_t exposure;
158
+    uint16_t crc;
159
+    uint16_t trail;
160
+} start_print_pkt_t;
161
+
162
+static const uint8_t PRN_PKT_INIT[] = {
163
+    PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_INIT),   PRN_LE(0), PRN_LE(0x01), PRN_LE(0)
164
+};
165
+
166
+static const uint8_t PRN_PKT_STATUS[] = {
167
+    PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_STATUS), PRN_LE(0), PRN_LE(0x0F), PRN_LE(0)
168
+};
169
+
170
+static const uint8_t PRN_PKT_EOF[] = {
171
+    PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_DATA),   PRN_LE(0), PRN_LE(0x04), PRN_LE(0)
172
+};
173
+
174
+static const uint8_t PRN_PKT_CANCEL[] = {
175
+    PRN_LE(PRN_MAGIC), PRN_LE(PRN_CMD_BREAK),  PRN_LE(0), PRN_LE(0x01), PRN_LE(0)
176
+};
177
+
178
+static start_print_pkt_t PRN_PKT_START = {
179
+    .magic = PRN_MAGIC, .command = PRN_CMD_PRINT,
180
+    .length = 4, .print = TRUE,
181
+    .margins = 0, .palette = PRN_PALETTE_NORMAL, .exposure = PRN_EXPOSURE_DARK,
182
+    .crc = 0, .trail = 0
183
+};
184
+
185
+static uint16_t printer_status;
186
+static uint8_t printer_tile_num;
187
+
188
+static inline void gbprinter_set_print_params(uint8_t margins, uint8_t palette, uint8_t exposure) {
189
+    PRN_PKT_START.crc = ((PRN_CMD_PRINT + 0x04u + 0x01u)
190
+            + (PRN_PKT_START.margins = margins)
191
+            + (PRN_PKT_START.palette = palette)
192
+            + (PRN_PKT_START.exposure = exposure));
193
+}
194
+
195
+static uint8_t printer_send_receive(uint8_t b) {
196
+    SB_REG = b;
197
+    SC_REG = START_TRANSFER;
198
+    while (SC_REG & 0x80);
199
+    return SB_REG;
200
+}
201
+
202
+static uint8_t printer_send_byte(uint8_t b) {
203
+    return (uint8_t)(printer_status = ((printer_status << 8) | printer_send_receive(b)));
204
+}
205
+
206
+static uint8_t printer_send_command(const uint8_t *command, uint8_t length) {
207
+    uint8_t index = 0;
208
+    while (index++ < length) printer_send_byte(*command++);
209
+    return ((uint8_t)(printer_status >> 8) == PRN_MAGIC_DETECT) ? (uint8_t)printer_status : PRN_STATUS_MASK_ERRORS;
210
+}
211
+
212
+#define PRINTER_SEND_COMMAND(CMD) printer_send_command((const uint8_t *)&(CMD), sizeof(CMD))
213
+
214
+static uint8_t printer_print_tile(const uint8_t *tiledata) {
215
+    static const uint8_t PRINT_TILE[] = { 0x88,0x33,0x04,0x00,0x80,0x02 };
216
+    static uint16_t printer_CRC;
217
+    if (printer_tile_num == 0) {
218
+        const uint8_t * data = PRINT_TILE;
219
+        for (uint8_t i = sizeof(PRINT_TILE); i != 0; i--) printer_send_receive(*data++);
220
+        printer_CRC = 0x04 + 0x80 + 0x02;
221
+    }
222
+    for(uint8_t i = 0x10; i != 0; i--, tiledata++) {
223
+        printer_CRC += *tiledata;
224
+        printer_send_receive(*tiledata);
225
+    }
226
+    if (++printer_tile_num == 40) {
227
+        printer_send_receive((uint8_t)printer_CRC);
228
+        printer_send_receive((uint8_t)(printer_CRC >> 8));
229
+        printer_send_receive(0x00);
230
+        printer_send_receive(0x00);
231
+        printer_CRC = printer_tile_num = 0;
232
+        return TRUE;
233
+    }
234
+    return FALSE;
235
+}
236
+
237
+static inline void printer_init(void) {
238
+    printer_tile_num = 0;
239
+    PRINTER_SEND_COMMAND(PRN_PKT_INIT);
240
+}
241
+
242
+uint8_t printer_check_cancel(void) {
243
+    key_read();
244
+    return key_pressed(J_B);
245
+}
246
+
247
+static uint8_t printer_wait(uint16_t timeout, uint8_t mask, uint8_t value) {
248
+    uint8_t error;
249
+    while (((error = PRINTER_SEND_COMMAND(PRN_PKT_STATUS)) & mask) != value) {
250
+        if (printer_check_cancel()) {
251
+            PRINTER_SEND_COMMAND(PRN_PKT_CANCEL);
252
+            return PRN_STATUS_CANCELLED;
253
+        }
254
+        if (timeout-- == 0) return PRN_STATUS_MASK_ERRORS;
255
+        if (error & PRN_STATUS_MASK_ERRORS) break;
256
+        vsync();
257
+    }
258
+    return error;
259
+}
260
+
261
+uint8_t gbprinter_detect(uint8_t delay) BANKED {
262
+    printer_init();
263
+    uint8_t r = printer_wait(delay, PRN_STATUS_MASK_ANY, PRN_STATUS_OK);
264
+#ifdef DEBUG
265
+    EMU_printf("%s: %hu\n",  __func__, (uint8_t)r);
266
+#endif // DEBUG
267
+    return r;
268
+}
269
+
270
+uint8_t gbprinter_print_image(const uint8_t *image_map, const uint8_t *image,
271
+                              int8_t pos_x, uint8_t width, uint8_t height) BANKED {
272
+    uint8_t tile_data[16];
273
+    uint8_t rows = ((height + 1) >> 1) << 1;
274
+    uint8_t pkt_count = 0;
275
+
276
+    if ((rows >> 1) == 0) return PRN_STATUS_OK;
277
+
278
+    printer_tile_num = 0;
279
+
280
+    for (uint8_t y = 0; y < rows; y++) {
281
+        for (int16_t x = 0; x < PRN_TILE_WIDTH; x++) {
282
+#ifdef DEBUG
283
+            EMU_printf("%s: %hu %i\n",  __func__, (uint8_t)y, (int16_t)x);
284
+#endif // DEBUG
285
+
286
+            // overlay the picture tile if in range
287
+            if ((y < height) && (x >= pos_x) && (x < (pos_x + width))) {
288
+                uint8_t tile = image_map[(y * width) + (x - pos_x)];
289
+                memcpy(tile_data, image + ((uint16_t)tile << 4), sizeof(tile_data));
290
+            } else {
291
+                memset(tile_data, 0, sizeof(tile_data));
292
+            }
293
+
294
+            // print the resulting tile
295
+            if (printer_print_tile(tile_data)) {
296
+                pkt_count++;
297
+
298
+                if (printer_check_cancel()) {
299
+                    PRINTER_SEND_COMMAND(PRN_PKT_CANCEL);
300
+                    return PRN_STATUS_CANCELLED;
301
+                }
302
+            }
303
+
304
+            if (pkt_count == 9) {
305
+                pkt_count = 0;
306
+                PRINTER_SEND_COMMAND(PRN_PKT_EOF);
307
+
308
+                // setup margin if last packet
309
+                gbprinter_set_print_params((y == (rows - 1)) ? PRN_FINAL_MARGIN : PRN_NO_MARGINS,
310
+                                           PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK);
311
+
312
+                PRINTER_SEND_COMMAND(PRN_PKT_START);
313
+
314
+                // query printer status
315
+                uint8_t error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY);
316
+                if (error & PRN_STATUS_MASK_ERRORS) {
317
+                    return error;
318
+                }
319
+
320
+                error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0);
321
+                if (error & PRN_STATUS_MASK_ERRORS) {
322
+                    return error;
323
+                }
324
+
325
+#ifdef REINIT_SEIKO
326
+                // reinit printer (required by Seiko?)
327
+                if (y < (rows - 1)) {
328
+                    PRINTER_SEND_COMMAND(PRN_PKT_INIT);
329
+                    error = printer_wait(PRN_SEIKO_RESET_TIMEOUT, PRN_STATUS_MASK_ANY, PRN_STATUS_OK);
330
+                    if (error) {
331
+                        return error;
332
+                    }
333
+                }
334
+#endif
335
+            }
336
+        }
337
+    }
338
+
339
+    if (pkt_count) {
340
+        PRINTER_SEND_COMMAND(PRN_PKT_EOF);
341
+
342
+        // setup printing if required
343
+        gbprinter_set_print_params(PRN_FINAL_MARGIN, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK);
344
+        PRINTER_SEND_COMMAND(PRN_PKT_START);
345
+
346
+        // query printer status
347
+        uint8_t error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY);
348
+        if (error & PRN_STATUS_MASK_ERRORS) {
349
+            return error;
350
+        }
351
+
352
+        error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0);
353
+        if (error & PRN_STATUS_MASK_ERRORS) {
354
+            return error;
355
+        }
356
+    }
357
+
358
+    return PRINTER_SEND_COMMAND(PRN_PKT_STATUS);
359
+}
360
+
361
+uint8_t gbprinter_screenshot(void) BANKED {
362
+    return gbprinter_print_image(
363
+        (const uint8_t *)0x9C00, (const uint8_t *)0x8800,
364
+        0,
365
+        DEVICE_SCREEN_WIDTH, DEVICE_SCREEN_HEIGHT);
366
+}

+ 41
- 0
src/gbprinter.h View File

@@ -0,0 +1,41 @@
1
+/*
2
+ * gbprinter.h
3
+ * Duality
4
+ *
5
+ * Based on the gbprinter example from gbdk-2020:
6
+ * https://github.com/gbdk-2020/gbdk-2020/tree/develop/gbdk-lib/examples/gb/gbprinter
7
+ *
8
+ * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
9
+ *
10
+ * This program is free software: you can redistribute it and/or modify
11
+ * it under the terms of the GNU General Public License as published by
12
+ * the Free Software Foundation, either version 3 of the License, or
13
+ * (at your option) any later version.
14
+ *
15
+ * This program is distributed in the hope that it will be useful,
16
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+ * GNU General Public License for more details.
19
+ *
20
+ * See <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+#ifndef __GBPRINTER_H_INCLUDE__
24
+#define __GBPRINTER_H_INCLUDE__
25
+
26
+#include <gbdk/platform.h>
27
+#include <stdint.h>
28
+
29
+#define PRN_STATUS_OK           0x00
30
+#define PRINTER_DETECT_TIMEOUT  10
31
+
32
+uint8_t gbprinter_detect(uint8_t delay) BANKED;
33
+uint8_t gbprinter_print_image(const uint8_t *image_map, const uint8_t *image,
34
+                              int8_t pos_x, uint8_t width, uint8_t height) BANKED;
35
+uint8_t gbprinter_screenshot(void) BANKED;
36
+uint8_t gbprinter_error(uint8_t status, char *buff);
37
+
38
+BANKREF_EXTERN(gbprinter)
39
+BANKREF_EXTERN(gbprinter_error)
40
+
41
+#endif // __GBPRINTER_H_INCLUDE__

+ 65
- 0
src/gbprinter_error.c View File

@@ -0,0 +1,65 @@
1
+/*
2
+ * gbprinter_error.c
3
+ * Duality
4
+ *
5
+ * Based on the gbprinter example from gbdk-2020:
6
+ * https://github.com/gbdk-2020/gbdk-2020/tree/develop/gbdk-lib/examples/gb/gbprinter
7
+ *
8
+ * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
9
+ *
10
+ * This program is free software: you can redistribute it and/or modify
11
+ * it under the terms of the GNU General Public License as published by
12
+ * the Free Software Foundation, either version 3 of the License, or
13
+ * (at your option) any later version.
14
+ *
15
+ * This program is distributed in the hope that it will be useful,
16
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
+ * GNU General Public License for more details.
19
+ *
20
+ * See <http://www.gnu.org/licenses/>.
21
+ */
22
+
23
+#include <gbdk/platform.h>
24
+#include <stdio.h>
25
+#include <string.h>
26
+
27
+#include "banks.h"
28
+#include "gbprinter.h"
29
+
30
+BANKREF(gbprinter_error)
31
+
32
+static const char str_lowbat[] = "battery too low";
33
+static const char str_er2[] = "unknown error";
34
+static const char str_er1[] = "paper jam";
35
+static const char str_er0[] = "packet error";
36
+static const char str_untran[] = "unprocessed";
37
+static const char str_full[] = "data full";
38
+static const char str_busy[] = "printer busy";
39
+static const char str_sum[] = "checksum error";
40
+
41
+static const char * const error_strings[8] = {
42
+    str_sum, str_busy, str_full, str_untran,
43
+    str_er0, str_er1, str_er2, str_lowbat
44
+};
45
+
46
+uint8_t gbprinter_error(uint8_t status, char *buff) NONBANKED {
47
+    if (status == PRN_STATUS_OK) {
48
+        sprintf(buff, "ok");
49
+        return 2;
50
+    }
51
+
52
+    uint8_t n = 0;
53
+    START_ROM_BANK(BANK(gbprinter_error)) {
54
+        for (uint8_t i = 0; i < 8; i++) {
55
+            if (status & (1 << i)) {
56
+                if (n != 0) {
57
+                    buff[n++] = '\n';
58
+                }
59
+                strcpy(buff + n, error_strings[i]);
60
+                n += strlen(error_strings[i]);
61
+            }
62
+        }
63
+    } END_ROM_BANK
64
+    return n;
65
+}

+ 16
- 0
src/main.c View File

@@ -38,6 +38,7 @@
38 38
 #include "timer.h"
39 39
 #include "sample.h"
40 40
 #include "window.h"
41
+#include "gbprinter.h"
41 42
 #include "main.h"
42 43
 
43 44
 uint8_t debug_menu_index = 0;
@@ -82,6 +83,21 @@ static void highscore(uint8_t is_black) NONBANKED {
82 83
 
83 84
         if (key_pressed(J_A) || key_pressed(J_B)) {
84 85
             break;
86
+        } else if (key_pressed(J_SELECT)) {
87
+            uint8_t status = gbprinter_detect(PRINTER_DETECT_TIMEOUT);
88
+            if (status == PRN_STATUS_OK) {
89
+                status = gbprinter_screenshot();
90
+            }
91
+
92
+            win_score_clear(2);
93
+            win_score_print(status);
94
+            while (1) {
95
+                key_read();
96
+                if (key_pressed(0xFF)) break;
97
+                vsync();
98
+            }
99
+
100
+            break;
85 101
         }
86 102
 
87 103
         vsync();

+ 30
- 9
src/maps.c View File

@@ -44,8 +44,8 @@ static_assert(bg_map_HEIGHT == 256, "bg_map needs to be 256x256");
44 44
 #define camera_max_y ((bg_map_mapHeight - DEVICE_SCREEN_HEIGHT) * 8)
45 45
 
46 46
 #define MAP_FLIP_NONE 0x00
47
-#define MAP_FLIP_X (0x20)// | 0x01)
48
-#define MAP_FLIP_Y (0x40)// | 0x02)
47
+#define MAP_FLIP_X (0x20 | 0x01)
48
+#define MAP_FLIP_Y (0x40 | 0x02)
49 49
 #define MAP_FLIP_XY (MAP_FLIP_X | MAP_FLIP_Y)
50 50
 
51 51
 // current unscaled ship position
@@ -162,6 +162,25 @@ static inline void set(uint8_t dst_x, uint8_t dst_y,
162 162
     } END_ROM_BANK
163 163
 }
164 164
 
165
+void map_dbg_reset(void) NONBANKED {
166
+    uint16_t camera_x = abs_x >> POS_SCALE_BG;
167
+    uint16_t camera_y = abs_y >> POS_SCALE_BG;
168
+    uint8_t map_pos_x = camera_x >> 3;
169
+    uint8_t map_pos_y = camera_y >> 3;
170
+    for (uint8_t x = 0; x < DEVICE_SCREEN_WIDTH; x++) {
171
+        for (uint8_t y = 0; y < DEVICE_SCREEN_HEIGHT; y++) {
172
+            uint8_t is_flipped_x = ((camera_x >> 3) + x) & 0x10;
173
+            uint8_t is_flipped_y = ((camera_y >> 3) + y) & 0x10;
174
+            uint8_t attr = is_flipped_y ? (is_flipped_x ? MAP_FLIP_XY : MAP_FLIP_Y)
175
+                                        : (is_flipped_x ? MAP_FLIP_X : MAP_FLIP_NONE);
176
+            set(x, y,
177
+                is_flipped_x ? bg_map_mapWidth - map_pos_x : map_pos_x,
178
+                is_flipped_y ? bg_map_mapHeight - map_pos_y : map_pos_y,
179
+                attr);
180
+        }
181
+    }
182
+}
183
+
165 184
 void map_move(int16_t delta_x, int16_t delta_y) NONBANKED {
166 185
     abs_x += delta_x;
167 186
     abs_y += delta_y;
@@ -176,10 +195,10 @@ void map_move(int16_t delta_x, int16_t delta_y) NONBANKED {
176 195
     uint8_t map_pos_x = camera_x >> 3;
177 196
     uint8_t map_pos_y = camera_y >> 3;
178 197
 
179
-    uint8_t is_flipped_x_left = (camera_x >> 4) & 0x01;
180
-    uint8_t is_flipped_x_right = ((camera_x >> 4) + DEVICE_SCREEN_WIDTH) & 0x01;
181
-    uint8_t is_flipped_y_top = (camera_y >> 4) & 0x01;
182
-    uint8_t is_flipped_y_bottom = ((camera_y >> 4) + DEVICE_SCREEN_HEIGHT) & 0x01;
198
+    uint8_t is_flipped_x_left = (camera_x >> 3) & 0x10;
199
+    uint8_t is_flipped_x_right = ((camera_x >> 3) + DEVICE_SCREEN_WIDTH) & 0x10;
200
+    uint8_t is_flipped_y_top = (camera_y >> 3) & 0x10;
201
+    uint8_t is_flipped_y_bottom = ((camera_y >> 3) + DEVICE_SCREEN_HEIGHT) & 0x10;
183 202
 
184 203
     if (map_pos_x != old_map_pos_x) {
185 204
         old_map_pos_x = map_pos_x;
@@ -197,11 +216,13 @@ void map_move(int16_t delta_x, int16_t delta_y) NONBANKED {
197 216
                         bg_map_map, bg_map_MAP_ATTRIBUTES, MAP_FLIP_NONE, bg_map_mapWidth);
198 217
             */
199 218
             for (uint8_t i = 0; i < DEVICE_SCREEN_HEIGHT; i++) {
200
-                uint8_t is_flipped_y = i & 0x01;
201
-                set(map_pos_x + DEVICE_SCREEN_WIDTH, map_pos_y,
219
+                uint8_t is_flipped_y = (map_pos_y + i) & 0x04;
220
+                uint8_t attr = is_flipped_y ? (is_flipped_x_right ? MAP_FLIP_XY : MAP_FLIP_Y)
221
+                                            : (is_flipped_x_right ? MAP_FLIP_X : MAP_FLIP_NONE);
222
+                set(map_pos_x + DEVICE_SCREEN_WIDTH, map_pos_y + i,
202 223
                     is_flipped_x_right ? bg_map_mapWidth - map_pos_x : map_pos_x,
203 224
                     is_flipped_y ? bg_map_mapHeight - map_pos_y : map_pos_y,
204
-                    is_flipped_y ? (is_flipped_x_right ? MAP_FLIP_XY : MAP_FLIP_Y) : (is_flipped_x_right ? MAP_FLIP_X : MAP_FLIP_NONE));
225
+                    attr);
205 226
             }
206 227
         }
207 228
     }

+ 2
- 0
src/maps.h View File

@@ -27,4 +27,6 @@ void map_title(void);
27 27
 void map_game(void);
28 28
 void map_move(int16_t delta_x, int16_t delta_y);
29 29
 
30
+void map_dbg_reset(void);
31
+
30 32
 #endif // __MAPS_H__

+ 1
- 0
src/score.c View File

@@ -17,6 +17,7 @@
17 17
  * See <http://www.gnu.org/licenses/>.
18 18
  */
19 19
 
20
+#include <gbdk/emu_debug.h>
20 21
 #include <string.h>
21 22
 
22 23
 #include "banks.h"

+ 5
- 0
src/sgb_border.c View File

@@ -1,3 +1,8 @@
1
+/*
2
+ * From the sgb_border example from gbdk-2020:
3
+ * https://github.com/gbdk-2020/gbdk-2020/tree/develop/gbdk-lib/examples/gb/sgb_border
4
+ */
5
+
1 6
 #include "sgb_border.h"
2 7
 
3 8
 #include <gb/gb.h>

+ 5
- 0
src/sgb_border.h View File

@@ -1,3 +1,8 @@
1
+/*
2
+ * From the sgb_border example from gbdk-2020:
3
+ * https://github.com/gbdk-2020/gbdk-2020/tree/develop/gbdk-lib/examples/gb/sgb_border
4
+ */
5
+
1 6
 #ifndef __SGB_BORDER_H_INCLUDE
2 7
 #define __SGB_BORDER_H_INCLUDE
3 8
 

+ 52
- 3
src/window.c View File

@@ -19,6 +19,7 @@
19 19
 
20 20
 #include <gbdk/platform.h>
21 21
 #include <string.h>
22
+#include <stdio.h>
22 23
 #include <assert.h>
23 24
 
24 25
 #include "banks.h"
@@ -31,6 +32,7 @@
31 32
 #include "vincent_fnt8.h"
32 33
 #include "git.h"
33 34
 #include "main.h"
35
+#include "gbprinter.h"
34 36
 #include "window.h"
35 37
 
36 38
 #define MAX_DIGITS 7
@@ -169,13 +171,17 @@ static void str(const char *s, uint8_t x_off, uint8_t y_off, uint8_t is_black) {
169 171
     str_l(s, 0xFF, x_off, y_off, is_black);
170 172
 }
171 173
 
172
-static void str_ascii(const char *s, uint8_t x_off, uint8_t y_off) {
173
-    for (uint8_t n = 0; (*s) && (n < (2 * LINE_WIDTH)); n++) {
174
+static void str_ascii_l(const char *s, uint8_t len, uint8_t x_off, uint8_t y_off) {
175
+    for (uint8_t n = 0; (*s) && (n < (2 * LINE_WIDTH)) && (n < len); n++) {
174 176
         char c = *(s++);
175 177
         char_ascii(c, n, x_off, y_off);
176 178
     }
177 179
 }
178 180
 
181
+static void str_ascii(const char *s, uint8_t x_off, uint8_t y_off) {
182
+    str_ascii_l(s, 0xFF, x_off, y_off);
183
+}
184
+
179 185
 static void str_center(const char *s, uint8_t y_off, uint8_t is_black) {
180 186
     uint8_t n = strlen(s);
181 187
     if (n > LINE_WIDTH) n = LINE_WIDTH;
@@ -191,6 +197,19 @@ static void str_lines(const char *s, uint8_t y_off, uint8_t is_black) {
191 197
     }
192 198
 }
193 199
 
200
+static void str_ascii_lines(const char *s, uint8_t y_off) {
201
+    const char *nl = s;
202
+    uint8_t lines = 0;
203
+    do {
204
+        // find next newline
205
+        while (*nl && (*nl != '\n')) nl++;
206
+        str_ascii_l(s, nl - s, 0, y_off + lines);
207
+        lines++;
208
+        if (*nl) nl++;
209
+        s = nl;
210
+    } while (*nl);
211
+}
212
+
194 213
 static uint8_t number(int32_t score, uint8_t x_off, uint8_t y_off, uint8_t is_black) {
195 214
     // TODO can not set numbers larger than int16 max?!
196 215
     //score = 32767 + 1; // wtf?!
@@ -245,7 +264,9 @@ void win_score_clear(uint8_t is_black) BANKED {
245 264
                   title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
246 265
                   title_map_map, 0, BANK(title_map), title_map_MAP_ATTRIBUTES, BANK(title_map));
247 266
 
248
-    str_center(is_black ? "black" : "white", 1, is_black);
267
+    if (is_black < 2) {
268
+        str_center(is_black ? "black" : "white", 1, is_black);
269
+    }
249 270
 }
250 271
 
251 272
 void win_score_draw(struct scores score, uint8_t off, uint8_t is_black) BANKED {
@@ -253,6 +274,34 @@ void win_score_draw(struct scores score, uint8_t off, uint8_t is_black) BANKED {
253 274
     number(is_black ? -score.score : score.score, 7, 4 + off * 3, is_black);
254 275
 }
255 276
 
277
+void win_score_print(uint8_t status) BANKED {
278
+    static char buff[128];
279
+
280
+    if (_cpu == CGB_TYPE) {
281
+        str_ascii("GB Printer", 0, 2);
282
+        str_ascii("Score Printout", 0, 3);
283
+        str_ascii("Result:", 0, 6);
284
+
285
+        if (status == PRN_STATUS_OK) {
286
+            str_ascii("success", 0, 7);
287
+        } else {
288
+            sprintf(buff, "error: %d", status);
289
+            str_ascii(buff, 0, 7);
290
+        }
291
+
292
+        gbprinter_error(status, buff);
293
+        str_ascii_lines(buff, 9);
294
+    } else {
295
+        str("printout", 0, 4, 0);
296
+        if (status == PRN_STATUS_OK) {
297
+            str("success", 0, 8, 0);
298
+        } else {
299
+            str("error", 0, 8, 1);
300
+            number(status, 11, 8, 1);
301
+        }
302
+    }
303
+}
304
+
256 305
 static void get_git(char *line_buff) NONBANKED {
257 306
     START_ROM_BANK(BANK(git)) {
258 307
         strncpy(line_buff, git_version, 2 * LINE_WIDTH);

+ 1
- 0
src/window.h View File

@@ -28,6 +28,7 @@ void win_init(uint8_t is_splash);
28 28
 void win_splash_draw(int32_t lowest, int32_t highest) BANKED;
29 29
 void win_score_clear(uint8_t is_black) BANKED;
30 30
 void win_score_draw(struct scores score, uint8_t off, uint8_t is_black) BANKED;
31
+void win_score_print(uint8_t status) BANKED;
31 32
 void win_about(void) BANKED;
32 33
 void win_conf(void) BANKED;
33 34
 void win_debug(void) BANKED;

Loading…
Cancel
Save