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