
ãªããããå¿ èŠãªã®ã§ããïŒ
Fceuxãå«ãããŸããŸãªã²ãŒã ã³ã³ãœãŒã«ã®ãšãã¥ã¬ãŒã¿ãŒã䜿çšãããšãLuaã§ã«ã¹ã¿ã ã¹ã¯ãªãããäœæããã³å®è¡ã§ããŸãã ããããLuaã¯æ·±å»ãªããã°ã©ã ãäœæããã®ã«æªãèšèªã§ãã ããããCã§æžãããé¢æ°ãåŒã³åºãããã®èšèªã§ãã ãšãã¥ã¬ãŒã¿ã®äœæè ã䜿çšããã®ã¯ãåã蟌ã¿ã®è»œããšäœ¿ããããã ãã§ãã æ£ç¢ºãªãšãã¥ã¬ãŒã·ã§ã³ã«ã¯å€ãã®ããã»ããµãªãœãŒã¹ãå¿ èŠã§ãããåæã®ãšãã¥ã¬ãŒã·ã§ã³é床ã¯äœæè ã®äž»ãªç®æšã®1ã€ã§ãããã¹ã¯ãªããã¢ã¯ã·ã§ã³ã®å¯èœæ§ãèŠããŠããã°ãããããããã§ã¯ãããŸããã§ããã
NESããšãã¥ã¬ãŒãããã«ã¯ãå¹³åçãªããã»ããµã®ãã¯ãŒã§ååã§ãããšãã¥ã¬ãŒã¿ã§PythonãJavaScriptãªã©ã®åŒ·åãªã¹ã¯ãªããèšèªã䜿çšããŠã¿ãŸãããïŒ
æ®å¿µãªããã人æ°ã®ããNESãšãã¥ã¬ãŒã¿ãŒã«ã¯ããããã®èšèªãä»ã®èšèªã䜿çšããæ©èœã¯ãããŸããã ãªãããã®çç±ã§Javaã§æžãçŽããããFceuxã«ãŒãã«ãããŒã¹ã«ãããããŸãç¥ãããŠããªãNintacoãããžã§ã¯ãã®ã¿ãèŠã€ããŸããã 次ã«ããšãã¥ã¬ãŒã¿ãŒãèªåã§å¶åŸ¡ããããã®ã¹ã¯ãªãããPythonã§äœæããæ©èœãè¿œå ããããšã«ããŸããã
ç§ã®çµæã¯ããšãã¥ã¬ãŒã¿ãå¶åŸ¡ããèœåã®æŠå¿µå®èšŒã§ãããé床ãä¿¡é Œæ§ãè£ ãããã§ã¯ãããŸããããæ©èœããŸãã ç§ã¯èªåã§ãããããŸããããã¹ã¯ãªããã䜿çšããŠãšãã¥ã¬ãŒã¿ãå¶åŸ¡ããæ¹æ³ã®è³ªåã¯ååã«äžè¬çã§ãã ãã ããœãŒã¹ã³ãŒããgithubã«é 眮ããŸãã ã
ä»çµã¿
ãšãã¥ã¬ãŒã¿ãŒåŽ
Fceuxãšãã¥ã¬ãŒã¿ãŒã«ã¯ ãã³ã³ãã€ã«æžã¿ã³ãŒããšããŠããã€ãã®Luaã©ã€ãã©ãªãŒãæ¢ã«å«ãŸããŠããŸã ã ãã®1ã€ãLuaSocketã§ãã ææžåã¯äžååã§ããã Xkeeper0 ã¹ã¯ãªããã®ã³ã¬ã¯ã·ã§ã³ã®äžããäœæ¥ã³ãŒãã®äŸãèŠã€ããããšã ã§ã ãŸãã ã 圌ã¯ãœã±ããã䜿çšããŠãMircãä»ããŠãšãã¥ã¬ãŒã¿ãŒãå¶åŸ¡ããŸããã å®éãtcpãœã±ãããéãã³ãŒãã¯æ¬¡ã®ãšããã§ãã
function connect(address, port, laddress, lport) local sock, err = socket.tcp() if not sock then return nil, err end if laddress then local res, err = sock:bind(laddress, lport, -1) if not res then return nil, err end end local res, err = sock:connect(address, port) if not res then return nil, err end return sock end sock2, err2 = connect("127.0.0.1", 81) sock2:settimeout(0) --it's our socket object print("Connected", sock2, err2)
ããã¯ã1ãã€ãåäœã§ããŒã¿ãéåä¿¡ããäœã¬ãã«ã®ãœã±ããã§ãã
Fceuxãšãã¥ã¬ãŒã¿ãŒã§ã¯ãLuaã¹ã¯ãªããã®ã¡ã€ã³ã«ãŒãã¯æ¬¡ã®ããã«ãªããŸãã
function main() while true do -- passiveUpdate() --, emu.frameadvance() -- end end
ãœã±ããããã®ããŒã¿ã®ãã§ãã¯ïŒ
function passiveUpdate() local message, err, part = sock2:receive("*all") if not message then message = part end if message and string.len(message)>0 then --print(message) local recCommand = json.decode(message) table.insert(commandsQueue, recCommand) coroutine.resume(parseCommandCoroutine) end end
ã³ãŒãã¯éåžžã«åçŽã§ããããŒã¿ã¯ãœã±ããããèªã¿åããã次ã®ã³ãã³ããæ€åºããããšã解æãããŠå®è¡ãããŸãã 解æãšå®è¡ã¯ã³ã«ãŒãã³ ïŒã³ã«ãŒãã³ïŒã䜿çšããŠç·šæãããŸã-ããã¯ãã³ãŒãå®è¡ãäžæåæ¢ããã³ç¶ç¶ããããã®åŒ·åãªLuaèšèªã®æŠå¿µã§ãã
ãŸããFceuxã®Luaã¹ã¯ãªããã«é¢ãããã1ã€ã®éèŠãªããšã¯ãã¹ã¯ãªããããäžæçã«ãšãã¥ã¬ãŒã·ã§ã³ãåæ¢ã§ããããšã§ãã Lua-codeã®ç¶ç¶çãªå®è¡ãæŽçãããœã±ããããåãåã£ãã³ãã³ãã§åå®è¡ããæ¹æ³ã¯ïŒ ããã¯äžå¯èœã§ããããšãã¥ã¬ãŒã·ã§ã³ãåæ¢ããŠãLuaã³ãŒããåŒã³åºãããšã¯ã§ããŸããããææžåãããæ©èœã¯äžååã§ãïŒ feosã«ææããŠãã ããïŒã
gui.register(passiveUpdate) --undocumented. this function will call even if emulator paused
ããã䜿çšãããšã passiveUpdateå ã§ãšãã¥ã¬ãŒããåæ¢ããã³ç¶è¡ã§ããŸããããã«ããããœã±ãããä»ããŠãšãã¥ã¬ãŒã¿ãŒã®ãã¬ãŒã¯ãã€ã³ãã®ã€ã³ã¹ããŒã«ã調æŽã§ããŸãã
ãµãŒããŒåŽã³ãã³ã
éåžžã«ã·ã³ãã«ãªJSONããŒã¹ã®RPCããã¹ããããã³ã«ã䜿çšããŠããŸãã ãµãŒããŒã¯é¢æ°åãšåŒæ°ãJSONæååã«ã·ãªã¢ã«åãããœã±ãããä»ããŠéä¿¡ããŸãã ããã«ããšãã¥ã¬ãŒã¿ãã³ãã³ããå®äºããè¡ã§å¿çãããŸã§ãã³ãŒãã®å®è¡ã¯åæ¢ããŸãã å¿çã«ã¯ããã£ãŒã«ãã FUNCTIONNAME_finished ããšé¢æ°ã®çµæãå«ãŸããŸãã
ãã®ã¢ã€ãã¢ã¯ã syncCallã¯ã©ã¹ã«å®è£ ãããŠããŸã ã
class syncCall: @classmethod def waitUntil(cls, messageName): """cycle for reading data from socket until needed message was read from it. All other messages will added in message queue""" while True: cmd = messages.parseMessages(asyncCall.waitAnswer(), [messageName]) #print(cmd) if cmd != None: if len(cmd)>1: return cmd[1] return @classmethod def call(cls, *params): """wrapper for sending [functionName, [param1, param2, ...]] to socket and wait until client return [functionName_finished, [result1,...]] answer""" sender.send(*params) funcName = params[0] return syncCall.waitUntil(funcName + "_finished")
ãã®ã¯ã©ã¹ã䜿çšãããšãFceuxãšãã¥ã¬ãŒã¿ãŒã®Luaã¡ãœãããPythonã¯ã©ã¹ã§ã©ããã§ããŸãã
class emu: @classmethod def poweron(cls): return syncCall.call("emu.poweron") @classmethod def pause(cls): return syncCall.call("emu.pause") @classmethod def unpause(cls): return syncCall.call("emu.unpause") @classmethod def message(cls, str): return syncCall.call("emu.message", str) @classmethod def softreset(cls): return syncCall.call("emu.softreset") @classmethod def speedmode(cls, str): return syncCall.call("emu.speedmode", str)
ãããŠãLuaãããšåãæ¹æ³ã§éèªçã«åŒã°ããŸãïŒ
# : emu.poweron()
ã³ãŒã«ããã¯ã¡ãœãã
Luaã§ã¯ãã³ãŒã«ããã¯ïŒç¹å®ã®æ¡ä»¶ãæºãããããšãã«åŒã³åºãããé¢æ°ïŒãç»é²ã§ããŸãã 次ã®ããªãã¯ã䜿çšããŠããã®åäœãPythonã®ãµãŒããŒã«ç§»æ€ã§ããŸãã ãŸããPythonã§èšè¿°ãããã³ãŒã«ããã¯é¢æ°ã®èå¥åãä¿åããLuaã³ãŒãã«æž¡ããŸãã
class callbacks: functions = {} callbackList = [ "emu.registerbefore_callback", "emu.registerafter_callback", "memory.registerexecute_callback", "memory.registerwrite_callback", ] @classmethod def registerfunction(cls, func): if func == None: return 0 hfunc = hash(func) callbacks.functions[hfunc] = func return hfunc @classmethod def error(cls, e): emu.message("Python error: " + str(e)) @classmethod def checkAllCallbacks(cls, cmd): #print("check:", cmd) for callbackName in callbacks.callbackList: if cmd[0] == callbackName: hfunc = cmd[1] #print("hfunc:", hfunc) func = callbacks.functions.get(hfunc) #print("func:", func) if func: try: func(*cmd[2:]) #skip function name and function hash and save others arguments except Exception as e: callbacks.error(e) pass #TODO: thread locking sender.send(callbackName + "_finished")
Luaã³ãŒãã¯ãã®èå¥åãä¿åããPythonã³ãŒãã«å¶åŸ¡ã転éããéåžžã®Luaã³ãŒã«ããã¯ãç»é²ããŸãã 次ã«ãLuaããã®ã³ãŒã«ããã¯ã³ãã³ããåãå ¥ããããªãã£ãããšã®ç¢ºèªã®ã¿ã«é¢ä¿ããPythonã³ãŒãã«å¥ã®ã¹ã¬ãããäœæãããŸãã
def callbacksThread(): cycle = 0 while True: cycle += 1 try: cmd = messages.parseMessages(asyncCall.waitAnswer(), callbacks.callbackList) if cmd: #print("Callback received:", cmd) callbacks.checkAllCallbacks(cmd) pass except socket.timeout: pass time.sleep(0.001)
æåŸã®ã¹ãããã¯ãPythonã³ãŒã«ããã¯ãå®è¡ãããåŸãã CALLBACKNAME_finished ã ã³ãã³ãã䜿çšããŠå¶åŸ¡ãLuaã«è¿ãããã³ãŒã«ããã¯ãçµäºããããšããšãã¥ã¬ãŒã¿ãŒã«éç¥ããããšã§ãã
äŸãå®è¡ããæ¹æ³
- ã·ã¹ãã ã§Python 3ããã³Jupyter Notebookãå®è¡ããŠããå¿
èŠããããŸãã 次ã®ã³ãã³ãã§Jupyterãå®è¡ããå¿
èŠããããŸã
jupyter notebook
- FceuxPythonServer.py.ipynbã©ããããããéããæåã®è¡ãå®è¡ããŸã
- Fceuxãšãã¥ã¬ãŒã¿ãŒãå®è¡ãããã®äžã®ROMãã¡ã€ã«ãéããŠïŒç§ã¯ã²ãŒã CastlevaniaïŒUïŒïŒPRG0ïŒ[ïŒ]ãç§ã®äŸã§ã¯Nesã䜿çšããŸãïŒã fceux_listener.luaãšããååã®Luaã¹ã¯ãªãããå®è¡ããå¿
èŠããããŸãã Jupyterã©ãããããã§å®è¡ãããŠãããµãŒããŒã«æ¥ç¶ããå¿
èŠããããŸãã
ãããã®ã¢ã¯ã·ã§ã³ã¯ãã³ãã³ãã©ã€ã³ã䜿çšããŠå®è¡ã§ããŸãã
fceux.exe -lua fceux_listener.lua "Castlevania (U) (PRG0) [!].nes"
- Jupyter Notebookã«æ»ããŸãã ãšãã¥ã¬ãŒã¿ãŒãžã®æ£åžžãªæ¥ç¶ã«é¢ããã¡ãã»ãŒãžã衚瀺ãããã¯ãã§ãã
ããã ãã§ãããã©ãŠã¶ã®Jupyterã©ãããããããFceuxãšãã¥ã¬ãŒã¿ã«çŽæ¥ã³ãã³ããéä¿¡ã§ããŸãã
ãµã³ãã«ã©ãããããã®ãã¹ãŠã®è¡ãé çªã«å®è¡ãããšãã¥ã¬ãŒã¿ã§å®è¡çµæã確èªã§ããŸãã
å®å šãªäŸïŒ
https://github.com/spiiin/fceux_luaserver/blob/master/FceuxPythonServer.py.ipynb
ã¡ã¢ãªã®èªã¿åããªã©ã®åçŽãªæ©èœãå«ãŸããŠããŸãã

