SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
Core/camera.c
1 #include "gb.h"
2
3 static uint32_t noise_seed = 0;
4
5 /* This is not a complete emulation of the camera chip. Only the features used by the Game Boy Camera ROMs are supported.
6 We also do not emulate the timing of the real cart when a webcam is used, as it might be actually faster than the webcam. */
7
8 static uint8_t generate_noise(uint8_t x, uint8_t y)
9 {
10 uint32_t value = (x * 151 + y * 149) ^ noise_seed;
11 uint32_t hash = 0;
12
13 while (value) {
14 hash <<= 1;
15 if (hash & 0x100) {
16 hash ^= 0x101;
17 }
18 if (value & 0x80000000) {
19 hash ^= 0xA1;
20 }
21 value <<= 1;
22 }
23 return hash;
24 }
25
26 static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
27 {
28 if (x == 128) {
29 x = 127;
30 }
31 else if (x > 128) {
32 x = 0;
33 }
34
35 if (y == 112) {
36 y = 111;
37 }
38 else if (y >= 112) {
39 y = 0;
40 }
41
42 long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (generate_noise(x, y));
43
44 static const double gain_values[] =
45 {0.8809390, 0.9149149, 0.9457498, 0.9739758,
46 1.0000000, 1.0241412, 1.0466537, 1.0677433,
47 1.0875793, 1.1240310, 1.1568911, 1.1868043,
48 1.2142561, 1.2396208, 1.2743837, 1.3157323,
49 1.3525190, 1.3856512, 1.4157897, 1.4434309,
50 1.4689574, 1.4926697, 1.5148087, 1.5355703,
51 1.5551159, 1.5735801, 1.5910762, 1.6077008,
52 1.6235366, 1.6386550, 1.6531183, 1.6669808};
53 /* Multiply color by gain value */
54 color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F];
55
56
57 /* Color is multiplied by the exposure register to simulate exposure. */
58 color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000;
59
60 return color;
61 }
62
63 uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
64 {
65 uint8_t tile_x = addr / 0x10 % 0x10;
66 uint8_t tile_y = addr / 0x10 / 0x10;
67
68 uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8;
69 uint8_t bit = addr & 1;
70
71 uint8_t ret = 0;
72
73 for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) {
74
75 long color = get_processed_color(gb, x, y);
76
77 static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5};
78 double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7];
79 if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) {
80 color += (color * 4) * edge_enhancement_ratio;
81 color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio;
82 color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio;
83 color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio;
84 color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio;
85 }
86
87
88 /* The camera's registers are used as a threshold pattern, which defines the dithering */
89 uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START;
90
91 if (color < gb->camera_registers[pattern_base]) {
92 color = 3;
93 }
94 else if (color < gb->camera_registers[pattern_base + 1]) {
95 color = 2;
96 }
97 else if (color < gb->camera_registers[pattern_base + 2]) {
98 color = 1;
99 }
100 else {
101 color = 0;
102 }
103
104 ret <<= 1;
105 ret |= (color >> bit) & 1;
106 }
107
108 return ret;
109 }
110
111 void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback)
112 {
113 if (!callback) {
114 GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
115 }
116 gb->camera_get_pixel_callback = callback;
117 }
118
119 void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback)
120 {
121 if (!callback) {
122 GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
123 }
124 if (gb->camera_countdown > 0 && callback) {
125 GB_log(gb, "Camera update request callback set while camera was proccessing, clearing camera countdown.\n");
126 gb->camera_countdown = 0;
127 GB_camera_updated(gb);
128 }
129
130 gb->camera_update_request_callback = callback;
131 }
132
133 void GB_camera_updated(GB_gameboy_t *gb)
134 {
135 gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] &= ~1;
136 }
137
138 void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
139 {
140 addr &= 0x7F;
141 if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) {
142 value &= 0x7;
143 noise_seed = GB_random();
144 if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) {
145 if (gb->camera_update_request_callback) {
146 gb->camera_update_request_callback(gb);
147 }
148 else {
149 /* If no callback is set, wait the amount of time the real camera would take before clearing the busy bit */
150 uint16_t exposure = (gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) | gb->camera_registers[GB_CAMERA_EXPOSURE_LOW];
151 gb->camera_countdown = 129792 + ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x80)? 0 : 2048) + (exposure * 64) + (gb->camera_alignment & 4);
152 }
153 }
154
155 if (!(value & 1) && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) {
156 /* We don't support cancelling a camera shoot */
157 GB_log(gb, "ROM attempted to cancel camera shoot, which is currently not supported. The camera shoot will not be cancelled.\n");
158 value |= 1;
159 }
160
161 gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] = value;
162 }
163 else {
164 if (addr >= 0x36) {
165 GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value);
166 return;
167 }
168 gb->camera_registers[addr] = value;
169 }
170 }
171 uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr)
172 {
173 if ((addr & 0x7F) == 0) {
174 return gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS];
175 }
176 return 0;
177 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.