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) {
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) {
gb->apu_output.sample_cycles -= cycles_per_sample;

View File

@ -127,7 +127,7 @@ typedef struct {
volatile bool copy_in_progress;
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!
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, "\nCycles since frame start: %d\n", gb->display_cycles);
GB_log(gb, "Current line: %d\n", gb->display_cycles / 456);
GB_log(gb, "\nCycles since frame start: %d\n", gb->display_cycles / 2);
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, "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]);

View File

@ -15,13 +15,15 @@
Todo: Mode lengths are not constants, see http://blog.kevtris.org/blogfiles/Nitty%20Gritty%20Gameboy%20VRAM%20Timing.txt
*/
#define MODE2_LENGTH (80)
#define MODE3_LENGTH (172)
#define MODE0_LENGTH (204)
/* The display (logically) runs in 8MHz units, so we double our length constants */
#define MODE2_LENGTH (80 * 2)
#define MODE3_LENGTH (172 * 2)
#define MODE0_LENGTH (204 * 2)
#define LINE_LENGTH (MODE2_LENGTH + MODE3_LENGTH + MODE0_LENGTH) // = 456
#define LINES (144)
#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)) {
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 */
gb->display_cycles += cycles;
if (gb->display_cycles >= LCDC_PERIOD) {
if (gb->display_cycles >= FRAME_LENGTH) {
/* VBlank! */
gb->display_cycles -= LCDC_PERIOD;
gb->display_cycles -= FRAME_LENGTH;
display_vblank(gb);
}
@ -344,23 +346,23 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
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
these 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? */
uint8_t scx_delay = gb->effective_scx & 7;
uint8_t scx_delay = (gb->effective_scx & 7) * 2;
if (gb->cgb_double_speed) {
scx_delay = (scx_delay + 1) & ~1;
scx_delay = (scx_delay + 2) & ~3;
}
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? */
uint8_t oam_blocking_rush = gb->cgb_double_speed? 2 : 4;
uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4;
uint8_t oam_blocking_rush = gb->cgb_double_speed? 4 : 8;
uint8_t vram_blocking_rush = gb->is_cgb? 0 : 8;
for (; cycles; cycles -= atomic_increase) {
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->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. */
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->display_cycles += 4;
gb->display_cycles += 8;
}
bool should_compare_ly = true;
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 */
if (gb->display_cycles == LCDC_PERIOD) {
if (gb->display_cycles == FRAME_LENGTH) {
/* VBlank! */
gb->display_cycles = 0;
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:
should_compare_ly = false;
break;
case 4:
case 8:
gb->io_registers[GB_IO_LY] = 0;
ly_for_comparison = VIRTUAL_LINES - 1;
break;
case 8:
case 16:
gb->io_registers[GB_IO_LY] = 0;
should_compare_ly = false;
break;
@ -561,9 +563,9 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
case 0:
ly_for_comparison = VIRTUAL_LINES - 2;
break;
case 4:
break;
case 8:
break;
case 16:
gb->io_registers[GB_IO_LY] = 0;
break;
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) {
case 0:
break;
case 4:
case 8:
gb->io_registers[GB_IO_LY] = 0;
break;
default:
@ -591,11 +593,11 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
case 0:
ly_for_comparison = VIRTUAL_LINES - 2;
break;
case 2:
case 4:
break;
case 6:
case 8:
break;
case 12:
case 16:
gb->io_registers[GB_IO_LY] = 0;
break;
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.
Todo: Find out why my tests contradict these docs */
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};
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;
@ -709,7 +711,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
/* 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++) {
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_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)

View File

@ -267,7 +267,7 @@ struct GB_gameboy_internal_s {
bool hdma_on;
bool hdma_on_hblank;
uint8_t hdma_steps_left;
uint16_t hdma_cycles;
uint16_t hdma_cycles; // in 8MHz units
uint16_t hdma_current_src, hdma_current_dest;
uint8_t dma_steps_left;
@ -332,7 +332,7 @@ struct GB_gameboy_internal_s {
/* Timing */
GB_SECTION(timing,
uint32_t display_cycles;
uint32_t display_cycles; // In 8 MHz units
uint32_t div_cycles;
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
uint16_t serial_cycles;
@ -418,7 +418,7 @@ struct GB_gameboy_internal_s {
/* Timing */
uint64_t last_sync;
uint64_t cycles_since_last_sync;
uint64_t cycles_since_last_sync; // In 8MHz units
/* Audio */
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;
/* IR */
long cycles_since_ir_change;
long cycles_since_input_ir_change;
long cycles_since_ir_change; // In 8MHz units
long cycles_since_input_ir_change; // In 8MHz units
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
size_t ir_queue_length;
@ -493,7 +493,7 @@ struct GB_gameboy_internal_s {
uint32_t ram_size; // Different between CGB and DMG
uint8_t boot_rom[0x900];
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;
);
};
@ -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_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_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)
{
if (!gb->hdma_on) return;
while (gb->hdma_cycles >= 8) {
gb->hdma_cycles -= 8;
while (gb->hdma_cycles >= 0x10) {
gb->hdma_cycles -= 0x10;
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++)));

View File

@ -55,9 +55,9 @@ void GB_timing_sync(GB_gameboy_t *gb)
return;
}
/* 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();
if (labs((signed long)(nanoseconds - gb->last_sync)) < target_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;
cycles >>= gb->cgb_double_speed;
cycles <<= !gb->cgb_double_speed;
// Not affected by speed boost
gb->hdma_cycles += cycles;