git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

Core/workboy.c

      1 #include "gb.h"
      2 #include <time.h>
      3 #include <string.h>
      4 
      5 static inline uint8_t int_to_bcd(uint8_t i)
      6 {
      7     return (i % 10) + ((i / 10) << 4);
      8 }
      9 
     10 static inline uint8_t bcd_to_int(uint8_t i)
     11 {
     12     return (i & 0xF) + (i >> 4) * 10;
     13 }
     14 
     15 /*
     16     Note: This peripheral was never released. This is a hacky software reimplementation of it that allows
     17           reaccessing all of the features present in Workboy's ROM. Some of the implementation details are
     18           obviously wrong, but without access to the actual hardware, this is the best I can do.
     19 */
     20 
     21 static void serial_start(GB_gameboy_t *gb, bool bit_received)
     22 {
     23     gb->workboy.byte_being_received <<= 1;
     24     gb->workboy.byte_being_received |= bit_received;
     25     gb->workboy.bits_received++;
     26     if (gb->workboy.bits_received == 8) {
     27         gb->workboy.byte_to_send = 0;
     28         if (gb->workboy.mode != 'W' && gb->workboy.byte_being_received == 'R') {
     29             gb->workboy.byte_to_send = 'D';
     30             gb->workboy.key = GB_WORKBOY_NONE;
     31             gb->workboy.mode = gb->workboy.byte_being_received;
     32             gb->workboy.buffer_index = 1;
     33             
     34             time_t time = gb->workboy_get_time_callback(gb);
     35             struct tm tm;
     36             tm = *localtime(&time);
     37             memset(gb->workboy.buffer, 0, sizeof(gb->workboy.buffer));
     38             
     39             gb->workboy.buffer[0] = 4; // Unknown, unused, but appears to be expected to be 4
     40             gb->workboy.buffer[2] = int_to_bcd(tm.tm_sec); // Seconds, BCD
     41             gb->workboy.buffer[3] = int_to_bcd(tm.tm_min); // Minutes, BCD
     42             gb->workboy.buffer[4] = int_to_bcd(tm.tm_hour); // Hours, BCD
     43             gb->workboy.buffer[5] = int_to_bcd(tm.tm_mday); // Days, BCD. Upper most 2 bits are added to Year for some reason
     44             gb->workboy.buffer[6] = int_to_bcd(tm.tm_mon + 1); // Months, BCD
     45             gb->workboy.buffer[0xF] = tm.tm_year; // Years, plain number, since 1900
     46 
     47         }
     48         else if (gb->workboy.mode != 'W' && gb->workboy.byte_being_received == 'W') {
     49             gb->workboy.byte_to_send = 'D'; // It is actually unknown what this value should be
     50             gb->workboy.key = GB_WORKBOY_NONE;
     51             gb->workboy.mode = gb->workboy.byte_being_received;
     52             gb->workboy.buffer_index = 0;
     53         }
     54         else if (gb->workboy.mode != 'W' && (gb->workboy.byte_being_received == 'O' || gb->workboy.mode == 'O')) {
     55             gb->workboy.mode = 'O';
     56             gb->workboy.byte_to_send = gb->workboy.key;
     57             if (gb->workboy.key != GB_WORKBOY_NONE) {
     58                 if (gb->workboy.key & GB_WORKBOY_REQUIRE_SHIFT) {
     59                     gb->workboy.key &= ~GB_WORKBOY_REQUIRE_SHIFT;
     60                     if (gb->workboy.shift_down) {
     61                         gb->workboy.byte_to_send = gb->workboy.key;
     62                         gb->workboy.key = GB_WORKBOY_NONE;
     63                     }
     64                     else {
     65                         gb->workboy.byte_to_send = GB_WORKBOY_SHIFT_DOWN;
     66                         gb->workboy.shift_down = true;
     67                     }
     68                 }
     69                 else if (gb->workboy.key & GB_WORKBOY_FORBID_SHIFT) {
     70                     gb->workboy.key &= ~GB_WORKBOY_FORBID_SHIFT;
     71                     if (!gb->workboy.shift_down) {
     72                         gb->workboy.byte_to_send = gb->workboy.key;
     73                         gb->workboy.key = GB_WORKBOY_NONE;
     74                     }
     75                     else {
     76                         gb->workboy.byte_to_send = GB_WORKBOY_SHIFT_UP;
     77                         gb->workboy.shift_down = false;
     78                     }
     79                 }
     80                 else {
     81                     if (gb->workboy.key == GB_WORKBOY_SHIFT_DOWN) {
     82                         gb->workboy.shift_down = true;
     83                         gb->workboy.user_shift_down = true;
     84                     }
     85                     else if (gb->workboy.key == GB_WORKBOY_SHIFT_UP) {
     86                         gb->workboy.shift_down = false;
     87                         gb->workboy.user_shift_down = false;
     88                     }
     89                     gb->workboy.byte_to_send = gb->workboy.key;
     90                     gb->workboy.key = GB_WORKBOY_NONE;
     91                 }
     92             }
     93         }
     94         else if (gb->workboy.mode == 'R') {
     95             if (gb->workboy.buffer_index / 2  >= sizeof(gb->workboy.buffer)) {
     96                 gb->workboy.byte_to_send = 0;
     97             }
     98             else {
     99                 if (gb->workboy.buffer_index & 1) {
    100                     gb->workboy.byte_to_send = "0123456789ABCDEF"[gb->workboy.buffer[gb->workboy.buffer_index / 2] & 0xF];
    101                 }
    102                 else {
    103                     gb->workboy.byte_to_send = "0123456789ABCDEF"[gb->workboy.buffer[gb->workboy.buffer_index / 2] >> 4];
    104                 }
    105                 gb->workboy.buffer_index++;
    106             }
    107         }
    108         else if (gb->workboy.mode == 'W') {
    109             gb->workboy.byte_to_send = 'D';
    110             if (gb->workboy.buffer_index < 2) {
    111                 gb->workboy.buffer_index++;
    112             }
    113             else if ((gb->workboy.buffer_index - 2) < sizeof(gb->workboy.buffer)) {
    114                 gb->workboy.buffer[gb->workboy.buffer_index - 2] = gb->workboy.byte_being_received;
    115                 gb->workboy.buffer_index++;
    116                 if (gb->workboy.buffer_index - 2 == sizeof(gb->workboy.buffer)) {
    117                     struct tm tm = {0,};
    118                     tm.tm_sec = bcd_to_int(gb->workboy.buffer[7]);
    119                     tm.tm_min = bcd_to_int(gb->workboy.buffer[8]);
    120                     tm.tm_hour = bcd_to_int(gb->workboy.buffer[9]);
    121                     tm.tm_mday = bcd_to_int(gb->workboy.buffer[0xA]);
    122                     tm.tm_mon = bcd_to_int(gb->workboy.buffer[0xB] & 0x3F) - 1;
    123                     tm.tm_year = (uint8_t)(gb->workboy.buffer[0x14] + (gb->workboy.buffer[0xA] >> 6)); // What were they thinking?
    124                     gb->workboy_set_time_callback(gb, mktime(&tm));
    125                     gb->workboy.mode = 'O';
    126                 }
    127             }
    128         }
    129         gb->workboy.bits_received = 0;
    130         gb->workboy.byte_being_received = 0;
    131     }
    132 }
    133 
    134 static bool serial_end(GB_gameboy_t *gb)
    135 {
    136     bool ret = gb->workboy.bit_to_send;
    137     gb->workboy.bit_to_send = gb->workboy.byte_to_send & 0x80;
    138     gb->workboy.byte_to_send <<= 1;
    139     return ret;
    140 }
    141 
    142 void GB_connect_workboy(GB_gameboy_t *gb,
    143                         GB_workboy_set_time_callback_t set_time_callback,
    144                         GB_workboy_get_time_callback_t get_time_callback)
    145 {
    146     GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
    147     memset(&gb->workboy, 0, sizeof(gb->workboy));
    148     GB_set_serial_transfer_bit_start_callback(gb, serial_start);
    149     GB_set_serial_transfer_bit_end_callback(gb, serial_end);
    150     gb->workboy_set_time_callback = set_time_callback;
    151     gb->workboy_get_time_callback = get_time_callback;
    152     gb->accessory = GB_ACCESSORY_WORKBOY;
    153 }
    154 
    155 bool GB_workboy_is_enabled(GB_gameboy_t *gb)
    156 {
    157     return gb->accessory == GB_ACCESSORY_WORKBOY && gb->workboy.mode;
    158 }
    159 
    160 void GB_workboy_set_key(GB_gameboy_t *gb, uint8_t key)
    161 {
    162     if (gb->accessory != GB_ACCESSORY_WORKBOY) return;
    163     
    164     if (gb->workboy.user_shift_down != gb->workboy.shift_down &&
    165         (key & (GB_WORKBOY_REQUIRE_SHIFT | GB_WORKBOY_FORBID_SHIFT)) == 0) {
    166         if (gb->workboy.user_shift_down) {
    167             key |= GB_WORKBOY_REQUIRE_SHIFT;
    168         }
    169         else {
    170             key |= GB_WORKBOY_FORBID_SHIFT;
    171         }
    172     }
    173     gb->workboy.key = key;
    174 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.