https://github.com/akkartik/mu/blob/main/shell/trace.mu
   1 # A trace records the evolution of a computation.
   2 # Traces are useful for:
   3 #   error-handling
   4 #   testing
   5 #   auditing
   6 #   debugging
   7 #   learning
   8 #
   9 # An integral part of the Mu computer is facilities for browsing traces.
  10 
  11 type trace {
  12   max-depth: int
  13   curr-depth: int  # depth that will be assigned to next line appended
  14   data: (handle array trace-line)
  15   first-free: int
  16   first-full: int  # used only by check-trace-scan
  17 
  18   # steady-state life cycle of a trace:
  19   #   reload loop:
  20   #     there are already some visible lines
  21   #     append a bunch of new trace lines to the trace
  22   #     recreate trace caches
  23   #     render loop:
  24   #       rendering displays trace lines that match visible lines
  25   #         (caching in each line)
  26   #         (caching top-line)
  27   #       rendering computes cursor-line based on the cursor-y coordinate
  28   #       edit-trace updates cursor-y coordinate
  29   #       edit-trace might add/remove lines to visible
  30   #       edit-trace might update top-line
  31   visible: (handle array trace-line)
  32   recreate-caches?: boolean
  33   cursor-line-index: int  # index into data
  34   cursor-y: int  # row index on screen
  35   unclip-cursor-line?: boolean  # extremely short-lived; reset any time cursor moves
  36   top-line-index: int  # start rendering trace past this index into data (updated on re-evaluation)
  37   top-line-y: int  # trace starts rendering at this row index on screen (updated on re-evaluation)
  38   screen-height: int  # initialized during render-trace
  39 }
  40 
  41 type trace-line {
  42   depth: int
  43   label: (handle array byte)
  44   data: (handle array byte)
  45   visible?: boolean
  46 }
  47 
  48 # when we recreate the trace this data structure will help stabilize our view into it
  49 # we can shallowly copy handles because lines are not reused across reruns
  50 type trace-index-stash {
  51   cursor-line-depth: int
  52   cursor-line-label: (handle array byte)
  53   cursor-line-data: (handle array byte)
  54   top-line-depth: int
  55   top-line-label: (handle array byte)
  56   top-line-data: (handle array byte)
  57 }
  58 
  59 ## generating traces
  60 
  61 fn initialize-trace _self: (addr trace), max-depth: int, capacity: int, visible-capacity: int {
  62   var self/esi: (addr trace) <- copy _self
  63   compare self, 0
  64   {
  65     break-if-!=
  66     abort "null trace"
  67   }
  68   var src/ecx: int <- copy max-depth
  69   var dest/eax: (addr int) <- get self, max-depth
  70   copy-to *dest, src
  71   dest <- get self, curr-depth
  72   copy-to *dest, 1  # 0 is the error depth
  73   var trace-ah/eax: (addr handle array trace-line) <- get self, data
  74   populate trace-ah, capacity
  75   var visible-ah/eax: (addr handle array trace-line) <- get self, visible
  76   populate visible-ah, visible-capacity
  77   mark-lines-dirty self
  78 }
  79 
  80 fn clear-trace _self: (addr trace) {
  81   var self/eax: (addr trace) <- copy _self
  82   compare self, 0
  83   {
  84     break-if-!=
  85     abort "null trace"
  86   }
  87   var curr-depth-addr/ecx: (addr int) <- get self, curr-depth
  88   copy-to *curr-depth-addr, 1
  89   var len/edx: (addr int) <- get self, first-free
  90   copy-to *len, 0
  91   # leak: nested handles within trace-lines
  92 }
  93 
  94 fn has-errors? _self: (addr trace) -> _/eax: boolean {
  95   var self/eax: (addr trace) <- copy _self
  96   compare self, 0
  97   {
  98     break-if-!=
  99     abort "null trace"
 100   }
 101   var max/edx: (addr int) <- get self, first-free
 102   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 103   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
 104   var trace/esi: (addr array trace-line) <- copy _trace
 105   var i/ecx: int <- copy 0
 106   {
 107     compare i, *max
 108     break-if->=
 109     var offset/eax: (offset trace-line) <- compute-offset trace, i
 110     var curr/eax: (addr trace-line) <- index trace, offset
 111     var curr-depth-a/eax: (addr int) <- get curr, depth
 112     compare *curr-depth-a, 0/error
 113     {
 114       break-if-!=
 115       return 1/true
 116     }
 117     i <- increment
 118     loop
 119   }
 120   return 0/false
 121 }
 122 
 123 fn should-trace? _self: (addr trace) -> _/eax: boolean {
 124   var self/esi: (addr trace) <- copy _self
 125   compare self, 0
 126   {
 127     break-if-!=
 128     abort "null trace"
 129   }
 130   var depth-a/ecx: (addr int) <- get self, curr-depth
 131   var depth/ecx: int <- copy *depth-a
 132   var max-depth-a/eax: (addr int) <- get self, max-depth
 133   compare depth, *max-depth-a
 134   {
 135     break-if->=
 136     return 1/true
 137   }
 138   return 0/false
 139 }
 140 
 141 fn trace _self: (addr trace), label: (addr array byte), message: (addr stream byte) {
 142   var self/esi: (addr trace) <- copy _self
 143   compare self, 0
 144   {
 145     break-if-!=
 146     abort "null trace"
 147   }
 148   var should-trace?/eax: boolean <- should-trace? self
 149   compare should-trace?, 0/false
 150   {
 151     break-if-!=
 152     return
 153   }
 154   var data-ah/eax: (addr handle array trace-line) <- get self, data
 155   var data/eax: (addr array trace-line) <- lookup *data-ah
 156   var index-addr/edi: (addr int) <- get self, first-free
 157   {
 158     compare *index-addr, 0x8000/lines
 159     break-if-<
 160     return
 161   }
 162   var index/ecx: int <- copy *index-addr
 163   var offset/ecx: (offset trace-line) <- compute-offset data, index
 164   var dest/eax: (addr trace-line) <- index data, offset
 165   var depth/ecx: (addr int) <- get self, curr-depth
 166   rewind-stream message
 167   {
 168     compare *index-addr, 0x7fff/lines
 169     break-if-<
 170     clear-stream message
 171     write message, "No space left in trace\n"
 172     write message, "Please either:\n"
 173     write message, "  - find a smaller sub-computation to test,\n"
 174     write message, "  - allocate more space to the trace in initialize-sandbox\n"
 175     write message, "    (shell/sandbox.mu), or\n"
 176     write message, "  - move the computation to 'main' and run it using ctrl-r"
 177     initialize-trace-line 0/depth, "error", message, dest
 178     increment *index-addr
 179     return
 180   }
 181   initialize-trace-line *depth, label, message, dest
 182   increment *index-addr
 183 }
 184 
 185 fn trace-text self: (addr trace), label: (addr array byte), s: (addr array byte) {
 186   compare self, 0
 187   {
 188     break-if-!=
 189     abort "null trace"
 190   }
 191   var data-storage: (stream byte 0x100)
 192   var data/eax: (addr stream byte) <- address data-storage
 193   write data, s
 194   trace self, label, data
 195 }
 196 
 197 fn error _self: (addr trace), message: (addr array byte) {
 198   var self/esi: (addr trace) <- copy _self
 199   compare self, 0
 200   {
 201     break-if-!=
 202     abort "null trace"
 203   }
 204   var curr-depth-a/eax: (addr int) <- get self, curr-depth
 205   var save-depth/ecx: int <- copy *curr-depth-a
 206   copy-to *curr-depth-a, 0/error
 207   trace-text self, "error", message
 208   copy-to *curr-depth-a, save-depth
 209 }
 210 
 211 fn error-stream _self: (addr trace), message: (addr stream byte) {
 212   var self/esi: (addr trace) <- copy _self
 213   compare self, 0
 214   {
 215     break-if-!=
 216     abort "null trace"
 217   }
 218   var curr-depth-a/eax: (addr int) <- get self, curr-depth
 219   var save-depth/ecx: int <- copy *curr-depth-a
 220   copy-to *curr-depth-a, 0/error
 221   trace self, "error", message
 222   copy-to *curr-depth-a, save-depth
 223 }
 224 
 225 fn initialize-trace-line depth: int, label: (addr array byte), data: (addr stream byte), _out: (addr trace-line) {
 226   var out/edi: (addr trace-line) <- copy _out
 227   # depth
 228   var src/eax: int <- copy depth
 229   var dest/ecx: (addr int) <- get out, depth
 230   copy-to *dest, src
 231   # label
 232   var dest/eax: (addr handle array byte) <- get out, label
 233   copy-array-object label, dest
 234   # data
 235   var dest/eax: (addr handle array byte) <- get out, data
 236   stream-to-array data, dest
 237 }
 238 
 239 fn trace-lower _self: (addr trace) {
 240   var self/esi: (addr trace) <- copy _self
 241   compare self, 0
 242   {
 243     break-if-!=
 244     abort "null trace"
 245   }
 246   var depth/eax: (addr int) <- get self, curr-depth
 247   increment *depth
 248 }
 249 
 250 fn trace-higher _self: (addr trace) {
 251   var self/esi: (addr trace) <- copy _self
 252   compare self, 0
 253   {
 254     break-if-!=
 255     abort "null trace"
 256   }
 257   var depth/eax: (addr int) <- get self, curr-depth
 258   decrement *depth
 259 }
 260 
 261 ## checking traces
 262 
 263 fn check-trace-scans-to self: (addr trace), label: (addr array byte), data: (addr array byte), message: (addr array byte) {
 264   var tmp/eax: boolean <- trace-scans-to? self, label, data
 265   check tmp, message
 266 }
 267 
 268 fn trace-scans-to? _self: (addr trace), label: (addr array byte), data: (addr array byte) -> _/eax: boolean {
 269   var self/esi: (addr trace) <- copy _self
 270   var start/eax: (addr int) <- get self, first-full
 271   var result/eax: boolean <- trace-contains? self, label, data, *start
 272   return result
 273 }
 274 
 275 fn test-trace-scans-to {
 276   var t-storage: trace
 277   var t/esi: (addr trace) <- address t-storage
 278   initialize-trace t, 0x100/max-depth, 0x10/capacity, 0/visible  # we don't use trace UI
 279   #
 280   trace-text t, "label", "line 1"
 281   trace-text t, "label", "line 2"
 282   check-trace-scans-to t, "label", "line 1", "F - test-trace-scans-to/0"
 283   check-trace-scans-to t, "label", "line 2", "F - test-trace-scans-to/1"
 284   var tmp/eax: boolean <- trace-scans-to? t, "label", "line 1"
 285   check-not tmp, "F - test-trace-scans-to: fail on previously encountered lines"
 286   var tmp/eax: boolean <- trace-scans-to? t, "label", "line 3"
 287   check-not tmp, "F - test-trace-scans-to: fail on missing"
 288 }
 289 
 290 # scan trace from start
 291 # resets previous scans
 292 fn check-trace-contains self: (addr trace), label: (addr array byte), data: (addr array byte), message: (addr array byte) {
 293   var tmp/eax: boolean <- trace-contains? self, label, data, 0
 294   check tmp, message
 295 }
 296 
 297 fn test-trace-contains {
 298   var t-storage: trace
 299   var t/esi: (addr trace) <- address t-storage
 300   initialize-trace t, 0x100/max-depth, 0x10/capacity, 0/visible  # we don't use trace UI
 301   #
 302   trace-text t, "label", "line 1"
 303   trace-text t, "label", "line 2"
 304   check-trace-contains t, "label", "line 1", "F - test-trace-contains/0"
 305   check-trace-contains t, "label", "line 2", "F - test-trace-contains/1"
 306   check-trace-contains t, "label", "line 1", "F - test-trace-contains: find previously encountered lines"
 307   var tmp/eax: boolean <- trace-contains? t, "label", "line 3", 0/start
 308   check-not tmp, "F - test-trace-contains: fail on missing"
 309 }
 310 
 311 # this is super-inefficient, string comparing every trace line
 312 fn trace-contains? _self: (addr trace), label: (addr array byte), data: (addr array byte), start: int -> _/eax: boolean {
 313   var self/esi: (addr trace) <- copy _self
 314   var candidates-ah/eax: (addr handle array trace-line) <- get self, data
 315   var candidates/eax: (addr array trace-line) <- lookup *candidates-ah
 316   var i/ecx: int <- copy start
 317   var max/edx: (addr int) <- get self, first-free
 318   {
 319     compare i, *max
 320     break-if->=
 321     {
 322       var read-until-index/eax: (addr int) <- get self, first-full
 323       copy-to *read-until-index, i
 324     }
 325     {
 326       var curr-offset/ecx: (offset trace-line) <- compute-offset candidates, i
 327       var curr/ecx: (addr trace-line) <- index candidates, curr-offset
 328       # if curr->label does not match, return false
 329       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
 330       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
 331       var match?/eax: boolean <- string-equal? curr-label, label
 332       compare match?, 0/false
 333       break-if-=
 334       # if curr->data does not match, return false
 335       var curr-data-ah/eax: (addr handle array byte) <- get curr, data
 336       var curr-data/eax: (addr array byte) <- lookup *curr-data-ah
 337       var match?/eax: boolean <- string-equal? curr-data, data
 338       compare match?, 0/false
 339       break-if-=
 340       return 1/true
 341     }
 342     i <- increment
 343     loop
 344   }
 345   return 0/false
 346 }
 347 
 348 fn trace-lines-equal? _a: (addr trace-line), _b: (addr trace-line) -> _/eax: boolean {
 349   var a/esi: (addr trace-line) <- copy _a
 350   var b/edi: (addr trace-line) <- copy _b
 351   var a-depth/ecx: (addr int) <- get a, depth
 352   var b-depth/edx: (addr int) <- get b, depth
 353   var benchmark/eax: int <- copy *b-depth
 354   compare *a-depth, benchmark
 355   {
 356     break-if-=
 357     return 0/false
 358   }
 359   var a-label-ah/eax: (addr handle array byte) <- get a, label
 360   var _a-label/eax: (addr array byte) <- lookup *a-label-ah
 361   var a-label/ecx: (addr array byte) <- copy _a-label
 362   var b-label-ah/ebx: (addr handle array byte) <- get b, label
 363   var b-label/eax: (addr array byte) <- lookup *b-label-ah
 364   var label-match?/eax: boolean <- string-equal? a-label, b-label
 365   {
 366     compare label-match?, 0/false
 367     break-if-!=
 368     return 0/false
 369   }
 370   var a-data-ah/eax: (addr handle array byte) <- get a, data
 371   var _a-data/eax: (addr array byte) <- lookup *a-data-ah
 372   var a-data/ecx: (addr array byte) <- copy _a-data
 373   var b-data-ah/ebx: (addr handle array byte) <- get b, data
 374   var b-data/eax: (addr array byte) <- lookup *b-data-ah
 375   var data-match?/eax: boolean <- string-equal? a-data, b-data
 376   return data-match?
 377 }
 378 
 379 fn dump-trace _self: (addr trace) {
 380   var y/ecx: int <- copy 0
 381   var self/esi: (addr trace) <- copy _self
 382   compare self, 0
 383   {
 384     break-if-!=
 385     abort "null trace"
 386   }
 387   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 388   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
 389   var trace/edi: (addr array trace-line) <- copy _trace
 390   var i/edx: int <- copy 0
 391   var max-addr/ebx: (addr int) <- get self, first-free
 392   var max/ebx: int <- copy *max-addr
 393   $dump-trace:loop: {
 394     compare i, max
 395     break-if->=
 396     $dump-trace:iter: {
 397       var offset/ebx: (offset trace-line) <- compute-offset trace, i
 398       var curr/ebx: (addr trace-line) <- index trace, offset
 399       y <- render-trace-line 0/screen, curr, 0, y, 0x80/width, 0x30/height, 7/fg, 0/bg, 0/clip
 400     }
 401     i <- increment
 402     loop
 403   }
 404 }
 405 
 406 fn dump-trace-with-label _self: (addr trace), label: (addr array byte) {
 407   var y/ecx: int <- copy 0
 408   var self/esi: (addr trace) <- copy _self
 409   compare self, 0
 410   {
 411     break-if-!=
 412     abort "null trace"
 413   }
 414   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 415   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
 416   var trace/edi: (addr array trace-line) <- copy _trace
 417   var i/edx: int <- copy 0
 418   var max-addr/ebx: (addr int) <- get self, first-free
 419   var max/ebx: int <- copy *max-addr
 420   $dump-trace-with-label:loop: {
 421     compare i, max
 422     break-if->=
 423     $dump-trace-with-label:iter: {
 424       var offset/ebx: (offset trace-line) <- compute-offset trace, i
 425       var curr/ebx: (addr trace-line) <- index trace, offset
 426       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
 427       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
 428       var show?/eax: boolean <- string-equal? curr-label, label
 429       compare show?, 0/false
 430       break-if-=
 431       y <- render-trace-line 0/screen, curr, 0, y, 0x80/width, 0x30/height, 7/fg, 0/bg, 0/clip
 432     }
 433     i <- increment
 434     loop
 435   }
 436 }
 437 
 438 ## UI stuff
 439 
 440 fn mark-lines-dirty _self: (addr trace) {
 441   var self/eax: (addr trace) <- copy _self
 442   var dest/edx: (addr boolean) <- get self, recreate-caches?
 443   copy-to *dest, 1/true
 444 }
 445 
 446 fn mark-lines-clean _self: (addr trace) {
 447   var self/eax: (addr trace) <- copy _self
 448   var dest/edx: (addr boolean) <- get self, recreate-caches?
 449   copy-to *dest, 0/false
 450 }
 451 
 452 fn render-trace screen: (addr screen), _self: (addr trace), xmin: int, ymin: int, xmax: int, ymax: int, show-cursor?: boolean -> _/ecx: int {
 453   var already-hiding-lines?: boolean
 454   var self/esi: (addr trace) <- copy _self
 455   compare self, 0
 456   {
 457     break-if-!=
 458     abort "null trace"
 459   }
 460   var y/ecx: int <- copy ymin
 461   # recreate caches if necessary
 462   var recreate-caches?/eax: (addr boolean) <- get self, recreate-caches?
 463   compare *recreate-caches?, 0/false
 464   {
 465     break-if-=
 466     # cache ymin
 467     var dest/eax: (addr int) <- get self, top-line-y
 468     copy-to *dest, y
 469     # cache ymax
 470     var ymax/ecx: int <- copy ymax
 471     dest <- get self, screen-height
 472     copy-to *dest, ymax
 473     #
 474     recompute-all-visible-lines self
 475     mark-lines-clean self
 476   }
 477   clamp-cursor-to-top self, y
 478   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 479   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
 480   var trace/edi: (addr array trace-line) <- copy _trace
 481   var max-addr/ebx: (addr int) <- get self, first-free
 482   var max/ebx: int <- copy *max-addr
 483   # display trace depth (not in tests)
 484   $render-trace:render-depth: {
 485     compare max, 0
 486     break-if-<=
 487     var max-depth/edx: (addr int) <- get self, max-depth
 488     {
 489       var width/eax: int <- copy 0
 490       var height/ecx: int <- copy 0
 491       width, height <- screen-size screen
 492       compare width, 0x80
 493       break-if-< $render-trace:render-depth
 494     }
 495     set-cursor-position screen, 0x70/x, y
 496     draw-text-rightward-from-cursor-over-full-screen screen, "trace depth: ", 0x17/fg, 0xc5/bg=blue-bg
 497     draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, *max-depth, 0x7/fg, 0xc5/bg=blue-bg
 498   }
 499   var top-line-addr/edx: (addr int) <- get self, top-line-index
 500   var i/edx: int <- copy *top-line-addr
 501   $render-trace:loop: {
 502     compare i, max
 503     break-if->=
 504     compare y, ymax
 505     break-if->=
 506     $render-trace:iter: {
 507       var offset/ebx: (offset trace-line) <- compute-offset trace, i
 508       var curr/ebx: (addr trace-line) <- index trace, offset
 509       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
 510       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
 511       var bg: int
 512       copy-to bg, 0xc5/bg=blue-bg
 513       var fg: int
 514       copy-to fg, 0x38/fg=trace
 515       compare show-cursor?, 0/false
 516       {
 517         break-if-=
 518         var cursor-y/eax: (addr int) <- get self, cursor-y
 519         compare *cursor-y, y
 520         break-if-!=
 521         copy-to bg, 7/trace-cursor-line-bg
 522         copy-to fg, 0x68/cursor-line-fg=sober-blue
 523         var cursor-line-index/eax: (addr int) <- get self, cursor-line-index
 524         copy-to *cursor-line-index, i
 525       }
 526       # always display errors
 527       {
 528         var curr-depth/eax: (addr int) <- get curr, depth
 529         compare *curr-depth, 0/error
 530         break-if-!=
 531         y <- render-trace-line screen, curr, xmin, y, xmax, ymax, 0xc/fg=trace-error, bg, 0/clip
 532         copy-to already-hiding-lines?, 0/false
 533         break $render-trace:iter
 534       }
 535       # display expanded lines
 536       var display?/eax: boolean <- should-render? curr
 537       {
 538         compare display?, 0/false
 539         break-if-=
 540         var unclip-cursor-line?/eax: boolean <- unclip-cursor-line? self, i
 541         y <- render-trace-line screen, curr, xmin, y, xmax, ymax, fg, bg, unclip-cursor-line?
 542         copy-to already-hiding-lines?, 0/false
 543         break $render-trace:iter
 544       }
 545       # ignore the rest
 546       compare already-hiding-lines?, 0/false
 547       {
 548         break-if-!=
 549         var x/eax: int <- copy xmin
 550         x, y <- draw-text-wrapping-right-then-down screen, "...", xmin, ymin, xmax, ymax, x, y, fg, bg
 551         y <- increment
 552         copy-to already-hiding-lines?, 1/true
 553       }
 554     }
 555     i <- increment
 556     loop
 557   }
 558   # prevent cursor from going too far down
 559   clamp-cursor-to-bottom self, y, screen, xmin, ymin, xmax, ymax
 560   return y
 561 }
 562 
 563 fn unclip-cursor-line? _self: (addr trace), _i: int -> _/eax: boolean {
 564   # if unclip? and i == *cursor-line-index, render unclipped
 565   var self/esi: (addr trace) <- copy _self
 566   var unclip-cursor-line?/eax: (addr boolean) <- get self, unclip-cursor-line?
 567   compare *unclip-cursor-line?, 0/false
 568   {
 569     break-if-!=
 570     return 0/false
 571   }
 572   var cursor-line-index/eax: (addr int) <- get self, cursor-line-index
 573   var i/ecx: int <- copy _i
 574   compare i, *cursor-line-index
 575   {
 576     break-if-=
 577     return 0/false
 578   }
 579   return 1/true
 580 }
 581 
 582 fn render-trace-line screen: (addr screen), _self: (addr trace-line), xmin: int, ymin: int, xmax: int, ymax: int, fg: int, bg: int, unclip?: boolean -> _/ecx: int {
 583   var self/esi: (addr trace-line) <- copy _self
 584   var xsave/edx: int <- copy xmin
 585   var y/ecx: int <- copy ymin
 586   # show depth for non-errors
 587   var depth-a/ebx: (addr int) <- get self, depth
 588   compare *depth-a, 0/error
 589   {
 590     break-if-=
 591     var x/eax: int <- copy xsave
 592     {
 593       x, y <- draw-int32-decimal-wrapping-right-then-down screen, *depth-a, xmin, ymin, xmax, ymax, x, y, fg, bg
 594       x, y <- draw-text-wrapping-right-then-down screen, " ", xmin, ymin, xmax, ymax, x, y, fg, bg
 595       # don't show label in UI; it's just for tests
 596     }
 597     xsave <- copy x
 598   }
 599   var data-ah/eax: (addr handle array byte) <- get self, data
 600   var _data/eax: (addr array byte) <- lookup *data-ah
 601   var data/ebx: (addr array byte) <- copy _data
 602   var x/eax: int <- copy xsave
 603   compare unclip?, 0/false
 604   {
 605     break-if-=
 606     x, y <- draw-text-wrapping-right-then-down screen, data, xmin, ymin, xmax, ymax, x, y, fg, bg
 607   }
 608   compare unclip?, 0/false
 609   {
 610     break-if-!=
 611     x <- draw-text-rightward screen, data, x, xmax, y, fg, bg
 612   }
 613   y <- increment
 614   return y
 615 }
 616 
 617 fn should-render? _line: (addr trace-line) -> _/eax: boolean {
 618   var line/eax: (addr trace-line) <- copy _line
 619   var result/eax: (addr boolean) <- get line, visible?
 620   return *result
 621 }
 622 
 623 # This is super-inefficient, string-comparing every trace line
 624 # against every visible line.
 625 fn recompute-all-visible-lines _self: (addr trace) {
 626   var self/esi: (addr trace) <- copy _self
 627   var max-addr/edx: (addr int) <- get self, first-free
 628   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 629   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
 630   var trace/esi: (addr array trace-line) <- copy _trace
 631   var i/ecx: int <- copy 0
 632   {
 633     compare i, *max-addr
 634     break-if->=
 635     var offset/ebx: (offset trace-line) <- compute-offset trace, i
 636     var curr/ebx: (addr trace-line) <- index trace, offset
 637     recompute-visibility _self, curr
 638     i <- increment
 639     loop
 640   }
 641 }
 642 
 643 fn recompute-visibility _self: (addr trace), _line: (addr trace-line) {
 644   var self/esi: (addr trace) <- copy _self
 645   # recompute
 646   var candidates-ah/eax: (addr handle array trace-line) <- get self, visible
 647   var candidates/eax: (addr array trace-line) <- lookup *candidates-ah
 648   var i/ecx: int <- copy 0
 649   var len/edx: int <- length candidates
 650   {
 651     compare i, len
 652     break-if->=
 653     {
 654       var curr-offset/ecx: (offset trace-line) <- compute-offset candidates, i
 655       var curr/ecx: (addr trace-line) <- index candidates, curr-offset
 656       var match?/eax: boolean <- trace-lines-equal? curr, _line
 657       compare match?, 0/false
 658       break-if-=
 659       var line/eax: (addr trace-line) <- copy _line
 660       var dest/eax: (addr boolean) <- get line, visible?
 661       copy-to *dest, 1/true
 662       return
 663     }
 664     i <- increment
 665     loop
 666   }
 667   var line/eax: (addr trace-line) <- copy _line
 668   var dest/eax: (addr boolean) <- get line, visible?
 669   copy-to *dest, 0/false
 670 }
 671 
 672 fn clamp-cursor-to-top _self: (addr trace), _y: int {
 673   var y/ecx: int <- copy _y
 674   var self/esi: (addr trace) <- copy _self
 675   var cursor-y/eax: (addr int) <- get self, cursor-y
 676   compare *cursor-y, y
 677   break-if->=
 678   copy-to *cursor-y, y
 679 }
 680 
 681 # extremely hacky; consider deleting test-render-trace-empty-3 when you clean this up
 682 # TODO: duplicates logic for rendering a line
 683 fn clamp-cursor-to-bottom _self: (addr trace), _y: int, screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int {
 684   var y/ebx: int <- copy _y
 685   compare y, ymin
 686   {
 687     break-if->
 688     return
 689   }
 690   y <- decrement
 691   var self/esi: (addr trace) <- copy _self
 692   var cursor-y/eax: (addr int) <- get self, cursor-y
 693   compare *cursor-y, y
 694   break-if-<=
 695   copy-to *cursor-y, y
 696   # redraw cursor-line
 697   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 698   var trace/eax: (addr array trace-line) <- lookup *trace-ah
 699   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
 700   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
 701   var first-free/edx: (addr int) <- get self, first-free
 702   compare cursor-line-index, *first-free
 703   {
 704     break-if-<
 705     return
 706   }
 707   var cursor-offset/ecx: (offset trace-line) <- compute-offset trace, cursor-line-index
 708   var cursor-line/ecx: (addr trace-line) <- index trace, cursor-offset
 709   var display?/eax: boolean <- should-render? cursor-line
 710   {
 711     compare display?, 0/false
 712     break-if-=
 713     var dummy/ecx: int <- render-trace-line screen, cursor-line, xmin, y, xmax, ymax, 0x38/fg=trace, 7/cursor-line-bg, 0/clip
 714     return
 715   }
 716   var dummy1/eax: int <- copy 0
 717   var dummy2/ecx: int <- copy 0
 718   dummy1, dummy2 <- draw-text-wrapping-right-then-down screen, "...", xmin, ymin, xmax, ymax, xmin, y, 9/fg=trace, 7/cursor-line-bg
 719 }
 720 
 721 fn test-render-trace-empty {
 722   var t-storage: trace
 723   var t/esi: (addr trace) <- address t-storage
 724   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 725   # setup: screen
 726   var screen-on-stack: screen
 727   var screen/edi: (addr screen) <- address screen-on-stack
 728   initialize-screen screen, 5/width, 4/height, 0/no-pixel-graphics
 729   #
 730   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 5/xmax, 4/ymax, 0/no-cursor
 731   #
 732   check-ints-equal y, 0, "F - test-render-trace-empty/cursor"
 733   check-screen-row screen,                                  0/y, "    ", "F - test-render-trace-empty"
 734   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "    ", "F - test-render-trace-empty/bg"
 735 }
 736 
 737 fn test-render-trace-empty-2 {
 738   var t-storage: trace
 739   var t/esi: (addr trace) <- address t-storage
 740   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 741   # setup: screen
 742   var screen-on-stack: screen
 743   var screen/edi: (addr screen) <- address screen-on-stack
 744   initialize-screen screen, 5/width, 4/height, 0/no-pixel-graphics
 745   #
 746   var y/ecx: int <- render-trace screen, t, 0/xmin, 2/ymin, 5/xmax, 4/ymax, 0/no-cursor  # cursor below top row
 747   #
 748   check-ints-equal y, 2, "F - test-render-trace-empty-2/cursor"
 749   check-screen-row screen,                                  2/y, "    ", "F - test-render-trace-empty-2"
 750   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "    ", "F - test-render-trace-empty-2/bg"
 751 }
 752 
 753 fn test-render-trace-empty-3 {
 754   var t-storage: trace
 755   var t/esi: (addr trace) <- address t-storage
 756   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 757   # setup: screen
 758   var screen-on-stack: screen
 759   var screen/edi: (addr screen) <- address screen-on-stack
 760   initialize-screen screen, 5/width, 4/height, 0/no-pixel-graphics
 761   #
 762   var y/ecx: int <- render-trace screen, t, 0/xmin, 2/ymin, 5/xmax, 4/ymax, 1/show-cursor  # try show cursor
 763   # still no cursor to show
 764   check-ints-equal y, 2, "F - test-render-trace-empty-3/cursor"
 765   check-screen-row screen,                                  1/y, "    ", "F - test-render-trace-empty-3/line-above-cursor"
 766   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "    ", "F - test-render-trace-empty-3/bg-for-line-above-cursor"
 767   check-screen-row screen,                                  2/y, "    ", "F - test-render-trace-empty-3"
 768   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "    ", "F - test-render-trace-empty-3/bg"
 769 }
 770 
 771 fn test-render-trace-collapsed-by-default {
 772   var t-storage: trace
 773   var t/esi: (addr trace) <- address t-storage
 774   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 775   trace-text t, "l", "data"
 776   # setup: screen
 777   var screen-on-stack: screen
 778   var screen/edi: (addr screen) <- address screen-on-stack
 779   initialize-screen screen, 5/width, 4/height, 0/no-pixel-graphics
 780   #
 781   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 5/xmax, 4/ymax, 0/no-cursor
 782   #
 783   check-ints-equal y, 1, "F - test-render-trace-collapsed-by-default/cursor"
 784   check-screen-row screen, 0/y, "... ", "F - test-render-trace-collapsed-by-default"
 785 }
 786 
 787 fn test-render-trace-error {
 788   var t-storage: trace
 789   var t/esi: (addr trace) <- address t-storage
 790   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 791   error t, "error"
 792   # setup: screen
 793   var screen-on-stack: screen
 794   var screen/edi: (addr screen) <- address screen-on-stack
 795   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
 796   #
 797   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
 798   #
 799   check-ints-equal y, 1, "F - test-render-trace-error/cursor"
 800   check-screen-row screen, 0/y, "error", "F - test-render-trace-error"
 801 }
 802 
 803 fn test-render-trace-error-at-start {
 804   var t-storage: trace
 805   var t/esi: (addr trace) <- address t-storage
 806   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 807   #
 808   error t, "error"
 809   trace-text t, "l", "data"
 810   # setup: screen
 811   var screen-on-stack: screen
 812   var screen/edi: (addr screen) <- address screen-on-stack
 813   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
 814   #
 815   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
 816   #
 817   check-ints-equal y, 2, "F - test-render-trace-error-at-start/cursor"
 818   check-screen-row screen, 0/y, "error", "F - test-render-trace-error-at-start/0"
 819   check-screen-row screen, 1/y, "...  ", "F - test-render-trace-error-at-start/1"
 820 }
 821 
 822 fn test-render-trace-error-at-end {
 823   var t-storage: trace
 824   var t/esi: (addr trace) <- address t-storage
 825   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 826   #
 827   trace-text t, "l", "data"
 828   error t, "error"
 829   # setup: screen
 830   var screen-on-stack: screen
 831   var screen/edi: (addr screen) <- address screen-on-stack
 832   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
 833   #
 834   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
 835   #
 836   check-ints-equal y, 2, "F - test-render-trace-error-at-end/cursor"
 837   check-screen-row screen, 0/y, "...  ", "F - test-render-trace-error-at-end/0"
 838   check-screen-row screen, 1/y, "error", "F - test-render-trace-error-at-end/1"
 839 }
 840 
 841 fn test-render-trace-error-in-the-middle {
 842   var t-storage: trace
 843   var t/esi: (addr trace) <- address t-storage
 844   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 845   #
 846   trace-text t, "l", "line 1"
 847   error t, "error"
 848   trace-text t, "l", "line 3"
 849   # setup: screen
 850   var screen-on-stack: screen
 851   var screen/edi: (addr screen) <- address screen-on-stack
 852   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
 853   #
 854   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 0/no-cursor
 855   #
 856   check-ints-equal y, 3, "F - test-render-trace-error-in-the-middle/cursor"
 857   check-screen-row screen, 0/y, "...  ", "F - test-render-trace-error-in-the-middle/0"
 858   check-screen-row screen, 1/y, "error", "F - test-render-trace-error-in-the-middle/1"
 859   check-screen-row screen, 2/y, "...  ", "F - test-render-trace-error-in-the-middle/2"
 860 }
 861 
 862 fn test-render-trace-cursor-in-single-line {
 863   var t-storage: trace
 864   var t/esi: (addr trace) <- address t-storage
 865   initialize-trace t, 0x100/max-depth, 0x10, 0x10
 866   #
 867   trace-text t, "l", "line 1"
 868   error t, "error"
 869   trace-text t, "l", "line 3"
 870   # setup: screen
 871   var screen-on-stack: screen
 872   var screen/edi: (addr screen) <- address screen-on-stack
 873   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
 874   #
 875   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
 876   #
 877   check-screen-row screen,                                  0/y, "...   ", "F - test-render-trace-cursor-in-single-line/0"
 878   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-render-trace-cursor-in-single-line/0/cursor"
 879   check-screen-row screen,                                  1/y, "error ", "F - test-render-trace-cursor-in-single-line/1"
 880   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-render-trace-cursor-in-single-line/1/cursor"
 881   check-screen-row screen,                                  2/y, "...   ", "F - test-render-trace-cursor-in-single-line/2"
 882   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-render-trace-cursor-in-single-line/2/cursor"
 883 }
 884 
 885 fn render-trace-menu screen: (addr screen) {
 886   var width/eax: int <- copy 0
 887   var height/ecx: int <- copy 0
 888   width, height <- screen-size screen
 889   var y/ecx: int <- copy height
 890   y <- decrement
 891   var height/edx: int <- copy y
 892   height <- increment
 893   clear-rect screen, 0/x, y, width, height, 0xc5/bg=blue-bg
 894   set-cursor-position screen, 0/x, y
 895   draw-text-rightward-from-cursor screen, " ^r ", width, 0/fg, 0x5c/bg=menu-highlight
 896   draw-text-rightward-from-cursor screen, " run main  ", width, 7/fg, 0xc5/bg=blue-bg
 897   draw-text-rightward-from-cursor screen, " ^g ", width, 0/fg, 0x5c/bg=menu-highlight
 898   draw-text-rightward-from-cursor screen, " go to  ", width, 7/fg, 0xc5/bg=blue-bg
 899   draw-text-rightward-from-cursor screen, " ^m ", width, 0/fg, 3/bg=keyboard
 900   draw-text-rightward-from-cursor screen, " to keyboard  ", width, 7/fg, 0xc5/bg=blue-bg
 901   draw-text-rightward-from-cursor screen, " enter/bksp ", width, 0/fg, 0x5c/bg=menu-highlight
 902   draw-text-rightward-from-cursor screen, " expand/collapse  ", width, 7/fg, 0xc5/bg=blue-bg
 903   draw-text-rightward-from-cursor screen, " ^s ", width, 0/fg, 0x5c/bg=menu-highlight
 904   draw-text-rightward-from-cursor screen, " show whole line  ", width, 7/fg, 0xc5/bg=blue-bg
 905 }
 906 
 907 fn edit-trace _self: (addr trace), key: code-point-utf8 {
 908   var self/esi: (addr trace) <- copy _self
 909   # cursor down
 910   {
 911     compare key, 0x6a/j
 912     break-if-!=
 913     var cursor-y/eax: (addr int) <- get self, cursor-y
 914     increment *cursor-y
 915     var unclip-cursor-line?/eax: (addr boolean) <- get self, unclip-cursor-line?
 916     copy-to *unclip-cursor-line?, 0/false
 917     return
 918   }
 919   {
 920     compare key, 0x81/down-arrow
 921     break-if-!=
 922     var cursor-y/eax: (addr int) <- get self, cursor-y
 923     increment *cursor-y
 924     var unclip-cursor-line?/eax: (addr boolean) <- get self, unclip-cursor-line?
 925     copy-to *unclip-cursor-line?, 0/false
 926     return
 927   }
 928   # cursor up
 929   {
 930     compare key, 0x6b/k
 931     break-if-!=
 932     var cursor-y/eax: (addr int) <- get self, cursor-y
 933     decrement *cursor-y
 934     var unclip-cursor-line?/eax: (addr boolean) <- get self, unclip-cursor-line?
 935     copy-to *unclip-cursor-line?, 0/false
 936     return
 937   }
 938   {
 939     compare key, 0x82/up-arrow
 940     break-if-!=
 941     var cursor-y/eax: (addr int) <- get self, cursor-y
 942     decrement *cursor-y
 943     var unclip-cursor-line?/eax: (addr boolean) <- get self, unclip-cursor-line?
 944     copy-to *unclip-cursor-line?, 0/false
 945     return
 946   }
 947   # enter = expand
 948   {
 949     compare key, 0xa/newline
 950     break-if-!=
 951     expand self
 952     return
 953   }
 954   # backspace = collapse
 955   {
 956     compare key, 8/backspace
 957     break-if-!=
 958     collapse self
 959     return
 960   }
 961   # ctrl-s: temporarily unclip current line
 962   {
 963     compare key, 0x13/ctrl-s
 964     break-if-!=
 965     var unclip-cursor-line?/eax: (addr boolean) <- get self, unclip-cursor-line?
 966     copy-to *unclip-cursor-line?, 1/true
 967     return
 968   }
 969   # ctrl-f: scroll down
 970   {
 971     compare key, 6/ctrl-f
 972     break-if-!=
 973     scroll-down self
 974     return
 975   }
 976   # ctrl-b: scroll up
 977   {
 978     compare key, 2/ctrl-b
 979     break-if-!=
 980     scroll-up self
 981     return
 982   }
 983 }
 984 
 985 fn expand _self: (addr trace) {
 986   var self/esi: (addr trace) <- copy _self
 987   var trace-ah/eax: (addr handle array trace-line) <- get self, data
 988   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
 989   var trace/edi: (addr array trace-line) <- copy _trace
 990   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
 991   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
 992   var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index
 993   var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset
 994   var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible?
 995   var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth
 996   var target-depth/ebx: int <- copy *cursor-line-depth
 997   # if cursor-line is already visible, do nothing
 998   compare *cursor-line-visible?, 0/false
 999   {
1000     break-if-=
1001 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "visible", 7/fg 0/bg
1002     return
1003   }
1004   # reveal the run of lines starting at cursor-line-index with depth target-depth
1005   var i/ecx: int <- copy cursor-line-index
1006   var max/edx: (addr int) <- get self, first-free
1007   {
1008     compare i, *max
1009     break-if->=
1010     var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i
1011     var curr-line/edx: (addr trace-line) <- index trace, curr-line-offset
1012     var curr-line-depth/eax: (addr int) <- get curr-line, depth
1013     compare *curr-line-depth, target-depth
1014     break-if-<
1015     {
1016       break-if-!=
1017       var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible?
1018       copy-to *curr-line-visible?, 1/true
1019       reveal-trace-line self, curr-line
1020     }
1021     i <- increment
1022     loop
1023   }
1024 }
1025 
1026 fn collapse _self: (addr trace) {
1027   var self/esi: (addr trace) <- copy _self
1028   var trace-ah/eax: (addr handle array trace-line) <- get self, data
1029   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
1030   var trace/edi: (addr array trace-line) <- copy _trace
1031   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
1032   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
1033   var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index
1034   var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset
1035   var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible?
1036   # if cursor-line is not visible, do nothing
1037   compare *cursor-line-visible?, 0/false
1038   {
1039     break-if-!=
1040     return
1041   }
1042   # hide all lines between previous and next line with a lower depth
1043   var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth
1044   var cursor-y/edx: (addr int) <- get self, cursor-y
1045   var target-depth/ebx: int <- copy *cursor-line-depth
1046   var i/ecx: int <- copy cursor-line-index
1047   $collapse:loop1: {
1048     compare i, 0
1049     break-if-<
1050     var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i
1051     var curr-line/eax: (addr trace-line) <- index trace, curr-line-offset
1052     {
1053       var curr-line-depth/eax: (addr int) <- get curr-line, depth
1054       compare *curr-line-depth, target-depth
1055       break-if-< $collapse:loop1
1056     }
1057     # if cursor-line is visible, decrement cursor-y
1058     {
1059       var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible?
1060       compare *curr-line-visible?, 0/false
1061       break-if-=
1062       decrement *cursor-y
1063     }
1064     i <- decrement
1065     loop
1066   }
1067   i <- increment
1068   var max/edx: (addr int) <- get self, first-free
1069   $collapse:loop2: {
1070     compare i, *max
1071     break-if->=
1072     var curr-line-offset/eax: (offset trace-line) <- compute-offset trace, i
1073     var curr-line/edx: (addr trace-line) <- index trace, curr-line-offset
1074     var curr-line-depth/eax: (addr int) <- get curr-line, depth
1075     compare *curr-line-depth, target-depth
1076     break-if-<
1077     {
1078       hide-trace-line self, curr-line
1079       var curr-line-visible?/eax: (addr boolean) <- get curr-line, visible?
1080       copy-to *curr-line-visible?, 0/false
1081     }
1082     i <- increment
1083     loop
1084   }
1085 }
1086 
1087 # the 'visible' array is not required to be in order
1088 # elements can also be deleted out of order
1089 # so it can have holes
1090 # however, lines in it always have visible? set
1091 # we'll use visible? being unset as a sign of emptiness
1092 fn reveal-trace-line _self: (addr trace), line: (addr trace-line) {
1093   var self/esi: (addr trace) <- copy _self
1094   var visible-ah/eax: (addr handle array trace-line) <- get self, visible
1095   var visible/eax: (addr array trace-line) <- lookup *visible-ah
1096   var i/ecx: int <- copy 0
1097   var len/edx: int <- length visible
1098   {
1099     compare i, len
1100     break-if->=
1101     var curr-offset/edx: (offset trace-line) <- compute-offset visible, i
1102     var curr/edx: (addr trace-line) <- index visible, curr-offset
1103     var curr-visible?/eax: (addr boolean) <- get curr, visible?
1104     compare *curr-visible?, 0/false
1105     {
1106       break-if-!=
1107       # empty slot found
1108       copy-object line, curr
1109       return
1110     }
1111     i <- increment
1112     loop
1113   }
1114   abort "too many visible lines; increase size of array trace.visible"
1115 }
1116 
1117 fn hide-trace-line _self: (addr trace), line: (addr trace-line) {
1118   var self/esi: (addr trace) <- copy _self
1119   var visible-ah/eax: (addr handle array trace-line) <- get self, visible
1120   var visible/eax: (addr array trace-line) <- lookup *visible-ah
1121   var i/ecx: int <- copy 0
1122   var len/edx: int <- length visible
1123   {
1124     compare i, len
1125     break-if->=
1126     var curr-offset/edx: (offset trace-line) <- compute-offset visible, i
1127     var curr/edx: (addr trace-line) <- index visible, curr-offset
1128     var found?/eax: boolean <- trace-lines-equal? curr, line
1129     compare found?, 0/false
1130     {
1131       break-if-=
1132       clear-object curr
1133     }
1134     i <- increment
1135     loop
1136   }
1137 }
1138 
1139 fn cursor-too-deep? _self: (addr trace) -> _/eax: boolean {
1140   var self/esi: (addr trace) <- copy _self
1141   var trace-ah/eax: (addr handle array trace-line) <- get self, data
1142   var _trace/eax: (addr array trace-line) <- lookup *trace-ah
1143   var trace/edi: (addr array trace-line) <- copy _trace
1144   var cursor-line-index-addr/ecx: (addr int) <- get self, cursor-line-index
1145   var cursor-line-index/ecx: int <- copy *cursor-line-index-addr
1146   var cursor-line-offset/eax: (offset trace-line) <- compute-offset trace, cursor-line-index
1147   var cursor-line/edx: (addr trace-line) <- index trace, cursor-line-offset
1148   var cursor-line-visible?/eax: (addr boolean) <- get cursor-line, visible?
1149   var cursor-line-depth/ebx: (addr int) <- get cursor-line, depth
1150   var target-depth/ebx: int <- copy *cursor-line-depth
1151   # if cursor-line is visible, return false
1152   compare *cursor-line-visible?, 0/false
1153   {
1154     break-if-=
1155     return 0/false
1156   }
1157   # return cursor-line-depth >= max-depth-1
1158   target-depth <- increment
1159   var max-depth-addr/eax: (addr int) <- get self, max-depth
1160   compare target-depth, *max-depth-addr
1161   {
1162     break-if-<
1163     return 1/true
1164   }
1165   return 0/false
1166 }
1167 
1168 fn test-cursor-down-and-up-within-trace {
1169   var t-storage: trace
1170   var t/esi: (addr trace) <- address t-storage
1171   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1172   #
1173   trace-text t, "l", "line 1"
1174   error t, "error"
1175   trace-text t, "l", "line 3"
1176   # setup: screen
1177   var screen-on-stack: screen
1178   var screen/edi: (addr screen) <- address screen-on-stack
1179   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
1180   #
1181   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
1182   #
1183   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-and-up-within-trace/pre-0"
1184   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-cursor-down-and-up-within-trace/pre-0/cursor"
1185   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-and-up-within-trace/pre-1"
1186   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-and-up-within-trace/pre-1/cursor"
1187   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-and-up-within-trace/pre-2"
1188   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-and-up-within-trace/pre-2/cursor"
1189   # cursor down
1190   edit-trace t, 0x6a/j
1191   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
1192   #
1193   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-and-up-within-trace/down-0"
1194   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-cursor-down-and-up-within-trace/down-0/cursor"
1195   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-and-up-within-trace/down-1"
1196   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "||||| ", "F - test-cursor-down-and-up-within-trace/down-1/cursor"
1197   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-and-up-within-trace/down-2"
1198   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-and-up-within-trace/down-2/cursor"
1199   # cursor up
1200   edit-trace t, 0x6b/k
1201   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
1202   #
1203   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-and-up-within-trace/up-0"
1204   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-cursor-down-and-up-within-trace/up-0/cursor"
1205   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-and-up-within-trace/up-1"
1206   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-and-up-within-trace/up-1/cursor"
1207   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-and-up-within-trace/up-2"
1208   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-and-up-within-trace/up-2/cursor"
1209 }
1210 
1211 fn test-cursor-down-past-bottom-of-trace {
1212   var t-storage: trace
1213   var t/esi: (addr trace) <- address t-storage
1214   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1215   #
1216   trace-text t, "l", "line 1"
1217   error t, "error"
1218   trace-text t, "l", "line 3"
1219   # setup: screen
1220   var screen-on-stack: screen
1221   var screen/edi: (addr screen) <- address screen-on-stack
1222   initialize-screen screen, 0xa/width, 4/height, 0/no-pixel-graphics
1223   #
1224   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
1225   #
1226   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/pre-0"
1227   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||   ", "F - test-cursor-down-past-bottom-of-trace/pre-0/cursor"
1228   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-past-bottom-of-trace/pre-1"
1229   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-past-bottom-of-trace/pre-1/cursor"
1230   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/pre-2"
1231   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "      ", "F - test-cursor-down-past-bottom-of-trace/pre-2/cursor"
1232   # cursor down several times
1233   edit-trace t, 0x6a/j
1234   edit-trace t, 0x6a/j
1235   edit-trace t, 0x6a/j
1236   edit-trace t, 0x6a/j
1237   edit-trace t, 0x6a/j
1238   # hack: we do need to render to make this test pass; we're mixing state management with rendering
1239   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0xa/xmax, 4/ymax, 1/show-cursor
1240   # cursor clamps at bottom
1241   check-screen-row screen,                                  0/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/down-0"
1242   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "      ", "F - test-cursor-down-past-bottom-of-trace/down-0/cursor"
1243   check-screen-row screen,                                  1/y, "error ", "F - test-cursor-down-past-bottom-of-trace/down-1"
1244   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "      ", "F - test-cursor-down-past-bottom-of-trace/down-1/cursor"
1245   check-screen-row screen,                                  2/y, "...   ", "F - test-cursor-down-past-bottom-of-trace/down-2"
1246   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "|||   ", "F - test-cursor-down-past-bottom-of-trace/down-2/cursor"
1247 }
1248 
1249 fn test-expand-within-trace {
1250   var t-storage: trace
1251   var t/esi: (addr trace) <- address t-storage
1252   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1253   #
1254   trace-text t, "l", "line 1"
1255   trace-text t, "l", "line 2"
1256   # setup: screen
1257   var screen-on-stack: screen
1258   var screen/edi: (addr screen) <- address screen-on-stack
1259   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1260   #
1261   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1262   #
1263   check-screen-row screen,                                  0/y, "...      ", "F - test-expand-within-trace/pre-0"
1264   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||      ", "F - test-expand-within-trace/pre-0/cursor"
1265   check-screen-row screen,                                  1/y, "         ", "F - test-expand-within-trace/pre-1"
1266   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-expand-within-trace/pre-1/cursor"
1267   # expand
1268   edit-trace t, 0xa/enter
1269   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1270   #
1271   check-screen-row screen,                                  0/y, "1 line 1 ", "F - test-expand-within-trace/expand-0"
1272   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-expand-within-trace/expand-0/cursor"
1273   check-screen-row screen,                                  1/y, "1 line 2 ", "F - test-expand-within-trace/expand-1"
1274   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-expand-within-trace/expand-1/cursor"
1275   check-screen-row screen,                                  2/y, "         ", "F - test-expand-within-trace/expand-2"
1276   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "         ", "F - test-expand-within-trace/expand-2/cursor"
1277 }
1278 
1279 fn test-trace-expand-skips-lower-depth {
1280   var t-storage: trace
1281   var t/esi: (addr trace) <- address t-storage
1282   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1283   #
1284   trace-text t, "l", "line 1"
1285   trace-lower t
1286   trace-text t, "l", "line 2"
1287   # setup: screen
1288   var screen-on-stack: screen
1289   var screen/edi: (addr screen) <- address screen-on-stack
1290   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1291   #
1292   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1293   #
1294   check-screen-row screen,                                  0/y, "...      ", "F - test-trace-expand-skips-lower-depth/pre-0"
1295   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||      ", "F - test-trace-expand-skips-lower-depth/pre-0/cursor"
1296   check-screen-row screen,                                  1/y, "         ", "F - test-trace-expand-skips-lower-depth/pre-1"
1297   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-skips-lower-depth/pre-1/cursor"
1298   # expand
1299   edit-trace t, 0xa/enter
1300   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1301   #
1302   check-screen-row screen,                                  0/y, "1 line 1 ", "F - test-trace-expand-skips-lower-depth/expand-0"
1303   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-skips-lower-depth/expand-0/cursor"
1304   check-screen-row screen,                                  1/y, "...      ", "F - test-trace-expand-skips-lower-depth/expand-1"
1305   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-skips-lower-depth/expand-1/cursor"
1306   check-screen-row screen,                                  2/y, "         ", "F - test-trace-expand-skips-lower-depth/expand-2"
1307   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "         ", "F - test-trace-expand-skips-lower-depth/expand-2/cursor"
1308 }
1309 
1310 fn test-trace-expand-continues-past-lower-depth {
1311   var t-storage: trace
1312   var t/esi: (addr trace) <- address t-storage
1313   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1314   #
1315   trace-text t, "l", "line 1"
1316   trace-lower t
1317   trace-text t, "l", "line 1.1"
1318   trace-higher t
1319   trace-text t, "l", "line 2"
1320   # setup: screen
1321   var screen-on-stack: screen
1322   var screen/edi: (addr screen) <- address screen-on-stack
1323   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1324   #
1325   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1326   #
1327   check-screen-row screen,                                  0/y, "...      ", "F - test-trace-expand-continues-past-lower-depth/pre-0"
1328   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||      ", "F - test-trace-expand-continues-past-lower-depth/pre-0/cursor"
1329   check-screen-row screen,                                  1/y, "         ", "F - test-trace-expand-continues-past-lower-depth/pre-1"
1330   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-continues-past-lower-depth/pre-1/cursor"
1331   # expand
1332   edit-trace t, 0xa/enter
1333   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1334   #
1335   check-screen-row screen,                                  0/y, "1 line 1 ", "F - test-trace-expand-continues-past-lower-depth/expand-0"
1336   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||| ", "F - test-trace-expand-continues-past-lower-depth/expand-0/cursor"
1337   # TODO: might be too wasteful to show every place where lines are hidden
1338   check-screen-row screen,                                  1/y, "...      ", "F - test-trace-expand-continues-past-lower-depth/expand-1"
1339   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "         ", "F - test-trace-expand-continues-past-lower-depth/expand-1/cursor"
1340   check-screen-row screen,                                  2/y, "1 line 2 ", "F - test-trace-expand-continues-past-lower-depth/expand-2"
1341   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "         ", "F - test-trace-expand-continues-past-lower-depth/expand-2/cursor"
1342 }
1343 
1344 fn test-trace-expand-stops-at-higher-depth {
1345   var t-storage: trace
1346   var t/esi: (addr trace) <- address t-storage
1347   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1348   #
1349   trace-lower t
1350   trace-text t, "l", "line 1.1"
1351   trace-lower t
1352   trace-text t, "l", "line 1.1.1"
1353   trace-higher t
1354   trace-text t, "l", "line 1.2"
1355   trace-higher t
1356   trace-text t, "l", "line 2"
1357   trace-lower t
1358   trace-text t, "l", "line 2.1"
1359   # setup: screen
1360   var screen-on-stack: screen
1361   var screen/edi: (addr screen) <- address screen-on-stack
1362   initialize-screen screen, 0x10/width, 8/height, 0/no-pixel-graphics
1363   #
1364   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1365   #
1366   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-expand-stops-at-higher-depth/pre-0"
1367   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-expand-stops-at-higher-depth/pre-0/cursor"
1368   check-screen-row screen,                                  1/y, "           ", "F - test-trace-expand-stops-at-higher-depth/pre-1"
1369   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-stops-at-higher-depth/pre-1/cursor"
1370   # expand
1371   edit-trace t, 0xa/enter
1372   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1373   #
1374   check-screen-row screen,                                  0/y, "2 line 1.1 ", "F - test-trace-expand-stops-at-higher-depth/expand-0"
1375   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||||||||| ", "F - test-trace-expand-stops-at-higher-depth/expand-0/cursor"
1376   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-expand-stops-at-higher-depth/expand-1"
1377   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-1/cursor"
1378   check-screen-row screen,                                  2/y, "2 line 1.2 ", "F - test-trace-expand-stops-at-higher-depth/expand-2"
1379   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-2/cursor"
1380   check-screen-row screen,                                  3/y, "...        ", "F - test-trace-expand-stops-at-higher-depth/expand-3"
1381   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-3/cursor"
1382   check-screen-row screen,                                  4/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-4"
1383   check-background-color-in-screen-row screen, 7/bg=cursor, 4/y, "           ", "F - test-trace-expand-stops-at-higher-depth/expand-4/cursor"
1384 }
1385 
1386 fn test-trace-expand-twice {
1387   var t-storage: trace
1388   var t/esi: (addr trace) <- address t-storage
1389   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1390   #
1391   trace-text t, "l", "line 1"
1392   trace-lower t
1393   trace-text t, "l", "line 1.1"
1394   trace-higher t
1395   trace-text t, "l", "line 2"
1396   # setup: screen
1397   var screen-on-stack: screen
1398   var screen/edi: (addr screen) <- address screen-on-stack
1399   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1400   #
1401   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1402   #
1403   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-expand-twice/pre-0"
1404   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-expand-twice/pre-0/cursor"
1405   check-screen-row screen,                                  1/y, "           ", "F - test-trace-expand-twice/pre-1"
1406   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-twice/pre-1/cursor"
1407   # expand
1408   edit-trace t, 0xa/enter
1409   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1410   #
1411   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-expand-twice/expand-0"
1412   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-expand-twice/expand-0/cursor"
1413   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-expand-twice/expand-1"
1414   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-expand-twice/expand-1/cursor"
1415   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-expand-twice/expand-2"
1416   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-twice/expand-2/cursor"
1417   # cursor down
1418   edit-trace t, 0x6a/j
1419   # hack: we need to render here to make this test pass; we're mixing state management with rendering
1420   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1421   #
1422   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-expand-twice/down-0"
1423   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-expand-twice/down-0/cursor"
1424   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-expand-twice/down-1"
1425   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||        ", "F - test-trace-expand-twice/down-1/cursor"
1426   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-expand-twice/down-2"
1427   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-twice/down-2/cursor"
1428   # expand again
1429   edit-trace t, 0xa/enter
1430   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1431   #
1432   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-expand-twice/expand2-0"
1433   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-expand-twice/expand2-0/cursor"
1434   check-screen-row screen,                                  1/y, "2 line 1.1 ", "F - test-trace-expand-twice/expand2-1"
1435   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||||||||| ", "F - test-trace-expand-twice/expand2-1/cursor"
1436   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-expand-twice/expand2-2"
1437   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-expand-twice/expand2-2/cursor"
1438 }
1439 
1440 fn test-trace-refresh-cursor {
1441   var t-storage: trace
1442   var t/esi: (addr trace) <- address t-storage
1443   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1444   #
1445   trace-text t, "l", "line 1"
1446   trace-text t, "l", "line 2"
1447   trace-text t, "l", "line 3"
1448   # setup: screen
1449   var screen-on-stack: screen
1450   var screen/edi: (addr screen) <- address screen-on-stack
1451   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1452   #
1453   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1454   #
1455   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-refresh-cursor/pre-0"
1456   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-refresh-cursor/pre-0/cursor"
1457   check-screen-row screen,                                  1/y, "           ", "F - test-trace-refresh-cursor/pre-1"
1458   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/pre-1/cursor"
1459   # expand
1460   edit-trace t, 0xa/enter
1461   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1462   #
1463   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-refresh-cursor/expand-0"
1464   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-refresh-cursor/expand-0/cursor"
1465   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-refresh-cursor/expand-1"
1466   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/expand-1/cursor"
1467   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-refresh-cursor/expand-2"
1468   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-refresh-cursor/expand-2/cursor"
1469   # cursor down
1470   edit-trace t, 0x6a/j
1471   edit-trace t, 0x6a/j
1472   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1473   #
1474   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-refresh-cursor/down-0"
1475   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-refresh-cursor/down-0/cursor"
1476   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-refresh-cursor/down-1"
1477   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/down-1/cursor"
1478   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-refresh-cursor/down-2"
1479   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-refresh-cursor/down-2/cursor"
1480   # recreate trace
1481   clear-trace t
1482   trace-text t, "l", "line 1"
1483   trace-text t, "l", "line 2"
1484   trace-text t, "l", "line 3"
1485   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1486   # cursor remains unchanged
1487   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-refresh-cursor/refresh-0"
1488   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-refresh-cursor/refresh-0/cursor"
1489   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-refresh-cursor/refresh-1"
1490   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-refresh-cursor/refresh-1/cursor"
1491   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-refresh-cursor/refresh-2"
1492   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-refresh-cursor/refresh-2/cursor"
1493 }
1494 
1495 fn test-trace-preserve-cursor-on-refresh {
1496   var t-storage: trace
1497   var t/esi: (addr trace) <- address t-storage
1498   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1499   #
1500   trace-text t, "l", "line 1"
1501   trace-text t, "l", "line 2"
1502   trace-text t, "l", "line 3"
1503   # setup: screen
1504   var screen-on-stack: screen
1505   var screen/edi: (addr screen) <- address screen-on-stack
1506   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1507   #
1508   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1509   #
1510   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-preserve-cursor-on-refresh/pre-0"
1511   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-preserve-cursor-on-refresh/pre-0/cursor"
1512   check-screen-row screen,                                  1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/pre-1"
1513   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/pre-1/cursor"
1514   # expand
1515   edit-trace t, 0xa/enter
1516   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1517   #
1518   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-preserve-cursor-on-refresh/expand-0"
1519   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-preserve-cursor-on-refresh/expand-0/cursor"
1520   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-preserve-cursor-on-refresh/expand-1"
1521   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/expand-1/cursor"
1522   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-preserve-cursor-on-refresh/expand-2"
1523   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "              ", "F - test-trace-preserve-cursor-on-refresh/expand-2/cursor"
1524   # cursor down
1525   edit-trace t, 0x6a/j
1526   edit-trace t, 0x6a/j
1527   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1528   #
1529   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-preserve-cursor-on-refresh/down-0"
1530   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-preserve-cursor-on-refresh/down-0/cursor"
1531   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-preserve-cursor-on-refresh/down-1"
1532   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/down-1/cursor"
1533   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-preserve-cursor-on-refresh/down-2"
1534   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-preserve-cursor-on-refresh/down-2/cursor"
1535   # recreate trace with slightly different lines
1536   clear-trace t
1537   trace-text t, "l", "line 4"
1538   trace-text t, "l", "line 5"
1539   trace-text t, "l", "line 3"  # cursor line is unchanged
1540   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1541   # cursor remains unchanged
1542   check-screen-row screen,                                  0/y, "1 line 4   ", "F - test-trace-preserve-cursor-on-refresh/refresh-0"
1543   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-preserve-cursor-on-refresh/refresh-0/cursor"
1544   check-screen-row screen,                                  1/y, "1 line 5   ", "F - test-trace-preserve-cursor-on-refresh/refresh-1"
1545   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-preserve-cursor-on-refresh/refresh-1/cursor"
1546   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-preserve-cursor-on-refresh/refresh-2"
1547   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-preserve-cursor-on-refresh/refresh-2/cursor"
1548 }
1549 
1550 fn test-trace-keep-cursor-visible-on-refresh {
1551   var t-storage: trace
1552   var t/esi: (addr trace) <- address t-storage
1553   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1554   #
1555   trace-text t, "l", "line 1"
1556   trace-text t, "l", "line 2"
1557   trace-text t, "l", "line 3"
1558   # setup: screen
1559   var screen-on-stack: screen
1560   var screen/edi: (addr screen) <- address screen-on-stack
1561   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1562   #
1563   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1564   #
1565   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-keep-cursor-visible-on-refresh/pre-0"
1566   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-keep-cursor-visible-on-refresh/pre-0/cursor"
1567   check-screen-row screen,                                  1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/pre-1"
1568   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/pre-1/cursor"
1569   # expand
1570   edit-trace t, 0xa/enter
1571   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1572   #
1573   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-0"
1574   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-0/cursor"
1575   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-1"
1576   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/expand-1/cursor"
1577   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-keep-cursor-visible-on-refresh/expand-2"
1578   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "              ", "F - test-trace-keep-cursor-visible-on-refresh/expand-2/cursor"
1579   # cursor down
1580   edit-trace t, 0x6a/j
1581   edit-trace t, 0x6a/j
1582   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1583   #
1584   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-keep-cursor-visible-on-refresh/down-0"
1585   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/down-0/cursor"
1586   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-keep-cursor-visible-on-refresh/down-1"
1587   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/down-1/cursor"
1588   check-screen-row screen,                                  2/y, "1 line 3   ", "F - test-trace-keep-cursor-visible-on-refresh/down-2"
1589   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-keep-cursor-visible-on-refresh/down-2/cursor"
1590   # recreate trace with entirely different lines
1591   clear-trace t
1592   trace-text t, "l", "line 4"
1593   trace-text t, "l", "line 5"
1594   trace-text t, "l", "line 6"
1595   mark-lines-dirty t
1596   clear-screen screen
1597   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1598   # trace collapses, and cursor bumps up
1599   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-0"
1600   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-0/cursor"
1601   check-screen-row screen,                                  1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-1"
1602   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-1/cursor"
1603   check-screen-row screen,                                  2/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-2"
1604   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-keep-cursor-visible-on-refresh/refresh-2/cursor"
1605 }
1606 
1607 fn test-trace-collapse-at-top {
1608   var t-storage: trace
1609   var t/esi: (addr trace) <- address t-storage
1610   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1611   #
1612   trace-text t, "l", "line 1"
1613   trace-lower t
1614   trace-text t, "l", "line 1.1"
1615   trace-higher t
1616   trace-text t, "l", "line 2"
1617   # setup: screen
1618   var screen-on-stack: screen
1619   var screen/edi: (addr screen) <- address screen-on-stack
1620   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1621   #
1622   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1623   #
1624   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-at-top/pre-0"
1625   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-at-top/pre-0/cursor"
1626   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-at-top/pre-1"
1627   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-at-top/pre-1/cursor"
1628   # expand
1629   edit-trace t, 0xa/enter
1630   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1631   #
1632   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-at-top/expand-0"
1633   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-at-top/expand-0/cursor"
1634   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-at-top/expand-1"
1635   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-at-top/expand-1/cursor"
1636   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-at-top/expand-2"
1637   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-at-top/expand-2/cursor"
1638   # collapse
1639   edit-trace t, 8/backspace
1640   # hack: we need to render here to make this test pass; we're mixing state management with rendering
1641   clear-screen screen
1642   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1643   #
1644   check-ints-equal y, 1, "F - test-trace-collapse-at-top/post-0/y"
1645   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-at-top/post-0"
1646   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-at-top/post-0/cursor"
1647   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-at-top/post-1"
1648   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-at-top/post-1/cursor"
1649 }
1650 
1651 fn test-trace-collapse {
1652   var t-storage: trace
1653   var t/esi: (addr trace) <- address t-storage
1654   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1655   #
1656   trace-text t, "l", "line 1"
1657   trace-text t, "l", "line 2"
1658   # setup: screen
1659   var screen-on-stack: screen
1660   var screen/edi: (addr screen) <- address screen-on-stack
1661   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1662   #
1663   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1664   #
1665   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse/pre-0"
1666   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse/pre-0/cursor"
1667   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse/pre-1"
1668   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse/pre-1/cursor"
1669   # expand
1670   edit-trace t, 0xa/enter
1671   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1672   #
1673   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse/expand-0"
1674   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse/expand-0/cursor"
1675   check-screen-row screen,                                  1/y, "1 line 2   ", "F - test-trace-collapse/expand-1"
1676   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse/expand-1/cursor"
1677   # cursor down
1678   edit-trace t, 0x6a/j
1679   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1680   # collapse
1681   edit-trace t, 8/backspace
1682   clear-screen screen
1683   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1684   #
1685   check-ints-equal y, 1, "F - test-trace-collapse/post-0/y"
1686   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse/post-0"
1687   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse/post-0/cursor"
1688   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse/post-1"
1689   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse/post-1/cursor"
1690 }
1691 
1692 fn test-trace-collapse-skips-invisible-lines {
1693   var t-storage: trace
1694   var t/esi: (addr trace) <- address t-storage
1695   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1696   #
1697   trace-text t, "l", "line 1"
1698   trace-lower t
1699   trace-text t, "l", "line 1.1"
1700   trace-higher t
1701   trace-text t, "l", "line 2"
1702   # setup: screen
1703   var screen-on-stack: screen
1704   var screen/edi: (addr screen) <- address screen-on-stack
1705   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1706   #
1707   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1708   #
1709   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-skips-invisible-lines/pre-0"
1710   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-skips-invisible-lines/pre-0/cursor"
1711   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/pre-1"
1712   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/pre-1/cursor"
1713   # expand
1714   edit-trace t, 0xa/enter
1715   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1716   # two visible lines with an invisible line in between
1717   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-skips-invisible-lines/expand-0"
1718   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-skips-invisible-lines/expand-0/cursor"
1719   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-skips-invisible-lines/expand-1"
1720   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/expand-1/cursor"
1721   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-skips-invisible-lines/expand-2"
1722   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-skips-invisible-lines/expand-2/cursor"
1723   # cursor down to second visible line
1724   edit-trace t, 0x6a/j
1725   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1726   edit-trace t, 0x6a/j
1727   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1728   # collapse
1729   edit-trace t, 8/backspace
1730   clear-screen screen
1731   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1732   #
1733   check-ints-equal y, 1, "F - test-trace-collapse-skips-invisible-lines/post-0/y"
1734   var cursor-y/eax: (addr int) <- get t, cursor-y
1735   check-ints-equal *cursor-y, 0, "F - test-trace-collapse-skips-invisible-lines/post-0/cursor-y"
1736   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-skips-invisible-lines/post-0"
1737   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-skips-invisible-lines/post-0/cursor"
1738   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/post-1"
1739   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-skips-invisible-lines/post-1/cursor"
1740 }
1741 
1742 fn test-trace-collapse-two-levels {
1743   var t-storage: trace
1744   var t/esi: (addr trace) <- address t-storage
1745   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1746   #
1747   trace-text t, "l", "line 1"
1748   trace-lower t
1749   trace-text t, "l", "line 1.1"
1750   trace-higher t
1751   trace-text t, "l", "line 2"
1752   # setup: screen
1753   var screen-on-stack: screen
1754   var screen/edi: (addr screen) <- address screen-on-stack
1755   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1756   #
1757   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1758   #
1759   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-two-levels/pre-0"
1760   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-two-levels/pre-0/cursor"
1761   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-two-levels/pre-1"
1762   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-two-levels/pre-1/cursor"
1763   # expand
1764   edit-trace t, 0xa/enter
1765   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1766   # two visible lines with an invisible line in between
1767   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-two-levels/expand-0"
1768   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-two-levels/expand-0/cursor"
1769   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-two-levels/expand-1"
1770   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-two-levels/expand-1/cursor"
1771   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-two-levels/expand-2"
1772   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-two-levels/expand-2/cursor"
1773   # cursor down to ellipses
1774   edit-trace t, 0x6a/j
1775   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1776   # expand
1777   edit-trace t, 0xa/enter
1778   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1779   # two visible lines with an invisible line in between
1780   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-two-levels/expand2-0"
1781   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-collapse-two-levels/expand2-0/cursor"
1782   check-screen-row screen,                                  1/y, "2 line 1.1 ", "F - test-trace-collapse-two-levels/expand2-1"
1783   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "|||||||||| ", "F - test-trace-collapse-two-levels/expand2-1/cursor"
1784   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-two-levels/expand2-2"
1785   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-two-levels/expand2-2/cursor"
1786   # cursor down to second visible line
1787   edit-trace t, 0x6a/j
1788   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1789   # collapse
1790   edit-trace t, 8/backspace
1791   clear-screen screen
1792   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1793   #
1794   check-ints-equal y, 1, "F - test-trace-collapse-two-levels/post-0/y"
1795   var cursor-y/eax: (addr int) <- get t, cursor-y
1796   check-ints-equal *cursor-y, 0, "F - test-trace-collapse-two-levels/post-0/cursor-y"
1797   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-two-levels/post-0"
1798   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-two-levels/post-0/cursor"
1799   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-two-levels/post-1"
1800   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-two-levels/post-1/cursor"
1801 }
1802 
1803 fn test-trace-collapse-nested-level {
1804   var t-storage: trace
1805   var t/esi: (addr trace) <- address t-storage
1806   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1807   #
1808   trace-text t, "l", "line 1"
1809   trace-lower t
1810   trace-text t, "l", "line 1.1"
1811   trace-higher t
1812   trace-text t, "l", "line 2"
1813   trace-lower t
1814   trace-text t, "l", "line 2.1"
1815   trace-text t, "l", "line 2.2"
1816   trace-higher t
1817   # setup: screen
1818   var screen-on-stack: screen
1819   var screen/edi: (addr screen) <- address screen-on-stack
1820   initialize-screen screen, 0x10/width, 8/height, 0/no-pixel-graphics
1821   #
1822   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1823   #
1824   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-collapse-nested-level/pre-0"
1825   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-collapse-nested-level/pre-0/cursor"
1826   check-screen-row screen,                                  1/y, "           ", "F - test-trace-collapse-nested-level/pre-1"
1827   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/pre-1/cursor"
1828   # expand
1829   edit-trace t, 0xa/enter
1830   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1831   # two visible lines with an invisible line in between
1832   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-nested-level/expand-0"
1833   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-collapse-nested-level/expand-0/cursor"
1834   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-nested-level/expand-1"
1835   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/expand-1/cursor"
1836   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-nested-level/expand-2"
1837   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-nested-level/expand-2/cursor"
1838   check-screen-row screen,                                  3/y, "...        ", "F - test-trace-collapse-nested-level/expand-3"
1839   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-collapse-nested-level/expand-3/cursor"
1840   # cursor down to bottom
1841   edit-trace t, 0x6a/j
1842   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1843   edit-trace t, 0x6a/j
1844   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1845   edit-trace t, 0x6a/j
1846   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1847   # expand
1848   edit-trace t, 0xa/enter
1849   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1850   # two visible lines with an invisible line in between
1851   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-nested-level/expand2-0"
1852   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-collapse-nested-level/expand2-0/cursor"
1853   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-nested-level/expand2-1"
1854   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/expand2-1/cursor"
1855   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-nested-level/expand2-2"
1856   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-collapse-nested-level/expand2-2/cursor"
1857   check-screen-row screen,                                  3/y, "2 line 2.1 ", "F - test-trace-collapse-nested-level/expand2-3"
1858   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "|||||||||| ", "F - test-trace-collapse-nested-level/expand2-3/cursor"
1859   check-screen-row screen,                                  4/y, "2 line 2.2 ", "F - test-trace-collapse-nested-level/expand2-4"
1860   check-background-color-in-screen-row screen, 7/bg=cursor, 4/y, "           ", "F - test-trace-collapse-nested-level/expand2-4/cursor"
1861   # collapse
1862   edit-trace t, 8/backspace
1863   clear-screen screen
1864   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 8/ymax, 1/show-cursor
1865   #
1866   check-ints-equal y, 4, "F - test-trace-collapse-nested-level/post-0/y"
1867   var cursor-y/eax: (addr int) <- get t, cursor-y
1868   check-ints-equal *cursor-y, 2, "F - test-trace-collapse-nested-level/post-0/cursor-y"
1869   check-screen-row screen,                                  0/y, "1 line 1   ", "F - test-trace-collapse-nested-level/post-0"
1870   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "           ", "F - test-trace-collapse-nested-level/post-0/cursor"
1871   check-screen-row screen,                                  1/y, "...        ", "F - test-trace-collapse-nested-level/post-1"
1872   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-collapse-nested-level/post-1/cursor"
1873   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-collapse-nested-level/post-2"
1874   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "||||||||   ", "F - test-trace-collapse-nested-level/post-2/cursor"
1875   check-screen-row screen,                                  3/y, "...        ", "F - test-trace-collapse-nested-level/post-3"
1876   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-collapse-nested-level/post-3/cursor"
1877 }
1878 
1879 fn scroll-down _self: (addr trace) {
1880   var self/esi: (addr trace) <- copy _self
1881   var screen-height-addr/ebx: (addr int) <- get self, screen-height  # only available after first render
1882   var lines-to-skip/ebx: int <- copy *screen-height-addr
1883   var top-line-y-addr/eax: (addr int) <- get self, top-line-y
1884   lines-to-skip <- subtract *top-line-y-addr
1885   var already-hiding-lines-storage: boolean
1886   var already-hiding-lines/edx: (addr boolean) <- address already-hiding-lines-storage
1887   var top-line-addr/edi: (addr int) <- get self, top-line-index
1888   var i/eax: int <- copy *top-line-addr
1889   var max-addr/ecx: (addr int) <- get self, first-free
1890   {
1891     # if we run out of trace, return without changing anything
1892     compare i, *max-addr
1893     {
1894       break-if-<
1895       return
1896     }
1897     # if we've skipped enough, break
1898     compare lines-to-skip, 0
1899     break-if-<=
1900     #
1901     {
1902       var display?/eax: boolean <- count-line? self, i, already-hiding-lines
1903       compare display?, 0/false
1904       break-if-=
1905       lines-to-skip <- decrement
1906     }
1907     i <- increment
1908     loop
1909   }
1910   # update top-line
1911   copy-to *top-line-addr, i
1912 }
1913 
1914 fn scroll-up _self: (addr trace) {
1915   var self/esi: (addr trace) <- copy _self
1916   var screen-height-addr/ebx: (addr int) <- get self, screen-height  # only available after first render
1917   var lines-to-skip/ebx: int <- copy *screen-height-addr
1918   var top-line-y-addr/eax: (addr int) <- get self, top-line-y
1919   lines-to-skip <- subtract *top-line-y-addr
1920   var already-hiding-lines-storage: boolean
1921   var already-hiding-lines/edx: (addr boolean) <- address already-hiding-lines-storage
1922   var top-line-addr/ecx: (addr int) <- get self, top-line-index
1923   $scroll-up:loop: {
1924     # if we run out of trace, break
1925     compare *top-line-addr, 0
1926     break-if-<=
1927     # if we've skipped enough, break
1928     compare lines-to-skip, 0
1929     break-if-<=
1930     #
1931     var display?/eax: boolean <- count-line? self, *top-line-addr, already-hiding-lines
1932     compare display?, 0/false
1933     {
1934       break-if-=
1935       lines-to-skip <- decrement
1936     }
1937     decrement *top-line-addr
1938     loop
1939   }
1940 }
1941 
1942 # TODO: duplicates logic for counting lines rendered
1943 fn count-line? _self: (addr trace), index: int, _already-hiding-lines?: (addr boolean) -> _/eax: boolean {
1944   var self/esi: (addr trace) <- copy _self
1945   var trace-ah/eax: (addr handle array trace-line) <- get self, data
1946   var trace/eax: (addr array trace-line) <- lookup *trace-ah
1947   var offset/ecx: (offset trace-line) <- compute-offset trace, index
1948   var curr/eax: (addr trace-line) <- index trace, offset
1949   var already-hiding-lines?/ecx: (addr boolean) <- copy _already-hiding-lines?
1950   # count errors
1951   {
1952     var curr-depth/eax: (addr int) <- get curr, depth
1953     compare *curr-depth, 0/error
1954     break-if-!=
1955     copy-to *already-hiding-lines?, 0/false
1956     return 1/true
1957   }
1958   # count visible lines
1959   {
1960     var display?/eax: boolean <- should-render? curr
1961     compare display?, 0/false
1962     break-if-=
1963     copy-to *already-hiding-lines?, 0/false
1964     return 1/true
1965   }
1966   # count first undisplayed line after line to display
1967   compare *already-hiding-lines?, 0/false
1968   {
1969     break-if-!=
1970     copy-to *already-hiding-lines?, 1/true
1971     return 1/true
1972   }
1973   return 0/false
1974 }
1975 
1976 fn test-trace-scroll {
1977   var t-storage: trace
1978   var t/esi: (addr trace) <- address t-storage
1979   initialize-trace t, 0x100/max-depth, 0x10, 0x10
1980   #
1981   trace-text t, "l", "line 0"
1982   trace-text t, "l", "line 1"
1983   trace-text t, "l", "line 2"
1984   trace-text t, "l", "line 3"
1985   trace-text t, "l", "line 4"
1986   trace-text t, "l", "line 5"
1987   trace-text t, "l", "line 6"
1988   trace-text t, "l", "line 7"
1989   trace-text t, "l", "line 8"
1990   trace-text t, "l", "line 9"
1991   # setup: screen
1992   var screen-on-stack: screen
1993   var screen/edi: (addr screen) <- address screen-on-stack
1994   initialize-screen screen, 0x10/width, 4/height, 0/no-pixel-graphics
1995   # pre-render
1996   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
1997   #
1998   check-screen-row screen,                                  0/y, "...        ", "F - test-trace-scroll/pre-0"
1999   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "|||        ", "F - test-trace-scroll/pre-0/cursor"
2000   check-screen-row screen,                                  1/y, "           ", "F - test-trace-scroll/pre-1"
2001   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/pre-1/cursor"
2002   check-screen-row screen,                                  2/y, "           ", "F - test-trace-scroll/pre-2"
2003   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/pre-2/cursor"
2004   check-screen-row screen,                                  3/y, "           ", "F - test-trace-scroll/pre-3"
2005   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/pre-3/cursor"
2006   # expand
2007   edit-trace t, 0xa/enter
2008   clear-screen screen
2009   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2010   #
2011   check-screen-row screen,                                  0/y, "1 line 0   ", "F - test-trace-scroll/expand-0"
2012   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/expand-0/cursor"
2013   check-screen-row screen,                                  1/y, "1 line 1   ", "F - test-trace-scroll/expand-1"
2014   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/expand-1/cursor"
2015   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-scroll/expand-2"
2016   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/expand-2/cursor"
2017   check-screen-row screen,                                  3/y, "1 line 3   ", "F - test-trace-scroll/expand-3"
2018   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/expand-3/cursor"
2019   # scroll up
2020   # hack: we must have rendered before this point; we're mixing state management with rendering
2021   edit-trace t, 2/ctrl-b
2022   clear-screen screen
2023   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2024   # no change since we're already at the top
2025   check-screen-row screen,                                  0/y, "1 line 0   ", "F - test-trace-scroll/up0-0"
2026   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/up0-0/cursor"
2027   check-screen-row screen,                                  1/y, "1 line 1   ", "F - test-trace-scroll/up0-1"
2028   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/up0-1/cursor"
2029   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-scroll/up0-2"
2030   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/up0-2/cursor"
2031   check-screen-row screen,                                  3/y, "1 line 3   ", "F - test-trace-scroll/up0-3"
2032   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/up0-3/cursor"
2033   # scroll down
2034   edit-trace t, 6/ctrl-f
2035   clear-screen screen
2036   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2037   check-screen-row screen,                                  0/y, "1 line 4   ", "F - test-trace-scroll/down1-0"
2038   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/down1-0/cursor"
2039   check-screen-row screen,                                  1/y, "1 line 5   ", "F - test-trace-scroll/down1-1"
2040   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/down1-1/cursor"
2041   check-screen-row screen,                                  2/y, "1 line 6   ", "F - test-trace-scroll/down1-2"
2042   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/down1-2/cursor"
2043   check-screen-row screen,                                  3/y, "1 line 7   ", "F - test-trace-scroll/down1-3"
2044   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/down1-3/cursor"
2045   # scroll down
2046   edit-trace t, 6/ctrl-f
2047   clear-screen screen
2048   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2049   check-screen-row screen,                                  0/y, "1 line 8   ", "F - test-trace-scroll/down2-0"
2050   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/down2-0/cursor"
2051   check-screen-row screen,                                  1/y, "1 line 9   ", "F - test-trace-scroll/down2-1"
2052   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/down2-1/cursor"
2053   check-screen-row screen,                                  2/y, "           ", "F - test-trace-scroll/down2-2"
2054   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/down2-2/cursor"
2055   check-screen-row screen,                                  3/y, "           ", "F - test-trace-scroll/down2-3"
2056   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/down2-3/cursor"
2057   # scroll down
2058   edit-trace t, 6/ctrl-f
2059   clear-screen screen
2060   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2061   # no change since we're already at the bottom
2062   check-screen-row screen,                                  0/y, "1 line 8   ", "F - test-trace-scroll/down3-0"
2063   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/down3-0/cursor"
2064   check-screen-row screen,                                  1/y, "1 line 9   ", "F - test-trace-scroll/down3-1"
2065   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/down3-1/cursor"
2066   check-screen-row screen,                                  2/y, "           ", "F - test-trace-scroll/down3-2"
2067   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/down3-2/cursor"
2068   check-screen-row screen,                                  3/y, "           ", "F - test-trace-scroll/down3-3"
2069   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/down3-3/cursor"
2070   # scroll up
2071   edit-trace t, 2/ctrl-b
2072   clear-screen screen
2073   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2074   check-screen-row screen,                                  0/y, "1 line 4   ", "F - test-trace-scroll/up1-0"
2075   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/up1-0/cursor"
2076   check-screen-row screen,                                  1/y, "1 line 5   ", "F - test-trace-scroll/up1-1"
2077   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/up1-1/cursor"
2078   check-screen-row screen,                                  2/y, "1 line 6   ", "F - test-trace-scroll/up1-2"
2079   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/up1-2/cursor"
2080   check-screen-row screen,                                  3/y, "1 line 7   ", "F - test-trace-scroll/up1-3"
2081   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/up1-3/cursor"
2082   # scroll up
2083   edit-trace t, 2/ctrl-b
2084   clear-screen screen
2085   var y/ecx: int <- render-trace screen, t, 0/xmin, 0/ymin, 0x10/xmax, 4/ymax, 1/show-cursor
2086   check-screen-row screen,                                  0/y, "1 line 0   ", "F - test-trace-scroll/up2-0"
2087   check-background-color-in-screen-row screen, 7/bg=cursor, 0/y, "||||||||   ", "F - test-trace-scroll/up2-0/cursor"
2088   check-screen-row screen,                                  1/y, "1 line 1   ", "F - test-trace-scroll/up2-1"
2089   check-background-color-in-screen-row screen, 7/bg=cursor, 1/y, "           ", "F - test-trace-scroll/up2-1/cursor"
2090   check-screen-row screen,                                  2/y, "1 line 2   ", "F - test-trace-scroll/up2-2"
2091   check-background-color-in-screen-row screen, 7/bg=cursor, 2/y, "           ", "F - test-trace-scroll/up2-2/cursor"
2092   check-screen-row screen,                                  3/y, "1 line 3   ", "F - test-trace-scroll/up2-3"
2093   check-background-color-in-screen-row screen, 7/bg=cursor, 3/y, "           ", "F - test-trace-scroll/up2-3/cursor"
2094 }
2095 
2096 # saving and restoring trace indices
2097 
2098 fn save-indices _self: (addr trace), _out: (addr trace-index-stash) {
2099   var self/esi: (addr trace) <- copy _self
2100   var out/edi: (addr trace-index-stash) <- copy _out
2101   var data-ah/eax: (addr handle array trace-line) <- get self, data
2102   var _data/eax: (addr array trace-line) <- lookup *data-ah
2103   var data/ebx: (addr array trace-line) <- copy _data
2104   # cursor
2105   var cursor-line-index-addr/eax: (addr int) <- get self, cursor-line-index
2106   var cursor-line-index/eax: int <- copy *cursor-line-index-addr
2107 #?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, cursor-line-index, 2/fg 0/bg
2108   var offset/eax: (offset trace-line) <- compute-offset data, cursor-line-index
2109   var cursor-line/ecx: (addr trace-line) <- index data, offset
2110   var src/eax: (addr int) <- get cursor-line, depth
2111   var dest/edx: (addr int) <- get out, cursor-line-depth
2112   copy-object src, dest
2113   var src/eax: (addr handle array byte) <- get cursor-line, label
2114   var dest/edx: (addr handle array byte) <- get out, cursor-line-label
2115   copy-object src, dest
2116   src <- get cursor-line, data
2117 #?   {
2118 #?     var foo/eax: (addr array byte) <- lookup *src
2119 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg 0/bg
2120 #?     var cursor-line-visible-addr/eax: (addr boolean) <- get cursor-line, visible?
2121 #?     var cursor-line-visible?/eax: boolean <- copy *cursor-line-visible-addr
2122 #?     var foo/eax: int <- copy cursor-line-visible?
2123 #?     draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 5/fg 0/bg
2124 #?   }
2125   dest <- get out, cursor-line-data
2126   copy-object src, dest
2127   # top of screen
2128   var top-line-index-addr/eax: (addr int) <- get self, top-line-index
2129   var top-line-index/eax: int <- copy *top-line-index-addr
2130 #?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, top-line-index, 2/fg 0/bg
2131   var offset/eax: (offset trace-line) <- compute-offset data, top-line-index
2132   var top-line/ecx: (addr trace-line) <- index data, offset
2133   var src/eax: (addr int) <- get top-line, depth
2134   var dest/edx: (addr int) <- get out, top-line-depth
2135   copy-object src, dest
2136   var src/eax: (addr handle array byte) <- get top-line, label
2137   var dest/edx: (addr handle array byte) <- get out, top-line-label
2138   copy-object src, dest
2139   src <- get top-line, data
2140   dest <- get out, top-line-data
2141   copy-object src, dest
2142 }
2143 
2144 fn restore-indices _self: (addr trace), _in: (addr trace-index-stash) {
2145   var self/edi: (addr trace) <- copy _self
2146   var in/esi: (addr trace-index-stash) <- copy _in
2147   var data-ah/eax: (addr handle array trace-line) <- get self, data
2148   var _data/eax: (addr array trace-line) <- lookup *data-ah
2149   var data/ebx: (addr array trace-line) <- copy _data
2150   # cursor
2151   var cursor-depth/edx: (addr int) <- get in, cursor-line-depth
2152   var cursor-line-label-ah/eax: (addr handle array byte) <- get in, cursor-line-label
2153   var _cursor-line-label/eax: (addr array byte) <- lookup *cursor-line-label-ah
2154   var cursor-line-label/ecx: (addr array byte) <- copy _cursor-line-label
2155   var cursor-line-data-ah/eax: (addr handle array byte) <- get in, cursor-line-data
2156   var cursor-line-data/eax: (addr array byte) <- lookup *cursor-line-data-ah
2157   var new-cursor-line-index/eax: int <- find-in-trace self, *cursor-depth, cursor-line-label, cursor-line-data
2158   var dest/edx: (addr int) <- get self, cursor-line-index
2159   copy-to *dest, new-cursor-line-index
2160   # top of screen
2161   var top-depth/edx: (addr int) <- get in, top-line-depth
2162   var top-line-label-ah/eax: (addr handle array byte) <- get in, top-line-label
2163   var _top-line-label/eax: (addr array byte) <- lookup *top-line-label-ah
2164   var top-line-label/ecx: (addr array byte) <- copy _top-line-label
2165   var top-line-data-ah/eax: (addr handle array byte) <- get in, top-line-data
2166   var top-line-data/eax: (addr array byte) <- lookup *top-line-data-ah
2167   var new-top-line-index/eax: int <- find-in-trace self, *top-depth, top-line-label, top-line-data
2168   var dest/edx: (addr int) <- get self, top-line-index
2169   copy-to *dest, new-top-line-index
2170 }
2171 
2172 # like trace-contains? but stateless
2173 # this is super-inefficient, string comparing every trace line
2174 fn find-in-trace _self: (addr trace), depth: int, label: (addr array byte), data: (addr array byte) -> _/eax: int {
2175   var self/esi: (addr trace) <- copy _self
2176   var candidates-ah/eax: (addr handle array trace-line) <- get self, data
2177   var candidates/eax: (addr array trace-line) <- lookup *candidates-ah
2178   var i/ecx: int <- copy 0
2179   var max/edx: (addr int) <- get self, first-free
2180   {
2181     compare i, *max
2182     break-if->=
2183     {
2184       var curr-offset/edx: (offset trace-line) <- compute-offset candidates, i
2185       var curr/edx: (addr trace-line) <- index candidates, curr-offset
2186       # if curr->depth does not match, continue
2187       var curr-depth-addr/eax: (addr int) <- get curr, depth
2188       var curr-depth/eax: int <- copy *curr-depth-addr
2189       compare curr-depth, depth
2190       break-if-!=
2191       # if curr->label does not match, continue
2192       var curr-label-ah/eax: (addr handle array byte) <- get curr, label
2193       var curr-label/eax: (addr array byte) <- lookup *curr-label-ah
2194       var match?/eax: boolean <- string-equal? curr-label, label
2195       compare match?, 0/false
2196       break-if-=
2197       # if curr->data does not match, continue
2198       var curr-data-ah/eax: (addr handle array byte) <- get curr, data
2199       var curr-data/eax: (addr array byte) <- lookup *curr-data-ah
2200       {
2201         var match?/eax: boolean <- string-equal? curr-data, data
2202         compare match?, 0/false
2203       }
2204       break-if-=
2205 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, " => ", 7/fg 0/bg
2206 #? #?       draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, i, 4/fg 0/bg
2207 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, curr-data, 7/fg 0/bg
2208 #?       var curr-visible-addr/eax: (addr boolean) <- get curr, visible?
2209 #?       var curr-visible?/eax: boolean <- copy *curr-visible-addr
2210 #?       var foo/eax: int <- copy curr-visible?
2211 #?       draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 2/fg 0/bg
2212       return i
2213     }
2214     i <- increment
2215     loop
2216   }
2217   abort "not in trace"
2218   return -1
2219 }