Skip to content

Commit

Permalink
fixed qt signal handling
Browse files Browse the repository at this point in the history
  • Loading branch information
hugsy committed Jul 26, 2019
1 parent 1aa1430 commit 8742f0e
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 77 deletions.
55 changes: 42 additions & 13 deletions cemu/emulator.py
Expand Up @@ -22,7 +22,8 @@ class Emulator:

def __init__(self, parent, *args, **kwargs):
self.parent = parent
self.arch = self.parent.arch
self.root = self.parent
self.arch = self.root.arch
self.use_step_mode = False
self.widget = None
self.reset()
Expand All @@ -32,23 +33,25 @@ def __init__(self, parent, *args, **kwargs):
def reset(self):
self.vm = None
self.code = None
self.is_running = False
self.__vm_state = 0
self.stop_now = False
self.num_insns = -1
self.areas = {}
self.registers = {}
self.create_new_vm()
return


def __str__(self):
return "Emulator instance {}running".format("" if self.is_running else "not ")


def __xlog(self, wid, text, category):
if self.widget is None:
print("{} - {}".format(category, text))
return

if wid==Emulator.EMU:
if wid==Emulator.EMU:
widget = self.widget.emuWidget
msg = "{:1s} - {}".format(category, text)
elif wid==Emulator.LOG:
Expand Down Expand Up @@ -112,7 +115,7 @@ def unicorn_permissions(self, perms):
return p


def create_new_vm(self):
def create_new_vm(self) -> None:
arch, mode, endian = get_arch_mode("unicorn", self.parent.arch)
self.vm = unicorn.Uc(arch, mode | endian)
self.vm.hook_add(unicorn.UC_HOOK_BLOCK, self.hook_block)
Expand Down Expand Up @@ -248,38 +251,64 @@ def hook_mem_access(self, emu, access, address, size, value, user_data):
return


def run(self):
def run(self) -> None:
"""
Runs the emulation
"""
self.pprint("Starting emulation context")

try:
self.set_vm_state(1)

self.vm.emu_start(self.start_addr, self.end_addr)
if self.pc()==self.end_addr:
self.pprint("Ending emulation context")
# self.widget.commandWidget.runButton.setDisabled(True)
# self.widget.commandWidget.stepButton.setDisabled(True)

except unicorn.unicorn.UcError as e:
self.log("An error occured: {}".format(str(e)), "Error")
self.pprint("pc={:#x} , sp={:#x}: {:s}".format(self.pc(), self.sp(), str(e)), "Exception")
self.vm.emu_stop()
return
self.stop()

finally:
self.set_vm_state(0)

if self.pc()==self.end_addr:
self.pprint("Ending emulation context")
self.widget.commandWidget.runButton.setDisabled(True)
self.widget.commandWidget.stepButton.setDisabled(True)
return


def stop(self):
self.set_vm_state(0)

for area in self.areas.keys():
addr, size = self.areas[area][0:2]
self.vm.mem_unmap(addr, size)

del self.vm
self.vm = None
self.is_running = False
return


def lookup_map(self, mapname):
for area in self.areas.keys():
if area == mapname:
return self.areas[area][0]
return None
raise KeyError("Section '{}' not found".format(mapname))


def set_vm_state(self, state: int) -> None:
if state == 0: # off
self.root.signals["refreshRegisterGrid"].emit()
self.root.signals["refreshMemoryEditor"].emit()
self.root.signals["setCommandButtonsForStop"].emit()
return

if state == 1: # running
self.root.signals["setCommandButtonsForRunning"].emit()

return


@property
def is_running(self):
return self.__vm_state == 1
54 changes: 40 additions & 14 deletions cemu/ui/command.py
@@ -1,3 +1,9 @@
from PyQt5.QtCore import (
QEvent,
pyqtSignal,
)


from PyQt5.QtWidgets import (
QPushButton,
QHBoxLayout,
Expand All @@ -7,12 +13,17 @@
)

class CommandWidget(QDockWidget):

setCommandButtonsForRunningSignal = pyqtSignal()
setCommandButtonsForStopSignal = pyqtSignal()

def __init__(self, parent, *args, **kwargs):
super(CommandWidget, self).__init__("Control Panel", parent)
self.parent = self.parentWidget()
self.log = self.parent.log
self.emulator = self.parent.emulator
sc = self.parent.shortcuts
self.root = self.parent
self.log = self.root.log
self.emulator = self.root.emulator
sc = self.root.shortcuts
layout = QHBoxLayout()
layout.addStretch(1)

Expand Down Expand Up @@ -41,6 +52,12 @@ def __init__(self, parent, *args, **kwargs):
widget = QWidget(self)
widget.setLayout(layout)
self.setWidget(widget)

self.root.signals["setCommandButtonsForRunning"] = self.setCommandButtonsForRunningSignal
self.setCommandButtonsForRunningSignal.connect(self.onEmulationStart)

self.root.signals["setCommandButtonsForStop"] = self.setCommandButtonsForStopSignal
self.setCommandButtonsForStopSignal.connect(self.onEmulationStop)
return


Expand All @@ -49,12 +66,14 @@ def stop(self):
self.log("No emulation context loaded.")
return

self.log("Stopping emulation...")
self.emulator.stop()
self.registerWidget.updateGrid()
self.log("Emulation context reset")
self.__stopButton.setDisabled(True)
self.__runButton.setDisabled(False)
self.__stepButton.setDisabled(False)
self.log("Emulation context has stopped")
#self.registerWidget.updateGrid()

