Как получить ППУ память от FCEUX в Lua?

Я не уверен, что это подходящее сообщество для этого, но решил, что я попробую.

FCEUX - это удивительный эмулятор для NES, который богат функциями отладки. Он также предлагает пользователям возможность запускать сценарии Lua, которые имеют доступ к различным функциям эмулятора. Тем не менее, я не могу понять, как получить доступ к памяти PPU NES. Он предлагает прямой доступ к памяти ЦП и данным ПЗУ, но, похоже, не имеет прямого доступа к памяти ППУ. Поскольку NES использует ввод-вывод с отображением в памяти, теоретически возможно получить данные из специальных адресов памяти ЦП, но это кажется громоздким, а также может помешать эмуляции.

Кто-нибудь знает способ программного извлечения памяти PPU через Lua API FCEUX? Если нет, кто-нибудь знает об эмуляторе, который имеет API для программного извлечения памяти PPU?

3 ответа

Решение

После осознания того, что "Ой, подождите, я программист, а FCEUX с открытым исходным кодом! Так что, может быть, мне стоит потратить время на просмотр их источника / хранилища, чтобы посмотреть, смогу ли я ответить на вопрос сам!", Я нашел ответ, который я искал:

Фиксация [r3327] 22 декабря 2016 года: добавлена ​​библиотека ppu lua, пока есть только readbyte и readbyterange

Таким образом, может показаться, что на момент написания этой статьи доступ к памяти PPU через Lua невозможен в текущей версии выпуска ( 2.2.3, выпущенной 28 июля 2016 года), но, вероятно, будет доступен в будущем выпуске.

Кроме того, после проверки Nestopia и Jnes (двух других, казалось бы, самых популярных эмуляторов NES), кажется, что эти эмуляторы не предлагают такой функциональности. Относительно того, существуют ли какие-либо другие эмуляторы, которые предлагают эту функцию, все еще остается открытым вопрос, поскольку в настоящее время существует множество других эмуляторов для проверки.

Вот что я использую:

function memory.readbyteppu(a)
    memory.writebyte(0x2001,0x00) -- Turn off rendering
    memory.readbyte(0x2002) -- PPUSTATUS (reset address latch)
    memory.writebyte(0x2006,math.floor(a/0x100)) -- PPUADDR high byte
    memory.writebyte(0x2006,a % 0x100) -- PPUADDR low byte
    if a < 0x3f00 then 
        dummy=memory.readbyte(0x2007) -- PPUDATA (discard contents of internal buffer if not reading palette area)
    end
    ret=memory.readbyte(0x2007) -- PPUDATA
    memory.writebyte(0x2001,0x1e) -- Turn on rendering
    return ret
end

function memory.readbytesppu(a,l)
    memory.writebyte(0x2001,0x00) -- Turn off rendering
    local ret
    local i
    ret=""
    for i=0,l-1 do
        memory.readbyte(0x2002) -- PPUSTATUS (reset address latch)
        memory.writebyte(0x2006,math.floor((a+i)/0x100)) -- PPUADDR high byte
        memory.writebyte(0x2006,(a+i) % 0x100) -- PPUADDR low byte
        if (a+i) < 0x3f00 then 
            dummy=memory.readbyte(0x2007) -- PPUDATA (discard contents of internal buffer if not reading palette area)
        end
        ret=ret..string.char(memory.readbyte(0x2007)) -- PPUDATA
    end
    memory.writebyte(0x2001,0x1e) -- Turn on rendering
    return ret
end


function memory.writebyteppu(a,v)
    memory.writebyte(0x2001,0x00) -- Turn off rendering
    memory.readbyte(0x2002) -- PPUSTATUS (reset address latch)
    memory.writebyte(0x2006,math.floor(a/0x100)) -- PPUADDR high byte
    memory.writebyte(0x2006,a % 0x100) -- PPUADDR low byte
    memory.writebyte(0x2007,v) -- PPUDATA
    memory.writebyte(0x2001,0x1e) -- Turn on rendering
end

function memory.writebytesppu(a,str)
    memory.writebyte(0x2001,0x00) -- Turn off rendering

    local i
    for i = 0, #str-1 do
        memory.readbyte(0x2002) -- PPUSTATUS (reset address latch)
        memory.writebyte(0x2006,math.floor((a+i)/0x100)) -- PPUADDR high byte
        memory.writebyte(0x2006,(a+i) % 0x100) -- PPUADDR low byte
        memory.writebyte(0x2007,string.byte(str,i+1)) -- PPUDATA
    end

    memory.writebyte(0x2001,0x1e) -- Turn on rendering
end

В 2.2.3 он не работает на старом ядре PPU, но на 2.2.2 работает. Работает с новым ядром ППУ на обеих версиях.

Начиная с FCEUX 2.3.0, вы можете использовать ppu.readbyte(int address) а также ppu.readbyterange(int address, int length). Впрочем, для записи байтов по-прежнему ничего.

Чтобы добавить к ответу SpiderDave, вам может повезти больше, если вы назовете его writebyteppu взломать registerexec обратный вызов по адресу сразу после того, как игра закончила писать графику для кадра.

      -- For Rockman 2. Directly after all graphics update routines finish.
memory.registerexec(0xD031, function()
    local paletteBase = 0x3F00
    -- Make all BG colors pink
    for i = 0x00, 0x0F do
        memory.writebyteppu(paletteBase + i, 0x35)
    end
end)
Другие вопросы по тегам