/ emu8051-3102.py
emu8051-3102.py
  1  #!/usr/bin/env python3
  2  import sys, os
  3  from subprocess import PIPE, STDOUT, Popen
  4  
  5  trace = True
  6  vm_trace = True
  7  
  8  getc = [ord(c) for c in reversed("5\n23\n42\n13\nY\n")]
  9  
 10  fns = {
 11      0x2d92: 'vm_swapAB',
 12      0x2e6f: 'vm_storeB@dptr-4',
 13      0x2d4c: 'vm_add',
 14      0x2d5c: 'vm_sub',
 15      0x2d6c: 'vm_umul',
 16      0x2dc8: 'vm_mul',
 17      0x2d7f: 'vm_udiv',
 18      0x2e7c: 'vm_storeB@dptr-4ThenSwapAB',
 19      0x2e82: 'vm_mov_b_imm',
 20      0x2e99: 'vm_mov_imm_a_and_store@0x7f40',
 21      0x3008: 'vm_fn1',
 22      0x2ff3: 'vm_fn2',
 23      0x306f: 'vm_fn3',
 24      0x2d9f: 'vm_abs',
 25      0x2f9f: 'getshort2vm_b',
 26      0x2f1c: 'print_int_vm_a',
 27      0x2f02: 'vm_print',
 28      0x2eb8: 'vm_jmp',
 29      0x2ea4: 'push_pc++',
 30      0x2ef8: 'pop_pc',
 31      0x2ec8: 'vm_jz',
 32      0x2ecf: 'vm_jnz',
 33      0x2ed6: 'vm_jns',
 34      0x2edd: 'vm_js'}
 35  
 36  def split_by_n(obj, n):
 37    # src https://stackoverflow.com/questions/9475241/split-string-every-nth-character
 38    return [obj[i:i+n] for i in range(0, len(obj), n)]
 39  
 40  display = []
 41  def flush_display():
 42      global display
 43      if display == []: return
 44      if not (trace or vm_trace):
 45          print("display:", repr(''.join(display)))
 46      display = []
 47  
 48  class MEM:
 49    def __init__(self, mcu, getter, setter):
 50      self.mcu = mcu
 51      self.getter = getter
 52      self.setter = setter
 53  
 54    def __setitem__(self, addr, value):
 55      self.mcu.send(f"{self.setter} 0x{addr:x} 0x{value:x}\n".encode("utf8"))
 56  
 57    def __getitem__(self, addr):
 58      if isinstance(addr,slice):
 59          if addr.step:
 60            raise ValueError("step not supported for slicing mem")
 61          if not addr.stop: addr.stop = addr.start + 1
 62          size = addr.stop - addr.start
 63  
 64          # send cmd
 65          msg = f"{self.getter} 0x{addr.start:02x} 0x{size:02x}\n".encode("utf8")
 66          return bytes([int(x,16)
 67                      for line in self.mcu.send(msg,int(((size-1)/16)+1)*72)
 68                      for x in line[6:53].split()])
 69  
 70      msg = f"{self.getter} 0x{addr:02x} 1\n".encode("utf8")
 71      return int(self.mcu.send(msg,72)[0][6:53].split()[0],16)
 72  
 73  class EMU:
 74    def __init__(self):
 75      self.mem  = MEM(self,"di", "wi")
 76      self.xmem = MEM(self,"de", "we")
 77      self.rom  = MEM(self,"dp", "wp")
 78      self.p = Popen(['emu8051/src/cli/emu8051-cli', '-q', '1', 'ROM.hex'],
 79                stdin=PIPE, stdout=PIPE, stderr=STDOUT, bufsize=0)
 80      _, x = self.read(3) # skip initial prompt
 81      self.regs = self.get_regs()
 82  
 83    def read(self, toread):
 84        res = []
 85        cur = 0
 86        while(cur<toread):
 87            #print(sum(len(e) for e in res),toread)
 88            buf = os.read(self.p.stdout.fileno(), toread - cur)
 89            if not buf: # EOF
 90                print('eof')
 91                break
 92            else:
 93                #print(len(buf), repr(buf))
 94                res.append(buf)
 95            cur = sum(len(e) for e in res)
 96        return sum(len(e) for e in res), [l.strip() for l in b''.join(res).decode('utf8').split('\n')]
 97  
 98    def send(self, msg, expected=None):
 99        #print("sending", msg)
