https://github.com/akkartik/mu/blob/main/shell/global.mu
   1 type global {
   2   name: (handle array byte)
   3   input: (handle gap-buffer)
   4   value: (handle cell)
   5 }
   6 
   7 type global-table {
   8   data: (handle array global)
   9   final-index: int
  10 }
  11 
  12 fn initialize-globals _self: (addr global-table) {
  13   var self/esi: (addr global-table) <- copy _self
  14   var data-ah/eax: (addr handle array global) <- get self, data
  15   populate data-ah, 0x40
  16   # for numbers
  17   append-primitive self, "+"
  18   append-primitive self, "-"
  19   append-primitive self, "*"
  20   append-primitive self, "/"
  21   append-primitive self, "sqrt"
  22   append-primitive self, "abs"
  23   append-primitive self, "sgn"
  24   append-primitive self, "<"
  25   append-primitive self, ">"
  26   append-primitive self, "<="
  27   append-primitive self, ">="
  28   # generic
  29   append-primitive self, "="
  30   append-primitive self, "no"
  31   append-primitive self, "not"
  32   # for pairs
  33   append-primitive self, "car"
  34   append-primitive self, "cdr"
  35   append-primitive self, "cons"
  36   # for screens
  37   append-primitive self, "print"
  38   append-primitive self, "clear"
  39   append-primitive self, "lines"
  40   append-primitive self, "columns"
  41   append-primitive self, "up"
  42   append-primitive self, "down"
  43   append-primitive self, "left"
  44   append-primitive self, "right"
  45   append-primitive self, "cr"
  46   append-primitive self, "pixel"
  47   append-primitive self, "width"
  48   append-primitive self, "height"
  49   # for keyboards
  50   append-primitive self, "key"
  51   # for streams
  52   append-primitive self, "stream"
  53   append-primitive self, "write"
  54   # misc
  55   append-primitive self, "abort"
  56   # keep sync'd with render-primitives
  57 }
  58 
  59 fn load-globals in: (addr handle cell), self: (addr global-table) {
  60   var remaining-ah/esi: (addr handle cell) <- copy in
  61   {
  62     var _remaining/eax: (addr cell) <- lookup *remaining-ah
  63     var remaining/ecx: (addr cell) <- copy _remaining
  64     var done?/eax: boolean <- nil? remaining
  65     compare done?, 0/false
  66     break-if-!=
  67     var curr-ah/eax: (addr handle cell) <- get remaining, left
  68     var curr/eax: (addr cell) <- lookup *curr-ah
  69     remaining-ah <- get remaining, right
  70     var value-ah/eax: (addr handle cell) <- get curr, right
  71     var value/eax: (addr cell) <- lookup *value-ah
  72     var value-data-ah/eax: (addr handle stream byte) <- get value, text-data
  73     var _value-data/eax: (addr stream byte) <- lookup *value-data-ah
  74     var value-data/ecx: (addr stream byte) <- copy _value-data
  75     var value-gap-buffer-storage: (handle gap-buffer)
  76     var value-gap-buffer-ah/edx: (addr handle gap-buffer) <- address value-gap-buffer-storage
  77     allocate value-gap-buffer-ah
  78     var value-gap-buffer/eax: (addr gap-buffer) <- lookup *value-gap-buffer-ah
  79     initialize-gap-buffer value-gap-buffer, 0x1000/4KB
  80     load-gap-buffer-from-stream value-gap-buffer, value-data
  81     read-evaluate-and-move-to-globals value-gap-buffer-ah, self
  82     loop
  83   }
  84 }
  85 
  86 fn write-globals out: (addr stream byte), _self: (addr global-table) {
  87   var self/esi: (addr global-table) <- copy _self
  88   write out, "  (globals . (\n"
  89   var data-ah/eax: (addr handle array global) <- get self, data
  90   var data/eax: (addr array global) <- lookup *data-ah
  91   var final-index/edx: (addr int) <- get self, final-index
  92   var curr-index/ecx: int <- copy 1/skip-0
  93   {
  94     compare curr-index, *final-index
  95     break-if->
  96     var curr-offset/ebx: (offset global) <- compute-offset data, curr-index
  97     var curr/ebx: (addr global) <- index data, curr-offset
  98     var curr-value-ah/edx: (addr handle cell) <- get curr, value
  99     var curr-value/eax: (addr cell) <- lookup *curr-value-ah
 100     var curr-type/eax: (addr int) <- get curr-value, type
 101     {
 102       compare *curr-type, 4/primitive-function
 103       break-if-=
 104       compare *curr-type, 5/screen
 105       break-if-=
 106       compare *curr-type, 6/keyboard
 107       break-if-=
 108       compare *curr-type, 3/stream  # not implemented yet
 109       break-if-=
 110       write out, "    ("
 111       var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 112       var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
 113       write out, curr-name
 114       write out, " . ["
 115       var curr-input-ah/eax: (addr handle gap-buffer) <- get curr, input
 116       var curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
 117       append-gap-buffer curr-input, out
 118       write out, "])\n"
 119     }
 120     curr-index <- increment
 121     loop
 122   }
 123   write out, "  ))\n"
 124 }
 125 
 126 # globals layout: 1 char padding, 41 code, 1 padding, 41 code, 1 padding =  85 chars
 127 fn render-globals screen: (addr screen), _self: (addr global-table) {
 128   clear-rect screen, 0/xmin, 0/ymin, 0x55/xmax, 0x2f/ymax=screen-height-without-menu, 0xdc/bg=green-bg
 129   var self/esi: (addr global-table) <- copy _self
 130   # render primitives
 131   render-primitives screen, 1/xmin=padding-left, 0x55/xmax, 0x2f/ymax
 132   var data-ah/eax: (addr handle array global) <- get self, data
 133   var data/eax: (addr array global) <- lookup *data-ah
 134   var curr-index/edx: int <- copy 1
 135   {
 136     var curr-offset/ebx: (offset global) <- compute-offset data, curr-index
 137     var curr/ebx: (addr global) <- index data, curr-offset
 138     var continue?/eax: boolean <- primitive-global? curr
 139     compare continue?, 0/false
 140     break-if-=
 141     curr-index <- increment
 142     loop
 143   }
 144   var lowest-index/edi: int <- copy curr-index
 145   var final-index/edx: (addr int) <- get self, final-index
 146   var curr-index/edx: int <- copy *final-index
 147   var y1: int
 148   copy-to y1, 1/padding-top
 149   var y2: int
 150   copy-to y2, 1/padding-top
 151   $render-globals:loop: {
 152     compare curr-index, lowest-index
 153     break-if-<
 154     {
 155       compare y1, 0x2f/ymax
 156       break-if-<
 157       compare y2, 0x2f/ymax
 158       break-if-<
 159       break $render-globals:loop
 160     }
 161     {
 162       var curr-offset/edx: (offset global) <- compute-offset data, curr-index
 163       var curr/edx: (addr global) <- index data, curr-offset
 164       var curr-input-ah/edx: (addr handle gap-buffer) <- get curr, input
 165       var _curr-input/eax: (addr gap-buffer) <- lookup *curr-input-ah
 166       var curr-input/ebx: (addr gap-buffer) <- copy _curr-input
 167       compare curr-input, 0
 168       break-if-=
 169       $render-globals:render-global: {
 170         var x/eax: int <- copy 0
 171         var y/ecx: int <- copy y1
 172         compare y, y2
 173         {
 174           break-if->=
 175           x, y <- render-gap-buffer-wrapping-right-then-down screen, curr-input, 1/padding-left, y1, 0x2a/xmax, 0x2f/ymax, 0/no-cursor, 7/fg=definition, 0xc5/bg=blue-bg
 176           y <- add 2
 177           copy-to y1, y
 178           break $render-globals:render-global
 179         }
 180         x, y <- render-gap-buffer-wrapping-right-then-down screen, curr-input, 0x2b/xmin, y2, 0x54/xmax, 0x2f/ymax, 0/no-cursor, 7/fg=definition, 0xc5/bg=blue-bg
 181         y <- add 2
 182         copy-to y2, y
 183       }
 184     }
 185     curr-index <- decrement
 186     loop
 187   }
 188 }
 189 
 190 fn render-primitives screen: (addr screen), xmin: int, xmax: int, ymax: int {
 191   var y/ecx: int <- copy ymax
 192   y <- subtract 0xf
 193   var tmpx/eax: int <- copy xmin
 194   tmpx <- draw-text-rightward screen, "cursor graphics", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 195   y <- increment
 196   var tmpx/eax: int <- copy xmin
 197   tmpx <- draw-text-rightward screen, "  print", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 198   tmpx <- draw-text-rightward screen, ": screen a -> a", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 199   y <- increment
 200   var tmpx/eax: int <- copy xmin
 201   tmpx <- draw-text-rightward screen, "  lines columns", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 202   tmpx <- draw-text-rightward screen, ": screen -> number", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 203   y <- increment
 204   var tmpx/eax: int <- copy xmin
 205   tmpx <- draw-text-rightward screen, "  up down left right", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 206   tmpx <- draw-text-rightward screen, ": screen", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 207   y <- increment
 208   var tmpx/eax: int <- copy xmin
 209   tmpx <- draw-text-rightward screen, "  cr", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 210   tmpx <- draw-text-rightward screen, ": screen   ", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 211   tmpx <- draw-text-rightward screen, "# move cursor down and to left margin", tmpx, xmax, y, 0x38/fg=trace, 0xdc/bg=green-bg
 212   y <- increment
 213   var tmpx/eax: int <- copy xmin
 214   tmpx <- draw-text-rightward screen, "pixel graphics", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 215   y <- increment
 216   var tmpx/eax: int <- copy xmin
 217   tmpx <- draw-text-rightward screen, "  width height", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 218   tmpx <- draw-text-rightward screen, ": screen -> number", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 219   y <- increment
 220   var tmpx/eax: int <- copy xmin
 221   tmpx <- draw-text-rightward screen, "  pixel", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 222   tmpx <- draw-text-rightward screen, ": screen x y color", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 223   y <- increment
 224   var tmpx/eax: int <- copy xmin
 225   tmpx <- draw-text-rightward screen, "screen/keyboard", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 226   y <- increment
 227   var tmpx/eax: int <- copy xmin
 228   tmpx <- draw-text-rightward screen, "  clear", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 229   tmpx <- draw-text-rightward screen, ": screen", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 230   y <- increment
 231   var tmpx/eax: int <- copy xmin
 232   tmpx <- draw-text-rightward screen, "  key", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 233   tmpx <- draw-text-rightward screen, ": () -> grapheme?", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 234   y <- increment
 235   var tmpx/eax: int <- copy xmin
 236   tmpx <- draw-text-rightward screen, "streams", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 237   y <- increment
 238   var tmpx/eax: int <- copy xmin
 239   tmpx <- draw-text-rightward screen, "  stream", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 240   tmpx <- draw-text-rightward screen, ": () -> stream ", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 241   y <- increment
 242   var tmpx/eax: int <- copy xmin
 243   tmpx <- draw-text-rightward screen, "  write", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 244   tmpx <- draw-text-rightward screen, ": stream grapheme -> stream", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 245   y <- increment
 246   var tmpx/eax: int <- copy xmin
 247   tmpx <- draw-text-rightward screen, "fn def set if while = no(t) car cdr cons  ", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 248   tmpx <- draw-text-rightward screen, "num: ", tmpx, xmax, y, 7/fg=grey, 0xdc/bg=green-bg
 249   tmpx <- draw-text-rightward screen, "+ - * / sqrt abs sgn < > <= >=   ", tmpx, xmax, y, 0x2a/fg=orange, 0xdc/bg=green-bg
 250 }
 251 
 252 fn primitive-global? _x: (addr global) -> _/eax: boolean {
 253   var x/eax: (addr global) <- copy _x
 254   var value-ah/eax: (addr handle cell) <- get x, value
 255   var value/eax: (addr cell) <- lookup *value-ah
 256   compare value, 0/null
 257   {
 258     break-if-!=
 259     return 0/false
 260   }
 261   var value-type/eax: (addr int) <- get value, type
 262   compare *value-type, 4/primitive
 263   {
 264     break-if-=
 265     return 0/false
 266   }
 267   return 1/true
 268 }
 269 
 270 fn append-primitive _self: (addr global-table), name: (addr array byte) {
 271   var self/esi: (addr global-table) <- copy _self
 272   var final-index-addr/ecx: (addr int) <- get self, final-index
 273   increment *final-index-addr
 274   var curr-index/ecx: int <- copy *final-index-addr
 275   var data-ah/eax: (addr handle array global) <- get self, data
 276   var data/eax: (addr array global) <- lookup *data-ah
 277   var curr-offset/esi: (offset global) <- compute-offset data, curr-index
 278   var curr/esi: (addr global) <- index data, curr-offset
 279   var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 280   copy-array-object name, curr-name-ah
 281   var curr-value-ah/eax: (addr handle cell) <- get curr, value
 282   new-primitive-function curr-value-ah, curr-index
 283 }
 284 
 285 fn assign-or-create-global _self: (addr global-table), name: (addr array byte), value: (handle cell), trace: (addr trace) {
 286   var self/esi: (addr global-table) <- copy _self
 287   var curr-index/ecx: int <- find-symbol-name-in-globals self, name
 288   {
 289     compare curr-index, -1/not-found
 290     break-if-!=
 291     var final-index-addr/eax: (addr int) <- get self, final-index
 292     increment *final-index-addr
 293     curr-index <- copy *final-index-addr
 294   }
 295   var data-ah/eax: (addr handle array global) <- get self, data
 296   var data/eax: (addr array global) <- lookup *data-ah
 297   var curr-offset/esi: (offset global) <- compute-offset data, curr-index
 298   var curr/esi: (addr global) <- index data, curr-offset
 299   var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 300   copy-array-object name, curr-name-ah
 301   var curr-value-ah/eax: (addr handle cell) <- get curr, value
 302   copy-handle value, curr-value-ah
 303 }
 304 
 305 fn lookup-symbol-in-globals _sym: (addr cell), out: (addr handle cell), _globals: (addr global-table), trace: (addr trace), screen-cell: (addr handle cell), keyboard-cell: (addr handle cell) {
 306   var sym/eax: (addr cell) <- copy _sym
 307   var sym-name-ah/eax: (addr handle stream byte) <- get sym, text-data
 308   var _sym-name/eax: (addr stream byte) <- lookup *sym-name-ah
 309   var sym-name/edx: (addr stream byte) <- copy _sym-name
 310   var globals/esi: (addr global-table) <- copy _globals
 311   {
 312     compare globals, 0
 313     break-if-=
 314     var curr-index/ecx: int <- find-symbol-in-globals globals, sym-name
 315     compare curr-index, -1/not-found
 316     break-if-=
 317     var global-data-ah/eax: (addr handle array global) <- get globals, data
 318     var global-data/eax: (addr array global) <- lookup *global-data-ah
 319     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
 320     var curr/ebx: (addr global) <- index global-data, curr-offset
 321     var curr-value/eax: (addr handle cell) <- get curr, value
 322     copy-object curr-value, out
 323     return
 324   }
 325   # if sym is "screen" and screen-cell exists, return it
 326   {
 327     var sym-is-screen?/eax: boolean <- stream-data-equal? sym-name, "screen"
 328     compare sym-is-screen?, 0/false
 329     break-if-=
 330     compare screen-cell, 0
 331     break-if-=
 332     copy-object screen-cell, out
 333     return
 334   }
 335   # if sym is "keyboard" and keyboard-cell exists, return it
 336   {
 337     var sym-is-keyboard?/eax: boolean <- stream-data-equal? sym-name, "keyboard"
 338     compare sym-is-keyboard?, 0/false
 339     break-if-=
 340     compare keyboard-cell, 0
 341     break-if-=
 342     copy-object keyboard-cell, out
 343     return
 344   }
 345   # otherwise error "unbound symbol: ", sym
 346   var stream-storage: (stream byte 0x40)
 347   var stream/ecx: (addr stream byte) <- address stream-storage
 348   write stream, "unbound symbol: "
 349   rewind-stream sym-name
 350   write-stream stream, sym-name
 351   trace trace, "error", stream
 352 }
 353 
 354 # return the index in globals containing 'sym'
 355 # or -1 if not found
 356 fn find-symbol-in-globals _globals: (addr global-table), sym-name: (addr stream byte) -> _/ecx: int {
 357   var globals/esi: (addr global-table) <- copy _globals
 358   compare globals, 0
 359   {
 360     break-if-!=
 361     return -1/not-found
 362   }
 363   var global-data-ah/eax: (addr handle array global) <- get globals, data
 364   var global-data/eax: (addr array global) <- lookup *global-data-ah
 365   var final-index/ecx: (addr int) <- get globals, final-index
 366   var curr-index/ecx: int <- copy *final-index
 367   {
 368     compare curr-index, 0
 369     break-if-<
 370     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
 371     var curr/ebx: (addr global) <- index global-data, curr-offset
 372     var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 373     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
 374     var found?/eax: boolean <- stream-data-equal? sym-name, curr-name
 375     compare found?, 0/false
 376     {
 377       break-if-=
 378       return curr-index
 379     }
 380     curr-index <- decrement
 381     loop
 382   }
 383   return -1/not-found
 384 }
 385 
 386 # return the index in globals containing 'sym'
 387 # or -1 if not found
 388 fn find-symbol-name-in-globals _globals: (addr global-table), sym-name: (addr array byte) -> _/ecx: int {
 389   var globals/esi: (addr global-table) <- copy _globals
 390   compare globals, 0
 391   {
 392     break-if-!=
 393     return -1/not-found
 394   }
 395   var global-data-ah/eax: (addr handle array global) <- get globals, data
 396   var global-data/eax: (addr array global) <- lookup *global-data-ah
 397   var final-index/ecx: (addr int) <- get globals, final-index
 398   var curr-index/ecx: int <- copy *final-index
 399   {
 400     compare curr-index, 0
 401     break-if-<
 402     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
 403     var curr/ebx: (addr global) <- index global-data, curr-offset
 404     var curr-name-ah/eax: (addr handle array byte) <- get curr, name
 405     var curr-name/eax: (addr array byte) <- lookup *curr-name-ah
 406     var found?/eax: boolean <- string-equal? sym-name, curr-name
 407     compare found?, 0/false
 408     {
 409       break-if-=
 410       return curr-index
 411     }
 412     curr-index <- decrement
 413     loop
 414   }
 415   return -1/not-found
 416 }
 417 
 418 fn mutate-binding-in-globals name: (addr stream byte), val: (addr handle cell), _globals: (addr global-table), trace: (addr trace) {
 419   var globals/esi: (addr global-table) <- copy _globals
 420   {
 421     compare globals, 0
 422     break-if-=
 423     var curr-index/ecx: int <- find-symbol-in-globals globals, name
 424     compare curr-index, -1/not-found
 425     break-if-=
 426     var global-data-ah/eax: (addr handle array global) <- get globals, data
 427     var global-data/eax: (addr array global) <- lookup *global-data-ah
 428     var curr-offset/ebx: (offset global) <- compute-offset global-data, curr-index
 429     var curr/ebx: (addr global) <- index global-data, curr-offset
 430     var dest/eax: (addr handle cell) <- get curr, value
 431     copy-object val, dest
 432     return
 433   }
 434   # otherwise error "unbound symbol: ", sym
 435   var stream-storage: (stream byte 0x40)
 436   var stream/ecx: (addr stream byte) <- address stream-storage
 437   write stream, "unbound symbol: "
 438   rewind-stream name
 439   write-stream stream, name
 440   trace trace, "error", stream
 441 }
 442 
 443 # a little strange; goes from value to name and selects primitive based on name
 444 fn apply-primitive _f: (addr cell), args-ah: (addr handle cell), out: (addr handle cell), _globals: (addr global-table), trace: (addr trace) {
 445   var f/esi: (addr cell) <- copy _f
 446   var f-index-a/ecx: (addr int) <- get f, index-data
 447   var f-index/ecx: int <- copy *f-index-a
 448   var globals/eax: (addr global-table) <- copy _globals
 449   var global-data-ah/eax: (addr handle array global) <- get globals, data
 450   var global-data/eax: (addr array global) <- lookup *global-data-ah
 451   var f-offset/ecx: (offset global) <- compute-offset global-data, f-index
 452   var f-value/ecx: (addr global) <- index global-data, f-offset
 453   var f-name-ah/ecx: (addr handle array byte) <- get f-value, name
 454   var f-name/eax: (addr array byte) <- lookup *f-name-ah
 455   {
 456     var add?/eax: boolean <- string-equal? f-name, "+"
 457     compare add?, 0/false
 458     break-if-=
 459     apply-add args-ah, out, trace
 460     return
 461   }
 462   {
 463     var subtract?/eax: boolean <- string-equal? f-name, "-"
 464     compare subtract?, 0/false
 465     break-if-=
 466     apply-subtract args-ah, out, trace
 467     return
 468   }
 469   {
 470     var multiply?/eax: boolean <- string-equal? f-name, "*"
 471     compare multiply?, 0/false
 472     break-if-=
 473     apply-multiply args-ah, out, trace
 474     return
 475   }
 476   {
 477     var divide?/eax: boolean <- string-equal? f-name, "/"
 478     compare divide?, 0/false
 479     break-if-=
 480     apply-divide args-ah, out, trace
 481     return
 482   }
 483   {
 484     var square-root?/eax: boolean <- string-equal? f-name, "sqrt"
 485     compare square-root?, 0/false
 486     break-if-=
 487     apply-square-root args-ah, out, trace
 488     return
 489   }
 490   {
 491     var abs?/eax: boolean <- string-equal? f-name, "abs"
 492     compare abs?, 0/false
 493     break-if-=
 494     apply-abs args-ah, out, trace
 495     return
 496   }
 497   {
 498     var sgn?/eax: boolean <- string-equal? f-name, "sgn"
 499     compare sgn?, 0/false
 500     break-if-=
 501     apply-sgn args-ah, out, trace
 502     return
 503   }
 504   {
 505     var car?/eax: boolean <- string-equal? f-name, "car"
 506     compare car?, 0/false
 507     break-if-=
 508     apply-car args-ah, out, trace
 509     return
 510   }
 511   {
 512     var cdr?/eax: boolean <- string-equal? f-name, "cdr"
 513     compare cdr?, 0/false
 514     break-if-=
 515     apply-cdr args-ah, out, trace
 516     return
 517   }
 518   {
 519     var cons?/eax: boolean <- string-equal? f-name, "cons"
 520     compare cons?, 0/false
 521     break-if-=
 522     apply-cons args-ah, out, trace
 523     return
 524   }
 525   {
 526     var structurally-equal?/eax: boolean <- string-equal? f-name, "="
 527     compare structurally-equal?, 0/false
 528     break-if-=
 529     apply-structurally-equal args-ah, out, trace
 530     return
 531   }
 532   {
 533     var not?/eax: boolean <- string-equal? f-name, "no"
 534     compare not?, 0/false
 535     break-if-=
 536     apply-not args-ah, out, trace
 537     return
 538   }
 539   {
 540     var not?/eax: boolean <- string-equal? f-name, "not"
 541     compare not?, 0/false
 542     break-if-=
 543     apply-not args-ah, out, trace
 544     return
 545   }
 546   {
 547     var lesser?/eax: boolean <- string-equal? f-name, "<"
 548     compare lesser?, 0/false
 549     break-if-=
 550     apply-< args-ah, out, trace
 551     return
 552   }
 553   {
 554     var greater?/eax: boolean <- string-equal? f-name, ">"
 555     compare greater?, 0/false
 556     break-if-=
 557     apply-> args-ah, out, trace
 558     return
 559   }
 560   {
 561     var lesser-or-equal?/eax: boolean <- string-equal? f-name, "<="
 562     compare lesser-or-equal?, 0/false
 563     break-if-=
 564     apply-<= args-ah, out, trace
 565     return
 566   }
 567   {
 568     var greater-or-equal?/eax: boolean <- string-equal? f-name, ">="
 569     compare greater-or-equal?, 0/false
 570     break-if-=
 571     apply->= args-ah, out, trace
 572     return
 573   }
 574   {
 575     var print?/eax: boolean <- string-equal? f-name, "print"
 576     compare print?, 0/false
 577     break-if-=
 578     apply-print args-ah, out, trace
 579     return
 580   }
 581   {
 582     var clear?/eax: boolean <- string-equal? f-name, "clear"
 583     compare clear?, 0/false
 584     break-if-=
 585     apply-clear args-ah, out, trace
 586     return
 587   }
 588   {
 589     var lines?/eax: boolean <- string-equal? f-name, "lines"
 590     compare lines?, 0/false
 591     break-if-=
 592     apply-lines args-ah, out, trace
 593     return
 594   }
 595   {
 596     var columns?/eax: boolean <- string-equal? f-name, "columns"
 597     compare columns?, 0/false
 598     break-if-=
 599     apply-columns args-ah, out, trace
 600     return
 601   }
 602   {
 603     var up?/eax: boolean <- string-equal? f-name, "up"
 604     compare up?, 0/false
 605     break-if-=
 606     apply-up args-ah, out, trace
 607     return
 608   }
 609   {
 610     var down?/eax: boolean <- string-equal? f-name, "down"
 611     compare down?, 0/false
 612     break-if-=
 613     apply-down args-ah, out, trace
 614     return
 615   }
 616   {
 617     var left?/eax: boolean <- string-equal? f-name, "left"
 618     compare left?, 0/false
 619     break-if-=
 620     apply-left args-ah, out, trace
 621     return
 622   }
 623   {
 624     var right?/eax: boolean <- string-equal? f-name, "right"
 625     compare right?, 0/false
 626     break-if-=
 627     apply-right args-ah, out, trace
 628     return
 629   }
 630   {
 631     var cr?/eax: boolean <- string-equal? f-name, "cr"
 632     compare cr?, 0/false
 633     break-if-=
 634     apply-cr args-ah, out, trace
 635     return
 636   }
 637   {
 638     var pixel?/eax: boolean <- string-equal? f-name, "pixel"
 639     compare pixel?, 0/false
 640     break-if-=
 641     apply-pixel args-ah, out, trace
 642     return
 643   }
 644   {
 645     var width?/eax: boolean <- string-equal? f-name, "width"
 646     compare width?, 0/false
 647     break-if-=
 648     apply-width args-ah, out, trace
 649     return
 650   }
 651   {
 652     var height?/eax: boolean <- string-equal? f-name, "height"
 653     compare height?, 0/false
 654     break-if-=
 655     apply-height args-ah, out, trace
 656     return
 657   }
 658   {
 659     var wait-for-key?/eax: boolean <- string-equal? f-name, "key"
 660     compare wait-for-key?, 0/false
 661     break-if-=
 662     apply-wait-for-key args-ah, out, trace
 663     return
 664   }
 665   {
 666     var stream?/eax: boolean <- string-equal? f-name, "stream"
 667     compare stream?, 0/false
 668     break-if-=
 669     apply-stream args-ah, out, trace
 670     return
 671   }
 672   {
 673     var write?/eax: boolean <- string-equal? f-name, "write"
 674     compare write?, 0/false
 675     break-if-=
 676     apply-write args-ah, out, trace
 677     return
 678   }
 679   {
 680     var abort?/eax: boolean <- string-equal? f-name, "abort"
 681     compare abort?, 0/false
 682     break-if-=
 683     apply-abort args-ah, out, trace
 684     return
 685   }
 686   abort "unknown primitive function"
 687 }
 688 
 689 fn apply-add _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 690   trace-text trace, "eval", "apply +"
 691   var args-ah/eax: (addr handle cell) <- copy _args-ah
 692   var _args/eax: (addr cell) <- lookup *args-ah
 693   var args/esi: (addr cell) <- copy _args
 694   # TODO: check that args is a pair
 695   var empty-args?/eax: boolean <- nil? args
 696   compare empty-args?, 0/false
 697   {
 698     break-if-=
 699     error trace, "+ needs 2 args but got 0"
 700     return
 701   }
 702   # args->left->value
 703   var first-ah/eax: (addr handle cell) <- get args, left
 704   var first/eax: (addr cell) <- lookup *first-ah
 705   var first-type/ecx: (addr int) <- get first, type
 706   compare *first-type, 1/number
 707   {
 708     break-if-=
 709     error trace, "first arg for + is not a number"
 710     return
 711   }
 712   var first-value/ecx: (addr float) <- get first, number-data
 713   # args->right->left->value
 714   var right-ah/eax: (addr handle cell) <- get args, right
 715 #?   dump-cell right-ah
 716 #?   abort "aaa"
 717   var right/eax: (addr cell) <- lookup *right-ah
 718   # TODO: check that right is a pair
 719   var second-ah/eax: (addr handle cell) <- get right, left
 720   var second/eax: (addr cell) <- lookup *second-ah
 721   var second-type/edx: (addr int) <- get second, type
 722   compare *second-type, 1/number
 723   {
 724     break-if-=
 725     error trace, "second arg for + is not a number"
 726     return
 727   }
 728   var second-value/edx: (addr float) <- get second, number-data
 729   # add
 730   var result/xmm0: float <- copy *first-value
 731   result <- add *second-value
 732   new-float out, result
 733 }
 734 
 735 fn apply-subtract _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 736   trace-text trace, "eval", "apply -"
 737   var args-ah/eax: (addr handle cell) <- copy _args-ah
 738   var _args/eax: (addr cell) <- lookup *args-ah
 739   var args/esi: (addr cell) <- copy _args
 740   # TODO: check that args is a pair
 741   var empty-args?/eax: boolean <- nil? args
 742   compare empty-args?, 0/false
 743   {
 744     break-if-=
 745     error trace, "- needs 2 args but got 0"
 746     return
 747   }
 748   # args->left->value
 749   var first-ah/eax: (addr handle cell) <- get args, left
 750   var first/eax: (addr cell) <- lookup *first-ah
 751   var first-type/ecx: (addr int) <- get first, type
 752   compare *first-type, 1/number
 753   {
 754     break-if-=
 755     error trace, "first arg for - is not a number"
 756     return
 757   }
 758   var first-value/ecx: (addr float) <- get first, number-data
 759   # args->right->left->value
 760   var right-ah/eax: (addr handle cell) <- get args, right
 761   var right/eax: (addr cell) <- lookup *right-ah
 762   # TODO: check that right is a pair
 763   var second-ah/eax: (addr handle cell) <- get right, left
 764   var second/eax: (addr cell) <- lookup *second-ah
 765   var second-type/edx: (addr int) <- get second, type
 766   compare *second-type, 1/number
 767   {
 768     break-if-=
 769     error trace, "second arg for - is not a number"
 770     return
 771   }
 772   var second-value/edx: (addr float) <- get second, number-data
 773   # subtract
 774   var result/xmm0: float <- copy *first-value
 775   result <- subtract *second-value
 776   new-float out, result
 777 }
 778 
 779 fn apply-multiply _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 780   trace-text trace, "eval", "apply *"
 781   var args-ah/eax: (addr handle cell) <- copy _args-ah
 782   var _args/eax: (addr cell) <- lookup *args-ah
 783   var args/esi: (addr cell) <- copy _args
 784   # TODO: check that args is a pair
 785   var empty-args?/eax: boolean <- nil? args
 786   compare empty-args?, 0/false
 787   {
 788     break-if-=
 789     error trace, "* needs 2 args but got 0"
 790     return
 791   }
 792   # args->left->value
 793   var first-ah/eax: (addr handle cell) <- get args, left
 794   var first/eax: (addr cell) <- lookup *first-ah
 795   var first-type/ecx: (addr int) <- get first, type
 796   compare *first-type, 1/number
 797   {
 798     break-if-=
 799     error trace, "first arg for * is not a number"
 800     return
 801   }
 802   var first-value/ecx: (addr float) <- get first, number-data
 803   # args->right->left->value
 804   var right-ah/eax: (addr handle cell) <- get args, right
 805   var right/eax: (addr cell) <- lookup *right-ah
 806   # TODO: check that right is a pair
 807   var second-ah/eax: (addr handle cell) <- get right, left
 808   var second/eax: (addr cell) <- lookup *second-ah
 809   var second-type/edx: (addr int) <- get second, type
 810   compare *second-type, 1/number
 811   {
 812     break-if-=
 813     error trace, "second arg for * is not a number"
 814     return
 815   }
 816   var second-value/edx: (addr float) <- get second, number-data
 817   # multiply
 818   var result/xmm0: float <- copy *first-value
 819   result <- multiply *second-value
 820   new-float out, result
 821 }
 822 
 823 fn apply-divide _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 824   trace-text trace, "eval", "apply /"
 825   var args-ah/eax: (addr handle cell) <- copy _args-ah
 826   var _args/eax: (addr cell) <- lookup *args-ah
 827   var args/esi: (addr cell) <- copy _args
 828   # TODO: check that args is a pair
 829   var empty-args?/eax: boolean <- nil? args
 830   compare empty-args?, 0/false
 831   {
 832     break-if-=
 833     error trace, "/ needs 2 args but got 0"
 834     return
 835   }
 836   # args->left->value
 837   var first-ah/eax: (addr handle cell) <- get args, left
 838   var first/eax: (addr cell) <- lookup *first-ah
 839   var first-type/ecx: (addr int) <- get first, type
 840   compare *first-type, 1/number
 841   {
 842     break-if-=
 843     error trace, "first arg for / is not a number"
 844     return
 845   }
 846   var first-value/ecx: (addr float) <- get first, number-data
 847   # args->right->left->value
 848   var right-ah/eax: (addr handle cell) <- get args, right
 849   var right/eax: (addr cell) <- lookup *right-ah
 850   # TODO: check that right is a pair
 851   var second-ah/eax: (addr handle cell) <- get right, left
 852   var second/eax: (addr cell) <- lookup *second-ah
 853   var second-type/edx: (addr int) <- get second, type
 854   compare *second-type, 1/number
 855   {
 856     break-if-=
 857     error trace, "second arg for / is not a number"
 858     return
 859   }
 860   var second-value/edx: (addr float) <- get second, number-data
 861   # divide
 862   var result/xmm0: float <- copy *first-value
 863   result <- divide *second-value
 864   new-float out, result
 865 }
 866 
 867 fn apply-square-root _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 868   trace-text trace, "eval", "apply sqrt"
 869   var args-ah/eax: (addr handle cell) <- copy _args-ah
 870   var _args/eax: (addr cell) <- lookup *args-ah
 871   var args/esi: (addr cell) <- copy _args
 872   # TODO: check that args is a pair
 873   var empty-args?/eax: boolean <- nil? args
 874   compare empty-args?, 0/false
 875   {
 876     break-if-=
 877     error trace, "sqrt needs 1 args but got 0"
 878     return
 879   }
 880   # args->left->value
 881   var first-ah/eax: (addr handle cell) <- get args, left
 882   var first/eax: (addr cell) <- lookup *first-ah
 883   var first-type/ecx: (addr int) <- get first, type
 884   compare *first-type, 1/number
 885   {
 886     break-if-=
 887     error trace, "arg for sqrt is not a number"
 888     return
 889   }
 890   var first-value/ecx: (addr float) <- get first, number-data
 891   # square-root
 892   var result/xmm0: float <- square-root *first-value
 893   new-float out, result
 894 }
 895 
 896 fn apply-abs _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 897   trace-text trace, "eval", "apply abs"
 898   var args-ah/eax: (addr handle cell) <- copy _args-ah
 899   var _args/eax: (addr cell) <- lookup *args-ah
 900   var args/esi: (addr cell) <- copy _args
 901   # TODO: check that args is a pair
 902   var empty-args?/eax: boolean <- nil? args
 903   compare empty-args?, 0/false
 904   {
 905     break-if-=
 906     error trace, "abs needs 1 args but got 0"
 907     return
 908   }
 909   # args->left->value
 910   var first-ah/eax: (addr handle cell) <- get args, left
 911   var first/eax: (addr cell) <- lookup *first-ah
 912   var first-type/ecx: (addr int) <- get first, type
 913   compare *first-type, 1/number
 914   {
 915     break-if-=
 916     error trace, "arg for abs is not a number"
 917     return
 918   }
 919   var first-value/ecx: (addr float) <- get first, number-data
 920   #
 921   var result/xmm0: float <- copy *first-value
 922   var zero: float
 923   compare result, zero
 924   {
 925     break-if-float>=
 926     var neg1/eax: int <- copy -1
 927     var neg1-f/xmm1: float <- convert neg1
 928     result <- multiply neg1-f
 929   }
 930   new-float out, result
 931 }
 932 
 933 fn apply-sgn _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 934   trace-text trace, "eval", "apply sgn"
 935   var args-ah/eax: (addr handle cell) <- copy _args-ah
 936   var _args/eax: (addr cell) <- lookup *args-ah
 937   var args/esi: (addr cell) <- copy _args
 938   # TODO: check that args is a pair
 939   var empty-args?/eax: boolean <- nil? args
 940   compare empty-args?, 0/false
 941   {
 942     break-if-=
 943     error trace, "sgn needs 1 args but got 0"
 944     return
 945   }
 946   # args->left->value
 947   var first-ah/eax: (addr handle cell) <- get args, left
 948   var first/eax: (addr cell) <- lookup *first-ah
 949   var first-type/ecx: (addr int) <- get first, type
 950   compare *first-type, 1/number
 951   {
 952     break-if-=
 953     error trace, "arg for sgn is not a number"
 954     return
 955   }
 956   var first-value/ecx: (addr float) <- get first, number-data
 957   #
 958   var result/xmm0: float <- copy *first-value
 959   var zero: float
 960   $apply-sgn:core: {
 961     compare result, zero
 962     break-if-=
 963     {
 964       break-if-float>
 965       var neg1/eax: int <- copy -1
 966       result <- convert neg1
 967       break $apply-sgn:core
 968     }
 969     {
 970       break-if-float<
 971       var one/eax: int <- copy 1
 972       result <- convert one
 973       break $apply-sgn:core
 974     }
 975   }
 976   new-float out, result
 977 }
 978 
 979 fn apply-car _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
 980   trace-text trace, "eval", "apply car"
 981   var args-ah/eax: (addr handle cell) <- copy _args-ah
 982   var _args/eax: (addr cell) <- lookup *args-ah
 983   var args/esi: (addr cell) <- copy _args
 984   # TODO: check that args is a pair
 985   var empty-args?/eax: boolean <- nil? args
 986   compare empty-args?, 0/false
 987   {
 988     break-if-=
 989     error trace, "car needs 1 args but got 0"
 990     return
 991   }
 992   # args->left
 993   var first-ah/eax: (addr handle cell) <- get args, left
 994   var first/eax: (addr cell) <- lookup *first-ah
 995   var first-type/ecx: (addr int) <- get first, type
 996   compare *first-type, 0/pair
 997   {
 998     break-if-=
 999     error trace, "arg for car is not a pair"
1000     return
1001   }
1002   # car
1003   var result/eax: (addr handle cell) <- get first, left
1004   copy-object result, out
1005 }
1006 
1007 fn apply-cdr _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1008   trace-text trace, "eval", "apply cdr"
1009   var args-ah/eax: (addr handle cell) <- copy _args-ah
1010   var _args/eax: (addr cell) <- lookup *args-ah
1011   var args/esi: (addr cell) <- copy _args
1012   # TODO: check that args is a pair
1013   var empty-args?/eax: boolean <- nil? args
1014   compare empty-args?, 0/false
1015   {
1016     break-if-=
1017     error trace, "cdr needs 1 args but got 0"
1018     return
1019   }
1020   # args->left
1021   var first-ah/eax: (addr handle cell) <- get args, left
1022   var first/eax: (addr cell) <- lookup *first-ah
1023   var first-type/ecx: (addr int) <- get first, type
1024   compare *first-type, 0/pair
1025   {
1026     break-if-=
1027     error trace, "arg for cdr is not a pair"
1028     return
1029   }
1030   # cdr
1031   var result/eax: (addr handle cell) <- get first, right
1032   copy-object result, out
1033 }
1034 
1035 fn apply-cons _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1036   trace-text trace, "eval", "apply cons"
1037   var args-ah/eax: (addr handle cell) <- copy _args-ah
1038   var _args/eax: (addr cell) <- lookup *args-ah
1039   var args/esi: (addr cell) <- copy _args
1040   # TODO: check that args is a pair
1041   var empty-args?/eax: boolean <- nil? args
1042   compare empty-args?, 0/false
1043   {
1044     break-if-=
1045     error trace, "cons needs 2 args but got 0"
1046     return
1047   }
1048   # args->left
1049   var first-ah/ecx: (addr handle cell) <- get args, left
1050   # args->right->left
1051   var right-ah/eax: (addr handle cell) <- get args, right
1052   var right/eax: (addr cell) <- lookup *right-ah
1053   # TODO: check that right is a pair
1054   var second-ah/eax: (addr handle cell) <- get right, left
1055   # cons
1056   new-pair out, *first-ah, *second-ah
1057 }
1058 
1059 fn apply-structurally-equal _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1060   trace-text trace, "eval", "apply '='"
1061   var args-ah/eax: (addr handle cell) <- copy _args-ah
1062   var _args/eax: (addr cell) <- lookup *args-ah
1063   var args/esi: (addr cell) <- copy _args
1064   # TODO: check that args is a pair
1065   var empty-args?/eax: boolean <- nil? args
1066   compare empty-args?, 0/false
1067   {
1068     break-if-=
1069     error trace, "'=' needs 2 args but got 0"
1070     return
1071   }
1072   # args->left
1073   var first-ah/ecx: (addr handle cell) <- get args, left
1074   # args->right->left
1075   var right-ah/eax: (addr handle cell) <- get args, right
1076   var right/eax: (addr cell) <- lookup *right-ah
1077   # TODO: check that right is a pair
1078   var second-ah/edx: (addr handle cell) <- get right, left
1079   # compare
1080   var _first/eax: (addr cell) <- lookup *first-ah
1081   var first/ecx: (addr cell) <- copy _first
1082   var second/eax: (addr cell) <- lookup *second-ah
1083   var match?/eax: boolean <- cell-isomorphic? first, second, trace
1084   compare match?, 0/false
1085   {
1086     break-if-!=
1087     nil out
1088     return
1089   }
1090   new-integer out, 1/true
1091 }
1092 
1093 fn apply-not _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1094   trace-text trace, "eval", "apply not"
1095   var args-ah/eax: (addr handle cell) <- copy _args-ah
1096   var _args/eax: (addr cell) <- lookup *args-ah
1097   var args/esi: (addr cell) <- copy _args
1098   # TODO: check that args is a pair
1099   var empty-args?/eax: boolean <- nil? args
1100   compare empty-args?, 0/false
1101   {
1102     break-if-=
1103     error trace, "not needs 1 args but got 0"
1104     return
1105   }
1106   # args->left
1107   var first-ah/eax: (addr handle cell) <- get args, left
1108   var first/eax: (addr cell) <- lookup *first-ah
1109   # not
1110   var nil?/eax: boolean <- nil? first
1111   compare nil?, 0/false
1112   {
1113     break-if-!=
1114     nil out
1115     return
1116   }
1117   new-integer out, 1
1118 }
1119 
1120 fn apply-< _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1121   trace-text trace, "eval", "apply '<'"
1122   var args-ah/eax: (addr handle cell) <- copy _args-ah
1123   var _args/eax: (addr cell) <- lookup *args-ah
1124   var args/esi: (addr cell) <- copy _args
1125   # TODO: check that args is a pair
1126   var empty-args?/eax: boolean <- nil? args
1127   compare empty-args?, 0/false
1128   {
1129     break-if-=
1130     error trace, "'<' needs 2 args but got 0"
1131     return
1132   }
1133   # args->left
1134   var first-ah/ecx: (addr handle cell) <- get args, left
1135   # args->right->left
1136   var right-ah/eax: (addr handle cell) <- get args, right
1137   var right/eax: (addr cell) <- lookup *right-ah
1138   # TODO: check that right is a pair
1139   var second-ah/edx: (addr handle cell) <- get right, left
1140   # compare
1141   var _first/eax: (addr cell) <- lookup *first-ah
1142   var first/ecx: (addr cell) <- copy _first
1143   var first-type/eax: (addr int) <- get first, type
1144   compare *first-type, 1/number
1145   {
1146     break-if-=
1147     error trace, "first arg for '<' is not a number"
1148     return
1149   }
1150   var first-value/ecx: (addr float) <- get first, number-data
1151   var first-float/xmm0: float <- copy *first-value
1152   var second/eax: (addr cell) <- lookup *second-ah
1153   var second-type/edx: (addr int) <- get second, type
1154   compare *second-type, 1/number
1155   {
1156     break-if-=
1157     error trace, "first arg for '<' is not a number"
1158     return
1159   }
1160   var second-value/eax: (addr float) <- get second, number-data
1161   compare first-float, *second-value
1162   {
1163     break-if-float<
1164     nil out
1165     return
1166   }
1167   new-integer out, 1/true
1168 }
1169 
1170 fn apply-> _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1171   trace-text trace, "eval", "apply '>'"
1172   var args-ah/eax: (addr handle cell) <- copy _args-ah
1173   var _args/eax: (addr cell) <- lookup *args-ah
1174   var args/esi: (addr cell) <- copy _args
1175   # TODO: check that args is a pair
1176   var empty-args?/eax: boolean <- nil? args
1177   compare empty-args?, 0/false
1178   {
1179     break-if-=
1180     error trace, "'>' needs 2 args but got 0"
1181     return
1182   }
1183   # args->left
1184   var first-ah/ecx: (addr handle cell) <- get args, left
1185   # args->right->left
1186   var right-ah/eax: (addr handle cell) <- get args, right
1187   var right/eax: (addr cell) <- lookup *right-ah
1188   # TODO: check that right is a pair
1189   var second-ah/edx: (addr handle cell) <- get right, left
1190   # compare
1191   var _first/eax: (addr cell) <- lookup *first-ah
1192   var first/ecx: (addr cell) <- copy _first
1193   var first-type/eax: (addr int) <- get first, type
1194   compare *first-type, 1/number
1195   {
1196     break-if-=
1197     error trace, "first arg for '>' is not a number"
1198     return
1199   }
1200   var first-value/ecx: (addr float) <- get first, number-data
1201   var first-float/xmm0: float <- copy *first-value
1202   var second/eax: (addr cell) <- lookup *second-ah
1203   var second-type/edx: (addr int) <- get second, type
1204   compare *second-type, 1/number
1205   {
1206     break-if-=
1207     error trace, "first arg for '>' is not a number"
1208     return
1209   }
1210   var second-value/eax: (addr float) <- get second, number-data
1211   compare first-float, *second-value
1212   {
1213     break-if-float>
1214     nil out
1215     return
1216   }
1217   new-integer out, 1/true
1218 }
1219 
1220 fn apply-<= _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1221   trace-text trace, "eval", "apply '<='"
1222   var args-ah/eax: (addr handle cell) <- copy _args-ah
1223   var _args/eax: (addr cell) <- lookup *args-ah
1224   var args/esi: (addr cell) <- copy _args
1225   # TODO: check that args is a pair
1226   var empty-args?/eax: boolean <- nil? args
1227   compare empty-args?, 0/false
1228   {
1229     break-if-=
1230     error trace, "'<=' needs 2 args but got 0"
1231     return
1232   }
1233   # args->left
1234   var first-ah/ecx: (addr handle cell) <- get args, left
1235   # args->right->left
1236   var right-ah/eax: (addr handle cell) <- get args, right
1237   var right/eax: (addr cell) <- lookup *right-ah
1238   # TODO: check that right is a pair
1239   var second-ah/edx: (addr handle cell) <- get right, left
1240   # compare
1241   var _first/eax: (addr cell) <- lookup *first-ah
1242   var first/ecx: (addr cell) <- copy _first
1243   var first-type/eax: (addr int) <- get first, type
1244   compare *first-type, 1/number
1245   {
1246     break-if-=
1247     error trace, "first arg for '<=' is not a number"
1248     return
1249   }
1250   var first-value/ecx: (addr float) <- get first, number-data
1251   var first-float/xmm0: float <- copy *first-value
1252   var second/eax: (addr cell) <- lookup *second-ah
1253   var second-type/edx: (addr int) <- get second, type
1254   compare *second-type, 1/number
1255   {
1256     break-if-=
1257     error trace, "first arg for '<=' is not a number"
1258     return
1259   }
1260   var second-value/eax: (addr float) <- get second, number-data
1261   compare first-float, *second-value
1262   {
1263     break-if-float<=
1264     nil out
1265     return
1266   }
1267   new-integer out, 1/true
1268 }
1269 
1270 fn apply->= _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1271   trace-text trace, "eval", "apply '>='"
1272   var args-ah/eax: (addr handle cell) <- copy _args-ah
1273   var _args/eax: (addr cell) <- lookup *args-ah
1274   var args/esi: (addr cell) <- copy _args
1275   # TODO: check that args is a pair
1276   var empty-args?/eax: boolean <- nil? args
1277   compare empty-args?, 0/false
1278   {
1279     break-if-=
1280     error trace, "'>=' needs 2 args but got 0"
1281     return
1282   }
1283   # args->left
1284   var first-ah/ecx: (addr handle cell) <- get args, left
1285   # args->right->left
1286   var right-ah/eax: (addr handle cell) <- get args, right
1287   var right/eax: (addr cell) <- lookup *right-ah
1288   # TODO: check that right is a pair
1289   var second-ah/edx: (addr handle cell) <- get right, left
1290   # compare
1291   var _first/eax: (addr cell) <- lookup *first-ah
1292   var first/ecx: (addr cell) <- copy _first
1293   var first-type/eax: (addr int) <- get first, type
1294   compare *first-type, 1/number
1295   {
1296     break-if-=
1297     error trace, "first arg for '>=' is not a number"
1298     return
1299   }
1300   var first-value/ecx: (addr float) <- get first, number-data
1301   var first-float/xmm0: float <- copy *first-value
1302   var second/eax: (addr cell) <- lookup *second-ah
1303   var second-type/edx: (addr int) <- get second, type
1304   compare *second-type, 1/number
1305   {
1306     break-if-=
1307     error trace, "first arg for '>=' is not a number"
1308     return
1309   }
1310   var second-value/eax: (addr float) <- get second, number-data
1311   compare first-float, *second-value
1312   {
1313     break-if-float>=
1314     nil out
1315     return
1316   }
1317   new-integer out, 1/true
1318 }
1319 
1320 fn apply-print _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1321   trace-text trace, "eval", "apply print"
1322   var args-ah/eax: (addr handle cell) <- copy _args-ah
1323   var _args/eax: (addr cell) <- lookup *args-ah
1324   var args/esi: (addr cell) <- copy _args
1325   # TODO: check that args is a pair
1326   var empty-args?/eax: boolean <- nil? args
1327   compare empty-args?, 0/false
1328   {
1329     break-if-=
1330     error trace, "print needs 2 args but got 0"
1331     return
1332   }
1333   # screen = args->left
1334   var first-ah/eax: (addr handle cell) <- get args, left
1335   var first/eax: (addr cell) <- lookup *first-ah
1336   var first-type/ecx: (addr int) <- get first, type
1337   compare *first-type, 5/screen
1338   {
1339     break-if-=
1340     error trace, "first arg for 'print' is not a screen"
1341     return
1342   }
1343   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1344   var _screen/eax: (addr screen) <- lookup *screen-ah
1345   var screen/ecx: (addr screen) <- copy _screen
1346   # args->right->left
1347   var right-ah/eax: (addr handle cell) <- get args, right
1348   var right/eax: (addr cell) <- lookup *right-ah
1349   # TODO: check that right is a pair
1350   var second-ah/eax: (addr handle cell) <- get right, left
1351   var stream-storage: (stream byte 0x100)
1352   var stream/edi: (addr stream byte) <- address stream-storage
1353   print-cell second-ah, stream, trace
1354   draw-stream-wrapping-right-then-down-from-cursor-over-full-screen screen, stream, 7/fg, 0/bg
1355   # return what was printed
1356   copy-object second-ah, out
1357 }
1358 
1359 fn apply-clear _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1360   trace-text trace, "eval", "apply clear"
1361   var args-ah/eax: (addr handle cell) <- copy _args-ah
1362   var _args/eax: (addr cell) <- lookup *args-ah
1363   var args/esi: (addr cell) <- copy _args
1364   # TODO: check that args is a pair
1365   var empty-args?/eax: boolean <- nil? args
1366   compare empty-args?, 0/false
1367   {
1368     break-if-=
1369     error trace, "'clear' needs 1 arg but got 0"
1370     return
1371   }
1372   # screen = args->left
1373   var first-ah/eax: (addr handle cell) <- get args, left
1374   var first/eax: (addr cell) <- lookup *first-ah
1375   var first-type/ecx: (addr int) <- get first, type
1376   compare *first-type, 5/screen
1377   {
1378     break-if-=
1379     error trace, "first arg for 'clear' is not a screen"
1380     return
1381   }
1382   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1383   var _screen/eax: (addr screen) <- lookup *screen-ah
1384   var screen/ecx: (addr screen) <- copy _screen
1385   #
1386   clear-screen screen
1387 }
1388 
1389 fn apply-up _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1390   trace-text trace, "eval", "apply up"
1391   var args-ah/eax: (addr handle cell) <- copy _args-ah
1392   var _args/eax: (addr cell) <- lookup *args-ah
1393   var args/esi: (addr cell) <- copy _args
1394   # TODO: check that args is a pair
1395   var empty-args?/eax: boolean <- nil? args
1396   compare empty-args?, 0/false
1397   {
1398     break-if-=
1399     error trace, "'up' needs 1 arg but got 0"
1400     return
1401   }
1402   # screen = args->left
1403   var first-ah/eax: (addr handle cell) <- get args, left
1404   var first/eax: (addr cell) <- lookup *first-ah
1405   var first-type/ecx: (addr int) <- get first, type
1406   compare *first-type, 5/screen
1407   {
1408     break-if-=
1409     error trace, "first arg for 'up' is not a screen"
1410     return
1411   }
1412   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1413   var _screen/eax: (addr screen) <- lookup *screen-ah
1414   var screen/ecx: (addr screen) <- copy _screen
1415   #
1416   move-cursor-up screen
1417 }
1418 
1419 fn apply-down _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1420   trace-text trace, "eval", "apply 'down'"
1421   var args-ah/eax: (addr handle cell) <- copy _args-ah
1422   var _args/eax: (addr cell) <- lookup *args-ah
1423   var args/esi: (addr cell) <- copy _args
1424   # TODO: check that args is a pair
1425   var empty-args?/eax: boolean <- nil? args
1426   compare empty-args?, 0/false
1427   {
1428     break-if-=
1429     error trace, "'down' needs 1 arg but got 0"
1430     return
1431   }
1432   # screen = args->left
1433   var first-ah/eax: (addr handle cell) <- get args, left
1434   var first/eax: (addr cell) <- lookup *first-ah
1435   var first-type/ecx: (addr int) <- get first, type
1436   compare *first-type, 5/screen
1437   {
1438     break-if-=
1439     error trace, "first arg for 'down' is not a screen"
1440     return
1441   }
1442   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1443   var _screen/eax: (addr screen) <- lookup *screen-ah
1444   var screen/ecx: (addr screen) <- copy _screen
1445   #
1446   move-cursor-down screen
1447 }
1448 
1449 fn apply-left _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1450   trace-text trace, "eval", "apply 'left'"
1451   var args-ah/eax: (addr handle cell) <- copy _args-ah
1452   var _args/eax: (addr cell) <- lookup *args-ah
1453   var args/esi: (addr cell) <- copy _args
1454   # TODO: check that args is a pair
1455   var empty-args?/eax: boolean <- nil? args
1456   compare empty-args?, 0/false
1457   {
1458     break-if-=
1459     error trace, "'left' needs 1 arg but got 0"
1460     return
1461   }
1462   # screen = args->left
1463   var first-ah/eax: (addr handle cell) <- get args, left
1464   var first/eax: (addr cell) <- lookup *first-ah
1465   var first-type/ecx: (addr int) <- get first, type
1466   compare *first-type, 5/screen
1467   {
1468     break-if-=
1469     error trace, "first arg for 'left' is not a screen"
1470     return
1471   }
1472   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1473   var _screen/eax: (addr screen) <- lookup *screen-ah
1474   var screen/ecx: (addr screen) <- copy _screen
1475   #
1476   move-cursor-left screen
1477 }
1478 
1479 fn apply-right _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1480   trace-text trace, "eval", "apply 'right'"
1481   var args-ah/eax: (addr handle cell) <- copy _args-ah
1482   var _args/eax: (addr cell) <- lookup *args-ah
1483   var args/esi: (addr cell) <- copy _args
1484   # TODO: check that args is a pair
1485   var empty-args?/eax: boolean <- nil? args
1486   compare empty-args?, 0/false
1487   {
1488     break-if-=
1489     error trace, "'right' needs 1 arg but got 0"
1490     return
1491   }
1492   # screen = args->left
1493   var first-ah/eax: (addr handle cell) <- get args, left
1494   var first/eax: (addr cell) <- lookup *first-ah
1495   var first-type/ecx: (addr int) <- get first, type
1496   compare *first-type, 5/screen
1497   {
1498     break-if-=
1499     error trace, "first arg for 'right' is not a screen"
1500     return
1501   }
1502   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1503   var _screen/eax: (addr screen) <- lookup *screen-ah
1504   var screen/ecx: (addr screen) <- copy _screen
1505   #
1506   move-cursor-right screen
1507 }
1508 
1509 fn apply-cr _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1510   trace-text trace, "eval", "apply 'cr'"
1511   var args-ah/eax: (addr handle cell) <- copy _args-ah
1512   var _args/eax: (addr cell) <- lookup *args-ah
1513   var args/esi: (addr cell) <- copy _args
1514   # TODO: check that args is a pair
1515   var empty-args?/eax: boolean <- nil? args
1516   compare empty-args?, 0/false
1517   {
1518     break-if-=
1519     error trace, "'cr' needs 1 arg but got 0"
1520     return
1521   }
1522   # screen = args->left
1523   var first-ah/eax: (addr handle cell) <- get args, left
1524   var first/eax: (addr cell) <- lookup *first-ah
1525   var first-type/ecx: (addr int) <- get first, type
1526   compare *first-type, 5/screen
1527   {
1528     break-if-=
1529     error trace, "first arg for 'cr' is not a screen"
1530     return
1531   }
1532   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1533   var _screen/eax: (addr screen) <- lookup *screen-ah
1534   var screen/ecx: (addr screen) <- copy _screen
1535   #
1536   move-cursor-to-left-margin-of-next-line screen
1537 }
1538 
1539 fn apply-pixel _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1540   trace-text trace, "eval", "apply pixel"
1541   var args-ah/eax: (addr handle cell) <- copy _args-ah
1542   var _args/eax: (addr cell) <- lookup *args-ah
1543   var args/esi: (addr cell) <- copy _args
1544   # TODO: check that args is a pair
1545   var empty-args?/eax: boolean <- nil? args
1546   compare empty-args?, 0/false
1547   {
1548     break-if-=
1549     error trace, "pixel needs 4 args but got 0"
1550     return
1551   }
1552   # screen = args->left
1553   var first-ah/eax: (addr handle cell) <- get args, left
1554   var first/eax: (addr cell) <- lookup *first-ah
1555   var first-type/ecx: (addr int) <- get first, type
1556   compare *first-type, 5/screen
1557   {
1558     break-if-=
1559     error trace, "first arg for 'pixel' is not a screen"
1560     return
1561   }
1562   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1563   var _screen/eax: (addr screen) <- lookup *screen-ah
1564   var screen/edi: (addr screen) <- copy _screen
1565   # x = args->right->left->value
1566   var rest-ah/eax: (addr handle cell) <- get args, right
1567   var _rest/eax: (addr cell) <- lookup *rest-ah
1568   var rest/esi: (addr cell) <- copy _rest
1569   # TODO: check that rest is a pair
1570   var second-ah/eax: (addr handle cell) <- get rest, left
1571   var second/eax: (addr cell) <- lookup *second-ah
1572   var second-type/ecx: (addr int) <- get second, type
1573   compare *second-type, 1/number
1574   {
1575     break-if-=
1576     error trace, "second arg for 'pixel' is not an int (x coordinate)"
1577     return
1578   }
1579   var second-value/eax: (addr float) <- get second, number-data
1580   var x/edx: int <- convert *second-value
1581   # y = rest->right->left->value
1582   var rest-ah/eax: (addr handle cell) <- get rest, right
1583   var _rest/eax: (addr cell) <- lookup *rest-ah
1584   rest <- copy _rest
1585   # TODO: check that rest is a pair
1586   var third-ah/eax: (addr handle cell) <- get rest, left
1587   var third/eax: (addr cell) <- lookup *third-ah
1588   var third-type/ecx: (addr int) <- get third, type
1589   compare *third-type, 1/number
1590   {
1591     break-if-=
1592     error trace, "third arg for 'pixel' is not an int (y coordinate)"
1593     return
1594   }
1595   var third-value/eax: (addr float) <- get third, number-data
1596   var y/ebx: int <- convert *third-value
1597   # color = rest->right->left->value
1598   var rest-ah/eax: (addr handle cell) <- get rest, right
1599   var _rest/eax: (addr cell) <- lookup *rest-ah
1600   rest <- copy _rest
1601   # TODO: check that rest is a pair
1602   var fourth-ah/eax: (addr handle cell) <- get rest, left
1603   var fourth/eax: (addr cell) <- lookup *fourth-ah
1604   var fourth-type/ecx: (addr int) <- get fourth, type
1605   compare *fourth-type, 1/number
1606   {
1607     break-if-=
1608     error trace, "fourth arg for 'pixel' is not an int (color; 0..0xff)"
1609     return
1610   }
1611   var fourth-value/eax: (addr float) <- get fourth, number-data
1612   var color/eax: int <- convert *fourth-value
1613   pixel screen, x, y, color
1614   # return nothing
1615 }
1616 
1617 fn apply-wait-for-key _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1618   trace-text trace, "eval", "apply key"
1619   var args-ah/eax: (addr handle cell) <- copy _args-ah
1620   var _args/eax: (addr cell) <- lookup *args-ah
1621   var args/esi: (addr cell) <- copy _args
1622   # TODO: check that args is a pair
1623   var empty-args?/eax: boolean <- nil? args
1624   compare empty-args?, 0/false
1625   {
1626     break-if-=
1627     error trace, "key needs 1 arg but got 0"
1628     return
1629   }
1630   # keyboard = args->left
1631   var first-ah/eax: (addr handle cell) <- get args, left
1632   var first/eax: (addr cell) <- lookup *first-ah
1633   var first-type/ecx: (addr int) <- get first, type
1634   compare *first-type, 6/keyboard
1635   {
1636     break-if-=
1637     error trace, "first arg for 'key' is not a keyboard"
1638     return
1639   }
1640   var keyboard-ah/eax: (addr handle gap-buffer) <- get first, keyboard-data
1641   var _keyboard/eax: (addr gap-buffer) <- lookup *keyboard-ah
1642   var keyboard/ecx: (addr gap-buffer) <- copy _keyboard
1643   var result/eax: int <- wait-for-key keyboard
1644   # return key typed
1645   new-integer out, result
1646 }
1647 
1648 fn wait-for-key keyboard: (addr gap-buffer) -> _/eax: int {
1649   # if keyboard is 0, use real keyboard
1650   {
1651     compare keyboard, 0/real-keyboard
1652     break-if-!=
1653     var key/eax: byte <- read-key 0/real-keyboard
1654     var result/eax: int <- copy key
1655     return result
1656   }
1657   # otherwise read from fake keyboard
1658   var g/eax: grapheme <- read-from-gap-buffer keyboard
1659   var result/eax: int <- copy g
1660   return result
1661 }
1662 
1663 fn apply-stream _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1664   trace-text trace, "eval", "apply stream"
1665   allocate-stream out
1666 }
1667 
1668 fn apply-write _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1669   trace-text trace, "eval", "apply write"
1670   var args-ah/eax: (addr handle cell) <- copy _args-ah
1671   var _args/eax: (addr cell) <- lookup *args-ah
1672   var args/esi: (addr cell) <- copy _args
1673   # TODO: check that args is a pair
1674   var empty-args?/eax: boolean <- nil? args
1675   compare empty-args?, 0/false
1676   {
1677     break-if-=
1678     error trace, "write needs 2 args but got 0"
1679     return
1680   }
1681   # stream = args->left
1682   var first-ah/edx: (addr handle cell) <- get args, left
1683   var first/eax: (addr cell) <- lookup *first-ah
1684   var first-type/ecx: (addr int) <- get first, type
1685   compare *first-type, 3/stream
1686   {
1687     break-if-=
1688     error trace, "first arg for 'write' is not a stream"
1689     return
1690   }
1691   var stream-data-ah/eax: (addr handle stream byte) <- get first, text-data
1692   var _stream-data/eax: (addr stream byte) <- lookup *stream-data-ah
1693   var stream-data/ebx: (addr stream byte) <- copy _stream-data
1694   # args->right->left
1695   var right-ah/eax: (addr handle cell) <- get args, right
1696   var right/eax: (addr cell) <- lookup *right-ah
1697   # TODO: check that right is a pair
1698   var second-ah/eax: (addr handle cell) <- get right, left
1699   var second/eax: (addr cell) <- lookup *second-ah
1700   var second-type/ecx: (addr int) <- get second, type
1701   compare *second-type, 1/number
1702   {
1703     break-if-=
1704     error trace, "second arg for stream is not a number/grapheme"
1705     return
1706   }
1707   var second-value/eax: (addr float) <- get second, number-data
1708   var x-float/xmm0: float <- copy *second-value
1709   var x/eax: int <- convert x-float
1710   var x-grapheme/eax: grapheme <- copy x
1711   write-grapheme stream-data, x-grapheme
1712   # return the stream
1713   copy-object first-ah, out
1714 }
1715 
1716 fn apply-lines _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1717   trace-text trace, "eval", "apply lines"
1718   var args-ah/eax: (addr handle cell) <- copy _args-ah
1719   var _args/eax: (addr cell) <- lookup *args-ah
1720   var args/esi: (addr cell) <- copy _args
1721   # TODO: check that args is a pair
1722   var empty-args?/eax: boolean <- nil? args
1723   compare empty-args?, 0/false
1724   {
1725     break-if-=
1726     error trace, "lines needs 1 arg but got 0"
1727     return
1728   }
1729   # screen = args->left
1730   var first-ah/eax: (addr handle cell) <- get args, left
1731   var first/eax: (addr cell) <- lookup *first-ah
1732   var first-type/ecx: (addr int) <- get first, type
1733   compare *first-type, 5/screen
1734   {
1735     break-if-=
1736     error trace, "first arg for 'lines' is not a screen"
1737     return
1738   }
1739   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1740   var _screen/eax: (addr screen) <- lookup *screen-ah
1741   var screen/edx: (addr screen) <- copy _screen
1742   # compute dimensions
1743   var dummy/eax: int <- copy 0
1744   var height/ecx: int <- copy 0
1745   dummy, height <- screen-size screen
1746   var result/xmm0: float <- convert height
1747   new-float out, result
1748 }
1749 
1750 fn apply-abort _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1751   abort "aa"
1752 }
1753 
1754 fn apply-columns _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1755   trace-text trace, "eval", "apply columns"
1756   var args-ah/eax: (addr handle cell) <- copy _args-ah
1757   var _args/eax: (addr cell) <- lookup *args-ah
1758   var args/esi: (addr cell) <- copy _args
1759   # TODO: check that args is a pair
1760   var empty-args?/eax: boolean <- nil? args
1761   compare empty-args?, 0/false
1762   {
1763     break-if-=
1764     error trace, "columns needs 1 arg but got 0"
1765     return
1766   }
1767   # screen = args->left
1768   var first-ah/eax: (addr handle cell) <- get args, left
1769   var first/eax: (addr cell) <- lookup *first-ah
1770   var first-type/ecx: (addr int) <- get first, type
1771   compare *first-type, 5/screen
1772   {
1773     break-if-=
1774     error trace, "first arg for 'columns' is not a screen"
1775     return
1776   }
1777   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1778   var _screen/eax: (addr screen) <- lookup *screen-ah
1779   var screen/edx: (addr screen) <- copy _screen
1780   # compute dimensions
1781   var width/eax: int <- copy 0
1782   var dummy/ecx: int <- copy 0
1783   width, dummy <- screen-size screen
1784   var result/xmm0: float <- convert width
1785   new-float out, result
1786 }
1787 
1788 fn apply-width _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1789   trace-text trace, "eval", "apply width"
1790   var args-ah/eax: (addr handle cell) <- copy _args-ah
1791   var _args/eax: (addr cell) <- lookup *args-ah
1792   var args/esi: (addr cell) <- copy _args
1793   # TODO: check that args is a pair
1794   var empty-args?/eax: boolean <- nil? args
1795   compare empty-args?, 0/false
1796   {
1797     break-if-=
1798     error trace, "width needs 1 arg but got 0"
1799     return
1800   }
1801   # screen = args->left
1802   var first-ah/eax: (addr handle cell) <- get args, left
1803   var first/eax: (addr cell) <- lookup *first-ah
1804   var first-type/ecx: (addr int) <- get first, type
1805   compare *first-type, 5/screen
1806   {
1807     break-if-=
1808     error trace, "first arg for 'width' is not a screen"
1809     return
1810   }
1811   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1812   var _screen/eax: (addr screen) <- lookup *screen-ah
1813   var screen/edx: (addr screen) <- copy _screen
1814   # compute dimensions
1815   var width/eax: int <- copy 0
1816   var dummy/ecx: int <- copy 0
1817   width, dummy <- screen-size screen
1818   width <- shift-left 3/log2-font-width
1819   var result/xmm0: float <- convert width
1820   new-float out, result
1821 }
1822 
1823 fn apply-height _args-ah: (addr handle cell), out: (addr handle cell), trace: (addr trace) {
1824   trace-text trace, "eval", "apply height"
1825   var args-ah/eax: (addr handle cell) <- copy _args-ah
1826   var _args/eax: (addr cell) <- lookup *args-ah
1827   var args/esi: (addr cell) <- copy _args
1828   # TODO: check that args is a pair
1829   var empty-args?/eax: boolean <- nil? args
1830   compare empty-args?, 0/false
1831   {
1832     break-if-=
1833     error trace, "height needs 1 arg but got 0"
1834     return
1835   }
1836   # screen = args->left
1837   var first-ah/eax: (addr handle cell) <- get args, left
1838   var first/eax: (addr cell) <- lookup *first-ah
1839   var first-type/ecx: (addr int) <- get first, type
1840   compare *first-type, 5/screen
1841   {
1842     break-if-=
1843     error trace, "first arg for 'height' is not a screen"
1844     return
1845   }
1846   var screen-ah/eax: (addr handle screen) <- get first, screen-data
1847   var _screen/eax: (addr screen) <- lookup *screen-ah
1848   var screen/edx: (addr screen) <- copy _screen
1849   # compute dimensions
1850   var dummy/eax: int <- copy 0
1851   var height/ecx: int <- copy 0
1852   dummy, height <- screen-size screen
1853   height <- shift-left 4/log2-font-height
1854   var result/xmm0: float <- convert height
1855   new-float out, result
1856 }
1857 
1858 # Accepts an input s-expression, naively checks if it is a definition, and if
1859 # so saves the gap-buffer to the appropriate global, spinning up a new empty
1860 # one to replace it with.
1861 fn maybe-stash-gap-buffer-to-global _globals: (addr global-table), _definition-ah: (addr handle cell), gap: (addr handle gap-buffer) {
1862   # if 'definition' is not a pair, return
1863   var definition-ah/eax: (addr handle cell) <- copy _definition-ah
1864   var _definition/eax: (addr cell) <- lookup *definition-ah
1865   var definition/esi: (addr cell) <- copy _definition
1866   var definition-type/eax: (addr int) <- get definition, type
1867   compare *definition-type, 0/pair
1868   {
1869     break-if-=
1870     return
1871   }
1872   # if definition->left is neither "def" nor "set", return
1873   var left-ah/eax: (addr handle cell) <- get definition, left
1874   var _left/eax: (addr cell) <- lookup *left-ah
1875   var left/ecx: (addr cell) <- copy _left
1876   {
1877     var def?/eax: boolean <- symbol-equal? left, "def"
1878     compare def?, 0/false
1879     break-if-!=
1880     var set?/eax: boolean <- symbol-equal? left, "set"
1881     compare set?, 0/false
1882     break-if-!=
1883     return
1884   }
1885   # locate the global for definition->right->left
1886   var right-ah/eax: (addr handle cell) <- get definition, right
1887   var right/eax: (addr cell) <- lookup *right-ah
1888   var defined-symbol-ah/eax: (addr handle cell) <- get right, left
1889   var defined-symbol/eax: (addr cell) <- lookup *defined-symbol-ah
1890   var defined-symbol-name-ah/eax: (addr handle stream byte) <- get defined-symbol, text-data
1891   var defined-symbol-name/eax: (addr stream byte) <- lookup *defined-symbol-name-ah
1892   var index/ecx: int <- find-symbol-in-globals _globals, defined-symbol-name
1893   {
1894     compare index, -1/not-found
1895     break-if-!=
1896     return
1897   }
1898   # stash 'gap' to it
1899   var globals/eax: (addr global-table) <- copy _globals
1900   var global-data-ah/eax: (addr handle array global) <- get globals, data
1901   var global-data/eax: (addr array global) <- lookup *global-data-ah
1902   var offset/ebx: (offset global) <- compute-offset global-data, index
1903   var dest-global/eax: (addr global) <- index global-data, offset
1904   var dest-ah/eax: (addr handle gap-buffer) <- get dest-global, input
1905   copy-object gap, dest-ah
1906   # initialize a new gap-buffer in 'gap'
1907   var dest/eax: (addr gap-buffer) <- lookup *dest-ah
1908   var capacity/ecx: int <- gap-buffer-capacity dest
1909   var gap2/eax: (addr handle gap-buffer) <- copy gap
1910   allocate gap2
1911   var gap-addr/eax: (addr gap-buffer) <- lookup *gap2
1912   initialize-gap-buffer gap-addr, capacity
1913 }
1914 
1915 # Accepts an input s-expression, naively checks if it is a definition, and if
1916 # so saves the gap-buffer to the appropriate global.
1917 fn move-gap-buffer-to-global _globals: (addr global-table), _definition-ah: (addr handle cell), gap: (addr handle gap-buffer) {
1918   # if 'definition' is not a pair, return
1919   var definition-ah/eax: (addr handle cell) <- copy _definition-ah
1920   var _definition/eax: (addr cell) <- lookup *definition-ah
1921   var definition/esi: (addr cell) <- copy _definition
1922   var definition-type/eax: (addr int) <- get definition, type
1923   compare *definition-type, 0/pair
1924   {
1925     break-if-=
1926     return
1927   }
1928   # if definition->left is neither "def" nor "set", return
1929   var left-ah/eax: (addr handle cell) <- get definition, left
1930   var _left/eax: (addr cell) <- lookup *left-ah
1931   var left/ecx: (addr cell) <- copy _left
1932   {
1933     var def?/eax: boolean <- symbol-equal? left, "def"
1934     compare def?, 0/false
1935     break-if-!=
1936     var set?/eax: boolean <- symbol-equal? left, "set"
1937     compare set?, 0/false
1938     break-if-!=
1939     return
1940   }
1941   # locate the global for definition->right->left
1942   var right-ah/eax: (addr handle cell) <- get definition, right
1943   var right/eax: (addr cell) <- lookup *right-ah
1944   var defined-symbol-ah/eax: (addr handle cell) <- get right, left
1945   var defined-symbol/eax: (addr cell) <- lookup *defined-symbol-ah
1946   var defined-symbol-name-ah/eax: (addr handle stream byte) <- get defined-symbol, text-data
1947   var defined-symbol-name/eax: (addr stream byte) <- lookup *defined-symbol-name-ah
1948   var index/ecx: int <- find-symbol-in-globals _globals, defined-symbol-name
1949   {
1950     compare index, -1/not-found
1951     break-if-!=
1952     return
1953   }
1954   # move 'gap' to it
1955   var globals/eax: (addr global-table) <- copy _globals
1956   var global-data-ah/eax: (addr handle array global) <- get globals, data
1957   var global-data/eax: (addr array global) <- lookup *global-data-ah
1958   var offset/ebx: (offset global) <- compute-offset global-data, index
1959   var dest-global/eax: (addr global) <- index global-data, offset
1960   var dest-ah/eax: (addr handle gap-buffer) <- get dest-global, input
1961   copy-object gap, dest-ah
1962 }