SetupSpritePalletes(DisplayDefinition disDef, Memory memory, MMR pallete) { if (pallete == MMR.OBP0) { byte obp0 = memory.LowLevelRead((ushort)MMR.OBP0); disDef.SpritePallete0[0] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp0 >> (2 * color)) & 1; int up = (obp0 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete0[color] = disDef.SpriteColors[index]; } } else if (pallete == MMR.OBP1) { byte obp1 = memory.LowLevelRead((ushort)MMR.OBP1); disDef.SpritePallete1[1] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp1 >> (2 * color)) & 1; int up = (obp1 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete1[color] = disDef.SpriteColors[index]; } } else { throw new InvalidProgramException("Invalid pallete register given"); } }
public void GetMeetsWithVenueName() { //Setup var listOfMeets = new List <Meet> { new Meet() { MeetDate = DateTime.Now, MeetName = "Test Meet 1", MeetId = 1, MeetVenue = "Test Venue 1", PoolLength = "11" }, new Meet() { MeetDate = DateTime.Now, MeetName = "Test Meet 2", MeetId = 2, MeetVenue = "Test Venue 1", PoolLength = "21" } }; var mockMeetsRepo = new Mock <IMeetRepo>(); mockMeetsRepo.Setup(MMR => MMR.GetMeets("Test Venue 1")).Returns(listOfMeets); var sut = new MeetsController(mockMeetsRepo.Object); //Action var res = sut.GetMeets("Test Venue 1"); //Assert res.Should().BeOfType <OkNegotiatedContentResult <List <Meet> > >(); res.As <OkNegotiatedContentResult <List <Meet> > >().Content.Should().BeEquivalentTo(listOfMeets); }
public void GetMeetsFormattedByStartAndEndDateReturnsMeetsWithinStartAndEndDate() { //Setup var listOfMeets = new List <Meet> { new Meet() { MeetDate = new DateTime(2019, 04, 14), MeetName = "Test Meet 1", MeetId = 1, MeetVenue = "Test Venue 1", PoolLength = "11" }, new Meet() { MeetDate = new DateTime(2019, 04, 13), MeetName = "Test Meet 2", MeetId = 2, MeetVenue = "Test Venue 1", PoolLength = "21" } }; var mockMeetsRepo = new Mock <IMeetRepo>(); mockMeetsRepo.Setup(MMR => MMR.GetMeets(new DateTime(2019, 04, 12), new DateTime(2019, 04, 15))).Returns(listOfMeets); var sut = new MeetsController(mockMeetsRepo.Object); //Action var res = sut.GetMeetsFormatted(new DateTime(2019, 04, 12), new DateTime(2019, 04, 15)); //Assert res.Should().BeOfType <OkNegotiatedContentResult <List <string> > >(); res.As <OkNegotiatedContentResult <List <string> > >().Content.Should().BeEquivalentTo(FormatMeets(listOfMeets)); }
internal void HandleMemoryChange(MMR mappedRegister, byte value) { switch (mappedRegister) { case MMR.LCDC: // We set all the LCDC bits for (int i = 0; i < 8; ++i) { _state.LCDCBits[i] = (value & (1 << i)) != 0; } break; case MMR.STAT: _state.STAT = value; break; case MMR.SCY: _state.SCY = value; break; case MMR.SCX: _state.SCX = value; break; case MMR.LY: _state.CurrentLine = 0; break; case MMR.LYC: _state.LYC = value; break; case MMR.DMA: LoadSprites(); break; case MMR.BGP: DisFuncs.SetupTilePallete(_disDef, _memory); break; case MMR.OBP0: DisFuncs.SetupSpritePalletes(_disDef, _memory, MMR.OBP0); break; case MMR.OBP1: DisFuncs.SetupSpritePalletes(_disDef, _memory, MMR.OBP1); break; case MMR.WY: _state.WY = value; break; case MMR.WX: _state.WX = value; break; default: throw new InvalidProgramException("All cases should be handled..."); } }
/// <summary> /// Creates a DiscordEmbed to represent the profile /// </summary> /// <returns></returns> public DiscordEmbed CreateEmbed() { var main = TopOperators.FirstOrDefault(); StringBuilder desc = new StringBuilder(); desc.AppendLine($"**Level {Level}**"); desc.AppendLine($"**{main.Key} Main**"); DiscordEmbedBuilder builder = new DiscordEmbedBuilder(); builder.WithAuthor(this.Name, this.URL, this.Avatar).WithDescription(desc.ToString()); if (Rank != Rank.Unranked) { builder.WithColor(Rank.GetColor()) .AddField("MMR", MMR.ToString(), true).AddField("Best", BestMMR.ToString(), true).AddField("Rank", RankText, true); } builder.AddField("=== Casual Time", CasualStatistics.TimePlayed) .AddField("Kills", CasualStatistics.Kills.ToString(), true).AddField("Deaths", CasualStatistics.Deaths.ToString(), true).AddField("KD", CasualStatistics.KD.ToString("F2"), true) .AddField("Wins", CasualStatistics.Wins.ToString(), true).AddField("Loss", CasualStatistics.Losses.ToString(), true).AddField("Win%", CasualStatistics.WinPercent.ToString("F2"), true) .WithThumbnailUrl(main.Value); if (Rank != Rank.Unranked) { builder.AddField("=== Ranked Time", RankedStatistics.TimePlayed) .AddField("Kills", RankedStatistics.Kills.ToString(), true).AddField("Deaths", RankedStatistics.Deaths.ToString(), true).AddField("KD", RankedStatistics.KD.ToString("F2"), true) .AddField("Wins", RankedStatistics.Wins.ToString(), true).AddField("Loss", RankedStatistics.Losses.ToString(), true).AddField("Win%", RankedStatistics.WinPercent.ToString("F2"), true); } return(builder.Build()); }
internal void HandleMemoryChange(MMR register, byte value) { switch (register) { case MMR.TIMA: _state.TimaCounter = value; this._memory.LowLevelWrite((ushort)MMR.TIMA, value); break; case MMR.TMA: _state.TmaValue = value; this._memory.LowLevelWrite((ushort)MMR.TMA, value); break; case MMR.TAC: // Clock select is the bits 0 and 1 of the TAC register byte clockSelect = (byte)(value & 0x03); switch (clockSelect) { case 1: _state.TacMask = 0x000F; // f/2^4 0000 0000 0000 1111, (262144 Hz) break; case 2: _state.TacMask = 0x003F; // f/2^6 0000 0000 0011 1111, (65536 Hz) break; case 3: _state.TacMask = 0x00FF; // f/2^8 0000 0000 1111 1111, (16384 Hz) break; default: _state.TacMask = 0x03FF; // f/2^10 0000 0011 1111 1111, (4096 Hz) break; } // We restart the counter _state.TacCounter = 0; // TAC has a 0xF8 mask (only lower 3 bits are useful) this._memory.LowLevelWrite((ushort)MMR.TAC, (byte)(0xF8 | value)); break; } }
public void UpdateMeetValidMeetAndDbSuccessfulReturnsOk() { //Setup var mockMeetsRepo = new Mock <IMeetRepo>(); var meet = new Meet() { MeetDate = new DateTime(), MeetName = "Test Meet", MeetId = 1, MeetVenue = "Test Venue", PoolLength = "11" }; mockMeetsRepo.Setup(MMR => MMR.UpdateMeet(meet)).Returns(true); var sut = new MeetsController(mockMeetsRepo.Object); //Action var res = sut.UpdateMeet(meet); //Assert res.Should().BeOfType <OkResult>(); }
public void UpdateMeetValidMeetAndDbUnSuccessfulReturnsBadRequest() { //Setup var mockMeetsRepo = new Mock <IMeetRepo>(); var meet = new Meet() { MeetDate = new DateTime(), MeetName = "Test Meet", MeetId = 1, MeetVenue = "Test Venue", PoolLength = "11" }; mockMeetsRepo.Setup(MMR => MMR.UpdateMeet(meet)).Returns(false); var sut = new MeetsController(mockMeetsRepo.Object); //Action var res = sut.UpdateMeet(meet); //Assert res.Should().BeOfType <BadRequestErrorMessageResult>(); res.As <BadRequestErrorMessageResult>().Message.Should().Be("Meet failed to be updated"); }
internal SquareChannel(Memory memory, FrameSequencer frameSequencer, int sampleRate, int numChannels, int sampleSize, int channelIndex, MMR sweepRegister, MMR wavePatternDutyRegister, MMR volumeEnvelopeRegister, MMR freqLowRegister, MMR freqHighRegister) { _memory = memory; _frameSequencer = frameSequencer; SampleRate = sampleRate; _msSampleRate = SampleRate / 1000; NumChannels = numChannels; SampleSize = sampleSize; _buffer = new short[SampleRate * NumChannels * SampleSize * _milliseconds / 1000]; _channelIndex = channelIndex; _soundEventQueue = new SoundEventQueue(1000); // Register setup _sweepRegister = sweepRegister; _wavePatternDutyRegister = wavePatternDutyRegister; _volumeEnvelopeRegister = volumeEnvelopeRegister; _freqLowRegister = freqLowRegister; _freqHighRegister = freqHighRegister; }
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); } }
public void HandleMemoryChange(MMR register, byte value) { byte before = _memory.LowLevelRead((ushort)register); #if SoundTiming //Timeline[TimelineCount++] = (long)register; //Timeline[TimelineCount++] = value; #endif if (register == _sweepRegister) { // Sweep Shift Number (Bits 0-2) _state.SweepShifts = value & 0x07; // Sweep Direction (Bit 3) bool prevSweepUp = _state.SweepUp; _state.SweepUp = ((value & 0x08) == 0); // Going from neg->pos after a calc has occurred disabled the channel if (_state.SweepCalcOcurred && !prevSweepUp && _state.SweepUp) { SetEnabled(false); } _state.SweepCalcOcurred = false; // Sweep Time (Bits 4-6) _state.SweepLength = ((value >> 4) & 0x07); // Bit 7 is always 1 _memory.LowLevelWrite((ushort)register, (byte)(value | 0x80)); } else if (register == _wavePatternDutyRegister) { // TODO(Cristian): Wave Pattern Duty _state.SoundLengthCounter = 0x3F - (value & 0x3F); _memory.LowLevelWrite((ushort)register, value); } else if (register == _volumeEnvelopeRegister) { _state.EnvelopeTicks = value & 0x7; _state.EnvelopeUp = (value & 0x8) != 0; _state.EnvelopeDefaultValue = value >> 4; // Putting volume 0 disables the channel if ((_state.EnvelopeDefaultValue == 0) && !_state.EnvelopeUp) { _state.EnvelopeDACOn = false; SetEnabled(false); } else { _state.EnvelopeDACOn = true; } _memory.LowLevelWrite((ushort)register, value); } else if (register == _freqLowRegister) { ushort newFreqFactor = (ushort)(((_state.HighFreqByte & 0x7) << 8) | value); SetFrequencyFactor(newFreqFactor); _memory.LowLevelWrite((ushort)register, value); } else if (register == _freqHighRegister) { ushort newFreqFactor = (ushort)(((value & 0x7) << 8) | _state.LowFreqByte); SetFrequencyFactor(newFreqFactor); bool prevContinuousOutput = _state.ContinuousOutput; _state.ContinuousOutput = (Utils.UtilFuncs.TestBit(value, 6) == 0); // Only enabling sound length (disabled -> enabled) could trigger a clock if ((!_state.ContinuousOutput) && (prevContinuousOutput != _state.ContinuousOutput)) { // If the next frameSequencer WON'T trigger the length period, // the counter is somehow decremented... if ((_frameSequencer.Value & 0x01) == 0) { ClockLengthCounter(); } } // Bit 7 is called a channel INIT. On this event the following occurs: // * The Volume Envelope values value are changed bool init = (Utils.UtilFuncs.TestBit(value, 7) != 0); // INIT triggers only if the DAC (volume) is on if (init) { if(_state.EnvelopeDACOn) { SetEnabled(true); } // FREQUENCY SWEEP _state.SweepFrequencyRegister = _state.FrequencyFactor; _state.SweepCounter = _state.SweepLength; if (_state.SweepLength == 0) { _state.SweepCounter = 8; } _state.SweepEnabled = ((_state.SweepLength > 0) || (_state.SweepShifts > 0)); // We create immediate frequency calculation if (_state.SweepShifts > 0) { CalculateSweepChange(updateValue: false, redoCalculation: false); _state.SweepCalcOcurred = true; } // NOTE(Cristian): If the length counter is empty at INIT, // it's reloaded with full length if(_state.SoundLengthCounter < 0) { _state.SoundLengthCounter = 0x3F; // If INIT on an zerioed empty enabled length channel // AND the next frameSequencer tick WON'T tick the length period // The lenght counter is somehow decremented if (!_state.ContinuousOutput && ((_frameSequencer.Value & 0x01) == 0)) { ClockLengthCounter(); } } // Envelope is reloaded _state.EnvelopeTickCounter = _state.EnvelopeTicks; _state.EnvelopeCurrentUp = _state.EnvelopeUp; SetEnvelopeCurrentValue(_state.EnvelopeDefaultValue); } // Bits 3-5 are always 1 _memory.LowLevelWrite((ushort)register, (byte)(value | 0x38)); } else if (register == MMR.NR52) { SetEnabled((Utils.UtilFuncs.TestBit(value, _channelIndex) != 0)); } #if SoundTiming //Timeline[TimelineCount++] = before; //Timeline[TimelineCount++] = _memory.LowLevelRead((ushort)register); #endif }
public void HandleMemoryChange(MMR register, byte value) { switch (register) { case MMR.NR41: // Sound Length _state.SoundLengthCounter = 0x3F - (value & 0x3F); _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR42: _state.EnvelopeTicks = value & 0x07; _state.EnvelopeUp = (value & 0x8) != 0; _state.EnvelopeDefaultValue = value >> 4; // Putting volume 0 disables the channel if ((_state.EnvelopeDefaultValue == 0) && !_state.EnvelopeUp) { _state.EnvelopeDACOn = false; SetEnabled(false); } else { _state.EnvelopeDACOn = true; } _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR43: AddSoundEvent(NoiseChannelEvents.NR43_WRITE, value); _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR44: bool prevContinuousOutput = _state.ContinuousOutput; _state.ContinuousOutput = (Utils.UtilFuncs.TestBit(value, 6) == 0); // Only enabling sound length (disabled -> enabled) could trigger a clock if ((!_state.ContinuousOutput) && (prevContinuousOutput != _state.ContinuousOutput)) { // If the next frameSequencer WON'T trigger the length period, // the counter is somehow decremented... if ((_frameSequencer.Value & 0x01) == 0) { ClockLengthCounter(); } } bool init = (Utils.UtilFuncs.TestBit(value, 7) != 0); if (init) { AddSoundEvent(NoiseChannelEvents.INIT, 0); if (_state.EnvelopeDACOn) { SetEnabled(true); } // NOTE(Cristian): If the length counter is empty at INIT, // it's reloaded with full length if (_state.SoundLengthCounter < 0) { _state.SoundLengthCounter = 0x3F; // If INIT on an zerioed empty enabled length channel // AND the next frameSequencer tick WON'T tick the length period // The lenght counter is somehow decremented if (!_state.ContinuousOutput && ((_frameSequencer.Value & 0x01) == 0)) { ClockLengthCounter(); } } // Envelope is reloaded _state.EnvelopeTickCounter = _state.EnvelopeTicks; _state.EnvelopeCurrentUp = _state.EnvelopeUp; SetEnvelopeCurrentValue(_state.EnvelopeDefaultValue); } _memory.LowLevelWrite((ushort)register, value); break; } }
public MemoryMappedRegisterViewModel(string name, MMR register, IGameBoy gameBoy) { _name = name; _register = register; _gameBoy = gameBoy; }
internal static void SetupSpritePalletes(DisplayDefinition disDef, Memory memory, MMR pallete) { if (pallete == MMR.OBP0) { byte obp0 = memory.LowLevelRead((ushort)MMR.OBP0); disDef.SpritePallete0[0] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp0 >> (2 * color)) & 1; int up = (obp0 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete0[color] = disDef.SpriteColors[index]; } } else if (pallete == MMR.OBP1) { byte obp1 = memory.LowLevelRead((ushort)MMR.OBP1); disDef.SpritePallete1[1] = 0x00000000; // Sprite colors are trasparent for (int color = 1; color < 4; ++color) { int down = (obp1 >> (2 * color)) & 1; int up = (obp1 >> (2 * color + 1)) & 1; int index = (up << 1) | down; disDef.SpritePallete1[color] = disDef.SpriteColors[index]; } } else { throw new InvalidProgramException("Invalid pallete register given"); } }
public void HandleMemoryChange(MMR register, byte value) { switch (register) { case MMR.NR41: // Sound Length _state.SoundLengthCounter = 0x3F - (value & 0x3F); _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR42: _state.EnvelopeTicks = value & 0x07; _state.EnvelopeUp = (value & 0x8) != 0; _state.EnvelopeDefaultValue = value >> 4; // Putting volume 0 disables the channel if ((_state.EnvelopeDefaultValue == 0) && !_state.EnvelopeUp) { _state.EnvelopeDACOn = false; SetEnabled(false); } else { _state.EnvelopeDACOn = true; } _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR43: AddSoundEvent(NoiseChannelEvents.NR43_WRITE, value); _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR44: bool prevContinuousOutput = _state.ContinuousOutput; _state.ContinuousOutput = (Utils.UtilFuncs.TestBit(value, 6) == 0); // Only enabling sound length (disabled -> enabled) could trigger a clock if ((!_state.ContinuousOutput) && (prevContinuousOutput != _state.ContinuousOutput)) { // If the next frameSequencer WON'T trigger the length period, // the counter is somehow decremented... if ((_frameSequencer.Value & 0x01) == 0) { ClockLengthCounter(); } } bool init = (Utils.UtilFuncs.TestBit(value, 7) != 0); if (init) { AddSoundEvent(NoiseChannelEvents.INIT, 0); if(_state.EnvelopeDACOn) { SetEnabled(true); } // NOTE(Cristian): If the length counter is empty at INIT, // it's reloaded with full length if(_state.SoundLengthCounter < 0) { _state.SoundLengthCounter = 0x3F; // If INIT on an zerioed empty enabled length channel // AND the next frameSequencer tick WON'T tick the length period // The lenght counter is somehow decremented if (!_state.ContinuousOutput && ((_frameSequencer.Value & 0x01) == 0)) { ClockLengthCounter(); } } // Envelope is reloaded _state.EnvelopeTickCounter = _state.EnvelopeTicks; _state.EnvelopeCurrentUp = _state.EnvelopeUp; SetEnvelopeCurrentValue(_state.EnvelopeDefaultValue); } _memory.LowLevelWrite((ushort)register, value); break; } }
public void HandleMemoryChange(MMR register, byte value) { switch(register) { case MMR.NR30: // Sound on/off // Last bit determines sound on/off _state.ChannelDACOn = (Utils.UtilFuncs.TestBit(value, 7) != 0); // When DAC re-enabled, the channel doesn't enables itself if (!_state.ChannelDACOn) { SetEnabled(false); } _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR31: // Sound Length _state.SoundLengthCounter = 0xFF - value; //_memory.LowLevelWrite((ushort)register, 0xFF); _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR32: // Output Level (volume) // Basically, we shift by this amount. // If the amount is 0, it means we mute SetVolumeRightShift(((value >> 5) & 0x3) - 1); // We reload the sample //_outputValue = (short)Volume; _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR33: // FrequencyFactor lower { ushort newFreqFactor = (ushort)(((_state.HighFreqByte & 0x7) << 8) | value); SetFrequencyFactor(newFreqFactor); _memory.LowLevelWrite((ushort)register, value); break; } case MMR.NR34: // FrequencyFactor higher { ushort newFreqFactor = (ushort)(((value & 0x7) << 8) | _state.LowFreqByte); SetFrequencyFactor(newFreqFactor); bool prevContinuousOutput = _state.ContinuousOutput; _state.ContinuousOutput = (Utils.UtilFuncs.TestBit(value, 6) == 0); // Only enabling sound length (disabled -> enabled) could trigger a clock if ((!_state.ContinuousOutput) && (prevContinuousOutput != _state.ContinuousOutput)) { // If the next frameSequencer WON'T trigger the length period, // the counter is somehow decremented... if ((_frameSequencer.Value & 0x01) == 0) { ClockLengthCounter(); } } bool init = (Utils.UtilFuncs.TestBit(value, 7) != 0); if (init) { AddSoundEvent(WaveChannelEvents.INIT, value); _state.TickCounter = _state.TickThreshold; _state.CurrentSampleIndex = 0; // NOTE(Cristian): If the length counter is empty at INIT, // it's reloaded with full length if (_state.SoundLengthCounter < 0) { _state.SoundLengthCounter = 0xFF; // If INIT on an zerioed empty enabled length channel // AND the next frameSequencer tick WON'T tick the length period // The lenght counter is somehow decremented if (!_state.ContinuousOutput && ((_frameSequencer.Value & 0x01) == 0)) { ClockLengthCounter(); } } if (_state.ChannelDACOn) { SetEnabled(true); } } _memory.LowLevelWrite((ushort)register, value); break; } } }
public void HandleMemoryChange(MMR register, byte value) { byte before = _memory.LowLevelRead((ushort)register); #if SoundTiming //Timeline[TimelineCount++] = (long)register; //Timeline[TimelineCount++] = value; #endif if (register == _sweepRegister) { // Sweep Shift Number (Bits 0-2) _state.SweepShifts = value & 0x07; // Sweep Direction (Bit 3) bool prevSweepUp = _state.SweepUp; _state.SweepUp = ((value & 0x08) == 0); // Going from neg->pos after a calc has occurred disabled the channel if (_state.SweepCalcOcurred && !prevSweepUp && _state.SweepUp) { SetEnabled(false); } _state.SweepCalcOcurred = false; // Sweep Time (Bits 4-6) _state.SweepLength = ((value >> 4) & 0x07); // Bit 7 is always 1 _memory.LowLevelWrite((ushort)register, (byte)(value | 0x80)); } else if (register == _wavePatternDutyRegister) { // TODO(Cristian): Wave Pattern Duty _state.SoundLengthCounter = 0x3F - (value & 0x3F); _memory.LowLevelWrite((ushort)register, value); } else if (register == _volumeEnvelopeRegister) { _state.EnvelopeTicks = value & 0x7; _state.EnvelopeUp = (value & 0x8) != 0; _state.EnvelopeDefaultValue = value >> 4; // Putting volume 0 disables the channel if ((_state.EnvelopeDefaultValue == 0) && !_state.EnvelopeUp) { _state.EnvelopeDACOn = false; SetEnabled(false); } else { _state.EnvelopeDACOn = true; } _memory.LowLevelWrite((ushort)register, value); } else if (register == _freqLowRegister) { ushort newFreqFactor = (ushort)(((_state.HighFreqByte & 0x7) << 8) | value); SetFrequencyFactor(newFreqFactor); _memory.LowLevelWrite((ushort)register, value); } else if (register == _freqHighRegister) { ushort newFreqFactor = (ushort)(((value & 0x7) << 8) | _state.LowFreqByte); SetFrequencyFactor(newFreqFactor); bool prevContinuousOutput = _state.ContinuousOutput; _state.ContinuousOutput = (Utils.UtilFuncs.TestBit(value, 6) == 0); // Only enabling sound length (disabled -> enabled) could trigger a clock if ((!_state.ContinuousOutput) && (prevContinuousOutput != _state.ContinuousOutput)) { // If the next frameSequencer WON'T trigger the length period, // the counter is somehow decremented... if ((_frameSequencer.Value & 0x01) == 0) { ClockLengthCounter(); } } // Bit 7 is called a channel INIT. On this event the following occurs: // * The Volume Envelope values value are changed bool init = (Utils.UtilFuncs.TestBit(value, 7) != 0); // INIT triggers only if the DAC (volume) is on if (init) { if (_state.EnvelopeDACOn) { SetEnabled(true); } // FREQUENCY SWEEP _state.SweepFrequencyRegister = _state.FrequencyFactor; _state.SweepCounter = _state.SweepLength; if (_state.SweepLength == 0) { _state.SweepCounter = 8; } _state.SweepEnabled = ((_state.SweepLength > 0) || (_state.SweepShifts > 0)); // We create immediate frequency calculation if (_state.SweepShifts > 0) { CalculateSweepChange(updateValue: false, redoCalculation: false); _state.SweepCalcOcurred = true; } // NOTE(Cristian): If the length counter is empty at INIT, // it's reloaded with full length if (_state.SoundLengthCounter < 0) { _state.SoundLengthCounter = 0x3F; // If INIT on an zerioed empty enabled length channel // AND the next frameSequencer tick WON'T tick the length period // The lenght counter is somehow decremented if (!_state.ContinuousOutput && ((_frameSequencer.Value & 0x01) == 0)) { ClockLengthCounter(); } } // Envelope is reloaded _state.EnvelopeTickCounter = _state.EnvelopeTicks; _state.EnvelopeCurrentUp = _state.EnvelopeUp; SetEnvelopeCurrentValue(_state.EnvelopeDefaultValue); } // Bits 3-5 are always 1 _memory.LowLevelWrite((ushort)register, (byte)(value | 0x38)); } else if (register == MMR.NR52) { SetEnabled((Utils.UtilFuncs.TestBit(value, _channelIndex) != 0)); } #if SoundTiming //Timeline[TimelineCount++] = before; //Timeline[TimelineCount++] = _memory.LowLevelRead((ushort)register); #endif }
public void HandleMemoryChange(MMR register, byte value) { switch (register) { case MMR.NR30: // Sound on/off // Last bit determines sound on/off _state.ChannelDACOn = (Utils.UtilFuncs.TestBit(value, 7) != 0); // When DAC re-enabled, the channel doesn't enables itself if (!_state.ChannelDACOn) { SetEnabled(false); } _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR31: // Sound Length _state.SoundLengthCounter = 0xFF - value; //_memory.LowLevelWrite((ushort)register, 0xFF); _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR32: // Output Level (volume) // Basically, we shift by this amount. // If the amount is 0, it means we mute SetVolumeRightShift(((value >> 5) & 0x3) - 1); // We reload the sample //_outputValue = (short)Volume; _memory.LowLevelWrite((ushort)register, value); break; case MMR.NR33: // FrequencyFactor lower { ushort newFreqFactor = (ushort)(((_state.HighFreqByte & 0x7) << 8) | value); SetFrequencyFactor(newFreqFactor); _memory.LowLevelWrite((ushort)register, value); break; } case MMR.NR34: // FrequencyFactor higher { ushort newFreqFactor = (ushort)(((value & 0x7) << 8) | _state.LowFreqByte); SetFrequencyFactor(newFreqFactor); bool prevContinuousOutput = _state.ContinuousOutput; _state.ContinuousOutput = (Utils.UtilFuncs.TestBit(value, 6) == 0); // Only enabling sound length (disabled -> enabled) could trigger a clock if ((!_state.ContinuousOutput) && (prevContinuousOutput != _state.ContinuousOutput)) { // If the next frameSequencer WON'T trigger the length period, // the counter is somehow decremented... if ((_frameSequencer.Value & 0x01) == 0) { ClockLengthCounter(); } } bool init = (Utils.UtilFuncs.TestBit(value, 7) != 0); if (init) { AddSoundEvent(WaveChannelEvents.INIT, value); _state.TickCounter = _state.TickThreshold; _state.CurrentSampleIndex = 0; // NOTE(Cristian): If the length counter is empty at INIT, // it's reloaded with full length if (_state.SoundLengthCounter < 0) { _state.SoundLengthCounter = 0xFF; // If INIT on an zerioed empty enabled length channel // AND the next frameSequencer tick WON'T tick the length period // The lenght counter is somehow decremented if (!_state.ContinuousOutput && ((_frameSequencer.Value & 0x01) == 0)) { ClockLengthCounter(); } } if (_state.ChannelDACOn) { SetEnabled(true); } } _memory.LowLevelWrite((ushort)register, value); break; } } }