100        self.p.stdin.write(msg)
101        size, buf = self.read(len(msg))
102        if size!=len(msg):
103            buf = ''.join(buf)
104            print('meh', len(buf), repr(buf))
105            raise ValueError
106  
107        if expected:
108           # read hexdump
109           msize, m = self.read(expected)
110           if msize != expected:
111                 buf = ''.join(m)
112                 print('meh dumpmem', expected, msize, repr(m))
113                 raise ValueError
114  
115        size, buf = self.read(3)
116        if size!=3:
117            buf = ''.join(buf)
118            print('meh prompt', len(buf), repr(buf))
119            raise ValueError
120        #print('sent')
121  
122        if expected:
123          return m
124  
125    @property
126    def a(self):
127        return self.regs['ACC']
128  
129    @a.setter
130    def a(self, value):
131      self.send((f"wr a 0x{value:02x}\n").encode("utf8"))
132      self.regs['ACC'] = value
133  
134    @property
135    def dptr(self):
136        return self.regs['DPTR']
137  
138    @dptr.setter
139    def dptr(self, value):
140      self.send((f"wr dptr 0x{value:04x}\n").encode("utf8"))
141      self.regs['DPTR'] = value
142  
143    @property
144    def pc(self):
145      return self.regs['PC']
146  
147    @pc.setter
148    def pc(self, value):
149      self.send((f"wr pc 0x{value:04x}\n").encode("utf8"))
150      self.regs['PC'] = value
151  
152    @property
153    def sp(self):
154      return self.regs['SP']
155  
156    @sp.setter
157    def sp(self, value):
158      self.send((f"wr sp 0x{value:04x}\n").encode("utf8"))
159      self.regs['SP'] = value
160  
161    @property
162    def r0(self):
163      return self.regs['R0']
164  
165    @r0.setter
166    def r0(self, value):
167      self.send((f"wr r0 0x{value:04x}\n").encode("utf8"))
168      self.regs['R0'] = value
169  
170    @property
171    def r1(self):
172      return self.regs['R1']
173  
174    @r1.setter
175    def r1(self, value):
176      self.send((f"wr r1 0x{value:04x}\n").encode("utf8"))
177      self.regs['R1'] = value
178  
179    @property
180    def r2(self):
181      return self.regs['R2']
182  
183    @r2.setter
184    def r2(self, value):
185      self.send((f"wr r2 0x{value:04x}\n").encode("utf8"))
186      self.regs['R2'] = value
187  
188    def disasm_pc(self):
189       msg = (f"u 0x{self.pc:04x} 1\n").encode("utf8")
190       self.p.stdin.write(msg)
191       size, buf = self.read(len(msg))
192       if size!=len(msg):
193           buf = ''.join(buf)
194           print('meh', len(buf), repr(buf))
195           raise ValueError
196  
197       m = self.p.stdout.readline().strip().decode('utf8')
198  
199       size, buf = self.read(3)
200       if size!=3:
201           buf = ''.join(buf)
202           print('meh prompt', len(buf), repr(buf))
203           raise ValueError
204  
205       addr, asm, op = m.split("  ",2)
206       return int(addr,16), tuple(int(x,16) for x in asm.split()), ' '.join(op.split())
207  
208    def get_regs(self):
209        # send cmd
210        msg = b'dr\n'
211        b = self.send(b'dr\n',590)
212        if b[0] != '-' * 70: return
213        res = {}
214        for r, v in (zip(b[1].split('|')+b[4].split('|'), b[2].split('|')+b[5].split('|'))):
215            r = r.strip()
216            v = v.strip()
217            if (r,v) == ('',''): continue
218            if not r.startswith("PSW:"): res[r] = int(v,16)
219            else: res['CY']=int(v[0])
220        return res
221  
222    def next_cycle(self):
223      self.send(b"s\n")
224      self.regs = self.get_regs()
225  
226    def quit(self):
227      self.send(b"q\n")
228      return self.p.wait()
229  
230    def ret(self):
231       self.pc = (mcu.mem[(mcu.sp)] << 8) | mcu.mem[mcu.sp - 1]
232       self.sp -= 2
233  
234  def run(mcu, end_addr):
235      i = 0
236      while mcu.pc != end_addr:
237          i+=1
238          if vm_trace and int(mcu.pc) in fns:
239              #flush_display()
240              vm_op = fns[int(mcu.pc)]
241              vm_addr = int(mcu.mem[0x32]) << 8 | int(mcu.mem[0x33])
242              print(f"\033[92mv {vm_addr-1:04x}   {vm_op}", end="")
243              if vm_op in {"vm_add", "vm_sub", "vm_mul", "vm_udiv", "vm_umul", "vm_swapAB"}:
244                  a = (int(mcu.mem[0x2a]) << 24 | int(mcu.mem[0x2b]) << 16 | int(mcu.mem[0x2c]) << 8 | int(mcu.mem[0x2d]))
245                  b = (int(mcu.mem[0x2e]) << 24 | int(mcu.mem[0x2f]) << 16 | int(mcu.mem[0x30]) << 8 | int(mcu.mem[0x31]))
246                  print(f"({a:08x}, {b:08x})", end="")
247              elif vm_op in {"vm_abs", "vm_jns", 'vm_jmp', 'vm_jz', 'vm_jnz', 'vm_jns', 'vm_js'}:
248                  b = (int(mcu.mem[0x2e]) << 24 | int(mcu.mem[0x2f]) << 16 | int(mcu.mem[0x30]) << 8 | int(mcu.mem[0x31]))
249                  print(f"({b:08x})", end="")
250              elif vm_op in {"vm_storeB@dptr-4", 'vm_storeB@dptr-4ThenSwapAB'}:
251                  b = (int(mcu.mem[0x2e]) << 24 | int(mcu.mem[0x2f]) << 16 | int(mcu.mem[0x30]) << 8 | int(mcu.mem[0x31]))
252                  print(f"({int(mcu.dptr)-4:04x}, {b:08x}", end="")
253              elif vm_op in {"vm_mov_imm_a_and_store@0x7f40", "vm_mov_b_imm"}:
254                  tmp = (int(mcu.rom[vm_addr]) << 24 | int(mcu.rom[vm_addr+1]) << 16 | int(mcu.rom[vm_addr+2]) << 8 | int(mcu.rom[vm_addr+3]))
255                  print(f"({tmp:08x})", end="")
256              elif vm_op == "vm_print":
257                  txt = []
258                  idx = 0
259                  while not (mcu.rom[vm_addr+idx] & 0x80):
260                      txt.append(chr(mcu.rom[vm_addr+idx]))
261                      idx+=1
262                  txt.append(chr(mcu.rom[vm_addr+idx] & 0x7f))
263                  print(f"({repr(''.join(txt))})", end="")
264              print("\033[00m")
265  
266          if vm_trace:
267              if int(mcu.pc) == 0x2ce9:
268                  flush_display()
269                  print("loading 0x2e from offset:", hex(int(mcu.a)), end='\n' if trace else ' ')
270              elif int(mcu.pc) == 0x2cfb:
271                  flush_display()
272                  print("value", hex(int(mcu.mem[0x2e]) << 24 | int(mcu.mem[0x2f]) << 16 | int(mcu.mem[0x30]) << 8 | int(mcu.mem[0x31])))
273  
274          #op = Operation(mcu.rom[int(mcu.pc)])
275          #op.args = mcu.rom[int(mcu.pc + 1):int(mcu.pc + len(op))]
276          pc, asm, op = mcu.disasm_pc()
277          if trace:
278              flush_display()
279              a = (int(mcu.mem[0x2a]) << 24 | int(mcu.mem[0x2b]) << 16 | int(mcu.mem[0x2c]) << 8 | int(mcu.mem[0x2d]))
280              b = (int(mcu.mem[0x2e]) << 24 | int(mcu.mem[0x2f]) << 16 | int(mcu.mem[0x30]) << 8 | int(mcu.mem[0x31]))
281              print(f"i {int(mcu.pc):04x}   {str(op):<30} | "
282                    f"{int(mcu.a):02x} "
283                    f"{int(mcu.r0):02x} "
284                    f"{int(mcu.r1):02x} "
285                    f"{int(mcu.r2):02x} "
286                    f"{int(mcu.dptr):04x} "
287                    f"{int(mcu.mem[0xd0]) & 0xfe:02x} "
288                    f"{int(mcu.mem[0xd1]):02x} "
289                    f"{a:08x} {b:08x}")
290              if i == 25665:
291                print(f"{mcu.mem[mcu.r0]:02x}")
292  
293          if asm[0] == 0x12: # lcall
294              if asm[1:] == (0x03,0x7a): # putc
295                  if trace:
296                      print(f"putc({repr(chr(int(mcu.a)))})")
297                  mcu.pc += len(asm)
298                  #print(f"{chr(int(mcu.a))}", end='')
299                  display.append(chr(int(mcu.a)))
300                  continue
301              elif asm[1:] == (0x05,0x65): # getc
302                  if trace:
303                      flush_display()
304                      print("getc()")
305                  mcu.pc += len(asm)
306                  #mcu.a = ord(input('')[0])
307                  mcu.a = getc.pop()
308                  continue
309          elif asm[0] == 0x2: # jmp
310              if asm[1:] == (0x03,0x7a): # putc
311                  if trace:
312                      print(f"putc({repr(chr(int(mcu.a)))})")
313                  #print(f"{chr(int(mcu.a))}", end='')
314                  display.append(chr(int(mcu.a)))
315                  mcu.ret() # ret
316                  continue
317              elif asm[1:] == (0x05,0x65): # getc
318                  if trace:
319                      flush_display()
320                      print("getc()")
321                  #mcu.a = ord(input('')[0])
322                  mcu.a = getc.pop()
323                  mcu.ret() # ret
324                  continue
325          mcu.next_cycle()
326      if trace:
327          flush_display()
328          print("end, total insn", i)
329      return i
330  
331  mcu = EMU()
332  
333  mcu.mem[0x25] = 0
334  mcu.mem[0x2a] = 64
335  mcu.mem[0x2b] = 0
336  mcu.mem[0x2c] = 127
337  mcu.mem[0x2d] = 0
338  mcu.mem[0x2e] = 0
339  mcu.mem[0x2f] = 0
340  mcu.mem[0x30] = 0
341  mcu.mem[0x31] = 0
342  
343  mcu.xmem[0x7f00] = 0xde
344  mcu.xmem[0x7f01] = 0xad
345  mcu.xmem[0x7f02] = 0xbe
346  mcu.xmem[0x7f03] = 0xef
347  
348  mcu.xmem[0x7f04] = 0x13
349  mcu.xmem[0x7f05] = 0x37
350  mcu.xmem[0x7f06] = 0xc0
351  mcu.xmem[0x7f07] = 0xde
352  
353  mcu.xmem[0x7f08] = 0xac
354  mcu.xmem[0x7f09] = 0xad
355  mcu.xmem[0x7f0a] = 0xea
356  mcu.xmem[0x7f0b] = 0xdb
357  
358  mcu.xmem[0x7f0c] = 0xc0
359  mcu.xmem[0x7f0d] = 0x01
360  mcu.xmem[0x7f0e] = 0xba
361  mcu.xmem[0x7f0f] = 0xbe
362  
363  mcu.xmem[0x7f1c] = 0x1c
364  mcu.xmem[0x7f1d] = 0x7f
365  mcu.xmem[0x7f1e] = 0x7f
366  mcu.xmem[0x7f1f] = 0x1c
367  
368  mcu.xmem[0x7f30] = 0x30
369  mcu.xmem[0x7f31] = 0x7f
370  mcu.xmem[0x7f32] = 0x7f
371  mcu.xmem[0x7f33] = 0x30
372  
373  mcu.xmem[0x7f34] = 0x34
374  mcu.xmem[0x7f35] = 0x7f
375  mcu.xmem[0x7f36] = 0x7f
376  mcu.xmem[0x7f37] = 0x34
377  
378  mcu.xmem[0x7f38] = 0x38
379  mcu.xmem[0x7f39] = 0x7f
380  mcu.xmem[0x7f3a] = 0x7f
381  mcu.xmem[0x7f3b] = 0x38
382  
383  mcu.xmem[0x7f3c] = 0x3c
384  mcu.xmem[0x7f3d] = 0x7f
385  mcu.xmem[0x7f3e] = 0x7f
386  mcu.xmem[0x7f3f] = 0x3c
387  
388  mcu.pc = 0x3102
389  steps = run(mcu, 0x64f)