public void Read_RegisteredMmio_ReadSuccess() { AgbMemoryMap map = new AgbMemoryMap(); map.RegisterMmio(0xDEADBEEF, () => 0xAA, (b) => { }); map.UpdateMmio(); Assert.Equal(0xAA, map.Read(0xDEADBEEF)); }
public static AgbCpu CreateCpu() { AgbMemoryMap map = new AgbMemoryMap(); map.RegisterRegion(new InternalWramRegion()); AgbCpu cpu = new AgbCpu(map); return(cpu); }
public void WriteU32_WithDummyData_WriteSuccess() { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = new TestMemoryRegion(); map.RegisterRegion(region); map.WriteU32(FIRST_RANGE_START, 0xAABBCCDD); Assert.Equal(0xAABBCCDD, map.ReadU32(FIRST_RANGE_START)); }
public void ReadWrite_WriteToRealAndReadFromMirror_MirrorReadSuccess() { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = CreateRegion(); map.RegisterRegion(region); map.Write(RegionStart + RegionSize - 1, 0xAB); Assert.Equal(0xAB, map.Read(MirrorStart + RegionSize - 1)); }
public void UpdateKeyState_ChangeStateToPressedAndCheckBitfield_BitSet(ControllerKey key) { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbController controller = new AgbController(memoryMap, null); controller.UpdateKeyState(key, true); ushort mmioValue = memoryMap.ReadU16(0x4000130); Assert.True(BitUtil.IsBitSet(mmioValue, (int)key)); }
public void Dispcnt_SetAllBitsWithMemoryMap_AllBitsSet() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap); memoryMap.WriteU16(0x4000000, 0xFFFF); memoryMap.FlushMmio(); Assert.Equal(0xFFFF, memoryMap.ReadU16(0x4000000)); }
public void Dispstat_SetAllConfigurableBitsWithMemoryMap_AllConfigurableBitsSet() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap); memoryMap.WriteU16(0x4000004, 0xFF38); memoryMap.FlushMmio(); Assert.Equal(0xFF38, memoryMap.ReadU16(0x4000004)); }
public void Vcount_TickPpuToLine100_VcountCorrect() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap); PpuUtil.TickPpuByAmount(ppu, 100, 0); memoryMap.UpdateMmio(); Assert.Equal(0x0064, memoryMap.ReadU16(0x4000006)); // V-Count flag }
public void ReadU16_WithDummyData_ReadSuccess() { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = new TestMemoryRegion(); // Write directly to the region region.Write(FIRST_RANGE_START, 0xBB); region.Write(FIRST_RANGE_START + 1, 0xAA); map.RegisterRegion(region); Assert.Equal(0xAABB, map.ReadU16(FIRST_RANGE_START)); }
public AgbDevice() { MemoryMap = new AgbMemoryMap(); Cpu = new AgbCpu(MemoryMap); Ppu = new AgbPpu(MemoryMap, Cpu); Controller = new AgbController(MemoryMap, Cpu); MemoryMap.RegisterRegion(new InternalWramRegion()); MemoryMap.RegisterRegion(new ExternalWramRegion()); PostBootFlag = 0; MemoryMap.RegisterMmio(0x4000300, () => PostBootFlag, (x) => PostBootFlag = x); }
public void Read_FromRegion_ReadSuccess(uint startAddress) { byte[] dummyData = CreateDummyData(); AgbMemoryMap map = new AgbMemoryMap(); RomRegion region = new RomRegion(dummyData); map.RegisterRegion(region); for (uint address = startAddress; address < startAddress + RomRegion.REGION_SIZE; address++) { Assert.Equal(dummyData[address - startAddress], map.Read(address)); } }
public void Write_RegisteredTestRegion_WriteRangeSuccess(uint rangeStart) { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = new TestMemoryRegion(); map.RegisterRegion(region); for (uint i = rangeStart; i < rangeStart + TEST_REGION_SIZE; i++) { byte value = (byte)(i & 0xFF); map.Write(i, value); Assert.Equal(value, map.Read(i)); } }
public void Write_RegisteredMmio32AndWriteToAll_WriteSuccess() { AgbMemoryMap map = new AgbMemoryMap(); uint val = 0xCAFEBABE; map.RegisterMmio32(0xDEADBEEF, () => val, (b) => { val = b; }); map.WriteU32(0xDEADBEEF, 0xAABBCCDD); map.FlushMmio(); Assert.Equal(0xAABBCCDD, val); }
public void Write_RegisteredMmio32AndWriteToFourthByte_WriteSuccess() { AgbMemoryMap map = new AgbMemoryMap(); uint val = 0xCAFEBABE; map.RegisterMmio32(0xDEADBEEF, () => val, (b) => { val = b; }); map.Write(0xDEADBEEF + 3, 0xDD); map.FlushMmio(); Assert.Equal(0xDDFEBABE, val); }
public void Dispstat_SetVCountAndTickUntilVCountLine_VCountFlagSet() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap); memoryMap.WriteU16(0x4000004, 0x6400); // VCount = line 100 memoryMap.FlushMmio(); PpuUtil.TickPpuByAmount(ppu, 100, 0); memoryMap.UpdateMmio(); Assert.Equal(0x6404, memoryMap.ReadU16(0x4000004)); // V-Count flag }
public void Dispstat_TickUntilHBlankPeriodInVBlank_HBlankFlagSet() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap); memoryMap.WriteU16(0x4000004, 0xFF00); // VCount = line 255 (impossible) memoryMap.FlushMmio(); PpuUtil.TickPpuByAmount(ppu, 160, 240); memoryMap.UpdateMmio(); Assert.Equal(0xFF03, memoryMap.ReadU16(0x4000004)); // H-Blank and V-Blank flags }
public void Write_RegisteredMmio16AndWriteToHiAndLo_WriteSuccess() { AgbMemoryMap map = new AgbMemoryMap(); ushort val = 0xFFFE; map.RegisterMmio16(0xDEADBEEF, () => val, (b) => { val = b; }); map.WriteU16(0xDEADBEEF, 0xAABB); map.FlushMmio(); Assert.Equal(0xAABB, val); }
public void Write_RegisteredRegion_WriteRangeSuccess() { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = CreateRegion(); map.RegisterRegion(region); for (uint i = RegionStart; i < RegionStart + RegionSize; i++) { byte value = (byte)(i & 0xFF); map.Write(i, value); Assert.Equal(value, map.Read(i)); } }
public void UpdateKeyState_ChangeAStateToPressedWithLogicalOrInterrupts_CpuInterrupted() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbCpu cpu = new AgbCpu(memoryMap); AgbController controller = new AgbController(memoryMap, cpu); memoryMap.WriteU32(0x4000208, 1); // IME = 1 memoryMap.WriteU16(0x4000200, 0x1000); // IE = Key memoryMap.WriteU16(0x4000132, 0x4003); // Key interrupts enabled, logical OR, A or B memoryMap.FlushMmio(); controller.UpdateKeyState(ControllerKey.A, true); Assert.Equal(CpuMode.Irq, cpu.CurrentStatus.Mode); }
public void UpdateKeyState_OnlyAPressedWithLogicalAndInterrupts_CpuNotInterrupted() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbCpu cpu = new AgbCpu(memoryMap); AgbController controller = new AgbController(memoryMap, cpu); memoryMap.WriteU32(0x4000208, 1); // IME = 1 memoryMap.WriteU16(0x4000200, 0x1000); // IE = Key memoryMap.WriteU16(0x4000132, 0xC003); // Key interrupts enabled, logical AND, A and B memoryMap.FlushMmio(); controller.UpdateKeyState(ControllerKey.A, true); Assert.Equal(CpuMode.User, cpu.CurrentStatus.Mode); }
public void Write_RegisteredMmio_WriteSuccess() { AgbMemoryMap map = new AgbMemoryMap(); byte val = 0; map.RegisterMmio(0xDEADBEEF, () => val, (b) => { val = b; }); map.Write(0xDEADBEEF, 0xBB); map.FlushMmio(); Assert.Equal(0xBB, val); }
public AgbController(AgbMemoryMap memoryMap, AgbCpu cpu) { Cpu = cpu; PressedKeys = 0x3FF; // all released InterruptBitfield = 0; // all ignore InterruptCondition = 0; // disabled InterruptCondition = ControllerInterruptCondition.LogicalOr; memoryMap.RegisterMmio16(0x4000130, () => { return((ushort)PressedKeys); }, (x) => { // Reads ignored }); memoryMap.RegisterMmio16(0x4000132, () => { ushort x = (ushort)InterruptBitfield; if (InterruptsEnabled) { BitUtil.SetBit(ref x, 14); } if (InterruptCondition == ControllerInterruptCondition.LogicalAnd) { BitUtil.SetBit(ref x, 15); } return(x); }, (x) => { InterruptBitfield = (uint)(x & 0x3ff); InterruptsEnabled = BitUtil.IsBitSet(x, 14); if (BitUtil.IsBitSet(x, 15)) { InterruptCondition = ControllerInterruptCondition.LogicalAnd; } else { InterruptCondition = ControllerInterruptCondition.LogicalOr; } }); }
public void Read_RegisteredRegion_ReadRangeSuccess() { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = CreateRegion(); // Write direct to the region instead of through AgbMemoryMap for (uint i = RegionStart; i < RegionStart + RegionSize; i++) { region.Write(i, (byte)(i & 0xFF)); } map.RegisterRegion(region); for (uint i = RegionStart; i < RegionStart + RegionSize; i++) { Assert.Equal(i & 0xFF, map.Read(i)); } }
public void Read_RegisteredTestRegion_ReadRangeSuccess(uint rangeStart) { AgbMemoryMap map = new AgbMemoryMap(); RangedMemoryRegion region = new TestMemoryRegion(); // Write direct to the region instead of through AgbMemoryMap for (uint i = rangeStart; i < rangeStart + TEST_REGION_SIZE; i++) { region.Write(i, (byte)(i & 0xFF)); } map.RegisterRegion(region); for (uint i = rangeStart; i < rangeStart + TEST_REGION_SIZE; i++) { Assert.Equal(i & 0xFF, map.Read(i)); } }
public void Dispstat_EnableHBlankIrqAndTickUntilHBlank_InterruptRequested() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbCpu cpu = new AgbCpu(memoryMap); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap, cpu); memoryMap.WriteU32(0x4000208, 0x01); // Interrupt Master Enable memoryMap.WriteU16(0x4000200, 0x0002); // IE = H-Blank IRQ enabled memoryMap.WriteU16(0x4000004, 0xFF10); // DISPSTAT = H-Blank IRQ enabled, VCount = line 255 memoryMap.FlushMmio(); PpuUtil.TickPpuByAmount(ppu, 0, 240); memoryMap.UpdateMmio(); Assert.Equal(0xFF12, memoryMap.ReadU16(0x4000004)); // H-Blank flag, H-Blank IRQ enabled Assert.Equal(0x0002, memoryMap.ReadU16(0x4000202)); // IF = H-Count Assert.Equal(CpuMode.Irq, cpu.CurrentStatus.Mode); }
public void Dispstat_SetVCountAndEnableVCountIrqAndTickUntilVCountLine_InterruptRequested() { AgbMemoryMap memoryMap = new AgbMemoryMap(); AgbCpu cpu = new AgbCpu(memoryMap); AgbPpu ppu = PpuUtil.CreatePpu(memoryMap, cpu); memoryMap.WriteU32(0x4000208, 0x01); // Interrupt Master Enable memoryMap.WriteU16(0x4000200, 0x0004); // IE = V-Count IRQ enabled memoryMap.WriteU16(0x4000004, 0x6420); // DISPSTAT = V-Blank IRQ enabled, VCount = line 100 memoryMap.FlushMmio(); PpuUtil.TickPpuByAmount(ppu, 100, 0); memoryMap.UpdateMmio(); Assert.Equal(0x6424, memoryMap.ReadU16(0x4000004)); // V-Count flag, V-Count IRQ enabled Assert.Equal(0x0004, memoryMap.ReadU16(0x4000202)); // IF = V-Count Assert.Equal(CpuMode.Irq, cpu.CurrentStatus.Mode); }
public AgbCpu(AgbMemoryMap memoryMap) { RegisterSets = new Dictionary <CpuMode, IRegisterSet>(); IRegisterSet baseSet = new BaseRegisterSet(); RegisterSets[CpuMode.User] = baseSet; RegisterSets[CpuMode.System] = baseSet; RegisterSets[CpuMode.FastIrq] = new FastIrqRegisterSet(baseSet); RegisterSets[CpuMode.Irq] = new OverrideRegisterSet(baseSet); RegisterSets[CpuMode.Supervisor] = new OverrideRegisterSet(baseSet); RegisterSets[CpuMode.Abort] = new OverrideRegisterSet(baseSet); RegisterSets[CpuMode.Undefined] = new OverrideRegisterSet(baseSet); SavedStatuses = new Dictionary <CpuMode, ProgramStatus>(); SavedStatuses[CpuMode.System] = new ProgramStatus(); SavedStatuses[CpuMode.FastIrq] = new ProgramStatus(); SavedStatuses[CpuMode.Irq] = new ProgramStatus(); SavedStatuses[CpuMode.Supervisor] = new ProgramStatus(); SavedStatuses[CpuMode.Abort] = new ProgramStatus(); SavedStatuses[CpuMode.Undefined] = new ProgramStatus(); CurrentStatus = new ProgramStatus(); CurrentStatus.Mode = CpuMode.User; InterruptMasterEnable = true; EnabledInterrupts = 0; AcknowledgedInterrupts = 0; ArmInterpreter = new ArmInterpreter(this); ThumbInterpreter = new ThumbInterpreter(this); MemoryMap = memoryMap; // Interrupts MMIO memoryMap.RegisterMmio32(0x4000208, () => // IME { if (InterruptMasterEnable) { return(1); } else { return(0); } }, (x) => { InterruptMasterEnable = BitUtil.IsBitSet(x, 0); }); memoryMap.RegisterMmio16(0x4000200, () => // IE { return((ushort)EnabledInterrupts); }, (x) => { EnabledInterrupts = x; }); memoryMap.RegisterMmio16(0x4000202, () => // IF { return((ushort)AcknowledgedInterrupts); }, (x) => { AcknowledgedInterrupts = x; }); }
public AgbPpu(AgbMemoryMap memoryMap, AgbCpu cpu) { MemoryMap = memoryMap; Cpu = cpu; State = PpuState.Render; HorizontalDot = 0; VerticalLine = 0; Framebuffer = new byte[4 * 240 * 160]; Renderbuffer = new byte[4 * 240 * 160]; PaletteRam = new PaletteRamRegion(); memoryMap.RegisterRegion(PaletteRam); VideoRam = new VideoRamRegion(); memoryMap.RegisterRegion(VideoRam); Oam = new OamRegion(); memoryMap.RegisterRegion(Oam); #region DISPCNT BackgroundMode = 0; CgbMode = false; UseAltFrame = false; AllowOamDuringHBlank = false; UseOneDimensionalObjs = false; ForcedBlank = false; DisplayBgZero = false; DisplayBgOne = false; DisplayBgTwo = false; DisplayBgThree = false; DisplayObjs = false; DisplayWindowZero = false; DisplayWindowOne = false; DisplayWindowObjs = false; MemoryMap.RegisterMmio16(0x4000000, () => { ushort x = 0; if (CgbMode) { BitUtil.SetBit(ref x, 3); } if (UseAltFrame) { BitUtil.SetBit(ref x, 4); } if (AllowOamDuringHBlank) { BitUtil.SetBit(ref x, 5); } if (UseOneDimensionalObjs) { BitUtil.SetBit(ref x, 6); } if (ForcedBlank) { BitUtil.SetBit(ref x, 7); } if (DisplayBgZero) { BitUtil.SetBit(ref x, 8); } if (DisplayBgOne) { BitUtil.SetBit(ref x, 9); } if (DisplayBgTwo) { BitUtil.SetBit(ref x, 10); } if (DisplayBgThree) { BitUtil.SetBit(ref x, 11); } if (DisplayObjs) { BitUtil.SetBit(ref x, 12); } if (DisplayWindowZero) { BitUtil.SetBit(ref x, 13); } if (DisplayWindowOne) { BitUtil.SetBit(ref x, 14); } if (DisplayWindowObjs) { BitUtil.SetBit(ref x, 15); } x |= (ushort)BackgroundMode; return(x); }, (x) => { BackgroundMode = BitUtil.GetBitRange(x, 0, 2); CgbMode = BitUtil.IsBitSet(x, 3); UseAltFrame = BitUtil.IsBitSet(x, 4); AllowOamDuringHBlank = BitUtil.IsBitSet(x, 5); UseOneDimensionalObjs = BitUtil.IsBitSet(x, 6); ForcedBlank = BitUtil.IsBitSet(x, 7); DisplayBgZero = BitUtil.IsBitSet(x, 8); DisplayBgOne = BitUtil.IsBitSet(x, 9); DisplayBgTwo = BitUtil.IsBitSet(x, 10); DisplayBgThree = BitUtil.IsBitSet(x, 11); DisplayObjs = BitUtil.IsBitSet(x, 12); DisplayWindowZero = BitUtil.IsBitSet(x, 13); DisplayWindowOne = BitUtil.IsBitSet(x, 14); DisplayWindowObjs = BitUtil.IsBitSet(x, 15); }); #endregion #region DISPSTAT VBlankIrqEnable = false; HBlankIrqEnable = false; VCountIrqEnable = false; VCountSetting = 0; MemoryMap.RegisterMmio16(0x4000004, () => { ushort x = 0; if (State == PpuState.VBlank && VerticalLine != 227) { BitUtil.SetBit(ref x, 0); } if (HorizontalDot >= 240) // H-Blank flag set even in V-Blank { BitUtil.SetBit(ref x, 1); } if (VCountSetting == VerticalLine) { BitUtil.SetBit(ref x, 2); } if (VBlankIrqEnable) { BitUtil.SetBit(ref x, 3); } if (HBlankIrqEnable) { BitUtil.SetBit(ref x, 4); } if (VCountIrqEnable) { BitUtil.SetBit(ref x, 5); } // Bits 6 and 7 unused on AGB x |= (ushort)(VCountSetting << 8); return(x); }, (x) => { // Bits 0 to 2 are read-only, and bits 6 and 7 are unused on AGB VBlankIrqEnable = BitUtil.IsBitSet(x, 3); HBlankIrqEnable = BitUtil.IsBitSet(x, 4); VCountIrqEnable = BitUtil.IsBitSet(x, 5); VCountSetting = BitUtil.GetBitRange(x, 8, 15); }); #endregion #region VCOUNT MemoryMap.RegisterMmio16(0x4000006, () => { // Bit 8 unused on AGB, bits 9 to 15 unused on all platforms return((ushort)VerticalLine); }, (x) => { // Writes ignored }); #endregion }
public static AgbPpu CreatePpu() { AgbMemoryMap memoryMap = new AgbMemoryMap(); return(CreatePpu(memoryMap)); }
public static AgbPpu CreatePpu(AgbMemoryMap memoryMap, AgbCpu cpu = null) { AgbPpu ppu = new AgbPpu(memoryMap, cpu); return(ppu); }