示例#1
0
        internal void ResetControllerDefinition(bool subframe)
        {
            ControllerDefinition = null;

            ControllerDeck       = ControllerSettings.Instantiate(ppu.LightGunCallback);
            ControllerDefinition = ControllerDeck.ControllerDef;

            // controls other than the deck
            ControllerDefinition.BoolButtons.Add("Power");
            ControllerDefinition.BoolButtons.Add("Reset");
            if (Board is FDS b)
            {
                ControllerDefinition.BoolButtons.Add("FDS Eject");
                for (int i = 0; i < b.NumSides; i++)
                {
                    ControllerDefinition.BoolButtons.Add("FDS Insert " + i);
                }
            }

            if (_isVS)
            {
                ControllerDefinition.BoolButtons.Add("Insert Coin P1");
                ControllerDefinition.BoolButtons.Add("Insert Coin P2");
                ControllerDefinition.BoolButtons.Add("Service Switch");
            }

            // Add in the reset timing axis for subneshawk
            if (subframe)
            {
                ControllerDefinition.AddAxis("Reset Cycle", 0.RangeTo(500000), 0);
            }

            ControllerDefinition.MakeImmutable();
        }
示例#2
0
        public static IControllerDeck AddSystemToControllerDef(this IControllerDeck deck)
        {
            var def = deck.ControllerDef;

            //TODO FDS
            def.BoolButtons.Add("Reset");
            def.BoolButtons.Add("Power");
            def.MakeImmutable();
            return(deck);
        }
示例#3
0
        public void HardReset()
        {
            cpu = new MOS6502X();
            cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory);

            cpu.BCD_Enabled = false;
            cpu.OnExecFetch = ExecFetch;
            ppu             = new PPU(this);
            ram             = new byte[0x800];
            CIRAM           = new byte[0x800];

            // wire controllers
            // todo: allow changing this
            ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback);
            // set controller definition first time only
            if (ControllerDefinition == null)
            {
                ControllerDefinition      = new ControllerDefinition(ControllerDeck.GetDefinition());
                ControllerDefinition.Name = "NES Controller";
                // controls other than the deck
                ControllerDefinition.BoolButtons.Add("Power");
                ControllerDefinition.BoolButtons.Add("Reset");
                if (Board is FDS)
                {
                    var b = Board as FDS;
                    ControllerDefinition.BoolButtons.Add("FDS Eject");
                    for (int i = 0; i < b.NumSides; i++)
                    {
                        ControllerDefinition.BoolButtons.Add("FDS Insert " + i);
                    }
                }

                if (_isVS)
                {
                    ControllerDefinition.BoolButtons.Add("Insert Coin P1");
                    ControllerDefinition.BoolButtons.Add("Insert Coin P2");
                    ControllerDefinition.BoolButtons.Add("Service Switch");
                }
            }

            // don't replace the magicSoundProvider on reset, as it's not needed
            // if (magicSoundProvider != null) magicSoundProvider.Dispose();

            // set up region
            switch (_display_type)
            {
            case Common.DisplayType.PAL:
                apu               = new APU(this, apu, true);
                ppu.region        = PPU.Region.PAL;
                CoreComm.VsyncNum = 50;
                CoreComm.VsyncDen = 1;
                cpuclockrate      = 1662607;
                cpu_sequence      = cpu_sequence_PAL;
                _display_type     = DisplayType.PAL;
                break;

            case Common.DisplayType.NTSC:
                apu               = new APU(this, apu, false);
                ppu.region        = PPU.Region.NTSC;
                CoreComm.VsyncNum = 39375000;
                CoreComm.VsyncDen = 655171;
                cpuclockrate      = 1789773;
                cpu_sequence      = cpu_sequence_NTSC;
                break;

            // this is in bootgod, but not used at all
            case Common.DisplayType.DENDY:
                apu               = new APU(this, apu, false);
                ppu.region        = PPU.Region.Dendy;
                CoreComm.VsyncNum = 50;
                CoreComm.VsyncDen = 1;
                cpuclockrate      = 1773448;
                cpu_sequence      = cpu_sequence_NTSC;
                _display_type     = DisplayType.DENDY;
                break;

            default:
                throw new Exception("Unknown displaytype!");
            }
            if (magicSoundProvider == null)
            {
                magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate);
            }

            BoardSystemHardReset();

            // apu has some specific power up bahaviour that we will emulate here
            apu.NESHardReset();


            if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any())
            {
                for (int i = 0; i < 0x800; i++)
                {
                    ram[i] = SyncSettings.InitialWRamStatePattern[i % SyncSettings.InitialWRamStatePattern.Count];
                }
            }
            else
            {
                // check fceux's PowerNES and FCEU_MemoryRand function for more information:
                // relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack
                for (int i = 0; i < 0x800; i++)
                {
                    if ((i & 4) != 0)
                    {
                        ram[i] = 0xFF;
                    }
                    else
                    {
                        ram[i] = 0x00;
                    }
                }
            }

            SetupMemoryDomains();

            // some boards cannot have specific values in RAM upon initialization
            // Let's hard code those cases here
            // these will be defined through the gameDB exclusively for now.

            if (cart.DB_GameInfo != null)
            {
                if (cart.DB_GameInfo.Hash == "60FC5FA5B5ACCAF3AEFEBA73FC8BFFD3C4DAE558" ||              // Camerica Golden 5
                    cart.DB_GameInfo.Hash == "BAD382331C30B22A908DA4BFF2759C25113CC26A" ||                     // Camerica Golden 5
                    cart.DB_GameInfo.Hash == "40409FEC8249EFDB772E6FFB2DCD41860C6CCA23"                        // Camerica Pegasus 4-in-1
                    )
                {
                    ram[0x701] = 0xFF;
                }
            }
        }
