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/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.