mirror of https://github.com/bsnes-emu/bsnes.git
Proper (I believe) emulation of most GameBoy Camera registers
This commit is contained in:
parent
2a84d62187
commit
dd23fffcc0
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
|
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
if (gb->camera_registers[0]) {
|
if (gb->camera_registers[GB_CAMERA_FLAGS] & 1) {
|
||||||
/* Forbid reading the image while the camera is busy. */
|
/* Forbid reading the image while the camera is busy. */
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
@ -16,19 +16,33 @@ uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
|
||||||
|
|
||||||
for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) {
|
for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) {
|
||||||
|
|
||||||
int color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x,y) ^ 0xFF : (rand() & 0xFF);
|
long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x,y) : (rand() & 0xFF);
|
||||||
|
|
||||||
/* Dither using a pattern */
|
/* Color is multiplied by the multiplier register. */
|
||||||
color += ((x + y) & 1? 21 : -21) >> (y & 1);
|
/* It is unknown what register 1 does, but changing bits 2-3 from 0x4 to 0x8 seems equivalent to adding 0x2000
|
||||||
if (color > 255) {
|
to the multiplier. Is it related to actual exposure time? */
|
||||||
color = 255;
|
unsigned long multiplier_bias = (gb->camera_registers[GB_CAMERA_UNKNOWN_FLAGS] & 0xF) * 0x800;
|
||||||
|
color = color * ((gb->camera_registers[GB_CAMERA_MULTIPLIER_HIGH] << 8) + gb->camera_registers[GB_CAMERA_MULTIPLIER_LOW] + multiplier_bias) / 0x3000;
|
||||||
|
|
||||||
|
/* The camera's registers are used as a threshold pattern, which defines the dithering */
|
||||||
|
uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START;
|
||||||
|
|
||||||
|
/* Todo: I have absolutely no reason to assume that this does not go backwards! */
|
||||||
|
if (color < gb->camera_registers[pattern_base]) {
|
||||||
|
color = 3;
|
||||||
}
|
}
|
||||||
else if (color < 0) {
|
else if (color < gb->camera_registers[pattern_base + 1]) {
|
||||||
|
color = 2;
|
||||||
|
}
|
||||||
|
else if (color < gb->camera_registers[pattern_base + 2]) {
|
||||||
|
color = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
color = 0;
|
color = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret <<= 1;
|
ret <<= 1;
|
||||||
ret |= (color >> (6 + bit)) & 1;
|
ret |= (color >> bit) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -46,22 +60,23 @@ void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_re
|
||||||
|
|
||||||
void GB_camera_updated(GB_gameboy_t *gb)
|
void GB_camera_updated(GB_gameboy_t *gb)
|
||||||
{
|
{
|
||||||
gb->camera_registers[0] = 0;
|
gb->camera_registers[GB_CAMERA_FLAGS] &= ~1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
addr &= 0x7F;
|
addr &= 0x7F;
|
||||||
if (addr == 0) {
|
if (addr == GB_CAMERA_FLAGS) {
|
||||||
if ((value & 1) && gb->camera_update_request_callback) {
|
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_FLAGS] & 1) && gb->camera_update_request_callback) {
|
||||||
/* If no callback is set, ignore the write as if the camera is instantly done */
|
/* If no callback is set, ignore the write as if the camera is instantly done */
|
||||||
gb->camera_registers[0] = 1;
|
gb->camera_registers[GB_CAMERA_FLAGS] |= 1;
|
||||||
gb->camera_update_request_callback(gb);
|
gb->camera_update_request_callback(gb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (addr >= 0x36) {
|
if (addr >= 0x36) {
|
||||||
GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value);
|
GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
/* Todo: find out what these registers do */
|
/* Todo: find out what these registers do */
|
||||||
gb->camera_registers[addr] = value;
|
gb->camera_registers[addr] = value;
|
||||||
|
@ -70,7 +85,7 @@ void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr)
|
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr)
|
||||||
{
|
{
|
||||||
if ((addr & 0x7F) == 0) {
|
if ((addr & 0x7F) == 0) {
|
||||||
return gb->camera_registers[0];
|
return gb->camera_registers[GB_CAMERA_FLAGS];
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -2,6 +2,15 @@
|
||||||
#define camera_h
|
#define camera_h
|
||||||
#include "gb.h"
|
#include "gb.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GB_CAMERA_FLAGS = 0,
|
||||||
|
GB_CAMERA_UNKNOWN_FLAGS = 1,
|
||||||
|
GB_CAMERA_MULTIPLIER_HIGH = 2,
|
||||||
|
GB_CAMERA_MULTIPLIER_LOW = 3,
|
||||||
|
GB_CAMERA_DITHERING_PATTERN_START = 6,
|
||||||
|
GB_CAMERA_DITHERING_PATTERN_END = 0x35,
|
||||||
|
};
|
||||||
|
|
||||||
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr);
|
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr);
|
||||||
|
|
||||||
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback);
|
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback);
|
||||||
|
|
Loading…
Reference in New Issue