示例#4
0
        protected override void RunImport()
        {
            var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName;

            Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName;

            using var r = new BinaryReader(SourceFile.Open(FileMode.Open, FileAccess.Read));
            var signature = new string(r.ReadChars(4));

            if (signature != "FCM\x1A")
            {
                Result.Errors.Add("This is not a valid .FCM file.");
                return;
            }

            Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NES";

            var syncSettings = new NES.NESSyncSettings();

            var controllerSettings = new NESControlSettings
            {
                NesLeftPort  = nameof(ControllerNES),
                NesRightPort = nameof(ControllerNES)
            };

            _deck = controllerSettings.Instantiate((x, y) => true);
            AddDeckControlButtons();

            // 004 4-byte little-endian unsigned int: version number, must be 2
            uint version = r.ReadUInt32();

            if (version != 2)
            {
                Result.Errors.Add(".FCM movie version must always be 2.");
                return;
            }

            Result.Movie.Comments.Add($"{MovieOrigin} .FCM version {version}");

            // 008 1-byte flags
            byte flags = r.ReadByte();

            /*
             * bit 0: reserved, set to 0
             * bit 1:
             * if "0", movie begins from an embedded "quicksave" snapshot
             * if "1", movie begins from reset or power-on[1]
             */
            if (((flags >> 1) & 0x1) == 0)
            {
                Result.Errors.Add("Movies that begin with a savestate are not supported.");
                return;
            }

            /*
             * bit 2:
             * if "0", NTSC timing
             * if "1", "PAL" timing
             * Starting with version 0.98.12 released on September 19, 2004, a "PAL" flag was added to the header but
             * unfortunately it is not reliable - the emulator does not take the "PAL" setting from the ROM, but from a user
             * preference. This means that this site cannot calculate movie lengths reliably.
             */
            bool pal = ((flags >> 2) & 0x1) != 0;

            Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();

            // other: reserved, set to 0
            bool syncHack = ((flags >> 4) & 0x1) != 0;

            Result.Movie.Comments.Add($"SyncHack {syncHack}");

            // 009 1-byte flags: reserved, set to 0
            r.ReadByte();

            // 00A 1-byte flags: reserved, set to 0
            r.ReadByte();

            // 00B 1-byte flags: reserved, set to 0
            r.ReadByte();

            // 00C 4-byte little-endian unsigned int: number of frames
            uint frameCount = r.ReadUInt32();

            // 010 4-byte little-endian unsigned int: rerecord count
            uint rerecordCount = r.ReadUInt32();

            Result.Movie.Rerecords = rerecordCount;

            /*
             * 018 4-byte little-endian unsigned int: offset to the savestate inside file
             * The savestate offset is <header_size + length_of_metadata_in_bytes + padding>. The savestate offset should be
             * 4-byte aligned. At the savestate offset there is a savestate file. The savestate exists even if the movie is
             * reset-based.
             */
            r.ReadUInt32();

            // 01C 4-byte little-endian unsigned int: offset to the controller data inside file
            uint firstFrameOffset = r.ReadUInt32();

            // 020 16-byte md5sum of the ROM used
            byte[] md5 = r.ReadBytes(16);
            Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();

            // 030 4-byte little-endian unsigned int: version of the emulator used
            uint emuVersion = r.ReadUInt32();

            Result.Movie.Comments.Add($"{EmulationOrigin} FCEU {emuVersion}");

            // 034 name of the ROM used - UTF8 encoded nul-terminated string.
            var gameBytes = new List <byte>();

            while (r.PeekChar() != 0)
            {
                gameBytes.Add(r.ReadByte());
            }

            // Advance past null byte.
            r.ReadByte();
            string gameName = Encoding.UTF8.GetString(gameBytes.ToArray());

            Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;

            /*
             * After the header comes "metadata", which is UTF8-coded movie title string. The metadata begins after the ROM
             * name and ends at the savestate offset. This string is displayed as "Author Info" in the Windows version of the
             * emulator.
             */
            var authorBytes = new List <byte>();

            while (r.PeekChar() != 0)
            {
                authorBytes.Add(r.ReadByte());
            }

            // Advance past null byte.
            r.ReadByte();
            string author = Encoding.UTF8.GetString(authorBytes.ToArray());

            Result.Movie.HeaderEntries[HeaderKeys.Author] = author;

            // Advance to first byte of input data.
            r.BaseStream.Position = firstFrameOffset;

            var controllers = new SimpleController
            {
                Definition = _deck.GetDefinition()
            };

            string[] buttons = { "A", "B", "Select", "Start", "Up", "Down", "Left", "Right" };
            bool     fds     = false;

            int frame = 1;

            while (frame <= frameCount)
            {
                byte update = r.ReadByte();

                // aa: Number of delta bytes to follow
                int delta  = (update >> 5) & 0x3;
                int frames = 0;

                /*
                 * The delta byte(s) indicate the number of emulator frames between this update and the next update. It is
                 * encoded in little-endian format and its size depends on the magnitude of the delta:
                 * Delta of:	  Number of bytes:
                 * 0			  0
                 * 1-255		  1
                 * 256-65535	  2
                 * 65536-(2^24-1) 3
                 */
                for (int b = 0; b < delta; b++)
                {
                    frames += r.ReadByte() * (int)Math.Pow(2, b * 8);
                }

                frame += frames;
                while (frames > 0)
                {
                    Result.Movie.AppendFrame(controllers);
                    if (controllers["Reset"])
                    {
                        controllers["Reset"] = false;
                    }

                    frames--;
                }

                if (((update >> 7) & 0x1) != 0)
                {
                    // Control update: 0x1aabbbbb
                    bool reset   = false;
                    int  command = update & 0x1F;

                    // 0xbbbbb:
                    controllers["Reset"] = command == 1;
                    switch (command)
                    {
                    case 0:                             // Do nothing
                        break;

                    case 1:                             // Reset
                        reset = true;
                        break;

                    case 2:                             // Power cycle
                        reset = true;
                        if (frame != 1)
                        {
                            controllers["Power"] = true;
                        }

                        break;

                    case 7:                             // VS System Insert Coin
                        Result.Warnings.Add($"Unsupported command: VS System Insert Coin at frame {frame}");
                        break;

                    case 8:                             // VS System Dipswitch 0 Toggle
                        Result.Warnings.Add($"Unsupported command: VS System Dipswitch 0 Toggle at frame {frame}");
                        break;

                    case 24:                             // FDS Insert
                        fds = true;
                        Result.Warnings.Add($"Unsupported command: FDS Insert at frame {frame}");
                        break;

                    case 25:                             // FDS Eject
                        fds = true;
                        Result.Warnings.Add($"Unsupported command: FDS Eject at frame {frame}");
                        break;

                    case 26:                             // FDS Select Side
                        fds = true;
                        Result.Warnings.Add($"Unsupported command: FDS Select Side at frame {frame}");
                        break;

                    default:
                        Result.Warnings.Add($"Unknown command: {command} detected at frame {frame}");
                        break;
                    }

                    /*
                     * 1 Even if the header says "movie begins from reset", the file still contains a quicksave, and the
                     * quicksave is actually loaded. This flag can't therefore be trusted. To check if the movie actually
                     * begins from reset, one must analyze the controller data and see if the first non-idle command in the
                     * file is a Reset or Power Cycle type control command.
                     */
                    if (!reset && frame == 1)
                    {
                        Result.Errors.Add("Movies that begin with a savestate are not supported.");
                        return;
                    }
                }
                else
                {
                    /*
                     * Controller update: 0aabbccc
                     * bb: Gamepad number minus one (?)
                     */
                    int player = ((update >> 3) & 0x3) + 1;
                    if (player > 2)
                    {
                        Result.Errors.Add("Four score not yet supported.");
                        return;
                    }

                    /*
                     * ccc:
                     * 0	  A
                     * 1	  B
                     * 2	  Select
                     * 3	  Start
                     * 4	  Up
                     * 5	  Down
                     * 6	  Left
                     * 7	  Right
                     */
                    int button = update & 0x7;

                    /*
                     * The controller update toggles the affected input. Controller update data is emitted to the movie file
                     * only when the state of the controller changes.
                     */
                    controllers[$"P{player} {buttons[button]}"] = !controllers[$"P{player} {buttons[button]}"];
                }

                Result.Movie.AppendFrame(controllers);
            }

            if (fds)
            {
                Result.Movie.HeaderEntries[HeaderKeys.BoardName] = "FDS";
            }

            syncSettings.Controls         = controllerSettings;
            Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
        }
