예제 #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 ControllerAdapter(
                List <NPortInfoT> allPorts,
                IDictionary <int, string> config,
                Func <string, string> overrideName,
                bool hasCds,
                ref SystemInfo systemInfo,
                HashSet <string> hiddenPorts,
                string controllerDeckName)
            {
                var ret = new ControllerDefinition
                {
                    Name           = controllerDeckName,
                    CategoryLabels =
                    {
                        { "Power",         "System" },
                        { "Reset",         "System" },
                        { "Previous Disk", "System" },
                        { "Next Disk",     "System" },
                    }
                };

                var finalDevices = new List <string>();

                var switchPreviousFrame = new List <byte>();

                for (int port = 0, devByteStart = 0; port < allPorts.Count; port++)
                {
                    var portInfo   = allPorts[port];
                    var deviceName = config.ContainsKey(port) ? config[port] : portInfo.DefaultDeviceShortName;
                    finalDevices.Add(deviceName);

                    if (hiddenPorts.Contains(portInfo.ShortName))
                    {
                        continue;
                    }

                    var devices = portInfo.Devices;

                    var device = devices.FirstOrDefault(a => a.ShortName == deviceName);
                    if (device == null)
                    {
                        Console.WriteLine($"Warn: unknown controller device {deviceName}");
                        device = devices.FirstOrDefault(a => a.ShortName == portInfo.DefaultDeviceShortName);
                        if (device == null)
                        {
                            throw new InvalidOperationException($"Fail: unknown controller device {portInfo.DefaultDeviceShortName}");
                        }
                    }

                    ActualPortData.Add(new PortResult
                    {
                        Port   = portInfo,
                        Device = device
                    });

                    var deviceInfo = device;
                    var category   = portInfo.FullName + " - " + deviceInfo.FullName;

                    var inputs = deviceInfo.Inputs
                                 .OrderBy(a => a.ConfigOrder);

                    foreach (var input in inputs)
                    {
                        if (input.Type == InputType.Padding)
                        {
                            continue;
                        }

                        var bitSize   = (int)input.BitSize;
                        var bitOffset = (int)input.BitOffset;
                        var byteStart = devByteStart + bitOffset / 8;
                        bitOffset %= 8;
                        var baseName = input.Name;
                        if (baseName != null)
                        {
                            baseName = overrideName(baseName);
                        }
                        var name = input.Type == InputType.ResetButton ? "Reset" : $"P{port + 1} {baseName}";

                        switch (input.Type)
                        {
                        case InputType.ResetButton:
                        case InputType.Button:
                        case InputType.ButtonCanRapid:
                        {
                            // var data = inputInfo.Extra.AsButton();
                            // TODO: Wire up data.ExcludeName
                            if (input.Type != InputType.ResetButton)
                            {
                                ret.BoolButtons.Add(name);
                                ret.CategoryLabels[name] = category;
                            }
                            _thunks.Add((c, b) =>
                                {
                                    if (c.IsPressed(name))
                                    {
                                        b[byteStart] |= (byte)(1 << bitOffset);
                                    }
                                });
                            break;
                        }

                        case InputType.Switch:
                        {
                            var data = input.Extra.AsSwitch();
                            if (data.Positions.Count > 8)
                            {
                                throw new NotImplementedException("Need code changes to support Mdfn switch with more than 8 positions");
                            }

                            // fake switches as a series of push downs that select each state
                            // imagine the "gear" selector on a Toyota Prius

                            var si = switchPreviousFrame.Count;
                            // [si]: position of this switch on the previous frame
                            switchPreviousFrame.Add((byte)data.DefaultPosition);
                            // [si + 1]: bit array of the previous state of each selector button
                            switchPreviousFrame.Add(0);

                            var names = data.Positions.Select(p => $"{name}: Set {p.Name}").ToArray();
                            if (!input.Name.StartsWith("AF ") && !input.Name.EndsWith(" AF") && !input.Name.StartsWith("Autofire "))                                     // hack: don't support some devices
                            {
                                foreach (var n in names)
                                {
                                    {
                                        ret.BoolButtons.Add(n);
                                        ret.CategoryLabels[n] = category;
                                    }
                                }
                            }

                            _thunks.Add((c, b) =>
                                {
                                    var val            = _switchPreviousFrame[si];
                                    var allOldPressed  = _switchPreviousFrame[si + 1];
                                    byte allNewPressed = 0;
                                    for (var i = 0; i < names.Length; i++)
                                    {
                                        var mask       = (byte)(1 << i);
                                        var oldPressed = allOldPressed & mask;
                                        var newPressed = c.IsPressed(names[i]) ? mask : (byte)0;
                                        if (newPressed > oldPressed)
                                        {
                                            val = (byte)i;
                                        }
                                        allNewPressed |= newPressed;
                                    }
                                    _switchPreviousFrame[si]     = val;
                                    _switchPreviousFrame[si + 1] = allNewPressed;
                                    b[byteStart] |= (byte)(val << bitOffset);
                                });
                            break;
                        }

                        case InputType.Axis:
                        {
                            var data     = input.Extra.AsAxis();
                            var fullName = $"{name} {overrideName(data.NameNeg)} / {overrideName(data.NamePos)}";

                            ret.AddAxis(fullName, 0.RangeTo(0xFFFF), 0x8000, (input.Flags & AxisFlags.InvertCo) != 0);
                            ret.CategoryLabels[fullName] = category;
                            _thunks.Add((c, b) =>
                                {
                                    var val          = c.AxisValue(fullName);
                                    b[byteStart]     = (byte)val;
                                    b[byteStart + 1] = (byte)(val >> 8);
                                });
                            break;
                        }

                        case InputType.AxisRel:
                        {
                            var data     = input.Extra.AsAxis();
                            var fullName = $"{name} {input.Extra.AsAxis().NameNeg} / {input.Extra.AsAxis().NamePos}";

                            // TODO: Mednafen docs say this range should be [-32768, 32767], and inspecting the code
                            // reveals that a 16 bit value is read, but using anywhere near this full range makes
                            // PCFX mouse completely unusable.  Maybe this is some TAS situation where average users
                            // will want a 1/400 multiplier on sensitivity but TASers might want one frame screenwide movement?
                            ret.AddAxis(fullName, (-127).RangeTo(127), 0, (input.Flags & AxisFlags.InvertCo) != 0);
                            ret.CategoryLabels[fullName] = category;
                            _thunks.Add((c, b) =>
                                {
                                    var val          = c.AxisValue(fullName);
                                    b[byteStart]     = (byte)val;
                                    b[byteStart + 1] = (byte)(val >> 8);
                                });
                            break;
                        }

                        case InputType.PointerX:
                        {
                            // I think the core expects to be sent some sort of 16 bit integer, but haven't investigated much
                            ret.AddAxis(name, systemInfo.PointerOffsetX.RangeTo(systemInfo.PointerScaleX), systemInfo.PointerOffsetX);
                            _thunks.Add((c, b) =>
                                {
                                    var val          = c.AxisValue(name);
                                    b[byteStart]     = (byte)val;
                                    b[byteStart + 1] = (byte)(val >> 8);
                                });
                            break;
                        }

                        case InputType.PointerY:
                        {
                            // I think the core expects to be sent some sort of 16 bit integer, but haven't investigated much
                            ret.AddAxis(name, systemInfo.PointerOffsetY.RangeTo(systemInfo.PointerScaleY), systemInfo.PointerOffsetY);
                            _thunks.Add((c, b) =>
                                {
                                    var val          = c.AxisValue(name);
                                    b[byteStart]     = (byte)val;
                                    b[byteStart + 1] = (byte)(val >> 8);
                                });
                            break;
                        }

                        case InputType.ButtonAnalog:
                        {
                            ret.AddAxis(name, 0.RangeTo(0xFFFF), 0);
                            ret.CategoryLabels[name] = category;
                            _thunks.Add((c, b) =>
                                {
                                    var val          = c.AxisValue(name);
                                    b[byteStart]     = (byte)val;
                                    b[byteStart + 1] = (byte)(val >> 8);
                                });
                            break;
                        }

                        case InputType.Status:
                            // TODO: wire up statuses to something (not controller, of course)
                            break;

                        default:
                        {
                            throw new NotImplementedException($"Unimplemented button type {input.Type}");
                        }
                        }
                    }
                    devByteStart += (int)deviceInfo.ByteLength;
                    if (devByteStart > MAX_INPUT_DATA)
                    {
                        throw new NotImplementedException($"More than {MAX_INPUT_DATA} input data bytes");
                    }
                }
                ret.BoolButtons.Add("Power");
                ret.BoolButtons.Add("Reset");
                if (hasCds)
                {
                    ret.BoolButtons.Add("Previous Disk");
                    ret.BoolButtons.Add("Next Disk");
                }
                Definition = ret;
                finalDevices.Add(null);
                Devices = finalDevices.ToArray();
                _switchPreviousFrame = switchPreviousFrame.ToArray();
            }
예제 #3
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())
                {
                    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 == "60FC5FA5B5ACCAF3AEFEBA73FC8BFFD3C4DAE558" ||              // Camerica Golden 5
                    cart.GameInfo.Hash == "BAD382331C30B22A908DA4BFF2759C25113CC26A" ||                     // Camerica Golden 5
                    cart.GameInfo.Hash == "40409FEC8249EFDB772E6FFB2DCD41860C6CCA23"                        // Camerica Pegasus 4-in-1
                    )
                {
                    ram[0x701] = 0xFF;
                }

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

                if (cart.GameInfo.Hash == "00C50062A2DECE99580063777590F26A253AAB6B")                 // Silva Saga
                {
                    for (int i = 0; i < Board.Wram.Length; i++)
                    {
                        Board.Wram[i] = 0xFF;
                    }
                }
            }
        }