Exemple #1
0
 public MemoryBus(PPURegisters ppuRegisters)
 {
     PPURegisters = ppuRegisters;
     //Randomize(wram);
     //Randomize(zram);
     //Randomize(VideoMemory);
     //Randomize(ObjectAttributeMemory);
     TileSet   = new TileSet(VideoMemory);
     SpriteOam = new SpriteOam(ppuRegisters, ObjectAttributeMemory, TileSet);
     Timer     = new Timer(this);
 }
Exemple #2
0
        public byte this[int address]
        {
            get
            {
                if (address < 0)
                {
                    throw new ArgumentOutOfRangeException("Address cannot be negative.");
                }
                else if (address < 0x4000)
                {
                    if (address < 0x100 && IsBootRomMapped)
                    {
                        return(bootRom[address]);
                    }
                    return(cart[address]);  //ROM bank 0 (16K)
                }
                else if (address < 0x8000)
                {
                    return(cart[address]);  //ROM bank 1-N (16K, switchable if cart has an MBC)
                }
                else if (address < 0xA000)
                {
                    //VRAM (8K)
                    return(VideoMemory[address - 0x8000]);
                }
                else if (address < 0xC000)
                {
                    //external RAM (8K)
                    return(cart[address - 0xA000]);
                }
                else if (address < 0xE000)
                {
                    //internal work RAM (8K)
                    return(wram[address - 0xC000]);
                }
                else if (address < 0xFE00)
                {
                    //internal work RAM shadow
                    return(wram[address - 0xE000]);
                }
                else if (address < 0xFEA0)
                {
                    //sprite OAM (160 bytes)
                    return(ObjectAttributeMemory[address - 0xFE00]);
                }
                else if (address < 0xFF00)
                {
                    //unusable memory range, should always read as zero
                    return(0);
                }
                else if (address < 0xFF80)
                {
                    //various hardware I/O registers (PPU, APU, joypad, etc)
                    if (address == 0xFF00)
                    {
                        return(JoypadRegister.Read());
                    }
                    else if (address == 0xFF01)
                    {
                        return(serialData);
                    }
                    else if (address == 0xFF02)
                    {
                        return(serialControl);
                    }
                    else if (Timer.MappedToAddress(address))
                    {
                        return(Timer[address]);
                    }
                    else if (address == 0xFF0F)
                    {
                        return(InterruptFlags.Data);
                    }
                    else if (soundRegisters.MappedToAddress(address))
                    {
                        return(soundRegisters[address]);
                    }
                    else if (address == 0xFF40)
                    {
                        return(PPURegisters.LCDControl.Data);
                    }
                    else if (address == 0xFF41)
                    {
                        return(PPURegisters.LCDStatus.Data);
                    }
                    else if (address == 0xFF42)
                    {
                        return(PPURegisters.ScrollY);
                    }
                    else if (address == 0xFF43)
                    {
                        return(PPURegisters.ScrollX);
                    }
                    else if (address == 0xFF44)
                    {
                        return(PPURegisters.CurrentScanline);
                    }
                    else if (address == 0xFF45)
                    {
                        return(PPURegisters.CompareScanline);
                    }
                    else if (address == 0xFF47)
                    {
                        return(PPURegisters.BackgroundPalette.Data);
                    }
                    else if (address == 0xFF48)
                    {
                        return(PPURegisters.SpritePalette0.Data);
                    }
                    else if (address == 0xFF49)
                    {
                        return(PPURegisters.SpritePalette1.Data);
                    }
                    else if (address == 0xFF4A)
                    {
                        return(PPURegisters.WindowY);
                    }
                    else if (address == 0xFF4B)
                    {
                        return(PPURegisters.WindowX);
                    }
                    else
                    {
                        return(0xFF);
                    }
                    //else throw new NotImplementedException($"Unsupported read of address ${address:X4} (not all hardware I/O registers are implemented yet).");
                }
                else if (address < 0xFFFF)
                {
                    //zero page (128 bytes)
                    return(zram[address - 0xFF80]);
                }
                else if (address == 0xFFFF)
                {
                    return(InterruptEnable.Data);
                }
                else
                {
                    throw new ArgumentOutOfRangeException("Address must be between $0000 and $FFFF");
                }
            }

            set
            {
                if (address < 0)
                {
                    throw new ArgumentOutOfRangeException("Address cannot be negative.");
                }
                else if (address < 0x8000)
                {
                    //memory bank controllers intercept ROM writes and use them for bank switching and etc
                    cart[address] = value;

                    //why does Tetris write here when it doesn't use one?
                    //see: https://www.reddit.com/r/EmuDev/comments/5ht388/gb_why_does_tetris_write_to_the_rom/
                }
                else if (address >= 0x8000 && address < 0xA000)
                {
                    //VRAM (8K)
                    var  vramAddress    = address - 0x8000;
                    bool isTilesetDirty = address < 0x9800 && VideoMemory[vramAddress] != value;
                    VideoMemory[vramAddress] = value;
                    if (isTilesetDirty)
                    {
                        TileSet.UpdateFromMemoryWrite(VideoMemory, vramAddress);
                    }
                }
                else if (address < 0xC000)
                {
                    //external RAM (8K)
                    cart[address - 0xA000] = value;
                }
                else if (address < 0xE000)
                {
                    //internal work RAM (8K)
                    wram[address - 0xC000] = value;
                }
                else if (address < 0xFE00)
                {
                    //internal work RAM shadow
                    wram[address - 0xE000] = value;
                }
                else if (address < 0xFEA0)
                {
                    //sprite OAM (160 bytes)
                    var  oamAddress = address - 0xFE00;
                    bool isOamDirty = ObjectAttributeMemory[oamAddress] != value;
                    ObjectAttributeMemory[address - 0xFE00] = value;
                    if (isOamDirty)
                    {
                        SpriteOam.UpdateFromMemoryWrite(ObjectAttributeMemory, oamAddress);
                    }
                }
                else if (address < 0xFF00)
                {
                    //unusable memory, but same games (like Tetris) write here (by mistake?)... so don't crash
                    //see: https://www.reddit.com/r/EmuDev/comments/5nixai/gb_tetris_writing_to_unused_memory/
                }
                else if (address < 0xFF80)
                {
                    //various hardware I/O registers (PPU, APU, joypad, etc)
                    if (address == 0xFF00)
                    {
                        JoypadRegister.Write(value);
                    }
                    else if (address == 0xFF01)
                    {
                        serialData = value;
                        gameLinkConsole?.Print(value);
                    }
                    else if (address == 0xFF02)
                    {
                        serialControl = value;
                    }
                    else if (Timer.MappedToAddress(address))
                    {
                        Timer[address] = value;
                    }
                    else if (address == 0xFF0F)
                    {
                        InterruptFlags.Data = (byte)(value | 0b1110_0000);                          //upper 3 bits are unused and always read as 1
                    }
                    else if (soundRegisters.MappedToAddress(address))
                    {
                        soundRegisters[address] = value;
                    }
                    else if (address == 0xFF40)
                    {
                        PPURegisters.LCDControl.Data = value;
                    }
                    else if (address == 0xFF41)
                    {
                        PPURegisters.LCDStatus.Data = value;
                    }
                    else if (address == 0xFF42)
                    {
                        PPURegisters.ScrollY = value;
                    }
                    else if (address == 0xFF43)
                    {
                        PPURegisters.ScrollX = value;
                    }
                    else if (address == 0xFF44)
                    {
                        PPURegisters.CurrentScanline = value;
                    }
                    else if (address == 0xFF45)
                    {
                        PPURegisters.CompareScanline = value;
                    }
                    else if (address == 0xFF46)
                    {
                        OamDmaTransfer(value);
                    }
                    else if (address == 0xFF47)
                    {
                        PPURegisters.BackgroundPalette.Data = value;
                    }
                    else if (address == 0xFF48)
                    {
                        PPURegisters.SpritePalette0.Data = value;
                    }
                    else if (address == 0xFF49)
                    {
                        PPURegisters.SpritePalette1.Data = value;
                    }
                    else if (address == 0xFF50)
                    {
                        IsBootRomMapped = false;
                    }
                    else if (address == 0xFF4A)
                    {
                        PPURegisters.WindowY = value;
                    }
                    else if (address == 0xFF4B)
                    {
                        PPURegisters.WindowX = value;
                    }
                    //else throw new NotImplementedException($"Unsupported write to address ${address:X4} (not all hardware I/O registers are implemented yet).");
                }
                else if (address < 0xFFFF)
                {
                    //zero page (128 bytes)
                    zram[address - 0xFF80] = value;
                }
                else if (address == 0xFFFF)
                {
                    InterruptEnable.Data = value;
                }
                else
                {
                    throw new ArgumentOutOfRangeException($"Unexpected write to address ${address:X4}");
                }
            }
        }