public void Read_RegisteredMmio_ReadSuccess()
        {
            AgbMemoryMap map = new AgbMemoryMap();

            map.RegisterMmio(0xDEADBEEF, () => 0xAA, (b) => { });

            map.UpdateMmio();

            Assert.Equal(0xAA, map.Read(0xDEADBEEF));
        }
Exemple #2
0
        public static AgbCpu CreateCpu()
        {
            AgbMemoryMap map = new AgbMemoryMap();

            map.RegisterRegion(new InternalWramRegion());

            AgbCpu cpu = new AgbCpu(map);

            return(cpu);
        }
Exemple #3
0
        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));
        }
Exemple #4
0
        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
        }
Exemple #9
0
        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));
        }
Exemple #10
0
        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));
            }
        }
Exemple #12
0
        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));
            }
        }
Exemple #13
0
        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);
        }
Exemple #14
0
        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);
        }
Exemple #18
0
        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);
        }
Exemple #22
0
        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;
                }
            });
        }
Exemple #23
0
        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));
            }
        }
Exemple #24
0
        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);
        }
Exemple #27
0
        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;
            });
        }
Exemple #28
0
        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
        }
Exemple #29
0
        public static AgbPpu CreatePpu()
        {
            AgbMemoryMap memoryMap = new AgbMemoryMap();

            return(CreatePpu(memoryMap));
        }
Exemple #30
0
        public static AgbPpu CreatePpu(AgbMemoryMap memoryMap, AgbCpu cpu = null)
        {
            AgbPpu ppu = new AgbPpu(memoryMap, cpu);

            return(ppu);
        }