using System; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Nintendo.NES; namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk { [Core( "SubNESHawk", "", isPorted: false, isReleased: true)] [ServiceNotApplicable(typeof(IDriveLight))] public partial class SubNESHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, ISettable, INESPPUViewable { public NES.NES subnes; // needed for movies to accurately calculate timing public int VBL_CNT; [CoreConstructor("NES")] public SubNESHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) { var ser = new BasicServiceProvider(this); subnesSettings = (NES.NES.NESSettings)settings ?? new NES.NES.NESSettings(); subnesSyncSettings = (NES.NES.NESSyncSettings)syncSettings ?? new NES.NES.NESSyncSettings(); CoreComm = comm; subnes = new NES.NES(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, game, rom, subnesSettings, subnesSyncSettings); ser.Register(subnes.videoProvider); ser.Register(subnes); _tracer = new TraceBuffer { Header = "6502: PC, machine code, mnemonic, operands, registers (A, X, Y, P, SP), flags (NVTBDIZCR), CPU Cycle, PPU Cycle" }; ser.Register(_tracer); ServiceProvider = ser; (ServiceProvider as BasicServiceProvider).Register(subnes._memoryDomains); subnes.using_reset_timing = true; HardReset(); current_cycle = 0; subnes.cpu.ext_ppu_cycle = current_cycle; VBL_CNT = 0; } public void HardReset() { subnes.HardReset(); } public void SoftReset() { subnes.Board.NESSoftReset(); subnes.cpu.NESSoftReset(); subnes.apu.NESSoftReset(); subnes.ppu.NESSoftReset(); current_cycle = 0; subnes.cpu.ext_ppu_cycle = current_cycle; } public DisplayType Region => DisplayType.NTSC; public int _frame = 0; private readonly ITraceable _tracer; private void ExecFetch(ushort addr) { MemoryCallbacks.CallExecutes(addr, "System Bus"); } #region ISettable private NES.NES.NESSettings subnesSettings = new NES.NES.NESSettings(); public NES.NES.NESSyncSettings subnesSyncSettings = new NES.NES.NESSyncSettings(); public NES.NES.NESSettings GetSettings() { return subnesSettings.Clone(); } public NES.NES.NESSyncSettings GetSyncSettings() { return subnesSyncSettings.Clone(); } public bool PutSettings(NES.NES.NESSettings o) { subnesSettings = o; if (subnesSettings.ClipLeftAndRight) { subnes.videoProvider.left = 8; subnes.videoProvider.right = 247; } else { subnes.videoProvider.left = 0; subnes.videoProvider.right = 255; } CoreComm.ScreenLogicalOffsetX = subnes.videoProvider.left; CoreComm.ScreenLogicalOffsetY = Region == DisplayType.NTSC ? subnesSettings.NTSC_TopLine : subnesSettings.PAL_TopLine; subnes.SetPalette(subnesSettings.Palette); subnes.apu.m_vol = subnesSettings.APU_vol; return false; } public bool PutSyncSettings(NES.NES.NESSyncSettings o) { bool ret = NES.NES.NESSyncSettings.NeedsReboot(subnesSyncSettings, o); subnesSyncSettings = o; return ret; } #endregion #region PPU Viewable public int[] GetPalette() { return subnes.palette_compiled; } public bool BGBaseHigh { get { return subnes.ppu.reg_2000.bg_pattern_hi; } } public bool SPBaseHigh { get { return subnes.ppu.reg_2000.obj_pattern_hi; } } public bool SPTall { get { return subnes.ppu.reg_2000.obj_size_16; } } public byte[] GetPPUBus() { byte[] ret = new byte[0x3000]; for (int i = 0; i < 0x3000; i++) { ret[i] = subnes.ppu.ppubus_peek(i); } return ret; } public byte[] GetPalRam() { return subnes.ppu.PALRAM; } public byte[] GetOam() { return subnes.ppu.OAM; } public byte PeekPPU(int addr) { return subnes.Board.PeekPPU(addr); } public byte[] GetExTiles() { if (subnes.Board is ExROM) { return subnes.Board.VROM ?? subnes.Board.VRAM; } else { throw new InvalidOperationException(); } } public bool ExActive { get { return subnes.Board is ExROM && (subnes.Board as ExROM).ExAttrActive; } } public byte[] GetExRam() { if (subnes.Board is ExROM) { return (subnes.Board as ExROM).GetExRAMArray(); } else { throw new InvalidOperationException(); } } public MemoryDomain GetCHRROM() { return _memoryDomains["CHR VROM"]; } public void InstallCallback1(Action cb, int sl) { subnes.ppu.NTViewCallback = new PPU.DebugCallback { Callback = cb, Scanline = sl }; } public void InstallCallback2(Action cb, int sl) { subnes.ppu.PPUViewCallback = new PPU.DebugCallback { Callback = cb, Scanline = sl }; } public void RemoveCallback1() { subnes.ppu.NTViewCallback = null; } public void RemoveCallback2() { subnes.ppu.PPUViewCallback = null; } #endregion } }