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
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
29 var HistoryFile = path.Clean(os.ExpandEnv("$HOME/.iasmhistory"))
30
31
32 func (self *IASM) Start() {
33 var err error
34 var efd libedit.EditLine
35
36
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
43 if efd, err = libedit.Init("iasm", false); err != nil {
44 panic(err)
45 }
46
47
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
55 defer self.efd.Close()
56 self.efd.RebindControlKeys()
57
58
59 _ = self.efd.UseHistory(-1, true)
60 _ = self.efd.LoadHistory(HistoryFile)
61
62
63 self.efd.SetLeftPrompt(">>> ")
64 self.efd.SetAutoSaveHistory(HistoryFile, true)
65
66
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
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
88 if ret == "" {
89 return ""
90 }
91
92
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
120 if cmd == "" {
121 return
122 }
123
124
125 if pos = strings.IndexFunc(cmd, unicode.IsSpace); pos == -1 {
126 pos = len(cmd)
127 }
128
129
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
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
155 if buf, err = self.ias.doasm(self.off, asm); err != nil {
156 println("iasm: " + err.Error())
157 return
158 }
159
160
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
187 scan (v).
188 uint (&mid).
189 close ()
190
191
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
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
212 scan (v).
213 uint (&mid).
214 uintopt (&nbs).
215 close ()
216
217
218 if nbs == 0 {
219 nbs = _PageSize
220 }
221
222
223 if _, ok := self.mem[mid]; ok {
224 println(fmt.Sprintf("iasm: duplicated memory ID: %d", mid))
225 return
226 }
227
228
229 if mem, err = mmap(nbs); err != nil {
230 println("iasm: mmap(): " + err.Error())
231 return
232 }
233
234
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
245 scan (v).
246 uint (&mid).
247 close ()
248
249
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
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
267 nbs := uint64(math.MaxUint64)
268 scan(v).idoff(&mid, &off).uintopt(&nbs).close()
269
270
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
277 if nbs == math.MaxUint64 {
278 nbs = mem.size - off
279 }
280
281
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
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
307 scan(v).idoff(&mid, &off).str(&val).close()
308 nbs := uint64(len(val))
309
310
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
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
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
338 nbs := uint64(math.MaxUint64)
339 scan(v).idoff(&mid, &off).uint(&val).uintopt(&nbs).close()
340
341
342 if val > 255 {
343 println(fmt.Sprintf("iasm: invalid filling value: %d is not a byte", val))
344 return
345 }
346
347
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
354 if nbs == math.MaxUint64 {
355 nbs = mem.size - off
356 }
357
358
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
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
378 if fv := strings.Fields(v); len(fv) != 0 {
379 for _, x := range fv {
380 sels[strings.ToLower(x)] = true
381 }
382 }
383
384
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
401 scan (v).
402 idoff (&mid, &off).
403 close ()
404
405
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
412 if fnv = mem.addr + uintptr(off); off >= mem.size {
413 println("iasm: indexing past the end of memory")
414 return
415 }
416
417
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
422 buf := []byte(nil)
423 rem := mem.size - off
424 defer self.efd.SetLeftPrompt(">>> ")
425 self.efd.SetLeftPrompt(fmt.Sprintf("(%#x) ", fnv))
426
427
428 for {
429 src := strings.TrimSuffix(self.readLine(), "\n")
430 val := strings.TrimSpace(src)
431
432
433 if val == ".end" {
434 break
435 }
436
437
438 if buf, err = self.ias.doasm(fnv, src); err != nil {
439 println("iasm: assembly failed: " + err.Error())
440 continue
441 }
442
443
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
450 if isatty(os.Stdout.Fd()) {
451 println("\x1b[F\x1b[K" + asmdump(buf, fnv, src))
452 }
453
454
455 ptr := mem.buf()
456 copy(ptr[off:], buf)
457
458
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
477 scan (v).
478 idoff (&mid, &off).
479 close ()
480
481
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
488 if fnv = mem.addr + uintptr(off); off >= mem.size {
489 println("iasm: indexing past the end of memory")
490 return
491 }
492
493
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
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 }
554
View as plain text