emulator.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. from threading import Thread, Event
  2. from os import path
  3. import _msp430emu
  4. import wx
  5. source_dir = path.dirname(path.realpath(__file__))
  6. class Emulator:
  7. EVENT_CONSOLE = 0
  8. EVENT_SERIAL = 1
  9. EVENT_GPIO = 2
  10. P1_0_ON_PACKET = 0x00
  11. P1_0_OFF_PACKET = 0x01
  12. P1_1_ON_PACKET = 0x02
  13. P1_1_OFF_PACKET = 0x03
  14. P1_2_ON_PACKET = 0x04
  15. P1_2_OFF_PACKET = 0x05
  16. P1_3_ON_PACKET = 0x06
  17. P1_3_OFF_PACKET = 0x07
  18. P1_4_ON_PACKET = 0x08
  19. P1_4_OFF_PACKET = 0x09
  20. P1_5_ON_PACKET = 0x0A
  21. P1_5_OFF_PACKET = 0x0B
  22. P1_6_ON_PACKET = 0x0C
  23. P1_6_OFF_PACKET = 0x0D
  24. P1_7_ON_PACKET = 0x0E
  25. P1_7_OFF_PACKET = 0x0F
  26. SET_REG_P1_IN = 0x05
  27. def __init__(self, load=None, callback=None):
  28. # self.process = Popen([path.join(emu_dir, 'MSP430'), str(ws_port)], stdout=PIPE, stderr=PIPE)
  29. # self.ws_port = ws_port
  30. # self._start_ws()
  31. self.load = load
  32. self.started = False
  33. self.callback = callback
  34. _msp430emu.on_serial(self._on_serial)
  35. _msp430emu.on_console(self._on_console)
  36. # _msp430emu.on_control(self._on_control)
  37. if self.load is not None:
  38. self.process = Thread(target=self._start_emu, daemon=False)
  39. self.process.start()
  40. def _on_serial(self, s):
  41. self._cb(self.EVENT_SERIAL, s)
  42. def _on_console(self, s):
  43. self._cb(self.EVENT_CONSOLE, s)
  44. def _on_control(self, opcode, data):
  45. if opcode <= 0x0F:
  46. self._cb(self.EVENT_GPIO, opcode)
  47. def send_command(self, cmd):
  48. if self.started:
  49. _msp430emu.cmd(cmd)
  50. def get_port1_regs(self):
  51. if self.started:
  52. return _msp430emu.get_regs(0x05)
  53. def set_port1_in(self, value):
  54. if 255 >= value >= 0 and self.started:
  55. return _msp430emu.set_regs(0x05, value)
  56. def reset(self):
  57. if self.started:
  58. _msp430emu.reset()
  59. def _start_emu(self):
  60. print("starting emulator...")
  61. self.started = True
  62. _msp430emu.init(self.load)
  63. print("stopping emulator...")
  64. def load_file(self, fname):
  65. print("loading " + fname)
  66. self.load = fname
  67. self.process = Thread(target=self._start_emu, daemon=False)
  68. self.process.start()
  69. def _cb(self, ev, data):
  70. if callable(self.callback):
  71. self.callback(ev, data)
  72. def __del__(self):
  73. self.close()
  74. def emulation_pause(self):
  75. if self.started:
  76. _msp430emu.pause()
  77. # if self.load is not None:
  78. # self.ws.send(b'\x02', websocket.ABNF.OPCODE_BINARY)
  79. def emulation_start(self):
  80. if self.started:
  81. _msp430emu.play()
  82. # if self.load is not None:
  83. # self.ws.send(b'\x01', websocket.ABNF.OPCODE_BINARY)
  84. def close(self):
  85. if self.started:
  86. try:
  87. _msp430emu.stop()
  88. except SystemError:
  89. print("Failed gradually stop emulator")
  90. self.process.join(2)
  91. class EmulatorWindow(wx.Frame):
  92. def __init__(self, parent, title, load=None):
  93. wx.Frame.__init__(self, parent, title=title)
  94. self.control = wx.TextCtrl(self, size=wx.Size(400, 450),
  95. style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
  96. self.control.Hide()
  97. self.serial = wx.TextCtrl(self, size=wx.Size(400, 450), style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_DONTWRAP)
  98. self.serial_input = wx.TextCtrl(self)
  99. self.statusBar = self.CreateStatusBar() # A Statusbar in the bottom of the window
  100. file_menu = wx.Menu()
  101. menuFile = file_menu.Append(wx.ID_OPEN, "&Firmware", " Open firmware")
  102. self.Bind(wx.EVT_MENU, self.OnOpen, menuFile)
  103. menuReset = file_menu.Append(wx.ID_CLOSE_ALL, "&Reset", " Reset Emulator")
  104. self.Bind(wx.EVT_MENU, self.RestartEmulator, menuReset)
  105. menuExit = file_menu.Append(wx.ID_EXIT, "E&xit", " Terminate the program")
  106. self.Bind(wx.EVT_MENU, self.OnClose, menuExit)
  107. self.Bind(wx.EVT_CLOSE, self.OnExit)
  108. view_menu = wx.Menu()
  109. self.view_console = view_menu.AppendCheckItem(101, "View Console", "Show/Hide Emulator debug console")
  110. self.view_registers = view_menu.AppendCheckItem(102, "View Registers", "Show/Hide Emulator registers table")
  111. self.Bind(wx.EVT_MENU, self.ToggleConsole, self.view_console)
  112. self.Bind(wx.EVT_MENU, self.ToggleRegisters, self.view_registers)
  113. self.registers = None
  114. menuBar = wx.MenuBar()
  115. menuBar.Append(file_menu, "&File")
  116. menuBar.Append(view_menu, "&View")
  117. self.SetMenuBar(menuBar)
  118. self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)
  119. self.btn_start_emu = wx.Button(self, -1, "Start")
  120. self.Bind(wx.EVT_BUTTON, self.OnStart, self.btn_start_emu)
  121. self.btn_stop_emu = wx.Button(self, -1, "Pause")
  122. self.Bind(wx.EVT_BUTTON, self.OnPause, self.btn_stop_emu)
  123. self.btn_key = wx.Button(self, -1, "Press Key")
  124. self.btn_key.Bind(wx.EVT_LEFT_DOWN, self.OnMouseDown)
  125. self.btn_key.Bind(wx.EVT_LEFT_UP, self.OnMouseUp)
  126. self.btn_rst = wx.Button(self, -1, "Reset")
  127. self.Bind(wx.EVT_BUTTON, self.OnKeyReset, self.btn_rst)
  128. self.sizer2.Add(self.btn_key, 1, wx.EXPAND)
  129. self.sizer2.Add(self.btn_start_emu, 1, wx.EXPAND)
  130. self.sizer2.Add(self.btn_stop_emu, 1, wx.EXPAND)
  131. self.sizer2.Add(self.btn_rst, 1, wx.EXPAND)
  132. self.sizer = wx.BoxSizer(wx.VERTICAL)
  133. self.sizer3 = wx.BoxSizer(wx.HORIZONTAL)
  134. self.btn_start_emu = wx.Button(self, -1, "Send")
  135. self.Bind(wx.EVT_BUTTON, self.SendSerial, self.btn_start_emu)
  136. self.sizer3.Add(self.serial_input, 1)
  137. self.sizer3.Add(self.btn_start_emu, 0)
  138. self.sizer0 = wx.BoxSizer(wx.VERTICAL)
  139. self.sizer0.Add(self.serial, 1, wx.EXPAND)
  140. self.sizer0.Add(self.sizer3, 0, wx.EXPAND)
  141. panel = wx.Panel(self, size=wx.Size(275, 375))
  142. # img =
  143. # wx.StaticBitmap(panel, -1, img, (0, 0), (img.GetWidth(), img.GetHeight()))
  144. self.diagram = DrawRect(panel, -1, size=wx.Size(275, 375))
  145. #
  146. # dc = wx.WindowDC(panel)
  147. # dc.SetPen(wx.WHITE_PEN)
  148. # dc.SetBrush(wx.WHITE_BRUSH)
  149. # dc.DrawRectangle(50, 50, 500, 500)
  150. # self.sizer_key_rst = wx.BoxSizer(wx.HORIZONTAL)
  151. # btn_key = wx.Button(self, -1, "Press Key")
  152. # self.Bind(wx.EVT_BUTTON, self.OnKeyPress, btn_key)
  153. # btn_rst = wx.Button(self, -1, "Reset")
  154. # self.Bind(wx.EVT_BUTTON, self.OnKeyReset, btn_rst)
  155. # self.sizer_key_rst.Add(btn_key, 1, wx.EXPAND)
  156. # self.sizer_key_rst.Add(btn_rst, 1, wx.EXPAND)
  157. self.sizer_diagram = wx.BoxSizer(wx.VERTICAL)
  158. self.sizer_diagram.Add(panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)
  159. #
  160. # self.sizer_left = wx.BoxSizer(wx.VERTICAL)
  161. # self.sizer_left.Add(self.sizer_diagram, 1, wx.EXPAND)
  162. # self.sizer_left.Add(self.sizer_key_rst, 0, wx.ALIGN_BOTTOM)
  163. self.sizer1 = wx.BoxSizer(wx.HORIZONTAL)
  164. self.sizer1.Add(self.sizer_diagram, 0, wx.EXPAND)
  165. self.sizer1.Add(self.control, 1, wx.EXPAND)
  166. self.sizer1.Add(self.sizer0, 1, wx.EXPAND)
  167. self.sizer.Add(self.sizer1, 1, wx.EXPAND)
  168. self.sizer.Add(self.sizer2, 0, wx.EXPAND)
  169. self.SetSizer(self.sizer)
  170. self.SetAutoLayout(1)
  171. self.sizer.Fit(self)
  172. self.Show()
  173. self.timer_locked = True
  174. self.timer_running = Event()
  175. self.timer = Thread(target=self.OnTimer)
  176. self.timer.start()
  177. self.control.WriteText("Initialising Emulator..\n")
  178. self.load = load
  179. self.emu = Emulator(load=self.load, callback=self.callback)
  180. if self.load is None:
  181. self.serial_input.Disable()
  182. self.serial.Disable()
  183. self.btn_rst.Disable()
  184. self.btn_key.Disable()
  185. self.btn_start_emu.Disable()
  186. self.btn_stop_emu.Disable()
  187. else:
  188. self.statusBar.SetStatusText("Press start to run emulation")
  189. def callback(self, event, data):
  190. if event == Emulator.EVENT_CONSOLE:
  191. wx.CallAfter(self.control.AppendText, data)
  192. elif event == Emulator.EVENT_SERIAL:
  193. wx.CallAfter(self.serial.AppendText, data)
  194. # elif event == Emulator.EVENT_GPIO:
  195. # self.diagram.port1[data // 2] = data % 2 == 0
  196. # wx.CallAfter(self.diagram.Refresh)
  197. def RestartEmulator(self, e):
  198. self.emu.close()
  199. self.control.Clear()
  200. self.serial.Clear()
  201. self.emu = Emulator(load=self.load, callback=self.callback)
  202. def ToggleConsole(self, e):
  203. if e.Int == 0:
  204. self.control.Hide()
  205. self.Layout()
  206. else:
  207. self.control.Show()
  208. self.Layout()
  209. def OnOpen(self, e):
  210. with wx.FileDialog(self, "Open Firmware File", wildcard="BIN files (*.bin)|*.bin",
  211. style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog:
  212. if fileDialog.ShowModal() == wx.ID_CANCEL:
  213. return
  214. self.load = fileDialog.GetPath()
  215. self.emu.load_file(self.load)
  216. self.diagram.power = False
  217. self.serial_input.Enable()
  218. self.serial.Enable()
  219. self.btn_rst.Enable()
  220. self.btn_key.Enable()
  221. self.btn_start_emu.Enable()
  222. self.btn_stop_emu.Enable()
  223. self.statusBar.SetStatusText("Press start to run emulation")
  224. def OnTimer(self):
  225. while 1:
  226. try:
  227. if self.timer_running.wait(0.04):
  228. break
  229. except TimeoutError:
  230. pass
  231. if self.timer_locked:
  232. continue
  233. ports = self.emu.get_port1_regs()
  234. if ports is not None:
  235. for i in range(8):
  236. self.diagram.port1[i] = (ports[0] >> i) & 1 == 1
  237. wx.CallAfter(self.diagram.Refresh)
  238. if self.view_registers.IsChecked():
  239. wx.CallAfter(self.registers.set_values, self.emu)
  240. def OnPause(self, e):
  241. self.emu.emulation_pause()
  242. self.diagram.power = False
  243. self.diagram.Refresh()
  244. self.emu.get_port1_regs()
  245. self.timer_locked = True
  246. def OnStart(self, e):
  247. if self.load is None:
  248. self.OnOpen(e)
  249. else:
  250. self.statusBar.SetStatusText("")
  251. self.emu.emulation_start()
  252. self.diagram.power = True
  253. self.diagram.Refresh()
  254. self.timer_locked = False
  255. def OnClose(self, e):
  256. self.Close(True)
  257. def OnExit(self, e):
  258. self.emu.close()
  259. self.timer_running.set()
  260. e.Skip()
  261. def OnMouseDown(self, e):
  262. self.emu.set_port1_in(8) # P1.3 high
  263. e.Skip()
  264. def OnMouseUp(self, e):
  265. self.emu.set_port1_in(0) # P1.3 low
  266. e.Skip()
  267. def OnKeyReset(self, e):
  268. self.diagram.port1 = [False, False, False, False, False, False, False, False]
  269. self.emu.reset()
  270. def SendSerial(self, e):
  271. text = self.serial_input.GetValue()
  272. print(text)
  273. self.serial_input.Clear()
  274. def ToggleRegisters(self, e):
  275. if e.Int == 0:
  276. self.registers.Close()
  277. else:
  278. self.registers = RegisterPanel(self)
  279. self.registers.Bind(wx.EVT_CLOSE, self.OnRegistersClose)
  280. self.registers.set_values(self.emu)
  281. def OnRegistersClose(self, e):
  282. self.view_registers.Check(False)
  283. e.Skip()
  284. class RegisterPanel(wx.Frame):
  285. def __init__(self, parent):
  286. wx.Frame.__init__(self, parent, title="Emulator Registers")
  287. self.parent = parent
  288. self.grid = wx.FlexGridSizer(9, 2, 0, 10)
  289. self.p1_registers = {
  290. 'P1OUT': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  291. 'P1DIR': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  292. 'P1IFG': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  293. 'P1IES': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  294. 'P1IE': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  295. 'P1SEL': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  296. 'P1SEL2': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  297. 'P1REN': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  298. 'P1IN': wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_NO_VSCROLL),
  299. }
  300. gridvals = []
  301. for name, text in self.p1_registers.items():
  302. text.SetMinSize((80, 15))
  303. text.SetValue("00000000")
  304. gridvals.append((wx.StaticText(self, label=name),))
  305. gridvals.append((text, 1, wx.EXPAND))
  306. self.grid.AddMany(gridvals)
  307. box = wx.BoxSizer(wx.VERTICAL)
  308. box.Add(self.grid, proportion=1, flag=wx.ALL | wx.EXPAND, border=15)
  309. self.SetSizer(box)
  310. self.Center()
  311. self.Show()
  312. self.Fit()
  313. self.Layout()
  314. def set_values(self, emu):
  315. p1regs = emu.get_port1_regs()
  316. if p1regs is not None:
  317. for i, reg in enumerate(self.p1_registers.values()):
  318. reg.SetValue(f"{p1regs[i]:08b}")
  319. class DrawRect(wx.Panel):
  320. """ class MyPanel creates a panel to draw on, inherits wx.Panel """
  321. RED = wx.Colour(255, 0, 0, wx.ALPHA_OPAQUE)
  322. GREEN = wx.Colour(0, 255, 0, wx.ALPHA_OPAQUE)
  323. def __init__(self, parent, id, **kwargs):
  324. # create a panel
  325. wx.Panel.__init__(self, parent, id, **kwargs)
  326. # self.SetBackgroundColour("white")
  327. self.Bind(wx.EVT_PAINT, self.OnPaint)
  328. self.power = False
  329. self.port1 = [False, False, False, False, False, False, False, False]
  330. self.image = wx.Bitmap(path.join(source_dir, "msp430.png"), wx.BITMAP_TYPE_PNG)
  331. def OnPaint(self, evt):
  332. """set up the device context (DC) for painting"""
  333. self.dc = wx.PaintDC(self)
  334. self.dc.DrawBitmap(self.image, 0, 0, True)
  335. if self.power:
  336. self.dc.SetPen(wx.Pen(self.GREEN, style=wx.TRANSPARENT))
  337. self.dc.SetBrush(wx.Brush(self.GREEN, wx.SOLID))
  338. # set x, y, w, h for rectangle
  339. self.dc.DrawRectangle(39, 110, 8, 15)
  340. if self.port1[6]:
  341. self.dc.SetPen(wx.Pen(self.GREEN, style=wx.TRANSPARENT))
  342. self.dc.SetBrush(wx.Brush(self.GREEN, wx.SOLID))
  343. # set x, y, w, h for rectangle
  344. self.dc.DrawRectangle(83, 356, 8, 15)
  345. if self.port1[0]:
  346. self.dc.SetPen(wx.Pen(self.RED, style=wx.TRANSPARENT))
  347. self.dc.SetBrush(wx.Brush(self.RED, wx.SOLID))
  348. self.dc.DrawRectangle(70, 356, 8, 15)
  349. del self.dc
  350. def run(load=None):
  351. app = wx.App(False)
  352. frame = EmulatorWindow(None, "MSP430 Emulator", load)
  353. app.MainLoop()