ããè€éãªã³ãŒã«ããã¯ã®äŸïŒ

ãŸãã ã¹ãŒããŒããªãªãã©ã¶ãŒãºããæµã移åãããç¹å®ã®ã²ãŒã ã®ã¹ã¯ãªããã ããŠã¹ã§ïŒ

ã©ãããããå®è¡ãããªïŒ
å¶éãšã¢ããªã±ãŒã·ã§ã³
ãã®ã¹ã¯ãªããã«ã¯ãã°ãã«å¯Ÿããä¿è·ããªããå®è¡é床ãæé©åãããŠããŸãã-ããã¹ããããã³ã«ã®ä»£ããã«ãã€ããªRPCãããã³ã«ã䜿çšããã¡ãã»ãŒãžãã°ã«ãŒãåããããšããå§ãããŸãããå®è£ ã«ã¯ã³ã³ãã€ã«ã¯å¿ èŠãããŸããã ãã®ã¹ã¯ãªããã¯ãã©ãããããã§å®è¡ã³ã³ããã¹ããLuaããPythonã«åãæ¿ãã1ç§ããã500ã1000åæ»ãããšãã§ããŸãã ãããªããã»ããµã®ãã¯ã»ã«ããšãŸãã¯ã©ã€ã³ããšã®ãããã°ã®ç¹å®ã®å Žåãé€ããããã¯ã»ãšãã©ãã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ã«ååã§ãããFceuxã¯ãŸã Luaããã®ãã®ãããªæäœãèš±å¯ããªããããåé¡ã§ã¯ãããŸããã
èããããã¢ããªã±ãŒã·ã§ã³ã®ã¢ã€ãã¢ïŒ
- ä»ã®ãšãã¥ã¬ãŒã¿ãŒããã³èšèªã«å¯Ÿãããã®ãããªå¶åŸ¡ã®å®è£ äŸãšããŠ
- ã²ãŒã ãªãµãŒã
- TASããã»ãŒãžãæŽçããããã®ããŒããŸãã¯æ©èœã®è¿œå
- ã²ãŒã ã«ããŒã¿ãšã³ãŒããæ¿å ¥ãŸãã¯æœåºãã
- ãšãã¥ã¬ãŒã¿ãŒã®æ©èœåŒ·å-ãããã¬ãŒã®äœæããã¥ãŒããªã¢ã«ã®èšé²ãšè¡šç€ºçšã®ã¹ã¯ãªãããã¹ã¯ãªããã©ã€ãã©ãªãŒãã²ãŒã ãšãã£ã¿ãŒ
- ãããã¯ãŒã¯ã²ãŒã ãã¢ãã€ã«ããã€ã¹ã䜿çšããã²ãŒã ã³ã³ãããŒã«ããªã¢ãŒããµãŒãã¹ããžã§ã€ããããŸãã¯ãã®ä»ã®å¶åŸ¡ããã€ã¹ãã¯ã©ãŠããµãŒãã¹ã§ã®ä¿åãšããã
- ã¯ãã¹ãšãã¥ã¬ãŒã¿æ©èœ
- ããŒã¿åæããã³ã²ãŒã å¶åŸ¡ïŒãããã®äœæïŒã«PythonãŸãã¯ä»ã®èšèªã©ã€ãã©ãªã䜿çšãã
æè¡ã¹ã¿ãã¯
ç§ã䜿çšããïŒ
Fceux-www.fceux.com/web/home.html
ããã¯å€å žçãªNESãšãã¥ã¬ãŒã¿ã§ãããã»ãšãã©ã®äººã䜿çšããŠããŸãã ããã¯é·ãéæŽæ°ãããŠããããæ©èœçã«ãæé«ã§ã¯ãããŸããããå€ãã®ãã ããã«ãŒã«ãšã£ãŠããã©ã«ãã®ãšãã¥ã¬ãŒã¿ã®ãŸãŸã§ãã ãŸããLuaãœã±ãããµããŒããçµ±åãããŠãããããèªåã§æ¥ç¶ããå¿ èŠããªãããããããéžæããŸããã
Json.lua-github.com/spiiin/json.lua
ããã¯çŽç²ãªLuaã®JSONå®è£ ã§ãã ã³ãŒããã³ã³ãã€ã«ããå¿ èŠã®ãªãäŸãäœæãããã£ãããããããéžæããŸããã ããããFceuxã«çµã¿èŸŒãŸããã©ã€ãã©ãªã®äžéšãã©ã€ãã©ãªé¢æ°ãæååã«ãªãŒããŒããŒãããã·ãªã¢ã«åãäžæãããããã©ã€ãã©ãªããã©ãŒã¯ããå¿ èŠããããŸããïŒå ã®ã©ã€ãã©ãªã®äœæè ãžã®ããŒã«èŠæ±ãæåŠããŸããïŒã
Python 3-www.python.org
Fceux LuaãµãŒããŒã¯tcpãœã±ãããéããããããåä¿¡ããã³ãã³ãããªãã¹ã³ããŸãã ãšãã¥ã¬ãŒã¿ã«ã³ãã³ããéä¿¡ãããµãŒããŒã¯ãä»»æã®èšèªã§å®è£ ã§ããŸãã Pythonãéžãã ã®ã¯ããããããªãŒãå«ãããšããå²åŠã§ããã»ãšãã©ã®ã¢ãžã¥ãŒã«ã¯æšæºã©ã€ãã©ãªã«å«ãŸããŠããŸãïŒãœã±ãããšJSONãåæ§ã«æ©èœããŸãïŒã Pythonã¯ããã¥ãŒã©ã«ãããã¯ãŒã¯ãæäœããããã®ã©ã€ãã©ãªãç¥ã£ãŠããã®ã§ããããã䜿çšããŠNESã²ãŒã ã§ããããäœæããããšæããŸãã
JupyterããŒããã㯠-jupyter.org
Jupyter Notebookã¯ãPythonã³ãŒãã察話çã«å®è¡ããããã®éåžžã«ã¯ãŒã«ãªç°å¢ã§ãã ããã䜿çšãããšããã©ãŠã¶ãŒå ã®ã¹ãã¬ããã·ãŒããšãã£ã¿ãŒã§ã³ãã³ããèšè¿°ããŠå®è¡ã§ããŸãã ãŸããæ瀺å¯èœãªäŸãäœæããã®ã«ãé©ããŠããŸãã
Dexpot-www.dexpot.de
ãã®ä»®æ³ãã¹ã¯ããããããŒãžã£ãŒã䜿çšããŠããšãã¥ã¬ãŒã¿ãŠã£ã³ããŠãä»ã®ãŠã£ã³ããŠã®äžã«ãããã³ã°ããŸããã ããã¯ããšãã¥ã¬ãŒã¿ãŠã£ã³ããŠã®å€æŽãå³åº§ã«è¿œè·¡ããããã«ããµãŒããŒãå šç»é¢ã§å±éããå Žåã«éåžžã«äŸ¿å©ã§ãã 確ç«ãããWindowsããŒã«ã§ã¯ããããã³ã°ãŠã£ã³ããŠãä»ã®ãŠã£ã³ããŠã®äžã«æŽçããããšã¯ã§ããŸããã
åç §è³æ
å®éã«ã¯ããããžã§ã¯ããªããžããªã§ã ã
Nintaco-ãªã¢ãŒã管çãåããJava NESãšãã¥ã¬ãŒã¿
Xkeeper0 emu-luaã³ã¬ã¯ã·ã§ã³ -ããŸããŸãªLuaã¹ã¯ãªããã®ã³ã¬ã¯ã·ã§ã³
Mesenã¯ã匷åãªLuaã¹ã¯ãªããæ©èœãåããCïŒã®ææ°ã®NESãšãã¥ã¬ãŒã¿ãŒã§ãã ãããŸã§ã®ãšããããœã±ããã®ãµããŒããšãªã¢ãŒãã³ã³ãããŒã«ã¯ãããŸããã
CadEditorã¯ãNESããã³ãã®ä»ã®ãã©ãããã©ãŒã çšã®ãŠãããŒãµã«ã¬ãã«ãšãã£ã¿ãŒã®ãããžã§ã¯ãã§ãããã²ãŒã ãç 究ããããã®åŒ·åãªããŒã«ã§ãã æçš¿ã§èª¬æããã¹ã¯ãªãããšãµãŒããŒã䜿çšããŠãã²ãŒã ãæ¢çŽ¢ãããšãã£ã¿ãŒã«è¿œå ããŸãã
ãã£ãŒãããã¯ããã¹ããããã³ã¹ã¯ãªããã®äœ¿çšã®è©Šã¿ã«æè¬ããŸãã