From 465d1ea3a18ca7c606817d72b8d0a82421ab0775 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 6 Sep 2017 19:38:18 -0400 Subject: [PATCH] Add files via upload --- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 24 +- .../Consoles/Nintendo/GBHawk/PPU.cs | 241 +++++++++++++----- 2 files changed, 185 insertions(+), 80 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index 2b1a3bdb0a..da99274751 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -49,15 +49,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if (addr < 0x9800) { - return CHR_RAM[addr - 0x8000]; + if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } + else { return 0xFF; } } else if (addr < 0x9C00) { - return BG_map_1[addr - 0x9800]; + if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } + else { return 0xFF; } } else if (addr < 0xA000) { - return BG_map_2[addr - 0x9C00]; + if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + else { return 0xFF; } } else if (addr < 0xC000) { @@ -71,9 +74,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { return RAM[addr - 0xE000]; } - else if (addr < 0xFEA0 && ppu.OAM_access) + else if (addr < 0xFEA0) { - return OAM[addr - 0xFE00]; + if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } + else { return 0xFF; } } else if (addr < 0xFF00) { @@ -117,15 +121,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if (addr < 0x9800) { - CHR_RAM[addr - 0x8000] = value; + if (ppu.VRAM_access_write) { CHR_RAM[addr - 0x8000] = value; } } else if (addr < 0x9C00) { - BG_map_1[addr - 0x9800] = value; + if (ppu.VRAM_access_write) { BG_map_1[addr - 0x9800] = value; } } else if (addr < 0xA000) { - BG_map_2[addr - 0x9C00] = value; + if (ppu.VRAM_access_write) { BG_map_2[addr - 0x9C00] = value; } } else if (addr < 0xC000) { @@ -139,9 +143,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { RAM[addr - 0xE000] = value; } - else if (addr < 0xFEA0 && ppu.OAM_access) + else if (addr < 0xFEA0) { - OAM[addr - 0xFE00] = value; + if (ppu.OAM_access_write) { OAM[addr - 0xFE00] = value; } } else if (addr < 0xFF00) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index fdb381ae9e..d413c74adc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -39,15 +39,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool LCD_was_off; public bool stat_line; public bool stat_line_old; - public bool hbl_set_once; + public int hbl_countdown; // OAM scan - public bool OAM_access; + public bool OAM_access_read; + public bool OAM_access_write; public int OAM_scan_index; public int SL_sprites_index; public int[] SL_sprites = new int[40]; public int write_sprite; + public bool no_scan; // render - public bool VRAM_access; + public bool VRAM_access_read; + public bool VRAM_access_write; public int read_case; public int internal_cycle; public int y_tile; @@ -166,7 +169,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (DMA_clock >= 4) { - OAM_access = false; + OAM_access_read = false; if ((DMA_clock % 4) == 1) { // the cpu can't access memory during this time, but we still need the ppu to be able to. @@ -194,13 +197,51 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (DMA_clock==648) { DMA_start = false; - OAM_access = true; + OAM_access_read = true; } } - + // the ppu only does anything if it is turned on via bit 7 of LCDC if (LCDC.Bit(7)) { + // start the next scanline + if (cycle == 456) + { + cycle = 0; + LY += LY_inc; + + no_scan = false; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + } + + Core.cpu.LY = LY; + + if (LY == 144) + { + Core.in_vblank = true; + + } + } + // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -211,6 +252,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // we exit vblank into mode 0 for 4 cycles // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; } // the VBL stat is continuously asserted @@ -256,26 +303,88 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (!Core.in_vblank) { - if (cycle == 4) + if (no_scan) { - // here mode 2 will be set to true and interrupts fired if enabled - STAT &= 0xFC; - STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } - - HBL_INT = false; - } + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? - if (cycle >= 4 && cycle < 84) - { - // here OAM scanning is performed - OAM_scan(cycle - 4); + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } } - else if (cycle >= 84 && LY < 144) + else { - // render the screen and handle hblank - render(cycle - 84); + if (cycle < 80) + { + if (cycle == 4) + { + // apparently, writes can make it to OAm one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if (cycle >= 80 && LY < 144) + { + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } } + } @@ -316,34 +425,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle++; - - if (cycle==456) - { - cycle = 0; - LY+=LY_inc; - - if (LY==0 && LY_inc == 0) - { - LY_inc = 1; - Core.in_vblank = false; - VBL_INT = false; - - // special note here, the y coordiate of the window is kept if the window is deactivated - // meaning it will pick up where it left off if re-enabled later - // so we don't reset it in the scanline loop - window_y_tile = 0; - window_y_tile_inc = 0; - window_started = false; - } - - Core.cpu.LY = LY; - - if (LY==144) - { - Core.in_vblank = true; - - } - } } else { @@ -391,7 +472,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { - OAM_access = false; + OAM_access_read = false; + OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; @@ -458,12 +540,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; - OAM_access = false; - VRAM_access = false; OAM_scan_index = 0; read_case = 0; internal_cycle = 0; @@ -526,6 +606,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x - 7)) / 8); + window_tile_inc = 0; window_started = true; } @@ -576,7 +657,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8); t_index = 7 - t_index; - + sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1); sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1); @@ -633,7 +714,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (pixel_counter == 160) { read_case = 8; - hbl_set_once = true; + hbl_countdown = 4; } } render_counter++; @@ -772,7 +853,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } else { @@ -859,19 +942,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 8: // done reading, we are now in phase 0 - - OAM_access = true; - VRAM_access = true; - STAT &= 0xFC; - STAT |= 0x00; pre_render = true; - if (hbl_set_once) + + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) { - if (STAT.Bit(3)) { HBL_INT = true; } - hbl_set_once = false; + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } } - + break; } @@ -942,6 +1034,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y = 0; window_x = 0; LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; cycle = 0; LYC_INT = false; @@ -1043,15 +1140,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("OAM_INT", ref OAM_INT); ser.Sync("stat_line", ref stat_line); ser.Sync("stat_line_old", ref stat_line_old); - ser.Sync("hbl_set_once", ref hbl_set_once); - ser.Sync("LCD_was_off", ref LCD_was_off); - ser.Sync("OAM_access", ref OAM_access); + ser.Sync("hbl_countdown", ref hbl_countdown); + ser.Sync("LCD_was_off", ref LCD_was_off); ser.Sync("OAM_scan_index", ref OAM_scan_index); ser.Sync("SL_sprites_index", ref SL_sprites_index); ser.Sync("SL_sprites", ref SL_sprites, false); ser.Sync("write_sprite", ref write_sprite); + ser.Sync("no_scan", ref no_scan); + + ser.Sync("OAM_access_read", ref OAM_access_read); + ser.Sync("OAM_access_write", ref OAM_access_write); + ser.Sync("VRAM_access_read", ref VRAM_access_read); + ser.Sync("VRAM_access_write", ref VRAM_access_write); - ser.Sync("VRAM_access", ref VRAM_access); ser.Sync("read_case", ref read_case); ser.Sync("internal_cycle", ref internal_cycle); ser.Sync("y_tile", ref y_tile);