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 }