SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
Core/sgb.c
1 #include "gb.h"
2 #include "random.h"
3 #include <math.h>
4 #include <assert.h>
5 #include <string.h>
6
7 #ifndef M_PI
8 #define M_PI 3.14159265358979323846
9 #endif
10
11 enum {
12 PAL01 = 0x00,
13 PAL23 = 0x01,
14 PAL03 = 0x02,
15 PAL12 = 0x03,
16 ATTR_BLK = 0x04,
17 ATTR_LIN = 0x05,
18 ATTR_DIV = 0x06,
19 ATTR_CHR = 0x07,
20 PAL_SET = 0x0A,
21 PAL_TRN = 0x0B,
22 DATA_SND = 0x0F,
23 MLT_REQ = 0x11,
24 CHR_TRN = 0x13,
25 PCT_TRN = 0x14,
26 ATTR_TRN = 0x15,
27 ATTR_SET = 0x16,
28 MASK_EN = 0x17,
29 };
30
31 typedef enum {
32 MASK_DISABLED,
33 MASK_FREEZE,
34 MASK_BLACK,
35 MASK_COLOR_0,
36 } mask_mode_t;
37
38 typedef enum {
39 TRANSFER_LOW_TILES,
40 TRANSFER_HIGH_TILES,
41 TRANSFER_BORDER_DATA,
42 TRANSFER_PALETTES,
43 TRANSFER_ATTRIBUTES,
44 } transfer_dest_t;
45
46 #define SGB_PACKET_SIZE 16
47 static inline void pal_command(GB_gameboy_t *gb, unsigned first, unsigned second)
48 {
49 gb->sgb->effective_palettes[0] = gb->sgb->effective_palettes[4] =
50 gb->sgb->effective_palettes[8] = gb->sgb->effective_palettes[12] =
51 *(uint16_t *)&gb->sgb->command[1];
52
53 for (unsigned i = 0; i < 3; i++) {
54 gb->sgb->effective_palettes[first * 4 + i + 1] = *(uint16_t *)&gb->sgb->command[3 + i * 2];
55 }
56
57 for (unsigned i = 0; i < 3; i++) {
58 gb->sgb->effective_palettes[second * 4 + i + 1] = *(uint16_t *)&gb->sgb->command[9 + i * 2];
59 }
60 }
61
62 static inline void load_attribute_file(GB_gameboy_t *gb, unsigned file_index)
63 {
64 if (file_index > 0x2C) return;
65 uint8_t *output = gb->sgb->attribute_map;
66 for (unsigned i = 0; i < 90; i++) {
67 uint8_t byte = gb->sgb->attribute_files[file_index * 90 + i];
68 for (unsigned j = 4; j--;) {
69 *(output++) = byte >> 6;
70 byte <<= 2;
71 }
72 }
73 }
74
75 static const uint16_t built_in_palettes[] =
76 {
77 0x67BF, 0x265B, 0x10B5, 0x2866,
78 0x637B, 0x3AD9, 0x0956, 0x0000,
79 0x7F1F, 0x2A7D, 0x30F3, 0x4CE7,
80 0x57FF, 0x2618, 0x001F, 0x006A,
81 0x5B7F, 0x3F0F, 0x222D, 0x10EB,
82 0x7FBB, 0x2A3C, 0x0015, 0x0900,
83 0x2800, 0x7680, 0x01EF, 0x2FFF,
84 0x73BF, 0x46FF, 0x0110, 0x0066,
85 0x533E, 0x2638, 0x01E5, 0x0000,
86 0x7FFF, 0x2BBF, 0x00DF, 0x2C0A,
87 0x7F1F, 0x463D, 0x74CF, 0x4CA5,
88 0x53FF, 0x03E0, 0x00DF, 0x2800,
89 0x433F, 0x72D2, 0x3045, 0x0822,
90 0x7FFA, 0x2A5F, 0x0014, 0x0003,
91 0x1EED, 0x215C, 0x42FC, 0x0060,
92 0x7FFF, 0x5EF7, 0x39CE, 0x0000,
93 0x4F5F, 0x630E, 0x159F, 0x3126,
94 0x637B, 0x121C, 0x0140, 0x0840,
95 0x66BC, 0x3FFF, 0x7EE0, 0x2C84,
96 0x5FFE, 0x3EBC, 0x0321, 0x0000,
97 0x63FF, 0x36DC, 0x11F6, 0x392A,
98 0x65EF, 0x7DBF, 0x035F, 0x2108,
99 0x2B6C, 0x7FFF, 0x1CD9, 0x0007,
100 0x53FC, 0x1F2F, 0x0E29, 0x0061,
101 0x36BE, 0x7EAF, 0x681A, 0x3C00,
102 0x7BBE, 0x329D, 0x1DE8, 0x0423,
103 0x739F, 0x6A9B, 0x7293, 0x0001,
104 0x5FFF, 0x6732, 0x3DA9, 0x2481,
105 0x577F, 0x3EBC, 0x456F, 0x1880,
106 0x6B57, 0x6E1B, 0x5010, 0x0007,
107 0x0F96, 0x2C97, 0x0045, 0x3200,
108 0x67FF, 0x2F17, 0x2230, 0x1548,
109 };
110
111 static const struct {
112 char name[16];
113 unsigned palette_index;
114 } palette_assignments[] =
115 {
116 {"ZELDA", 5},
117 {"SUPER MARIOLAND", 6},
118 {"MARIOLAND2", 0x14},
119 {"SUPERMARIOLAND3", 2},
120 {"KIRBY DREAM LAND", 0xB},
121 {"HOSHINOKA-BI", 0xB},
122 {"KIRBY'S PINBALL", 3},
123 {"YOSSY NO TAMAGO", 0xC},
124 {"MARIO & YOSHI", 0xC},
125 {"YOSSY NO COOKIE", 4},
126 {"YOSHI'S COOKIE", 4},
127 {"DR.MARIO", 0x12},
128 {"TETRIS", 0x11},
129 {"YAKUMAN", 0x13},
130 {"METROID2", 0x1F},
131 {"KAERUNOTAMENI", 9},
132 {"GOLF", 0x18},
133 {"ALLEY WAY", 0x16},
134 {"BASEBALL", 0xF},
135 {"TENNIS", 0x17},
136 {"F1RACE", 0x1E},
137 {"KID ICARUS", 0xE},
138 {"QIX", 0x19},
139 {"SOLARSTRIKER", 7},
140 {"X", 0x1C},
141 {"GBWARS", 0x15},
142 };
143
144 static void command_ready(GB_gameboy_t *gb)
145 {
146 /* SGB header commands are used to send the contents of the header to the SNES CPU.
147 A header command looks like this:
148 Command ID: 0b1111xxx1, where xxx is the packet index. (e.g. F1 for [0x104, 0x112), F3 for [0x112, 0x120))
149 Checksum: Simple one byte sum for the following content bytes
150 0xE content bytes. The last command, FB, is padded with zeros, so information past the header is not sent. */
151
152 if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
153 if (gb->boot_rom_finished) return;
154
155 uint8_t checksum = 0;
156 for (unsigned i = 2; i < 0x10; i++) {
157 checksum += gb->sgb->command[i];
158 }
159 if (checksum != gb->sgb->command[1]) {
160 GB_log(gb, "Failed checksum for SGB header command, disabling SGB features\n");
161 gb->sgb->disable_commands = true;
162 return;
163 }
164 unsigned index = (gb->sgb->command[0] >> 1) & 7;
165 if (index > 5) {
166 return;
167 }
168 memcpy(&gb->sgb->received_header[index * 14], &gb->sgb->command[2], 14);
169 if (gb->sgb->command[0] == 0xFB) {
170 if (gb->sgb->received_header[0x42] != 3 || gb->sgb->received_header[0x47] != 0x33) {
171 gb->sgb->disable_commands = true;
172 nounroll for (unsigned i = 0; i < sizeof(palette_assignments) / sizeof(palette_assignments[0]); i++) {
173 if (memcmp(palette_assignments[i].name, &gb->sgb->received_header[0x30], sizeof(palette_assignments[i].name)) == 0) {
174 gb->sgb->effective_palettes[0] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 - 4]);
175 gb->sgb->effective_palettes[1] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 + 1 - 4]);
176 gb->sgb->effective_palettes[2] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 + 2 - 4]);
177 gb->sgb->effective_palettes[3] = LE16(built_in_palettes[palette_assignments[i].palette_index * 4 + 3 - 4]);
178 break;
179 }
180 }
181 }
182 }
183 return;
184 }
185
186 /* Ignore malformed commands (0 length)*/
187 if ((gb->sgb->command[0] & 7) == 0) return;
188
189 switch (gb->sgb->command[0] >> 3) {
190 case PAL01:
191 pal_command(gb, 0, 1);
192 break;
193 case PAL23:
194 pal_command(gb, 2, 3);
195 break;
196 case PAL03:
197 pal_command(gb, 0, 3);
198 break;
199 case PAL12:
200 pal_command(gb, 1, 2);
201 break;
202 case ATTR_BLK: {
203 struct {
204 uint8_t count;
205 struct {
206 uint8_t control;
207 uint8_t palettes;
208 uint8_t left, top, right, bottom;
209 } data[];
210 } *command = (void *)(gb->sgb->command + 1);
211 if (command->count > 0x12) return;
212
213 for (unsigned i = 0; i < command->count; i++) {
214 bool inside = command->data[i].control & 1;
215 bool middle = command->data[i].control & 2;
216 bool outside = command->data[i].control & 4;
217 uint8_t inside_palette = command->data[i].palettes & 0x3;
218 uint8_t middle_palette = (command->data[i].palettes >> 2) & 0x3;
219 uint8_t outside_palette = (command->data[i].palettes >> 4) & 0x3;
220
221 if (inside && !middle && !outside) {
222 middle = true;
223 middle_palette = inside_palette;
224 }
225 else if (outside && !middle && !inside) {
226 middle = true;
227 middle_palette = outside_palette;
228 }
229
230 command->data[i].left &= 0x1F;
231 command->data[i].top &= 0x1F;
232 command->data[i].right &= 0x1F;
233 command->data[i].bottom &= 0x1F;
234
235 for (unsigned y = 0; y < 18; y++) {
236 for (unsigned x = 0; x < 20; x++) {
237 if (x < command->data[i].left || x > command->data[i].right ||
238 y < command->data[i].top || y > command->data[i].bottom) {
239 if (outside) {
240 gb->sgb->attribute_map[x + 20 * y] = outside_palette;
241 }
242 }
243 else if (x > command->data[i].left && x < command->data[i].right &&
244 y > command->data[i].top && y < command->data[i].bottom) {
245 if (inside) {
246 gb->sgb->attribute_map[x + 20 * y] = inside_palette;
247 }
248 }
249 else if (middle) {
250 gb->sgb->attribute_map[x + 20 * y] = middle_palette;
251 }
252 }
253 }
254 }
255 break;
256 }
257 case ATTR_CHR: {
258 struct __attribute__((packed)) {
259 uint8_t x, y;
260 uint16_t length;
261 uint8_t direction;
262 uint8_t data[];
263 } *command = (void *)(gb->sgb->command + 1);
264
265 uint16_t count = command->length;
266 count = LE16(count);
267 uint8_t x = command->x;
268 uint8_t y = command->y;
269 if (x >= 20 || y >= 18) {
270 /* TODO: Verify with the SFC BIOS */
271 break;
272 }
273
274 for (unsigned i = 0; i < count; i++) {
275 uint8_t palette = (command->data[i / 4] >> (((~i) & 3) << 1)) & 3;
276 gb->sgb->attribute_map[x + 20 * y] = palette;
277 if (command->direction) {
278 y++;
279 if (y == 18) {
280 x++;
281 y = 0;
282 if (x == 20) {
283 break;
284 }
285 }
286 }
287 else {
288 x++;
289 if (x == 20) {
290 y++;
291 x = 0;
292 if (y == 18) {
293 break;
294 }
295 }
296 }
297 }
298
299 break;
300 }
301 case ATTR_LIN: {
302 struct {
303 uint8_t count;
304 uint8_t data[];
305 } *command = (void *)(gb->sgb->command + 1);
306 if (command->count > sizeof(gb->sgb->command) - 2) return;
307
308 for (unsigned i = 0; i < command->count; i++) {
309 bool horizontal = command->data[i] & 0x80;
310 uint8_t palette = (command->data[i] >> 5) & 0x3;
311 uint8_t line = (command->data[i]) & 0x1F;
312
313 if (horizontal) {
314 if (line > 18) continue;
315 for (unsigned x = 0; x < 20; x++) {
316 gb->sgb->attribute_map[x + 20 * line] = palette;
317 }
318 }
319 else {
320 if (line > 20) continue;
321 for (unsigned y = 0; y < 18; y++) {
322 gb->sgb->attribute_map[line + 20 * y] = palette;
323 }
324 }
325 }
326 break;
327 }
328 case ATTR_DIV: {
329 uint8_t high_palette = gb->sgb->command[1] & 3;
330 uint8_t low_palette = (gb->sgb->command[1] >> 2) & 3;
331 uint8_t middle_palette = (gb->sgb->command[1] >> 4) & 3;
332 bool horizontal = gb->sgb->command[1] & 0x40;
333 uint8_t line = gb->sgb->command[2] & 0x1F;
334
335 for (unsigned y = 0; y < 18; y++) {
336 for (unsigned x = 0; x < 20; x++) {
337 if ((horizontal? y : x) < line) {
338 gb->sgb->attribute_map[x + 20 * y] = low_palette;
339 }
340 else if ((horizontal? y : x) == line) {
341 gb->sgb->attribute_map[x + 20 * y] = middle_palette;
342 }
343 else {
344 gb->sgb->attribute_map[x + 20 * y] = high_palette;
345 }
346 }
347 }
348
349 break;
350 }
351 case PAL_SET:
352 memcpy(&gb->sgb->effective_palettes[0],
353 &gb->sgb->ram_palettes[4 * (gb->sgb->command[1] + (gb->sgb->command[2] & 1) * 0x100)],
354 8);
355 memcpy(&gb->sgb->effective_palettes[4],
356 &gb->sgb->ram_palettes[4 * (gb->sgb->command[3] + (gb->sgb->command[4] & 1) * 0x100)],
357 8);
358 memcpy(&gb->sgb->effective_palettes[8],
359 &gb->sgb->ram_palettes[4 * (gb->sgb->command[5] + (gb->sgb->command[6] & 1) * 0x100)],
360 8);
361 memcpy(&gb->sgb->effective_palettes[12],
362 &gb->sgb->ram_palettes[4 * (gb->sgb->command[7] + (gb->sgb->command[8] & 1) * 0x100)],
363 8);
364
365 gb->sgb->effective_palettes[12] = gb->sgb->effective_palettes[8] =
366 gb->sgb->effective_palettes[4] = gb->sgb->effective_palettes[0];
367
368 if (gb->sgb->command[9] & 0x80) {
369 load_attribute_file(gb, gb->sgb->command[9] & 0x3F);
370 }
371
372 if (gb->sgb->command[9] & 0x40) {
373 gb->sgb->mask_mode = MASK_DISABLED;
374 }
375 break;
376 case PAL_TRN:
377 gb->sgb->vram_transfer_countdown = 3;
378 gb->sgb->transfer_dest = TRANSFER_PALETTES;
379 break;
380 case DATA_SND:
381 // Not supported, but used by almost all SGB games for hot patching, so let's mute the warning for this
382 break;
383 case MLT_REQ:
384 gb->sgb->player_count = (gb->sgb->command[1] & 3) + 1; /* Todo: When breaking save state comaptibility,
385 fix this to be 0 based. */
386 if (gb->sgb->player_count == 3) {
387 gb->sgb->player_count++;
388 }
389 gb->sgb->current_player &= (gb->sgb->player_count - 1);
390 break;
391 case CHR_TRN:
392 gb->sgb->vram_transfer_countdown = 3;
393 gb->sgb->transfer_dest = (gb->sgb->command[1] & 1)? TRANSFER_HIGH_TILES : TRANSFER_LOW_TILES;
394 break;
395 case PCT_TRN:
396 gb->sgb->vram_transfer_countdown = 3;
397 gb->sgb->transfer_dest = TRANSFER_BORDER_DATA;
398 break;
399 case ATTR_TRN:
400 gb->sgb->vram_transfer_countdown = 3;
401 gb->sgb->transfer_dest = TRANSFER_ATTRIBUTES;
402 break;
403 case ATTR_SET:
404 load_attribute_file(gb, gb->sgb->command[1] & 0x3F);
405
406 if (gb->sgb->command[1] & 0x40) {
407 gb->sgb->mask_mode = MASK_DISABLED;
408 }
409 break;
410 case MASK_EN:
411 gb->sgb->mask_mode = gb->sgb->command[1] & 3;
412 break;
413 default:
414 if ((gb->sgb->command[0] >> 3) == 8 &&
415 (gb->sgb->command[1] & ~0x80) == 0 &&
416 (gb->sgb->command[2] & ~0x80) == 0) {
417 /* Mute/dummy sound commands, ignore this command as it's used by many games at startup */
418 break;
419 }
420 GB_log(gb, "Unimplemented SGB command %x: ", gb->sgb->command[0] >> 3);
421 for (unsigned i = 0; i < gb->sgb->command_write_index / 8; i++) {
422 GB_log(gb, "%02x ", gb->sgb->command[i]);
423 }
424 GB_log(gb, "\n");
425 }
426 }
427
428 void GB_sgb_write(GB_gameboy_t *gb, uint8_t value)
429 {
430 if (!GB_is_sgb(gb)) return;
431 if (!GB_is_hle_sgb(gb)) {
432 /* Notify via callback */
433 return;
434 }
435 if (gb->sgb->disable_commands) return;
436
437 uint16_t command_size = (gb->sgb->command[0] & 7 ?: 1) * SGB_PACKET_SIZE * 8;
438 if ((gb->sgb->command[0] & 0xF1) == 0xF1) {
439 command_size = SGB_PACKET_SIZE * 8;
440 }
441
442 if ((value & 0x20) != 0 && (gb->io_registers[GB_IO_JOYP] & 0x20) == 0) {
443 if ((gb->sgb->player_count & 1) == 0) {
444 gb->sgb->current_player++;
445 gb->sgb->current_player &= (gb->sgb->player_count - 1);
446 }
447 }
448
449 switch ((value >> 4) & 3) {
450 case 3:
451 gb->sgb->ready_for_pulse = true;
452 break;
453
454 case 2: // Zero
455 if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
456 if (gb->sgb->ready_for_stop) {
457 if (gb->sgb->command_write_index == command_size) {
458 command_ready(gb);
459 gb->sgb->command_write_index = 0;
460 memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
461 }
462 gb->sgb->ready_for_pulse = false;
463 gb->sgb->ready_for_write = false;
464 gb->sgb->ready_for_stop = false;
465 }
466 else {
467 if (gb->sgb->command_write_index < sizeof(gb->sgb->command) * 8) {
468 gb->sgb->command_write_index++;
469 gb->sgb->ready_for_pulse = false;
470 if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
471 gb->sgb->ready_for_stop = true;
472 }
473 }
474 }
475 break;
476 case 1: // One
477 if (!gb->sgb->ready_for_pulse || !gb->sgb->ready_for_write) return;
478 if (gb->sgb->ready_for_stop) {
479 GB_log(gb, "Corrupt SGB command.\n");
480 gb->sgb->ready_for_pulse = false;
481 gb->sgb->ready_for_write = false;
482 gb->sgb->command_write_index = 0;
483 memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
484 }
485 else {
486 if (gb->sgb->command_write_index < sizeof(gb->sgb->command) * 8) {
487 gb->sgb->command[gb->sgb->command_write_index / 8] |= 1 << (gb->sgb->command_write_index & 7);
488 gb->sgb->command_write_index++;
489 gb->sgb->ready_for_pulse = false;
490 if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) == 0) {
491 gb->sgb->ready_for_stop = true;
492 }
493 }
494 }
495 break;
496
497 case 0:
498 if (!gb->sgb->ready_for_pulse) return;
499 gb->sgb->ready_for_write = true;
500 gb->sgb->ready_for_pulse = false;
501 if (((gb->sgb->command_write_index) & (SGB_PACKET_SIZE * 8 - 1)) != 0 ||
502 gb->sgb->command_write_index == 0 ||
503 gb->sgb->ready_for_stop) {
504 gb->sgb->command_write_index = 0;
505 memset(gb->sgb->command, 0, sizeof(gb->sgb->command));
506 gb->sgb->ready_for_stop = false;
507 }
508 break;
509
510 default:
511 break;
512 }
513 }
514
515 static uint32_t convert_rgb15(GB_gameboy_t *gb, uint16_t color)
516 {
517 return GB_convert_rgb15(gb, color, false);
518 }
519
520 static uint32_t convert_rgb15_with_fade(GB_gameboy_t *gb, uint16_t color, uint8_t fade)
521 {
522 uint8_t r = ((color) & 0x1F) - fade;
523 uint8_t g = ((color >> 5) & 0x1F) - fade;
524 uint8_t b = ((color >> 10) & 0x1F) - fade;
525
526 if (r >= 0x20) r = 0;
527 if (g >= 0x20) g = 0;
528 if (b >= 0x20) b = 0;
529
530 color = r | (g << 5) | (b << 10);
531
532 return GB_convert_rgb15(gb, color, false);
533 }
534
535 static void render_boot_animation (GB_gameboy_t *gb)
536 {
537 #include "graphics/sgb_animation_logo.inc"
538 uint32_t *output = gb->screen;
539 if (gb->border_mode != GB_BORDER_NEVER) {
540 output += 48 + 40 * 256;
541 }
542 uint8_t *input = animation_logo;
543 unsigned fade_blue = 0;
544 unsigned fade_red = 0;
545 if (gb->sgb->intro_animation < 80 - 32) {
546 fade_blue = 32;
547 }
548 else if (gb->sgb->intro_animation < 80) {
549 fade_blue = 80 - gb->sgb->intro_animation;
550 }
551 else if (gb->sgb->intro_animation > GB_SGB_INTRO_ANIMATION_LENGTH - 32) {
552 fade_red = fade_blue = gb->sgb->intro_animation - GB_SGB_INTRO_ANIMATION_LENGTH + 32;
553 }
554 uint32_t colors[] = {
555 convert_rgb15(gb, 0),
556 convert_rgb15_with_fade(gb, 0x14A5, fade_blue),
557 convert_rgb15_with_fade(gb, 0x54E0, fade_blue),
558 convert_rgb15_with_fade(gb, 0x0019, fade_red),
559 convert_rgb15(gb, 0x0011),
560 convert_rgb15(gb, 0x0009),
561 };
562 unsigned y_min = (144 - animation_logo_height) / 2;
563 unsigned y_max = y_min + animation_logo_height;
564 for (unsigned y = 0; y < 144; y++) {
565 for (unsigned x = 0; x < 160; x++) {
566 if (y < y_min || y >= y_max) {
567 *(output++) = colors[0];
568 }
569 else {
570 uint8_t color = *input;
571 if (color >= 3) {
572 if (color == gb->sgb->intro_animation / 2 - 3) {
573 color = 5;
574 }
575 else if (color == gb->sgb->intro_animation / 2 - 4) {
576 color = 4;
577 }
578 else if (color < gb->sgb->intro_animation / 2 - 4) {
579 color = 3;
580 }
581 else {
582 color = 0;
583 }
584 }
585 *(output++) = colors[color];
586 input++;
587 }
588 }
589 if (gb->border_mode != GB_BORDER_NEVER) {
590 output += 256 - 160;
591 }
592 }
593 }
594
595 static void render_jingle(GB_gameboy_t *gb, size_t count);
596 void GB_sgb_render(GB_gameboy_t *gb, bool incomplete)
597 {
598 if (gb->apu_output.sample_rate) {
599 render_jingle(gb, gb->apu_output.sample_rate / GB_get_usual_frame_rate(gb));
600 }
601
602 if (gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) gb->sgb->intro_animation++;
603
604 if (gb->sgb->vram_transfer_countdown) {
605 if (--gb->sgb->vram_transfer_countdown == 0) {
606 unsigned size = 0;
607 uint16_t *data = NULL;
608
609 switch (gb->sgb->transfer_dest) {
610 case TRANSFER_LOW_TILES:
611 size = 0x100;
612 data = (uint16_t *)gb->sgb->pending_border.tiles;
613 break;
614 case TRANSFER_HIGH_TILES:
615 size = 0x100;
616 data = (uint16_t *)gb->sgb->pending_border.tiles + 0x800;
617 break;
618 case TRANSFER_PALETTES:
619 size = 0x100;
620 data = gb->sgb->ram_palettes;
621 break;
622 case TRANSFER_BORDER_DATA:
623 size = 0x88;
624 data = gb->sgb->pending_border.raw_data;
625 break;
626 case TRANSFER_ATTRIBUTES:
627 size = 0xFE;
628 data = (uint16_t *)gb->sgb->attribute_files;
629 break;
630 default:
631 return; // Corrupt state?
632 }
633
634 for (unsigned tile = 0; tile < size; tile++) {
635 unsigned tile_x = (tile % 20) * 8;
636 unsigned tile_y = (tile / 20) * 8;
637 for (unsigned y = 0; y < 0x8; y++) {
638 static const uint16_t pixel_to_bits[4] = {0x0000, 0x0080, 0x8000, 0x8080};
639 *data = 0;
640 for (unsigned x = 0; x < 8; x++) {
641 *data |= pixel_to_bits[gb->sgb->screen_buffer[(tile_x + x) + (tile_y + y) * 160] & 3] >> x;
642 }
643 if (gb->sgb->transfer_dest == TRANSFER_PALETTES || gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
644 *data = LE16(*data);
645 }
646 data++;
647 }
648 }
649 if (gb->sgb->transfer_dest == TRANSFER_BORDER_DATA) {
650 gb->sgb->border_animation = 105; // Measured on an SGB2, but might be off by ±2
651 }
652 }
653 }
654
655 if (!gb->screen || !gb->rgb_encode_callback || gb->disable_rendering) {
656 if (gb->sgb->border_animation > 32) {
657 gb->sgb->border_animation--;
658 }
659 else if (gb->sgb->border_animation != 0) {
660 gb->sgb->border_animation--;
661 }
662 if (gb->sgb->border_animation == 32) {
663 memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border));
664 }
665 return;
666 }
667
668 uint32_t colors[4 * 4];
669 for (unsigned i = 0; i < 4 * 4; i++) {
670 colors[i] = convert_rgb15(gb, LE16(gb->sgb->effective_palettes[i]));
671 }
672
673 if (gb->sgb->mask_mode != MASK_FREEZE && !incomplete) {
674 memcpy(gb->sgb->effective_screen_buffer,
675 gb->sgb->screen_buffer,
676 sizeof(gb->sgb->effective_screen_buffer));
677 }
678
679 if (gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) {
680 render_boot_animation(gb);
681 }
682 else {
683 uint32_t *output = gb->screen;
684 if (gb->border_mode != GB_BORDER_NEVER) {
685 output += 48 + 40 * 256;
686 }
687 uint8_t *input = gb->sgb->effective_screen_buffer;
688 switch ((mask_mode_t) gb->sgb->mask_mode) {
689 case MASK_DISABLED:
690 case MASK_FREEZE: {
691 for (unsigned y = 0; y < 144; y++) {
692 for (unsigned x = 0; x < 160; x++) {
693 uint8_t palette = gb->sgb->attribute_map[x / 8 + y / 8 * 20] & 3;
694 *(output++) = colors[(*(input++) & 3) + palette * 4];
695 }
696 if (gb->border_mode != GB_BORDER_NEVER) {
697 output += 256 - 160;
698 }
699 }
700 break;
701 }
702 case MASK_BLACK:
703 {
704 uint32_t black = convert_rgb15(gb, 0);
705 for (unsigned y = 0; y < 144; y++) {
706 for (unsigned x = 0; x < 160; x++) {
707 *(output++) = black;
708 }
709 if (gb->border_mode != GB_BORDER_NEVER) {
710 output += 256 - 160;
711 }
712 }
713 break;
714 }
715 case MASK_COLOR_0:
716 {
717 for (unsigned y = 0; y < 144; y++) {
718 for (unsigned x = 0; x < 160; x++) {
719 *(output++) = colors[0];
720 }
721 if (gb->border_mode != GB_BORDER_NEVER) {
722 output += 256 - 160;
723 }
724 }
725 break;
726 }
727 }
728 }
729
730 uint32_t border_colors[16 * 4];
731 if (gb->sgb->border_animation == 0 || gb->sgb->border_animation > 64 || gb->sgb->intro_animation < GB_SGB_INTRO_ANIMATION_LENGTH) {
732 if (gb->sgb->border_animation != 0) {
733 gb->sgb->border_animation--;
734 }
735 for (unsigned i = 0; i < 16 * 4; i++) {
736 border_colors[i] = convert_rgb15(gb, LE16(gb->sgb->border.palette[i]));
737 }
738 }
739 else if (gb->sgb->border_animation > 32) {
740 gb->sgb->border_animation--;
741 for (unsigned i = 0; i < 16 * 4; i++) {
742 border_colors[i] = convert_rgb15_with_fade(gb, LE16(gb->sgb->border.palette[i]), 64 - gb->sgb->border_animation);
743 }
744 }
745 else {
746 gb->sgb->border_animation--;
747 for (unsigned i = 0; i < 16 * 4; i++) {
748 border_colors[i] = convert_rgb15_with_fade(gb, LE16(gb->sgb->border.palette[i]), gb->sgb->border_animation);
749 }
750 }
751
752
753 if (gb->sgb->border_animation == 32) {
754 memcpy(&gb->sgb->border, &gb->sgb->pending_border, sizeof(gb->sgb->border));
755 }
756
757 for (unsigned tile_y = 0; tile_y < 28; tile_y++) {
758 for (unsigned tile_x = 0; tile_x < 32; tile_x++) {
759 bool gb_area = false;
760 if (tile_x >= 6 && tile_x < 26 && tile_y >= 5 && tile_y < 23) {
761 gb_area = true;
762 }
763 else if (gb->border_mode == GB_BORDER_NEVER) {
764 continue;
765 }
766 uint16_t tile = LE16(gb->sgb->border.map[tile_x + tile_y * 32]);
767 if (tile & 0x300) continue; // Unused tile
768 uint8_t flip_x = (tile & 0x4000)? 0:7;
769 uint8_t flip_y = (tile & 0x8000)? 7:0;
770 uint8_t palette = (tile >> 10) & 3;
771 for (unsigned y = 0; y < 8; y++) {
772 unsigned base = (tile & 0xFF) * 32 + (y ^ flip_y) * 2;
773 for (unsigned x = 0; x < 8; x++) {
774 uint8_t bit = 1 << (x ^ flip_x);
775 uint8_t color = ((gb->sgb->border.tiles[base] & bit) ? 1: 0) |
776 ((gb->sgb->border.tiles[base + 1] & bit) ? 2: 0) |
777 ((gb->sgb->border.tiles[base + 16] & bit) ? 4: 0) |
778 ((gb->sgb->border.tiles[base + 17] & bit) ? 8: 0);
779
780 uint32_t *output = gb->screen;
781 if (gb->border_mode == GB_BORDER_NEVER) {
782 output += (tile_x - 6) * 8 + x + ((tile_y - 5) * 8 + y) * 160;
783 }
784 else {
785 output += tile_x * 8 + x + (tile_y * 8 + y) * 256;
786 }
787 if (color == 0) {
788 if (gb_area) continue;
789 *output = colors[0];
790 }
791 else {
792 *output = border_colors[color + palette * 16];
793 }
794 }
795 }
796 }
797 }
798 }
799
800 void GB_sgb_load_default_data(GB_gameboy_t *gb)
801 {
802
803 #include "graphics/sgb_border.inc"
804
805 #ifdef GB_BIG_ENDIAN
806 for (unsigned i = 0; i < sizeof(tilemap) / 2; i++) {
807 gb->sgb->border.map[i] = LE16(tilemap[i]);
808 }
809 for (unsigned i = 0; i < sizeof(palette) / 2; i++) {
810 gb->sgb->border.palette[i] = LE16(palette[i]);
811 }
812 #else
813 memcpy(gb->sgb->border.map, tilemap, sizeof(tilemap));
814 memcpy(gb->sgb->border.palette, palette, sizeof(palette));
815 #endif
816 memcpy(gb->sgb->border.tiles, tiles, sizeof(tiles));
817
818 if (gb->model != GB_MODEL_SGB2) {
819 /* Delete the "2" */
820 gb->sgb->border.map[25 * 32 + 25] = gb->sgb->border.map[25 * 32 + 26] =
821 gb->sgb->border.map[26 * 32 + 25] = gb->sgb->border.map[26 * 32 + 26] =
822 gb->sgb->border.map[27 * 32 + 25] = gb->sgb->border.map[27 * 32 + 26] =
823 gb->sgb->border.map[0];
824
825 /* Re-center */
826 memmove(&gb->sgb->border.map[25 * 32 + 1], &gb->sgb->border.map[25 * 32], (32 * 3 - 1) * sizeof(gb->sgb->border.map[0]));
827 }
828 gb->sgb->effective_palettes[0] = LE16(built_in_palettes[0]);
829 gb->sgb->effective_palettes[1] = LE16(built_in_palettes[1]);
830 gb->sgb->effective_palettes[2] = LE16(built_in_palettes[2]);
831 gb->sgb->effective_palettes[3] = LE16(built_in_palettes[3]);
832 }
833
834 static double fm_sin(double phase)
835 {
836 #define SIN_TABLE_LENGTH 128
837 phase /= 2 * M_PI;
838 phase = fabs(phase);
839 phase -= floor(phase);
840 if (phase > 0.5) {
841 return -(fm_sin(1 - phase));
842 }
843 if (phase > 0.25) {
844 return fm_sin(0.5 - phase);
845 }
846
847 static bool once = false;
848 static double table[SIN_TABLE_LENGTH + 1];
849 if (!once) {
850 for (unsigned i = 0; i < SIN_TABLE_LENGTH + 1; i++) {
851 table[i] = sin(i * M_PI / 2 / SIN_TABLE_LENGTH);
852 }
853 once = true;
854 }
855
856 phase *= 4 * SIN_TABLE_LENGTH;
857 double fraction = phase - floor(phase);
858 return table[(unsigned)floor(phase)] * (1 - fraction) + table[(unsigned)ceil(phase)] * (fraction);
859 }
860
861 static double fm_synth(double phase)
862 {
863 return (fm_sin(phase * M_PI * 2) +
864 fm_sin(phase * M_PI * 2 + fm_sin(phase * M_PI * 2)) +
865 fm_sin(phase * M_PI * 2 + fm_sin(phase * M_PI * 3)) +
866 fm_sin(phase * M_PI * 2 + fm_sin(phase * M_PI * 4))) / 4;
867 }
868
869 static double fm_sweep(double phase)
870 {
871 double ret = 0;
872 for (unsigned i = 0; i < 8; i++) {
873 ret += fm_sin((phase * M_PI * 2 + fm_sin(phase * M_PI * 8) / 4) * pow(1.25, i)) * (8 - i) / 36;
874 }
875 return ret;
876 }
877 static double random_double(void)
878 {
879 return ((signed)(GB_random32() % 0x10001) - 0x8000) / (double) 0x8000;
880 }
881
882 static void render_jingle(GB_gameboy_t *gb, size_t count)
883 {
884 const double frequencies[7] = {
885 466.16, // Bb4
886 587.33, // D5
887 698.46, // F5
888 830.61, // Ab5
889 1046.50, // C6
890 1244.51, // Eb6
891 1567.98, // G6
892 };
893
894 assert(gb->apu_output.sample_callback);
895
896 if (gb->sgb->intro_animation < 0) {
897 GB_sample_t sample = {0, 0};
898 for (unsigned i = 0; i < count; i++) {
899 gb->apu_output.sample_callback(gb, &sample);
900 }
901 return;
902 }
903
904 if (gb->sgb->intro_animation >= GB_SGB_INTRO_ANIMATION_LENGTH) return;
905
906 signed jingle_stage = (gb->sgb->intro_animation - 64) / 3;
907 double sweep_cutoff_ratio = 2000.0 * pow(2, gb->sgb->intro_animation / 20.0) / gb->apu_output.sample_rate;
908 double sweep_phase_shift = 1000.0 * pow(2, gb->sgb->intro_animation / 40.0) / gb->apu_output.sample_rate;
909 if (sweep_cutoff_ratio > 1) {
910 sweep_cutoff_ratio = 1;
911 }
912
913 // Render at a lower resolution if our sample rate is too high
914 uint8_t downsample_mask = 0;
915 size_t temp_count = count;
916 while (temp_count > 2048) {
917 temp_count /= 2;
918 downsample_mask <<= 1;
919 downsample_mask |= 1;
920 sweep_phase_shift *= 2;
921 sweep_cutoff_ratio *= 2;
922 }
923
924 GB_sample_t stereo = {0, 0};
925 for (unsigned i = 0; i < count; i++) {
926 if ((i & downsample_mask)) {
927 gb->apu_output.sample_callback(gb, &stereo);
928 continue;
929 }
930 double sample = 0;
931 for (signed f = 0; f < 7 && f < jingle_stage; f++) {
932 sample += fm_synth(gb->sgb_intro_jingle_phases[f]) *
933 (0.75 * pow(0.5, jingle_stage - f) + 0.25) / 5.0;
934 gb->sgb_intro_jingle_phases[f] += (frequencies[f] / gb->apu_output.sample_rate) * (downsample_mask + 1);
935 }
936 if (gb->sgb->intro_animation > 100) {
937 sample *= pow((GB_SGB_INTRO_ANIMATION_LENGTH - gb->sgb->intro_animation) / (GB_SGB_INTRO_ANIMATION_LENGTH - 100.0), 3);
938 }
939
940 if (gb->sgb->intro_animation < 120) {
941 double next = fm_sweep(gb->sgb_intro_sweep_phase) * 0.3 + random_double() * 0.7;
942 gb->sgb_intro_sweep_phase += sweep_phase_shift;
943
944 gb->sgb_intro_sweep_previous_sample = next * (sweep_cutoff_ratio) +
945 gb->sgb_intro_sweep_previous_sample * (1 - sweep_cutoff_ratio);
946 sample += gb->sgb_intro_sweep_previous_sample * pow((120 - gb->sgb->intro_animation) / 120.0, 2) * 0.8;
947 }
948
949 stereo.left = stereo.right = sample * 0x7000;
950 gb->apu_output.sample_callback(gb, &stereo);
951 }
952
953 return;
954 }
955
956 unsigned GB_get_player_count(GB_gameboy_t *gb)
957 {
958 return GB_is_hle_sgb(gb)? gb->sgb->player_count : 1;
959 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.