feat(memory): add MMU memory map and OAM DMA

This commit is contained in:
dualfroz committed 2026-08-26 15:12:19 +02:00
1 parent 3c947b637e
commit de52cc23cf
2 files changed
+206

No files matched your search

+142
View File
@@ -0,0 +1,142 @@
using System;
using System.IO;
using GameBoy.Audio;
using GameBoy.Cpu;
using GameBoy.Input;
using GameBoy.Serial;
using GameBoy.Timing;
using GameBoy.Video;
namespace GameBoy.Memory
{
public sealed class Mmu
{
private readonly Cartridge.Cartridge cartridge;
private readonly Ppu ppu;
private readonly Timer timer;
private readonly Joypad joypad;
private readonly SerialPort serial;
private readonly Apu apu;
private readonly InterruptController interrupts;
private readonly byte[] wram = new byte[0x2000];
private readonly byte[] hram = new byte[0x7F];
public OamDma? Dma;
public Mmu(
Cartridge.Cartridge cartridge,
Ppu ppu,
Timer timer,
Joypad joypad,
SerialPort serial,
Apu apu,
InterruptController interrupts)
{
this.cartridge = cartridge;
this.ppu = ppu;
this.timer = timer;
this.joypad = joypad;
this.serial = serial;
this.apu = apu;
this.interrupts = interrupts;
}
public byte Read(ushort address)
{
if (address < 0x8000)
return cartridge.Read(address);
if (address < 0xA000)
return ppu.ReadVram(address);
if (address < 0xC000)
return cartridge.Read(address);
if (address < 0xE000)
return wram[address - 0xC000];
if (address < 0xFE00)
return wram[address - 0xE000];
if (address < 0xFEA0)
return ppu.ReadOam(address);
if (address < 0xFF00)
return 0xFF;
if (address < 0xFF80)
return ReadIo(address);
if (address < 0xFFFF)
return hram[address - 0xFF80];
return interrupts.Enable;
}
public void Write(ushort address, byte value)
{
if (address < 0x8000)
cartridge.Write(address, value);
else if (address < 0xA000)
ppu.WriteVram(address, value);
else if (address < 0xC000)
cartridge.Write(address, value);
else if (address < 0xE000)
wram[address - 0xC000] = value;
else if (address < 0xFE00)
wram[address - 0xE000] = value;
else if (address < 0xFEA0)
ppu.WriteOam(address, value);
else if (address < 0xFF00)
{
}
else if (address < 0xFF80)
WriteIo(address, value);
else if (address < 0xFFFF)
hram[address - 0xFF80] = value;
else
interrupts.Enable = value;
}
private byte ReadIo(ushort address)
{
if (address == 0xFF00)
return joypad.Read();
if (address == 0xFF01 || address == 0xFF02)
return serial.Read(address);
if (address >= 0xFF04 && address <= 0xFF07)
return timer.Read(address);
if (address == 0xFF0F)
return interrupts.ReadFlag();
if (address >= 0xFF10 && address <= 0xFF3F)
return apu.Read(address);
if (address == 0xFF46)
return Dma!.Page;
if (address >= 0xFF40 && address <= 0xFF4B)
return ppu.ReadRegister(address);
return 0xFF;
}
private void WriteIo(ushort address, byte value)
{
if (address == 0xFF00)
joypad.Write(value);
else if (address == 0xFF01 || address == 0xFF02)
serial.Write(address, value);
else if (address >= 0xFF04 && address <= 0xFF07)
timer.Write(address, value);
else if (address == 0xFF0F)
interrupts.WriteFlag(value);
else if (address >= 0xFF10 && address <= 0xFF3F)
apu.Write(address, value);
else if (address == 0xFF46)
Dma!.Start(value);
else if (address >= 0xFF40 && address <= 0xFF4B)
ppu.WriteRegister(address, value);
}
public void SaveState(BinaryWriter writer)
{
writer.Write(wram);
writer.Write(hram);
}
public void LoadState(BinaryReader reader)
{
Array.Copy(reader.ReadBytes(wram.Length), wram, wram.Length);
Array.Copy(reader.ReadBytes(hram.Length), hram, hram.Length);
}
}
}
+64
View File
@@ -0,0 +1,64 @@
using System.IO;
using GameBoy.Video;
namespace GameBoy.Memory
{
public sealed class OamDma
{
private readonly Mmu mmu;
private readonly Ppu ppu;
private bool active;
private int index;
private int subDot;
public byte Page { get; private set; }
public OamDma(Mmu mmu, Ppu ppu)
{
this.mmu = mmu;
this.ppu = ppu;
}
public void Start(byte page)
{
Page = page;
active = true;
index = 0;
subDot = 0;
}
public void Step()
{
if (!active)
return;
subDot++;
if (subDot < 4)
return;
subDot = 0;
ushort source = (ushort)((Page << 8) | index);
ppu.WriteOamDma(index, mmu.Read(source));
index++;
if (index == 0xA0)
active = false;
}
public void SaveState(BinaryWriter writer)
{
writer.Write(active);
writer.Write(index);
writer.Write(subDot);
writer.Write(Page);
}
public void LoadState(BinaryReader reader)
{
active = reader.ReadBoolean();
index = reader.ReadInt32();
subDot = reader.ReadInt32();
Page = reader.ReadByte();
}
}
}