{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#this is example of interactive python server for manupulate fceux emulator\n",
    "#first, load module for manipulation and wait connection for emulator\n",
    "\n",
    "from python_server import *\n",
    "waitForConnection()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Now run emulator, open ROM Castlevania (U) (PRG0) [!].nes and run lua script: fceux_listener.lua\n",
    "#If emulator connects with our server without error, then all is ok. \n",
    "\n",
    "#Now select emulator window always visible and active (I used Dexpot https://www.dexpot.de/index.php?id=features for it)\n",
    "#We can start to send commands to emulator from this notebook"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#We can use all standart fceux lua functions. for example:\n",
    "emu.poweron()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.framecount()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.setrenderplanes(True, True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Callbacks also allowed\n",
    "\n",
    "def showFrame():\n",
    "    emu.message(\"Hello from python. Frame:\" + str(emu.framecount()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.registerafter(showFrame)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "count = 0\n",
    "def onExecute(addr, size):\n",
    "    global count\n",
    "    count += 1\n",
    "    emu.message(\"Exec %s count: %d\"%(hex(addr),count))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Castlevania 1 example, address of IRQ on title screen\n",
    "memory.registerexecute(0xC0BB, 1, onExecute)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Callbacks not very fast now (about 600 executes per frame)\n",
    "memory.registerexecute(0xC037, 1, onExecute)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#disable callbacks to stop freezing emulator\n",
    "memory.registerexecute(0xC0BB, 1, None)\n",
    "memory.registerexecute(0xC037, 1, None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#we can pause emulator to do some work with it\n",
    "emu.pause()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Emulator paused: \",emu.paused(), \" Current frame:\", emu.framecount())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#continue running\n",
    "emu.unpause()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#calling emulator debugger\n",
    "debugger.hitbreakpoint()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.framecount()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "debugger.getinstructionscount()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.emulating()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.poweron()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.print(\"Printing to emulator log\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.lagged()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Read byte from RAM\n",
    "hex(rom.readbyte(0x82))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Read sevaral bytes\n",
    "a = memory.readbyterange(0x100, 256)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Read screen pixel\n",
    "emu.getscreenpixel(150,150,True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#conditional breakpoint - on write to memory address\n",
    "\n",
    "memory.registerwrite(0x3F, 1, lambda addr,size: debugger.hitbreakpoint())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "memory.registerwrite(0x3F, 1, lambda addr,size: None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#print info on screen, when game write to selected address\n",
    "memory.registerwrite(0x3F, 1, onExecute)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Read info about pressed buttons\n",
    "#numeration of joypads starts from 1!\n",
    "joypad.readimmediate(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Send joypad button keys\n",
    "#All buttons must be filled #press START!\n",
    "a = { \"A\" : True, \"B\":True, \"down\":False, \"right\":False, \"select\":False, \"start\":True, \"up\":False }\n",
    "joypad.write(1, a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Read mouse position\n",
    "zapper.read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Read keyboard info\n",
    "input.read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Check if button pressed every frame\n",
    "def onEndFrame():\n",
    "    keys = input.read()\n",
    "    if keys.get(\"N\"):\n",
    "        emu.message(\"N pressed\")\n",
    "\n",
    "emu.registerafter(onEndFrame)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Draw rect on screen\n",
    "def draw():\n",
    "    gui.line(0,0,100,100, \"red\", False)\n",
    "    gui.box(50,50,100,100, \"FFFFFFFF\", \"white\")\n",
    "    gui.pixel(75,75,\"blue\")\n",
    "    gui.text(80,80, \"hello\", \"red\", \"white\")\n",
    "    \n",
    "emu.registerafter(draw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Check error in python #test callback with error\n",
    "def error():\n",
    "    1/0\n",
    "emu.registerafter(error)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.registerafter(None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Now change rom\n",
    "SUPER_MARIO = \"Super Mario Bros. (JU) [!].nes\"\n",
    "emu.loadrom(SUPER_MARIO)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def smbGetEnemyCoord(n):\n",
    "    x2 = memory.readbyte(0x006D + n)\n",
    "    x1 = memory.readbyte(0x0086 + n)\n",
    "    y2 = memory.readbyte(0x00B5 + n)\n",
    "    y1 = memory.readbyte(0x00CE + n)\n",
    "    #ax = memory.readbytesigned(0x0057 + n)\n",
    "    #ay = memory.readbytesigned(0x009F + n)\n",
    "    ax = memory.readbytesigned(0x0057 + n)\n",
    "    ay = memory.readbytesigned(0x009F + n)\n",
    "    x = x2*256+x1\n",
    "    y = y2*256+y1\n",
    "    return x,y,ax,ay"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Show info about first enemy slot on screen\n",
    "def printEnemyInfo():\n",
    "    info = smbGetEnemyCoord(1)\n",
    "    emu.message(\"x:%d y:%d ax:%d ay:%d\"%info)\n",
    "    \n",
    "emu.registerafter(printEnemyInfo)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def smbMoveEnemy(n, x, y, ax, ay):\n",
    "    x1 = x % 0x100\n",
    "    x2 = x // 0x100\n",
    "    y1 = y % 0x100\n",
    "    y2 = y // 0x100\n",
    "    ax = max(-128, min(ax, 0x7F))\n",
    "    ay = max(-128, min(ay, 0x7F))\n",
    "\n",
    "    memory.writebyte(0x006D + n, x2)\n",
    "    memory.writebyte(0x0086 + n, x1)\n",
    "    memory.writebyte(0x00B5 + n, y2)\n",
    "    memory.writebyte(0x00CE + n, y1)\n",
    "    memory.writebyte(0x0057 + n, ax)\n",
    "    memory.writebyte(0x009F + n, ay)\n",
    "\n",
    "    direction = ax > 0 and 1 or 2\n",
    "    memory.writebyte(0x0045 + n, direction);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#And now, try to change enemy's position and speed\n",
    "smbMoveEnemy(1, 650, 440, 32, 0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "smbMoveEnemy(1, 650, 430, 16, -8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emu.poweron()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Show position off scroll\n",
    "def smbGetScreenPos():\n",
    "    return memory.readbyte(0x71a) * 256 + memory.readbyte(0x71c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "smbGetScreenPos()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def showScreenPos():\n",
    "    emu.message(\"Screen pos: %d\"%smbGetScreenPos())\n",
    "    \n",
    "emu.registerafter(showScreenPos)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#We can just move enemy with mouse clicks\n",
    "def onMouseClick():\n",
    "    inp = input.read()\n",
    "    mouseX, mouseY = inp[\"xmouse\"], inp[\"ymouse\"]\n",
    "    click = inp[\"click\"]==1\n",
    "    if click:\n",
    "        x = smbGetScreenPos() + mouseX\n",
    "        y = mouseY + 256\n",
    "        smbMoveEnemy(1, x, y, 0, 0)\n",
    "    \n",
    "emu.registerafter(onMouseClick)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#that's all!\n",
    "#thank you for watching."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
