gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit ba6eb004963dcc4fceedd36584e89113ec7e0164 parent a0cbbae203f02f63322205edaeb5973fa200d63f Author: Toxa <untoxa@mail.ru> Date: Fri, 18 Jul 2025 14:11:40 +0300 EXAMPLES: Convert GBPrinter example to cross-platform (Game Boy/Mega Duck/Game Gear) Diffstat:
9 files changed, 391 insertions(+), 257 deletions(-)
diff --git a/gbdk-lib/examples/cross-platform/gbprinter/Makefile b/gbdk-lib/examples/cross-platform/gbprinter/Makefile @@ -0,0 +1,118 @@ +# If you move this project you can change the directory +# to match your GBDK root directory (ex: GBDK_HOME = "C:/GBDK/" +ifndef GBDK_HOME + GBDK_HOME = ../../../ +endif + +LCC = $(GBDK_HOME)bin/lcc +PNG2ASSET = $(GBDK_HOME)/bin/png2asset + +# Set platforms to build here, spaced separated. (These are in the separate Makefile.targets) +# They can also be built/cleaned individually: "make gg" and "make gg-clean" +# Possible are: gb gbc pocket megaduck sms gg +TARGETS=gb gbc megaduck gg + +# Configure platform specific LCC flags here: +LCCFLAGS_gb = -Wl-yt0x1B -autobank # Set an MBC for banking (1B-ROM+MBC5+RAM+BATT) +LCCFLAGS_pocket = -Wl-yt0x1B -autobank # Usually the same as required for .gb +LCCFLAGS_duck = -Wl-yt0x1B -autobank # Usually the same as required for .gb +LCCFLAGS_gbc = -Wl-yt0x1B -Wm-yc -autobank # Same as .gb with: -Wm-yc (gb & gbc) or Wm-yC (gbc exclusive) +LCCFLAGS_sms = +LCCFLAGS_gg = +LCCFLAGS_nes = -autobank + +LCCFLAGS += $(LCCFLAGS_$(EXT)) # This adds the current platform specific LCC Flags + +LCCFLAGS += -Wl-j -Wm-yoA -Wm-ya4 -Wb-ext=.rel -Wb-v # MBC + Autobanking related flags + +# GBDK_DEBUG = ON +ifdef GBDK_DEBUG + LCCFLAGS += -debug -v +endif + + +# You can set the name of the ROM file here +PROJECTNAME = GBPrinter + +CFLAGS = -Wf-MMD -Wf-Wp-MP # Header file dependency output (-MMD) for Makefile use + per-header Phoney rules (-MP) + +# EXT?=gb # Only sets extension to default (game boy .gb) if not populated +SRCDIR = src +OBJDIR = obj/$(EXT) +RESOBJSRC = obj/$(EXT)/res +RESDIR = res +BINDIR = build/$(EXT) +MKDIRS = $(OBJDIR) $(BINDIR) $(RESOBJSRC) # See bottom of Makefile for directory auto-creation + +BINS = $(OBJDIR)/$(PROJECTNAME).$(EXT) +CSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.c))) $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.c))) +ASMSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.s))) +OBJS = $(CSOURCES:%.c=$(OBJDIR)/%.o) $(ASMSOURCES:%.s=$(OBJDIR)/%.o) + +# For png2asset: converting map source pngs -> .c -> .o +MAPPNGS = $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.png))) +MAPSRCS = $(MAPPNGS:%.png=$(RESOBJSRC)/%.c) +MAPOBJS = $(MAPSRCS:$(RESOBJSRC)/%.c=$(OBJDIR)/%.o) + +.PRECIOUS: $(MAPSRCS) + +CFLAGS += -I$(OBJDIR) + +# Builds all targets sequentially +all: $(TARGETS) + +# Use png2asset to convert the png into C formatted map data +# -c ... : Set C output file +# Convert metasprite .pngs in res/ -> .c files in obj/<platform ext>/src/ +$(RESOBJSRC)/%.c: $(RESDIR)/%.png + $(PNG2ASSET) $< -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip -c $@ + +# Compile the map pngs that were converted to .c files +# .c files in obj/<platform ext>/src/ -> .o files in obj/ +$(OBJDIR)/%.o: $(RESOBJSRC)/%.c + $(LCC) $(LCCFLAGS) $(CFLAGS) -c -o $@ $< + +# Dependencies (using output from -Wf-MMD -Wf-Wp-MP) +DEPS = $(OBJS:%.o=%.d) + +-include $(DEPS) + +# Compile .c files in "src/" to .o object files +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(LCC) $(LCCFLAGS) $(CFLAGS) -c -o $@ $< + +# Compile .c files in "res/" to .o object files +$(OBJDIR)/%.o: $(RESDIR)/%.c + $(LCC) $(LCCFLAGS) $(CFLAGS) -c -o $@ $< + +# Compile .s assembly files in "src/" to .o object files +$(OBJDIR)/%.o: $(SRCDIR)/%.s + $(LCC) $(LCCFLAGS) $(CFLAGS) -c -o $@ $< + +# If needed, compile .c files in "src/" to .s assembly files +# (not required if .c is compiled directly to .o) +$(OBJDIR)/%.s: $(SRCDIR)/%.c + $(LCC) $(LCCFLAGS) $(CFLAGS) -S -o $@ $< + +# Convert and build maps first so they're available when compiling the main sources +$(OBJS): $(MAPOBJS) + +# Link the compiled object files into a .gb ROM file +$(BINS): $(OBJS) + $(LCC) $(LCCFLAGS) $(CFLAGS) -o $(BINDIR)/$(PROJECTNAME).$(EXT) $(MAPOBJS) $(OBJS) + +clean: + @echo Cleaning + @for target in $(TARGETS); do \ + $(MAKE) $$target-clean; \ + done + +# Include available build targets +include Makefile.targets + + +# create necessary directories after Makefile is parsed but before build +# info prevents the command from being pasted into the makefile +ifneq ($(strip $(EXT)),) # Only make the directories if EXT has been set by a target +$(info $(shell mkdir -p $(MKDIRS))) +endif diff --git a/gbdk-lib/examples/cross-platform/gbprinter/Makefile.targets b/gbdk-lib/examples/cross-platform/gbprinter/Makefile.targets @@ -0,0 +1,56 @@ + +# Platform specific flags for compiling (only populate if they're both present) +ifneq ($(strip $(PORT)),) +ifneq ($(strip $(PLAT)),) +CFLAGS += -m$(PORT):$(PLAT) +endif +endif + +# Called by the individual targets below to build a ROM +build-target: $(BINS) + +clean-target: + rm -rf $(OBJDIR) + rm -rf $(BINDIR) + +gb-clean: + ${MAKE} clean-target EXT=gb +gb: + ${MAKE} build-target PORT=sm83 PLAT=gb EXT=gb + + +gbc-clean: + ${MAKE} clean-target EXT=gbc +gbc: + ${MAKE} build-target PORT=sm83 PLAT=gb EXT=gbc + + +pocket-clean: + ${MAKE} clean-target EXT=pocket +pocket: + ${MAKE} build-target PORT=sm83 PLAT=ap EXT=pocket + + +megaduck-clean: + ${MAKE} clean-target EXT=duck +megaduck: + ${MAKE} build-target PORT=sm83 PLAT=duck EXT=duck + + +sms-clean: + ${MAKE} clean-target EXT=sms +sms: + ${MAKE} build-target PORT=z80 PLAT=sms EXT=sms + + +gg-clean: + ${MAKE} clean-target EXT=gg +gg: + ${MAKE} build-target PORT=z80 PLAT=gg EXT=gg + + +nes-clean: + ${MAKE} clean-target EXT=nes +nes: + ${MAKE} build-target PORT=mos6502 PLAT=nes EXT=nes + diff --git a/gbdk-lib/examples/cross-platform/gbprinter/Readme.md b/gbdk-lib/examples/cross-platform/gbprinter/Readme.md @@ -0,0 +1,17 @@ + +GBPrinter +========= + +Prints the image onto the game boy printer. To connect the game boy printer to the +Mega Duck or the Sega Game Gear you need to solder the specific link cable adapter. + +Sega Game Gear cable pinout: + +DPC0 <--> SOUT +DPC1 <--> SIN +DPC6 <--> CLK +GND <--> GND + +Mega Duck cable pinout: + +WIP diff --git a/gbdk-lib/examples/gb/gbprinter/res/scene00001.png b/gbdk-lib/examples/cross-platform/gbprinter/res/scene00001.png Binary files differ. diff --git a/gbdk-lib/examples/cross-platform/gbprinter/src/gbprinter.c b/gbdk-lib/examples/cross-platform/gbprinter/src/gbprinter.c @@ -0,0 +1,200 @@ +#include <gbdk/platform.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> + +#include "gbprinter.h" + +#define REINIT_SEIKO + +#define START_TRANSFER 0x81 +#define PRN_BUSY_TIMEOUT PRN_SECONDS(2) +#define PRN_COMPLETION_TIMEOUT PRN_SECONDS(20) +#define PRN_SEIKO_RESET_TIMEOUT 10 + +#define PRN_FINAL_MARGIN 0x03 + +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) }; +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) }; +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) }; +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) }; + +start_print_pkt_t PRN_PKT_START = { + .magic = PRN_MAGIC, .command = PRN_CMD_PRINT, .length = 4, + .print = TRUE, .margins = 0, .palette = PRN_PALETTE_NORMAL, .exposure = PRN_EXPOSURE_DARK, + .crc = 0, .trail = 0 +}; + +static uint16_t printer_status; +static uint8_t printer_tile_num; + +#if defined(NINTENDO) +uint8_t printer_send_receive(uint8_t b) { + SB_REG = b; + SC_REG = 0x81; + while (SC_REG & 0x80); + return SB_REG; +} +#elif defined(SEGA) +// DPC0 = MISO +// DPC1 = MOSI +// DPC6 = CLK +// GND = GND +uint8_t printer_send_receive(uint8_t data) NAKED PRESERVES_REGS(h, l, iyh, iyl) { + data; + __asm + ld e, #0b00000001 + ld c, #_GG_EXT_CTL + out (c), e + ld c, #_GG_EXT_7BIT + out (c), e + ld b, #8 +1$: + rlca + rl e + rl e + + res 7, e + res 6, e + out (c), e + + ld d, b + ld b, #15 +2$: djnz 2$ + + set 6, e + out (c), e + + ld b, #8 +3$: djnz 3$ + ld b, d + + in e, (c) + rrca + srl e + rla + djnz 1$ + ret + __endasm; +} +#endif + +uint8_t printer_send_byte(uint8_t b) { + return (uint8_t)(printer_status = ((printer_status << 8) | printer_send_receive(b))); +} + +uint8_t printer_send_command(const uint8_t *command, uint8_t length) { + uint8_t index = 0; + while (index++ < length) printer_send_byte(*command++); + return ((uint8_t)(printer_status >> 8) == PRN_MAGIC_DETECT) ? (uint8_t)printer_status : PRN_STATUS_MASK_ERRORS; +} +#define PRINTER_SEND_COMMAND(CMD) printer_send_command((const uint8_t *)&(CMD), sizeof(CMD)) + +uint8_t printer_print_tile(const uint8_t *tiledata) { + static const uint8_t PRINT_TILE[] = { 0x88,0x33,0x04,0x00,0x80,0x02 }; + static uint16_t printer_CRC; + if (printer_tile_num == 0) { + const uint8_t * data = PRINT_TILE; + for (uint8_t i = sizeof(PRINT_TILE); i != 0; i--) printer_send_receive(*data++); + printer_CRC = 0x04 + 0x80 + 0x02; + } + for(uint8_t i = 0x10; i != 0; i--, tiledata++) { + printer_CRC += *tiledata; + printer_send_receive(*tiledata); + } + if (++printer_tile_num == 40) { + printer_send_receive((uint8_t)printer_CRC); + printer_send_receive((uint8_t)(printer_CRC >> 8)); + printer_send_receive(0x00); + printer_send_receive(0x00); + printer_CRC = printer_tile_num = 0; + return TRUE; + } + return FALSE; +} + +inline void printer_init(void) { + printer_tile_num = 0; + PRINTER_SEND_COMMAND(PRN_PKT_INIT); +} + +extern bool printer_check_cancel(void); + +uint8_t printer_wait(uint16_t timeout, uint8_t mask, uint8_t value) { + uint8_t error; + while (((error = PRINTER_SEND_COMMAND(PRN_PKT_STATUS)) & mask) != value) { + if (printer_check_cancel()) { + PRINTER_SEND_COMMAND(PRN_PKT_CANCEL); + return PRN_STATUS_CANCELLED; + } + if (timeout-- == 0) return PRN_STATUS_MASK_ERRORS; + if (error & PRN_STATUS_MASK_ERRORS) break; + vsync(); + } + return error; +} + +uint8_t gbprinter_detect(uint8_t delay) { + printer_init(); + return printer_wait(delay, PRN_STATUS_MASK_ANY, PRN_STATUS_OK); +} + +uint8_t gbprinter_print_image(const uint8_t * image_map, const uint8_t * image, int8_t pos_x, uint8_t width, uint8_t height) { + static const uint8_t * img; + static uint8_t error; + + uint8_t tile_data[16], rows = (((height + 1) >> 1) << 1), pkt_count = 0; + + if ((rows >> 1) == 0) return PRN_STATUS_OK; + + img = image; + + printer_tile_num = 0; + + for (uint8_t y = 0; y != rows; y++) { + for (int16_t x = 0; x != PRN_TILE_WIDTH; x++) { + // overlay the picture tile if in range + if ((y < height) && (x >= pos_x) && (x < (pos_x + width))) { + uint8_t tile = image_map[(y * width) + (x - pos_x)]; + memcpy(tile_data, img + ((uint16_t)tile << 4), sizeof(tile_data)); + } else { + memset(tile_data, 0, sizeof(tile_data)); + } + // print the resulting tile + if (printer_print_tile(tile_data)) { + pkt_count++; + if (printer_check_cancel()) { + PRINTER_SEND_COMMAND(PRN_PKT_CANCEL); + return PRN_STATUS_CANCELLED; + } + } + if (pkt_count == 9) { + pkt_count = 0; + PRINTER_SEND_COMMAND(PRN_PKT_EOF); + // setup margin if last packet + gbprinter_set_print_params((y == (rows - 1)) ? PRN_FINAL_MARGIN : PRN_NO_MARGINS, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK); + PRINTER_SEND_COMMAND(PRN_PKT_START); + // query printer status + if ((error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY)) & PRN_STATUS_MASK_ERRORS) return error; + if ((error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0)) & PRN_STATUS_MASK_ERRORS) return error; +#ifdef REINIT_SEIKO + // reinit printer (required by Seiko?) + if (y != (rows - 1)) { + PRINTER_SEND_COMMAND(PRN_PKT_INIT); + if (error = printer_wait(PRN_SEIKO_RESET_TIMEOUT, PRN_STATUS_MASK_ANY, PRN_STATUS_OK)) return error; + } +#endif + } + } + } + if (pkt_count) { + PRINTER_SEND_COMMAND(PRN_PKT_EOF); + // setup printing if required + gbprinter_set_print_params(PRN_FINAL_MARGIN, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK); + PRINTER_SEND_COMMAND(PRN_PKT_START); + // query printer status + if ((error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY)) & PRN_STATUS_MASK_ERRORS) return error; + if ((error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0)) & PRN_STATUS_MASK_ERRORS) return error; + } + return PRINTER_SEND_COMMAND(PRN_PKT_STATUS); +} diff --git a/gbdk-lib/examples/gb/gbprinter/src/gbprinter.h b/gbdk-lib/examples/cross-platform/gbprinter/src/gbprinter.h diff --git a/gbdk-lib/examples/gb/gbprinter/src/print_example.c b/gbdk-lib/examples/cross-platform/gbprinter/src/print_example.c diff --git a/gbdk-lib/examples/gb/gbprinter/Makefile b/gbdk-lib/examples/gb/gbprinter/Makefile @@ -1,101 +0,0 @@ -# -# A Makefile that compiles all .c and .s files in "src" and "res" -# subdirectories and places the output in a "obj" subdirectory -# - -# If you move this project you can change the directory -# to match your GBDK root directory (ex: GBDK_HOME = "C:/GBDK/" -ifndef GBDK_HOME - GBDK_HOME = ../../../ -endif - -LCC = $(GBDK_HOME)bin/lcc -PNG2ASSET = $(GBDK_HOME)bin/png2asset - -# GBDK_DEBUG = ON -ifdef GBDK_DEBUG - LCCFLAGS += -debug -v -endif - - -# Add directory where image gets converted into (obj/) -# So they can be included with "#include <res/somefile.h>" -LCCFLAGS += -I$(OBJDIR) - -# Make the ROM CGB compatible (but not exclusive) -# LCCFLAGS += -Wm-yc - -# You can set the name of the .gb ROM file here -PROJECTNAME = print_example - -SRCDIR = src -OBJDIR = obj -RESOBJSRC = obj/res -RESDIR = res -MKDIRS = $(OBJDIR) $(BINDIR) $(RESOBJSRC) # See bottom of Makefile for directory auto-creation - -BINS = $(OBJDIR)/$(PROJECTNAME).gb - -# For png2asset: converting source pngs -> .c -> .o -IMGPNGS = $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.png))) -IMGSOURCES = $(IMGPNGS:%.png=$(RESOBJSRC)/%.c) -IMGOBJS = $(IMGSOURCES:$(RESOBJSRC)/%.c=$(OBJDIR)/%.o) - -CSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.c))) $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.c))) -ASMSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.s))) -OBJS = $(CSOURCES:%.c=$(OBJDIR)/%.o) $(ASMSOURCES:%.s=$(OBJDIR)/%.o) - -all: $(BINS) - -compile.bat: Makefile - @echo "REM Automatically generated from Makefile" > compile.bat - @make -sn | sed y/\\//\\\\/ | sed s/mkdir\ -p\/mkdir\/ | grep -v make >> compile.bat - -# Use png2asset to convert the png into C formatted metasprite data -# -keep_duplicate_tiles : Don't remove duplicate tiles -# -map : Use "map style" output, not metasprite -# -tiles_only : Only keep tiles, no map (since APA mode expects tiles in linear order) -# -bpp 2 : Use 2bpp output -# -c ... : Set C output file -# Convert metasprite .pngs in res/ -> .c files in obj/<platform ext>/src/ -$(RESOBJSRC)/%.c: $(RESDIR)/%.png - $(PNG2ASSET) $< -keep_duplicate_tiles -map -bpp 2 -c $@ - -# Compile the pngs that were converted to .c files -# .c files in obj/res/ -> .o files in obj/ -$(OBJDIR)/%.o: $(RESOBJSRC)/%.c - $(LCC) $(LCCFLAGS) $(CFLAGS) -c -o $@ $< - -# Compile .c files in "src/" to .o object files -$(OBJDIR)/%.o: $(SRCDIR)/%.c - $(LCC) $(LCCFLAGS) -c -o $@ $< - -# Compile .c files in "res/" to .o object files -$(OBJDIR)/%.o: $(RESDIR)/%.c - $(LCC) $(LCCFLAGS) $(CFLAGS) -c -o $@ $< - -# Compile .s assembly files in "src/" to .o object files -$(OBJDIR)/%.o: $(SRCDIR)/%.s - $(LCC) $(LCCFLAGS) -c -o $@ $< - -# If needed, compile .c files in "src/" to .s assembly files -# (not required if .c is compiled directly to .o) -$(OBJDIR)/%.s: $(SRCDIR)/%.c - $(LCC) $(LCCFLAGS) -S -o $@ $< - -# Convert images first so they're available when compiling the main sources -$(OBJS): $(IMGOBJS) - -# Link the compiled object files into a .gb ROM file -$(BINS): $(OBJS) - $(LCC) $(LCCFLAGS) -o $(BINS) $(IMGOBJS) $(OBJS) - -clean: - rm -f $(OBJDIR)/*.* - rm -f $(RESOBJSRC)/*.* - - -# create necessary directories after Makefile is parsed but before build -# info prevents the command from being pasted into the makefile -$(info $(shell mkdir -p $(MKDIRS))) - diff --git a/gbdk-lib/examples/gb/gbprinter/src/gbprinter.c b/gbdk-lib/examples/gb/gbprinter/src/gbprinter.c @@ -1,156 +0,0 @@ -#include <gbdk/platform.h> -#include <stdint.h> -#include <string.h> -#include <stdbool.h> - -#include "gbprinter.h" - -#define REINIT_SEIKO - -#define START_TRANSFER 0x81 -#define PRN_BUSY_TIMEOUT PRN_SECONDS(2) -#define PRN_COMPLETION_TIMEOUT PRN_SECONDS(20) -#define PRN_SEIKO_RESET_TIMEOUT 10 - -#define PRN_FINAL_MARGIN 0x03 - -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) }; -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) }; -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) }; -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) }; - -start_print_pkt_t PRN_PKT_START = { - .magic = PRN_MAGIC, .command = PRN_CMD_PRINT, .length = 4, - .print = TRUE, .margins = 0, .palette = PRN_PALETTE_NORMAL, .exposure = PRN_EXPOSURE_DARK, - .crc = 0, .trail = 0 -}; - -static uint16_t printer_status; -static uint8_t printer_tile_num; - -uint8_t printer_send_receive(uint8_t b) { - SB_REG = b; - SC_REG = START_TRANSFER; - while (SC_REG & 0x80); - return SB_REG; -} - -uint8_t printer_send_byte(uint8_t b) { - return (uint8_t)(printer_status = ((printer_status << 8) | printer_send_receive(b))); -} - -uint8_t printer_send_command(const uint8_t *command, uint8_t length) { - uint8_t index = 0; - while (index++ < length) printer_send_byte(*command++); - return ((uint8_t)(printer_status >> 8) == PRN_MAGIC_DETECT) ? (uint8_t)printer_status : PRN_STATUS_MASK_ERRORS; -} -#define PRINTER_SEND_COMMAND(CMD) printer_send_command((const uint8_t *)&(CMD), sizeof(CMD)) - -uint8_t printer_print_tile(const uint8_t *tiledata) { - static const uint8_t PRINT_TILE[] = { 0x88,0x33,0x04,0x00,0x80,0x02 }; - static uint16_t printer_CRC; - if (printer_tile_num == 0) { - const uint8_t * data = PRINT_TILE; - for (uint8_t i = sizeof(PRINT_TILE); i != 0; i--) printer_send_receive(*data++); - printer_CRC = 0x04 + 0x80 + 0x02; - } - for(uint8_t i = 0x10; i != 0; i--, tiledata++) { - printer_CRC += *tiledata; - printer_send_receive(*tiledata); - } - if (++printer_tile_num == 40) { - printer_send_receive((uint8_t)printer_CRC); - printer_send_receive((uint8_t)(printer_CRC >> 8)); - printer_send_receive(0x00); - printer_send_receive(0x00); - printer_CRC = printer_tile_num = 0; - return TRUE; - } - return FALSE; -} - -inline void printer_init(void) { - printer_tile_num = 0; - PRINTER_SEND_COMMAND(PRN_PKT_INIT); -} - -extern bool printer_check_cancel(void); - -uint8_t printer_wait(uint16_t timeout, uint8_t mask, uint8_t value) { - uint8_t error; - while (((error = PRINTER_SEND_COMMAND(PRN_PKT_STATUS)) & mask) != value) { - if (printer_check_cancel()) { - PRINTER_SEND_COMMAND(PRN_PKT_CANCEL); - return PRN_STATUS_CANCELLED; - } - if (timeout-- == 0) return PRN_STATUS_MASK_ERRORS; - if (error & PRN_STATUS_MASK_ERRORS) break; - vsync(); - } - return error; -} - -uint8_t gbprinter_detect(uint8_t delay) { - printer_init(); - return printer_wait(delay, PRN_STATUS_MASK_ANY, PRN_STATUS_OK); -} - -uint8_t gbprinter_print_image(const uint8_t * image_map, const uint8_t * image, int8_t pos_x, uint8_t width, uint8_t height) { - static const uint8_t * img; - static uint8_t error; - - uint8_t tile_data[16], rows = (((height + 1) >> 1) << 1), pkt_count = 0; - - if ((rows >> 1) == 0) return PRN_STATUS_OK; - - img = image; - - printer_tile_num = 0; - - for (uint8_t y = 0; y != rows; y++) { - for (int16_t x = 0; x != PRN_TILE_WIDTH; x++) { - // overlay the picture tile if in range - if ((y < height) && (x >= pos_x) && (x < (pos_x + width))) { - uint8_t tile = image_map[(y * width) + (x - pos_x)]; - memcpy(tile_data, img + ((uint16_t)tile << 4), sizeof(tile_data)); - } else { - memset(tile_data, 0, sizeof(tile_data)); - } - // print the resulting tile - if (printer_print_tile(tile_data)) { - pkt_count++; - if (printer_check_cancel()) { - PRINTER_SEND_COMMAND(PRN_PKT_CANCEL); - return PRN_STATUS_CANCELLED; - } - } - if (pkt_count == 9) { - pkt_count = 0; - PRINTER_SEND_COMMAND(PRN_PKT_EOF); - // setup margin if last packet - gbprinter_set_print_params((y == (rows - 1)) ? PRN_FINAL_MARGIN : PRN_NO_MARGINS, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK); - PRINTER_SEND_COMMAND(PRN_PKT_START); - // query printer status - if ((error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY)) & PRN_STATUS_MASK_ERRORS) return error; - if ((error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0)) & PRN_STATUS_MASK_ERRORS) return error; -#ifdef REINIT_SEIKO - // reinit printer (required by Seiko?) - if (y != (rows - 1)) { - PRINTER_SEND_COMMAND(PRN_PKT_INIT); - if (error = printer_wait(PRN_SEIKO_RESET_TIMEOUT, PRN_STATUS_MASK_ANY, PRN_STATUS_OK)) return error; - } -#endif - } - } - } - if (pkt_count) { - PRINTER_SEND_COMMAND(PRN_PKT_EOF); - // setup printing if required - gbprinter_set_print_params(PRN_FINAL_MARGIN, PRN_PALETTE_NORMAL, PRN_EXPOSURE_DARK); - PRINTER_SEND_COMMAND(PRN_PKT_START); - // query printer status - if ((error = printer_wait(PRN_BUSY_TIMEOUT, PRN_STATUS_BUSY, PRN_STATUS_BUSY)) & PRN_STATUS_MASK_ERRORS) return error; - if ((error = printer_wait(PRN_COMPLETION_TIMEOUT, PRN_STATUS_BUSY, 0)) & PRN_STATUS_MASK_ERRORS) return error; - } - return PRINTER_SEND_COMMAND(PRN_PKT_STATUS); -}
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.