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), fake-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 fake-screen-and-keyboard?, 0/false
  24     break-if-=
  25     var screen-ah/eax: (addr handle cell) <- get self, screen-var
  26     new-fake-screen screen-ah, 5/width, 4/height, 1/enable-pixel-graphics
  27     var keyboard-ah/eax: (addr handle cell) <- get self, keyboard-var
  28     new-fake-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, 0x8000/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 fn write-sandbox out: (addr stream byte), _self: (addr sandbox) {
  57   var self/eax: (addr sandbox) <- copy _self
  58   var data-ah/eax: (addr handle gap-buffer) <- get self, data
  59   var data/eax: (addr gap-buffer) <- lookup *data-ah
  60   {
  61     var len/eax: int <- gap-buffer-length data
  62     compare len, 0
  63     break-if-!=
  64     return
  65   }
  66   write out, "  (sandbox . "
  67   append-gap-buffer data, out
  68   write out, ")\n"
  69 }
  70 
  71 ##
  72 
  73 fn render-sandbox screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int {
  74   clear-rect screen, xmin, ymin, xmax, ymax, 0/bg=black
  75   var self/esi: (addr sandbox) <- copy _self
  76   # data
  77   var data-ah/eax: (addr handle gap-buffer) <- get self, data
  78   var _data/eax: (addr gap-buffer) <- lookup *data-ah
  79   var data/edx: (addr gap-buffer) <- copy _data
  80   var x/eax: int <- copy xmin
  81   var y/ecx: int <- copy ymin
  82   y <- maybe-render-empty-screen screen, self, xmin, y
  83   y <- maybe-render-keyboard screen, self, xmin, y
  84   var cursor-in-sandbox?/ebx: (addr boolean) <- get self, cursor-in-data?
  85   x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, *cursor-in-sandbox?
  86   y <- increment
  87   # trace
  88   var trace-ah/eax: (addr handle trace) <- get self, trace
  89   var _trace/eax: (addr trace) <- lookup *trace-ah
  90   var trace/edx: (addr trace) <- copy _trace
  91   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
  92   y <- render-trace screen, trace, xmin, y, xmax, ymax, *cursor-in-trace?
  93   # value
  94   $render-sandbox:value: {
  95     var value-ah/eax: (addr handle stream byte) <- get self, value
  96     var _value/eax: (addr stream byte) <- lookup *value-ah
  97     var value/esi: (addr stream byte) <- copy _value
  98     rewind-stream value
  99     var done?/eax: boolean <- stream-empty? value
 100     compare done?, 0/false
 101     break-if-!=
 102     var x/eax: int <- copy 0
 103     x, y <- draw-text-wrapping-right-then-down screen, "=> ", xmin, y, xmax, ymax, xmin, y, 7/fg, 0/bg
 104     var x2/edx: int <- copy x
 105     var dummy/eax: int <- draw-stream-rightward screen, value, x2, xmax, y, 7/fg=grey, 0/bg
 106   }
 107   y <- add 2  # padding
 108   y <- maybe-render-screen screen, self, xmin, y
 109   # render menu
 110   var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 111   compare *cursor-in-data?, 0/false
 112   {
 113     break-if-=
 114     render-sandbox-menu screen, self
 115     return
 116   }
 117   var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 118   compare *cursor-in-trace?, 0/false
 119   {
 120     break-if-=
 121     render-trace-menu screen
 122     return
 123   }
 124   var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
 125   compare *cursor-in-keyboard?, 0/false
 126   {
 127     break-if-=
 128     render-keyboard-menu screen
 129     return
 130   }
 131 }
 132 
 133 fn clear-sandbox-output screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int, xmax: int, ymax: int {
 134   # render just enough of the sandbox to figure out what to erase
 135   var self/esi: (addr sandbox) <- copy _self
 136   var data-ah/eax: (addr handle gap-buffer) <- get self, data
 137   var _data/eax: (addr gap-buffer) <- lookup *data-ah
 138   var data/edx: (addr gap-buffer) <- copy _data
 139   var x/eax: int <- copy xmin
 140   var y/ecx: int <- copy ymin
 141   y <- maybe-render-empty-screen screen, self, xmin, y
 142   y <- maybe-render-keyboard screen, self, xmin, y
 143   var cursor-in-sandbox?/ebx: (addr boolean) <- get self, cursor-in-data?
 144   x, y <- render-gap-buffer-wrapping-right-then-down screen, data, x, y, xmax, ymax, *cursor-in-sandbox?
 145   y <- increment
 146   clear-rect screen, xmin, y, xmax, ymax, 0/bg=black
 147 }
 148 
 149 fn maybe-render-empty-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
 150   var self/esi: (addr sandbox) <- copy _self
 151   var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
 152   var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
 153   compare screen-obj-cell, 0
 154   {
 155     break-if-!=
 156     return ymin
 157   }
 158   var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
 159   compare *screen-obj-cell-type, 5/screen
 160   {
 161     break-if-=
 162     return ymin  # silently give up on rendering the screen
 163   }
 164   var y/ecx: int <- copy ymin
 165   var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
 166   var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
 167   var screen-obj/edx: (addr screen) <- copy _screen-obj
 168   var x/eax: int <- draw-text-rightward screen, "screen:   ", xmin, 0x99/xmax, y, 7/fg, 0/bg
 169   y <- render-empty-screen screen, screen-obj, x, y
 170   return y
 171 }
 172 
 173 fn maybe-render-screen screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
 174   var self/esi: (addr sandbox) <- copy _self
 175   var screen-obj-cell-ah/eax: (addr handle cell) <- get self, screen-var
 176   var screen-obj-cell/eax: (addr cell) <- lookup *screen-obj-cell-ah
 177   compare screen-obj-cell, 0
 178   {
 179     break-if-!=
 180     return ymin
 181   }
 182   var screen-obj-cell-type/ecx: (addr int) <- get screen-obj-cell, type
 183   compare *screen-obj-cell-type, 5/screen
 184   {
 185     break-if-=
 186     return ymin  # silently give up on rendering the screen
 187   }
 188   var screen-obj-ah/eax: (addr handle screen) <- get screen-obj-cell, screen-data
 189   var _screen-obj/eax: (addr screen) <- lookup *screen-obj-ah
 190   var screen-obj/edx: (addr screen) <- copy _screen-obj
 191   {
 192     var screen-empty?/eax: boolean <- fake-screen-empty? screen-obj
 193     compare screen-empty?, 0/false
 194     break-if-=
 195     return ymin
 196   }
 197   var x/eax: int <- draw-text-rightward screen, "screen:   ", xmin, 0x99/xmax, ymin, 7/fg, 0/bg
 198   var y/ecx: int <- copy ymin
 199   y <- render-screen screen, screen-obj, x, y
 200   return y
 201 }
 202 
 203 fn render-empty-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
 204   var target-screen/esi: (addr screen) <- copy _target-screen
 205   var screen-y/edi: int <- copy ymin
 206   # top border
 207   {
 208     set-cursor-position screen, xmin, screen-y
 209     move-cursor-right screen
 210     var width/edx: (addr int) <- get target-screen, width
 211     var x/ebx: int <- copy 0
 212     {
 213       compare x, *width
 214       break-if->=
 215       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
 216       move-cursor-right screen
 217       x <- increment
 218       loop
 219     }
 220     screen-y <- increment
 221   }
 222   # screen
 223   var height/edx: (addr int) <- get target-screen, height
 224   var y/ecx: int <- copy 0
 225   {
 226     compare y, *height
 227     break-if->=
 228     set-cursor-position screen, xmin, screen-y
 229     draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
 230     move-cursor-right screen
 231     var width/edx: (addr int) <- get target-screen, width
 232     var x/ebx: int <- copy 0
 233     {
 234       compare x, *width
 235       break-if->=
 236       draw-code-point-at-cursor screen, 0x20/space, 0x18/fg, 0/bg
 237       move-cursor-right screen
 238       x <- increment
 239       loop
 240     }
 241     draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
 242     y <- increment
 243     screen-y <- increment
 244     loop
 245   }
 246   # bottom border
 247   {
 248     set-cursor-position screen, xmin, screen-y
 249     move-cursor-right screen
 250     var width/edx: (addr int) <- get target-screen, width
 251     var x/ebx: int <- copy 0
 252     {
 253       compare x, *width
 254       break-if->=
 255       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
 256       move-cursor-right screen
 257       x <- increment
 258       loop
 259     }
 260     screen-y <- increment
 261   }
 262   return screen-y
 263 }
 264 
 265 fn render-screen screen: (addr screen), _target-screen: (addr screen), xmin: int, ymin: int -> _/ecx: int {
 266   var target-screen/esi: (addr screen) <- copy _target-screen
 267   var screen-y/edi: int <- copy ymin
 268   # top border
 269   {
 270     set-cursor-position screen, xmin, screen-y
 271     move-cursor-right screen
 272     var width/edx: (addr int) <- get target-screen, width
 273     var x/ebx: int <- copy 0
 274     {
 275       compare x, *width
 276       break-if->=
 277       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
 278       move-cursor-right screen
 279       x <- increment
 280       loop
 281     }
 282     screen-y <- increment
 283   }
 284   # text data
 285   {
 286     var height/edx: (addr int) <- get target-screen, height
 287     var y/ecx: int <- copy 0
 288     {
 289       compare y, *height
 290       break-if->=
 291       set-cursor-position screen, xmin, screen-y
 292       draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
 293       move-cursor-right screen
 294       var width/edx: (addr int) <- get target-screen, width
 295       var x/ebx: int <- copy 0
 296       {
 297         compare x, *width
 298         break-if->=
 299         print-screen-cell-of-fake-screen screen, target-screen, x, y
 300         move-cursor-right screen
 301         x <- increment
 302         loop
 303       }
 304       draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x18/fg, 0/bg
 305       y <- increment
 306       screen-y <- increment
 307       loop
 308     }
 309   }
 310   # pixel data
 311   {
 312     # screen top left pixels x y width height
 313     var tmp/eax: int <- copy xmin
 314     tmp <- add 1/margin-left
 315     tmp <- shift-left 3/log2-font-width
 316     var left: int
 317     copy-to left, tmp
 318     tmp <- copy ymin
 319     tmp <- add 1/margin-top
 320     tmp <- shift-left 4/log2-font-height
 321     var top: int
 322     copy-to top, tmp
 323     var pixels-ah/eax: (addr handle array byte) <- get target-screen, pixels
 324     var _pixels/eax: (addr array byte) <- lookup *pixels-ah
 325     var pixels/edi: (addr array byte) <- copy _pixels
 326     compare pixels, 0
 327     break-if-=
 328     var y/ebx: int <- copy 0
 329     var height-addr/edx: (addr int) <- get target-screen, height
 330     var height/edx: int <- copy *height-addr
 331     height <- shift-left 4/log2-font-height
 332     {
 333       compare y, height
 334       break-if->=
 335       var width-addr/edx: (addr int) <- get target-screen, width
 336       var width/edx: int <- copy *width-addr
 337       width <- shift-left 3/log2-font-width
 338       var x/eax: int <- copy 0
 339       {
 340         compare x, width
 341         break-if->=
 342         {
 343           var idx/ecx: int <- pixel-index target-screen, x, y
 344           var color-addr/ecx: (addr byte) <- index pixels, idx
 345           var color/ecx: byte <- copy-byte *color-addr
 346           var color2/ecx: int <- copy color
 347           compare color2, 0
 348           break-if-=
 349           var x2/eax: int <- copy x
 350           x2 <- add left
 351           var y2/ebx: int <- copy y
 352           y2 <- add top
 353           pixel screen, x2, y2, color2
 354         }
 355         x <- increment
 356         loop
 357       }
 358       y <- increment
 359       loop
 360     }
 361   }
 362   # bottom border
 363   {
 364     set-cursor-position screen, xmin, screen-y
 365     move-cursor-right screen
 366     var width/edx: (addr int) <- get target-screen, width
 367     var x/ebx: int <- copy 0
 368     {
 369       compare x, *width
 370       break-if->=
 371       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
 372       move-cursor-right screen
 373       x <- increment
 374       loop
 375     }
 376     screen-y <- increment
 377   }
 378   return screen-y
 379 }
 380 
 381 fn has-keyboard? _self: (addr sandbox) -> _/eax: boolean {
 382   var self/esi: (addr sandbox) <- copy _self
 383   var keyboard-obj-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
 384   var keyboard-obj-cell/eax: (addr cell) <- lookup *keyboard-obj-cell-ah
 385   compare keyboard-obj-cell, 0
 386   {
 387     break-if-!=
 388     return 0/false
 389   }
 390   var keyboard-obj-cell-type/ecx: (addr int) <- get keyboard-obj-cell, type
 391   compare *keyboard-obj-cell-type, 6/keyboard
 392   {
 393     break-if-=
 394     return 0/false
 395   }
 396   var keyboard-obj-ah/eax: (addr handle gap-buffer) <- get keyboard-obj-cell, keyboard-data
 397   var _keyboard-obj/eax: (addr gap-buffer) <- lookup *keyboard-obj-ah
 398   var keyboard-obj/edx: (addr gap-buffer) <- copy _keyboard-obj
 399   compare keyboard-obj, 0
 400   {
 401     break-if-!=
 402     return 0/false
 403   }
 404   return 1/true
 405 }
 406 
 407 fn maybe-render-keyboard screen: (addr screen), _self: (addr sandbox), xmin: int, ymin: int -> _/ecx: int {
 408   var self/esi: (addr sandbox) <- copy _self
 409   var keyboard-obj-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
 410   var keyboard-obj-cell/eax: (addr cell) <- lookup *keyboard-obj-cell-ah
 411   compare keyboard-obj-cell, 0
 412   {
 413     break-if-!=
 414     return ymin
 415   }
 416   var keyboard-obj-cell-type/ecx: (addr int) <- get keyboard-obj-cell, type
 417   compare *keyboard-obj-cell-type, 6/keyboard
 418   {
 419     break-if-=
 420     return ymin  # silently give up on rendering the keyboard
 421   }
 422   var keyboard-obj-ah/eax: (addr handle gap-buffer) <- get keyboard-obj-cell, keyboard-data
 423   var _keyboard-obj/eax: (addr gap-buffer) <- lookup *keyboard-obj-ah
 424   var keyboard-obj/edx: (addr gap-buffer) <- copy _keyboard-obj
 425   var x/eax: int <- draw-text-rightward screen, "keyboard: ", xmin, 0x99/xmax, ymin, 7/fg, 0/bg
 426   var y/ecx: int <- copy ymin
 427   var cursor-in-keyboard?/esi: (addr boolean) <- get self, cursor-in-keyboard?
 428   y <- render-keyboard screen, keyboard-obj, x, y, *cursor-in-keyboard?
 429   y <- increment  # padding
 430   return y
 431 }
 432 
 433 # draw an evocative shape
 434 fn render-keyboard screen: (addr screen), _keyboard: (addr gap-buffer), xmin: int, ymin: int, render-cursor?: boolean -> _/ecx: int {
 435   var keyboard/esi: (addr gap-buffer) <- copy _keyboard
 436   var width/edx: int <- copy 0x10/keyboard-capacity
 437   var y/edi: int <- copy ymin
 438   # top border
 439   {
 440     set-cursor-position screen, xmin, y
 441     move-cursor-right screen
 442     var x/ebx: int <- copy 0
 443     {
 444       compare x, width
 445       break-if->=
 446       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
 447       move-cursor-right screen
 448       x <- increment
 449       loop
 450     }
 451     y <- increment
 452   }
 453   # keyboard
 454   var x/eax: int <- copy xmin
 455   draw-code-point screen, 0x7c/vertical-bar, x, y, 0x18/fg, 0/bg
 456   x <- increment
 457   x <- render-gap-buffer screen, keyboard, x, y, render-cursor?
 458   x <- copy xmin
 459   x <- add 1  # for left bar
 460   x <- add 0x10/keyboard-capacity
 461   draw-code-point screen, 0x7c/vertical-bar, x, y, 0x18/fg, 0/bg
 462   y <- increment
 463   # bottom border
 464   {
 465     set-cursor-position screen, xmin, y
 466     move-cursor-right screen
 467     var x/ebx: int <- copy 0
 468     {
 469       compare x, width
 470       break-if->=
 471       draw-code-point-at-cursor screen, 0x2d/horizontal-bar, 0x18/fg, 0/bg
 472       move-cursor-right screen
 473       x <- increment
 474       loop
 475     }
 476     y <- increment
 477   }
 478   return y
 479 }
 480 
 481 fn print-screen-cell-of-fake-screen screen: (addr screen), _target: (addr screen), x: int, y: int {
 482   var target/ecx: (addr screen) <- copy _target
 483   var data-ah/eax: (addr handle array screen-cell) <- get target, data
 484   var data/eax: (addr array screen-cell) <- lookup *data-ah
 485   var index/ecx: int <- screen-cell-index target, x, y
 486   var offset/ecx: (offset screen-cell) <- compute-offset data, index
 487   var src-cell/esi: (addr screen-cell) <- index data, offset
 488   var src-grapheme/eax: (addr grapheme) <- get src-cell, data
 489   var src-color/ecx: (addr int) <- get src-cell, color
 490   var src-background-color/edx: (addr int) <- get src-cell, background-color
 491   draw-grapheme-at-cursor screen, *src-grapheme, *src-color, *src-background-color
 492 }
 493 
 494 fn render-sandbox-menu screen: (addr screen), _self: (addr sandbox) {
 495   var _width/eax: int <- copy 0
 496   var height/ecx: int <- copy 0
 497   _width, height <- screen-size screen
 498   var width/edx: int <- copy _width
 499   var y/ecx: int <- copy height
 500   y <- decrement
 501   var height/ebx: int <- copy y
 502   height <- increment
 503   clear-rect screen, 0/x, y, width, height, 0/bg=black
 504   set-cursor-position screen, 0/x, y
 505   draw-text-rightward-from-cursor screen, " ctrl-r ", width, 0/fg, 7/bg=grey
 506   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0/bg
 507   draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey
 508   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0/bg
 509   $render-sandbox-menu:render-tab: {
 510     var self/eax: (addr sandbox) <- copy _self
 511     var has-trace?/eax: boolean <- has-trace? self
 512     compare has-trace?, 0/false
 513     {
 514       break-if-=
 515       draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 9/bg=blue
 516       draw-text-rightward-from-cursor screen, " to trace  ", width, 7/fg, 0/bg
 517       break $render-sandbox-menu:render-tab
 518     }
 519     draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 0x18/bg=keyboard
 520     draw-text-rightward-from-cursor screen, " to keyboard  ", width, 7/fg, 0/bg
 521   }
 522   draw-text-rightward-from-cursor screen, " ctrl-a ", width, 0/fg, 7/bg=grey
 523   draw-text-rightward-from-cursor screen, " <<  ", width, 7/fg, 0/bg
 524   draw-text-rightward-from-cursor screen, " ctrl-b ", width, 0/fg, 7/bg=grey
 525   draw-text-rightward-from-cursor screen, " <word  ", width, 7/fg, 0/bg
 526   draw-text-rightward-from-cursor screen, " ctrl-f ", width, 0/fg, 7/bg=grey
 527   draw-text-rightward-from-cursor screen, " word>  ", width, 7/fg, 0/bg
 528   draw-text-rightward-from-cursor screen, " ctrl-e ", width, 0/fg, 7/bg=grey
 529   draw-text-rightward-from-cursor screen, " >>  ", width, 7/fg, 0/bg
 530 }
 531 
 532 fn render-keyboard-menu screen: (addr screen) {
 533   var width/eax: int <- copy 0
 534   var height/ecx: int <- copy 0
 535   width, height <- screen-size screen
 536   var y/ecx: int <- copy height
 537   y <- decrement
 538   var height/edx: int <- copy y
 539   height <- increment
 540   clear-rect screen, 0/x, y, width, height, 0/bg=black
 541   set-cursor-position screen, 0/x, y
 542   draw-text-rightward-from-cursor screen, " ctrl-r ", width, 0/fg, 7/bg=grey
 543   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0/bg
 544   draw-text-rightward-from-cursor screen, " ctrl-s ", width, 0/fg, 7/bg=grey
 545   draw-text-rightward-from-cursor screen, " run sandbox  ", width, 7/fg, 0/bg
 546   draw-text-rightward-from-cursor screen, " tab ", width, 0/fg, 3/bg=cyan
 547   draw-text-rightward-from-cursor screen, " to sandbox  ", width, 7/fg, 0/bg
 548 }
 549 
 550 fn edit-sandbox _self: (addr sandbox), key: byte, globals: (addr global-table), data-disk: (addr disk), real-screen: (addr screen), tweak-real-screen?: boolean {
 551   var self/esi: (addr sandbox) <- copy _self
 552   var g/edx: grapheme <- copy key
 553   # ctrl-s
 554   {
 555     compare g, 0x13/ctrl-s
 556     break-if-!=
 557     # minor gotcha here: any bindings created later in this iteration won't be
 558     # persisted.
 559     # That's ok since we don't clear the gap buffer. If we start doing so
 560     # we'll need to revisit where serialization happens.
 561     store-state data-disk, self, globals
 562     # run sandbox
 563     var data-ah/eax: (addr handle gap-buffer) <- get self, data
 564     var _data/eax: (addr gap-buffer) <- lookup *data-ah
 565     var data/ecx: (addr gap-buffer) <- copy _data
 566     var value-ah/eax: (addr handle stream byte) <- get self, value
 567     var _value/eax: (addr stream byte) <- lookup *value-ah
 568     var value/edx: (addr stream byte) <- copy _value
 569     var trace-ah/eax: (addr handle trace) <- get self, trace
 570     var _trace/eax: (addr trace) <- lookup *trace-ah
 571     var trace/ebx: (addr trace) <- copy _trace
 572     clear-trace trace
 573     {
 574       compare tweak-real-screen?, 0/false
 575       break-if-=
 576       clear-sandbox-output real-screen, self, 0x40/sandbox-left-margin, 0/y, 0x80/screen-width, 0x2f/screen-height-without-menu
 577     }
 578     var screen-cell/eax: (addr handle cell) <- get self, screen-var
 579     clear-screen-cell screen-cell
 580     var keyboard-cell/esi: (addr handle cell) <- get self, keyboard-var
 581     rewind-keyboard-cell keyboard-cell  # don't clear keys from before
 582     {
 583       compare tweak-real-screen?, 0/false
 584       break-if-=
 585       set-cursor-position real-screen, 0/x, 0/y  # for any debug prints during evaluation
 586     }
 587     run data, value, globals, trace, screen-cell, keyboard-cell
 588     return
 589   }
 590   # tab
 591   {
 592     compare g, 9/tab
 593     break-if-!=
 594     # if cursor in data, switch to trace or fall through to keyboard
 595     {
 596       var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 597       compare *cursor-in-data?, 0/false
 598       break-if-=
 599       var has-trace?/eax: boolean <- has-trace? self
 600       compare has-trace?, 0/false
 601       {
 602         break-if-=
 603         var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 604         copy-to *cursor-in-data?, 0/false
 605         var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 606         copy-to *cursor-in-trace?, 1/false
 607         return
 608       }
 609       var has-keyboard?/eax: boolean <- has-keyboard? self
 610       compare has-keyboard?, 0/false
 611       {
 612         break-if-=
 613         var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 614         copy-to *cursor-in-data?, 0/false
 615         var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
 616         copy-to *cursor-in-keyboard?, 1/false
 617         return
 618       }
 619       return
 620     }
 621     # if cursor in trace, switch to keyboard or fall through to data
 622     {
 623       var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 624       compare *cursor-in-trace?, 0/false
 625       break-if-=
 626       copy-to *cursor-in-trace?, 0/false
 627       var cursor-target/ecx: (addr boolean) <- get self, cursor-in-keyboard?
 628       var has-keyboard?/eax: boolean <- has-keyboard? self
 629       compare has-keyboard?, 0/false
 630       {
 631         break-if-!=
 632         cursor-target <- get self, cursor-in-data?
 633       }
 634       copy-to *cursor-target, 1/true
 635       return
 636     }
 637     # otherwise if cursor in keyboard, switch to data
 638     {
 639       var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
 640       compare *cursor-in-keyboard?, 0/false
 641       break-if-=
 642       copy-to *cursor-in-keyboard?, 0/false
 643       var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 644       copy-to *cursor-in-data?, 1/true
 645       return
 646     }
 647     return
 648   }
 649   # if cursor in data, send key to data
 650   {
 651     var cursor-in-data?/eax: (addr boolean) <- get self, cursor-in-data?
 652     compare *cursor-in-data?, 0/false
 653     break-if-=
 654     var data-ah/eax: (addr handle gap-buffer) <- get self, data
 655     var data/eax: (addr gap-buffer) <- lookup *data-ah
 656     edit-gap-buffer data, g
 657     return
 658   }
 659   # if cursor in keyboard, send key to keyboard
 660   {
 661     var cursor-in-keyboard?/eax: (addr boolean) <- get self, cursor-in-keyboard?
 662     compare *cursor-in-keyboard?, 0/false
 663     break-if-=
 664     var keyboard-cell-ah/eax: (addr handle cell) <- get self, keyboard-var
 665     var keyboard-cell/eax: (addr cell) <- lookup *keyboard-cell-ah
 666     compare keyboard-cell, 0
 667     {
 668       break-if-!=
 669       return
 670     }
 671     var keyboard-cell-type/ecx: (addr int) <- get keyboard-cell, type
 672     compare *keyboard-cell-type, 6/keyboard
 673     {
 674       break-if-=
 675       return
 676     }
 677     var keyboard-ah/eax: (addr handle gap-buffer) <- get keyboard-cell, keyboard-data
 678     var keyboard/eax: (addr gap-buffer) <- lookup *keyboard-ah
 679     edit-gap-buffer keyboard, g
 680     return
 681   }
 682   # if cursor in trace, send key to trace
 683   {
 684     var cursor-in-trace?/eax: (addr boolean) <- get self, cursor-in-trace?
 685     compare *cursor-in-trace?, 0/false
 686     break-if-=
 687     var trace-ah/eax: (addr handle trace) <- get self, trace
 688     var trace/eax: (addr trace) <- lookup *trace-ah
 689     edit-trace trace, g
 690     return
 691   }
 692 }
 693 
 694 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) {
 695   var read-result-storage: (handle cell)
 696   var read-result/esi: (addr handle cell) <- address read-result-storage
 697   read-cell in, read-result, trace
 698   var error?/eax: boolean <- has-errors? trace
 699   {
 700     compare error?, 0/false
 701     break-if-=
 702     return
 703   }
 704   var nil-storage: (handle cell)
 705   var nil-ah/eax: (addr handle cell) <- address nil-storage
 706   allocate-pair nil-ah
 707   var eval-result-storage: (handle cell)
 708   var eval-result/edi: (addr handle cell) <- address eval-result-storage
 709   debug-print "^", 4/fg, 0/bg
 710   evaluate read-result, eval-result, *nil-ah, globals, trace, screen-cell, keyboard-cell, 1/call-number
 711   debug-print "$", 4/fg, 0/bg
 712   var error?/eax: boolean <- has-errors? trace
 713   {
 714     compare error?, 0/false
 715     break-if-=
 716     return
 717   }
 718   clear-stream out
 719   print-cell eval-result, out, trace
 720   mark-lines-dirty trace
 721 }
 722 
 723 fn test-run-integer {
 724   var sandbox-storage: sandbox
 725   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 726   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 727   # type "1"
 728   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 729   # eval
 730   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 731   # setup: screen
 732   var screen-on-stack: screen
 733   var screen/edi: (addr screen) <- address screen-on-stack
 734   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 735   #
 736   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 737   check-screen-row screen, 0/y, "1    ", "F - test-run-integer/0"
 738   check-screen-row screen, 1/y, "...  ", "F - test-run-integer/1"
 739   check-screen-row screen, 2/y, "=> 1 ", "F - test-run-integer/2"
 740 }
 741 
 742 fn test-run-with-spaces {
 743   var sandbox-storage: sandbox
 744   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 745   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 746   # type input with whitespace before and after
 747   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 748   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 749   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 750   edit-sandbox sandbox, 0xa/newline, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 751   # eval
 752   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 753   # setup: screen
 754   var screen-on-stack: screen
 755   var screen/edi: (addr screen) <- address screen-on-stack
 756   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 757   #
 758   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 759   check-screen-row screen, 0/y, " 1   ", "F - test-run-with-spaces/0"
 760   check-screen-row screen, 1/y, "     ", "F - test-run-with-spaces/1"
 761   check-screen-row screen, 2/y, "...  ", "F - test-run-with-spaces/2"
 762   check-screen-row screen, 3/y, "=> 1 ", "F - test-run-with-spaces/3"
 763 }
 764 
 765 fn test-run-quote {
 766   var sandbox-storage: sandbox
 767   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 768   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 769   # type "'a"
 770   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 771   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 772   # eval
 773   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 774   # setup: screen
 775   var screen-on-stack: screen
 776   var screen/edi: (addr screen) <- address screen-on-stack
 777   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 778   #
 779   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 780   check-screen-row screen, 0/y, "'a   ", "F - test-run-quote/0"
 781   check-screen-row screen, 1/y, "...  ", "F - test-run-quote/1"
 782   check-screen-row screen, 2/y, "=> a ", "F - test-run-quote/2"
 783 }
 784 
 785 fn test-run-dotted-list {
 786   var sandbox-storage: sandbox
 787   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 788   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 789   # type "'(a . b)"
 790   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 791   edit-sandbox sandbox, 0x28/open-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 792   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 793   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 794   edit-sandbox sandbox, 0x2e/dot, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 795   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 796   edit-sandbox sandbox, 0x62/b, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 797   edit-sandbox sandbox, 0x29/close-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 798   # eval
 799   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 800   # setup: screen
 801   var screen-on-stack: screen
 802   var screen/edi: (addr screen) <- address screen-on-stack
 803   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 804   #
 805   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 806   check-screen-row screen, 0/y, "'(a . b)   ", "F - test-run-dotted-list/0"
 807   check-screen-row screen, 1/y, "...        ", "F - test-run-dotted-list/1"
 808   check-screen-row screen, 2/y, "=> (a . b) ", "F - test-run-dotted-list/2"
 809 }
 810 
 811 fn test-run-dot-and-list {
 812   var sandbox-storage: sandbox
 813   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 814   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 815   # type "'(a . (b))"
 816   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 817   edit-sandbox sandbox, 0x28/open-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 818   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 819   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 820   edit-sandbox sandbox, 0x2e/dot, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 821   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 822   edit-sandbox sandbox, 0x28/open-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 823   edit-sandbox sandbox, 0x62/b, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 824   edit-sandbox sandbox, 0x29/close-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 825   edit-sandbox sandbox, 0x29/close-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 826   # eval
 827   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 828   # setup: screen
 829   var screen-on-stack: screen
 830   var screen/edi: (addr screen) <- address screen-on-stack
 831   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 832   #
 833   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 834   check-screen-row screen, 0/y, "'(a . (b)) ", "F - test-run-dot-and-list/0"
 835   check-screen-row screen, 1/y, "...        ", "F - test-run-dot-and-list/1"
 836   check-screen-row screen, 2/y, "=> (a b)   ", "F - test-run-dot-and-list/2"
 837 }
 838 
 839 fn test-run-final-dot {
 840   var sandbox-storage: sandbox
 841   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 842   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 843   # type "'(a .)"
 844   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 845   edit-sandbox sandbox, 0x28/open-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 846   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 847   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 848   edit-sandbox sandbox, 0x2e/dot, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 849   edit-sandbox sandbox, 0x29/close-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 850   # eval
 851   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 852   # setup: screen
 853   var screen-on-stack: screen
 854   var screen/edi: (addr screen) <- address screen-on-stack
 855   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 856   #
 857   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 858   check-screen-row screen, 0/y, "'(a .)               ", "F - test-run-final-dot/0"
 859   check-screen-row screen, 1/y, "...                  ", "F - test-run-final-dot/1"
 860   check-screen-row screen, 2/y, "'. )' makes no sense ", "F - test-run-final-dot/2"
 861   # further errors may occur
 862 }
 863 
 864 fn test-run-double-dot {
 865   var sandbox-storage: sandbox
 866   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 867   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 868   # type "'(a . .)"
 869   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 870   edit-sandbox sandbox, 0x28/open-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 871   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 872   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 873   edit-sandbox sandbox, 0x2e/dot, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 874   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 875   edit-sandbox sandbox, 0x2e/dot, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 876   edit-sandbox sandbox, 0x29/close-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 877   # eval
 878   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 879   # setup: screen
 880   var screen-on-stack: screen
 881   var screen/edi: (addr screen) <- address screen-on-stack
 882   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 883   #
 884   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 885   check-screen-row screen, 0/y, "'(a . .)             ", "F - test-run-double-dot/0"
 886   check-screen-row screen, 1/y, "...                  ", "F - test-run-double-dot/1"
 887   check-screen-row screen, 2/y, "'. .' makes no sense ", "F - test-run-double-dot/2"
 888   # further errors may occur
 889 }
 890 
 891 fn test-run-multiple-expressions-after-dot {
 892   var sandbox-storage: sandbox
 893   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 894   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 895   # type "'(a . b c)"
 896   edit-sandbox sandbox, 0x27/quote, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 897   edit-sandbox sandbox, 0x28/open-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 898   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 899   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 900   edit-sandbox sandbox, 0x2e/dot, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 901   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 902   edit-sandbox sandbox, 0x62/b, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 903   edit-sandbox sandbox, 0x20/space, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 904   edit-sandbox sandbox, 0x63/c, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 905   edit-sandbox sandbox, 0x29/close-paren, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 906   # eval
 907   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 908   # setup: screen
 909   var screen-on-stack: screen
 910   var screen/edi: (addr screen) <- address screen-on-stack
 911   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 912   #
 913   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 914   check-screen-row screen, 0/y, "'(a . b c)                                           ", "F - test-run-multiple-expressions-after-dot/0"
 915   check-screen-row screen, 1/y, "...                                                  ", "F - test-run-multiple-expressions-after-dot/1"
 916   check-screen-row screen, 2/y, "cannot have multiple expressions between '.' and ')' ", "F - test-run-multiple-expressions-after-dot/2"
 917   # further errors may occur
 918 }
 919 
 920 fn test-run-error-invalid-integer {
 921   var sandbox-storage: sandbox
 922   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 923   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 924   # type "1a"
 925   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 926   edit-sandbox sandbox, 0x61/a, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 927   # eval
 928   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 929   # setup: screen
 930   var screen-on-stack: screen
 931   var screen/edi: (addr screen) <- address screen-on-stack
 932   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 933   #
 934   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 935   check-screen-row screen, 0/y, "1a             ", "F - test-run-error-invalid-integer/0"
 936   check-screen-row screen, 1/y, "...            ", "F - test-run-error-invalid-integer/0"
 937   check-screen-row screen, 2/y, "invalid number ", "F - test-run-error-invalid-integer/2"
 938 }
 939 
 940 fn test-run-move-cursor-into-trace {
 941   var sandbox-storage: sandbox
 942   var sandbox/esi: (addr sandbox) <- address sandbox-storage
 943   initialize-sandbox sandbox, 0/no-screen-or-keyboard
 944   # type "12"
 945   edit-sandbox sandbox, 0x31/1, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 946   edit-sandbox sandbox, 0x32/2, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 947   # eval
 948   edit-sandbox sandbox, 0x13/ctrl-s, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 949   # setup: screen
 950   var screen-on-stack: screen
 951   var screen/edi: (addr screen) <- address screen-on-stack
 952   initialize-screen screen, 0x80/width, 0x10/height, 0/no-pixel-graphics
 953   #
 954   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 955   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/pre-0"
 956   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  |   ", "F - test-run-move-cursor-into-trace/pre-0/cursor"
 957   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/pre-1"
 958   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-run-move-cursor-into-trace/pre-1/cursor"
 959   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/pre-2"
 960   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/pre-2/cursor"
 961   # move cursor into trace
 962   edit-sandbox sandbox, 9/tab, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 963   #
 964   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 965   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/trace-0"
 966   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-run-move-cursor-into-trace/trace-0/cursor"
 967   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/trace-1"
 968   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||   ", "F - test-run-move-cursor-into-trace/trace-1/cursor"
 969   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/trace-2"
 970   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/trace-2/cursor"
 971   # move cursor into input
 972   edit-sandbox sandbox, 9/tab, 0/no-globals, 0/no-disk, 0/no-screen, 0/no-tweak-screen
 973   #
 974   render-sandbox screen, sandbox, 0/x, 0/y, 0x80/width, 0x10/height
 975   check-screen-row screen,                                  0/y, "12    ", "F - test-run-move-cursor-into-trace/input-0"
 976   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "  |   ", "F - test-run-move-cursor-into-trace/input-0/cursor"
 977   check-screen-row screen,                                  1/y, "...   ", "F - test-run-move-cursor-into-trace/input-1"
 978   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-run-move-cursor-into-trace/input-1/cursor"
 979   check-screen-row screen,                                  2/y, "=> 12 ", "F - test-run-move-cursor-into-trace/input-2"
 980   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-run-move-cursor-into-trace/input-2/cursor"
 981 }
 982 
 983 fn has-trace? _self: (addr sandbox) -> _/eax: boolean {
 984   var self/esi: (addr sandbox) <- copy _self
 985   var trace-ah/eax: (addr handle trace) <- get self, trace
 986   var _trace/eax: (addr trace) <- lookup *trace-ah
 987   var trace/edx: (addr trace) <- copy _trace
 988   compare trace, 0
 989   {
 990     break-if-!=
 991     return 0/false
 992   }
 993   var first-free/ebx: (addr int) <- get trace, first-free
 994   compare *first-free, 0
 995   {
 996     break-if->
 997     return 0/false
 998   }
 999   return 1/true
1000 }