public void WriteByte(int address, byte value) { if (address < 0x8000) { _cartridge.WriteByte(address, value); } else if (address < 0xA000) { _gpu.WriteByte(address, value); } else if (address < 0xC000) { _cartridge.WriteByte(address, value); } else if (address < 0xFE00) { _lowRam[address] = value; } else if (address < 0xFEA0) { _gpu.WriteByte(address, value); } else if (address < 0xFF40) { _sound[address - 0xFF00] = value; } else if (address < 0xFF80) { _gpu.WriteByte(address, value); } else if (address < 0xFFFF) // FF80 -> FFFE { _highRam[address & 0x7F] = value; } else { throw new InvalidOperationException("Non managed MMU access"); } //if (address >= 0x8000 && address < 0xA000) //{ // int shiftedAddress = address & 0x1FFF; // _gpu.VRam[shiftedAddress] = value; //} //else if (address >= 0xFF40 && address < 0xFF7F) // _gpu.WriteByte(address, value); //else //{ // var area = GetBufferArea(address); // area[0] = value; //} }
public void WriteByteTest(ushort address, byte value, byte expected) { var data = new byte[0x8000]; _cartridge = new Cartridge(data); _cartridge.WriteByte(address, value); Assert.Equal(expected, _cartridge.ReadByte(address)); }
private void LoadMemoryFromRegister(ushort address, byte register) { cartridge.WriteByte(address, register); m = 2; // Two m-time taken. }
public void WriteByte(int address, int value) { if (address >= 0xC000 && address <= 0xDFFF) { wRam[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; vRam[videoRamIndex] = (byte)value; if (address < 0x9000) { GPU.spriteTileInvalidated[videoRamIndex >> 4] = true; } if (address < 0x9800) { GPU.invalidateAllBackgroundTilesRequest = true; } else if (address >= 0x9C00) { int tileIndex = address - 0x9C00; GPU.backgroundTileInvalidated[tileIndex >> 5, tileIndex & 0x1F] = true; } else { int tileIndex = address - 0x9800; GPU.backgroundTileInvalidated[tileIndex >> 5, tileIndex & 0x1F] = true; } } else if (address <= 0x7FFF || (address >= 0xA000 && address <= 0xBFFF)) { cartridge.WriteByte(address, value); } else if (address >= 0xE000 && address <= 0xFDFF) { wRam[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 Z80.timerCounter = value; break; case 0xFF06: // Timer modulo Z80.timerModulo = value; break; case 0xFF07: // Time Control Z80.timerRunning = (value & 0x04) == 0x04; Z80.timerFrequency = (TimerFrequencyType)(0x03 & value); break; case 0xFF0F: // Interrupt Flag (an interrupt request) Z80.keyPressedInterruptRequested = (value & 0x10) == 0x10; Z80.serialIOTransferCompleteInterruptRequested = (value & 0x08) == 0x08; Z80.timerOverflowInterruptRequested = (value & 0x04) == 0x04; Z80.lcdcInterruptRequested = (value & 0x02) == 0x02; Z80.vBlankInterruptRequested = (value & 0x01) == 0x01; break; case 0xFF40: { // LCDC control bool _backgroundAndWindowTileDataSelect = GPU.backgroundAndWindowTileDataSelect; bool _backgroundTileMapDisplaySelect = GPU.backgroundTileMapDisplaySelect; bool _windowTileMapDisplaySelect = GPU.windowTileMapDisplaySelect; Z80.lcdControlOperationEnabled = (value & 0x80) == 0x80; GPU.windowTileMapDisplaySelect = (value & 0x40) == 0x40; GPU.windowDisplayed = (value & 0x20) == 0x20; GPU.backgroundAndWindowTileDataSelect = (value & 0x10) == 0x10; GPU.backgroundTileMapDisplaySelect = (value & 0x08) == 0x08; GPU.largeSprites = (value & 0x04) == 0x04; GPU.spritesDisplayed = (value & 0x02) == 0x02; GPU.backgroundDisplayed = (value & 0x01) == 0x01; if (_backgroundAndWindowTileDataSelect != GPU.backgroundAndWindowTileDataSelect || _backgroundTileMapDisplaySelect != GPU.backgroundTileMapDisplaySelect || _windowTileMapDisplaySelect != GPU.windowTileMapDisplaySelect) { GPU.invalidateAllBackgroundTilesRequest = true; } break; } case 0xFF41: // LCDC Status GPU.lcdcLycLyCoincidenceInterruptEnabled = (value & 0x40) == 0x40; GPU.lcdcOamInterruptEnabled = (value & 0x20) == 0x20; GPU.lcdcVBlankInterruptEnabled = (value & 0x10) == 0x10; GPU.lcdcHBlankInterruptEnabled = (value & 0x08) == 0x08; GPU.lcdcMode = (LcdcModeType)(value & 0x03); break; case 0xFF42: // Scroll Y; GPU.scrollY = value; break; case 0xFF43: // Scroll X; GPU.scrollX = value; break; case 0xFF44: // LY GPU.ly = value; break; case 0xFF45: // LY Compare GPU.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: GPU.backgroundPalette[i] = 0xFFFFFFFF; break; case 1: GPU.backgroundPalette[i] = 0xFFAAAAAA; break; case 2: GPU.backgroundPalette[i] = 0xFF555555; break; case 3: GPU.backgroundPalette[i] = 0xFF000000; break; } value >>= 2; } GPU.invalidateAllBackgroundTilesRequest = true; break; case 0xFF48: // Object palette 0 for (int i = 0; i < 4; i++) { switch (value & 0x03) { case 0: GPU.objectPallete0[i] = 0xFFFFFFFF; break; case 1: GPU.objectPallete0[i] = 0xFFAAAAAA; break; case 2: GPU.objectPallete0[i] = 0xFF555555; break; case 3: GPU.objectPallete0[i] = 0xFF000000; break; } value >>= 2; } GPU.invalidateAllSpriteTilesRequest = true; break; case 0xFF49: // Object palette 1 for (int i = 0; i < 4; i++) { switch (value & 0x03) { case 0: GPU.objectPallete1[i] = 0xFFFFFFFF; break; case 1: GPU.objectPallete1[i] = 0xFFAAAAAA; break; case 2: GPU.objectPallete1[i] = 0xFF555555; break; case 3: GPU.objectPallete1[i] = 0xFF000000; break; } value >>= 2; } GPU.invalidateAllSpriteTilesRequest = true; break; case 0xFF4A: // Window Y GPU.windowY = value; break; case 0xFF4B: // Window X GPU.windowX = value; break; case 0xFFFF: // Interrupt Enable Z80.keyPressedInterruptEnabled = (value & 0x10) == 0x10; Z80.serialIOTransferCompleteInterruptEnabled = (value & 0x08) == 0x08; Z80.timerOverflowInterruptEnabled = (value & 0x04) == 0x04; Z80.lcdcInterruptEnabled = (value & 0x02) == 0x02; Z80.vBlankInterruptEnabled = (value & 0x01) == 0x01; break; } } }
public void WriteByte(ushort addr, byte data, bool applySideEffects = true) { if (addr < 0x8000) { cartridge.WriteByte(addr, data); } else if (addr < 0xA000) { byte lcdControl = ioRegisters[0xFF40 - 0xFF00]; int lcdMode = ioRegisters[0xFF41 - 0xFF00] & 3; if (!lcdControl.IsBitSet(7) || lcdMode != (int)PPU.Mode.PixelTransfer) { // VRAM is not accessible during pixel transfer if lcd is enabled videoRam[addr - 0x8000] = data; } } else if (addr < 0xC000) { cartridge.WriteByte(addr, data); } else if (addr < 0xE000) { workRam[addr - 0xC000] = data; } else if (addr < 0xFE00) { // Echo of internal RAM workRam[addr - 0xE000] = data; } else if (addr < 0xFEA0) { byte lcdControl = ioRegisters[0xFF40 - 0xFF00]; int lcdMode = ioRegisters[0xFF41 - 0xFF00] & 3; if (!lcdControl.IsBitSet(7) || (lcdMode != (int)PPU.Mode.OamSearch && lcdMode != (int)PPU.Mode.PixelTransfer)) { // OAM not accessible during OAM search and pixel transfer oam[addr - 0xFE00] = data; } } else if (addr < 0xFF00) { // Empty but unusable for I/O } else if (addr < 0xFF4C) { if (addr == 0xFF04 && applySideEffects) { data = 0x00; DivResetEvent?.Invoke(); } if (addr == 0xFF02 && data == 0x81 && applySideEffects) { Serial.Add(ioRegisters[1]); } if (addr == 0xFF00 && applySideEffects) { data = (byte)((ioRegisters[0] & 0xF) | (data & 0x30)); } if (addr == 0xFF46 && applySideEffects) { LaunchDMATransfer(data); } ioRegisters[addr - 0xFF00] = data; } else if (addr < 0xFF80) { if (addr == BOOT_ROM_LOCK_ADDRESS && data == 0b1 && RomMapMode == MapMode.Boot && applySideEffects) { RomMapMode = MapMode.Cartridge; } else { // Empty but unusable for I/O } }