示例#5
0
        public void HardReset()
        {
            cpu = new MOS6502X <CpuLink>(new CpuLink(this))
            {
                BCD_Enabled = false
            };

            ppu   = new PPU(this);
            ram   = new byte[0x800];
            CIRAM = new byte[0x800];

            // wire controllers
            // todo: allow changing this
            ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback);
            // set controller definition first time only
            if (ControllerDefinition == null)
            {
                ControllerDefinition      = new ControllerDefinition(ControllerDeck.GetDefinition());
                ControllerDefinition.Name = "NES Controller";
                // controls other than the deck
                ControllerDefinition.BoolButtons.Add("Power");
                ControllerDefinition.BoolButtons.Add("Reset");
                if (Board is FDS)
                {
                    var b = Board as FDS;
                    ControllerDefinition.BoolButtons.Add("FDS Eject");
                    for (int i = 0; i < b.NumSides; i++)
                    {
                        ControllerDefinition.BoolButtons.Add("FDS Insert " + i);
                    }
                }

                if (_isVS)
                {
                    ControllerDefinition.BoolButtons.Add("Insert Coin P1");
                    ControllerDefinition.BoolButtons.Add("Insert Coin P2");
                    ControllerDefinition.BoolButtons.Add("Service Switch");
                }
            }

            // Add in the reset timing float control for subneshawk
            if (using_reset_timing && (ControllerDefinition.FloatControls.Count() == 0))
            {
                ControllerDefinition.FloatControls.Add("Reset Cycle");
                ControllerDefinition.FloatRanges.Add(new ControllerDefinition.FloatRange(0, 0, 500000));
            }

            // don't replace the magicSoundProvider on reset, as it's not needed
            // if (magicSoundProvider != null) magicSoundProvider.Dispose();

            // set up region
            switch (_display_type)
            {
            case Common.DisplayType.PAL:
                apu           = new APU(this, apu, true);
                ppu.region    = PPU.Region.PAL;
                VsyncNum      = 50;
                VsyncDen      = 1;
                cpuclockrate  = 1662607;
                cpu_sequence  = cpu_sequence_PAL;
                _display_type = DisplayType.PAL;
                ClockRate     = 5320342.5;
                break;

            case Common.DisplayType.NTSC:
                apu          = new APU(this, apu, false);
                ppu.region   = PPU.Region.NTSC;
                VsyncNum     = 39375000;
                VsyncDen     = 655171;
                cpuclockrate = 1789773;
                cpu_sequence = cpu_sequence_NTSC;
                ClockRate    = 5369318.1818181818181818181818182;
                break;

            // this is in bootgod, but not used at all
            case Common.DisplayType.Dendy:
                apu           = new APU(this, apu, false);
                ppu.region    = PPU.Region.Dendy;
                VsyncNum      = 50;
                VsyncDen      = 1;
                cpuclockrate  = 1773448;
                cpu_sequence  = cpu_sequence_NTSC;
                _display_type = DisplayType.Dendy;
                ClockRate     = 5320342.5;
                break;

            default:
                throw new Exception("Unknown displaytype!");
            }

            blip.SetRates((uint)cpuclockrate, 44100);

            BoardSystemHardReset();

            // apu has some specific power up bahaviour that we will emulate here
            apu.NESHardReset();

            if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any())
            {
                for (int i = 0; i < 0x800; i++)
                {
                    ram[i] = SyncSettings.InitialWRamStatePattern[i % SyncSettings.InitialWRamStatePattern.Count];
                }
            }
            else
            {
                // check fceux's PowerNES and FCEU_MemoryRand function for more information:
                // relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack
                for (int i = 0; i < 0x800; i++)
                {
                    if ((i & 4) != 0)
                    {
                        ram[i] = 0xFF;
                    }
                    else
                    {
                        ram[i] = 0x00;
                    }
                }
            }

            SetupMemoryDomains();

            // some boards cannot have specific values in RAM upon initialization
            // Let's hard code those cases here
            // these will be defined through the gameDB exclusively for now.

            if (cart.DB_GameInfo != null)
            {
                if (cart.DB_GameInfo.Hash == "60FC5FA5B5ACCAF3AEFEBA73FC8BFFD3C4DAE558" ||              // Camerica Golden 5
                    cart.DB_GameInfo.Hash == "BAD382331C30B22A908DA4BFF2759C25113CC26A" ||                     // Camerica Golden 5
                    cart.DB_GameInfo.Hash == "40409FEC8249EFDB772E6FFB2DCD41860C6CCA23"                        // Camerica Pegasus 4-in-1
                    )
                {
                    ram[0x701] = 0xFF;
                }

                if (cart.DB_GameInfo.Hash == "68ABE1E49C9E9CCEA978A48232432C252E5912C0")                 // Dancing Blocks
                {
                    ram[0xEC] = 0;
                    ram[0xED] = 0;
                }

                if (cart.DB_GameInfo.Hash == "00C50062A2DECE99580063777590F26A253AAB6B")                 // Silva Saga
                {
                    for (int i = 0; i < Board.WRAM.Length; i++)
                    {
                        Board.WRAM[i] = 0xFF;
                    }
                }
            }
        }
