Components not affected by CGB’s double speed mode now operate in 8MHz mode to theoretically make advance_cycles(gb, 1) safe.

This commit is contained in:
Lior Halphon 2018-02-20 21:17:12 +02:00
parent d0202a3f9a
commit 9802ca41dd
8 changed files with 46 additions and 44 deletions

View File

@ -385,7 +385,7 @@ void GB_apu_run(GB_gameboy_t *gb)
if (gb->apu_output.sample_rate) { if (gb->apu_output.sample_rate) {
gb->apu_output.cycles_since_render += cycles; gb->apu_output.cycles_since_render += cycles;
double cycles_per_sample = GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; double cycles_per_sample = 2 * GB_get_clock_rate(gb) / (double)gb->apu_output.sample_rate; /* 2 * because we use 8MHz units */
if (gb->apu_output.sample_cycles > cycles_per_sample) { if (gb->apu_output.sample_cycles > cycles_per_sample) {
gb->apu_output.sample_cycles -= cycles_per_sample; gb->apu_output.sample_cycles -= cycles_per_sample;

View File

@ -127,7 +127,7 @@ typedef struct {
volatile bool copy_in_progress; volatile bool copy_in_progress;
volatile bool lock; volatile bool lock;
double sample_cycles; double sample_cycles; // In 8 MHz units
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage! // Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
unsigned cycles_since_render; unsigned cycles_since_render;

View File

@ -1411,8 +1411,8 @@ static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
GB_log(gb, " LYC interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 64)? "Enabled" : "Disabled"); GB_log(gb, " LYC interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 64)? "Enabled" : "Disabled");
GB_log(gb, "\nCycles since frame start: %d\n", gb->display_cycles); GB_log(gb, "\nCycles since frame start: %d\n", gb->display_cycles / 2);
GB_log(gb, "Current line: %d\n", gb->display_cycles / 456); GB_log(gb, "Current line: %d\n", gb->display_cycles / 456 / 2);
GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]); GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]);
GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]); GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]);
GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7 , gb->io_registers[GB_IO_WY]); GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7 , gb->io_registers[GB_IO_WY]);

View File

