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.