示例#6
0
		public void HardReset()
		{
			cpu = new MOS6502X();
			cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory);

			cpu.BCD_Enabled = false;
			cpu.OnExecFetch = ExecFetch;
			ppu = new PPU(this);
			ram = new byte[0x800];
			CIRAM = new byte[0x800];
			
			// wire controllers
			// todo: allow changing this
			ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback);
			// set controller definition first time only
			if (ControllerDefinition == null)
			{
				ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition());
				ControllerDefinition.Name = "NES Controller";
				// controls other than the deck
				ControllerDefinition.BoolButtons.Add("Power");
				ControllerDefinition.BoolButtons.Add("Reset");
				if (Board is FDS)
				{
					var b = Board as FDS;
					ControllerDefinition.BoolButtons.Add("FDS Eject");
					for (int i = 0; i < b.NumSides; i++)
						ControllerDefinition.BoolButtons.Add("FDS Insert " + i);
				}
			}

			// don't replace the magicSoundProvider on reset, as it's not needed
			// if (magicSoundProvider != null) magicSoundProvider.Dispose();

			// set up region
			switch (_display_type)
			{
				case Common.DisplayType.PAL:
					apu = new APU(this, apu, true);
					ppu.region = PPU.Region.PAL;
					CoreComm.VsyncNum = 50;
					CoreComm.VsyncDen = 1;
					cpuclockrate = 1662607;
					cpu_sequence = cpu_sequence_PAL;
					_display_type = DisplayType.PAL;
					break;
				case Common.DisplayType.NTSC:
					apu = new APU(this, apu, false);
					ppu.region = PPU.Region.NTSC;
					CoreComm.VsyncNum = 39375000;
					CoreComm.VsyncDen = 655171;
					cpuclockrate = 1789773;
					cpu_sequence = cpu_sequence_NTSC;
					break;
				// this is in bootgod, but not used at all
				case Common.DisplayType.DENDY:
					apu = new APU(this, apu, false);
					ppu.region = PPU.Region.Dendy;
					CoreComm.VsyncNum = 50;
					CoreComm.VsyncDen = 1;
					cpuclockrate = 1773448;
					cpu_sequence = cpu_sequence_NTSC;
					_display_type = DisplayType.DENDY;
					break;
				default:
					throw new Exception("Unknown displaytype!");
			}
			if (magicSoundProvider == null)
				magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate);

			BoardSystemHardReset();

			//check fceux's PowerNES and FCEU_MemoryRand function for more information:
			//relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack
			for(int i=0;i<0x800;i++) if((i&4)!=0) ram[i] = 0xFF; else ram[i] = 0x00;

			SetupMemoryDomains();

			//in this emulator, reset takes place instantaneously
			cpu.PC = (ushort)(ReadMemory(0xFFFC) | (ReadMemory(0xFFFD) << 8));
			cpu.P = 0x34;
			cpu.S = 0xFD;
		}