# self.__stopButton.setDisabled(True)
# self.__runButton.setDisabled(False)
# self.__stepButton.setDisabled(False)
return


Expand Down Expand Up @@ -88,20 +107,16 @@ def __run(self) -> None:
if not self.load_emulation_context():
self.log("An error occured when loading context")
return
self.emulator.is_running = True
self.commandWidget.stopButton.setDisabled(False)

self.emulator.run()
self.registerWidget.updateGrid()
self.memoryViewerWidget.updateEditor()
return


def check_assembly_code(self) -> bool:
"""
Command to trigger a syntaxic check of the code in the code pane.
"""
code = self.parent.get_code()
code = self.root.get_code()
if self.emulator.compile_code(code, False):
msg = "Your code is syntaxically valid."
popup = QMessageBox.information
Expand Down Expand Up @@ -136,4 +151,15 @@ def load_emulation_context(self) -> bool:
if not self.emulator.map_code():
return False

return True
return True


def onEmulationStart(self) -> None:
self.__stopButton.setDisabled(False)
return


def onEmulationStop(self) -> None:
self.__runButton.setDisabled(True)
self.__stepButton.setDisabled(True)
return
15 changes: 10 additions & 5 deletions cemu/ui/main.py
Expand Up @@ -5,7 +5,10 @@

from typing import Callable, Dict, List, Tuple, Any

from PyQt5.QtCore import Qt
from PyQt5.QtCore import (
Qt,
pyqtSignal,
)

from PyQt5.QtWidgets import (
QApplication,
Expand Down Expand Up @@ -81,6 +84,7 @@ def __init__(self, *args, **kwargs):
self.recentFileActions = []
self.__plugins = []
self.archActions = {}
self.signals = {}
self.current_file = None
self.setAttribute(Qt.WA_DeleteOnClose)
self.shortcuts = Shortcut()
Expand Down Expand Up @@ -111,12 +115,10 @@ def __init__(self, *args, **kwargs):
self.addDockWidget(Qt.RightDockWidgetArea, self.__emuWidget)
self.addDockWidget(Qt.RightDockWidgetArea, self.__logWidget)

# todo:
# handle show/hide for dockable items

# todo:
# ... and the extra plugins too
self.LoadExtraPlugins()


# show everything
self.show()
return
Expand Down Expand Up @@ -531,3 +533,6 @@ def get_memory_layout(self) -> List[ Tuple[str, int, int, str, Any] ]:






50 changes: 29 additions & 21 deletions cemu/ui/memory.py
Expand Up @@ -14,7 +14,9 @@
)

from PyQt5.QtCore import(
QFileInfo
QFileInfo,
pyqtSignal,
QEvent
)

import unicorn
Expand All @@ -23,9 +25,13 @@


class MemoryWidget(QDockWidget):

refreshMemoryEditorSignal = pyqtSignal()

def __init__(self, parent, *args, **kwargs):
super(MemoryWidget, self).__init__("Memory Viewer", parent)
self.parent = self.parentWidget()
self.root = self.parent
title_layout = QHBoxLayout()
title_layout.addWidget(QLabel("Location"))
self.address = QLineEdit()
Expand All @@ -36,31 +42,27 @@ def __init__(self, parent, *args, **kwargs):
title_widget.setMouseTracking(True)

memview_layout = QVBoxLayout()
self.editor = QTextEdit()
self.editor.setFrameStyle(QFrame.Panel | QFrame.Plain)
self.editor.setFont(QFont('Courier', 10))
self.editor.setReadOnly(True)
self.__editor = QTextEdit()
self.__editor.setFrameStyle(QFrame.Panel | QFrame.Plain)
self.__editor.setFont(QFont('Courier', 10))
self.__editor.setReadOnly(True)
memview_layout.addWidget(title_widget)
memview_layout.addWidget(self.editor)
memview_layout.addWidget(self.__editor)

widget = QWidget(self)
widget.setLayout(memview_layout)
self.setWidget(widget)
return

def enterEvent(self, evt):
return

def leaveEvent(self, evt):
# define signals
self.refreshMemoryEditorSignal.connect(self.onRefreshMemoryEditor)
self.root.signals["refreshMemoryEditor"] = self.refreshMemoryEditorSignal
return

def mouseMoveEvent(self, evt):
return

def updateEditor(self):
emu = self.parent.emulator
if emu.vm is None:
self.editor.setText("VM not running")
def updateEditor(self) -> None:
emu = self.root.emulator
if not emu.is_running:
self.__editor.setText("VM not running")
return

value = self.address.text()
Expand All @@ -69,8 +71,9 @@ def updateEditor(self):

if value.startswith("@"):
# if the value of the "memory viewer" field starts with @.<section_name>
addr = emu.lookup_map(value[1:])
if addr is None:
try:
addr = emu.lookup_map(value[1:])
except KeyError:
return

elif value.startswith("$"):
Expand All @@ -91,8 +94,13 @@ def updateEditor(self):
l = 256
data = emu.vm.mem_read(addr, l)
text = hexdump(data, base=addr)
self.editor.setText(text)
self.__editor.setText(text)
except unicorn.unicorn.UcError:
self.editor.setText("Cannot read at address %x" % addr)
self.__editor.setText("Cannot read at address %x" % addr)

return


def onRefreshMemoryEditor(self) -> None:
self.updateEditor()
return

0 comments on commit 8742f0e

Please sign in to comment.