1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 container screen [
17 num-rows:num
18 num-columns:num
19 cursor-row:num
20 cursor-column:num
21 data:&:@:screen-cell
22 pending-scroll?:bool
23 top-idx:num
24
25 ]
26
27 container screen-cell [
28 contents:char
29 color:num
30 ]
31
32 def new-fake-screen w:num, h:num -> result:&:screen [
33 local-scope
34 load-inputs
35 result <- new screen:type
36 non-zero-width?:bool <- greater-than w, 0
37 assert non-zero-width?, [screen can't have zero width]
38 non-zero-height?:bool <- greater-than h, 0
39 assert non-zero-height?, [screen can't have zero height]
40 bufsize:num <- multiply w, h
41 data:&:@:screen-cell <- new screen-cell:type, bufsize
42 *result <- merge h/num-rows, w/num-columns, 0/cursor-row, 0/cursor-column, data, 0/pending-scroll?, 0/top-idx
43 result <- clear-screen result
44 ]
45
46 def clear-screen screen:&:screen -> screen:&:screen [
47 local-scope
48 load-inputs
49
50 {
51 break-if screen
52
53 clear-display
54 return
55 }
56
57 buf:&:@:screen-cell <- get *screen, data:offset
58 max:num <- length *buf
59 i:num <- copy 0
60 {
61 done?:bool <- greater-or-equal i, max
62 break-if done?
63 curr:screen-cell <- merge 0/empty, 7/white
64 *buf <- put-index *buf, i, curr
65 i <- add i, 1
66 loop
67 }
68
69 *screen <- put *screen, cursor-row:offset, 0
70 *screen <- put *screen, cursor-column:offset, 0
71 *screen <- put *screen, top-idx:offset, 0
72 ]
73
74 def fake-screen-is-empty? screen:&:screen -> result:bool [
75 local-scope
76 load-inputs
77
78 return-unless screen, 1/true
79 buf:&:@:screen-cell <- get *screen, data:offset
80 i:num <- copy 0
81 len:num <- length *buf
82 {
83 done?:bool <- greater-or-equal i, len
84 break-if done?
85 curr:screen-cell <- index *buf, i
86 curr-contents:char <- get curr, contents:offset
87 i <- add i, 1
88 loop-unless curr-contents
89
90 return 0/false
91 }
92 return 1/true
93 ]
94
95 def print screen:&:screen, c:char -> screen:&:screen [
96 local-scope
97 load-inputs
98 color:num, color-found?:bool <- next-input
99 {
100
101 break-if color-found?
102 color <- copy 7/white
103 }
104 bg-color:num, bg-color-found?:bool <- next-input
105 {
106
107 break-if bg-color-found?
108 bg-color <- copy 0/black
109 }
110 c2:num <- character-to-code c
111 trace 90, [print-character], c2
112 {
113
114 break-if screen
115 print-character-to-display c, color, bg-color
116 return
117 }
118
119
120 width:num <- get *screen, num-columns:offset
121 height:num <- get *screen, num-rows:offset
122 capacity:num <- multiply width, height
123 row:num <- get *screen, cursor-row:offset
124 column:num <- get *screen, cursor-column:offset
125 buf:&:@:screen-cell <- get *screen, data:offset
126
127
128 row <- round row
129 column <- round column
130
131 {
132 too-far-left?:bool <- lesser-than column, 0
133 break-unless too-far-left?
134 column <- copy 0
135 *screen <- put *screen, cursor-column:offset, column
136 }
137
138 {
139 at-right?:bool <- greater-or-equal column, width
140 break-unless at-right?
141 column <- copy 0
142 *screen <- put *screen, cursor-column:offset, column
143 row <- add row, 1
144 *screen <- put *screen, cursor-row:offset, row
145 }
146
147
148 {
149 pending-scroll?:bool <- get *screen, pending-scroll?:offset
150 break-unless pending-scroll?
151
152 scroll-fake-screen screen
153 *screen <- put *screen, pending-scroll?:offset, 0/false
154 }
155
156
157 {
158 newline?:bool <- equal c, 10/newline
159 break-unless newline?
160 cursor-down-on-fake-screen screen
161 return
162 }
163
164 {
165 linefeed?:bool <- equal c, 13/linefeed
166 break-unless linefeed?
167 *screen <- put *screen, cursor-column:offset, 0
168 return
169 }
170
171
172 {
173 backspace?:bool <- equal c, 8/backspace
174 break-unless backspace?
175 {
176 break-unless column
177 column <- subtract column, 1
178 *screen <- put *screen, cursor-column:offset, column
179 }
180 return
181 }
182
183 top-idx:num <- get *screen, top-idx:offset
184 index:num <- data-index row, column, width, height, top-idx
185 cursor:screen-cell <- merge c, color
186 *buf <- put-index *buf, index, cursor
187
188
189 column <- add column, 1
190 {
191 past-right?:bool <- greater-or-equal column, width
192 break-unless past-right?
193 column <- copy 0
194 row <- add row, 1
195 past-bottom?:bool <- greater-or-equal row, height
196 break-unless past-bottom?
197
198
199 *screen <- put *screen, pending-scroll?:offset, 1/true
200 row <- subtract row, 1
201 }
202 *screen <- put *screen, cursor-row:offset, row
203 *screen <- put *screen, cursor-column:offset, column
204 ]
205
206 def cursor-down-on-fake-screen screen:&:screen -> screen:&:screen [
207 local-scope
208 load-inputs
209
210 row:num <- get *screen, cursor-row:offset
211 height:num <- get *screen, num-rows:offset
212 bottom:num <- subtract height, 1
213 at-bottom?:bool <- greater-or-equal row, bottom
214 {
215 break-if at-bottom?
216 row <- add row, 1
217 *screen <- put *screen, cursor-row:offset, row
218 }
219 {
220 break-unless at-bottom?
221 scroll-fake-screen screen
222 }
223 ]
224
225 def scroll-fake-screen screen:&:screen -> screen:&:screen [
226 local-scope
227 load-inputs
228
229 width:num <- get *screen, num-columns:offset
230 height:num <- get *screen, num-rows:offset
231 buf:&:@:screen-cell <- get *screen, data:offset
232
233 top-idx:num <- get *screen, top-idx:offset
234 next-top-idx:num <- add top-idx, width
235 empty-cell:screen-cell <- merge 0/empty, 7/white
236 {
237 done?:bool <- greater-or-equal top-idx, next-top-idx
238 break-if done?
239 put-index *buf, top-idx, empty-cell
240 top-idx <- add top-idx, 1
241
242
243 loop
244 }
245
246 capacity:num <- multiply width, height
247 _, top-idx <- divide-with-remainder, top-idx, capacity
248 *screen <- put *screen, top-idx:offset, top-idx
249 ]
250
251
252
253 def data-index row:num, column:num, width:num, height:num, top-idx:num -> result:num [
254 local-scope
255 load-inputs
256 {
257 overflow?:bool <- greater-or-equal row, height
258 break-unless overflow?
259 row <- subtract height, 1
260 }
261 result <- multiply width, row
262 result <- add result, column, top-idx
263 capacity:num <- multiply width, height
264 _, result <- divide-with-remainder result, capacity
265 ]
266
267 scenario print-character-at-top-left [
268 local-scope
269 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
270 run [
271 a:char <- copy 97/a
272 fake-screen <- print fake-screen, a:char
273 cell:&:@:screen-cell <- get *fake-screen, data:offset
274 1:@:screen-cell/raw <- copy *cell
275 ]
276 memory-should-contain [
277 1 <- 6
278 2 <- 97
279 3 <- 7
280
281 4 <- 0
282 ]
283 ]
284
285 scenario print-character-at-fractional-coordinate [
286 local-scope
287 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
288 a:char <- copy 97/a
289 run [
290 move-cursor fake-screen, 0.5, 0
291 fake-screen <- print fake-screen, a:char
292 cell:&:@:screen-cell <- get *fake-screen, data:offset
293 1:@:screen-cell/raw <- copy *cell
294 ]
295 memory-should-contain [
296 1 <- 6
297 2 <- 97
298 3 <- 7
299
300 4 <- 0
301 ]
302 ]
303
304 scenario print-character-in-color [
305 local-scope
306 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
307 run [
308 a:char <- copy 97/a
309 fake-screen <- print fake-screen, a:char, 1/red
310 cell:&:@:screen-cell <- get *fake-screen, data:offset
311 1:@:screen-cell/raw <- copy *cell
312 ]
313 memory-should-contain [
314 1 <- 6
315 2 <- 97
316 3 <- 1
317
318 4 <- 0
319 ]
320 ]
321
322 scenario print-backspace-character [
323 local-scope
324 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
325 a:char <- copy 97/a
326 fake-screen <- print fake-screen, a
327 run [
328 backspace:char <- copy 8/backspace
329 fake-screen <- print fake-screen, backspace
330 10:num/raw <- get *fake-screen, cursor-column:offset
331 cell:&:@:screen-cell <- get *fake-screen, data:offset
332 11:@:screen-cell/raw <- copy *cell
333 ]
334 memory-should-contain [
335 10 <- 0
336 11 <- 6
337 12 <- 97
338 13 <- 7
339
340 14 <- 0
341 ]
342 ]
343
344 scenario print-extra-backspace-character [
345 local-scope
346 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
347 a:char <- copy 97/a
348 fake-screen <- print fake-screen, a
349 run [
350 backspace:char <- copy 8/backspace
351 fake-screen <- print fake-screen, backspace
352 fake-screen <- print fake-screen, backspace
353 1:num/raw <- get *fake-screen, cursor-column:offset
354 cell:&:@:screen-cell <- get *fake-screen, data:offset
355 3:@:screen-cell/raw <- copy *cell
356 ]
357 memory-should-contain [
358 1 <- 0
359 3 <- 6
360 4 <- 97
361 5 <- 7
362
363 6 <- 0
364 ]
365 ]
366
367 scenario print-character-at-right-margin [
368
369 local-scope
370 fake-screen:&:screen <- new-fake-screen 2/width, 2/height
371 a:char <- copy 97/a
372 fake-screen <- print fake-screen, a
373 b:char <- copy 98/b
374 fake-screen <- print fake-screen, b
375 run [
376
377 c:char <- copy 99/c
378 fake-screen <- print fake-screen, c
379 10:num/raw <- get *fake-screen, cursor-row:offset
380 11:num/raw <- get *fake-screen, cursor-column:offset
381 cell:&:@:screen-cell <- get *fake-screen, data:offset
382 12:@:screen-cell/raw <- copy *cell
383 ]
384 memory-should-contain [
385 10 <- 1
386 11 <- 1
387 12 <- 4
388 13 <- 97
389 14 <- 7
390 15 <- 98
391 16 <- 7
392 17 <- 99
393 18 <- 7
394 19 <- 0
395 20 <- 7
396 ]
397 ]
398
399 scenario print-newline-character [
400 local-scope
401 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
402 a:char <- copy 97/a
403 fake-screen <- print fake-screen, a
404 run [
405 newline:char <- copy 10/newline
406 fake-screen <- print fake-screen, newline
407 10:num/raw <- get *fake-screen, cursor-row:offset
408 11:num/raw <- get *fake-screen, cursor-column:offset
409 cell:&:@:screen-cell <- get *fake-screen, data:offset
410 12:@:screen-cell/raw <- copy *cell
411 ]
412 memory-should-contain [
413 10 <- 1
414 11 <- 1
415 12 <- 6
416 13 <- 97
417 14 <- 7
418
419 15 <- 0
420 ]
421 ]
422
423 scenario print-newline-at-bottom-line [
424 local-scope
425 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
426 newline:char <- copy 10/newline
427 fake-screen <- print fake-screen, newline
428 fake-screen <- print fake-screen, newline
429 run [
430
431 fake-screen <- print fake-screen, newline
432 10:num/raw <- get *fake-screen, cursor-row:offset
433 11:num/raw <- get *fake-screen, cursor-column:offset
434 ]
435
436 memory-should-contain [
437 10 <- 1
438 11 <- 0
439 ]
440 ]
441
442 scenario print-character-at-bottom-right [
443 local-scope
444 fake-screen:&:screen <- new-fake-screen 2/width, 2/height
445 a:char <- copy 97/a
446 fake-screen <- print fake-screen, a
447 b:char <- copy 98/b
448 fake-screen <- print fake-screen, b
449 c:char <- copy 99/c
450 fake-screen <- print fake-screen, c
451 run [
452
453 d:char <- copy 100/d
454 fake-screen <- print fake-screen, d
455 10:num/raw <- get *fake-screen, cursor-row:offset
456 11:num/raw <- get *fake-screen, cursor-column:offset
457 12:num/raw <- get *fake-screen, top-idx:offset
458 13:num/raw <- get *fake-screen, pending-scroll?:offset
459 cell:&:@:screen-cell <- get *fake-screen, data:offset
460 20:@:screen-cell/raw <- copy *cell
461 ]
462
463 memory-should-contain [
464 10 <- 1
465 11 <- 0
466 12 <- 0
467 13 <- 1
468 20 <- 4
469 21 <- 97
470 22 <- 7
471 23 <- 98
472 24 <- 7
473 25 <- 99
474 26 <- 7
475 27 <- 100
476 28 <- 7
477 ]
478 run [
479 e:char <- copy 101/e
480 print fake-screen, e
481 10:num/raw <- get *fake-screen, cursor-row:offset
482 11:num/raw <- get *fake-screen, cursor-column:offset
483 12:num/raw <- get *fake-screen, top-idx:offset
484 cell:&:@:screen-cell <- get *fake-screen, data:offset
485 20:@:screen-cell/raw <- copy *cell
486 ]
487 memory-should-contain [
488
489 10 <- 1
490 11 <- 1
491 12 <- 2
492 20 <- 4
493
494 25 <- 99
495 26 <- 7
496 27 <- 100
497 28 <- 7
498
499 21 <- 101
500 22 <- 7
501 23 <- 0
502 24 <- 7
503 ]
504 ]
505
506
507
508
509 def save-top-idx screen:&:screen -> result:num [
510 local-scope
511 load-inputs
512 return-unless screen, 0
513 result <- get *screen, top-idx:offset
514 ]
515 def assert-no-scroll screen:&:screen, old-top-idx:num [
516 local-scope
517 load-inputs
518 return-unless screen
519 new-top-idx:num <- get *screen, top-idx:offset
520 no-scroll?:bool <- equal old-top-idx, new-top-idx
521 assert no-scroll?, [render should never use screen's scrolling capabilities]
522 ]
523
524 def clear-line screen:&:screen -> screen:&:screen [
525 local-scope
526 load-inputs
527
528 space:char <- copy 0/nul
529 {
530 break-if screen
531
532 clear-line-on-display
533 return
534 }
535
536 width:num <- get *screen, num-columns:offset
537 column:num <- get *screen, cursor-column:offset
538 original-column:num <- copy column
539
540 {
541 right:num <- subtract width, 1
542 done?:bool <- greater-or-equal column, right
543 break-if done?
544 print screen, space
545 column <- add column, 1
546 loop
547 }
548
549 *screen <- put *screen, cursor-column:offset, original-column
550 ]
551
552
553 def clear-line-until screen:&:screen, right:num/inclusive -> screen:&:screen [
554 local-scope
555 load-inputs
556 row:num, column:num <- cursor-position screen
557
558 height:num <- screen-height screen
559 past-bottom?:bool <- greater-or-equal row, height
560 return-if past-bottom?
561 space:char <- copy 32/space
562 bg-color:num, bg-color-found?:bool <- next-input
563 {
564
565 break-if bg-color-found?
566 bg-color <- copy 0/black
567 }
568 {
569 done?:bool <- greater-than column, right
570 break-if done?
571 screen <- print screen, space, 7/white, bg-color
572 column <- add column, 1
573 loop
574 }
575 ]
576
577 def cursor-position screen:&:screen -> row:num, column:num [
578 local-scope
579 load-inputs
580 {
581 break-if screen
582
583 row, column <- cursor-position-on-display
584 return
585 }
586
587 row:num <- get *screen, cursor-row:offset
588 column:num <- get *screen, cursor-column:offset
589 ]
590
591 def move-cursor screen:&:screen, new-row:num, new-column:num -> screen:&:screen [
592 local-scope
593 load-inputs
594
595 {
596 break-if screen
597
598 move-cursor-on-display new-row, new-column
599 return
600 }
601
602 *screen <- put *screen, cursor-row:offset, new-row
603 *screen <- put *screen, cursor-column:offset, new-column
604
605 {
606 width:num <- get *screen, num-columns:offset
607 scroll?:bool <- greater-or-equal new-column, width
608 break-if scroll?
609
610 *screen <- put *screen, pending-scroll?:offset, 0/false
611 }
612 ]
613
614 scenario clear-line-erases-printed-characters [
615 local-scope
616 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
617
618 a:char <- copy 97/a
619 fake-screen <- print fake-screen, a
620
621 fake-screen <- move-cursor fake-screen, 0/row, 0/column
622 run [
623 fake-screen <- clear-line fake-screen
624 cell:&:@:screen-cell <- get *fake-screen, data:offset
625 10:@:screen-cell/raw <- copy *cell
626 ]
627
628 memory-should-contain [
629 10 <- 6
630 11 <- 0
631 12 <- 7
632 13 <- 0
633 14 <- 7
634 15 <- 0
635 16 <- 7
636 17 <- 0
637 18 <- 7
638 19 <- 0
639 20 <- 7
640 21 <- 0
641 22 <- 7
642 ]
643 ]
644
645 def cursor-down screen:&:screen -> screen:&:screen [
646 local-scope
647 load-inputs
648
649 {
650 break-if screen
651
652 move-cursor-down-on-display
653 return
654 }
655
656 cursor-down-on-fake-screen screen
657 ]
658
659 scenario cursor-down-scrolls [
660 local-scope
661 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
662
663 run [
664 print fake-screen, [abc]
665 cursor-to-next-line fake-screen
666 cursor-to-next-line fake-screen
667 data:&:@:screen-cell <- get *fake-screen, data:offset
668 10:@:screen-cell/raw <- copy *data
669 ]
670
671 memory-should-contain [
672 10 <- 6
673 11 <- 0
674 12 <- 7
675 13 <- 0
676 14 <- 7
677 15 <- 0
678 16 <- 7
679 17 <- 0
680 18 <- 7
681 19 <- 0
682 20 <- 7
683 21 <- 0
684 22 <- 7
685 ]
686 ]
687
688 def cursor-up screen:&:screen -> screen:&:screen [
689 local-scope
690 load-inputs
691
692 {
693 break-if screen
694
695 move-cursor-up-on-display
696 return
697 }
698
699 row:num <- get *screen, cursor-row:offset
700 at-top?:bool <- lesser-or-equal row, 0
701 return-if at-top?
702 row <- subtract row, 1
703 *screen <- put *screen, cursor-row:offset, row
704 ]
705
706 def cursor-right screen:&:screen -> screen:&:screen [
707 local-scope
708 load-inputs
709
710 {
711 break-if screen
712
713 move-cursor-right-on-display
714 return
715 }
716
717 width:num <- get *screen, num-columns:offset
718 column:num <- get *screen, cursor-column:offset
719 max:num <- subtract width, 1
720 at-bottom?:bool <- greater-or-equal column, max
721 return-if at-bottom?
722 column <- add column, 1
723 *screen <- put *screen, cursor-column:offset, column
724 ]
725
726 def cursor-left screen:&:screen -> screen:&:screen [
727 local-scope
728 load-inputs
729
730 {
731 break-if screen
732
733 move-cursor-left-on-display
734 return
735 }
736
737 column:num <- get *screen, cursor-column:offset
738 at-top?:bool <- lesser-or-equal column, 0
739 return-if at-top?
740 column <- subtract column, 1
741 *screen <- put *screen, cursor-column:offset, column
742 ]
743
744 def cursor-to-start-of-line screen:&:screen -> screen:&:screen [
745 local-scope
746 load-inputs
747
748 row:num <- cursor-position screen
749 screen <- move-cursor screen, row, 0/column
750 ]
751
752 def cursor-to-next-line screen:&:screen -> screen:&:screen [
753 local-scope
754 load-inputs
755
756 screen <- cursor-down screen
757 screen <- cursor-to-start-of-line screen
758 ]
759
760 def move-cursor-to-column screen:&:screen, column:num -> screen:&:screen [
761 local-scope
762 load-inputs
763 row:num, _ <- cursor-position screen
764
765 move-cursor screen, row, column
766 ]
767
768 def screen-width screen:&:screen -> width:num [
769 local-scope
770 load-inputs
771
772 {
773 break-unless screen
774
775 width <- get *screen, num-columns:offset
776 return
777 }
778
779 width <- display-width
780 ]
781
782 def screen-height screen:&:screen -> height:num [
783 local-scope
784 load-inputs
785
786 {
787 break-unless screen
788
789 height <- get *screen, num-rows:offset
790 return
791 }
792
793 height <- display-height
794 ]
795
796 def print screen:&:screen, s:text -> screen:&:screen [
797 local-scope
798 load-inputs
799 color:num, color-found?:bool <- next-input
800 {
801
802 break-if color-found?
803 color <- copy 7/white
804 }
805 bg-color:num, bg-color-found?:bool <- next-input
806 {
807
808 break-if bg-color-found?
809 bg-color <- copy 0/black
810 }
811 len:num <- length *s
812 i:num <- copy 0
813 {
814 done?:bool <- greater-or-equal i, len
815 break-if done?
816 c:char <- index *s, i
817 print screen, c, color, bg-color
818 i <- add i, 1
819 loop
820 }
821 ]
822
823 scenario print-text-wraps-past-right-margin [
824 local-scope
825 fake-screen:&:screen <- new-fake-screen 3/width, 2/height
826 run [
827 fake-screen <- print fake-screen, [abcd]
828 5:num/raw <- get *fake-screen, cursor-row:offset
829 6:num/raw <- get *fake-screen, cursor-column:offset
830 7:num/raw <- get *fake-screen, top-idx:offset
831 cell:&:@:screen-cell <- get *fake-screen, data:offset
832 10:@:screen-cell/raw <- copy *cell
833 ]
834 memory-should-contain [
835 5 <- 1
836 6 <- 1
837 7 <- 0
838 10 <- 6
839 11 <- 97
840 12 <- 7
841 13 <- 98
842 14 <- 7
843 15 <- 99
844 16 <- 7
845 17 <- 100
846 18 <- 7
847
848 19 <- 0
849 ]
850 ]
851
852 def print screen:&:screen, n:num -> screen:&:screen [
853 local-scope
854 load-inputs
855 color:num, color-found?:bool <- next-input
856 {
857
858 break-if color-found?
859 color <- copy 7/white
860 }
861 bg-color:num, bg-color-found?:bool <- next-input
862 {
863
864 break-if bg-color-found?
865 bg-color <- copy 0/black
866 }
867
868 s:text <- to-text n
869 screen <- print screen, s, color, bg-color
870 ]
871
872 def print screen:&:screen, n:bool -> screen:&:screen [
873 local-scope
874 load-inputs
875 color:num, color-found?:bool <- next-input
876 {
877
878 break-if color-found?
879 color <- copy 7/white
880 }
881 bg-color:num, bg-color-found?:bool <- next-input
882 {
883
884 break-if bg-color-found?
885 bg-color <- copy 0/black
886 }
887 n2:num <- copy n
888 screen <- print screen, n2, color, bg-color
889 ]
890
891 def print screen:&:screen, n:&:_elem -> screen:&:screen [
892 local-scope
893 load-inputs
894 color:num, color-found?:bool <- next-input
895 {
896
897 break-if color-found?
898 color <- copy 7/white
899 }
900 bg-color:num, bg-color-found?:bool <- next-input
901 {
902
903 break-if bg-color-found?
904 bg-color <- copy 0/black
905 }
906 n2:num <- copy n
907 screen <- print screen, n2, color, bg-color
908 ]