示例#7
0
        protected override void RunImport()
        {
            using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read);
            using var r  = new BinaryReader(fs);
            // 000 4-byte signature: 46 4D 56 1A "FMV\x1A"
            var signature = new string(r.ReadChars(4));

            if (signature != "FMV\x1A")
            {
                Result.Errors.Add("This is not a valid .FMV file.");
                return;
            }

            // 004 1-byte flags:
            byte flags = r.ReadByte();

            // bit 7: 0=reset-based, 1=savestate-based
            if (((flags >> 2) & 0x1) != 0)
            {
                Result.Errors.Add("Movies that begin with a savestate are not supported.");
                return;
            }

            Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "NES";
            var syncSettings = new NES.NESSyncSettings();

            // other bits: unknown, set to 0
            // 005 1-byte flags:
            flags = r.ReadByte();

            // bit 5: is a FDS recording
            bool fds;

            if (((flags >> 5) & 0x1) != 0)
            {
                fds = true;
                Result.Movie.HeaderEntries[HeaderKeys.BOARDNAME] = "FDS";
            }
            else
            {
                fds = false;
            }

            // bit 6: uses controller 2
            bool controller2 = ((flags >> 6) & 0x1) != 0;

            // bit 7: uses controller 1
            bool controller1 = ((flags >> 7) & 0x1) != 0;

            // other bits: unknown, set to 0
            // 006 4-byte little-endian unsigned int: unknown, set to 00000000
            r.ReadInt32();

            // 00A 4-byte little-endian unsigned int: rerecord count minus 1
            uint rerecordCount = r.ReadUInt32();

            /*
             * The rerecord count stored in the file is the number of times a savestate was loaded. If a savestate was never
             * loaded, the number is 0. Famtasia however displays "1" in such case. It always adds 1 to the number found in
             * the file.
             */
            Result.Movie.Rerecords = rerecordCount + 1;

            // 00E 2-byte little-endian unsigned int: unknown, set to 0000
            r.ReadInt16();

            // 010 64-byte zero-terminated emulator identifier string
            string emuVersion = NullTerminated(new string(r.ReadChars(64)));

            Result.Movie.Comments.Add($"{EmulationOrigin} Famtasia version {emuVersion}");
            Result.Movie.Comments.Add($"{MovieOrigin} .FMV");

            // 050 64-byte zero-terminated movie title string
            string description = NullTerminated(new string(r.ReadChars(64)));

            Result.Movie.Comments.Add(description);
            if (!controller1 && !controller2 && !fds)
            {
                Result.Warnings.Add("No input recorded.");
            }

            var controllerSettings = new NESControlSettings
            {
                NesLeftPort  = controller1 ? nameof(ControllerNES) : nameof(UnpluggedNES),
                NesRightPort = controller2 ? nameof(ControllerNES) : nameof(UnpluggedNES)
            };

            _deck = controllerSettings.Instantiate((x, y) => true);
            syncSettings.Controls.NesLeftPort  = controllerSettings.NesLeftPort;
            syncSettings.Controls.NesRightPort = controllerSettings.NesRightPort;

            AddDeckControlButtons();

            var controllers = new SimpleController
            {
                Definition = _deck.GetDefinition()
            };

            /*
             * 01 Right
             * 02 Left
             * 04 Up
             * 08 Down
             * 10 B
             * 20 A
             * 40 Select
             * 80 Start
             */
            string[] buttons = { "Right", "Left", "Up", "Down", "B", "A", "Select", "Start" };
            bool[]   masks   = { controller1, controller2, fds };

            /*
             * The file has no terminator byte or frame count. The number of frames is the <filesize minus 144> divided by
             * <number of bytes per frame>.
             */
            int bytesPerFrame = 0;

            for (int player = 1; player <= masks.Length; player++)
            {
                if (masks[player - 1])
                {
                    bytesPerFrame++;
                }
            }

            long frameCount = (fs.Length - 144) / bytesPerFrame;

            for (long frame = 1; frame <= frameCount; frame++)
            {
                /*
                 * Each frame consists of 1 or more bytes. Controller 1 takes 1 byte, controller 2 takes 1 byte, and the FDS
                 * data takes 1 byte. If all three exist, the frame is 3 bytes. For example, if the movie is a regular NES game
                 * with only controller 1 data, a frame is 1 byte.
                 */
                for (int player = 1; player <= masks.Length; player++)
                {
                    if (!masks[player - 1])
                    {
                        continue;
                    }

                    byte controllerState = r.ReadByte();
                    if (player != 3)
                    {
                        for (int button = 0; button < buttons.Length; button++)
                        {
                            controllers[$"P{player} {buttons[button]}"] = ((controllerState >> button) & 0x1) != 0;
                        }
                    }
                    else
                    {
                        Result.Warnings.Add("FDS commands are not properly supported.");
                    }
                }

                Result.Movie.AppendFrame(controllers);
            }

            Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
        }
