git.y1.nz

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.