git.y1.nz

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.