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: grapheme {
     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 }