245. Ppuscroll 第二次写入开关
实现 PPUSCROLL ($2005) 的两次写入滚动行为。
第 245 / 356 · tests/chapter_03_ppu_memory_and_graphics_data/test_245_ppuscroll_second_write_toggle.py
实现 PPUSCROLL ($2005) 的两次写入滚动行为。!! 请慢慢实现这一步
参考资料
https://www.nesdev.org/wiki/PPU_registers#PPUSCROLL
https://www.nesdev.org/wiki/PPU_scrolling#$2005_(PPUSCROLL)_first_write_(w_is_0)
https://www.nesdev.org/wiki/PPU_scrolling#$2005_(PPUSCROLL)_second_write_(w_is_1)要更新的文件
emulator/ppu/ppu.py要更新的方法
PPU.write_register(addr, value)为什么需要这一步
PPUSCROLL 是位于 $2005 的 CPU 可见寄存器。与 PPUADDR ($2006) 一样,它使用共享的第一次写入/第二次写入开关:
second_write_toggle == False -> first write
second_write_toggle == True -> second write第一次写入配置水平滚动
value bits 7-3 -> coarse X, stored in temp_vram_addr bits 0-4
value bits 2-0 -> fine X, stored in fine_x第二次写入配置垂直滚动
value bits 7-3 -> coarse Y, stored in temp_vram_addr bits 5-9
value bits 2-0 -> fine Y, stored in temp_vram_addr bits 12-14重要的内部寄存器模型
vram_addr -> v, current VRAM address
temp_vram_addr -> t, temporary VRAM address
fine_x -> x, fine horizontal scroll
second_write_toggle -> w, first/second write toggle建议的实现伪代码,与显式移动位的风格保持一致:
case 0x2005:
# Keep for old compatibility.
self.scroll = value
if not self.second_write_toggle:
# Reference:
# https://www.nesdev.org/wiki/PPU_scrolling#$2005_(PPUSCROLL)_first_write_(w_is_0)
#
# PPU Scroll first write.
# Incoming value bits are named ABCDEFGH.
#
# fine_x register:
# x: FGH <- value: .....FGH
#
# In other words, save the last 3 bits as fine horizontal scroll.
self.fine_x = value & 0b0000_0111
# temp_vram_addr register:
# t: ....... ...ABCDE <- value: ABCDE...
#
# In other words, save the upper 5 bits as coarse X in bits 0-4.
# The mask keeps every temp_vram_addr bit except coarse X.
self.temp_vram_addr = (
(self.temp_vram_addr & 0b1111_1111_1110_0000)
| (value >> 3)
)
# w <- 1
# Next $2005/$2006 write will be treated as the second write.
self.second_write_toggle = True
else:
# Reference:
# https://www.nesdev.org/wiki/PPU_scrolling#$2005_(PPUSCROLL)_second_write_(w_is_1)
#
# PPU Scroll second write.
# Incoming value bits are named ABCDEFGH.
#
# temp_vram_addr register:
# t: FGH..AB CDE..... <- value: ABCDEFGH
#
# Step 1: keep only untouched bits:
# - bit 15
# - nametable bits 10-11
# - coarse X bits 0-4
#
# This clears the vertical scroll destination bits:
# - coarse Y bits 5-9
# - fine Y bits 12-14
self.temp_vram_addr = (
self.temp_vram_addr & 0b1000_1100_0001_1111
)
# Step 2: put the last 3 bits into fine Y.
# value: .....FGH
# FGH << 12 -> bits 12-14
self.temp_vram_addr = (
self.temp_vram_addr | ((value & 0b0000_0111) << 12)
)
# Step 3: put the upper 5 bits into coarse Y.
# value: ABCDE...
# (value & 11111000) << 2 -> bits 5-9
self.temp_vram_addr = (
self.temp_vram_addr | ((value & 0b1111_1000) << 2)
)
# w <- 0
# The two-write PPUSCROLL sequence is complete.
self.second_write_toggle = False超出范围
- 在渲染期间把滚动位从 temp_vram_addr 复制到 vram_addr
- 扫描线/周期时序
- 实际的背景渲染
运行本课
uv run pytest tests/chapter_03_ppu_memory_and_graphics_data/test_245_ppuscroll_second_write_toggle.py -v