public void WriteByte( int address, int value ) { if( address >= 0xC000 && address <= 0xDFFF ) { workRam[ address - 0xC000 ] = (byte)value; } else if( address >= 0xFE00 && address <= 0xFEFF ) { oam[ address - 0xFE00 ] = (byte)value; } else if( address >= 0xFF80 && address <= 0xFFFE ) { highRam[ 0xFF & address ] = (byte)value; } else if( address >= 0x8000 && address <= 0x9FFF ) { int videoRamIndex = address - 0x8000; videoRam[ videoRamIndex ] = (byte)value; if( address < 0x9000 ) { spriteTileInvalidated[ videoRamIndex >> 4 ] = true; } if( address < 0x9800 ) { invalidateAllBackgroundTilesRequest = true; } else if( address >= 0x9C00 ) { int tileIndex = address - 0x9C00; backgroundTileInvalidated[ tileIndex >> 5, tileIndex & 0x1F ] = true; } else { int tileIndex = address - 0x9800; backgroundTileInvalidated[ tileIndex >> 5, tileIndex & 0x1F ] = true; } } else if( address <= 0x7FFF || ( address >= 0xA000 && address <= 0xBFFF ) ) { cartridge.WriteByte( address, value ); } else if( address >= 0xE000 && address <= 0xFDFF ) { workRam[ address - 0xE000 ] = (byte)value; } else { switch( address ) { case 0xFF00: // key pad keyP14 = ( value & 0x10 ) != 0x10; keyP15 = ( value & 0x20 ) != 0x20; break; case 0xFF04: // Timer divider break; case 0xFF05: // Timer counter timerCounter = value; break; case 0xFF06: // Timer modulo timerModulo = value; break; case 0xFF07: // Time Control timerRunning = ( value & 0x04 ) == 0x04; timerFrequency = (TimerFrequencyType)( 0x03 & value ); break; case 0xFF0F: // Interrupt Flag (an interrupt request) keyPressedInterruptRequested = ( value & 0x10 ) == 0x10; serialIOTransferCompleteInterruptRequested = ( value & 0x08 ) == 0x08; timerOverflowInterruptRequested = ( value & 0x04 ) == 0x04; lcdcInterruptRequested = ( value & 0x02 ) == 0x02; vBlankInterruptRequested = ( value & 0x01 ) == 0x01; break; case 0xFF40: { // LCDC control bool _backgroundAndWindowTileDataSelect = backgroundAndWindowTileDataSelect; bool _backgroundTileMapDisplaySelect = backgroundTileMapDisplaySelect; bool _windowTileMapDisplaySelect = windowTileMapDisplaySelect; lcdControlOperationEnabled = ( value & 0x80 ) == 0x80; windowTileMapDisplaySelect = ( value & 0x40 ) == 0x40; windowDisplayed = ( value & 0x20 ) == 0x20; backgroundAndWindowTileDataSelect = ( value & 0x10 ) == 0x10; backgroundTileMapDisplaySelect = ( value & 0x08 ) == 0x08; largeSprites = ( value & 0x04 ) == 0x04; spritesDisplayed = ( value & 0x02 ) == 0x02; backgroundDisplayed = ( value & 0x01 ) == 0x01; if( _backgroundAndWindowTileDataSelect != backgroundAndWindowTileDataSelect || _backgroundTileMapDisplaySelect != backgroundTileMapDisplaySelect || _windowTileMapDisplaySelect != windowTileMapDisplaySelect ) { invalidateAllBackgroundTilesRequest = true; } break; } case 0xFF41: // LCDC Status lcdcLycLyCoincidenceInterruptEnabled = ( value & 0x40 ) == 0x40; lcdcOamInterruptEnabled = ( value & 0x20 ) == 0x20; lcdcVBlankInterruptEnabled = ( value & 0x10 ) == 0x10; lcdcHBlankInterruptEnabled = ( value & 0x08 ) == 0x08; lcdcMode = (LcdcModeType)( value & 0x03 ); break; case 0xFF42: // Scroll Y; scrollY = value; break; case 0xFF43: // Scroll X; scrollX = value; break; case 0xFF44: // LY ly = value; break; case 0xFF45: // LY Compare lyCompare = value; break; case 0xFF46: // Memory Transfer value <<= 8; for( int i = 0; i < 0x8C; i++ ) { WriteByte( 0xFE00 | i, ReadByte( value | i ) ); } break; case 0xFF47: // Background palette Console.WriteLine( "[0xFF47] = {0:X}", value ); for( int i = 0; i < 4; i++ ) { switch( value & 0x03 ) { case 0: backgroundPalette[ i ] = WHITE; break; case 1: backgroundPalette[ i ] = LIGHT_GRAY; break; case 2: backgroundPalette[ i ] = DARK_GRAY; break; case 3: backgroundPalette[ i ] = BLACK; break; } value >>= 2; } invalidateAllBackgroundTilesRequest = true; break; case 0xFF48: // Object palette 0 for( int i = 0; i < 4; i++ ) { switch( value & 0x03 ) { case 0: objectPalette0[ i ] = WHITE; break; case 1: objectPalette0[ i ] = LIGHT_GRAY; break; case 2: objectPalette0[ i ] = DARK_GRAY; break; case 3: objectPalette0[ i ] = BLACK; break; } value >>= 2; } invalidateAllSpriteTilesRequest = true; break; case 0xFF49: // Object palette 1 for( int i = 0; i < 4; i++ ) { switch( value & 0x03 ) { case 0: objectPalette1[ i ] = WHITE; break; case 1: objectPalette1[ i ] = LIGHT_GRAY; break; case 2: objectPalette1[ i ] = DARK_GRAY; break; case 3: objectPalette1[ i ] = BLACK; break; } value >>= 2; } invalidateAllSpriteTilesRequest = true; break; case 0xFF4A: // Window Y windowY = value; break; case 0xFF4B: // Window X windowX = value; break; case 0xFFFF: // Interrupt Enable keyPressedInterruptEnabled = ( value & 0x10 ) == 0x10; serialIOTransferCompleteInterruptEnabled = ( value & 0x08 ) == 0x08; timerOverflowInterruptEnabled = ( value & 0x04 ) == 0x04; lcdcInterruptEnabled = ( value & 0x02 ) == 0x02; vBlankInterruptEnabled = ( value & 0x01 ) == 0x01; break; } } }
public void WriteByte(int address, int value) { if (address >= 0xC000 && address <= 0xDFFF) { workRam [address - 0xC000] = (byte)value; } else if (address >= 0xFE00 && address <= 0xFEFF) { oam [address - 0xFE00] = (byte)value; } else if (address >= 0xFF80 && address <= 0xFFFE) { highRam [0xFF & address] = (byte)value; } else if (address >= 0x8000 && address <= 0x9FFF) { int videoRamIndex = address - 0x8000; videoRam [videoRamIndex] = (byte)value; if (address < 0x9000) { spriteTileInvalidated [videoRamIndex >> 4] = true; } if (address < 0x9800) { invalidateAllBackgroundTilesRequest = true; } else if (address >= 0x9C00) { int tileIndex = address - 0x9C00; backgroundTileInvalidated [tileIndex >> 5, tileIndex & 0x1F] = true; } else { int tileIndex = address - 0x9800; backgroundTileInvalidated [tileIndex >> 5, tileIndex & 0x1F] = true; } } else if (address <= 0x7FFF || (address >= 0xA000 && address <= 0xBFFF)) { cartridge.WriteByte(address, value); } else if (address >= 0xE000 && address <= 0xFDFF) { workRam [address - 0xE000] = (byte)value; } else { switch (address) { case 0xFF00: // key pad keyP14 = (value & 0x10) != 0x10; keyP15 = (value & 0x20) != 0x20; break; case 0xFF04: // Timer divider break; case 0xFF05: // Timer counter timerCounter = value; break; case 0xFF06: // Timer modulo timerModulo = value; break; case 0xFF07: // Time Control timerRunning = (value & 0x04) == 0x04; timerFrequency = (TimerFrequencyType)(0x03 & value); break; case 0xFF0F: // Interrupt Flag (an interrupt request) keyPressedInterruptRequested = (value & 0x10) == 0x10; serialIOTransferCompleteInterruptRequested = (value & 0x08) == 0x08; timerOverflowInterruptRequested = (value & 0x04) == 0x04; lcdcInterruptRequested = (value & 0x02) == 0x02; vBlankInterruptRequested = (value & 0x01) == 0x01; break; case 0xFF10: // Sound channel 1, sweep SoundChip.channel1.SetSweep( (value & 0x70) >> 4, (value & 0x07), (value & 0x08) == 1); soundRegisters [0x10 - 0x10] = value; break; case 0xFF11: // Sound channel 1, length and wave duty SoundChip.channel1.SetDutyCycle((value & 0xC0) >> 6); SoundChip.channel1.SetLength(value & 0x3F); soundRegisters [0x11 - 0x10] = value; break; case 0xFF12: // Sound channel 1, volume envelope SoundChip.channel1.SetEnvelope( (value & 0xF0) >> 4, (value & 0x07), (value & 0x08) == 8); soundRegisters [0x12 - 0x10] = value; break; case 0xFF13: // Sound channel 1, frequency low soundRegisters [0x13 - 0x10] = value; SoundChip.channel1.SetFrequency( ((soundRegisters [0x14 - 0x10] & 0x07) << 8) + soundRegisters [0x13 - 0x10]); break; case 0xFF14: // Sound channel 1, frequency high soundRegisters [0x14 - 0x10] = value; if ((soundRegisters [0x14 - 0x10] & 0x80) != 0) { SoundChip.channel1.SetLength(soundRegisters [0x11 - 0x10] & 0x3F); SoundChip.channel1.SetEnvelope( (soundRegisters [0x12 - 0x10] & 0xF0) >> 4, (soundRegisters [0x12 - 0x10] & 0x07), (soundRegisters [0x12 - 0x10] & 0x08) == 8); } if ((soundRegisters [0x14 - 0x10] & 0x40) == 0) { SoundChip.channel1.SetLength(-1); } SoundChip.channel1.SetFrequency( ((soundRegisters [0x14 - 0x10] & 0x07) << 8) + soundRegisters [0x13 - 0x10]); break; case 0xFF17: // Sound channel 2, volume envelope SoundChip.channel2.SetEnvelope( (value & 0xF0) >> 4, value & 0x07, (value & 0x08) == 8); soundRegisters [0x17 - 0x10] = value; break; case 0xFF18: // Sound channel 2, frequency low soundRegisters [0x18 - 0x10] = value; SoundChip.channel2.SetFrequency( ((soundRegisters [0x19 - 0x10] & 0x07) << 8) + soundRegisters [0x18 - 0x10]); break; case 0xFF19: // Sound channel 2, frequency high soundRegisters [0x19 - 0x10] = value; if ((value & 0x80) != 0) { SoundChip.channel2.SetLength(soundRegisters [0x21 - 0x10] & 0x3F); SoundChip.channel2.SetEnvelope( (soundRegisters [0x17 - 0x10] & 0xF0) >> 4, (soundRegisters [0x17 - 0x10] & 0x07), (soundRegisters [0x17 - 0x10] & 0x08) == 8); } if ((soundRegisters [0x19 - 0x10] & 0x40) == 0) { SoundChip.channel2.SetLength(-1); } SoundChip.channel2.SetFrequency( ((soundRegisters [0x19 - 0x10] & 0x07) << 8) + soundRegisters [0x18 - 0x10]); break; case 0xFF16: // Sound channel 2, length and wave duty SoundChip.channel2.SetDutyCycle((value & 0xC0) >> 6); SoundChip.channel2.SetLength(value & 0x3F); soundRegisters [0x16 - 0x10] = value; break; case 0xFF1A: // Sound channel 3, on/off if ((value & 0x80) != 0) { SoundChip.channel3.SetVolume((soundRegisters [0x1C - 0x10] & 0x60) >> 5); } else { SoundChip.channel3.SetVolume(0); } soundRegisters [0x1A - 0x10] = value; break; case 0xFF1B: // Sound channel 3, length soundRegisters [0x1B - 0x10] = value; SoundChip.channel3.SetLength(value); break; case 0xFF1C: // Sound channel 3, volume soundRegisters [0x1C - 0x10] = value; SoundChip.channel3.SetVolume((value & 0x60) >> 5); break; case 0xFF1D: // Sound channel 3, frequency lower 8-bit soundRegisters [0x1D - 0x10] = value; SoundChip.channel3.SetFrequency( ((soundRegisters [0x1E - 0x10] & 0x07) << 8) + soundRegisters [0x1D - 0x10]); break; case 0xFF1E: // Sound channel 3, frequency higher 3-bit soundRegisters [0x1E - 0x10] = value; if ((soundRegisters [0x19 - 0x10] & 0x80) != 0) { SoundChip.channel3.SetLength(soundRegisters [0x1B - 0x10]); } SoundChip.channel3.SetFrequency( ((soundRegisters [0x1E - 0x10] & 0x07) << 8) + soundRegisters [0x1D - 0x10]); break; case 0xFF20: // Sound channel 4, length SoundChip.channel4.SetLength(value & 0x3F); soundRegisters [0x20 - 0x10] = value; break; case 0xFF21: // Sound channel 4, volume envelope SoundChip.channel4.SetEnvelope( (value & 0xF0) >> 4, (value & 0x07), (value & 0x08) == 8); soundRegisters [0x21 - 0x10] = value; break; case 0xFF22: // Sound channel 4, polynomial parameters SoundChip.channel4.SetParameters( (value & 0x07), (value & 0x08) == 8, (value & 0xF0) >> 4); soundRegisters [0x22 - 0x10] = value; break; case 0xFF23: // Sound channel 4, initial/consecutive soundRegisters [0x23 - 0x10] = value; if ((value & 0x80) != 0) { SoundChip.channel4.SetLength(soundRegisters [0x20 - 0x10] & 0x3F); } else if (((value & 0x80) & 0x40) == 0) { SoundChip.channel4.SetLength(-1); } break; case 0xFF24: // TODO volume break; case 0xFF25: // Stereo select int chanData; soundRegisters [0x25 - 0x10] = value; chanData = 0; if ((value & 0x01) != 0) { chanData |= SquareWaveGenerator.CHAN_LEFT; } if ((value & 0x10) != 0) { chanData |= SquareWaveGenerator.CHAN_RIGHT; } SoundChip.channel1.SetChannel(chanData); chanData = 0; if ((value & 0x02) != 0) { chanData |= SquareWaveGenerator.CHAN_LEFT; } if ((value & 0x20) != 0) { chanData |= SquareWaveGenerator.CHAN_RIGHT; } SoundChip.channel2.SetChannel(chanData); chanData = 0; if ((value & 0x04) != 0) { chanData |= VoluntaryWaveGenerator.CHAN_LEFT; } if ((value & 0x40) != 0) { chanData |= VoluntaryWaveGenerator.CHAN_RIGHT; } SoundChip.channel3.SetChannel(chanData); chanData = 0; if ((value & 0x08) != 0) { chanData |= NoiseGenerator.CHAN_LEFT; } if ((value & 0x80) != 0) { chanData |= NoiseGenerator.CHAN_RIGHT; } SoundChip.channel4.SetChannel(chanData); break; case 0xFF26: SoundChip.soundEnabled = (value & 0x80) == 1; break; case 0xFF30: case 0xFF31: case 0xFF32: case 0xFF33: case 0xFF34: case 0xFF35: case 0xFF36: case 0xFF37: case 0xFF38: case 0xFF39: case 0xFF3A: case 0xFF3B: case 0xFF3C: case 0xFF3D: case 0xFF3E: case 0xFF3F: SoundChip.channel3.SetSamplePair(address - 0xFF30, value); soundRegisters [address - 0xFF10] = value; break; case 0xFF40: { // LCDC control bool _backgroundAndWindowTileDataSelect = backgroundAndWindowTileDataSelect; bool _backgroundTileMapDisplaySelect = backgroundTileMapDisplaySelect; bool _windowTileMapDisplaySelect = windowTileMapDisplaySelect; lcdControlOperationEnabled = (value & 0x80) == 0x80; windowTileMapDisplaySelect = (value & 0x40) == 0x40; windowDisplayed = (value & 0x20) == 0x20; backgroundAndWindowTileDataSelect = (value & 0x10) == 0x10; backgroundTileMapDisplaySelect = (value & 0x08) == 0x08; largeSprites = (value & 0x04) == 0x04; spritesDisplayed = (value & 0x02) == 0x02; backgroundDisplayed = (value & 0x01) == 0x01; if (_backgroundAndWindowTileDataSelect != backgroundAndWindowTileDataSelect || _backgroundTileMapDisplaySelect != backgroundTileMapDisplaySelect || _windowTileMapDisplaySelect != windowTileMapDisplaySelect) { invalidateAllBackgroundTilesRequest = true; } break; } case 0xFF41: // LCDC Status lcdcLycLyCoincidenceInterruptEnabled = (value & 0x40) == 0x40; lcdcOamInterruptEnabled = (value & 0x20) == 0x20; lcdcVBlankInterruptEnabled = (value & 0x10) == 0x10; lcdcHBlankInterruptEnabled = (value & 0x08) == 0x08; lcdcMode = (LcdcModeType)(value & 0x03); break; case 0xFF42: // Scroll Y; scrollY = value; break; case 0xFF43: // Scroll X; scrollX = value; break; case 0xFF44: // LY ly = value; break; case 0xFF45: // LY Compare lyCompare = value; break; case 0xFF46: // Memory Transfer value <<= 8; for (int i = 0; i < 0x8C; i++) { WriteByte(0xFE00 | i, ReadByte(value | i)); } break; case 0xFF47: // Background palette Console.WriteLine("[0xFF47] = {0:X}", value); for (int i = 0; i < 4; i++) { switch (value & 0x03) { case 0: backgroundPalette [i] = WHITE; break; case 1: backgroundPalette [i] = LIGHT_GRAY; break; case 2: backgroundPalette [i] = DARK_GRAY; break; case 3: backgroundPalette [i] = BLACK; break; } value >>= 2; } invalidateAllBackgroundTilesRequest = true; break; case 0xFF48: // Object palette 0 for (int i = 0; i < 4; i++) { switch (value & 0x03) { case 0: objectPalette0 [i] = WHITE; break; case 1: objectPalette0 [i] = LIGHT_GRAY; break; case 2: objectPalette0 [i] = DARK_GRAY; break; case 3: objectPalette0 [i] = BLACK; break; } value >>= 2; } invalidateAllSpriteTilesRequest = true; break; case 0xFF49: // Object palette 1 for (int i = 0; i < 4; i++) { switch (value & 0x03) { case 0: objectPalette1 [i] = WHITE; break; case 1: objectPalette1 [i] = LIGHT_GRAY; break; case 2: objectPalette1 [i] = DARK_GRAY; break; case 3: objectPalette1 [i] = BLACK; break; } value >>= 2; } invalidateAllSpriteTilesRequest = true; break; case 0xFF4A: // Window Y windowY = value; break; case 0xFF4B: // Window X windowX = value; break; case 0xFFFF: // Interrupt Enable keyPressedInterruptEnabled = (value & 0x10) == 0x10; serialIOTransferCompleteInterruptEnabled = (value & 0x08) == 0x08; timerOverflowInterruptEnabled = (value & 0x04) == 0x04; lcdcInterruptEnabled = (value & 0x02) == 0x02; vBlankInterruptEnabled = (value & 0x01) == 0x01; break; } } }