@ -15,13 +15,15 @@
Todo: Mode lengths are not constants, see http://blog.kevtris.org/blogfiles/Nitty%20Gritty%20Gameboy%20VRAM%20Timing.txt Todo: Mode lengths are not constants, see http://blog.kevtris.org/blogfiles/Nitty%20Gritty%20Gameboy%20VRAM%20Timing.txt
*/ */
#define MODE2_LENGTH (80) /* The display (logically) runs in 8MHz units, so we double our length constants */
#define MODE3_LENGTH (172) #define MODE2_LENGTH (80 * 2)
#define MODE0_LENGTH (204) #define MODE3_LENGTH (172 * 2)
#define MODE0_LENGTH (204 * 2)
#define LINE_LENGTH (MODE2_LENGTH + MODE3_LENGTH + MODE0_LENGTH) // = 456 #define LINE_LENGTH (MODE2_LENGTH + MODE3_LENGTH + MODE0_LENGTH) // = 456
#define LINES (144) #define LINES (144)
#define WIDTH (160) #define WIDTH (160)
#define VIRTUAL_LINES (LCDC_PERIOD / LINE_LENGTH) // = 154 #define FRAME_LENGTH (LCDC_PERIOD * 2)
#define VIRTUAL_LINES (FRAME_LENGTH / LINE_LENGTH) // = 154
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint8_t y; uint8_t y;
@ -332,9 +334,9 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
/* Keep sending vblanks to user even if the screen is off */ /* Keep sending vblanks to user even if the screen is off */
gb->display_cycles += cycles; gb->display_cycles += cycles;
if (gb->display_cycles >= LCDC_PERIOD) { if (gb->display_cycles >= FRAME_LENGTH) {
/* VBlank! */ /* VBlank! */
gb->display_cycles -= LCDC_PERIOD; gb->display_cycles -= FRAME_LENGTH;
display_vblank(gb); display_vblank(gb);
} }
@ -344,23 +346,23 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
return; return;
} }
uint8_t atomic_increase = gb->cgb_double_speed? 2 : 4; uint8_t atomic_increase = gb->cgb_double_speed? 4 : 8;
/* According to AntonioND's docs this value should be 0 in CGB mode, but tests I ran on my CGB seem to contradict /* According to AntonioND's docs this value should be 0 in CGB mode, but tests I ran on my CGB seem to contradict
these findings. these findings.
Todo: Investigate what causes the difference between our findings */ Todo: Investigate what causes the difference between our findings */
uint8_t stat_delay = gb->cgb_double_speed? 2 : 4; // (gb->cgb_mode? 0 : 4); uint8_t stat_delay = gb->cgb_double_speed? 4 : 8; // (gb->cgb_mode? 0 : 8);
/* Todo: Is this correct for DMG mode CGB? */ /* Todo: Is this correct for DMG mode CGB? */
uint8_t scx_delay = gb->effective_scx & 7; uint8_t scx_delay = (gb->effective_scx & 7) * 2;
if (gb->cgb_double_speed) { if (gb->cgb_double_speed) {
scx_delay = (scx_delay + 1) & ~1; scx_delay = (scx_delay + 2) & ~3;
} }
else { else {
scx_delay = (scx_delay + (gb->first_scanline ? 2 : 0)) & ~3; scx_delay = (scx_delay + (gb->first_scanline ? 4 : 0)) & ~7;
} }
/* Todo: These are correct for DMG, DMG-mode CGB, and single speed CGB. Is is correct for double speed CGB? */ /* Todo: These are correct for DMG, DMG-mode CGB, and single speed CGB. Is is correct for double speed CGB? */
uint8_t oam_blocking_rush = gb->cgb_double_speed? 2 : 4; uint8_t oam_blocking_rush = gb->cgb_double_speed? 4 : 8;
uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4; uint8_t vram_blocking_rush = gb->is_cgb? 0 : 8;
for (; cycles; cycles -= atomic_increase) { for (; cycles; cycles -= atomic_increase) {
bool dmg_future_stat = false; bool dmg_future_stat = false;
@ -371,11 +373,11 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
gb->stat_interrupt_line = false; gb->stat_interrupt_line = false;
gb->display_cycles += atomic_increase; gb->display_cycles += atomic_increase;
/* The very first line is 4 clocks shorter when the LCD turns on. Verified on SGB2, CGB in CGB mode and /* The very first line is 1 M-cycle shorter when the LCD turns on. Verified on SGB2, CGB in CGB mode and
CGB in double speed mode. */ CGB in double speed mode. */
if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - 8) { if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - 0x10) {
gb->first_scanline = false; gb->first_scanline = false;
gb->display_cycles += 4; gb->display_cycles += 8;
} }
bool should_compare_ly = true; bool should_compare_ly = true;
uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH; uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH;
@ -383,7 +385,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
/* Handle cycle completion. STAT's initial value depends on model and mode */ /* Handle cycle completion. STAT's initial value depends on model and mode */
if (gb->display_cycles == LCDC_PERIOD) { if (gb->display_cycles == FRAME_LENGTH) {
/* VBlank! */ /* VBlank! */
gb->display_cycles = 0; gb->display_cycles = 0;
gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] &= ~3;
@ -542,11 +544,11 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
case 0: case 0:
should_compare_ly = false; should_compare_ly = false;
break; break;
case 4: case 8:
gb->io_registers[GB_IO_LY] = 0; gb->io_registers[GB_IO_LY] = 0;
ly_for_comparison = VIRTUAL_LINES - 1; ly_for_comparison = VIRTUAL_LINES - 1;
break; break;
case 8: case 16:
gb->io_registers[GB_IO_LY] = 0; gb->io_registers[GB_IO_LY] = 0;
should_compare_ly = false; should_compare_ly = false;
break; break;
@ -561,9 +563,9 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
case 0: case 0:
ly_for_comparison = VIRTUAL_LINES - 2; ly_for_comparison = VIRTUAL_LINES - 2;
break; break;
case 4:
break;
case 8: case 8:
break;
case 16:
gb->io_registers[GB_IO_LY] = 0; gb->io_registers[GB_IO_LY] = 0;
break; break;
default: default:
@ -576,7 +578,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
switch (gb->display_cycles - (VIRTUAL_LINES - 1) * LINE_LENGTH) { switch (gb->display_cycles - (VIRTUAL_LINES - 1) * LINE_LENGTH) {
case 0: case 0:
break; break;
case 4: case 8:
gb->io_registers[GB_IO_LY] = 0; gb->io_registers[GB_IO_LY] = 0;
break; break;
default: default:
@ -591,11 +593,11 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
case 0: case 0:
ly_for_comparison = VIRTUAL_LINES - 2; ly_for_comparison = VIRTUAL_LINES - 2;
break; break;
case 2:
case 4: case 4:
break;
case 6:
case 8: case 8:
break;
case 12:
case 16:
gb->io_registers[GB_IO_LY] = 0; gb->io_registers[GB_IO_LY] = 0;
break; break;
default: default:
@ -666,7 +668,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
This is based on AntonioND's docs, however I could not reproduce these findings on my CGB. This is based on AntonioND's docs, however I could not reproduce these findings on my CGB.
Todo: Find out why my tests contradict these docs */ Todo: Find out why my tests contradict these docs */
if (gb->cgb_mode && !gb->cgb_double_speed && if (gb->cgb_mode && !gb->cgb_double_speed &&
gb->display_cycles % LINE_LENGTH == LINE_LENGTH - 4) { gb->display_cycles % LINE_LENGTH == LINE_LENGTH - 8) {
uint8_t glitch_pattern[] = {0, 0, 2, 0, 4, 4, 6, 0, 8}; uint8_t glitch_pattern[] = {0, 0, 2, 0, 4, 4, 6, 0, 8};
if ((gb->io_registers[GB_IO_LY] & 0xF) == 0xF) { if ((gb->io_registers[GB_IO_LY] & 0xF) == 0xF) {
gb->io_registers[GB_IO_LY] = glitch_pattern[gb->io_registers[GB_IO_LY] >> 4] << 4; gb->io_registers[GB_IO_LY] = glitch_pattern[gb->io_registers[GB_IO_LY] >> 4] << 4;
@ -709,7 +711,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
/* Render */ /* Render */
int16_t current_lcdc_x = gb->display_cycles % LINE_LENGTH - MODE2_LENGTH - (gb->effective_scx & 0x7) - 7; int16_t current_lcdc_x = (gb->display_cycles % LINE_LENGTH - MODE2_LENGTH) / 2 - (gb->effective_scx & 0x7) - 7;
for (;gb->previous_lcdc_x < current_lcdc_x; gb->previous_lcdc_x++) { for (;gb->previous_lcdc_x < current_lcdc_x; gb->previous_lcdc_x++) {
if (gb->previous_lcdc_x >= WIDTH) { if (gb->previous_lcdc_x >= WIDTH) {

View File

@ -309,7 +309,7 @@ uint64_t GB_run_frame(GB_gameboy_t *gb)
} }
gb->turbo = old_turbo; gb->turbo = old_turbo;
gb->turbo_dont_skip = old_dont_skip; gb->turbo_dont_skip = old_dont_skip;
return gb->cycles_since_last_sync * 1000000000LL / GB_get_clock_rate(gb); return gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
} }
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output) void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output)

View File

@ -267,7 +267,7 @@ struct GB_gameboy_internal_s {
bool hdma_on; bool hdma_on;
bool hdma_on_hblank; bool hdma_on_hblank;
uint8_t hdma_steps_left; uint8_t hdma_steps_left;
uint16_t hdma_cycles; uint16_t hdma_cycles; // in 8MHz units
uint16_t hdma_current_src, hdma_current_dest; uint16_t hdma_current_src, hdma_current_dest;
uint8_t dma_steps_left; uint8_t dma_steps_left;
@ -332,7 +332,7 @@ struct GB_gameboy_internal_s {
/* Timing */ /* Timing */
GB_SECTION(timing, GB_SECTION(timing,
uint32_t display_cycles; uint32_t display_cycles; // In 8 MHz units
uint32_t div_cycles; uint32_t div_cycles;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */ uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles; uint16_t serial_cycles;
@ -418,7 +418,7 @@ struct GB_gameboy_internal_s {
/* Timing */ /* Timing */
uint64_t last_sync; uint64_t last_sync;
uint64_t cycles_since_last_sync; uint64_t cycles_since_last_sync; // In 8MHz units
/* Audio */ /* Audio */
GB_apu_output_t apu_output; GB_apu_output_t apu_output;
@ -438,8 +438,8 @@ struct GB_gameboy_internal_s {
GB_serial_transfer_end_callback_t serial_transfer_end_callback; GB_serial_transfer_end_callback_t serial_transfer_end_callback;
/* IR */ /* IR */
long cycles_since_ir_change; long cycles_since_ir_change; // In 8MHz units
long cycles_since_input_ir_change; long cycles_since_input_ir_change; // In 8MHz units
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE]; GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length; size_t ir_queue_length;
@ -493,7 +493,7 @@ struct GB_gameboy_internal_s {
uint32_t ram_size; // Different between CGB and DMG uint32_t ram_size; // Different between CGB and DMG
uint8_t boot_rom[0x900]; uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run() uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
double clock_multiplier; double clock_multiplier;
); );
}; };
@ -563,7 +563,7 @@ void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const cha
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output); void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
void GB_set_infrared_input(GB_gameboy_t *gb, bool state); void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); /* In 8MHz units*/
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback); void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback); void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);

View File

@ -708,8 +708,8 @@ void GB_dma_run(GB_gameboy_t *gb)
void GB_hdma_run(GB_gameboy_t *gb) void GB_hdma_run(GB_gameboy_t *gb)
{ {
if (!gb->hdma_on) return; if (!gb->hdma_on) return;
while (gb->hdma_cycles >= 8) { while (gb->hdma_cycles >= 0x10) {
gb->hdma_cycles -= 8; gb->hdma_cycles -= 0x10;
for (uint8_t i = 0; i < 0x10; i++) { for (uint8_t i = 0; i < 0x10; i++) {
GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++))); GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++)));

View File

@ -55,9 +55,9 @@ void GB_timing_sync(GB_gameboy_t *gb)
return; return;
} }
/* Prevent syncing if not enough time has passed.*/ /* Prevent syncing if not enough time has passed.*/
if (gb->cycles_since_last_sync < LCDC_PERIOD / 4) return; if (gb->cycles_since_last_sync < LCDC_PERIOD / 8) return;
uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / GB_get_clock_rate(gb); uint64_t target_nanoseconds = gb->cycles_since_last_sync * 1000000000LL / 2 / GB_get_clock_rate(gb); /* / 2 because we use 8MHz units */
int64_t nanoseconds = get_nanoseconds(); int64_t nanoseconds = get_nanoseconds();
if (labs((signed long)(nanoseconds - gb->last_sync)) < target_nanoseconds ) { if (labs((signed long)(nanoseconds - gb->last_sync)) < target_nanoseconds ) {
nsleep(target_nanoseconds + gb->last_sync - nanoseconds); nsleep(target_nanoseconds + gb->last_sync - nanoseconds);
@ -145,7 +145,7 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
gb->debugger_ticks += cycles; gb->debugger_ticks += cycles;
cycles >>= gb->cgb_double_speed; cycles <<= !gb->cgb_double_speed;
// Not affected by speed boost // Not affected by speed boost
gb->hdma_cycles += cycles; gb->hdma_cycles += cycles;