示例#8
0
文件: Core.cs 项目: stuff2600/RAEmus
        public void HardReset()
        {
            cpu = new MOS6502X();
            cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory);

            cpu.BCD_Enabled = false;
            cpu.OnExecFetch = ExecFetch;
            ppu             = new PPU(this);
            ram             = new byte[0x800];
            CIRAM           = new byte[0x800];

            // wire controllers
            // todo: allow changing this
            ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback);
            // set controller definition first time only
            if (ControllerDefinition == null)
            {
                ControllerDefinition      = new ControllerDefinition(ControllerDeck.GetDefinition());
                ControllerDefinition.Name = "NES Controller";
                // controls other than the deck
                ControllerDefinition.BoolButtons.Add("Power");
                ControllerDefinition.BoolButtons.Add("Reset");
                if (board is FDS)
                {
                    var b = board as FDS;
                    ControllerDefinition.BoolButtons.Add("FDS Eject");
                    for (int i = 0; i < b.NumSides; i++)
                    {
                        ControllerDefinition.BoolButtons.Add("FDS Insert " + i);
                    }
                }
            }

            // don't replace the magicSoundProvider on reset, as it's not needed
            // if (magicSoundProvider != null) magicSoundProvider.Dispose();

            // set up region
            switch (_display_type)
            {
            case Common.DisplayType.PAL:
                apu               = new APU(this, apu, true);
                ppu.region        = PPU.Region.PAL;
                CoreComm.VsyncNum = 50;
                CoreComm.VsyncDen = 1;
                cpuclockrate      = 1662607;
                cpu_sequence      = cpu_sequence_PAL;
                _display_type     = DisplayType.PAL;
                break;

            case Common.DisplayType.NTSC:
                apu               = new APU(this, apu, false);
                ppu.region        = PPU.Region.NTSC;
                CoreComm.VsyncNum = 39375000;
                CoreComm.VsyncDen = 655171;
                cpuclockrate      = 1789773;
                cpu_sequence      = cpu_sequence_NTSC;
                break;

            // this is in bootgod, but not used at all
            case Common.DisplayType.DENDY:
                apu               = new APU(this, apu, false);
                ppu.region        = PPU.Region.Dendy;
                CoreComm.VsyncNum = 50;
                CoreComm.VsyncDen = 1;
                cpuclockrate      = 1773448;
                cpu_sequence      = cpu_sequence_NTSC;
                _display_type     = DisplayType.DENDY;
                break;

            default:
                throw new Exception("Unknown displaytype!");
            }
            if (magicSoundProvider == null)
            {
                magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate);
            }

            BoardSystemHardReset();

            //check fceux's PowerNES and FCEU_MemoryRand function for more information:
            //relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack
            for (int i = 0; i < 0x800; i++)
            {
                if ((i & 4) != 0)
                {
                    ram[i] = 0xFF;
                }
                else
                {
                    ram[i] = 0x00;
                }
            }

            SetupMemoryDomains();

            //in this emulator, reset takes place instantaneously
            cpu.PC = (ushort)(ReadMemory(0xFFFC) | (ReadMemory(0xFFFD) << 8));
            cpu.P  = 0x34;
            cpu.S  = 0xFD;
        }
        public void HardReset()
        {
            cpu = new MOS6502X <CpuLink>(new CpuLink(this))
            {
                BCD_Enabled = false
            };

            ppu   = new PPU(this);
            ram   = new byte[0x800];
            CIRAM = new byte[0x800];

            // wire controllers
            // todo: allow changing this
            ControllerDeck = ControllerSettings.Instantiate(ppu.LightGunCallback);
            // set controller definition first time only
            if (ControllerDefinition == null)
            {
                ControllerDefinition = new ControllerDefinition(ControllerDeck.GetDefinition())
                {
                    Name = "NES Controller"
                };

                // controls other than the deck
                ControllerDefinition.BoolButtons.Add("Power");
                ControllerDefinition.BoolButtons.Add("Reset");
                if (Board is FDS b)
                {
                    ControllerDefinition.BoolButtons.Add("FDS Eject");
                    for (int i = 0; i < b.NumSides; i++)
                    {
                        ControllerDefinition.BoolButtons.Add("FDS Insert " + i);
                    }
                }

                if (_isVS)
                {
                    ControllerDefinition.BoolButtons.Add("Insert Coin P1");
                    ControllerDefinition.BoolButtons.Add("Insert Coin P2");
                    ControllerDefinition.BoolButtons.Add("Service Switch");
                }
            }

            // Add in the reset timing axis for subneshawk
            if (using_reset_timing && ControllerDefinition.Axes.Count == 0)
            {
                ControllerDefinition.AddAxis("Reset Cycle", 0.RangeTo(500000), 0);
            }

            // don't replace the magicSoundProvider on reset, as it's not needed
            // if (magicSoundProvider != null) magicSoundProvider.Dispose();

            // set up region
            switch (_display_type)
            {
            case DisplayType.PAL:
                apu           = new APU(this, apu, true);
                ppu.region    = PPU.Region.PAL;
                cpuclockrate  = 1662607;
                VsyncNum      = cpuclockrate * 2;
                VsyncDen      = 66495;
                cpu_sequence  = cpu_sequence_PAL;
                _display_type = DisplayType.PAL;
                ClockRate     = 5320342.5;
                break;

            case DisplayType.NTSC:
                apu          = new APU(this, apu, false);
                ppu.region   = PPU.Region.NTSC;
                cpuclockrate = 1789773;
                VsyncNum     = cpuclockrate * 2;
                VsyncDen     = 59561;
                cpu_sequence = cpu_sequence_NTSC;
                ClockRate    = 5369318.1818181818181818181818182;
                break;

            // this is in bootgod, but not used at all
            case DisplayType.Dendy:
                apu           = new APU(this, apu, false);
                ppu.region    = PPU.Region.Dendy;
                cpuclockrate  = 1773448;
                VsyncNum      = cpuclockrate;
                VsyncDen      = 35464;
                cpu_sequence  = cpu_sequence_NTSC;
                _display_type = DisplayType.Dendy;
                ClockRate     = 5320342.5;
                break;

            default:
                throw new Exception("Unknown displaytype!");
            }

            blip.SetRates((uint)cpuclockrate, 44100);

            BoardSystemHardReset();

            // apu has some specific power up bahaviour that we will emulate here
            apu.NESHardReset();

            if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any())
            {
                for (int i = 0; i < 0x800; i++)
                {
                    ram[i] = SyncSettings.InitialWRamStatePattern[i % SyncSettings.InitialWRamStatePattern.Count];
                }
            }
            else
            {
                // check fceux's PowerNES and FCEU_MemoryRand function for more information:
                // relevant games: Cybernoid; Minna no Taabou no Nakayoshi Daisakusen; Huang Di; and maybe mechanized attack
                for (int i = 0; i < 0x800; i++)
                {
                    if ((i & 4) != 0)
                    {
                        ram[i] = 0xFF;
                    }
                    else
                    {
                        ram[i] = 0x00;
                    }
                }
            }

            SetupMemoryDomains();

            // some boards cannot have specific values in RAM upon initialization
            // Let's hard code those cases here
            // these will be defined through the gameDB exclusively for now.

            if (cart.GameInfo != null)
            {
                if (cart.GameInfo.Hash is RomChecksums.CamericaGolden5 or RomChecksums.CamericaGolden5Overdump or RomChecksums.CamericaPegasus4in1)
                {
                    ram[0x701] = 0xFF;
                }
                else if (cart.GameInfo.Hash == RomChecksums.DancingBlocks)
                {
                    ram[0xEC] = 0;
                    ram[0xED] = 0;
                }
                else if (cart.GameInfo.Hash == RomChecksums.SilvaSaga)
                {
                    for (int i = 0; i < Board.Wram.Length; i++)
                    {
                        Board.Wram[i] = 0xFF;
                    }
                }
            }