github.com/chenzhuoyu/iasm@v0.9.1/repl/iasm.go (about)

     1  package repl
     2  
     3  import (
     4      `fmt`
     5      `io`
     6      `math`
     7      `os`
     8      `path`
     9      `runtime`
    10      `strconv`
    11      `strings`
    12      `unicode`
    13      `unsafe`
    14  
    15      `github.com/knz/go-libedit`
    16  )
    17  
    18  // IASM is the interactive REPL.
    19  type IASM struct {
    20      run bool
    21      off uintptr
    22      efd libedit.EditLine
    23      ias _IASMArchSpecific
    24      mem map[uint64]_Memory
    25      fns map[string]unsafe.Pointer
    26  }
    27  
    28  // HistoryFile is the file that saves the REPL history, defaults to "~/.iasmhistory".
    29  var HistoryFile = path.Clean(os.ExpandEnv("$HOME/.iasmhistory"))
    30  
    31  // Start starts a new REPL session.
    32  func (self *IASM) Start() {
    33      var err error
    34      var efd libedit.EditLine
    35  
    36      /* greeting messages */
    37      println("Interactive Assembler v1.0")
    38      println("Compiled with " + strconv.Quote(runtime.Version()) + ".")
    39      println("History will be loaded from " + strconv.Quote(HistoryFile) + ".")
    40      println(`Type ".help" for more information.`)
    41  
    42      /* initialize libedit */
    43      if efd, err = libedit.Init("iasm", false); err != nil {
    44          panic(err)
    45      }
    46  
    47      /* initialize IASM */
    48      self.off = 0
    49      self.efd = efd
    50      self.run = true
    51      self.mem = map[uint64]_Memory{}
    52      self.fns = map[string]unsafe.Pointer{}
    53  
    54      /* initialze the editor instance */
    55      defer self.efd.Close()
    56      self.efd.RebindControlKeys()
    57  
    58      /* enable history */
    59      _ = self.efd.UseHistory(-1, true)
    60      _ = self.efd.LoadHistory(HistoryFile)
    61  
    62      /* set prompt and enable auto-save */
    63      self.efd.SetLeftPrompt(">>> ")
    64      self.efd.SetAutoSaveHistory(HistoryFile, true)
    65  
    66      /* main REPL loop */
    67      for self.run {
    68          self.handleOnce()
    69      }
    70  }
    71  
    72  func (self *IASM) readLine() string {
    73      var err error
    74      var ret string
    75  
    76      /* read the line */
    77      for {
    78          if ret, err = self.efd.GetLine(); err == nil {
    79              break
    80          } else if err != libedit.ErrInterrupted {
    81              panic(err)
    82          } else {
    83              println("^C")
    84          }
    85      }
    86  
    87      /* check for empty line */
    88      if ret == "" {
    89          return ""
    90      }
    91  
    92      /* add to history */
    93      _ = self.efd.AddHistory(ret)
    94      return ret
    95  }
    96  
    97  func (self *IASM) handleEOF() {
    98      self.run = false
    99      println()
   100  }
   101  
   102  func (self *IASM) handleOnce() {
   103      defer self.handleError()
   104      self.handleCommand(strings.TrimSpace(self.readLine()))
   105  }
   106  
   107  func (self *IASM) handleError() {
   108      switch v := recover(); v {
   109          case nil    : break
   110          case io.EOF : self.handleEOF()
   111          default     : println(fmt.Sprintf("iasm: %v", v))
   112      }
   113  }
   114  
   115  func (self *IASM) handleCommand(cmd string) {
   116      var pos int
   117      var fnv func(*IASM, string)
   118  
   119      /* check for empty command */
   120      if cmd == "" {
   121          return
   122      }
   123  
   124      /* find the command delimiter */
   125      if pos = strings.IndexFunc(cmd, unicode.IsSpace); pos == -1 {
   126          pos = len(cmd)
   127      }
   128  
   129      /* handle syntax errors via panic/recover */
   130      defer func() {
   131          if v := recover(); v != nil {
   132              if e, ok := v.(_SyntaxError); !ok {
   133                  panic(v)
   134              } else {
   135                  println("iasm: " + e.Error())
   136              }
   137          }
   138      }()
   139  
   140      /* assembly immediately or call the command, if any */
   141      if cmd[0] != '.' {
   142          self.handleAsmImmediate(cmd)
   143      } else if fnv = _CMDS[cmd[1:pos]]; fnv != nil {
   144          fnv(self, strings.TrimSpace(cmd[pos:]))
   145      } else {
   146          println("iasm: unknown command: " + cmd)
   147      }
   148  }
   149  
   150  func (self *IASM) handleAsmImmediate(asm string) {
   151      var err error
   152      var buf []byte
   153  
   154      /* assemble the instruction */
   155      if buf, err = self.ias.doasm(self.off, asm); err != nil {
   156          println("iasm: " + err.Error())
   157          return
   158      }
   159  
   160      /* dump the instruction, and adjust the display offset */
   161      println(asmdump(buf, self.off, asm))
   162      self.off += uintptr(len(buf))
   163  }
   164  
   165  var _CMDS = map[string]func(*IASM, string) {
   166      "free"   : (*IASM)._cmd_free,
   167      "malloc" : (*IASM)._cmd_malloc,
   168      "info"   : (*IASM)._cmd_info,
   169      "read"   : (*IASM)._cmd_read,
   170      "write"  : (*IASM)._cmd_write,
   171      "fill"   : (*IASM)._cmd_fill,
   172      "regs"   : (*IASM)._cmd_regs,
   173      "asm"    : (*IASM)._cmd_asm,
   174      "sys"    : (*IASM)._cmd_sys,
   175      "base"   : (*IASM)._cmd_base,
   176      "exit"   : (*IASM)._cmd_exit,
   177      "help"   : (*IASM)._cmd_help,
   178  }
   179  
   180  func (self *IASM) _cmd_free(v string) {
   181      var ok  bool
   182      var err error
   183      var mid uint64
   184      var mem _Memory
   185  
   186      /* parse the memory ID */
   187      scan  (v).
   188      uint  (&mid).
   189      close ()
   190  
   191      /* find the memory block */
   192      if mem, ok = self.mem[mid]; !ok {
   193          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   194          return
   195      }
   196  
   197      /* unmap the memory block */
   198      if err = munmap(mem); err == nil {
   199          delete(self.mem, mid)
   200      } else {
   201          println("iasm: munmap(): " + err.Error())
   202      }
   203  }
   204  
   205  func (self *IASM) _cmd_malloc(v string) {
   206      var err error
   207      var mid uint64
   208      var nbs uint64
   209      var mem _Memory
   210  
   211      /* parse the memory ID and size */
   212      scan    (v).
   213      uint    (&mid).
   214      uintopt (&nbs).
   215      close   ()
   216  
   217      /* default to one page of memory */
   218      if nbs == 0 {
   219          nbs = _PageSize
   220      }
   221  
   222      /* check for duplicaded IDs */
   223      if _, ok := self.mem[mid]; ok {
   224          println(fmt.Sprintf("iasm: duplicated memory ID: %d", mid))
   225          return
   226      }
   227  
   228      /* map the memory */
   229      if mem, err = mmap(nbs); err != nil {
   230          println("iasm: mmap(): " + err.Error())
   231          return
   232      }
   233  
   234      /* save the memory block */
   235      self.mem[mid] = mem
   236      println(fmt.Sprintf("Memory mapped at address %#x with size %d", mem.addr, mem.size))
   237  }
   238  
   239  func (self *IASM) _cmd_info(v string) {
   240      var ok  bool
   241      var mid uint64
   242      var mem _Memory
   243  
   244      /* parse the memory ID */
   245      scan  (v).
   246      uint  (&mid).
   247      close ()
   248  
   249      /* find the memory block */
   250      if mem, ok = self.mem[mid]; !ok {
   251          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   252          return
   253      }
   254  
   255      /* print the memory info */
   256      println(fmt.Sprintf("Address : %#x", mem.addr))
   257      println(fmt.Sprintf("Size    : %d bytes", mem.size))
   258  }
   259  
   260  func (self *IASM) _cmd_read(v string) {
   261      var ok  bool
   262      var off uint64
   263      var mid uint64
   264      var mem _Memory
   265  
   266      /* parse the memory ID, offset and the optional size */
   267      nbs := uint64(math.MaxUint64)
   268      scan(v).idoff(&mid, &off).uintopt(&nbs).close()
   269  
   270      /* find the memory block */
   271      if mem, ok = self.mem[mid]; !ok {
   272          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   273          return
   274      }
   275  
   276      /* read the whole block if not specified */
   277      if nbs == math.MaxUint64 {
   278          nbs = mem.size - off
   279      }
   280  
   281      /* check the offset and size */
   282      if off + nbs > mem.size {
   283          if diff := off + nbs - mem.size; diff == 1 {
   284              println("iasm: memory read 1 byte past the boundary")
   285              return
   286          } else {
   287              println(fmt.Sprintf("iasm: memory read %d bytes past the boundary", diff))
   288              return
   289          }
   290      }
   291  
   292      /* dump the content */
   293      print(hexdump(
   294          mem.buf()[off:off + nbs],
   295          mem.addr,
   296      ))
   297  }
   298  
   299  func (self *IASM) _cmd_write(v string) {
   300      var ok  bool
   301      var val string
   302      var off uint64
   303      var mid uint64
   304      var mem _Memory
   305  
   306      /* parse the memory ID, offset, filling byte and the optional size */
   307      scan(v).idoff(&mid, &off).str(&val).close()
   308      nbs := uint64(len(val))
   309  
   310      /* find the memory block */
   311      if mem, ok = self.mem[mid]; !ok {
   312          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   313          return
   314      }
   315  
   316      /* check the offset and size */
   317      if off + nbs > mem.size {
   318          if diff := off + nbs - mem.size; diff == 1 {
   319              println("iasm: memory fill 1 byte past the boundary")
   320          } else {
   321              println(fmt.Sprintf("iasm: memory fill %d bytes past the boundary", diff))
   322          }
   323      }
   324  
   325      /* copy the data into memory */
   326      copy(mem.buf()[off:], val)
   327      println(fmt.Sprintf("%d bytes written into %#x+%#x", nbs, mem.addr, off))
   328  }
   329  
   330  func (self *IASM) _cmd_fill(v string) {
   331      var ok  bool
   332      var val uint64
   333      var off uint64
   334      var mid uint64
   335      var mem _Memory
   336  
   337      /* parse the memory ID, offset, filling byte and the optional size */
   338      nbs := uint64(math.MaxUint64)
   339      scan(v).idoff(&mid, &off).uint(&val).uintopt(&nbs).close()
   340  
   341      /* check the filling value */
   342      if val > 255 {
   343          println(fmt.Sprintf("iasm: invalid filling value: %d is not a byte", val))
   344          return
   345      }
   346  
   347      /* find the memory block */
   348      if mem, ok = self.mem[mid]; !ok {
   349          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   350          return
   351      }
   352  
   353      /* read the whole block if not specified */
   354      if nbs == math.MaxUint64 {
   355          nbs = mem.size - off
   356      }
   357  
   358      /* check the offset and size */
   359      if off + nbs > mem.size {
   360          if diff := off + nbs - mem.size; diff == 1 {
   361              println("iasm: memory fill 1 byte past the boundary")
   362          } else {
   363              println(fmt.Sprintf("iasm: memory fill %d bytes past the boundary", diff))
   364          }
   365      }
   366  
   367      /* fill the memory with byte */
   368      for i := off; i < off + nbs; i++ {
   369          *(*byte)(unsafe.Pointer(uintptr(mem.p()) + uintptr(i))) = byte(val)
   370      }
   371  }
   372  
   373  func (self *IASM) _cmd_regs(v string) {
   374      regs := _regs.Dump(13)
   375      sels := map[string]bool{}
   376  
   377      /* check for register selectors */
   378      if fv := strings.Fields(v); len(fv) != 0 {
   379          for _, x := range fv {
   380              sels[strings.ToLower(x)] = true
   381          }
   382      }
   383  
   384      /* dump the registers */
   385      for _, reg := range regs {
   386          if v == "*" || sels[reg.reg] || (!reg.vec && len(sels) == 0) {
   387              println(fmt.Sprintf("%10s = %s", reg.reg, reg.val))
   388          }
   389      }
   390  }
   391  
   392  func (self *IASM) _cmd_asm(v string) {
   393      var ok  bool
   394      var err error
   395      var off uint64
   396      var mid uint64
   397      var fnv uintptr
   398      var mem _Memory
   399  
   400      /* parse the memory ID, offset, filling byte and the optional size */
   401      scan  (v).
   402      idoff (&mid, &off).
   403      close ()
   404  
   405      /* find the memory block */
   406      if mem, ok = self.mem[mid]; !ok {
   407          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   408          return
   409      }
   410  
   411      /* check for memory boundary */
   412      if fnv = mem.addr + uintptr(off); off >= mem.size {
   413          println("iasm: indexing past the end of memory")
   414          return
   415      }
   416  
   417      /* prompt messages */
   418      println(fmt.Sprintf("Assemble in memory block #(%d)+%#x (%#x).", mid, off, fnv))
   419      println(`Type ".end" and ENTER to end the assembly session.`)
   420  
   421      /* restore prompt later */
   422      buf := []byte(nil)
   423      rem := mem.size - off
   424      defer self.efd.SetLeftPrompt(">>> ")
   425      self.efd.SetLeftPrompt(fmt.Sprintf("(%#x) ", fnv))
   426  
   427      /* read and assemble the assembly source */
   428      for {
   429          src := strings.TrimSuffix(self.readLine(), "\n")
   430          val := strings.TrimSpace(src)
   431  
   432          /* check for end of assembly */
   433          if val == ".end" {
   434              break
   435          }
   436  
   437          /* feed into the assembler */
   438          if buf, err = self.ias.doasm(fnv, src); err != nil {
   439              println("iasm: assembly failed: " + err.Error())
   440              continue
   441          }
   442  
   443          /* check for memory space */
   444          if rem < uint64(len(buf)) {
   445              println(fmt.Sprintf("iasm: no space left in memory block: %d < %d", rem, len(buf)))
   446              continue
   447          }
   448  
   449          /* update the input line if stdout is a terminal */
   450          if isatty(os.Stdout.Fd()) {
   451              println("\x1b[F\x1b[K" + asmdump(buf, fnv, src))
   452          }
   453  
   454          /* save the machine code */
   455          ptr := mem.buf()
   456          copy(ptr[off:], buf)
   457  
   458          /* update the prompt and offsets */
   459          rem -= uint64(len(buf))
   460          off += uint64(len(buf))
   461          fnv += uintptr(len(buf))
   462          self.efd.SetLeftPrompt(fmt.Sprintf("(%#x) ", fnv))
   463      }
   464  }
   465  
   466  func (self *IASM) _cmd_sys(v string) {
   467      var ok  bool
   468      var err error
   469      var off uint64
   470      var mid uint64
   471      var fnv uintptr
   472      var mem _Memory
   473      var rs0 _RegFile
   474      var rs1 _RegFile
   475  
   476      /* parse the memory ID, offset, filling byte and the optional size */
   477      scan  (v).
   478      idoff (&mid, &off).
   479      close ()
   480  
   481      /* find the memory block */
   482      if mem, ok = self.mem[mid]; !ok {
   483          println(fmt.Sprintf("iasm: no such memory block with ID %d", mid))
   484          return
   485      }
   486  
   487      /* check for memory boundary */
   488      if fnv = mem.addr + uintptr(off); off >= mem.size {
   489          println("iasm: indexing past the end of memory")
   490          return
   491      }
   492  
   493      /* execute the code */
   494      if rs0, rs1, err = _exec.Execute(fnv); err != nil {
   495          println(fmt.Sprintf("iasm: cannot execute at memory address %#x: %s", fnv, err))
   496          return
   497      }
   498  
   499      /* print the differences */
   500      for _, diff := range rs0.Compare(rs1, 13) {
   501          println(fmt.Sprintf("%10s = %s", diff.reg, diff.val))
   502      }
   503  }
   504  
   505  func (self *IASM) _cmd_base(v string) {
   506      scan(v).uint((*uint64)(unsafe.Pointer(&self.off))).close()
   507  }
   508  
   509  func (self *IASM) _cmd_exit(_ string) {
   510      self.run = false
   511  }
   512  
   513  func (self *IASM) _cmd_help(_ string) {
   514      println("Supported commands:")
   515      println("    .free   ID ........................ Free a block of memory with ID.")
   516      println()
   517      println("    .malloc ID [SIZE] ................. Allocate a block of memory with ID of")
   518      println("                                        SIZE bytes if specified, or one page of")
   519      println("                                        memory if SIZE is not present.")
   520      println()
   521      println("    .info   ID ........................ Print basic informations of a memory")
   522      println("                                        block identified by ID.")
   523      println()
   524      println("    .read   ID[+OFF] [SIZE] ........... Read a block of memory identified by")
   525      println("                                        ID[+OFF] of SIZE bytes, default to the")
   526      println("                                        whole block if SIZE is not set.")
   527      println()
   528      println("    .write  ID[+OFF] DATA ............. Write DATA into a block of memory")
   529      println("                                        identified by ID[+OFF].")
   530      println()
   531      println("    .fill   ID[+OFF] BYTE [SIZE] ...... Fill a block of memory identified by")
   532      println("                                        ID[+OFF] with BYTE of SIZE bytes,")
   533      println("                                        default to the whole block if SIZE is")
   534      println("                                        not set.")
   535      println()
   536      println("    .regs   [REG*] .................... Print the content of the specified")
   537      println("                                        registers, default to general purpose")
   538      println("                                        registers if not specified. To also")
   539      println(`                                        include SIMD registers, type ".regs *".`)
   540      println()
   541      println("    .asm    ID[+OFF] .................. Assemble into memory block identified by")
   542      println("                                        ID[+OFF].")
   543      println()
   544      println("    .sys    ID[+OFF] .................. Execute code in memory block identified")
   545      println("                                        by ID[+OFF] with the CALL instruction.")
   546      println()
   547      println("    .base   [BASE] .................... Set the base address for immediate")
   548      println("                                        assembling mode, just for display.")
   549      println()
   550      println("    .exit ............................. Exit Interactive Assembler.")
   551      println("    .help ............................. This help message.")
   552      println()
   553  }