https://github.com/akkartik/mu/blob/main/shell/sandbox.mu
  1 type sandbox {
  2   data: (handle gap-buffer)
  3   value: (handle stream byte)
  4   screen-var: (handle cell)
  5   keyboard-var: (handle cell)
  6   trace: (handle trace)
  7   cursor-in-data?: boolean
  8   cursor-in-keyboard?: boolean
  9   cursor-in-trace?: boolean
 10 }
 11 
 12 fn initialize-sandbox _self: (addr sandbox), screen-and-keyboard?: boolean {
 13   var self/esi: (addr sandbox) <- copy _self
 14   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 15   allocate data-ah
 16   var data/eax: (addr gap-buffer) <- lookup *data-ah
 17   initialize-gap-buffer data, 0x1000/4KB
 18   #
 19   var value-ah/eax: (addr handle stream byte) <- get self, value
 20   populate-stream value-ah, 0x1000/4KB
 21   #
 22   {
 23     compare screen-and-keyboard?, 0/false
 24     break-if-=
 25     var screen-ah/eax: (addr handle cell) <- get self, screen-var
 26     new-screen screen-ah, 5/width, 4/height
 27     var keyboard-ah/eax: (addr handle cell) <- get self, keyboard-var
 28     new-keyboard keyboard-ah, 0x10/keyboard-capacity
 29   }
 30   #
 31   var trace-ah/eax: (addr handle trace) <- get self, trace
 32   allocate trace-ah
 33   var trace/eax: (addr trace) <- lookup *trace-ah
 34   initialize-trace trace, 0x1000/lines, 0x80/visible-lines
 35   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 36   copy-to *cursor-in-data?, 1/true
 37 }
 38 
 39 ## some helpers for tests
 40 
 41 fn initialize-sandbox-with _self: (addr sandbox), s: (addr array byte) {
 42   var self/esi: (addr sandbox) <- copy _self
 43   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 44   allocate data-ah
 45   var data/eax: (addr gap-buffer) <- lookup *data-ah
 46   initialize-gap-buffer-with data, s
 47 }
 48 
 49 fn allocate-sandbox-with _out: (addr handle sandbox), s: (addr array byte) {
 50   var out/eax: (addr handle sandbox) <- copy _out
 51   allocate out
 52   var out-addr/eax: (addr sandbox) <- lookup *out
 53   initialize-sandbox-with out-addr, s
 54 }
 55 
 56 ##
 57 
 58 fn render-sandbox screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int, globals: (addr global-table) {
 59   clear-rect screen, xmin, ymin, xmax, ymax, 0/bg=black
 60   var self/esi: (addr sandbox) <- copy _self
 61   # data
 62   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 63   var _data/eax: (addr gap-buffer) <- lookup *data-ah
 64   var data/edx: (addr gap-buffer) <- copy _data
 65   var x/eax: int <- copy xmin
 66   var y/ecx: int <- copy ymin
 67   y <- maybe-render-empty-screen screen, self, xmin, y
 68   y <- maybe-render-keyboard screen, self, xmin, y
 69   var cursor-in-sandbox?/ebx: (addr boolean) <- get self, cursor-in-data?
 70   x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, *cursor-in-sandbox?
 71   y <- increment
 72   # trace
 73   var trace-ah/eax: (addr handle trace) <- get self, trace
 74   var _trace/eax: (addr trace) <- lookup *trace-ah
 75   var trace/edx: (addr trace) <- copy _trace
 76   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 77   y <- render-trace screen, trace, xmin, y, xmax, ymax, *cursor-in-trace?
 78   # value
 79   $render-sandbox:value: {
 80     var value-ah/eax: (addr handle stream byte) <- get self, value
 81     var _value/eax: (addr stream byte) <- lookup *value-ah
 82     var value/esi: (addr stream byte) <- copy _value
 83     rewind-stream value
 84     var done?/eax: boolean <- stream-empty? value
 85     compare done?, 0/false
 86     break-if-!=
 87     var x/eax: int <- copy 0
 88     x, y <- draw-text-wrapping-right-then-down screen, "=> ", xmin, y, xmax, ymax, xmin, y, 7/fg, 0/bg
 89     var x2/edx: int <- copy x
 90     var dummy/eax: int <- draw-stream-rightward screen, value, x2, xmax, y, 7/fg=grey, 0/bg
 91   }
 92   y <- add 2  # padding
 93   y <- maybe-render-screen screen, self, xmin, y
 94   # render menu
 95   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 96   compare *cursor-in-data?, 0/false
 97   {
 98     break-if-=
 99     render-sandbox-menu screen, self
100     return
101   }
102   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
103   compare *cursor-in-trace?, 0/false
104   {
105     break-if-=
106     render-trace-menu screen
107     return
108   }
109   var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
110   compare *cursor-in-keyboard?, 0/false
111   {
112     break-if-=
113     render-keyboard-menu screen
114     return
115   }
116 }
117 
118 fn maybe-render-empty-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
119   var self/esi: (addr sandbox) <- copy _self
120   var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
121   var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
122   compare screen-obj-cell, 0
123   {
124     break-if-!=
125     return ymin
126   }
127   var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
128   compare *screen-obj-cell-type, 5/screen
129   {
130     break-if-=
131     return ymin  # silently give up on rendering the screen
132   }
133   var y/ecx: int <- copy ymin
134   var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
135   var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
136   var screen-obj/edx: (addr screen) <- copy _screen-obj
137   var x/eax: int <- draw-text-rightward screen, "screen:   ", xmin, 0x99/xmax, y, 7/fg, 0/bg
138   y <- render-empty-screen screen, screen-obj, x, y
139   return y
140 }
141 
142 fn maybe-render-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
143   var self/esi: (addr sandbox) <- copy _self
144   var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
145   var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
146   compare screen-obj-cell, 0
147   {
148     break-if-!=
149     return ymin
150   }
151   var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
152   compare *screen-obj-cell-type, 5/screen
153   {
154     break-if-=
155     return ymin  # silently give up on rendering the screen
156   }
157   var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
158   var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
159   var screen-obj/edx: (addr screen) <- copy _screen-obj
160   {
161     var screen-empty?/eax: boolean <- fake-screen-empty? screen-obj
162     compare screen-empty?, 0/false
163     break-if-=
164     return ymin
165   }
166   var x/eax: int <- draw-text-rightward screen, "screen:   ", xmin, 0x99/xmax, ymin, 7/fg, 0/bg
167   var y/ecx: int <- copy ymin
168   y <- render-screen screen, screen-obj, x, y
169   return y
170 }
171 
172 fn render-empty-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
173   var target-screen/esi: (addr screen) <- copy _target-screen
174   var screen-y/edi: int <- copy ymin
175   # top border
176   {
177     set-cursor-position screen, xmin, screen-y
178     move-cursor-right screen
179     var width/edx: (addr int) <- get target-screen, width
180     var x/ebx: int <- copy 0
181     {
182       compare x, *width
183       break-if->=
184       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
185       move-cursor-right screen
186       x <- increment
187       loop
188     }
189     screen-y <- increment
190   }
191   # screen
192   var height/edx: (addr int) <- get target-screen, height
193   var y/ecx: int <- copy 0
194   {
195     compare y, *height
196     break-if->=
197     set-cursor-position screen, xmin, screen-y
198     draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
199     move-cursor-right screen
200     var width/edx: (addr int) <- get target-screen, width
201     var x/ebx: int <- copy 0
202     {
203       compare x, *width
204       break-if->=
205       draw-code-point-at-cursor screen, 0x20/space, 0x18/fg, 0/bg
206       move-cursor-right screen
207       x <- increment
208       loop
209     }
210     draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
211     y <- increment
212     screen-y <- increment
213     loop
214   }
215   # bottom border
216   {
217     set-cursor-position screen, xmin, screen-y
218     move-cursor-right screen
219     var width/edx: (addr int) <- get target-screen, width
220     var x/ebx: int <- copy 0
221     {
222       compare x, *width
223       break-if->=
224       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
225       move-cursor-right screen
226       x <- increment
227       loop
228     }
229     screen-y <- increment
230   }
231   return screen-y
232 }
233 
234 fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
235   var target-screen/esi: (addr screen) <- copy _target-screen
236   var screen-y/edi: int <- copy ymin
237   # top border
238   {
239     set-cursor-position screen, xmin, screen-y
240     move-cursor-right screen
241     var width/edx: (addr int) <- get target-screen, width
242     var x/ebx: int <- copy 0
243     {
244       compare x, *width
245       break-if->=
246       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
247       move-cursor-right screen
248       x <- increment
249       loop
250     }
251     screen-y <- increment
252   }
253   # screen
254   var height/edx: (addr int) <- get target-screen, height
255   var y/ecx: int <- copy 0
256   {
257     compare y, *height
258     break-if->=
259     set-cursor-position screen, xmin, screen-y
260     draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
261     move-cursor-right screen
262     var width/edx: (addr int) <- get target-screen, width
263     var x/ebx: int <- copy 0
264     {
265       compare x, *width
266       break-if->=
267       print-screen-cell-of-fake-screen screen, target-screen, x, y
268       move-cursor-right screen
269       x <- increment
270       loop
271     }
272     draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
273     y <- increment
274     screen-y <- increment
275     loop
276   }
277   # bottom border
278   {
279     set-cursor-position screen, xmin, screen-y
280     move-cursor-right screen
281     var width/edx: (addr int) <- get target-screen, width
282     var x/ebx: int <- copy 0
283     {
284       compare x, *width
285       break-if->=
286       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
287       move-cursor-right screen
288       x <- increment
289       loop
290     }
291     screen-y <- increment
292   }
293   return screen-y
294 }
295 
296 fn has-keyboard? _self: (addr sandbox) -> _/eax: boolean {
297   var self/esi: (addr sandbox) <- copy _self
298   var keyboard-obj-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
299   var keyboard-obj-cell/eax: (addr cell) <- lookup *keyboard-obj-cell-ah
300   compare keyboard-obj-cell, 0
301   {
302     break-if-!=
303     return 0/false
304   }
305   var keyboard-obj-cell-type/ecx: (addr int) <- get keyboard-obj-cell, type
306   compare *keyboard-obj-cell-type, 6/keyboard
307   {
308     break-if-=
309     return 0/false
310   }
311   var keyboard-obj-ah/eax: (addr handle gap-buffer) <- get keyboard-obj-cell, keyboard-data
312   var _keyboard-obj/eax: (addr gap-buffer) <- lookup *keyboard-obj-ah
313   var keyboard-obj/edx: (addr gap-buffer) <- copy _keyboard-obj
314   compare keyboard-obj, 0
315   {
316     break-if-!=
317     return 0/false
318   }
319   return 1/true
320 }
321 
322 fn maybe-render-keyboard screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
323   var self/esi: (addr sandbox) <- copy _self
324   var keyboard-obj-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
325   var keyboard-obj-cell/eax: (addr cell) <- lookup *keyboard-obj-cell-ah
326   compare keyboard-obj-cell, 0
327   {
328     break-if-!=
329     return ymin
330   }
331   var keyboard-obj-cell-type/ecx: (addr int) <- get keyboard-obj-cell, type
332   compare *keyboard-obj-cell-type, 6/keyboard
333   {
334     break-if-=
335     return ymin  # silently give up on rendering the keyboard
336   }
337   var keyboard-obj-ah/eax: (addr handle gap-buffer) <- get keyboard-obj-cell, keyboard-data
338   var _keyboard-obj/eax: (addr gap-buffer) <- lookup *keyboard-obj-ah
339   var keyboard-obj/edx: (addr gap-buffer) <- copy _keyboard-obj
340   var x/eax: int <- draw-text-rightward screen, "keyboard: ", xmin, 0x99/xmax, ymin, 7/fg, 0/bg
341   var y/ecx: int <- copy ymin
342   var cursor-in-keyboard?/esi: (addr boolean) <- get self, cursor-in-keyboard?
343   y <- render-keyboard screen, keyboard-obj, x, y, *cursor-in-keyboard?
344   y <- increment  # padding
345   return y
346 }
347 
348 # draw an evocative shape
349 fn render-keyboard screen: (addr screen), _keyboard: (addr gap-buffer), xmin: int, ymin: int, render-cursor?: boolean -> _/ecx: int {
350   var keyboard/esi: (addr gap-buffer) <- copy _keyboard
351   var width/edx: int <- copy 0x10/keyboard-capacity
352   var y/edi: int <- copy ymin
353   # top border
354   {
355     set-cursor-position screen, xmin, y
356     move-cursor-right screen
357     var x/ebx: int <- copy 0
358     {
359       compare x, width
360       break-if->=
361       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
362       move-cursor-right screen
363       x <- increment
364       loop
365     }
366     y <- increment
367   }
368   # keyboard
369   var x/eax: int <- copy xmin
370   draw-code-point screen, 0x7c/vertical-bar, x, y, 0x18/fg, 0/bg
371   x <- increment
372   x <- render-gap-buffer screen, keyboard, x, y, render-cursor?
373   x <- copy xmin
374   x <- add 1  # for left bar
375   x <- add 0x10/keyboard-capacity
376   draw-code-point screen, 0x7c/vertical-bar, x, y, 0x18/fg, 0/bg
377   y <- increment
378   # bottom border
379   {
380     set-cursor-position screen, xmin, y
381     move-cursor-right screen
382     var x/ebx: int <- copy 0
383     {
384       compare x, width
385       break-if->=
386       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
387       move-cursor-right screen
388       x <- increment
389       loop
390     }
391     y <- increment
392   }
393   return y
394 }
395 
396 fn print-screen-cell-of-fake-screen screen: (addr screen), _target: (addr screen), x: int, y: int {
397   var target/ecx: (addr screen) <- copy _target
398   var data-ah/eax: (addr handle array screen-cell) <- get target, data
399   var data/eax: (addr array screen-cell) <- lookup *data-ah
400   var index/ecx: int <- screen-cell-index target, x, y
401   var offset/ecx: (offset screen-cell) <- compute-offset data, index
402   var src-cell/esi: (addr screen-cell) <- index data, offset
403   var src-grapheme/eax: (addr grapheme) <- get src-cell, data
404   var src-color/ecx: (addr int) <- get src-cell, color
405   var src-background-color/edx: (addr int) <- get src-cell, background-color
406   draw-grapheme-at-cursor screen, *src-grapheme, *src-color, *src-background-color
407 }
408 
409 fn render-sandbox-menu screen: (addr screen), _self: (addr sandbox) {
410   var _width/eax: int <- copy 0
411   var height/ecx: int <- copy 0
412   _width, height <- screen-size screen
413   var width/edx: int <- copy _width
414   var y/ecx: int <- copy height
415   y <- decrement
416   var height/ebx: int <- copy y
417   height <- increment
418   clear-rect screen, 0/x, y, width, height, 0/bg=black
419   set-cursor-position screen, 0/x, y
420   draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey
421   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0/bg
422   $render-sandbox-menu:render-tab: {
423     var self/eax: (addr sandbox) <- copy _self
424     var has-trace?/eax: boolean <- has-trace? self
425     compare has-trace?, 0/false
426     {
427       break-if-=
428       draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 9/bg=blue
429       draw-text-rightward-from-cursor screen, " move to trace  ", width, 7/fg, 0/bg
430       break $render-sandbox-menu:render-tab
431     }
432     draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 0x18/bg=keyboard
433     draw-text-rightward-from-cursor screen, " move to keyboard  ", width, 7/fg, 0/bg
434   }
435 }
436 
437 fn render-keyboard-menu screen: (addr screen) {
438   var width/eax: int <- copy 0
439   var height/ecx: int <- copy 0
440   width, height <- screen-size screen
441   var y/ecx: int <- copy height
442   y <- decrement
443   var height/edx: int <- copy y
444   height <- increment
445   clear-rect screen, 0/x, y, width, height, 0/bg=black
446   set-cursor-position screen, 0/x, y
447   draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey
448   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0/bg
449   draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 3/bg=cyan
450   draw-text-rightward-from-cursor screen, " move to sandbox  ", width, 7/fg, 0/bg
451 }
452 
453 fn edit-sandbox _self: (addr sandbox), key: byte, globals: (addr global-table), real-screen: (addr screen), real-keyboard: (addr keyboard), data-disk: (addr disk) {
454   var self/esi: (addr sandbox) <- copy _self
455   var g/edx: grapheme <- copy key
456   # ctrl-r
457   {
458     compare g, 0x12/ctrl-r
459     break-if-!=
460     # run function outside sandbox
461     # required: fn (addr screen), (addr keyboard)
462     # Mu will pass in the real screen and keyboard.
463     return
464   }
465   # ctrl-s
466   {
467     compare g, 0x13/ctrl-s
468     break-if-!=
469     # save to disk
470     var data-ah/eax: (addr handle gap-buffer) <- get self, data
471     var _data/eax: (addr gap-buffer) <- lookup *data-ah
472     var data/ecx: (addr gap-buffer) <- copy _data
473     {
474       compare data-disk, 0/no-disk
475       break-if-=
476       var stream-storage: (stream byte 0x200)
477       var stream/esi: (addr stream byte) <- address stream-storage
478       emit-gap-buffer data, stream
479       store-sector data-disk, 0/lba, stream
480     }
481     # run sandbox
482     var value-ah/eax: (addr handle stream byte) <- get self, value
483     var _value/eax: (addr stream byte) <- lookup *value-ah
484     var value/edx: (addr stream byte) <- copy _value
485     var trace-ah/eax: (addr handle trace) <- get self, trace
486     var _trace/eax: (addr trace) <- lookup *trace-ah
487     var trace/ebx: (addr trace) <- copy _trace
488     clear-trace trace
489     var screen-cell/eax: (addr handle cell) <- get self, screen-var
490     clear-screen-cell screen-cell
491     var keyboard-cell/esi: (addr handle cell) <- get self, keyboard-var
492     rewind-keyboard-cell keyboard-cell  # don't clear keys from before
493     run data, value, globals, trace, screen-cell, keyboard-cell
494     return
495   }
496   # tab
497   {
498     compare g, 9/tab
499     break-if-!=
500     # if cursor in data, switch to trace or fall through to keyboard
501     {
502       var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
503       compare *cursor-in-data?, 0/false
504       break-if-=
505       var has-trace?/eax: boolean <- has-trace? self
506       compare has-trace?, 0/false
507       {
508         break-if-=
509         var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
510         copy-to *cursor-in-data?, 0/false
511         var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
512         copy-to *cursor-in-trace?, 1/false
513         return
514       }
515       var has-keyboard?/eax: boolean <- has-keyboard? self
516       compare has-keyboard?, 0/false
517       {
518         break-if-=
519         var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
520         copy-to *cursor-in-data?, 0/false
521         var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
522         copy-to *cursor-in-keyboard?, 1/false
523         return
524       }
525       return
526     }
527     # if cursor in trace, switch to keyboard or fall through to data
528     {
529       var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
530       compare *cursor-in-trace?, 0/false
531       break-if-=
532       copy-to *cursor-in-trace?, 0/false
533       var cursor-target/ecx: (addr boolean) <- get self, cursor-in-keyboard?
534       var has-keyboard?/eax: boolean <- has-keyboard? self
535       compare has-keyboard?, 0/false
536       {
537         break-if-!=
538         cursor-target <- get self, cursor-in-data?
539       }
540       copy-to *cursor-target, 1/true
541       return
542     }
543     # otherwise if cursor in keyboard, switch to data
544     {
545       var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
546       compare *cursor-in-keyboard?, 0/false
547       break-if-=
548       copy-to *cursor-in-keyboard?, 0/false
549       var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
550       copy-to *cursor-in-data?, 1/true
551       return
552     }
553     return
554   }
555   # if cursor in data, send key to data
556   {
557     var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
558     compare *cursor-in-data?, 0/false
559     break-if-=
560     var data-ah/eax: (addr handle gap-buffer) <- get self, data
561     var data/eax: (addr gap-buffer) <- lookup *data-ah
562     edit-gap-buffer data, g
563     return
564   }
565   # if cursor in keyboard, send key to keyboard
566   {
567     var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
568     compare *cursor-in-keyboard?, 0/false
569     break-if-=
570     var keyboard-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
571     var keyboard-cell/eax: (addr cell) <- lookup *keyboard-cell-ah
572     compare keyboard-cell, 0
573     {
574       break-if-!=
575       return
576     }
577     var keyboard-cell-type/ecx: (addr int) <- get keyboard-cell, type
578     compare *keyboard-cell-type, 6/keyboard
579     {
580       break-if-=
581       return
582     }
583     var keyboard-ah/eax: (addr handle gap-buffer) <- get keyboard-cell, keyboard-data
584     var keyboard/eax: (addr gap-buffer) <- lookup *keyboard-ah
585     edit-gap-buffer keyboard, g
586     return
587   }
588   # if cursor in trace, send key to trace
589   {
590     var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
591     compare *cursor-in-trace?, 0/false
592     break-if-=
593     var trace-ah/eax: (addr handle trace) <- get self, trace
594     var trace/eax: (addr trace) <- lookup *trace-ah
595     edit-trace trace, g
596     return
597   }
598 }
599 
600 fn run in: (addr gap-buffer), out: (addr stream byte), globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell) {
601   var read-result-storage: (handle cell)
602   var read-result/esi: (addr handle cell) <- address read-result-storage
603   read-cell in, read-result, trace
604   var error?/eax: boolean <- has-errors? trace
605   {
606     compare error?, 0/false
607     break-if-=
608     return
609   }
610   var nil-storage: (handle cell)
611   var nil-ah/eax: (addr handle cell) <- address nil-storage
612   allocate-pair nil-ah
613   var eval-result-storage: (handle cell)
614   var eval-result/edi: (addr handle cell) <- address eval-result-storage
615   evaluate read-result, eval-result, *nil-ah, globals, trace, screen-cell, keyboard-cell
616   var error?/eax: boolean <- has-errors? trace
617   {
618     compare error?, 0/false
619     break-if-=
620     return
621   }
622   clear-stream out
623   print-cell eval-result, out, trace
624   mark-lines-dirty trace
625 }
626 
627 fn test-run-integer {
628   var sandbox-storage: sandbox
629   var sandbox/esi: (addr sandbox) <- address sandbox-storage
630   initialize-sandbox sandbox, 0/no-screen-or-keyboard
631   # type "1"
632   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
633   # eval
634   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
635   # setup: screen
636   var screen-on-stack: screen
637   var screen/edi: (addr screen) <- address screen-on-stack
638   initialize-screen screen, 0x80/width, 0x10/height
639   #
640   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
641   check-screen-row screen, 0/y, "1    ", "F - test-run-integer/0"
642   check-screen-row screen, 1/y, "...  ", "F - test-run-integer/1"
643   check-screen-row screen, 2/y, "=> 1 ", "F - test-run-integer/2"
644 }
645 
646 fn test-run-with-spaces {
647   var sandbox-storage: sandbox
648   var sandbox/esi: (addr sandbox) <- address sandbox-storage
649   initialize-sandbox sandbox, 0/no-screen-or-keyboard
650   # type input with whitespace before and after
651   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
652   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
653   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
654   edit-sandbox sandbox, 0xa/newline, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
655   # eval
656   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
657   # setup: screen
658   var screen-on-stack: screen
659   var screen/edi: (addr screen) <- address screen-on-stack
660   initialize-screen screen, 0x80/width, 0x10/height
661   #
662   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
663   check-screen-row screen, 0/y, " 1   ", "F - test-run-with-spaces/0"
664   check-screen-row screen, 1/y, "     ", "F - test-run-with-spaces/1"
665   check-screen-row screen, 2/y, "...  ", "F - test-run-with-spaces/2"
666   check-screen-row screen, 3/y, "=> 1 ", "F - test-run-with-spaces/3"
667 }
668 
669 fn test-run-quote {
670   var sandbox-storage: sandbox
671   var sandbox/esi: (addr sandbox) <- address sandbox-storage
672   initialize-sandbox sandbox, 0/no-screen-or-keyboard
673   # type "'a"
674   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
675   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
676   # eval
677   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
678   # setup: screen
679   var screen-on-stack: screen
680   var screen/edi: (addr screen) <- address screen-on-stack
681   initialize-screen screen, 0x80/width, 0x10/height
682   #
683   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
684   check-screen-row screen, 0/y, "'a   ", "F - test-run-quote/0"
685   check-screen-row screen, 1/y, "...  ", "F - test-run-quote/1"
686   check-screen-row screen, 2/y, "=> a ", "F - test-run-quote/2"
687 }
688 
689 fn test-run-error-invalid-integer {
690   var sandbox-storage: sandbox
691   var sandbox/esi: (addr sandbox) <- address sandbox-storage
692   initialize-sandbox sandbox, 0/no-screen-or-keyboard
693   # type "1a"
694   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
695   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
696   # eval
697   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
698   # setup: screen
699   var screen-on-stack: screen
700   var screen/edi: (addr screen) <- address screen-on-stack
701   initialize-screen screen, 0x80/width, 0x10/height
702   #
703   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
704   check-screen-row screen, 0/y, "1a             ", "F - test-run-error-invalid-integer/0"
705   check-screen-row screen, 1/y, "...            ", "F - test-run-error-invalid-integer/0"
706   check-screen-row screen, 2/y, "invalid number ", "F - test-run-error-invalid-integer/2"
707 }
708 
709 fn test-run-move-cursor-into-trace {
710   var sandbox-storage: sandbox
711   var sandbox/esi: (addr sandbox) <- address sandbox-storage
712   initialize-sandbox sandbox, 0/no-screen-or-keyboard
713   # type "12"
714   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
715   edit-sandbox sandbox, 0x32/2, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
716   # eval
717   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
718   # setup: screen
719   var screen-on-stack: screen
720   var screen/edi: (addr screen) <- address screen-on-stack
721   initialize-screen screen, 0x80/width, 0x10/height
722   #
723   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
724   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/pre-0"
725   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  |   ", "F - test-run-move-cursor-into-trace/pre-0/cursor"
726   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/pre-1"
727   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-run-move-cursor-into-trace/pre-1/cursor"
728   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/pre-2"
729   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/pre-2/cursor"
730   # move cursor into trace
731   edit-sandbox sandbox, 9/tab, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
732   #
733   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
734   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/trace-0"
735   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-run-move-cursor-into-trace/trace-0/cursor"
736   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/trace-1"
737   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||   ", "F - test-run-move-cursor-into-trace/trace-1/cursor"
738   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/trace-2"
739   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/trace-2/cursor"
740   # move cursor into input
741   edit-sandbox sandbox, 9/tab, 0/no-globals, 0/no-screen, 0/no-keyboard, 0/no-disk
742   #
743   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height, 0/no-globals
744   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/input-0"
745   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  |   ", "F - test-run-move-cursor-into-trace/input-0/cursor"
746   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/input-1"
747   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-run-move-cursor-into-trace/input-1/cursor"
748   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/input-2"
749   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/input-2/cursor"
750 }
751 
752 fn has-trace? _self: (addr sandbox) -> _/eax: boolean {
753   var self/esi: (addr sandbox) <- copy _self
754   var trace-ah/eax: (addr handle trace) <- get self, trace
755   var _trace/eax: (addr trace) <- lookup *trace-ah
756   var trace/edx: (addr trace) <- copy _trace
757   compare trace, 0
758   {
759     break-if-!=
760     return 0/false
761   }
762   var first-free/ebx: (addr int) <- get trace, first-free
763   compare *first-free, 0
764   {
765     break-if->
766     return 0/false
767   }
768   return 1/true
769 }