|
|
@@ -1,48 +1,71 @@
|
|
|
import sys
|
|
|
# from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
# from subprocess import PIPE, Popen
|
|
|
+from subprocess import Popen, PIPE
|
|
|
from threading import Thread
|
|
|
from time import sleep
|
|
|
import websocket
|
|
|
-from os import path, chdir
|
|
|
-import ctypes
|
|
|
+from os import path, mkdir, chdir
|
|
|
|
|
|
+import _msp430emu
|
|
|
import wx
|
|
|
|
|
|
-pwd = path.dirname(path.realpath(__file__))
|
|
|
-chdir(pwd)
|
|
|
-libmsp430 = ctypes.cdll.LoadLibrary(path.join(pwd, "libmsp430.so"))
|
|
|
-run_emu = libmsp430.run
|
|
|
-run_emu.restype = ctypes.c_int
|
|
|
-run_emu.argtypes = [ctypes.c_uint]
|
|
|
+source_dir = path.dirname(path.realpath(__file__))
|
|
|
+mkdir(".emulator")
|
|
|
+chdir(".emulator")
|
|
|
|
|
|
|
|
|
class Emulator:
|
|
|
EVENT_CONSOLE = 0
|
|
|
EVENT_SERIAL = 1
|
|
|
-
|
|
|
- def __init__(self, emu_dir, ws_port=59981, load=None, callback=None):
|
|
|
+ EVENT_GPIO = 2
|
|
|
+
|
|
|
+ P1_0_ON_PACKET = 0x00
|
|
|
+ P1_0_OFF_PACKET = 0x01
|
|
|
+ P1_1_ON_PACKET = 0x02
|
|
|
+ P1_1_OFF_PACKET = 0x03
|
|
|
+ P1_2_ON_PACKET = 0x04
|
|
|
+ P1_2_OFF_PACKET = 0x05
|
|
|
+ P1_3_ON_PACKET = 0x06
|
|
|
+ P1_3_OFF_PACKET = 0x07
|
|
|
+ P1_4_ON_PACKET = 0x08
|
|
|
+ P1_4_OFF_PACKET = 0x09
|
|
|
+ P1_5_ON_PACKET = 0x0A
|
|
|
+ P1_5_OFF_PACKET = 0x0B
|
|
|
+ P1_6_ON_PACKET = 0x0C
|
|
|
+ P1_6_OFF_PACKET = 0x0D
|
|
|
+ P1_7_ON_PACKET = 0x0E
|
|
|
+ P1_7_OFF_PACKET = 0x0F
|
|
|
+
|
|
|
+ def __init__(self, ws_port=59981, load=None, callback=None):
|
|
|
# self.process = Popen([path.join(emu_dir, 'MSP430'), str(ws_port)], stdout=PIPE, stderr=PIPE)
|
|
|
- self.process = Thread(target=run_emu, args=(ws_port, ))
|
|
|
+ self.ws_port = ws_port
|
|
|
+ self.process = Thread(target=self._start_emu, daemon=True)
|
|
|
self.process.start()
|
|
|
- sleep(3)
|
|
|
- self.ws = websocket.WebSocketApp(f"ws://127.0.0.1:{ws_port}",
|
|
|
- subprotocols={"emu-protocol"},
|
|
|
- on_open=self._ws_open,
|
|
|
- on_data=self._ws_msg,
|
|
|
- on_error=self._ws_err,
|
|
|
- on_close=self._ws_close
|
|
|
- )
|
|
|
+ self._start_ws()
|
|
|
self.load = load
|
|
|
self.started = False
|
|
|
self.start_errors = 0
|
|
|
self.callback = callback
|
|
|
- Thread(target=self.ws.run_forever).start()
|
|
|
|
|
|
- def wait(self):
|
|
|
- self.process.wait()
|
|
|
+ def _start_emu(self):
|
|
|
+ print("starting emulator...")
|
|
|
+ _msp430emu.run(self.ws_port)
|
|
|
+ print("stopping emulator...")
|
|
|
+
|
|
|
+ def _start_ws(self):
|
|
|
+ self.ws = websocket.WebSocketApp(
|
|
|
+ f"ws://127.0.0.1:{self.ws_port}",
|
|
|
+ subprotocols={"emu-protocol"},
|
|
|
+ on_open=self._ws_open,
|
|
|
+ on_data=self._ws_msg,
|
|
|
+ on_error=self._ws_err,
|
|
|
+ on_close=self._ws_close
|
|
|
+ )
|
|
|
+ Thread(target=self.ws.run_forever).start()
|
|
|
|
|
|
def load_file(self, fname):
|
|
|
+ print("loading " + fname)
|
|
|
with open(fname, 'rb') as f:
|
|
|
fdata = f.read()
|
|
|
name = path.basename(fname)
|
|
|
@@ -52,7 +75,12 @@ class Emulator:
|
|
|
payload += name.encode() + fdata
|
|
|
self.ws.send(payload, websocket.ABNF.OPCODE_BINARY)
|
|
|
|
|
|
+ def _cb(self, ev, data):
|
|
|
+ if callable(self.callback):
|
|
|
+ self.callback(ev, data)
|
|
|
+
|
|
|
def _ws_open(self):
|
|
|
+ print("connection established...")
|
|
|
self.started = True
|
|
|
if self.load is not None:
|
|
|
self.load_file(self.load)
|
|
|
@@ -60,19 +88,17 @@ class Emulator:
|
|
|
def _ws_msg(self, data, frame, x):
|
|
|
opcode = data[0]
|
|
|
if opcode == 0:
|
|
|
- return
|
|
|
+ if len(data) == 2 and data[1] <= 15:
|
|
|
+ self._cb(self.EVENT_GPIO, data[1])
|
|
|
+
|
|
|
elif opcode == 1:
|
|
|
message = data[1:-1].decode()
|
|
|
- if "Type 'h' for" in message:
|
|
|
- return
|
|
|
- if callable(self.callback):
|
|
|
- self.callback(self.EVENT_CONSOLE, message)
|
|
|
- print(message, end=None)
|
|
|
+ self._cb(self.EVENT_CONSOLE, message)
|
|
|
+ # print(message, end=None)
|
|
|
return
|
|
|
elif opcode == 2:
|
|
|
message = data[1:-1].decode()
|
|
|
- if callable(self.callback):
|
|
|
- self.callback(self.EVENT_SERIAL, message)
|
|
|
+ self._cb(self.EVENT_SERIAL, message)
|
|
|
return
|
|
|
else:
|
|
|
pass
|
|
|
@@ -83,7 +109,7 @@ class Emulator:
|
|
|
if self.start_errors < 5:
|
|
|
print(f"Failed to connect to emulator backend attempt {self.start_errors}")
|
|
|
sleep(1)
|
|
|
- self.ws.run_forever()
|
|
|
+ self._start_ws()
|
|
|
raise ConnectionError("Failed to connect to emulation backend after 5 tries")
|
|
|
raise err
|
|
|
|
|
|
@@ -102,31 +128,40 @@ class Emulator:
|
|
|
self.ws.send(b'\x01', websocket.ABNF.OPCODE_BINARY)
|
|
|
|
|
|
def close(self):
|
|
|
+ print("connection ended...")
|
|
|
self.ws.close()
|
|
|
- self.process.join()
|
|
|
+ # _msp430emu.stop()
|
|
|
+ # self.process.join(1)
|
|
|
|
|
|
|
|
|
class EmulatorWindow(wx.Frame):
|
|
|
- def __init__(self, parent, title):
|
|
|
+ def __init__(self, parent, title, load=None):
|
|
|
wx.Frame.__init__(self, parent, title=title)
|
|
|
self.control = wx.TextCtrl(self, size=wx.Size(400, 450),
|
|
|
style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
|
|
|
+ self.control.Hide()
|
|
|
+
|
|
|
self.serial = wx.TextCtrl(self, size=wx.Size(400, 450), style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
|
|
|
self.serial_input = wx.TextCtrl(self, style=wx.TE_DONTWRAP)
|
|
|
|
|
|
self.CreateStatusBar() # A Statusbar in the bottom of the window
|
|
|
|
|
|
- filemenu = wx.Menu()
|
|
|
-
|
|
|
- menuFile = filemenu.Append(wx.ID_OPEN, "&Firmware", " Open firmware")
|
|
|
- menuReset = filemenu.Append(wx.ID_CLOSE_ALL, "&Reset", " Reset Emulator")
|
|
|
- menuExit = filemenu.Append(wx.ID_EXIT, "E&xit", " Terminate the program")
|
|
|
+ file_menu = wx.Menu()
|
|
|
+ menuFile = file_menu.Append(wx.ID_OPEN, "&Firmware", " Open firmware")
|
|
|
self.Bind(wx.EVT_MENU, self.OnOpen, menuFile)
|
|
|
+ menuReset = file_menu.Append(wx.ID_CLOSE_ALL, "&Reset", " Reset Emulator")
|
|
|
self.Bind(wx.EVT_MENU, self.RestartEmulator, menuReset)
|
|
|
+ menuExit = file_menu.Append(wx.ID_EXIT, "E&xit", " Terminate the program")
|
|
|
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
|
|
|
|
|
|
+ view_menu = wx.Menu()
|
|
|
+ view_console = view_menu.AppendCheckItem(101, "View Console", "Show/Hide Emulator debug console")
|
|
|
+ self.Bind(wx.EVT_MENU, self.ToggleConsole, view_console)
|
|
|
+
|
|
|
menuBar = wx.MenuBar()
|
|
|
- menuBar.Append(filemenu, "&File")
|
|
|
+ menuBar.Append(file_menu, "&File")
|
|
|
+ menuBar.Append(view_menu, "&View")
|
|
|
+
|
|
|
self.SetMenuBar(menuBar)
|
|
|
|
|
|
self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
@@ -149,7 +184,7 @@ class EmulatorWindow(wx.Frame):
|
|
|
self.sizer0.Add(self.sizer3, 0, wx.EXPAND)
|
|
|
|
|
|
panel = wx.Panel(self, size=wx.Size(275, 375))
|
|
|
- img = wx.Bitmap("msp430.png", wx.BITMAP_TYPE_PNG)
|
|
|
+ img = wx.Bitmap(path.join(source_dir, "msp430.png"), wx.BITMAP_TYPE_PNG)
|
|
|
wx.StaticBitmap(panel, -1, img, (0, 0), (img.GetWidth(), img.GetHeight()))
|
|
|
self.diagram = DrawRect(panel, -1, size=wx.Size(275, 375))
|
|
|
#
|
|
|
@@ -158,7 +193,6 @@ class EmulatorWindow(wx.Frame):
|
|
|
# dc.SetBrush(wx.WHITE_BRUSH)
|
|
|
# dc.DrawRectangle(50, 50, 500, 500)
|
|
|
|
|
|
-
|
|
|
self.sizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
|
self.sizer1.Add(panel, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)
|
|
|
self.sizer1.Add(self.control, 1, wx.EXPAND)
|
|
|
@@ -173,39 +207,51 @@ class EmulatorWindow(wx.Frame):
|
|
|
self.Show()
|
|
|
|
|
|
self.control.WriteText("Initialising Emulator..\n")
|
|
|
- self.load = None
|
|
|
- if len(sys.argv) >= 2:
|
|
|
- if path.exists(sys.argv[1]):
|
|
|
- self.load = sys.argv[1]
|
|
|
- self.emu = Emulator('emulator', load=self.load, callback=self.callback)
|
|
|
+ self.load = load
|
|
|
+ self.emu = Emulator(load=self.load, callback=self.callback)
|
|
|
|
|
|
def callback(self, event, data):
|
|
|
if event == Emulator.EVENT_CONSOLE:
|
|
|
wx.CallAfter(self.control.AppendText, data)
|
|
|
- if event == Emulator.EVENT_SERIAL:
|
|
|
+ elif event == Emulator.EVENT_SERIAL:
|
|
|
wx.CallAfter(self.serial.AppendText, data)
|
|
|
+ elif event == Emulator.EVENT_GPIO:
|
|
|
+ self.diagram.port1[data // 2] = data % 2 == 0
|
|
|
+ wx.CallAfter(self.diagram.Refresh)
|
|
|
|
|
|
def RestartEmulator(self, e):
|
|
|
self.control.AppendText("Stopping Emulator..")
|
|
|
self.emu.close()
|
|
|
self.control.Clear()
|
|
|
self.serial.Clear()
|
|
|
- self.control.WriteText("Initialising Emulator..\n")
|
|
|
- self.emu = Emulator('emulator', load=self.load, callback=self.callback)
|
|
|
+
|
|
|
+ def ToggleConsole(self, e):
|
|
|
+ if e.Int == 0:
|
|
|
+ self.control.Hide()
|
|
|
+ self.Layout()
|
|
|
+ else:
|
|
|
+ self.control.Show()
|
|
|
+ self.Layout()
|
|
|
|
|
|
def OnOpen(self, e):
|
|
|
- with wx.FileDialog(self, "Open Firmware", wildcard="ELF files (*.elf)|*.elf",
|
|
|
+ with wx.FileDialog(self, "Open Firmware File", wildcard="ELF files (*.elf)|*.elf",
|
|
|
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
|
|
|
if fileDialog.ShowModal() == wx.ID_CANCEL:
|
|
|
return
|
|
|
self.load = fileDialog.GetPath()
|
|
|
- self.RestartEmulator(None)
|
|
|
+ self.emu.load_file(self.load)
|
|
|
+ self.diagram.power = False
|
|
|
+ # self.RestartEmulator(None)
|
|
|
|
|
|
def OnPause(self, e):
|
|
|
self.emu.emulation_pause()
|
|
|
+ self.diagram.power = False
|
|
|
+ self.diagram.Refresh()
|
|
|
|
|
|
def OnStart(self, e):
|
|
|
self.emu.emulation_start()
|
|
|
+ self.diagram.power = True
|
|
|
+ self.diagram.Refresh()
|
|
|
|
|
|
def OnExit(self, e):
|
|
|
self.emu.close()
|
|
|
@@ -214,27 +260,35 @@ class EmulatorWindow(wx.Frame):
|
|
|
|
|
|
class DrawRect(wx.Panel):
|
|
|
""" class MyPanel creates a panel to draw on, inherits wx.Panel """
|
|
|
+ RED = wx.Colour(255, 0, 0, wx.ALPHA_OPAQUE)
|
|
|
+ GREEN = wx.Colour(0, 255, 0, wx.ALPHA_OPAQUE)
|
|
|
+
|
|
|
def __init__(self, parent, id, **kwargs):
|
|
|
# create a panel
|
|
|
wx.Panel.__init__(self, parent, id, **kwargs)
|
|
|
# self.SetBackgroundColour("white")
|
|
|
self.Bind(wx.EVT_PAINT, self.OnPaint)
|
|
|
+ self.power = False
|
|
|
+ self.port1 = [False, False, False, False, False, False, False, False]
|
|
|
|
|
|
def OnPaint(self, evt):
|
|
|
"""set up the device context (DC) for painting"""
|
|
|
self.dc = wx.PaintDC(self)
|
|
|
- self.dc.SetPen(wx.Pen("green",style=wx.TRANSPARENT))
|
|
|
- self.dc.SetBrush(wx.Brush("green", wx.SOLID))
|
|
|
- # set x, y, w, h for rectangle
|
|
|
- self.dc.DrawRectangle(83, 356, 8, 15)
|
|
|
-
|
|
|
- self.dc.SetPen(wx.Pen("red",style=wx.TRANSPARENT))
|
|
|
- self.dc.SetBrush(wx.Brush("red", wx.SOLID))
|
|
|
- self.dc.DrawRectangle(70, 356, 8, 15)
|
|
|
- del self.dc
|
|
|
|
|
|
-
|
|
|
-if __name__ == '__main__':
|
|
|
- app = wx.App(False)
|
|
|
- frame = EmulatorWindow(None, "MSP430 Emulator")
|
|
|
- app.MainLoop()
|
|
|
+ if self.power:
|
|
|
+ self.dc.SetPen(wx.Pen(self.GREEN, style=wx.TRANSPARENT))
|
|
|
+ self.dc.SetBrush(wx.Brush(self.GREEN, wx.SOLID))
|
|
|
+ # set x, y, w, h for rectangle
|
|
|
+ self.dc.DrawRectangle(39, 110, 8, 15)
|
|
|
+
|
|
|
+ if self.port1[6]:
|
|
|
+ self.dc.SetPen(wx.Pen(self.GREEN, style=wx.TRANSPARENT))
|
|
|
+ self.dc.SetBrush(wx.Brush(self.GREEN, wx.SOLID))
|
|
|
+ # set x, y, w, h for rectangle
|
|
|
+ self.dc.DrawRectangle(83, 356, 8, 15)
|
|
|
+
|
|
|
+ if self.port1[0]:
|
|
|
+ self.dc.SetPen(wx.Pen(self.RED, style=wx.TRANSPARENT))
|
|
|
+ self.dc.SetBrush(wx.Brush(self.RED, wx.SOLID))
|
|
|
+ self.dc.DrawRectangle(70, 356, 8, 15)
|
|
|
+ del self.dc
|