示例#1
0
        internal void HandleMemoryChange(MMR register, byte value, bool updatedEnabledFlag = true)
        {
            if (!_state.Enabled)
            {
                // When powered off, the internal length can be changed (DMG only)
                switch (register)
                {
                case MMR.NR11:
                    _channel1.ChangeLength(value);
                    break;

                case MMR.NR21:
                    _channel2.ChangeLength(value);
                    break;

                case MMR.NR31:
                    _channel3.ChangeLength(value);
                    break;

                case MMR.NR41:
                    _channel4.ChangeLength(value);
                    break;
                }

                // Other than turning on, all other writes are ignored
                if (register != MMR.NR52)
                {
                    return;
                }
            }

            // We store previous channel status
            bool channel1Enabled = _channel1.Enabled;
            bool channel2Enabled = _channel2.Enabled;
            bool channel3Enabled = _channel3.Enabled;
            bool channel4Enabled = _channel4.Enabled;
            bool prevEnabled     = _state.Enabled;

            switch (register)
            {
            case MMR.NR10:
            case MMR.NR11:
            case MMR.NR12:
            case MMR.NR13:
            case MMR.NR14:
                _channel1.HandleMemoryChange(register, value);
                break;

            case MMR.NR21:
            case MMR.NR22:
            case MMR.NR23:
            case MMR.NR24:
                _channel2.HandleMemoryChange(register, value);
                break;

            case MMR.NR30:
            case MMR.NR31:
            case MMR.NR32:
            case MMR.NR33:
            case MMR.NR34:
                _channel3.HandleMemoryChange(register, value);
                break;

            case MMR.NR41:
            case MMR.NR42:
            case MMR.NR43:
            case MMR.NR44:
                _channel4.HandleMemoryChange(register, value);
                break;

            case MMR.NR50:
                // NOTE(Cristian): No Vin support
                _memory.LowLevelWrite((ushort)register, value);
                break;

            case MMR.NR51:
                // TODO(Cristian): Implement this logic
                _state.OutputChannel1Left  = ((value & 0x01) != 0);
                _state.OutputChannel2Left  = ((value & 0x02) != 0);
                _state.OutputChannel3Left  = ((value & 0x04) != 0);
                _state.OutputChannel4Left  = ((value & 0x08) != 0);
                _state.OutputChannel1Right = ((value & 0x10) != 0);
                _state.OutputChannel2Right = ((value & 0x20) != 0);
                _state.OutputChannel3Right = ((value & 0x40) != 0);
                _state.OutputChannel4Right = ((value & 0x80) != 0);
                _memory.LowLevelWrite((ushort)register, value);
                break;

            case MMR.NR52:
                bool apuEnabled = (Utils.UtilFuncs.TestBit(value, 7) != 0);
                if (!apuEnabled)
                {
                    // Powering down the APU should power down all the registers
                    //for (ushort r = (ushort)MMR.NR10; r < (ushort)MMR.NR52; ++r)
                    //{
                    //  HandleMemoryChange((MMR)r, 0, false);
                    //}
                    _channel1.PowerOff();
                    _channel1.SetEnabled(false);
                    _channel2.PowerOff();
                    _channel2.SetEnabled(false);
                    _channel3.PowerOff();
                    _channel3.SetEnabled(false);
                    _channel4.PowerOff();
                    _channel4.SetEnabled(false);

                    _memory.LowLevelWrite((ushort)MMR.NR50, 0);
                    _memory.LowLevelWrite((ushort)MMR.NR51, 0);
                }
                else if (!_state.Enabled)
                {
                    _frameSequencer.Reset();
                }
                // We update at the end because otherwise the recursive calls would
                // be rejected by the guard
                _state.Enabled = apuEnabled;
                break;
            }

            // NOTE(Cristian): This is an "optimization" for when NR52 is disabled,
            //                 All the registers are set to 0. A normal recursive call
            //                 would write the NR52 memory several times unnecessarily
            if (!updatedEnabledFlag)
            {
                return;
            }

            // We compare to see if we have to change the NR52 byte
            if ((channel1Enabled != _channel1.Enabled) ||
                (channel2Enabled != _channel2.Enabled) ||
                (channel3Enabled != _channel3.Enabled) ||
                (channel4Enabled != _channel4.Enabled) ||
                (prevEnabled != _state.Enabled))
            {
                byte nr52 = 0x70;
                if (_state.Enabled)
                {
                    nr52 = (byte)((_channel1.Enabled ? 0x1 : 0) | // bit 0
                                  (_channel2.Enabled ? 0x2 : 0) | // bit 1
                                  (_channel3.Enabled ? 0x4 : 0) | // bit 2
                                  (_channel4.Enabled ? 0x8 : 0) | // bit 3
                                  0xF0);                          // bit 4-7 are 1
                }

                // We know bit 7 is 1 because otherwise the whole register is 0x70
                _memory.LowLevelWrite((ushort)MMR.NR52, nr52);
            }
        }