Implement GB Camera timing (when no webcam is connected), misc camera accuracy improvements, use GB_random instead of rand for noise_seed

This commit is contained in:
CasualPokePlayer 2022-07-22 20:15:00 -07:00
parent 1b38e8c932
commit bb8f973fb4
5 changed files with 69 additions and 13 deletions

View File

@ -3,7 +3,7 @@
static uint32_t noise_seed = 0;
/* This is not a complete emulation of the camera chip. Only the features used by the Game Boy Camera ROMs are supported.
We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */
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. */
static uint8_t generate_noise(uint8_t x, uint8_t y)
{
@ -55,10 +55,6 @@ static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
{
if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) {
/* Forbid reading the image while the camera is busy. */
return 0xFF;
}
uint8_t tile_x = addr / 0x10 % 0x10;
uint8_t tile_y = addr / 0x10 / 0x10;
@ -112,6 +108,12 @@ void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_call
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback)
{
if (gb->camera_countdown > 0 && callback) {
GB_log(gb, "Camera update request callback set while camera was proccessing, clearing camera countdown.\n");
gb->camera_countdown = 0;
GB_camera_updated(gb);
}
gb->camera_update_request_callback = callback;
}
@ -125,12 +127,25 @@ void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
addr &= 0x7F;
if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) {
value &= 0x7;
noise_seed = rand();
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) {
/* If no callback is set, ignore the write as if the camera is instantly done */
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1;
gb->camera_update_request_callback(gb);
noise_seed = GB_random();
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) {
if (gb->camera_update_request_callback) {
gb->camera_update_request_callback(gb);
}
else {
/* If no callback is set, wait the amount of time the real camera would take before clearing the busy bit */
uint16_t exposure = (gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) | gb->camera_registers[GB_CAMERA_EXPOSURE_LOW];
gb->camera_countdown = 129792 + ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x80)? 0 : 2048) + (exposure * 64) + (gb->camera_alignment & 4);
}
}
if (!(value & 1) && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) {
/* We don't support cancelling a camera shoot */
GB_log(gb, "ROM attempted to cancel camera shoot, which is currently not supported. The camera shoot will not be cancelled.\n");
value |= 1;
}
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] = value;
}
else {
if (addr >= 0x36) {

View File

@ -529,6 +529,8 @@ struct GB_gameboy_internal_s {
};
bool camera_registers_mapped;
uint8_t camera_registers[0x36];
uint8_t camera_alignment;
int32_t camera_countdown;
uint8_t rumble_strength;
bool cart_ir;
)

View File

@ -414,8 +414,15 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
return 0xFF;
}
if (gb->cartridge_type->mbc_type == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xA100 && addr < 0xAF00) {
return GB_camera_read_image(gb, addr - 0xA100);
if (gb->cartridge_type->mbc_type == GB_CAMERA) {
/* Forbid reading RAM while the camera is busy. */
if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) {
return 0;
}
if (gb->mbc_ram_bank == 0 && addr >= 0xA100 && addr < 0xAF00) {
return GB_camera_read_image(gb, addr - 0xA100);
}
}
uint8_t effective_bank = gb->mbc_ram_bank;
@ -1220,7 +1227,12 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
if (!gb->mbc_ram || !gb->mbc_ram_size) {
return;
}
if (gb->cartridge_type->mbc_type == GB_CAMERA && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1)) {
/* Forbid writing to RAM while the camera is busy. */
return;
}
uint8_t effective_bank = gb->mbc_ram_bank;
if (gb->cartridge_type->mbc_type == GB_MBC3 && !gb->is_mbc30) {
if (gb->cartridge_type->has_rtc) {

View File

@ -416,6 +416,9 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
if (gb->apu.global_enable && gb->cgb_double_speed) {
GB_log(gb, "ROM triggered an APU odd mode, which is currently not tested.\n");
}
if (gb->cartridge_type->mbc_type == GB_CAMERA && (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && !gb->cgb_double_speed) {
GB_log(gb, "ROM entered double speed mode with a camera cartridge, this could damage a real cartridge's camera.\n");
}
if (gb->cgb_double_speed) {
gb->cgb_double_speed = false;

View File

@ -365,6 +365,29 @@ static void rtc_run(GB_gameboy_t *gb, uint8_t cycles)
}
}
static void camera_run(GB_gameboy_t *gb, uint8_t cycles)
{
/* Do we have a camera? */
if (likely(gb->cartridge_type->mbc_type != GB_CAMERA)) return;
/* The camera mapper uses the PHI pin to clock itself */
/* PHI does not run in halt nor stop mode */
if (unlikely(gb->halted || gb->stopped)) return;
/* Only every other PHI is used (as the camera wants a 512KiHz clock) */
gb->camera_alignment += cycles;
/* Is the camera processing an image? */
if (likely(gb->camera_countdown == 0)) return;
gb->camera_countdown -= cycles;
if (gb->camera_countdown <= 0) {
gb->camera_countdown = 0;
GB_camera_updated(gb);
}
}
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
{
@ -389,6 +412,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
gb->dma_cycles = cycles;
timers_run(gb, cycles);
camera_run(gb, cycles);
if (unlikely(gb->speed_switch_halt_countdown)) {
gb->speed_switch_halt_countdown -= cycles;