Refined Window behavior once more, Fixes #12 (While not breaking Donkey Kong or 007)

This commit is contained in:
Lior Halphon 2017-08-20 01:34:12 +03:00
parent 7df4e56454
commit cbbaf2ee84
4 changed files with 46 additions and 16 deletions

View File

@ -75,7 +75,8 @@ static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
bg_enabled = false;
}
}
if (window_enabled(gb) && y >= gb->io_registers[GB_IO_WY] && x + 7 >= gb->io_registers[GB_IO_WX] && gb->current_window_line != 0xFF) {
if (window_enabled(gb) && y >= gb->io_registers[GB_IO_WY] + gb->wy_diff && x + 7 >= gb->io_registers[GB_IO_WX]) {
in_window = true;
}
@ -128,7 +129,7 @@ static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y)
if (in_window) {
x -= gb->io_registers[GB_IO_WX] - 7; // Todo: This value is probably latched
y = gb->current_window_line;
y -= gb->io_registers[GB_IO_WY] + gb->wy_diff;
}
else {
x += gb->effective_scx;
@ -282,7 +283,8 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
}
/* Reset window rendering state */
gb->current_window_line = 0xFF;
gb->wy_diff = 0;
gb->window_disabled_while_active = false;
return;
}
@ -330,7 +332,8 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
/* Reset window rendering state */
gb->current_window_line = 0xFF;
gb->wy_diff = 0;
gb->window_disabled_while_active = false;
}
/* Entered VBlank state, update STAT and IF */
@ -418,9 +421,6 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
if (position_in_line == stat_delay) {
gb->io_registers[GB_IO_STAT] &= ~3;
gb->io_registers[GB_IO_STAT] |= 2;
if (window_enabled(gb) && gb->display_cycles / LINE_LENGTH >= gb->io_registers[GB_IO_WY]) {
gb->current_window_line++;
}
}
else if (position_in_line == 0 && gb->display_cycles != 0) {
should_compare_ly = gb->is_cgb;
@ -431,11 +431,6 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
gb->io_registers[GB_IO_STAT] |= 3;
gb->effective_scx = gb->io_registers[GB_IO_SCX];
gb->previous_lcdc_x = - (gb->effective_scx & 0x7);
/* Todo: This works on both 007 - The World Is Not Enough and Donkey Kong 94, but should be verified better */
if (window_enabled(gb) && gb->display_cycles / LINE_LENGTH == gb->io_registers[GB_IO_WY] && gb->current_window_line == 0xFF) {
gb->current_window_line = 0;
}
}
else if (position_in_line == MODE2_LENGTH + MODE3_LENGTH + stat_delay + scx_delay) {
gb->io_registers[GB_IO_STAT] &= ~3;
@ -800,3 +795,32 @@ uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_h
}
return count;
}
/* Called when a write might enable or disable the window */
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value)
{
bool before = window_enabled(gb);
gb->io_registers[addr] = value;
bool after = window_enabled(gb);
if (before != after && gb->display_cycles < LINES * LINE_LENGTH) {
/* Window was disabled or enabled outside of vblank */
uint8_t current_line = gb->display_cycles / LINE_LENGTH;
if (current_line >= gb->io_registers[GB_IO_WY]) {
if (after) {
if (!gb->window_disabled_while_active) {
/* Window was turned on for the first time this frame while LY > WY,
should start window in the next line */
gb->wy_diff = current_line + 1 - gb->io_registers[GB_IO_WY];
}
else {
gb->wy_diff += current_line;
}
}
else {
gb->wy_diff -= current_line;
gb->window_disabled_while_active = true;
}
}
}
}

View File

@ -5,6 +5,7 @@
#ifdef GB_INTERNAL
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value);
#endif
typedef enum {

View File

@ -367,7 +367,7 @@ struct GB_gameboy_internal_s {
int16_t previous_lcdc_x;
bool stat_interrupt_line;
uint8_t effective_scx;
uint8_t current_window_line;
uint8_t wy_diff;
/* The LCDC will skip the first frame it renders after turning it on.
On the CGB, a frame is not skipped if the previous frame was skipped as well.
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
@ -383,6 +383,7 @@ struct GB_gameboy_internal_s {
bool vram_read_blocked;
bool oam_write_blocked;
bool vram_write_blocked;
bool window_disabled_while_active;
);
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */

View File

@ -408,10 +408,14 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return;
}
/* Todo: Clean this code up: use a function table and move relevant code to display.c and timing.c
(APU read and writes are already at apu.c) */
if (addr < 0xFF80) {
/* Hardware registers */
switch (addr & 0xFF) {
case GB_IO_WX:
GB_window_related_write(gb, addr & 0xFF, value);
break;
case GB_IO_SCX:
case GB_IO_IF:
case GB_IO_SCY:
@ -420,7 +424,6 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
case GB_IO_OBP0:
case GB_IO_OBP1:
case GB_IO_WY:
case GB_IO_WX:
case GB_IO_SB:
case GB_IO_DMG_EMULATION_INDICATION:
case GB_IO_UNKNOWN2:
@ -463,7 +466,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
/* Sync after turning off LCD */
GB_timing_sync(gb);
}
gb->io_registers[GB_IO_LCDC] = value;
/* Writing to LCDC might enable to disable the window, so we write it via GB_window_related_write */
GB_window_related_write(gb, addr & 0xFF, value);
return;
case GB_IO_STAT: