git.y1.nz

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.