https://github.com/akkartik/mu/blob/main/500fake-screen.mu
  1 # Testable primitives for writing to screen.
  2 #
  3 # Mu mostly uses the screen for text, but it builds it out of pixel graphics
  4 # and a bitmap font. There is no support for a blinking cursor, scrolling and
  5 # so on.
  6 #
  7 # Fake screens are primarily for testing text-mode prints. However, they do
  8 # support some rudimentary pixel operations as well. Caveats:
  9 #
 10 # - Drawing pixels atop text or vice versa is not supported. Results in a fake
 11 #   screen will not mimic real screens in these situations.
 12 # - Fake screens currently also assume a fixed-width 8x16 font.
 13 
 14 type screen {
 15   # text mode
 16   width: int
 17   height: int
 18   data: (handle array screen-cell)
 19   cursor-x: int  # [0..width)
 20   cursor-y: int  # [0..height)
 21   # pixel graphics
 22   pixels: (handle array byte)
 23 }
 24 
 25 type screen-cell {
 26   data: code-point  # TODO: support combining characters overlaid on another character
 27   color: int
 28   background-color: int
 29   unused?: boolean
 30 }
 31 
 32 fn initialize-screen _screen: (addr screen), width: int, height: int, pixel-graphics?: boolean {
 33   var screen/esi: (addr screen) <- copy _screen
 34   var tmp/eax: int <- copy 0
 35   var dest/edi: (addr int) <- copy 0
 36   # screen->width = width
 37   dest <- get screen, width
 38   tmp <- copy width
 39   copy-to *dest, tmp
 40   # screen->height = height
 41   dest <- get screen, height
 42   tmp <- copy height
 43   copy-to *dest, tmp
 44   # populate screen->data
 45   {
 46     var data-ah/edi: (addr handle array screen-cell) <- get screen, data
 47     var capacity/eax: int <- copy width
 48     capacity <- multiply height
 49     #
 50     populate data-ah, capacity
 51   }
 52   # if necessary, populate screen->pixels
 53   {
 54     compare pixel-graphics?, 0/false
 55     break-if-=
 56     var pixels-ah/edi: (addr handle array byte) <- get screen, pixels
 57     var capacity/eax: int <- copy width
 58     capacity <- shift-left 3/log2-font-width
 59     capacity <- multiply height
 60     capacity <- shift-left 4/log2-font-height
 61     #
 62     populate pixels-ah, capacity
 63   }
 64   # screen->cursor-x = 0
 65   dest <- get screen, cursor-x
 66   copy-to *dest, 0
 67   # screen->cursor-y = 0
 68   dest <- get screen, cursor-y
 69   copy-to *dest, 0
 70 }
 71 
 72 # in graphemes
 73 fn screen-size _screen: (addr screen) -> _/eax: int, _/ecx: int {
 74   var screen/esi: (addr screen) <- copy _screen
 75   var width/eax: int <- copy 0
 76   var height/ecx: int <- copy 0
 77   compare screen, 0
 78   {
 79     break-if-!=
 80     return 0x80/128, 0x30/48
 81   }
 82   # fake screen
 83   var tmp/edx: (addr int) <- get screen, width
 84   width <- copy *tmp
 85   tmp <- get screen, height
 86   height <- copy *tmp
 87   return width, height
 88 }
 89 
 90 # testable screen primitive
 91 # return number of 8x16 units drawn
 92 fn draw-code-point _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int -> _/eax: int {
 93   var screen/esi: (addr screen) <- copy _screen
 94   {
 95     compare screen, 0
 96     break-if-!=
 97     var result/eax: int <- draw-code-point-on-real-screen c, x, y, color, background-color
 98     return result
 99   }
100   # fake screen
101   var wide?/eax: boolean <- wide-code-point? c
102   compare wide?, 0/false
103   {
104     break-if-=
105     draw-wide-code-point-on-fake-screen screen, c, x, y, color, background-color
106     return 2
107   }
108   draw-narrow-code-point-on-fake-screen screen, c, x, y, color, background-color
109   return 1
110 }
111 
112 fn overlay-code-point _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int -> _/eax: int {
113   var screen/esi: (addr screen) <- copy _screen
114   {
115     compare screen, 0
116     break-if-!=
117     var result/eax: int <- overlay-code-point-on-real-screen c, x, y, color, background-color
118     return result
119   }
120   # fake screen
121   # TODO: support overlays in fake screen
122   var wide?/eax: boolean <- wide-code-point? c
123   compare wide?, 0/false
124   {
125     break-if-=
126     draw-wide-code-point-on-fake-screen screen, c, x, y, color, background-color
127     return 2
128   }
129   draw-narrow-code-point-on-fake-screen screen, c, x, y, color, background-color
130   return 1
131 }
132 
133 fn draw-narrow-code-point-on-fake-screen _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
134   var screen/esi: (addr screen) <- copy _screen
135   # ignore if out of bounds
136   {
137     compare x, 0
138     break-if->=
139     return
140   }
141   {
142     var xmax-addr/eax: (addr int) <- get screen, width
143     var xmax/eax: int <- copy *xmax-addr
144     compare x, xmax
145     break-if-<
146     {
147       loop
148     }
149     return
150   }
151   {
152     compare y, 0
153     break-if->=
154     return
155   }
156   {
157     var ymax-addr/eax: (addr int) <- get screen, height
158     var ymax/eax: int <- copy *ymax-addr
159     compare y, ymax
160     break-if-<
161     return
162   }
163   #
164   var index/ecx: int <- screen-cell-index screen, x, y
165   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
166   var data/eax: (addr array screen-cell) <- lookup *data-ah
167   var offset/ecx: (offset screen-cell) <- compute-offset data, index
168   var dest-cell/ecx: (addr screen-cell) <- index data, offset
169   var dest-code-point/eax: (addr code-point) <- get dest-cell, data
170   var c2/edx: code-point <- copy c
171   copy-to *dest-code-point, c2
172   var dest-color/eax: (addr int) <- get dest-cell, color
173   var src-color/edx: int <- copy color
174   copy-to *dest-color, src-color
175   dest-color <- get dest-cell, background-color
176   src-color <- copy background-color
177   copy-to *dest-color, src-color
178   var dest/eax: (addr boolean) <- get dest-cell, unused?
179   copy-to *dest, 0/false
180 }
181 
182 fn draw-wide-code-point-on-fake-screen _screen: (addr screen), c: code-point, x: int, y: int, color: int, background-color: int {
183   var screen/esi: (addr screen) <- copy _screen
184   # ignore if out of bounds
185   {
186     compare x, 0
187     break-if->=
188     return
189   }
190   {
191     var xmax-addr/eax: (addr int) <- get screen, width
192     var xmax/eax: int <- copy *xmax-addr
193     xmax <- decrement  # wide code-points need an extra unit
194     compare x, xmax
195     break-if-<
196     return
197   }
198   {
199     compare y, 0
200     break-if->=
201     return
202   }
203   {
204     var ymax-addr/eax: (addr int) <- get screen, height
205     var ymax/eax: int <- copy *ymax-addr
206     compare y, ymax
207     break-if-<
208     return
209   }
210   #
211   var index/ecx: int <- screen-cell-index screen, x, y
212   {
213     var data-ah/eax: (addr handle array screen-cell) <- get screen, data
214     var data/eax: (addr array screen-cell) <- lookup *data-ah
215     var offset/ecx: (offset screen-cell) <- compute-offset data, index
216     var dest-cell/ecx: (addr screen-cell) <- index data, offset
217     var dest-code-point/eax: (addr code-point) <- get dest-cell, data
218     var c2/edx: code-point <- copy c
219     copy-to *dest-code-point, c2
220     var dest-color/eax: (addr int) <- get dest-cell, color
221     var src-color/edx: int <- copy color
222     copy-to *dest-color, src-color
223     dest-color <- get dest-cell, background-color
224     src-color <- copy background-color
225     copy-to *dest-color, src-color
226     var dest/eax: (addr boolean) <- get dest-cell, unused?
227     copy-to *dest, 0/false
228   }
229   # set next screen-cell to unused
230   index <- increment
231   {
232     var data-ah/eax: (addr handle array screen-cell) <- get screen, data
233     var data/eax: (addr array screen-cell) <- lookup *data-ah
234     var offset/ecx: (offset screen-cell) <- compute-offset data, index
235     var dest-cell/ecx: (addr screen-cell) <- index data, offset
236     var dest/eax: (addr boolean) <- get dest-cell, unused?
237     copy-to *dest, 1/true
238   }
239 }
240 
241 # fake screens only
242 fn screen-cell-index _screen: (addr screen), x: int, y: int -> _/ecx: int {
243   var screen/esi: (addr screen) <- copy _screen
244   var width-addr/eax: (addr int) <- get screen, width
245   var result/ecx: int <- copy y
246   result <- multiply *width-addr
247   result <- add x
248   return result
249 }
250 
251 fn cursor-position _screen: (addr screen) -> _/eax: int, _/ecx: int {
252   var screen/esi: (addr screen) <- copy _screen
253   {
254     compare screen, 0
255     break-if-!=
256     var x/eax: int <- copy 0
257     var y/ecx: int <- copy 0
258     x, y <- cursor-position-on-real-screen
259     return x, y
260   }
261   # fake screen
262   var cursor-x-addr/eax: (addr int) <- get screen, cursor-x
263   var cursor-y-addr/ecx: (addr int) <- get screen, cursor-y
264   return *cursor-x-addr, *cursor-y-addr
265 }
266 
267 fn set-cursor-position _screen: (addr screen), x: int, y: int {
268   var screen/esi: (addr screen) <- copy _screen
269   {
270     compare screen, 0
271     break-if-!=
272     set-cursor-position-on-real-screen x, y
273     return
274   }
275   # fake screen
276   # ignore x < 0
277   {
278     compare x, 0
279     break-if->=
280     return
281   }
282   # ignore x >= width
283   {
284     var width-addr/eax: (addr int) <- get screen, width
285     var width/eax: int <- copy *width-addr
286     compare x, width
287     break-if-<=
288     return
289   }
290   # ignore y < 0
291   {
292     compare y, 0
293     break-if->=
294     return
295   }
296   # ignore y >= height
297   {
298     var height-addr/eax: (addr int) <- get screen, height
299     var height/eax: int <- copy *height-addr
300     compare y, height
301     break-if-<
302     return
303   }
304   # screen->cursor-x = x
305   var dest/edi: (addr int) <- get screen, cursor-x
306   var src/eax: int <- copy x
307   copy-to *dest, src
308   # screen->cursor-y = y
309   dest <- get screen, cursor-y
310   src <- copy y
311   copy-to *dest, src
312 }
313 
314 fn draw-cursor screen: (addr screen), c: code-point {
315   {
316     compare screen, 0
317     break-if-!=
318     draw-cursor-on-real-screen c
319     return
320   }
321   # fake screen
322   var cursor-x/eax: int <- copy 0
323   var cursor-y/ecx: int <- copy 0
324   cursor-x, cursor-y <- cursor-position screen
325   var dummy/eax: int <- draw-code-point screen, c, cursor-x, cursor-y, 0/fg, 7/bg
326 }
327 
328 fn clear-screen _screen: (addr screen) {
329   var screen/esi: (addr screen) <- copy _screen
330   {
331     compare screen, 0
332     break-if-!=
333     clear-real-screen
334     return
335   }
336   # fake screen
337   set-cursor-position screen, 0, 0
338   var y/eax: int <- copy 0
339   var height/ecx: (addr int) <- get screen, height
340   {
341     compare y, *height
342     break-if->=
343     var x/edx: int <- copy 0
344     var width/ebx: (addr int) <- get screen, width
345     {
346       compare x, *width
347       break-if->=
348       var dummy/eax: int <- draw-code-point screen, 0/nul, x, y, 0/fg=black, 0/bg=black
349       x <- increment
350       loop
351     }
352     y <- increment
353     loop
354   }
355   set-cursor-position screen, 0, 0
356   var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
357   var pixels/eax: (addr array byte) <- lookup *pixels-ah
358   var i/ecx: int <- copy 0
359   var max/edx: int <- length pixels
360   {
361     compare i, max
362     break-if->=
363     var curr/eax: (addr byte) <- index pixels, i
364     var zero/ebx: byte <- copy 0
365     copy-byte-to *curr, zero
366     i <- increment
367     loop
368   }
369 }
370 
371 fn fake-screen-empty? _screen: (addr screen) -> _/eax: boolean {
372   var screen/esi: (addr screen) <- copy _screen
373   var y/eax: int <- copy 0
374   var height/ecx: (addr int) <- get screen, height
375   {
376     compare y, *height
377     break-if->=
378     var x/edx: int <- copy 0
379     var width/ebx: (addr int) <- get screen, width
380     {
381       compare x, *width
382       break-if->=
383       var c/eax: code-point <- screen-code-point-at screen, x, y
384       {
385         compare c, 0
386         break-if-=
387         compare c, 0x20/space
388         break-if-=
389         return 0/false
390       }
391       x <- increment
392       loop
393     }
394     y <- increment
395     loop
396   }
397   var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
398   var pixels/eax: (addr array byte) <- lookup *pixels-ah
399   var y/ebx: int <- copy 0
400   var height-addr/edx: (addr int) <- get screen, height
401   var height/edx: int <- copy *height-addr
402   height <- shift-left 4/log2-font-height
403   {
404     compare y, height
405     break-if->=
406     var width-addr/edx: (addr int) <- get screen, width
407     var width/edx: int <- copy *width-addr
408     width <- shift-left 3/log2-font-width
409     var x/edi: int <- copy 0
410     {
411       compare x, width
412       break-if->=
413       var index/ecx: int <- pixel-index screen, x, y
414       var color-addr/ecx: (addr byte) <- index pixels, index
415       var color/ecx: byte <- copy-byte *color-addr
416       compare color, 0
417       {
418         break-if-=
419         return 0/false
420       }
421       x <- increment
422       loop
423     }
424     y <- increment
425     loop
426   }
427   return 1/true
428 }
429 
430 fn clear-rect _screen: (addr screen), xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
431   var screen/esi: (addr screen) <- copy _screen
432   {
433     compare screen, 0
434     break-if-!=
435     clear-rect-on-real-screen xmin, ymin, xmax, ymax, background-color
436     return
437   }
438   # fake screen
439   set-cursor-position screen, 0, 0
440   var y/eax: int <- copy ymin
441   var ymax/ecx: int <- copy ymax
442   {
443     compare y, ymax
444     break-if->=
445     var x/edx: int <- copy xmin
446     var xmax/ebx: int <- copy xmax
447     {
448       compare x, xmax
449       break-if->=
450       var dummy/eax: int <- draw-code-point screen, 0x20/space, x, y, 0/fg, background-color
451       x <- increment
452       loop
453     }
454     y <- increment
455     loop
456   }
457   set-cursor-position screen, 0, 0
458 }
459 
460 # there's no grapheme that guarantees to cover every pixel, so we'll bump down
461 # to pixels for a real screen
462 fn clear-real-screen {
463   var y/eax: int <- copy 0
464   {
465     compare y, 0x300/screen-height=768
466     break-if->=
467     var x/edx: int <- copy 0
468     {
469       compare x, 0x400/screen-width=1024
470       break-if->=
471       pixel-on-real-screen x, y, 0/color=black
472       x <- increment
473       loop
474     }
475     y <- increment
476     loop
477   }
478 }
479 
480 fn clear-rect-on-real-screen xmin: int, ymin: int, xmax: int, ymax: int, background-color: int {
481   var y/eax: int <- copy ymin
482   y <- shift-left 4/log2-font-height
483   var ymax/ecx: int <- copy ymax
484   ymax <- shift-left 4/log2-font-height
485   {
486     compare y, ymax
487     break-if->=
488     var x/edx: int <- copy xmin
489     x <- shift-left 3/log2-font-width
490     var xmax/ebx: int <- copy xmax
491     xmax <- shift-left 3/log2-font-width
492     {
493       compare x, xmax
494       break-if->=
495       pixel-on-real-screen x, y, background-color
496       x <- increment
497       loop
498     }
499     y <- increment
500     loop
501   }
502 }
503 
504 fn screen-cell-unused-at? _screen: (addr screen), x: int, y: int -> _/eax: boolean {
505   var screen/esi: (addr screen) <- copy _screen
506   var index/ecx: int <- screen-cell-index screen, x, y
507   var result/eax: boolean <- screen-cell-unused-at-index? screen, index
508   return result
509 }
510 
511 fn screen-cell-unused-at-index? _screen: (addr screen), _index: int -> _/eax: boolean {
512   var screen/esi: (addr screen) <- copy _screen
513   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
514   var data/eax: (addr array screen-cell) <- lookup *data-ah
515   var index/ecx: int <- copy _index
516   var offset/ecx: (offset screen-cell) <- compute-offset data, index
517   var cell/eax: (addr screen-cell) <- index data, offset
518   var src/eax: (addr boolean) <- get cell, unused?
519   return *src
520 }
521 
522 fn screen-code-point-at _screen: (addr screen), x: int, y: int -> _/eax: code-point {
523   var screen/esi: (addr screen) <- copy _screen
524   var index/ecx: int <- screen-cell-index screen, x, y
525   var result/eax: code-point <- screen-code-point-at-index screen, index
526   return result
527 }
528 
529 fn screen-code-point-at-index _screen: (addr screen), _index: int -> _/eax: code-point {
530   var screen/esi: (addr screen) <- copy _screen
531   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
532   var data/eax: (addr array screen-cell) <- lookup *data-ah
533   var index/ecx: int <- copy _index
534   var offset/ecx: (offset screen-cell) <- compute-offset data, index
535   var cell/eax: (addr screen-cell) <- index data, offset
536   var src/eax: (addr code-point) <- get cell, data
537   return *src
538 }
539 
540 fn screen-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
541   var screen/esi: (addr screen) <- copy _screen
542   var index/ecx: int <- screen-cell-index screen, x, y
543   var result/eax: int <- screen-color-at-index screen, index
544   return result
545 }
546 
547 fn screen-color-at-index _screen: (addr screen), _index: int -> _/eax: int {
548   var screen/esi: (addr screen) <- copy _screen
549   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
550   var data/eax: (addr array screen-cell) <- lookup *data-ah
551   var index/ecx: int <- copy _index
552   var offset/ecx: (offset screen-cell) <- compute-offset data, index
553   var cell/eax: (addr screen-cell) <- index data, offset
554   var src/eax: (addr int) <- get cell, color
555   var result/eax: int <- copy *src
556   return result
557 }
558 
559 fn screen-background-color-at _screen: (addr screen), x: int, y: int -> _/eax: int {
560   var screen/esi: (addr screen) <- copy _screen
561   var index/ecx: int <- screen-cell-index screen, x, y
562   var result/eax: int <- screen-background-color-at-index screen, index
563   return result
564 }
565 
566 fn screen-background-color-at-index _screen: (addr screen), _index: int -> _/eax: int {
567   var screen/esi: (addr screen) <- copy _screen
568   var data-ah/eax: (addr handle array screen-cell) <- get screen, data
569   var data/eax: (addr array screen-cell) <- lookup *data-ah
570   var index/ecx: int <- copy _index
571   var offset/ecx: (offset screen-cell) <- compute-offset data, index
572   var cell/eax: (addr screen-cell) <- index data, offset
573   var src/eax: (addr int) <- get cell, background-color
574   var result/eax: int <- copy *src
575   return result
576 }
577 
578 fn pixel screen: (addr screen), x: int, y: int, color: int {
579   {
580     compare screen, 0
581     break-if-!=
582     pixel-on-real-screen x, y, color
583     return
584   }
585   # fake screen
586   var screen/esi: (addr screen) <- copy screen
587   var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
588   var pixels/eax: (addr array byte) <- lookup *pixels-ah
589   {
590     compare pixels, 0
591     break-if-!=
592     abort "pixel graphics not enabled for this screen"
593   }
594   # ignore if out of bounds
595   {
596     compare x, 0
597     break-if->=
598     return
599   }
600   {
601     var xmax-addr/eax: (addr int) <- get screen, width
602     var xmax/eax: int <- copy *xmax-addr
603     xmax <- shift-left 3/log2-font-width
604     compare x, xmax
605     break-if-<
606     return
607   }
608   {
609     compare y, 0
610     break-if->=
611     return
612   }
613   {
614     var ymax-addr/eax: (addr int) <- get screen, height
615     var ymax/eax: int <- copy *ymax-addr
616     ymax <- shift-left 4/log2-font-height
617     compare y, ymax
618     break-if-<
619     return
620   }
621   #
622   var index/ecx: int <- pixel-index screen, x, y
623   var dest/ecx: (addr byte) <- index pixels, index
624   var src/eax: byte <- copy-byte color
625   copy-byte-to *dest, src
626 }
627 
628 fn pixel-index _screen: (addr screen), x: int, y: int -> _/ecx: int {
629   var screen/esi: (addr screen) <- copy _screen
630   var width-addr/eax: (addr int) <- get screen, width
631   var result/ecx: int <- copy y
632   result <- multiply *width-addr
633   result <- shift-left 3/log2-font-width
634   result <- add x
635   return result
636 }
637 
638 # double-buffering primitive
639 # 'screen' must be a fake screen. 'target-screen' is usually real.
640 # Both screens must have the same size.
641 fn copy-pixels _screen: (addr screen), target-screen: (addr screen) {
642   var screen/esi: (addr screen) <- copy _screen
643   var pixels-ah/eax: (addr handle array byte) <- get screen, pixels
644   var _pixels/eax: (addr array byte) <- lookup *pixels-ah
645   var pixels/edi: (addr array byte) <- copy _pixels
646   var width-a/edx: (addr int) <- get screen, width
647   var width/edx: int <- copy *width-a
648   width <- shift-left 3/log2-font-width
649   var height-a/ebx: (addr int) <- get screen, height
650   var height/ebx: int <- copy *height-a
651   height <- shift-left 4/log2-font-height
652   var i/esi: int <- copy 0
653   var y/ecx: int <- copy 0
654   {
655     # screen top left pixels x y width height
656     compare y, height
657     break-if->=
658     var x/eax: int <- copy 0
659     {
660       compare x, width
661       break-if->=
662       {
663         var color-addr/ebx: (addr byte) <- index pixels, i
664         var color/ebx: byte <- copy-byte *color-addr
665         var color2/ebx: int <- copy color
666         pixel target-screen, x, y, color2
667       }
668       x <- increment
669       i <- increment
670       loop
671     }
672     y <- increment
673     loop
674   }
675 }
676 
677 # It turns out double-buffering screen-cells is useless because rendering fonts
678 # takes too long. (At least under Qemu.)
679 # So we'll instead convert screen-cells to pixels when double-buffering.
680 # 'screen' must be a fake screen.
681 fn convert-screen-cells-to-pixels _screen: (addr screen) {
682   var screen/esi: (addr screen) <- copy _screen
683   var width-a/ebx: (addr int) <- get screen, width
684   var height-a/edx: (addr int) <- get screen, height
685   var data-ah/eax: (addr handle array byte) <- get screen, pixels
686   var _data/eax: (addr array byte) <- lookup *data-ah
687   var data: (addr array byte)
688   copy-to data, _data
689   var y/ecx: int <- copy 0
690   {
691     compare y, *height-a
692     break-if->=
693     var x/edi: int <- copy 0
694     $convert-screen-cells-to-pixels:loop-x: {
695       compare x, *width-a
696       break-if->=
697       {
698         var tmp/eax: code-point <- screen-code-point-at screen, x, y
699         # skip null code-points that only get created when clearing screen
700         # there may be other pixels drawn there, and we don't want to clobber them
701         # this is a situation where fake screens aren't faithful to real screens; we don't support overlap between screen-cells and raw pixels
702         compare tmp, 0
703         break-if-=
704         var c: code-point
705         copy-to c, tmp
706         var tmp/eax: int <- screen-color-at screen, x, y
707         var fg: int
708         copy-to fg, tmp
709         var bg/eax: int <- screen-background-color-at screen, x, y
710         var offset/eax: int <- draw-code-point-on-screen-array data, c, x, y, fg, bg, *width-a, *height-a
711         x <- add offset
712         loop $convert-screen-cells-to-pixels:loop-x
713       }
714       x <- increment
715       loop
716     }
717     y <- increment
718     loop
719   }
720 }