如何在Lua中从FCEUX获取PPU内存?

我不确定这是否是正确的社区,但我想试试。

FCEUX是NES的惊人模拟器,具有丰富的调试工具。它还提供了让用户运行具有访问各种模拟器函数能力的Lua脚本的能力。但是,我似乎无法弄清楚如何访问NES的PPU内存。它提供对CPU内存和ROM数据的直接访问,但似乎没有对PPU内存的直接访问权。由于NES使用内存映射I/O,理论上可以从特殊的CPU内存地址获取数据,但这似乎很麻烦,也可能会干扰模拟。

有没有人知道通过FCEUX的Lua API以编程方式提取PPU内存的方法?如果没有,有没有人知道具有以编程方式提取PPU内存的API的模拟器呢?

点赞
用户4376460
用户4376460

在我意识到“哦,等一下,我是个程序员,而且 FCEUX 是开源的!所以也许我应该花点时间看看他们的源代码/存储库,看看我能不能自己回答这个问题!”之后,我找到了我正在寻找的答案:

提交 [r3327]于2016年12月22日:添加了 PPU Lua 库,到目前为止仅具有 readbyte 和 readbyterange

因此,就目前而言,在当前版本中无法通过 Lua 访问 PPU 内存( 2.2.3 于2016年7月28日发布),但在将来的版本中可能会提供此功能。

此外,在检查了 NestopiaJnes(另外两个最受欢迎的 NES 模拟器),似乎这些模拟器都没有提供此功能。至于是否有其他模拟器提供此功能,仍然是一个开放的问题,因为目前存在许多其他模拟器需要检查。

2017-01-31 12:17:18
用户7887324
用户7887324

以下是我的使用方法:

function memory.readbyteppu(a)
    memory.writebyte(0x2001,0x00) -- 关闭渲染
    memory.readbyte(0x2002) -- PPU 状态(重置地址锁存器)
    memory.writebyte(0x2006,math.floor(a/0x100)) -- PPUADDR 的高字节
    memory.writebyte(0x2006,a % 0x100) -- PPUADDR 的低字节
    if a < 0x3f00 then
        dummy=memory.readbyte(0x2007) -- PPUDATA(如果不是读取调色板区域,丢弃内部缓冲区的内容)
    end
    ret=memory.readbyte(0x2007) -- PPUDATA
    memory.writebyte(0x2001,0x1e) -- 开启渲染
    return ret
end

function memory.readbytesppu(a,l)
    memory.writebyte(0x2001,0x00) -- 关闭渲染
    local ret
    local i
    ret=""
    for i=0,l-1 do
        memory.readbyte(0x2002) -- PPU 状态(重置地址锁存器)
        memory.writebyte(0x2006,math.floor((a+i)/0x100)) -- PPUADDR 的高字节
        memory.writebyte(0x2006,(a+i) % 0x100) -- PPUADDR 的低字节
        if (a+i) < 0x3f00 then
            dummy=memory.readbyte(0x2007) -- PPUDATA(如果不是读取调色板区域,丢弃内部缓冲区的内容)
        end
        ret=ret..string.char(memory.readbyte(0x2007)) -- PPUDATA
    end
    memory.writebyte(0x2001,0x1e) -- 开启渲染
    return ret
end

function memory.writebyteppu(a,v)
    memory.writebyte(0x2001,0x00) -- 关闭渲染
    memory.readbyte(0x2002) -- PPU 状态(重置地址锁存器)
    memory.writebyte(0x2006,math.floor(a/0x100)) -- PPUADDR 的高字节
    memory.writebyte(0x2006,a % 0x100) -- PPUADDR 的低字节
    memory.writebyte(0x2007,v) -- PPUDATA
    memory.writebyte(0x2001,0x1e) -- 开启渲染
end

function memory.writebytesppu(a,str)
    memory.writebyte(0x2001,0x00) -- 关闭渲染

    local i
    for i = 0, #str-1 do
        memory.readbyte(0x2002) -- PPU 状态(重置地址锁存器)
        memory.writebyte(0x2006,math.floor((a+i)/0x100)) -- PPUADDR 的高字节
        memory.writebyte(0x2006,(a+i) % 0x100) -- PPUADDR 的低字节
        memory.writebyte(0x2007,string.byte(str,i+1)) -- PPUDATA
    end

    memory.writebyte(0x2001,0x1e) -- 开启渲染
end

在 2.2.3 版本上似乎无法在旧的 PPU 核心上工作,但在 2.2.2 版本上可以。在两个版本上都适用于新的 PPU 核心。

2017-04-19 03:21:34
用户9769594
用户9769594

截至FCEUX 2.3.0,你可以使用ppu.readbyte(int address)ppu.readbyterange(int address, int length)。不过仍然没有写入字节的函数。

补充一下SpiderDave的答案,如果你在游戏完成当前帧的图形绘制之后,在一个registerexec回调函数中调用他的writebyteppu破解程序,可能会更加成功。

--对于洛克人2,直接在所有图形更新例程完成后。
memory.registerexec(0xD031, function()
    local paletteBase = 0x3F00
    --使所有的背景颜色为粉色
    for i = 0x00, 0x0F do
        memory.writebyteppu(paletteBase + i, 0x35)
    end
end)
2021-07-04 07:43:19