233. Vram 内存设备

创建一个简单的 VRAM 内存设备。

233 / 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_233_vram_memory_device.py

要创建的文件

emulator/memory/vram.py

要实现的类

VRAM

为什么需要这一步

PPU 拥有自己独立的地址空间,与 CPU 地址空间是分开的。CPU RAM 通常不是背景/精灵图形状态所在的地方。PPU 需要面向视频的内存,它可以通过 PPUADDR/PPUDATA 来访问,之后也会通过渲染逻辑访问。

重要的心智模型

CPU address space: $0000-$FFFF
    handled by CpuBus

PPU address space: $0000-$3FFF
    will be handled by PpuBus

在这个早期阶段,VRAM 只是 PPU 侧地址空间的一个简单可写存储。之后,PpuBus 会决定 PPU 地址如何映射到 CHR ROM、名称表 VRAM、调色板 RAM 以及镜像区域。

为什么 VRAM 与 PPU 寄存器是分开的

像 $2006 和 $2007 这样的 PPU 寄存器是 CPU 可见的控制/数据端口,它们本身并不是内存。

之后的示例流程

CPU writes $20 to $2006
CPU writes $00 to $2006
CPU writes $AA to $2007

含义

PPU internal address becomes $2000
PPUDATA writes $AA into PPU-side memory at $2000

CPU 访问的是寄存器 $2007,但实际的视频内存地址是 PPU 内部的地址,而不是 CPU 地址 $2007。

当前范围

  • VRAM 是一个 MemoryDevice
  • VRAM 存储 0x4000 个字节
  • VRAM.read(addr) 返回 addr 处存储的字节
  • VRAM.write(addr, value) 只存储 value 的低字节

重要的职责划分

VRAM 不应该自行应用 addr & 0x3FFF。地址归一化/路由应属于 PpuBus,因为 PpuBus 拥有 PPU 地址映射的所有权。VRAM 只负责存储。

建议实现的伪代码

from dataclasses import dataclass, field
from emulator.memory.memory_device import MemoryDevice

VRAM_SIZE = 0x4000


@dataclass
class VRAM(MemoryDevice):
    _data: bytearray = field(
        default_factory=lambda: bytearray(VRAM_SIZE),
        init=False,
    )

    def write(self, addr: int, value: int) -> None:
        self._data[addr] = value & 0xFF

    def read(self, addr: int) -> int:
        return self._data[addr]

不在本步范围内

  • PpuBus 路由
  • CHR ROM 映射
  • 名称表镜像
  • 调色板 RAM
  • 渲染

运行本课

uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_233_vram_memory_device.py -v