From d191575fd220392963dd52f0f02d1850e84260c0 Mon Sep 17 00:00:00 2001 From: goyuken Date: Mon, 6 Jan 2014 21:33:30 +0000 Subject: [PATCH] NES internal infrastructure --- .../tools/NES/NESNameTableViewer.cs | 36 ++--- BizHawk.Client.EmuHawk/tools/NES/NESPPU.cs | 69 +++++---- .../BizHawk.Emulation.Cores.csproj | 1 + .../Consoles/Nintendo/NES/INESPPUDebug.cs | 131 ++++++++++++++++++ .../Consoles/Nintendo/NES/NES.cs | 8 +- 5 files changed, 191 insertions(+), 54 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/NES/INESPPUDebug.cs diff --git a/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs b/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs index ad82d38f16..18828a6705 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NESNameTableViewer.cs @@ -2,7 +2,7 @@ using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; -using BizHawk.Emulation.Cores.Nintendo.NES; +using BizHawk.Emulation.Cores.Consoles.Nintendo.NES; using BizHawk.Client.Common; @@ -13,8 +13,8 @@ namespace BizHawk.Client.EmuHawk //TODO: //Show Scroll Lines + UI Toggle - private NES _nes; - private readonly NES.PPU.DebugCallback Callback = new NES.PPU.DebugCallback(); + private INESPPUDebug _nes; + private int Scanline = 0; public bool AskSave() { return true; } public bool UpdateBefore { get { return true; } } @@ -23,7 +23,6 @@ namespace BizHawk.Client.EmuHawk { InitializeComponent(); Closing += (o, e) => SaveConfigSettings(); - Callback.Callback = () => Generate(); } private void SaveConfigSettings() @@ -47,16 +46,16 @@ namespace BizHawk.Client.EmuHawk int* dptr = (int*)bmpdata.Scan0.ToPointer(); int pitch = bmpdata.Stride / 4; - int pt_add = _nes.ppu.reg_2000.bg_pattern_hi ? 0x1000 : 0; + int pt_add = _nes.bg_pattern_hi ? 0x1000 : 0; //buffer all the data from the ppu, because it will be read multiple times and that is slow byte[] p = new byte[0x3000]; for (int x = 0; x < 0x3000; x++) - p[x] = _nes.ppu.ppubus_peek(x); + p[x] = _nes.PPUBUS(x); byte[] palram = new byte[0x20]; for (int x = 0; x < 0x20; x++) - palram[x] = _nes.ppu.PALRAM[x]; + palram[x] = _nes.PALRAM(x); int ytable = 0, yline = 0; for (int y = 0; y < 480; y++) @@ -109,10 +108,11 @@ namespace BizHawk.Client.EmuHawk public void UpdateValues() { - if (Global.Emulator is NES) + if (Global.Emulator is IHasNESPPUDebug) { - NES.PPU ppu = (Global.Emulator as NES).ppu; - ppu.NTViewCallback = Callback; + _nes = (Global.Emulator as IHasNESPPUDebug).GetDebugger(); + _nes.SetNTViewCallback(Scanline, () => Generate()); + } else { @@ -122,9 +122,9 @@ namespace BizHawk.Client.EmuHawk public void Restart() { - if (Global.Emulator is NES) + if (Global.Emulator is IHasNESPPUDebug) { - _nes = Global.Emulator as NES; + _nes = (Global.Emulator as IHasNESPPUDebug).GetDebugger(); Generate(true); } else @@ -138,7 +138,7 @@ namespace BizHawk.Client.EmuHawk if (Global.Config.NESNameTableSaveWindowPosition && Global.Config.NESNameTableWndx >= 0 && Global.Config.NESNameTableWndy >= 0) Location = new Point(Global.Config.NESNameTableWndx, Global.Config.NESNameTableWndy); - _nes = Global.Emulator as NES; + _nes = (Global.Emulator as IHasNESPPUDebug).GetDebugger(); RefreshRate.Value = Global.Config.NESNameTableRefreshRate; Generate(true); } @@ -169,15 +169,15 @@ namespace BizHawk.Client.EmuHawk int temp; if (int.TryParse(txtScanline.Text, out temp)) { - Callback.Scanline = temp; + Scanline = temp; + _nes.SetNTViewCallback(Scanline, () => Generate()); } } private void NESNameTableViewer_FormClosed(object sender, FormClosedEventArgs e) { if (_nes == null) return; - if (_nes.ppu.NTViewCallback == Callback) - _nes.ppu.NTViewCallback = null; + _nes.SetNTViewCallback(Scanline, null); } @@ -225,7 +225,7 @@ namespace BizHawk.Client.EmuHawk XYLabel.Text = TileX.ToString() + " : " + TileY.ToString(); int PPUAddress = 0x2000 + (NameTable * 0x400) + ((TileY % 30) * 32) + (TileX % 32); PPUAddressLabel.Text = String.Format("{0:X4}", PPUAddress); - int TileID = _nes.ppu.ppubus_read(PPUAddress, true); + int TileID = _nes.PPUBUS(PPUAddress); TileIDLabel.Text = String.Format("{0:X2}", TileID); TableLabel.Text = NameTable.ToString(); @@ -242,7 +242,7 @@ namespace BizHawk.Client.EmuHawk int tx = px >> 3; int ty = py >> 3; int atbyte_ptr = ntaddr + 0x3C0 + ((ty >> 2) << 3) + (tx >> 2); - int at = _nes.ppu.ppubus_peek(atbyte_ptr + 0x2000); + int at = _nes.PPUBUS(atbyte_ptr + 0x2000); if ((ty & 2) != 0) at >>= 4; if ((tx & 2) != 0) at >>= 2; at &= 0x03; diff --git a/BizHawk.Client.EmuHawk/tools/NES/NESPPU.cs b/BizHawk.Client.EmuHawk/tools/NES/NESPPU.cs index 34916f01b7..5f7a872a81 100644 --- a/BizHawk.Client.EmuHawk/tools/NES/NESPPU.cs +++ b/BizHawk.Client.EmuHawk/tools/NES/NESPPU.cs @@ -2,9 +2,8 @@ using System.Drawing; using System.Windows.Forms; using System.Globalization; - using BizHawk.Client.Common; -using BizHawk.Emulation.Cores.Nintendo.NES; +using BizHawk.Emulation.Cores.Consoles.Nintendo.NES; namespace BizHawk.Client.EmuHawk { @@ -17,12 +16,12 @@ namespace BizHawk.Client.EmuHawk //Maybe 48 individual bitmaps for sprites is faster than the overhead of redrawing all that transparent space private Bitmap ZoomBoxDefaultImage = new Bitmap(64, 64); - private NES _nes; + private INESPPUDebug _nes; private readonly byte[] PPUBus = new byte[0x2000]; private readonly byte[] PPUBusprev = new byte[0x2000]; private readonly byte[] PALRAM = new byte[0x20]; private readonly byte[] PALRAMprev = new byte[0x20]; - private readonly NES.PPU.DebugCallback Callback = new NES.PPU.DebugCallback(); + private int Scanline; private bool ForceChange; public bool AskSave() { return true; } @@ -32,7 +31,6 @@ namespace BizHawk.Client.EmuHawk { InitializeComponent(); Closing += (o, e) => SaveConfigSettings(); - Callback.Callback = () => Generate(); for (int x = 0; x < 0x2000; x++) { PPUBus[x] = 0; @@ -55,9 +53,9 @@ namespace BizHawk.Client.EmuHawk public void Restart() { - if (Global.Emulator is NES) + if (Global.Emulator is IHasNESPPUDebug) { - _nes = Global.Emulator as NES; + _nes = (Global.Emulator as IHasNESPPUDebug).GetDebugger(); Generate(true); } else @@ -83,7 +81,7 @@ namespace BizHawk.Client.EmuHawk for (int x = 0; x < 0x20; x++) { PALRAMprev[x] = PALRAM[x]; - PALRAM[x] = _nes.ppu.PALRAM[x]; + PALRAM[x] = _nes.PALRAM(x); if (PALRAM[x] != PALRAMprev[x]) { changed = true; @@ -93,7 +91,7 @@ namespace BizHawk.Client.EmuHawk for (int x = 0; x < 0x2000; x++) { PPUBusprev[x] = PPUBus[x]; - PPUBus[x] = _nes.ppu.ppubus_peek(x); + PPUBus[x] = _nes.PPUBUS(x); if (PPUBus[x] != PPUBusprev[x]) { changed = true; @@ -127,8 +125,8 @@ namespace BizHawk.Client.EmuHawk { PaletteView.BgPalettesPrev[x].Value = PaletteView.BgPalettes[x].Value; PaletteView.SpritePalettesPrev[x].Value = PaletteView.SpritePalettes[x].Value; - PaletteView.BgPalettes[x].Value = _nes.LookupColor(_nes.ppu.PALRAM[PaletteView.BgPalettes[x].Address]); - PaletteView.SpritePalettes[x].Value = _nes.LookupColor(_nes.ppu.PALRAM[PaletteView.SpritePalettes[x].Address]); + PaletteView.BgPalettes[x].Value = _nes.LookupColor(_nes.PALRAM(PaletteView.BgPalettes[x].Address)); + PaletteView.SpritePalettes[x].Value = _nes.LookupColor(_nes.PALRAM(PaletteView.SpritePalettes[x].Address)); } if (PaletteView.HasChanged()) { @@ -158,7 +156,7 @@ namespace BizHawk.Client.EmuHawk b1 = (byte)(((PPUBus[address + 8] >> (7 - x)) & 1)); value = (byte)(b0 + (b1 << 1)); - cvalue = _nes.LookupColor(_nes.ppu.PALRAM[value + (pal << 2)]); + cvalue = _nes.LookupColor(_nes.PALRAM(value + (pal << 2))); int adr = (x + (j << 3)) + (y + (i << 3)) * (bmpdata.Stride >> 2); framebuf[adr + (z << 7)] = cvalue; } @@ -173,8 +171,8 @@ namespace BizHawk.Client.EmuHawk System.Drawing.Imaging.BitmapData bmpdata2 = SpriteView.sprites.LockBits(new Rectangle(new Point(0, 0), SpriteView.sprites.Size), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); int* framebuf2 = (int*)bmpdata2.Scan0.ToPointer(); - int pt_add = _nes.ppu.reg_2000.obj_pattern_hi ? 0x1000 : 0; - bool is8x16 = _nes.ppu.reg_2000.obj_size_16; + int pt_add = _nes.obj_pattern_hi ? 0x1000 : 0; + bool is8x16 = _nes.obj_size_16; //Sprite Viewer @@ -183,7 +181,7 @@ namespace BizHawk.Client.EmuHawk for (int r = 0; r < 16; r++) { int BaseAddr = (r << 2) + (n << 6); - int TileNum = _nes.ppu.OAM[BaseAddr + 1]; + int TileNum = _nes.OAM(BaseAddr + 1); int PatAddr; if (is8x16) @@ -198,7 +196,7 @@ namespace BizHawk.Client.EmuHawk } - int Attributes = _nes.ppu.OAM[BaseAddr + 2]; + int Attributes = _nes.OAM(BaseAddr + 2); int Palette = Attributes & 0x03; for (int x = 0; x < 8; x++) @@ -209,7 +207,7 @@ namespace BizHawk.Client.EmuHawk b0 = (byte)(((PPUBus[address] >> (7 - x)) & 1)); b1 = (byte)(((PPUBus[address + 8] >> (7 - x)) & 1)); value = (byte)(b0 + (b1 << 1)); - cvalue = _nes.LookupColor(_nes.ppu.PALRAM[16 + value + (Palette << 2)]); + cvalue = _nes.LookupColor(_nes.PALRAM(16 + value + (Palette << 2))); int adr = (x + (r * 16)) + (y + (n * 24)) * (bmpdata2.Stride >> 2); framebuf2[adr] = cvalue; @@ -223,7 +221,7 @@ namespace BizHawk.Client.EmuHawk b0 = (byte)(((PPUBus[address] >> (7 - x)) & 1)); b1 = (byte)(((PPUBus[address + 8] >> (7 - x)) & 1)); value = (byte)(b0 + (b1 << 1)); - cvalue = _nes.LookupColor(_nes.ppu.PALRAM[16 + value + (Palette << 2)]); + cvalue = _nes.LookupColor(_nes.PALRAM(16 + value + (Palette << 2))); int adr = (x + (r << 4)) + ((y+8) + (n * 24)) * (bmpdata2.Stride >> 2); framebuf2[adr] = cvalue; @@ -240,9 +238,10 @@ namespace BizHawk.Client.EmuHawk public void UpdateValues() { - if (Global.Emulator is NES) + if (Global.Emulator is IHasNESPPUDebug) { - _nes.ppu.PPUViewCallback = Callback; + _nes = (Global.Emulator as IHasNESPPUDebug).GetDebugger(); + _nes.SetPPUViewCallback(Scanline, () => Generate()); } else { @@ -253,7 +252,7 @@ namespace BizHawk.Client.EmuHawk private void NESPPU_Load(object sender, EventArgs e) { LoadConfigSettings(); - _nes = Global.Emulator as NES; + _nes = (Global.Emulator as IHasNESPPUDebug).GetDebugger(); ClearDetails(); RefreshRate.Value = Global.Config.NESPPURefreshRate; Generate(true); @@ -297,13 +296,13 @@ namespace BizHawk.Client.EmuHawk if (baseAddr == 0x3F00) { - val = _nes.ppu.PALRAM[PaletteView.BgPalettes[column].Address]; + val = _nes.PALRAM(PaletteView.BgPalettes[column].Address); ValueLabel.Text = "ID: BG" + (column / 4).ToString(); g.FillRectangle(new SolidBrush(PaletteView.BgPalettes[column].Color), 0, 0, 64, 64); } else { - val = _nes.ppu.PALRAM[PaletteView.SpritePalettes[column].Address]; + val = _nes.PALRAM(PaletteView.SpritePalettes[column].Address); ValueLabel.Text = "ID: SPR" + (column / 4).ToString(); g.FillRectangle(new SolidBrush(PaletteView.SpritePalettes[column].Color), 0, 0, 64, 64); } @@ -391,12 +390,12 @@ namespace BizHawk.Client.EmuHawk tile += (e.Y / 8) * 16; string Usage = "Usage: "; - if ((_nes.ppu.reg_2000.Value & 0x10) << 4 == ((address >> 4) & 0x100)) + if ((_nes.Reg2000Value & 0x10) << 4 == ((address >> 4) & 0x100)) Usage = "BG"; - else if (((_nes.ppu.reg_2000.Value & 0x08) << 5) == ((address >> 4) & 0x100)) + else if (((_nes.Reg2000Value & 0x08) << 5) == ((address >> 4) & 0x100)) Usage = "SPR"; - if ((_nes.ppu.reg_2000.Value & 0x20) > 0) + if ((_nes.Reg2000Value & 0x20) > 0) Usage += " (SPR16)"; AddressLabel.Text = "Address: " + String.Format("{0:X4}", address); @@ -534,15 +533,15 @@ namespace BizHawk.Client.EmuHawk int temp; if (int.TryParse(txtScanline.Text, out temp)) { - Callback.Scanline = temp; + Scanline = temp; + _nes.SetPPUViewCallback(Scanline, () => Generate()); } } private void NESPPU_FormClosed(object sender, FormClosedEventArgs e) { if (_nes == null) return; - if (_nes.ppu.PPUViewCallback == Callback) - _nes.ppu.PPUViewCallback = null; + _nes.SetPPUViewCallback(Scanline, null); } private void SpriteView_MouseEnter(object sender, EventArgs e) @@ -557,12 +556,12 @@ namespace BizHawk.Client.EmuHawk private void SpriteView_MouseMove(object sender, MouseEventArgs e) { - bool is8x16 = _nes.ppu.reg_2000.obj_size_16; + bool is8x16 = _nes.obj_size_16; int SpriteNumber = ((e.Y / 24) * 16) + (e.X / 16); - int X = _nes.ppu.OAM[(SpriteNumber * 4) + 3]; - int Y = _nes.ppu.OAM[SpriteNumber * 4]; - int Color = _nes.ppu.OAM[(SpriteNumber * 4) + 2] & 0x03; - int Attributes = _nes.ppu.OAM[(SpriteNumber * 4) + 2]; + int X = _nes.OAM((SpriteNumber * 4) + 3); + int Y = _nes.OAM(SpriteNumber * 4); + int Color = _nes.OAM((SpriteNumber * 4) + 2) & 0x03; + int Attributes = _nes.OAM((SpriteNumber * 4) + 2); string flags = "Flags: "; int h = GetBit(Attributes, 6); @@ -577,7 +576,7 @@ namespace BizHawk.Client.EmuHawk else flags += "Front"; - int Tile = _nes.ppu.OAM[SpriteNumber * 1]; + int Tile = _nes.OAM(SpriteNumber * 1); AddressLabel.Text = "Number: " + String.Format("{0:X2}", SpriteNumber); ValueLabel.Text = "X: " + String.Format("{0:X2}", X); diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index f502281ff3..44627a90d8 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -386,6 +386,7 @@ + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/INESPPUDebug.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/INESPPUDebug.cs new file mode 100644 index 0000000000..5fd74eb6e8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/INESPPUDebug.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Nintendo.NES +{ + public interface IHasNESPPUDebug + { + INESPPUDebug GetDebugger(); + } + + public interface INESPPUDebug + { + /// + /// looks up an internal NES pixel value to an rgb int (applying the core's current palette and assuming no deemph) + /// + int LookupColor(int pixel); + + // reg 2000 stuff + + Bit vram_incr32 { get; } //(0: increment by 1, going across; 1: increment by 32, going down) + Bit obj_pattern_hi { get; } //Sprite pattern table address for 8x8 sprites (0: $0000; 1: $1000) + Bit bg_pattern_hi { get; } //Background pattern table address (0: $0000; 1: $1000) + Bit obj_size_16 { get; } //Sprite size (0: 8x8 sprites; 1: 8x16 sprites) + Bit ppu_layer { get; } //PPU layer select (should always be 0 in the NES; some Nintendo arcade boards presumably had two PPUs) + Bit vblank_nmi_gen { get; } //Vertical blank NMI generation (0: off; 1: on) + + byte Reg2000Value { get; } + + byte OAM(int addr); + byte PALRAM(int addr); + byte PPUBUS(int addr); + + void SetNTViewCallback(int scanline, Action target); + void SetPPUViewCallback(int scanline, Action target); + } + + // implementation for NESHAWK + public class NESHawkPPUDebug : INESPPUDebug + { + private BizHawk.Emulation.Cores.Nintendo.NES.NES _nes; + private BizHawk.Emulation.Cores.Nintendo.NES.NES.PPU _ppu; + + public NESHawkPPUDebug(BizHawk.Emulation.Cores.Nintendo.NES.NES nes) + { + _nes = nes; + _ppu = nes.ppu; + } + + public int LookupColor(int pixel) + { + return _nes.LookupColor(pixel); + } + + public Bit vram_incr32 + { + get { return _ppu.reg_2000.vram_incr32; } + } + + public Bit obj_pattern_hi + { + get { return _ppu.reg_2000.obj_pattern_hi; } + } + + public Bit bg_pattern_hi + { + get { return _ppu.reg_2000.bg_pattern_hi; } + } + + public Bit obj_size_16 + { + get { return _ppu.reg_2000.obj_size_16; } + } + + public Bit ppu_layer + { + get { return _ppu.reg_2000.ppu_layer; } + } + + public Bit vblank_nmi_gen + { + get { return _ppu.reg_2000.vblank_nmi_gen; } + } + + public byte Reg2000Value + { + get { return _ppu.reg_2000.Value; } + } + + public byte OAM(int addr) + { + return _ppu.OAM[addr]; + } + + public byte PALRAM(int addr) + { + return _ppu.PALRAM[addr]; + } + + public byte PPUBUS(int addr) + { + return _ppu.ppubus_peek(addr); + } + + public void SetNTViewCallback(int scanline, Action target) + { + if (target != null) + _ppu.NTViewCallback = new Cores.Nintendo.NES.NES.PPU.DebugCallback + { + Scanline = scanline, + Callback = target + }; + else + _ppu.NTViewCallback = null; + } + + public void SetPPUViewCallback(int scanline, Action target) + { + if (target != null) + _ppu.PPUViewCallback = new Cores.Nintendo.NES.NES.PPU.DebugCallback + { + Scanline = scanline, + Callback = target + }; + else + _ppu.PPUViewCallback = null; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs index e8c1dec85f..67647907fc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.cs @@ -6,12 +6,18 @@ using System.Collections.Generic; using BizHawk.Common; using BizHawk.Emulation.Common; //TODO - redo all timekeeping in terms of master clock +using BizHawk.Emulation.Cores.Consoles.Nintendo.NES; namespace BizHawk.Emulation.Cores.Nintendo.NES { - public partial class NES : IEmulator + public partial class NES : IEmulator, IHasNESPPUDebug { + public INESPPUDebug GetDebugger() + { + return new NESHawkPPUDebug(this); + } + static readonly bool USE_DATABASE = true; public RomStatus RomStatus;